diff mbox series

[08/10] iio: backend: add new functionality

Message ID 20240328-iio-backend-axi-dac-v1-8-afc808b3fde3@analog.com (mailing list archive)
State Changes Requested
Headers show
Series iio: dac: support IIO backends on the output direction | expand

Commit Message

Nuno Sa via B4 Relay March 28, 2024, 1:22 p.m. UTC
From: Nuno Sa <nuno.sa@analog.com>

This adds the needed backend ops for supporting a backend inerfacing
with an high speed dac. The new ops are:

* data_source_set();
* set_sampling_freq();
* extend_chan_spec();
* ext_info_set();
* ext_info_get().

Also to note the new helpers that are meant to be used by the backends
when extending an IIO channel (adding extended info):

* iio_backend_ext_info_set();
* iio_backend_ext_info_get().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-backend.c | 144 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/backend.h        |  49 +++++++++++++
 2 files changed, 193 insertions(+)

Comments

Jonathan Cameron March 28, 2024, 3:16 p.m. UTC | #1
On Thu, 28 Mar 2024 14:22:32 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> This adds the needed backend ops for supporting a backend inerfacing
> with an high speed dac. The new ops are:
> 
> * data_source_set();
> * set_sampling_freq();
> * extend_chan_spec();
> * ext_info_set();
> * ext_info_get().
> 
> Also to note the new helpers that are meant to be used by the backends
> when extending an IIO channel (adding extended info):
> 
> * iio_backend_ext_info_set();
> * iio_backend_ext_info_get().
> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
I'm pretty flexible on this so far as I think we are still learning how front
ends and backends should interact. Maybe we'll figure that out in the medium
term and rework this stuff. For now it looks fine. A few minor things inline.
>  
> +/**
> + * iio_backend_ext_info_get - IIO ext_info read callback
> + * @indio_dev:	IIO device
> + * @private:	Data private to the driver
> + * @chan:	IIO channel
> + * @buf:	Buffer where to place the attribute data
> + *
> + * This helper is intended to be used by backends that extend an IIO channel
> + * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
> + * backends are not supposed to give their own callbacks (as they would not
> + * a way to get te backend from indio_dev). This is the getter.

te->the?


> +/**
> + * iio_backend_extend_chan_spec - Extend an IIO channel
> + * @indio_dev:	IIO device
> + * @back:	Backend device
> + * @chan:	IIO channel
> + *
> + * Some backends may have their own functionalities and hence capable of
> + * extending a frontend's channel.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
> +				 struct iio_backend *back,
> +				 struct iio_chan_spec *chan)
> +{
> +	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
This is getting confusing.  So this one is the front end value of ext_info?
Name it as such frontend_ext_info

> +	int ret;
> +
> +	ret = iio_backend_op_call(back, extend_chan_spec, chan);
> +	if (ret)
> +		return ret;
> +	/*
> +	 * Let's keep things simple for now. Don't allow to overwrite the
> +	 * frontend's extended info. If ever needed, we can support appending
> +	 * it.
> +	 */
> +	if (ext_info && chan->ext_info != ext_info)
> +		return -EOPNOTSUPP;
> +	if (!chan->ext_info)

This is checking if the backend added anything? Perhaps a comment on that
as we don't need a backend_ext_info local variable...

> +		return 0;
> +	/*
> +	 * !\NOTE: this will break as soon as we have multiple backends on one
> +	 * frontend and all of them extend channels. In that case, the core
> +	 * backend code has no way to get the correct backend given the
> +	 * iio device.
> +	 *
> +	 * One solution for this could be introducing a new backend
> +	 * dedicated callback in struct iio_info so we can callback into the
> +	 * frontend so it can give us the right backend given a chan_spec.
> +	 */

Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
a front end with multiple backends is using multiple IIO buffers?

As you say a dance via the front end would work fine.


> +	iio_device_set_drvdata(indio_dev, back);
> +
> +	/* Don't allow backends to get creative and force their own handlers */
> +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> +		if (ext_info->read != iio_backend_ext_info_get)
> +			return -EINVAL;
> +		if (ext_info->write != iio_backend_ext_info_set)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);

> diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> index a6d79381866e..09ff2f8f9fd8 100644
> --- a/include/linux/iio/backend.h
> +++ b/include/linux/iio/backend.h
> @@ -4,6 +4,7 @@
>  
>  #include <linux/types.h>
>  
> +struct iio_chan_spec;
>  struct fwnode_handle;
>  struct iio_backend;
>  struct device;
> @@ -15,6 +16,26 @@ enum iio_backend_data_type {
>  	IIO_BACKEND_DATA_TYPE_MAX
>  };
>  
> +enum iio_backend_data_source {
> +	IIO_BACKEND_INTERNAL_CW,

CW?  Either expand out what ever that is in definition of add a comment
at least.

> +	IIO_BACKEND_EXTERNAL,
What does external mean in this case?
> +	IIO_BACKEND_DATA_SOURCE_MAX
> +};
> +
> +/**
> + * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
> + * @_name:	Attribute name
> + * @_shared:	Whether the attribute is shared between all channels
> + * @_what:	Data private to the driver
> + */
> +#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
> +	.name = (_name),				\
> +	.shared = (_shared),				\
> +	.read =  iio_backend_ext_info_get,		\
> +	.write = iio_backend_ext_info_set,		\
> +	.private = (_what),				\
> +}
> +
>  /**
>   * struct iio_backend_data_fmt - Backend data format
>   * @type:		Data type.
> @@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
>   * @chan_enable:	Enable one channel.
>   * @chan_disable:	Disable one channel.
>   * @data_format_set:	Configure the data format for a specific channel.
> + * @data_source_set:	Configure the data source for a specific channel.
> + * @set_sample_rate:	Configure the sampling rate for a specific channel.
>   * @request_buffer:	Request an IIO buffer.
>   * @free_buffer:	Free an IIO buffer.
> + * @extend_chan_spec:	Extend an IIO channel.
> + * @ext_info_set:	Extended info setter.
> + * @ext_info_get:	Extended info getter.
>   **/
>  struct iio_backend_ops {
>  	int (*enable)(struct iio_backend *back);
> @@ -45,10 +71,21 @@ struct iio_backend_ops {
>  	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
>  	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
>  			       const struct iio_backend_data_fmt *data);
> +	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
> +			       enum iio_backend_data_source data);
> +	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
> +			       u64 sample_rate);

Name the parameter that so we know the units.  _hz?
Nuno Sá March 28, 2024, 3:42 p.m. UTC | #2
On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 14:22:32 +0100
> Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > This adds the needed backend ops for supporting a backend inerfacing
> > with an high speed dac. The new ops are:
> > 
> > * data_source_set();
> > * set_sampling_freq();
> > * extend_chan_spec();
> > * ext_info_set();
> > * ext_info_get().
> > 
> > Also to note the new helpers that are meant to be used by the backends
> > when extending an IIO channel (adding extended info):
> > 
> > * iio_backend_ext_info_set();
> > * iio_backend_ext_info_get().
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> I'm pretty flexible on this so far as I think we are still learning how front
> ends and backends should interact. Maybe we'll figure that out in the medium
> term and rework this stuff. For now it looks fine. A few minor things inline.
> >  
> > +/**
> > + * iio_backend_ext_info_get - IIO ext_info read callback
> > + * @indio_dev:	IIO device
> > + * @private:	Data private to the driver
> > + * @chan:	IIO channel
> > + * @buf:	Buffer where to place the attribute data
> > + *
> > + * This helper is intended to be used by backends that extend an IIO channel
> > + * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
> > + * backends are not supposed to give their own callbacks (as they would not
> > + * a way to get te backend from indio_dev). This is the getter.
> 
> te->the?

Yes and some more typos :).

> 
> 
> > +/**
> > + * iio_backend_extend_chan_spec - Extend an IIO channel
> > + * @indio_dev:	IIO device
> > + * @back:	Backend device
> > + * @chan:	IIO channel
> > + *
> > + * Some backends may have their own functionalities and hence capable of
> > + * extending a frontend's channel.
> > + *
> > + * RETURNS:
> > + * 0 on success, negative error number on failure.
> > + */
> > +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
> > +				 struct iio_backend *back,
> > +				 struct iio_chan_spec *chan)
> > +{
> > +	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
> This is getting confusing.  So this one is the front end value of ext_info?
> Name it as such frontend_ext_info

Yes, it's the frontend pointer. Just to enforce the below constrain. Will rename as
suggested.

> 
> > +	int ret;
> > +
> > +	ret = iio_backend_op_call(back, extend_chan_spec, chan);
> > +	if (ret)
> > +		return ret;
> > +	/*
> > +	 * Let's keep things simple for now. Don't allow to overwrite the
> > +	 * frontend's extended info. If ever needed, we can support appending
> > +	 * it.
> > +	 */
> > +	if (ext_info && chan->ext_info != ext_info)
> > +		return -EOPNOTSUPP;
> > +	if (!chan->ext_info)
> 
> This is checking if the backend added anything? Perhaps a comment on that
> as we don't need a backend_ext_info local variable...

Yes, but just regarding ext_info as that is the only thing we're handling below
(doing some sanity checks). Note that (as you said above) I'm keeping things a bit
"open" in that the backend is open to extend whatever it wants. With time we may
learn better what do we want constrain or not.

> 
> > +		return 0;
> > +	/*
> > +	 * !\NOTE: this will break as soon as we have multiple backends on one
> > +	 * frontend and all of them extend channels. In that case, the core
> > +	 * backend code has no way to get the correct backend given the
> > +	 * iio device.
> > +	 *
> > +	 * One solution for this could be introducing a new backend
> > +	 * dedicated callback in struct iio_info so we can callback into the
> > +	 * frontend so it can give us the right backend given a chan_spec.
> > +	 */
> 
> Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> a front end with multiple backends is using multiple IIO buffers?
> 

Hmm, the assumption of having multiple buffers seems plausible to me but considering
the example we have in hands it would be cumbersome to get the backend. Considering
iio_backend_ext_info_get(), how could we get the backend if it was associated to one
of the IIO buffers? I think we would need more "intrusive" changes to make that work
or do you have something in mind=
 
> As you say a dance via the front end would work fine.

I'm happy you're also open for a proper solution already. I mention this in the
cover. My idea was something like (consider the iio_backend_ext_info_get()):

if (!indio_dev->info->get_iio_backend())
	return -EOPNOTSUPP;

back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);

It would be nice to have some "default/generic" implementation for cases where we
only have one backend per frontend so that the frontend would not need to define the
callback.
  
> 
> 
> > +	iio_device_set_drvdata(indio_dev, back);
> > +
> > +	/* Don't allow backends to get creative and force their own handlers */
> > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > +		if (ext_info->read != iio_backend_ext_info_get)
> > +			return -EINVAL;
> > +		if (ext_info->write != iio_backend_ext_info_set)
> > +			return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
> 
> > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > index a6d79381866e..09ff2f8f9fd8 100644
> > --- a/include/linux/iio/backend.h
> > +++ b/include/linux/iio/backend.h
> > @@ -4,6 +4,7 @@
> >  
> >  #include <linux/types.h>
> >  
> > +struct iio_chan_spec;
> >  struct fwnode_handle;
> >  struct iio_backend;
> >  struct device;
> > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> >  	IIO_BACKEND_DATA_TYPE_MAX
> >  };
> >  
> > +enum iio_backend_data_source {
> > +	IIO_BACKEND_INTERNAL_CW,
> 
> CW?  Either expand out what ever that is in definition of add a comment
> at least.

Continuous wave :)

> 
> > +	IIO_BACKEND_EXTERNAL,
> What does external mean in this case?

In this particular case comes from a DMA source (IP). I thought external to be more
generic but if you prefer, I can do something like IIO_BACKEND_DMA?


> > +	IIO_BACKEND_DATA_SOURCE_MAX
> > +};
> > +
> > +/**
> > + * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
> > + * @_name:	Attribute name
> > + * @_shared:	Whether the attribute is shared between all channels
> > + * @_what:	Data private to the driver
> > + */
> > +#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
> > +	.name = (_name),				\
> > +	.shared = (_shared),				\
> > +	.read =  iio_backend_ext_info_get,		\
> > +	.write = iio_backend_ext_info_set,		\
> > +	.private = (_what),				\
> > +}
> > +
> >  /**
> >   * struct iio_backend_data_fmt - Backend data format
> >   * @type:		Data type.
> > @@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
> >   * @chan_enable:	Enable one channel.
> >   * @chan_disable:	Disable one channel.
> >   * @data_format_set:	Configure the data format for a specific channel.
> > + * @data_source_set:	Configure the data source for a specific channel.
> > + * @set_sample_rate:	Configure the sampling rate for a specific channel.
> >   * @request_buffer:	Request an IIO buffer.
> >   * @free_buffer:	Free an IIO buffer.
> > + * @extend_chan_spec:	Extend an IIO channel.
> > + * @ext_info_set:	Extended info setter.
> > + * @ext_info_get:	Extended info getter.
> >   **/
> >  struct iio_backend_ops {
> >  	int (*enable)(struct iio_backend *back);
> > @@ -45,10 +71,21 @@ struct iio_backend_ops {
> >  	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
> >  	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
> >  			       const struct iio_backend_data_fmt *data);
> > +	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
> > +			       enum iio_backend_data_source data);
> > +	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
> > +			       u64 sample_rate);
> 
> Name the parameter that so we know the units.  _hz? 

yes. And u64 to not fall in the CCF problem for 32bits :)

- Nuno Sá
Jonathan Cameron March 28, 2024, 3:59 p.m. UTC | #3
On Thu, 28 Mar 2024 16:42:38 +0100
Nuno Sá <noname.nuno@gmail.com> wrote:

> On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> > On Thu, 28 Mar 2024 14:22:32 +0100
> > Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> >   
> > > From: Nuno Sa <nuno.sa@analog.com>
> > > 
> > > This adds the needed backend ops for supporting a backend inerfacing
> > > with an high speed dac. The new ops are:
> > > 
> > > * data_source_set();
> > > * set_sampling_freq();
> > > * extend_chan_spec();
> > > * ext_info_set();
> > > * ext_info_get().
> > > 
> > > Also to note the new helpers that are meant to be used by the backends
> > > +		return 0;
> > > +	/*
> > > +	 * !\NOTE: this will break as soon as we have multiple backends on one
> > > +	 * frontend and all of them extend channels. In that case, the core
> > > +	 * backend code has no way to get the correct backend given the
> > > +	 * iio device.
> > > +	 *
> > > +	 * One solution for this could be introducing a new backend
> > > +	 * dedicated callback in struct iio_info so we can callback into the
> > > +	 * frontend so it can give us the right backend given a chan_spec.
> > > +	 */  
> > 
> > Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> > a front end with multiple backends is using multiple IIO buffers?
> >   
> 
> Hmm, the assumption of having multiple buffers seems plausible to me but considering
> the example we have in hands it would be cumbersome to get the backend. Considering
> iio_backend_ext_info_get(), how could we get the backend if it was associated to one
> of the IIO buffers? I think we would need more "intrusive" changes to make that work
> or do you have something in mind=

Nope. Just trying to get my head around the associations. I hadn't thought about
how to make that visible in the code.  Probably a callabck anyway.

>  
> > As you say a dance via the front end would work fine.  
> 
> I'm happy you're also open for a proper solution already. I mention this in the
> cover. My idea was something like (consider the iio_backend_ext_info_get()):
> 
> if (!indio_dev->info->get_iio_backend())
> 	return -EOPNOTSUPP;
> 
> back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);
> 
> It would be nice to have some "default/generic" implementation for cases where we
> only have one backend per frontend so that the frontend would not need to define the
> callback.
Agreed - either a default that means if the callback isn't provided we get the
single backend or if that proves fiddly at least a standard callback we can
use in all such cases.

>   
> > 
> >   
> > > +	iio_device_set_drvdata(indio_dev, back);
> > > +
> > > +	/* Don't allow backends to get creative and force their own handlers */
> > > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > > +		if (ext_info->read != iio_backend_ext_info_get)
> > > +			return -EINVAL;
> > > +		if (ext_info->write != iio_backend_ext_info_set)
> > > +			return -EINVAL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);  
> >   
> > > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > > index a6d79381866e..09ff2f8f9fd8 100644
> > > --- a/include/linux/iio/backend.h
> > > +++ b/include/linux/iio/backend.h
> > > @@ -4,6 +4,7 @@
> > >  
> > >  #include <linux/types.h>
> > >  
> > > +struct iio_chan_spec;
> > >  struct fwnode_handle;
> > >  struct iio_backend;
> > >  struct device;
> > > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> > >  	IIO_BACKEND_DATA_TYPE_MAX
> > >  };
> > >  
> > > +enum iio_backend_data_source {
> > > +	IIO_BACKEND_INTERNAL_CW,  
> > 
> > CW?  Either expand out what ever that is in definition of add a comment
> > at least.  
> 
> Continuous wave :)

Spell that out.

> 
> >   
> > > +	IIO_BACKEND_EXTERNAL,  
> > What does external mean in this case?  
> 
> In this particular case comes from a DMA source (IP). I thought external to be more
> generic but if you prefer, I can do something like IIO_BACKEND_DMA?

So from another IP block?   For that to be reasonably 'generic' we'd need a way
to known where it was coming from.

Now I remember advantage of reviewing on weekends - fewer replies during the reviews :)

Jonathan
Nuno Sá March 28, 2024, 4:54 p.m. UTC | #4
On Thu, 2024-03-28 at 15:59 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 16:42:38 +0100
> Nuno Sá <noname.nuno@gmail.com> wrote:
> 
> > On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> > > On Thu, 28 Mar 2024 14:22:32 +0100
> > > Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> > >   
> > > > From: Nuno Sa <nuno.sa@analog.com>
> > > > 
> > > > This adds the needed backend ops for supporting a backend inerfacing
> > > > with an high speed dac. The new ops are:
> > > > 
> > > > * data_source_set();
> > > > * set_sampling_freq();
> > > > * extend_chan_spec();
> > > > * ext_info_set();
> > > > * ext_info_get().
> > > > 
> > > > Also to note the new helpers that are meant to be used by the backends
> > > > +		return 0;
> > > > +	/*
> > > > +	 * !\NOTE: this will break as soon as we have multiple backends on
> > > > one
> > > > +	 * frontend and all of them extend channels. In that case, the core
> > > > +	 * backend code has no way to get the correct backend given the
> > > > +	 * iio device.
> > > > +	 *
> > > > +	 * One solution for this could be introducing a new backend
> > > > +	 * dedicated callback in struct iio_info so we can callback into the
> > > > +	 * frontend so it can give us the right backend given a chan_spec.
> > > > +	 */  
> > > 
> > > Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> > > a front end with multiple backends is using multiple IIO buffers?
> > >   
> > 
> > Hmm, the assumption of having multiple buffers seems plausible to me but
> > considering
> > the example we have in hands it would be cumbersome to get the backend.
> > Considering
> > iio_backend_ext_info_get(), how could we get the backend if it was associated to
> > one
> > of the IIO buffers? I think we would need more "intrusive" changes to make that
> > work
> > or do you have something in mind=
> 
> Nope. Just trying to get my head around the associations. I hadn't thought about
> how to make that visible in the code.  Probably a callabck anyway.
> 
> >  
> > > As you say a dance via the front end would work fine.  
> > 
> > I'm happy you're also open for a proper solution already. I mention this in the
> > cover. My idea was something like (consider the iio_backend_ext_info_get()):
> > 
> > if (!indio_dev->info->get_iio_backend())
> > 	return -EOPNOTSUPP;
> > 
> > back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);
> > 
> > It would be nice to have some "default/generic" implementation for cases where we
> > only have one backend per frontend so that the frontend would not need to define
> > the
> > callback.
> Agreed - either a default that means if the callback isn't provided we get the
> single backend or if that proves fiddly at least a standard callback we can
> use in all such cases.
> 

I'll have to think a bit about it. We may need some association/link between iio_dev
and iio_backend in order to "if the callback isn't provided we get the single
backend". The easiest that comes to my mind without much thinking would be to use
iio_device_set_drvdata()/iio_device_get_drvdata() in case the frontend does not
provide a callback. This would already force callers to assign the indio_dev->info
pointer before this call. Not that nice but acceptable if properly documented I
guess.

Anyways, I'll see if I can think of something better...

> >   
> > > 
> > >   
> > > > +	iio_device_set_drvdata(indio_dev, back);
> > > > +
> > > > +	/* Don't allow backends to get creative and force their own handlers
> > > > */
> > > > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > > > +		if (ext_info->read != iio_backend_ext_info_get)
> > > > +			return -EINVAL;
> > > > +		if (ext_info->write != iio_backend_ext_info_set)
> > > > +			return -EINVAL;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);  
> > >   
> > > > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > > > index a6d79381866e..09ff2f8f9fd8 100644
> > > > --- a/include/linux/iio/backend.h
> > > > +++ b/include/linux/iio/backend.h
> > > > @@ -4,6 +4,7 @@
> > > >  
> > > >  #include <linux/types.h>
> > > >  
> > > > +struct iio_chan_spec;
> > > >  struct fwnode_handle;
> > > >  struct iio_backend;
> > > >  struct device;
> > > > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> > > >  	IIO_BACKEND_DATA_TYPE_MAX
> > > >  };
> > > >  
> > > > +enum iio_backend_data_source {
> > > > +	IIO_BACKEND_INTERNAL_CW,  
> > > 
> > > CW?  Either expand out what ever that is in definition of add a comment
> > > at least.  
> > 
> > Continuous wave :)
> 
> Spell that out.
> 
> > 
> > >   
> > > > +	IIO_BACKEND_EXTERNAL,  
> > > What does external mean in this case?  
> > 
> > In this particular case comes from a DMA source (IP). I thought external to be
> > more
> > generic but if you prefer, I can do something like IIO_BACKEND_DMA?
> 
> So from another IP block?   For that to be reasonably 'generic' we'd need a way
> to known where it was coming from.
> 

Yeps, in this case comes from the IIO DMA buffer which in HW means a DMA IP core...

> Now I remember advantage of reviewing on weekends - fewer replies during the
> reviews :)
> 

:)

- Nuno Sá
diff mbox series

Patch

diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47f..0a26dd8c6343 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,6 +43,7 @@ 
 #include <linux/types.h>
 
 #include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
 
 struct iio_backend {
 	struct list_head entry;
@@ -186,6 +187,44 @@  int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 }
 EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
 
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back:	Backend device
+ * @chan:	Channel number
+ * @data:	Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data)
+{
+	if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+		return -EINVAL;
+
+	return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back:		Backend device
+ * @chan:		Channel number
+ * @sample_rate:	Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate)
+{
+	return iio_backend_op_call(back, set_sample_rate, chan, sample_rate);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
 static void iio_backend_free_buffer(void *arg)
 {
 	struct iio_backend_buffer_pair *pair = arg;
@@ -231,6 +270,111 @@  int devm_iio_backend_request_buffer(struct device *dev,
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
 
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer holding the sysfs attribute
+ * @len:	Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev:	IIO device
+ * @back:	Backend device
+ * @chan:	IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan)
+{
+	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
+	int ret;
+
+	ret = iio_backend_op_call(back, extend_chan_spec, chan);
+	if (ret)
+		return ret;
+	/*
+	 * Let's keep things simple for now. Don't allow to overwrite the
+	 * frontend's extended info. If ever needed, we can support appending
+	 * it.
+	 */
+	if (ext_info && chan->ext_info != ext_info)
+		return -EOPNOTSUPP;
+	if (!chan->ext_info)
+		return 0;
+	/*
+	 * !\NOTE: this will break as soon as we have multiple backends on one
+	 * frontend and all of them extend channels. In that case, the core
+	 * backend code has no way to get the correct backend given the
+	 * iio device.
+	 *
+	 * One solution for this could be introducing a new backend
+	 * dedicated callback in struct iio_info so we can callback into the
+	 * frontend so it can give us the right backend given a chan_spec.
+	 */
+	iio_device_set_drvdata(indio_dev, back);
+
+	/* Don't allow backends to get creative and force their own handlers */
+	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
+		if (ext_info->read != iio_backend_ext_info_get)
+			return -EINVAL;
+		if (ext_info->write != iio_backend_ext_info_set)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
 static void iio_backend_release(void *arg)
 {
 	struct iio_backend *back = arg;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866e..09ff2f8f9fd8 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@ 
 
 #include <linux/types.h>
 
+struct iio_chan_spec;
 struct fwnode_handle;
 struct iio_backend;
 struct device;
@@ -15,6 +16,26 @@  enum iio_backend_data_type {
 	IIO_BACKEND_DATA_TYPE_MAX
 };
 
+enum iio_backend_data_source {
+	IIO_BACKEND_INTERNAL_CW,
+	IIO_BACKEND_EXTERNAL,
+	IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name:	Attribute name
+ * @_shared:	Whether the attribute is shared between all channels
+ * @_what:	Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
+	.name = (_name),				\
+	.shared = (_shared),				\
+	.read =  iio_backend_ext_info_get,		\
+	.write = iio_backend_ext_info_set,		\
+	.private = (_what),				\
+}
+
 /**
  * struct iio_backend_data_fmt - Backend data format
  * @type:		Data type.
@@ -35,8 +56,13 @@  struct iio_backend_data_fmt {
  * @chan_enable:	Enable one channel.
  * @chan_disable:	Disable one channel.
  * @data_format_set:	Configure the data format for a specific channel.
+ * @data_source_set:	Configure the data source for a specific channel.
+ * @set_sample_rate:	Configure the sampling rate for a specific channel.
  * @request_buffer:	Request an IIO buffer.
  * @free_buffer:	Free an IIO buffer.
+ * @extend_chan_spec:	Extend an IIO channel.
+ * @ext_info_set:	Extended info setter.
+ * @ext_info_get:	Extended info getter.
  **/
 struct iio_backend_ops {
 	int (*enable)(struct iio_backend *back);
@@ -45,10 +71,21 @@  struct iio_backend_ops {
 	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
 	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
 			       const struct iio_backend_data_fmt *data);
+	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+			       enum iio_backend_data_source data);
+	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+			       u64 sample_rate);
 	struct iio_buffer *(*request_buffer)(struct iio_backend *back,
 					     struct iio_dev *indio_dev);
 	void (*free_buffer)(struct iio_backend *back,
 			    struct iio_buffer *buffer);
+	int (*extend_chan_spec)(struct iio_backend *back,
+				struct iio_chan_spec *chan);
+	int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan,
+			    const char *buf, size_t len);
+	int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan, char *buf);
 };
 
 int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +93,22 @@  int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
 int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
 int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 				const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate);
 int devm_iio_backend_request_buffer(struct device *dev,
 				    struct iio_backend *back,
 				    struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf);
 
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan);
 void *iio_backend_get_priv(const struct iio_backend *conv);
 struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
 struct iio_backend *