Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH V7 0/7] dmaengine: qcom_hidma: add support for bugfixed HW
From: Vinod Koul @ 2017-12-10 16:39 UTC (permalink / raw)
  To: Sinan Kaya
  Cc: Rafael J. Wysocki, dmaengine, Timur Tabi,
	devicetree@vger.kernel.org, ACPI Devel Maling List, Sakari Ailus,
	linux-arm-msm, linux-arm-kernel@lists.infradead.org
In-Reply-To: <bad52be7-4ff8-4f06-4bd0-83e97701f495@codeaurora.org>

On Fri, Dec 08, 2017 at 09:44:46AM -0500, Sinan Kaya wrote:
> On 12/8/2017 8:48 AM, Rafael J. Wysocki wrote:
> > The series is fine by me, by how do you want to route it?
> 
> Probably through the DMA engine route as it has pieces that didn't get any
> review from Vinod yet.
> 
> Vinod,
> 
> Do you have any preference?

Yeah sure sounds okay to me as DMA parts are dependent on these.
Rafael if you need an immutable tag to pull this, let me know.

Thanks
-- 
~Vinod

^ permalink raw reply

* Re: [PATCH 3/8] mfd: axp20x: probe axp20x_adc driver for AXP813
From: Jonathan Cameron @ 2017-12-10 16:40 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Quentin Schulz, Maxime Ripard, Sebastian Reichel, Rob Herring,
	Mark Rutland, Russell King, Lee Jones, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, open list:THERMAL,
	devicetree, linux-kernel, linux-arm-kernel, linux-iio,
	Icenowy Zheng, linux-sunxi, Thomas Petazzoni
In-Reply-To: <CAGb2v66esOimRhDHL6QgYU3iWg2a6JiQm__0ivv3SdnQv=SGQg@mail.gmail.com>

On Thu, 7 Dec 2017 17:14:30 +0800
Chen-Yu Tsai <wens@csie.org> wrote:

> On Thu, Dec 7, 2017 at 5:03 PM, Quentin Schulz
> <quentin.schulz@free-electrons.com> wrote:
> > Hi Chen-Yu,
> >
> > On 07/12/2017 09:54, Chen-Yu Tsai wrote:  
> >> On Thu, Dec 7, 2017 at 4:51 PM, Quentin Schulz
> >> <quentin.schulz@free-electrons.com> wrote:  
> >>> Hi Maxime,
> >>>
> >>> On 05/12/2017 09:08, Maxime Ripard wrote:  
> >>>> On Mon, Dec 04, 2017 at 03:12:49PM +0100, Quentin Schulz wrote:  
> >>>>> This makes the axp20x_adc driver probe with platform device id
> >>>>> "axp813-adc".
> >>>>>
> >>>>> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> >>>>> ---
> >>>>>  drivers/mfd/axp20x.c | 4 +++-
> >>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>>
> >>>>> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> >>>>> index 2468b43..42e54d1 100644
> >>>>> --- a/drivers/mfd/axp20x.c
> >>>>> +++ b/drivers/mfd/axp20x.c
> >>>>> @@ -878,7 +878,9 @@ static struct mfd_cell axp813_cells[] = {
> >>>>>              .resources              = axp803_pek_resources,
> >>>>>      }, {
> >>>>>              .name                   = "axp20x-regulator",
> >>>>> -    }
> >>>>> +    }, {
> >>>>> +            .name                   = "axp813-adc",
> >>>>> +    },  
> >>>>
> >>>> Any particular reason you're not adding it to the DT?
> >>>>  
> >>>
> >>> No, no particular reason. It's just the way it is currently for AXP209
> >>> and AXP22x so did the same for AXP813.
> >>>
> >>> I'll add DT "support" in next version for all AXPs supported by this
> >>> driver. Or is it worthy of a small separate patch series?  
> >>
> >> IIRC there's no DT support because there's no need to reference
> >> it in the device tree.
> >>  
> >
> > No current need but that does not mean there won't be a need later for
> > drivers to map IIO channels from the ADC driver (i.e. some components
> > wired to GPIOs of the PMIC for example).  
> 
> Hmm... Why would you map the IIO channels from the ADC? I thought those
> were all accessible from userspace?

There is a reasonably fully featured consumer interface for IIO channels
as well. Here it's being used internal to the hardware, but yes if
you want to do the mappings to other devices, it will need to 'exist'
in the device tree.

I'm guessing that you have something in mind that needs this.  If not I'd
leave it until there is a real user.

> 
> However, proper muxing of the GPIO pin to the ADC function makes sense.
> 
Agreed.

Jonathan

> ChenYu
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 4/8] dt-bindings: power: supply: axp20x: add AXP813 battery DT binding
From: Jonathan Cameron @ 2017-12-10 16:44 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <bf3682e87c75532884881d7b08840c61678cbce1.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

On Mon,  4 Dec 2017 15:12:50 +0100
Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:

> The AXP813 can have a battery as power supply, so let's add it to the
> list of compatibles.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/power/supply/axp20x_battery.txt | 8 +++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> index c248866..4614c8e 100644
> --- a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> +++ b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> @@ -4,12 +4,12 @@ Required Properties:
>   - compatible, one of:
>  			"x-powers,axp209-battery-power-supply"
>  			"x-powers,axp221-battery-power-supply"
> +			"x-powers,axp813-battery-power-supply"
>  
> -This node is a subnode of the axp20x/axp22x PMIC.
> +This node is a subnode of the axp20x/axp22x/axp81x PMIC.
>  
> -The AXP20X and AXP22X can read the battery voltage, charge and discharge
> -currents of the battery by reading ADC channels from the AXP20X/AXP22X
> -ADC.
> +The AXP20X, AXP22X and AXP81X can read the battery voltage, charge and
> +discharge currents of the battery by reading ADC channels from the ADC.
Might just be me, but this looks like a recipe for unneeded churn in future.

The supported devices can read...

Maybe also

This node is a subnode of the PMIC with the same part number. ?

I don't really care though!

>  
>  Example:
>  

^ permalink raw reply

* Re: [PATCH 5/8] power: supply: axp20x_battery: add support for AXP813
From: Jonathan Cameron @ 2017-12-10 16:49 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, icenowy-h8G6r0blFSE,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <545d3aa6339c9e33060d651c42d652d0b848c06b.1512396054.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

On Mon,  4 Dec 2017 15:12:51 +0100
Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:

> The X-Powers AXP813 PMIC has got some slight differences from
> AXP20X/AXP22X PMICs:
>  - the maximum voltage supplied by the PMIC is 4.35 instead of 4.36/4.24
>  for AXP20X/AXP22X,
>  - the constant charge current formula is different,
> 
> It also has a bit to tell whether the battery percentage returned by the
> PMIC is valid.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

I'd use switch statements when matching the IDs as that'll be more elegant
as you perhaps add further devices going forward...

Other than that, looks good to me.

Jonathan

> ---
>  drivers/power/supply/axp20x_battery.c | 44 +++++++++++++++++++++++++++-
>  1 file changed, 43 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> index 7494f0f..cb30302 100644
> --- a/drivers/power/supply/axp20x_battery.c
> +++ b/drivers/power/supply/axp20x_battery.c
> @@ -46,6 +46,8 @@
>  #define AXP20X_CHRG_CTRL1_TGT_4_2V	(2 << 5)
>  #define AXP20X_CHRG_CTRL1_TGT_4_36V	(3 << 5)
>  
> +#define AXP813_CHRG_CTRL1_TGT_4_35V	(3 << 5)
> +
>  #define AXP22X_CHRG_CTRL1_TGT_4_22V	(1 << 5)
>  #define AXP22X_CHRG_CTRL1_TGT_4_24V	(3 << 5)
>  
> @@ -123,10 +125,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
>  	return 0;
>  }
>  
> +static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> +					  int *val)
> +{
> +	int ret, reg;
> +
> +	ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, &reg);
> +	if (ret)
> +		return ret;
> +
> +	switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {

You could do a lookup based from a table instead which might
be ever so slightly more elegant..

> +	case AXP20X_CHRG_CTRL1_TGT_4_1V:
> +		*val = 4100000;
> +		break;
> +	case AXP20X_CHRG_CTRL1_TGT_4_15V:
> +		*val = 4150000;
> +		break;
> +	case AXP20X_CHRG_CTRL1_TGT_4_2V:
> +		*val = 4200000;
> +		break;
> +	case AXP813_CHRG_CTRL1_TGT_4_35V:
> +		*val = 4350000;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val)
>  {
>  	if (axp->axp_id == AXP209_ID)
>  		*val = *val * 100000 + 300000;
> +	else if (axp->axp_id == AXP813_ID)
> +		*val = *val * 200000 + 200000;
>  	else
>  		*val = *val * 150000 + 300000;

Switch?

>  }
> @@ -135,6 +168,8 @@ static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
>  {
>  	if (axp->axp_id == AXP209_ID)
>  		*val = (*val - 300000) / 100000;
> +	else if (axp->axp_id == AXP813_ID)
> +		*val = (*val - 200000) / 200000;
>  	else
>  		*val = (*val - 300000) / 150000;
>  }
> @@ -269,7 +304,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
>  		if (ret)
>  			return ret;
>  
> -		if (axp20x_batt->axp_id == AXP221_ID &&
> +		if ((axp20x_batt->axp_id == AXP221_ID ||
> +		     axp20x_batt->axp_id == AXP813_ID) &&
>  		    !(reg & AXP22X_FG_VALID))
>  			return -EINVAL;
>  
> @@ -284,6 +320,9 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
>  		if (axp20x_batt->axp_id == AXP209_ID)
>  			return axp20x_battery_get_max_voltage(axp20x_batt,
>  							      &val->intval);
> +		else if (axp20x_batt->axp_id == AXP813_ID)
> +			return axp813_battery_get_max_voltage(axp20x_batt,
> +							      &val->intval);
>  		return axp22x_battery_get_max_voltage(axp20x_batt,
>  						      &val->intval);

Worth converting to a switch statement to make it more elegant for future
devices?

>  
> @@ -467,6 +506,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
>  	}, {
>  		.compatible = "x-powers,axp221-battery-power-supply",
>  		.data = (void *)AXP221_ID,
> +	}, {
> +		.compatible = "x-powers,axp813-battery-power-supply",
> +		.data = (void *)AXP813_ID,
>  	}, { /* sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);

^ permalink raw reply

* Re: [PATCH v7 04/13] IIO: inkern: API for manipulating channel attributes
From: Jonathan Cameron @ 2017-12-10 17:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Maxime Coquelin,
	Alexandre Torgue
In-Reply-To: <1512744566-13233-5-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

On Fri, 8 Dec 2017 15:49:17 +0100
Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org> wrote:

> Extend the inkern API with functions for reading and writing
> attribute of iio channels.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
One nitpick inline. If you aren't rerolling can get sorted
whilst applying the series.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> ---
> V6 to V7 update:
>  - Move iio_chan_info_enum from iio.h to types.h.
> 
>  drivers/iio/inkern.c         | 18 +++++++++++++-----
>  include/linux/iio/consumer.h | 26 ++++++++++++++++++++++++++
>  include/linux/iio/iio.h      | 28 ----------------------------
>  include/linux/iio/types.h    | 28 ++++++++++++++++++++++++++++
>  4 files changed, 67 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
> index 069defc..f2e7824 100644
> --- a/drivers/iio/inkern.c
> +++ b/drivers/iio/inkern.c
> @@ -664,9 +664,8 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
>  }
>  EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
>  
> -static int iio_read_channel_attribute(struct iio_channel *chan,
> -				      int *val, int *val2,
> -				      enum iio_chan_info_enum attribute)
> +int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
> +			       enum iio_chan_info_enum attribute)
>  {
>  	int ret;
>  
> @@ -682,6 +681,8 @@ static int iio_read_channel_attribute(struct iio_channel *chan,
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
> +
Nitpick if you are rerolling the series.  Don't need the extra line here.
>  
>  int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
>  {
> @@ -850,7 +851,8 @@ static int iio_channel_write(struct iio_channel *chan, int val, int val2,
>  						chan->channel, val, val2, info);
>  }
>  
> -int iio_write_channel_raw(struct iio_channel *chan, int val)
> +int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
> +				enum iio_chan_info_enum attribute)
>  {
>  	int ret;
>  
> @@ -860,12 +862,18 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
>  		goto err_unlock;
>  	}
>  
> -	ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW);
> +	ret = iio_channel_write(chan, val, val2, attribute);
>  err_unlock:
>  	mutex_unlock(&chan->indio_dev->info_exist_lock);
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
> +
> +int iio_write_channel_raw(struct iio_channel *chan, int val)
> +{
> +	return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW);
> +}
>  EXPORT_SYMBOL_GPL(iio_write_channel_raw);
>  
>  unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index 5e347a9..2017f35 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -216,6 +216,32 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val);
>  int iio_read_channel_processed(struct iio_channel *chan, int *val);
>  
>  /**
> + * iio_write_channel_attribute() - Write values to the device attribute.
> + * @chan:	The channel being queried.
> + * @val:	Value being written.
> + * @val2:	Value being written.val2 use depends on attribute type.
> + * @attribute:	info attribute to be read.
> + *
> + * Returns an error code or 0.
> + */
> +int iio_write_channel_attribute(struct iio_channel *chan, int val,
> +				int val2, enum iio_chan_info_enum attribute);
> +
> +/**
> + * iio_read_channel_attribute() - Read values from the device attribute.
> + * @chan:	The channel being queried.
> + * @val:	Value being written.
> + * @val2:	Value being written.Val2 use depends on attribute type.
> + * @attribute:	info attribute to be written.
> + *
> + * Returns an error code if failed. Else returns a description of what is in val
> + * and val2, such as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val
> + * + val2/1e6
> + */
> +int iio_read_channel_attribute(struct iio_channel *chan, int *val,
> +			       int *val2, enum iio_chan_info_enum attribute);
> +
> +/**
>   * iio_write_channel_raw() - write to a given channel
>   * @chan:		The channel being queried.
>   * @val:		Value being written.
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index c380daa..007caf7 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -20,34 +20,6 @@
>   * Currently assumes nano seconds.
>   */
>  
> -enum iio_chan_info_enum {
> -	IIO_CHAN_INFO_RAW = 0,
> -	IIO_CHAN_INFO_PROCESSED,
> -	IIO_CHAN_INFO_SCALE,
> -	IIO_CHAN_INFO_OFFSET,
> -	IIO_CHAN_INFO_CALIBSCALE,
> -	IIO_CHAN_INFO_CALIBBIAS,
> -	IIO_CHAN_INFO_PEAK,
> -	IIO_CHAN_INFO_PEAK_SCALE,
> -	IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,
> -	IIO_CHAN_INFO_AVERAGE_RAW,
> -	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY,
> -	IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY,
> -	IIO_CHAN_INFO_SAMP_FREQ,
> -	IIO_CHAN_INFO_FREQUENCY,
> -	IIO_CHAN_INFO_PHASE,
> -	IIO_CHAN_INFO_HARDWAREGAIN,
> -	IIO_CHAN_INFO_HYSTERESIS,
> -	IIO_CHAN_INFO_INT_TIME,
> -	IIO_CHAN_INFO_ENABLE,
> -	IIO_CHAN_INFO_CALIBHEIGHT,
> -	IIO_CHAN_INFO_CALIBWEIGHT,
> -	IIO_CHAN_INFO_DEBOUNCE_COUNT,
> -	IIO_CHAN_INFO_DEBOUNCE_TIME,
> -	IIO_CHAN_INFO_CALIBEMISSIVITY,
> -	IIO_CHAN_INFO_OVERSAMPLING_RATIO,
> -};
> -
>  enum iio_shared_by {
>  	IIO_SEPARATE,
>  	IIO_SHARED_BY_TYPE,
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 2aa7b63..6eb3d683 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -34,4 +34,32 @@ enum iio_available_type {
>  	IIO_AVAIL_RANGE,
>  };
>  
> +enum iio_chan_info_enum {
> +	IIO_CHAN_INFO_RAW = 0,
> +	IIO_CHAN_INFO_PROCESSED,
> +	IIO_CHAN_INFO_SCALE,
> +	IIO_CHAN_INFO_OFFSET,
> +	IIO_CHAN_INFO_CALIBSCALE,
> +	IIO_CHAN_INFO_CALIBBIAS,
> +	IIO_CHAN_INFO_PEAK,
> +	IIO_CHAN_INFO_PEAK_SCALE,
> +	IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,
> +	IIO_CHAN_INFO_AVERAGE_RAW,
> +	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY,
> +	IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY,
> +	IIO_CHAN_INFO_SAMP_FREQ,
> +	IIO_CHAN_INFO_FREQUENCY,
> +	IIO_CHAN_INFO_PHASE,
> +	IIO_CHAN_INFO_HARDWAREGAIN,
> +	IIO_CHAN_INFO_HYSTERESIS,
> +	IIO_CHAN_INFO_INT_TIME,
> +	IIO_CHAN_INFO_ENABLE,
> +	IIO_CHAN_INFO_CALIBHEIGHT,
> +	IIO_CHAN_INFO_CALIBWEIGHT,
> +	IIO_CHAN_INFO_DEBOUNCE_COUNT,
> +	IIO_CHAN_INFO_DEBOUNCE_TIME,
> +	IIO_CHAN_INFO_CALIBEMISSIVITY,
> +	IIO_CHAN_INFO_OVERSAMPLING_RATIO,
> +};
> +
>  #endif /* _IIO_TYPES_H_ */

^ permalink raw reply

* Re: [PATCH v7 10/13] IIO: ADC: add stm32 DFSDM support for PDM microphone
From: Jonathan Cameron @ 2017-12-10 18:14 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Maxime Coquelin,
	Alexandre Torgue
In-Reply-To: <1512744566-13233-11-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

On Fri, 8 Dec 2017 15:49:23 +0100
Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org> wrote:

> This code offers a way to handle PDM audio microphones in
> ASOC framework. Audio driver should use consumer API.
> A specific management is implemented for DMA, with a
> callback, to allows to handle audio buffers efficiently.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

Hi Arnaud,

I'm afraid I missed a few things on the earlier versions.

Around the use of the iio_triggered_buffer_setup
(which does some stuff we don't need here)

Also ordering isn't quite consistent between probe and
remove.

Jonathan 

> ---
> V6 to V7 updates:
> - SPDX-Licensing.
> - Minor typo fixes.
> 
>  .../ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32      |  16 +
>  drivers/iio/adc/stm32-dfsdm-adc.c                  | 508 ++++++++++++++++++++-
>  include/linux/iio/adc/stm32-dfsdm-adc.h            |  18 +
>  3 files changed, 534 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-adc.h
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32
> new file mode 100644
> index 0000000..da98223
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32
> @@ -0,0 +1,16 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltage_spi_clk_freq
> +KernelVersion:	4.14
> +Contact:	arnaud.pouliquen-qxv4g6HH51o@public.gmane.org
> +Description:
> +		For audio purpose only.
> +		Used by audio driver to set/get the spi input frequency.
> +		This is mandatory if DFSDM is slave on SPI bus, to
> +		provide information on the SPI clock frequency during runtime
> +		Notice that the SPI frequency should be a multiple of sample
> +		frequency to ensure the precision.
> +		if DFSDM input is SPI master
> +			Reading  SPI clkout frequency,
> +			error on writing
> +		If DFSDM input is SPI Slave:
> +			Reading returns value previously set.
> +			Writing value before starting conversions.
> \ No newline at end of file
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 68b5920..2d6aed5 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -6,19 +6,25 @@
>   * Author: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
>   */
>  
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/interrupt.h>
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/hw-consumer.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
>  #include <linux/module.h>
> -#include <linux/of.h>
> +#include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/slab.h>
>  
>  #include "stm32-dfsdm.h"
>  
> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
> +
>  /* Conversion timeout */
>  #define DFSDM_TIMEOUT_US 100000
>  #define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> @@ -58,6 +64,18 @@ struct stm32_dfsdm_adc {
>  	struct completion completion;
>  	u32 *buffer;
>  
> +	/* Audio specific */
> +	unsigned int spi_freq;  /* SPI bus clock frequency */
> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
> +	int (*cb)(const void *data, size_t size, void *cb_priv);
> +	void *cb_priv;
> +
> +	/* DMA */
> +	u8 *rx_buf;
> +	unsigned int bufi; /* Buffer current position */
> +	unsigned int buf_sz; /* Buffer size */
> +	struct dma_chan	*dma_chan;
> +	dma_addr_t dma_buf;
>  };
>  
>  struct stm32_dfsdm_str2field {
> @@ -351,10 +369,63 @@ int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>  	return 0;
>  }
>  
> +static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
> +}
> +
> +static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[adc->ch_id];
> +	unsigned int sample_freq = adc->sample_freq;
> +	unsigned int spi_freq;
> +	int ret;
> +
> +	dev_err(&indio_dev->dev, "enter %s\n", __func__);
> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		return -EPERM;
> +
> +	ret = kstrtoint(buf, 0, &spi_freq);
> +	if (ret)
> +		return ret;
> +
> +	if (!spi_freq)
> +		return -EINVAL;
> +
> +	if (sample_freq) {
> +		if (spi_freq % sample_freq)
> +			dev_warn(&indio_dev->dev,
> +				 "Sampling rate not accurate (%d)\n",
> +				 spi_freq / (spi_freq / sample_freq));
> +
> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"No filter parameters that match!\n");
> +			return ret;
> +		}
> +	}
> +	adc->spi_freq = spi_freq;
> +
> +	return len;
> +}
> +
>  static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, bool dma)
>  {
>  	struct regmap *regmap = adc->dfsdm->regmap;
>  	int ret;
> +	unsigned int dma_en = 0, cont_en = 0;
>  
>  	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>  	if (ret < 0)
> @@ -365,6 +436,24 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, bool dma)
>  	if (ret < 0)
>  		goto stop_channels;
>  
> +	if (dma) {
> +		/* Enable DMA transfer*/
> +		dma_en =  DFSDM_CR1_RDMAEN(1);
> +		/* Enable conversion triggered by SPI clock*/
> +		cont_en = DFSDM_CR1_RCONT(1);
> +	}
> +	/* Enable DMA transfer*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> +				 DFSDM_CR1_RDMAEN_MASK, dma_en);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	/* Enable conversion triggered by SPI clock*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> +				 DFSDM_CR1_RCONT_MASK, cont_en);
> +	if (ret < 0)
> +		goto stop_channels;
> +
>  	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>  	if (ret < 0)
>  		goto stop_channels;
> @@ -398,6 +487,231 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>  	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>  }
>  
> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> +				     unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
> +
> +	/*
> +	 * DMA cyclic transfers are used, buffer is split into two periods.
> +	 * There should be :
> +	 * - always one buffer (period) DMA is working on
> +	 * - one buffer (period) driver pushed to ASoC side.
> +	 */
> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
> +	adc->buf_sz = watermark * 2;
> +
> +	return 0;
> +}
> +
> +static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	status = dmaengine_tx_status(adc->dma_chan,
> +				     adc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		unsigned int i = adc->buf_sz - state.residue;
> +		unsigned int size;
> +
> +		/* Return available bytes */
> +		if (i >= adc->bufi)
> +			size = i - adc->bufi;
> +		else
> +			size = adc->buf_sz + i - adc->bufi;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> +{
> +	struct iio_dev *indio_dev = data;
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int available = stm32_dfsdm_adc_dma_residue(adc);
> +	size_t old_pos;
> +
> +	/*
> +	 * FIXME: In Kernel interface does not support cyclic DMA buffer,and
> +	 * offers only an interface to push data samples per samples.
> +	 * For this reason IIO buffer interface is not used and interface is
> +	 * bypassed using a private callback registered by ASoC.
> +	 * This should be a temporary solution waiting a cyclic DMA engine
> +	 * support in IIO.
> +	 */
> +
> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
> +		adc->bufi, available);
> +	old_pos = adc->bufi;
> +
> +	while (available >= indio_dev->scan_bytes) {
> +		u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
> +
> +		/* Mask 8 LSB that contains the channel ID */
> +		*buffer = (*buffer & 0xFFFFFF00) << 8;
> +		available -= indio_dev->scan_bytes;
> +		adc->bufi += indio_dev->scan_bytes;
> +		if (adc->bufi >= adc->buf_sz) {
> +			if (adc->cb)
> +				adc->cb(&adc->rx_buf[old_pos],
> +					 adc->buf_sz - old_pos, adc->cb_priv);
> +			adc->bufi = 0;
> +			old_pos = 0;
> +		}
> +	}
> +	if (adc->cb)
> +		adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
> +			adc->cb_priv);
> +}
> +
> +static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	int ret;
> +
> +	if (!adc->dma_chan)
> +		return -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
> +		adc->buf_sz, adc->buf_sz / 2);
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
> +					 adc->dma_buf,
> +					 adc->buf_sz, adc->buf_sz / 2,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc)
> +		return -EBUSY;
> +
> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
> +	desc->callback_param = indio_dev;
> +
> +	cookie = dmaengine_submit(desc);
> +	ret = dma_submit_error(cookie);
> +	if (ret) {
> +		dmaengine_terminate_all(adc->dma_chan);
> +		return ret;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(adc->dma_chan);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	/* Reset adc buffer index */
> +	adc->bufi = 0;
> +
> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_conv(adc, true);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
> +		goto stop_dfsdm;
> +	}
> +
> +	if (adc->dma_chan) {
> +		ret = stm32_dfsdm_adc_dma_start(indio_dev);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Can't start DMA\n");
> +			goto err_stop_conv;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_stop_conv:
> +	stm32_dfsdm_stop_conv(adc);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	if (adc->dma_chan)
> +		dmaengine_terminate_all(adc->dma_chan);
> +
> +	stm32_dfsdm_stop_conv(adc);
> +
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +/**
> + * stm32_dfsdm_get_buff_cb() - register a callback that will be called when
> + *                             DMA transfer period is achieved.
> + *
> + * @iio_dev: Handle to IIO device.
> + * @cb: Pointer to callback function:
> + *      - data: pointer to data buffer
> + *      - size: size in byte of the data buffer
> + *      - private: pointer to consumer private structure.
> + * @private: Pointer to consumer private structure.
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private)
> +{
> +	struct stm32_dfsdm_adc *adc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	adc = iio_priv(iio_dev);
> +
> +	adc->cb = cb;
> +	adc->cb_priv = private;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
> +
> +/**
> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
> + *
> + * @iio_dev: Handle to IIO device.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	adc = iio_priv(iio_dev);
> +
> +	adc->cb = NULL;
> +	adc->cb_priv = NULL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
> +
>  static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>  				   const struct iio_chan_spec *chan, int *res)
>  {
> @@ -453,15 +767,41 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
>  {
>  	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>  	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[adc->ch_id];
> +	unsigned int spi_freq = adc->spi_freq;
>  	int ret = -EINVAL;
>  
> -	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>  		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>  		if (!ret)
>  			adc->oversamp = val;
> +
> +		return ret;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (!val)
> +			return -EINVAL;
> +		if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +			spi_freq = adc->dfsdm->spi_master_freq;
> +
> +		if (spi_freq % val)
> +			dev_warn(&indio_dev->dev,
> +				 "Sampling rate not accurate (%d)\n",
> +				 spi_freq / (spi_freq / val));
> +
> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val));
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"Not able to find parameter that match!\n");
> +			return ret;
> +		}
> +		adc->sample_freq = val;
> +
> +		return 0;
>  	}
>  
> -	return ret;
> +	return -EINVAL;
>  }
>  
>  static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> @@ -494,11 +834,22 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
>  		*val = adc->oversamp;
>  
>  		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = adc->sample_freq;
> +
> +		return IIO_VAL_INT;
>  	}
>  
>  	return -EINVAL;
>  }
>  
> +static const struct iio_info stm32_dfsdm_info_audio = {
> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +};
> +
>  static const struct iio_info stm32_dfsdm_info_adc = {
>  	.read_raw = stm32_dfsdm_read_raw,
>  	.write_raw = stm32_dfsdm_write_raw,
> @@ -531,6 +882,60 @@ static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>  	return IRQ_HANDLED;
>  }
>  
> +/*
> + * Define external info for SPI Frequency and audio sampling rate that can be
> + * configured by ASoC driver through consumer.h API
> + */
> +static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = {
> +	/* spi_clk_freq : clock freq on SPI/manchester bus used by channel */
> +	{
> +		.name = "spi_clk_freq",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_adc_audio_get_spiclk,
> +		.write = dfsdm_adc_audio_set_spiclk,
> +	},
> +	{},
> +};
> +
> +static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct dma_slave_config config;
> +	int ret;
> +
> +	adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (!adc->dma_chan)
> +		return -EINVAL;
> +
> +	adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
> +					 DFSDM_DMA_BUFFER_SIZE,
> +					 &adc->dma_buf, GFP_KERNEL);
> +	if (!adc->rx_buf) {
> +		ret = -ENOMEM;
> +		goto err_release;
> +	}
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)adc->dfsdm->phys_base;
> +	config.src_addr += DFSDM_RDATAR(adc->fl_id);
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
Superficially looks like this could be more elegantly done
using C99 structure init above.

struct dma_slave_config config = {
	.src_addr = (dma_addr_t)adc-dfsdm->phys_base +
		DFSDM_RDATAR(adc->fl_ix),
	.src_addr_width = DMA_SLAVE_BUS_WIDTH_4_BYTES
};
All the rest will be zeroed by the compiler..

> +
> +	ret = dmaengine_slave_config(adc->dma_chan, &config);
> +	if (ret)
> +		goto err_free;
> +
> +	return 0;
> +
> +err_free:
> +	dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
> +			  adc->rx_buf, adc->dma_buf);
> +err_release:
> +	dma_release_channel(adc->dma_chan);
> +
> +	return ret;
> +}
> +
>  static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>  					 struct iio_chan_spec *ch)
>  {
> @@ -551,7 +956,12 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>  	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>  	ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>  
> -	ch->scan_type.sign = 'u';
> +	if (adc->dev_data->type == DFSDM_AUDIO) {
> +		ch->scan_type.sign = 's';
> +		ch->ext_info = dfsdm_adc_audio_ext_info;
> +	} else {
> +		ch->scan_type.sign = 'u';
> +	}
>  	ch->scan_type.realbits = 24;
>  	ch->scan_type.storagebits = 32;
>  	adc->ch_id = ch->channel;
> @@ -560,6 +970,64 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>  					  &adc->dfsdm->ch_list[ch->channel]);
>  }
>  
> +static int stm32_dfsdm_audio_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *ch;
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *d_ch;
> +	int ret;
> +
> +	ret = stm32_dfsdm_dma_request(indio_dev);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "DMA request failed\n");
> +		return ret;
> +	}
> +
> +	indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> +
> +	ret = iio_triggered_buffer_setup(indio_dev,
> +					 &iio_pollfunc_store_time,

Why?  What reads the time?
I'm not terribly keen on this being used here given we don't actually
have a triggered_buffer...  This is going to allocate an IIO kfifo
that we don't use as well (not huge, but not elegant to allocate
one we don't use).

> +					 NULL,
> +					 &stm32_dfsdm_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Buffer setup failed\n");
> +		goto err_dma_disable;
> +	}
> +
> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	ch->scan_index = 0;
> +	ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "channels init failed\n");
> +		goto err_buffer_cleanup;
> +	}
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +
> +	d_ch = &adc->dfsdm->ch_list[adc->ch_id];
> +	if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		adc->spi_freq = adc->dfsdm->spi_master_freq;
> +
> +	indio_dev->num_channels = 1;
> +	indio_dev->channels = ch;
> +
> +	return 0;
> +
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +
> +err_dma_disable:
> +	if (adc->dma_chan) {
> +		dma_free_coherent(adc->dma_chan->device->dev,
> +				  DFSDM_DMA_BUFFER_SIZE,
> +				  adc->rx_buf, adc->dma_buf);
> +		dma_release_channel(adc->dma_chan);
> +	}
> +	return ret;
> +}
> +
>  static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
>  {
>  	struct iio_chan_spec *ch;
> @@ -612,11 +1080,20 @@ static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = {
>  	.init = stm32_dfsdm_adc_init,
>  };
>  
> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = {
> +	.type = DFSDM_AUDIO,
> +	.init = stm32_dfsdm_audio_init,
> +};
> +
>  static const struct of_device_id stm32_dfsdm_adc_match[] = {
>  	{
>  		.compatible = "st,stm32-dfsdm-adc",
>  		.data = &stm32h7_dfsdm_adc_data,
>  	},
> +	{
> +		.compatible = "st,stm32-dfsdm-dmic",
> +		.data = &stm32h7_dfsdm_audio_data,
> +	},
>  	{}
>  };
>  
> @@ -667,8 +1144,13 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>  	name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL);
>  	if (!name)
>  		return -ENOMEM;
> -	iio->info = &stm32_dfsdm_info_adc;
> -	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> +	if (dev_data->type == DFSDM_AUDIO) {
> +		iio->info = &stm32_dfsdm_info_audio;
> +		snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id);
> +	} else {
> +		iio->info = &stm32_dfsdm_info_adc;
> +		snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> +	}
>  	iio->name = name;
>  
>  	/*
> @@ -700,7 +1182,10 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>  	if (ret < 0)
>  		return ret;
>  
> -	return iio_device_register(iio);
> +	iio_device_register(iio);
> +	if (dev_data->type == DFSDM_AUDIO)
> +		return devm_of_platform_populate(&pdev->dev);
Hmm. This is a little ugly in that the devm call will get
unwound after the IIO device is unregistered and all if it's
interfaces have gone away.

I don't think it 'matters' as such here but it would be
nicer if the remove order was the precise reverse of the probe order
and to do that you can't use a managed function here.

> +	return 0;
>  }
>  
>  static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> @@ -709,7 +1194,14 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>  
>  	iio_device_unregister(indio_dev);
> -
> +	if (indio_dev->pollfunc)
> +		iio_triggered_buffer_cleanup(indio_dev);
> +	if (adc->dma_chan) {
> +		dma_free_coherent(adc->dma_chan->device->dev,
> +				  DFSDM_DMA_BUFFER_SIZE,
> +				  adc->rx_buf, adc->dma_buf);
> +		dma_release_channel(adc->dma_chan);

This is not in the reverse order of the probe as these are (I think)
set up in the audio init as a result of the platform populate?
So these should be before the iio_device_unregister.

This whole chunk is really an unwind of the audio init. I would
make that explicit by having an audio exit function called here.
 
> +	}
>  	return 0;
>  }
>  
> diff --git a/include/linux/iio/adc/stm32-dfsdm-adc.h b/include/linux/iio/adc/stm32-dfsdm-adc.h
> new file mode 100644
> index 0000000..e7dc7a5
> --- /dev/null
> +++ b/include/linux/iio/adc/stm32-dfsdm-adc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * This file discribe the STM32 DFSDM IIO driver API for audio part
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
> + */
> +
> +#ifndef STM32_DFSDM_ADC_H
> +#define STM32_DFSDM_ADC_H
> +
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private);
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
> +
> +#endif

^ permalink raw reply

* Re: [PATCH 0/3] Meson8 GPIO interrupt support
From: Martin Blumenstingl @ 2017-12-10 18:15 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Marc Zyngier, carlo-KA+7E9HrN00dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ, jason-NLaQJdtUoK4Be96aLqz0jA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <7h1sljmxp6.fsf-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>

Hi Kevin,

On Tue, Oct 31, 2017 at 11:13 AM, Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org> writes:
>
>> On Mon, Oct 30 2017 at 12:05:20 am GMT, Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:
>>> This is a simple follow-up of two series from Jerome:
>>> - "irqchip: meson: add support for the gpio interrupt controller"
>>> - "ARM: meson: enable gpio interrupt controller"
>>>
>>> This fills in the missing bits to have GPIO interrupt support on Meson8,
>>> which includes:
>>> - a new binding in the driver with the correct IRQ number
>>> - enabling the GPIO interrupt controller in meson8.dts
>>> - enabling the GPIO interrupt controller via Kconfig for MACH_MESON8
>>>
>>>
>>> Martin Blumenstingl (3):
>>>   irqchip/meson-gpio: add support for Meson8 SoCs
>>>   ARM: dts: meson8: enable the GPIO interrupt controller
>>>   ARM: meson: enable MESON_IRQ_GPIO also for MACH_MESON8
>>>
>>>  .../bindings/interrupt-controller/amlogic,meson-gpio-intc.txt        | 1 +
>>>  arch/arm/boot/dts/meson8.dtsi                                        | 5 +++++
>>>  arch/arm/mach-meson/Kconfig                                          | 1 +
>>>  drivers/irqchip/irq-meson-gpio.c                                     | 5 +++++
>>>  4 files changed, 12 insertions(+)
>>
>> Kevin, Carlos: Are you OK with this one? It seems pretty minor, and I'm
>> happy to queue it if I get an Ack for it. If you'd rather get it through
>> via armsoc, let me know.
>
> Can you take the drivers/irqchip and bindings patch (PATCH 1/3) and I'll
> take the rest to avoid conflicts with a bunch of other moving parts we
> have in arch/arm.
PATCH 1/3 even made it into v4.15
can you please queue the remaining two patches for v4.16 - or do you
want me to re-send these?


thank you!
Martin
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v7 13/13] ASoC: stm32: add DFSDM DAI support
From: Jonathan Cameron @ 2017-12-10 18:18 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Maxime Coquelin,
	Alexandre Torgue
In-Reply-To: <1512744566-13233-14-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

On Fri, 8 Dec 2017 15:49:26 +0100
Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org> wrote:

> Add driver to handle DAI interface for PDM microphones connected
> to Digital Filter for Sigma Delta Modulators IP.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

I don't know nearly enough about the sound side to comment on the details
in here, but a few nitpicks inline I saw whilst glancing through it.

Thanks,

Jonathan

> ---
> V6 to V7 updates:
> - SPDX-Licensing.
> - Supress workaround in stm32_adfsdm_pcm_new as issue fixed in generic code in recent kernel version.
> - Fix kbuild report issue on a log parameter type.
> - Suppress test in stm32_adfsdm_set_sysclk on clock direction.
> 
>  sound/soc/stm/Kconfig        |  11 ++
>  sound/soc/stm/Makefile       |   3 +
>  sound/soc/stm/stm32_adfsdm.c | 351 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 365 insertions(+)
>  create mode 100644 sound/soc/stm/stm32_adfsdm.c
> 
> diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
> index 3398e6c..a78f770 100644
> --- a/sound/soc/stm/Kconfig
> +++ b/sound/soc/stm/Kconfig
> @@ -28,4 +28,15 @@ config SND_SOC_STM32_SPDIFRX
>  	help
>  	  Say Y if you want to enable S/PDIF capture for STM32
>  
> +config SND_SOC_STM32_DFSDM
> +	tristate "SoC Audio support for STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF && STM32_DFSDM_ADC) || COMPILE_TEST
> +	depends on SND_SOC
> +	select SND_SOC_GENERIC_DMAENGINE_PCM
> +	select SND_SOC_DMIC
> +	select IIO_BUFFER_CB
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series for digital microphone capture.
>  endmenu
> diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
> index 4ed22e6..53e90e6 100644
> --- a/sound/soc/stm/Makefile
> +++ b/sound/soc/stm/Makefile
> @@ -12,3 +12,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
>  # SPDIFRX
>  snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o
>  obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o
> +
> +#DFSDM
> +obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
> diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
> new file mode 100644
> index 0000000..eef725e
> --- /dev/null
> +++ b/sound/soc/stm/stm32_adfsdm.c
> @@ -0,0 +1,351 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * This file is part of STM32 DFSDM ASoC DAI driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Authors: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> + *          Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/adc/stm32-dfsdm-adc.h>
> +
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +
> +#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
> +
> +#define DFSDM_MAX_PERIOD_SIZE	(PAGE_SIZE / 2)
> +#define DFSDM_MAX_PERIODS	6
> +
> +struct stm32_adfsdm_priv {
> +	struct snd_soc_dai_driver dai_drv;
> +	struct snd_pcm_substream *substream;
> +	struct device *dev;
> +
> +	/* IIO */
> +	struct iio_channel *iio_ch;
> +	struct iio_cb_buffer *iio_cb;
> +	bool iio_active;
> +
> +	/* PCM buffer */
> +	unsigned char *pcm_buff;
> +	unsigned int pos;
> +};
> +
> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +	    SNDRV_PCM_INFO_PAUSE,
> +	.formats = SNDRV_PCM_FMTBIT_S32_LE,
> +
> +	.rate_min = 8000,
> +	.rate_max = 32000,
> +
> +	.channels_min = 1,
> +	.channels_max = 1,
> +
> +	.periods_min = 2,
> +	.periods_max = DFSDM_MAX_PERIODS,
> +
> +	.period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
> +	.buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
> +};
> +
> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	if (priv->iio_active) {
> +		iio_channel_stop_all_cb(priv->iio_cb);
> +		priv->iio_active = false;
> +	}
> +}
> +
> +static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
> +				    struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	int ret;
> +
> +	ret = iio_write_channel_attribute(priv->iio_ch,
> +					  substream->runtime->rate, 0,
> +					  IIO_CHAN_INFO_SAMP_FREQ);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "%s: Failed to set %d sampling rate\n",
> +			__func__, substream->runtime->rate);
> +		return ret;
> +	}
> +
> +	if (!priv->iio_active) {
> +		ret = iio_channel_start_all_cb(priv->iio_cb);
> +		if (!ret)
> +			priv->iio_active = true;
> +		else
> +			dev_err(dai->dev, "%s: IIO channel start failed (%d)\n",
> +				__func__, ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> +				   unsigned int freq, int dir)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	ssize_t size;
> +	char str_freq[10];
> +
> +	dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
> +
> +	/* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
Space before */
> +
> +	snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
> +	size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
> +					  str_freq, sizeof(str_freq));
> +	if (size != sizeof(str_freq)) {
> +		dev_err(dai->dev, "%s: Failed to set SPI clock\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
> +	.shutdown = stm32_adfsdm_shutdown,
> +	.prepare = stm32_adfsdm_dai_prepare,
> +	.set_sysclk = stm32_adfsdm_set_sysclk,
> +};
> +
> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
> +	.capture = {
> +		    .channels_min = 1,
> +		    .channels_max = 1,
> +		    .formats = SNDRV_PCM_FMTBIT_S32_LE,
> +		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
> +			      SNDRV_PCM_RATE_32000),
> +		    },
> +	.ops = &stm32_adfsdm_dai_ops,
> +};
> +
> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
> +	.name = "stm32_dfsdm_audio",
> +};
> +
> +static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
> +{
> +	struct stm32_adfsdm_priv *priv = private;
> +	struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
> +	u8 *pcm_buff = priv->pcm_buff;
> +	u8 *src_buff = (u8 *)data;
> +	unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
> +	unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
> +	unsigned int old_pos = priv->pos;
> +	unsigned int cur_size = size;
> +
> +	dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n",
> +		__func__, &pcm_buff[priv->pos], priv->pos, size);
> +
> +	if ((priv->pos + size) > buff_size) {
> +		memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
> +		cur_size -= buff_size - priv->pos;
> +		priv->pos = 0;
> +	}
> +
> +	memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
> +	priv->pos = (priv->pos + cur_size) % buff_size;
> +
> +	if (cur_size != size || (old_pos && (old_pos % period_size < size)))
> +		snd_pcm_period_elapsed(priv->substream);
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv =
> +		snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		priv->pos = 0;
> +		return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
> +					       stm32_afsdm_pcm_cb, priv);
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +	int ret;
> +
> +	ret =  snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
> +	if (!ret)
> +		priv->substream = substream;
> +
> +	return ret;
> +}
> +
> +static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv =
> +		snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> +	snd_pcm_lib_free_pages(substream);
> +	priv->substream = NULL;
> +
> +	return 0;
> +}
> +
> +static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
> +					    struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv =
> +		snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> +	return bytes_to_frames(substream->runtime, priv->pos);
> +}
> +
> +static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
> +				      struct snd_pcm_hw_params *params)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv =
> +		snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +	int ret;
> +
> +	ret =  snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
> +	if (ret < 0)
> +		return ret;
> +	priv->pcm_buff = substream->runtime->dma_area;
> +
> +	return iio_channel_cb_set_buffer_watermark(priv->iio_cb,
> +						   params_period_size(params));
> +}
> +
> +static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> +	snd_pcm_lib_free_pages(substream);
> +
> +	return 0;
> +}
> +
> +static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
> +	.open		= stm32_adfsdm_pcm_open,
> +	.close		= stm32_adfsdm_pcm_close,
> +	.hw_params	= stm32_adfsdm_pcm_hw_params,
> +	.hw_free	= stm32_adfsdm_pcm_hw_free,
> +	.trigger	= stm32_adfsdm_trigger,
> +	.pointer	= stm32_adfsdm_pcm_pointer,
> +};
> +
> +static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
> +{
> +	struct snd_pcm *pcm = rtd->pcm;
> +	struct stm32_adfsdm_priv *priv =
> +		snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +	unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
> +	int ret;
> +
> +	ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +						    priv->dev, size, size);
> +	return ret;
nitpick. Nicer as
return snd_pcm_lib_preallocate_pages_for_all(..

> +}
> +
> +static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
> +{
> +	struct snd_pcm_substream *substream;
> +	struct snd_soc_pcm_runtime *rtd;
> +	struct stm32_adfsdm_priv *priv;
> +
> +	substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
> +	if (substream) {
> +		rtd = substream->private_data;
> +		priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> +		snd_pcm_lib_preallocate_free_for_all(pcm);
> +	}
> +}
> +
> +static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
> +	.ops		= &stm32_adfsdm_pcm_ops,
> +	.pcm_new	= stm32_adfsdm_pcm_new,
> +	.pcm_free	= stm32_adfsdm_pcm_free,
> +};
> +
> +static const struct of_device_id stm32_adfsdm_of_match[] = {
> +	{.compatible = "st,stm32h7-dfsdm-dai"},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
> +
> +static int stm32_adfsdm_probe(struct platform_device *pdev)
> +{
> +	struct stm32_adfsdm_priv *priv;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +	priv->dai_drv = stm32_adfsdm_dai;
> +
> +	dev_set_drvdata(&pdev->dev, priv);
> +
> +	ret = devm_snd_soc_register_component(&pdev->dev,
> +					      &stm32_adfsdm_dai_component,
> +					      &priv->dai_drv, 1);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Associate iio channel */
> +	priv->iio_ch  = devm_iio_channel_get_all(&pdev->dev);
> +	if (IS_ERR(priv->iio_ch))
> +		return PTR_ERR(priv->iio_ch);
> +
> +	priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
> +	if (IS_ERR(priv->iio_cb))
> +		return PTR_ERR(priv->iio_ch);
> +
> +	ret = devm_snd_soc_register_platform(&pdev->dev,
> +					     &stm32_adfsdm_soc_platform);
> +	if (ret < 0)
> +		dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
> +			__func__);
> +
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_adfsdm_driver = {
> +	.driver = {
> +		   .name = STM32_ADFSDM_DRV_NAME,
> +		   .of_match_table = stm32_adfsdm_of_match,
> +		   },
> +	.probe = stm32_adfsdm_probe,
> +};
> +
> +module_platform_driver(stm32_adfsdm_driver);
> +
> +MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);

^ permalink raw reply

* Re: [PATCH v4 3/5] staging: Introduce NVIDIA Tegra video decoder driver
From: Dmitry Osipenko @ 2017-12-10 18:56 UTC (permalink / raw)
  To: Hans Verkuil, Thierry Reding, Jonathan Hunter, Greg Kroah-Hartman,
	Rob Herring, Mauro Carvalho Chehab, Stephen Warren,
	Vladimir Zapolskiy
  Cc: devel, devicetree, linux-kernel, linux-tegra, Maxime Ripard,
	Giulio Benetti, Dan Carpenter, linux-media
In-Reply-To: <93c98569-0282-80d9-78ad-c8ab8fd9db92@xs4all.nl>

On 05.12.2017 16:03, Hans Verkuil wrote:
> On 12/05/17 13:17, Dmitry Osipenko wrote:
>> Hi Hans,
>>
>> On 04.12.2017 17:04, Hans Verkuil wrote:
>>> Hi Dmitry,
>>>
>>> As you already mention in the TODO, this should become a v4l2 codec driver.
>>>
>>> Good existing examples are the coda, qcom/venus and mtk-vcodec drivers.
>>>
>>> One thing that is not clear from this code is if the tegra hardware is a
>>> stateful or stateless codec, i.e. does it keep track of the decoder state
>>> in the hardware, or does the application have to keep track of the state and
>>> provide the state information together with the video data?
>>>
>>> I ask because at the moment only stateful codecs are supported. Work is ongoing
>>> to support stateless codecs, but we don't support that for now.
>>>
>>
>> It is stateless. Is there anything ready to try out? If yes, could you please
>> give a reference to that work?
> 
> I rebased my two year old 'requests2' branch to the latest mainline version and
> gave it the imaginative name 'requests3':
> 
> https://git.linuxtv.org/hverkuil/media_tree.git/log/?h=requests3
> 
> (Note: only compile tested!)

Thank you very much.

> This is what ChromeOS has been using (actually they use a slightly older version)
> and the new version that is currently being developed will be similar, so any work
> you do on top of this will carry over to the final version without too much effort.
> 
> At least, that's the intention :-)
> 
> I've CC-ed Maxime and Giulio as well: they are looking into adding support for
> the stateless allwinner codec based on this code as well. There may well be
> opportunities for you to work together, esp. on the userspace side. Note that
> Rockchip has the same issue, they too have a stateless HW codec.

IIUC, we will have to define video decoder parameters in V4L API and then make a
V4L driver / userspace prototype (ffmpeg for example) that will use the requests
API for video decoding in order to upstream the requests API. Does it sound good?

>>
>>> Anyway, I'm OK with merging this in staging. Although I think it should go
>>> to staging/media since we want to keep track of it.
>>>
>>
>> Awesome, I'll move driver to staging/media in V5. Thanks!
> 
> Nice, thanks!

^ permalink raw reply

* Re: [PATCH v2 2/5] ARM: dts: imx53: add srtc node
From: Fabio Estevam @ 2017-12-10 19:03 UTC (permalink / raw)
  To: Patrick Brünn
  Cc: linux-kernel-dev, Shawn Guo, Sascha Hauer, Alessandro Zummo,
	Alexandre Belloni, Mark Rutland,
	open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Russell King, Noel Vellemans, Rob Herring,
	Philippe Ombredanne, Fabio Estevam,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <3BB206AB2B1BD448954845CE6FF69A8E01CB531C1F-vUij6NSodOcSOXk/eDA+lhuDmiLEvxRJ@public.gmane.org>

On Tue, Dec 5, 2017 at 12:20 PM, Patrick Brünn <P.Bruenn-QonKdJ6Bx35Wk0Htik3J/w@public.gmane.org> wrote:

> I will wait a few days for more reviewers and then integrate your comments in a v3. If nothing major show up I will wait until the imx53.dtsi revert landed in Linus tree.

It is in Linus' tree now as commit e501506d3ea0 ("Revert "ARM: dts:
imx53: add srtc node"").
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v4 3/5] staging: Introduce NVIDIA Tegra video decoder driver
From: Nicolas Dufresne @ 2017-12-10 19:29 UTC (permalink / raw)
  To: Dmitry Osipenko, Hans Verkuil, Thierry Reding, Jonathan Hunter,
	Greg Kroah-Hartman, Rob Herring, Mauro Carvalho Chehab,
	Stephen Warren, Vladimir Zapolskiy
  Cc: Dan Carpenter, linux-media, linux-tegra, devel, devicetree,
	linux-kernel, Maxime Ripard, Giulio Benetti
In-Reply-To: <f46f397d-28e1-f4e5-55d4-9863214e8f6c@gmail.com>

Le dimanche 10 décembre 2017 à 21:56 +0300, Dmitry Osipenko a écrit :
> > I've CC-ed Maxime and Giulio as well: they are looking into adding support for
> > the stateless allwinner codec based on this code as well. There may well be
> > opportunities for you to work together, esp. on the userspace side. Note that
> > Rockchip has the same issue, they too have a stateless HW codec.
> 
> IIUC, we will have to define video decoder parameters in V4L API and then make a
> V4L driver / userspace prototype (ffmpeg for example) that will use the requests
> API for video decoding in order to upstream the requests API. Does it sound good?

Chromium/Chrome already have support for that type of decoder in their
tree. In theory, it should just work.

Nicolas

^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: Add binding for Sitronix ST7735R display panels
From: David Lechner @ 2017-12-10 19:33 UTC (permalink / raw)
  To: Noralf Trønnes, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: limor-6aDhHjTmHzzR7s880joybQ, Rob Herring, Mark Rutland,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <0cade030-030c-f6da-6f79-6fbd520ad753-L59+Z2yzLopAfugRpC6u6w@public.gmane.org>

On 12/08/2017 03:41 PM, Noralf Trønnes wrote:
> 
> Den 29.11.2017 04.01, skrev David Lechner:
>> This adds a new device tree binding for Sitronix ST7735R display panels,
>> such as the Adafruit 1.8" TFT.
>>
>> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>> ---
>>   .../bindings/display/sitronix,st7735r.txt          | 35 
>> ++++++++++++++++++++++
>>   1 file changed, 35 insertions(+)
>>   create mode 100644 
>> Documentation/devicetree/bindings/display/sitronix,st7735r.txt
>>
>> diff --git 
>> a/Documentation/devicetree/bindings/display/sitronix,st7735r.txt 
>> b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
>> new file mode 100644
>> index 0000000..bbb8ba6
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
>> @@ -0,0 +1,35 @@
>> +Sitronix ST7735R display panels
>> +
>> +This binding is for display panels using a Sitronix ST7735R 
>> controller in SPI
>> +mode.
>> +
>> +Required properties:
>> +- compatible:    "sitronix,st7735r-jd-t18003-t01"
>> +- dc-gpios:    Display data/command selection (D/CX)
>> +- reset-gpios:    Reset signal (RSTX)
> 
> I'm wondering if this should be optional.
> 
> Even though the display needs the reset line to be driven, it doesn't
> have to be so by a gpio, I believe you can even get away with just
> using a resistor as a reset circuit.
> 
> Not terribly important, it's up to you.
> 

It can be made optional later if needed, so I'm going to leave it as-is.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 0/2] DRM driver for Sitronix ST7735R display panels
From: David Lechner @ 2017-12-10 22:10 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: David Lechner, Noralf Trønnes, limor-6aDhHjTmHzzR7s880joybQ,
	Linus Walleij, Rob Herring, Mark Rutland,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

This series adds a new DRM/TinyDRM driver for Sitronix ST7735R, specifically
the Adafruit 1.8" TFT.

Nothing fancy here. Just mostly TinyDRM boilerplate with the init sequence
from the fbtft driver for the same panel.

Please see new discussion on device tree bindings in patch 1/2.

David Lechner (2):
  dt-bindings: Add binding for Sitronix ST7735R display panels
  drm/tinydrm: add driver for ST7735R panels

 .../bindings/display/sitronix,st7735r.txt          |  35 ++++
 MAINTAINERS                                        |   6 +
 drivers/gpu/drm/tinydrm/Kconfig                    |  10 +
 drivers/gpu/drm/tinydrm/Makefile                   |   1 +
 drivers/gpu/drm/tinydrm/st7735r.c                  | 219 +++++++++++++++++++++
 5 files changed, 271 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sitronix,st7735r.txt
 create mode 100644 drivers/gpu/drm/tinydrm/st7735r.c

-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 1/2] dt-bindings: Add binding for Sitronix ST7735R display panels
From: David Lechner @ 2017-12-10 22:10 UTC (permalink / raw)
  To: dri-devel, devicetree
  Cc: Mark Rutland, limor, David Lechner, linux-kernel, Rob Herring
In-Reply-To: <1512943833-31352-1-git-send-email-david@lechnology.com>

This adds a new device tree binding for Sitronix ST7735R display panels,
such as the Adafruit 1.8" TFT.

Signed-off-by: David Lechner <david@lechnology.com>
Acked-by: Rob Herring <robh@kernel.org>
---

v2: changes:
* None, but...

I'm wondering about my choice of compatible here. I chose the name
"sitronix,st7735r-jd-t18003-t01" to mean a Sitronix ST7735R controller
connected to a JD-T18003-T01 LCD screen.

What I am actually using, though, is an Adafruit 1.8" TFT breakout [1]. It
has some additional electronics which just pass through signals to the
controller. The bare display is also available from Adafruit [2].

Limor brought up an interesting point in an off-list discussion. The
same controller can be wired to the same LCD in different ways. This is
evident in the fbftf drivers in staging. There is an "adafruit18" and an
"adafruit18_green" in fbftf_devices.c where apparently, two otherwise
identical displays were wired slightly differently at the factory so that
on one, the on-board GRAM word 0 does not correspond to pixel 0,0 on the
LCD. It requires a special offset to the GRAM starting address in order to
have the image displayed correctly.

Additionally, fbtft supports a SainSmart 1.8" TFT [3] that uses the same
controller, but it appears that these have different gamma curves (perhaps
they use different LCDs?). The available pins are exactly the same as the
Adafruit display though.

As I am writing this, I am looking at the JD-1800 datasheet [4] again for
the Adafruit display and I see that in addition to specifying the size of
the display it says "IC: ST7735B". So, I am starting to think that "jianda,
jd-t18003-t01" would be a better compatible string since it tells you
everything you need to know about this display. Just using the controller
name by itself ("sitronix,st7735r") is not enough because it does not tell
you things like the number of pixels or the physical size of the LCD.

What I am not sure about, though, is how to represent the Adafruit display
that requires the offset in the device tree. Would it be a different
compatible string or should we add an extra property to indicate this
quirk?

On a related note, I recently submitted device tree bindings for a similar
SPI display breakout board [5][6]. After more digging though, I found a
datasheet for that display [7], so I'm thinking a compatible string of
"vot,v220hf01a-t" would be better by the same reasoning above.

And, I know this is getting long, but one more thing...

There was a binding acked recently for the LCD on a D-Link DIR-685 Wireless
N Storage Router [8]. This uses the compound compatible string of "dlink,
dir-685-panel", "ilitek,ili9322". If we want to try to keep things 
consistent, perhaps I should be adopting this pattern as well? And perhaps
it would be better to use the better known vendor name instead of the
obscure vendors from the datasheets that I have found? For example,
"adafruit,618" "sitronix,st7735r" instead of "jianda,jd-t18003-t01"?

[1]: https://www.adafruit.com/product/358
[2]: https://www.adafruit.com/product/618
[3]: https://www.sainsmart.com/products/1-8-tft-spi-lcd-screen-with-microsd-socket
[4]: http://www.adafruit.com/datasheets/JD-T1800.pdf
[5]: https://patchwork.freedesktop.org/patch/187038/
[6]: https://patchwork.freedesktop.org/patch/189233/
[7]: http://www.hotmcu.com/22-176x220-tft-lcd-with-spi-interface-p-316.html
[8]: https://patchwork.freedesktop.org/patch/191462/

 .../bindings/display/sitronix,st7735r.txt          | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sitronix,st7735r.txt

diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.txt b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
new file mode 100644
index 0000000..bbb8ba6
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
@@ -0,0 +1,35 @@
+Sitronix ST7735R display panels
+
+This binding is for display panels using a Sitronix ST7735R controller in SPI
+mode.
+
+Required properties:
+- compatible:	"sitronix,st7735r-jd-t18003-t01"
+- dc-gpios:	Display data/command selection (D/CX)
+- reset-gpios:	Reset signal (RSTX)
+
+The node for this driver must be a child node of a SPI controller, hence
+all mandatory properties described in ../spi/spi-bus.txt must be specified.
+
+Optional properties:
+- rotation:	panel rotation in degrees counter clockwise (0,90,180,270)
+- backlight:	phandle of the backlight device attached to the panel
+
+Example:
+
+	backlight: backlight {
+		compatible = "gpio-backlight";
+		gpios = <&gpio 44 GPIO_ACTIVE_HIGH>;
+	}
+
+	...
+
+	display@0{
+		compatible = "sitronix,st7735r-jd-t18003-t01";
+		reg = <0>;
+		spi-max-frequency = <32000000>;
+		dc-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>;
+		rotation = <270>;
+		backlight = &backlight;
+	};
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH v2 2/2] drm/tinydrm: add driver for ST7735R panels
From: David Lechner @ 2017-12-10 22:10 UTC (permalink / raw)
  To: dri-devel, devicetree
  Cc: Mark Rutland, limor, David Lechner, linux-kernel, Rob Herring
In-Reply-To: <1512943833-31352-1-git-send-email-david@lechnology.com>

This adds a new driver for Sitronix ST7735R display panels.

This has been tested using an Adafruit 1.8" TFT.

Signed-off-by: David Lechner <david@lechnology.com>
---

v2 changes:
* Change delay from 10ms to 20ms to avoid checkpatch warning
* Use mipi_dbi_pipe_enable()/mipi_dbi_pipe_disable() to reduce duplicated code
* Rebase on drm-misc-next (tinydrm_lastclose => drm_fb_helper_lastclose)
* Use mipi_dbi_debugfs_init
* Add mipi->read_commands = NULL; since this display is write-only


 MAINTAINERS                       |   6 ++
 drivers/gpu/drm/tinydrm/Kconfig   |  10 ++
 drivers/gpu/drm/tinydrm/Makefile  |   1 +
 drivers/gpu/drm/tinydrm/st7735r.c | 219 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 236 insertions(+)
 create mode 100644 drivers/gpu/drm/tinydrm/st7735r.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0e49099..2ce17f5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4551,6 +4551,12 @@ S:	Maintained
 F:	drivers/gpu/drm/tinydrm/st7586.c
 F:	Documentation/devicetree/bindings/display/st7586.txt
 
+DRM DRIVER FOR SITRONIX ST7735R PANELS
+M:	David Lechner <david@lechnology.com>
+S:	Maintained
+F:	drivers/gpu/drm/tinydrm/st7735r.c
+F:	Documentation/devicetree/bindings/display/st7735r.txt
+
 DRM DRIVER FOR TDFX VIDEO CARDS
 S:	Orphan / Obsolete
 F:	drivers/gpu/drm/tdfx/
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 90c5bd5..b0e567d 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -52,3 +52,13 @@ config TINYDRM_ST7586
 	  * LEGO MINDSTORMS EV3
 
 	  If M is selected the module will be called st7586.
+
+config TINYDRM_ST7735R
+	tristate "DRM support for Sitronix ST7735R display panels"
+	depends on DRM_TINYDRM && SPI
+	select TINYDRM_MIPI_DBI
+	help
+	  DRM driver Sitronix ST7735R with one of the following LCDs:
+	  * JD-T18003-T01 1.8" 128x160 TFT
+
+	  If M is selected the module will be called st7735r.
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
index 8aeee53..49a1119 100644
--- a/drivers/gpu/drm/tinydrm/Makefile
+++ b/drivers/gpu/drm/tinydrm/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_TINYDRM_ILI9225)		+= ili9225.o
 obj-$(CONFIG_TINYDRM_MI0283QT)		+= mi0283qt.o
 obj-$(CONFIG_TINYDRM_REPAPER)		+= repaper.o
 obj-$(CONFIG_TINYDRM_ST7586)		+= st7586.o
+obj-$(CONFIG_TINYDRM_ST7735R)		+= st7735r.o
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
new file mode 100644
index 0000000..0f5c295
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -0,0 +1,219 @@
+/*
+ * DRM driver for Sitronix ST7735R panels
+ *
+ * Copyright 2017 David Lechner <david@lechnology.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/tinydrm/mipi-dbi.h>
+#include <drm/tinydrm/tinydrm-helpers.h>
+
+#define ST7735R_FRMCTR1		0xb1
+#define ST7735R_FRMCTR2		0xb2
+#define ST7735R_FRMCTR3		0xb3
+#define ST7735R_INVCTR		0xb4
+#define ST7735R_PWCTR1		0xc0
+#define ST7735R_PWCTR2		0xc1
+#define ST7735R_PWCTR3		0xc2
+#define ST7735R_PWCTR4		0xc3
+#define ST7735R_PWCTR5		0xc4
+#define ST7735R_VMCTR1		0xc5
+#define ST7735R_GAMCTRP1	0xe0
+#define ST7735R_GAMCTRN1	0xe1
+
+#define ST7735R_MY	BIT(7)
+#define ST7735R_MX	BIT(6)
+#define ST7735R_MV	BIT(5)
+
+static void st7735r_pipe_enable(struct drm_simple_display_pipe *pipe,
+				struct drm_crtc_state *crtc_state)
+{
+	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
+	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct device *dev = tdev->drm->dev;
+	int ret;
+	u8 addr_mode;
+
+	DRM_DEBUG_KMS("\n");
+
+	mipi_dbi_hw_reset(mipi);
+
+	ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
+		return;
+	}
+
+	msleep(150);
+
+	mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE);
+	msleep(500);
+
+	mipi_dbi_command(mipi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d);
+	mipi_dbi_command(mipi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d);
+	mipi_dbi_command(mipi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c,
+			 0x2d);
+	mipi_dbi_command(mipi, ST7735R_INVCTR, 0x07);
+	mipi_dbi_command(mipi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84);
+	mipi_dbi_command(mipi, ST7735R_PWCTR2, 0xc5);
+	mipi_dbi_command(mipi, ST7735R_PWCTR3, 0x0a, 0x00);
+	mipi_dbi_command(mipi, ST7735R_PWCTR4, 0x8a, 0x2a);
+	mipi_dbi_command(mipi, ST7735R_PWCTR5, 0x8a, 0xee);
+	mipi_dbi_command(mipi, ST7735R_VMCTR1, 0x0e);
+	mipi_dbi_command(mipi, MIPI_DCS_EXIT_INVERT_MODE);
+	switch (mipi->rotation) {
+	default:
+		addr_mode = ST7735R_MX | ST7735R_MY;
+		break;
+	case 90:
+		addr_mode = ST7735R_MX | ST7735R_MV;
+		break;
+	case 180:
+		addr_mode = 0;
+		break;
+	case 270:
+		addr_mode = ST7735R_MY | ST7735R_MV;
+		break;
+	}
+	mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+	mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT,
+			 MIPI_DCS_PIXEL_FMT_16BIT);
+	mipi_dbi_command(mipi, ST7735R_GAMCTRP1, 0x0f, 0x1a, 0x0f, 0x18, 0x2f,
+			 0x28, 0x20, 0x22, 0x1f, 0x1b, 0x23, 0x37, 0x00, 0x07,
+			 0x02, 0x10);
+	mipi_dbi_command(mipi, ST7735R_GAMCTRN1, 0x0f, 0x1b, 0x0f, 0x17, 0x33,
+			 0x2c, 0x29, 0x2e, 0x30, 0x30, 0x39, 0x3f, 0x00, 0x07,
+			 0x03, 0x10);
+	mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
+
+	msleep(100);
+
+	mipi_dbi_command(mipi, MIPI_DCS_ENTER_NORMAL_MODE);
+
+	msleep(20);
+
+	mipi_dbi_pipe_enable(pipe, crtc_state);
+}
+
+static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = {
+	.enable		= st7735r_pipe_enable,
+	.disable	= mipi_dbi_pipe_disable,
+	.update		= tinydrm_display_pipe_update,
+	.prepare_fb	= tinydrm_display_pipe_prepare_fb,
+};
+
+static const struct drm_display_mode st7735r_mode = {
+	TINYDRM_MODE(128, 160, 28, 35),
+};
+
+DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
+
+static struct drm_driver st7735r_driver = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+				  DRIVER_ATOMIC,
+	.fops			= &st7735r_fops,
+	TINYDRM_GEM_DRIVER_OPS,
+	.lastclose		= drm_fb_helper_lastclose,
+	.debugfs_init		= mipi_dbi_debugfs_init,
+	.name			= "st7735r",
+	.desc			= "Sitronix ST7735R",
+	.date			= "20171128",
+	.major			= 1,
+	.minor			= 0,
+};
+
+static const struct of_device_id st7735r_of_match[] = {
+	{ .compatible = "sitronix,st7735r-jd-t18003-t01" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, st7735r_of_match);
+
+static const struct spi_device_id st7735r_id[] = {
+	{ "st7735r-jd-t18003-t01", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(spi, st7735r_id);
+
+static int st7735r_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct mipi_dbi *mipi;
+	struct gpio_desc *dc;
+	u32 rotation = 0;
+	int ret;
+
+	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	if (!mipi)
+		return -ENOMEM;
+
+	mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(mipi->reset)) {
+		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
+		return PTR_ERR(mipi->reset);
+	}
+
+	dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+	if (IS_ERR(dc)) {
+		DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
+		return PTR_ERR(dc);
+	}
+
+	mipi->backlight = tinydrm_of_find_backlight(dev);
+	if (IS_ERR(mipi->backlight))
+		return PTR_ERR(mipi->backlight);
+
+	device_property_read_u32(dev, "rotation", &rotation);
+
+	ret = mipi_dbi_spi_init(spi, mipi, dc);
+	if (ret)
+		return ret;
+
+	/* Cannot read from Adafruit 1.8" display via SPI */
+	mipi->read_commands = NULL;
+
+	ret = mipi_dbi_init(&spi->dev, mipi, &st7735r_pipe_funcs,
+			    &st7735r_driver, &st7735r_mode, rotation);
+	if (ret)
+		return ret;
+
+	spi_set_drvdata(spi, mipi);
+
+	return devm_tinydrm_register(&mipi->tinydrm);
+}
+
+static void st7735r_shutdown(struct spi_device *spi)
+{
+	struct mipi_dbi *mipi = spi_get_drvdata(spi);
+
+	tinydrm_shutdown(&mipi->tinydrm);
+}
+
+static struct spi_driver st7735r_spi_driver = {
+	.driver = {
+		.name = "st7735r",
+		.owner = THIS_MODULE,
+		.of_match_table = st7735r_of_match,
+	},
+	.id_table = st7735r_id,
+	.probe = st7735r_probe,
+	.shutdown = st7735r_shutdown,
+};
+module_spi_driver(st7735r_spi_driver);
+
+MODULE_DESCRIPTION("Sitronix ST7735R DRM driver");
+MODULE_AUTHOR("David Lechner <david@lechnology.com>");
+MODULE_LICENSE("GPL");
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH net-next 1/2 v8] net: ethernet: Add DT bindings for the Gemini ethernet
From: Linus Walleij @ 2017-12-10 22:45 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA, David S . Miller,
	Michał Mirosław
  Cc: Janos Laube, Paulius Zaleckas,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hans Ulli Kroll, Florian Fainelli, Linus Walleij,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tobias Waldvogel

This adds the device tree bindings for the Gemini ethernet
controller. It is pretty straight-forward, using standard
bindings and modelling the two child ports as child devices
under the parent ethernet controller device.

Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Tobias Waldvogel <tobias.waldvogel-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Michał Mirosław <mirq-linux-CoA6ZxLDdyEEUmgCuDUIdw@public.gmane.org>
Signed-off-by: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
ChangeLog v7->v8:
- Use ethernet-port@0 and ethernet-port@1 with unit names
  and following OF graph requirements.
---
 .../bindings/net/cortina,gemini-ethernet.txt       | 92 ++++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt

diff --git a/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt
new file mode 100644
index 000000000000..6c559981d110
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt
@@ -0,0 +1,92 @@
+Cortina Systems Gemini Ethernet Controller
+==========================================
+
+This ethernet controller is found in the Gemini SoC family:
+StorLink SL3512 and SL3516, also known as Cortina Systems
+CS3512 and CS3516.
+
+Required properties:
+- compatible: must be "cortina,gemini-ethernet"
+- reg: must contain the global registers and the V-bit and A-bit
+  memory areas, in total three register sets.
+- syscon: a phandle to the system controller
+- #address-cells: must be specified, must be <1>
+- #size-cells: must be specified, must be <1>
+- ranges: should be state like this giving a 1:1 address translation
+  for the subnodes
+
+The subnodes represents the two ethernet ports in this device.
+They are not independent of each other since they share resources
+in the parent node, and are thus children.
+
+Required subnodes:
+- port0: contains the resources for ethernet port 0
+- port1: contains the resources for ethernet port 1
+
+Required subnode properties:
+- compatible: must be "cortina,gemini-ethernet-port"
+- reg: must contain two register areas: the DMA/TOE memory and
+  the GMAC memory area of the port
+- interrupts: should contain the interrupt line of the port.
+  this is nominally a level interrupt active high.
+- resets: this must provide an SoC-integrated reset line for
+  the port.
+- clocks: this should contain a handle to the PCLK clock for
+  clocking the silicon in this port
+- clock-names: must be "PCLK"
+
+Optional subnode properties:
+- phy-mode: see ethernet.txt
+- phy-handle: see ethernet.txt
+
+Example:
+
+mdio-bus {
+	(...)
+	phy0: ethernet-phy@1 {
+		reg = <1>;
+		device_type = "ethernet-phy";
+	};
+	phy1: ethernet-phy@3 {
+		reg = <3>;
+		device_type = "ethernet-phy";
+	};
+};
+
+
+ethernet@60000000 {
+	compatible = "cortina,gemini-ethernet";
+	reg = <0x60000000 0x4000>, /* Global registers, queue */
+	      <0x60004000 0x2000>, /* V-bit */
+	      <0x60006000 0x2000>; /* A-bit */
+	syscon = <&syscon>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+
+	gmac0: ethernet-port@0 {
+		compatible = "cortina,gemini-ethernet-port";
+		reg = <0x60008000 0x2000>, /* Port 0 DMA/TOE */
+		      <0x6000a000 0x2000>; /* Port 0 GMAC */
+		interrupt-parent = <&intcon>;
+		interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
+		resets = <&syscon GEMINI_RESET_GMAC0>;
+		clocks = <&syscon GEMINI_CLK_GATE_GMAC0>;
+		clock-names = "PCLK";
+		phy-mode = "rgmii";
+		phy-handle = <&phy0>;
+	};
+
+	gmac1: ethernet-port@1 {
+		compatible = "cortina,gemini-ethernet-port";
+		reg = <0x6000c000 0x2000>, /* Port 1 DMA/TOE */
+		      <0x6000e000 0x2000>; /* Port 1 GMAC */
+		interrupt-parent = <&intcon>;
+		interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+		resets = <&syscon GEMINI_RESET_GMAC1>;
+		clocks = <&syscon GEMINI_CLK_GATE_GMAC1>;
+		clock-names = "PCLK";
+		phy-mode = "rgmii";
+		phy-handle = <&phy1>;
+	};
+};
-- 
2.14.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH v2 2/2] pinctrl: Allow indicating loss of pin states during low-power
From: Florian Fainelli @ 2017-12-10 23:38 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Tony Lindgren, linux-gpio, Rob Herring, Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Charles Keepax, Charles Keepax, Stephen Warren,
	Andy Shevchenko, Al Cooper, bcm-kernel-feedback-list
In-Reply-To: <CACRpkdbFHj9pM6R3azVPKgbQ-LMmitEdJHQ5BOEb7Vy=kpNk4A@mail.gmail.com>



On 12/02/2017 04:48 AM, Linus Walleij wrote:
> On Wed, Nov 29, 2017 at 6:37 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
>> On 11/29/2017 09:02 AM, Tony Lindgren wrote:
> 
>>> Hmm well typically a device driver that loses it's context just does
>>> save and restore of the registers in runtime PM suspend/resume
>>> as needed. In this case it would mean duplicating the state for
>>> potentially for hundreds of registers.. So using the existing
>>> state in the pinctrl subsystem totally makes sense for the pins.
>>>
>>> Florian do you have other reasons why this should be done in the
>>> pinctrl framework instead of the driver? Might be worth describing
>>> the reasoning in the patch descriptions :)
>>
>> The pinctrl provider driver that I am using is pinctrl-single, which has
>> proper suspend/resume callbacks but those are not causing any HW
>> programming to happen because of the (p->state == state) check, hence
>> this patch series.
> 
> So we are talking about these callbacks, correct?
> 
> #ifdef CONFIG_PM
> static int pinctrl_single_suspend(struct platform_device *pdev,
>                                         pm_message_t state)
> {
>         struct pcs_device *pcs;
> 
>         pcs = platform_get_drvdata(pdev);
>         if (!pcs)
>                 return -EINVAL;
> 
>         return pinctrl_force_sleep(pcs->pctl);
> }
> 
> static int pinctrl_single_resume(struct platform_device *pdev)
> {
>         struct pcs_device *pcs;
> 
>         pcs = platform_get_drvdata(pdev);
>         if (!pcs)
>                 return -EINVAL;
> 
>         return pinctrl_force_default(pcs->pctl);
> }
> #endif
> 
> Which falls through to this:
> 
> /**
>  * pinctrl_force_sleep() - turn a given controller device into sleep state
>  * @pctldev: pin controller device
>  */
> int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
> {
>         if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
>                 return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
>         return 0;
> }
> EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
> 
> /**
>  * pinctrl_force_default() - turn a given controller device into default state
>  * @pctldev: pin controller device
>  */
> int pinctrl_force_default(struct pinctrl_dev *pctldev)
> {
>         if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
>                 return pinctrl_select_state(pctldev->p, pctldev->hog_default);
>         return 0;
> }
> EXPORT_SYMBOL_GPL(pinctrl_force_default);
> 
> So am I right in assuming it is actually the hogs that is your biggest
> problem, and those are the states that get lost over suspend/resume
> that are especially problematic?
> 
> I.e. you don't have any problem with any non-hogged pinctrl
> handles, those are handled just fine in the suspend/resume
> paths of the client drivers?
> 
> If this is the case, it changes the problem scope slightly.
> 
> It is fair that functions named *force* should actually enforce
> programming a state.
> 
> So then I would suggest somethin else: break pinctrl_select_state()
> into two:
> 
> pinctrl_select_state() that works just like before, checking if
> (p->state == state) but which calls a static function
> pinctrl_select_state_commit() that commits the change unconditonally.
> Then alter pinctrl_force_sleep() and pinctrl_force_sleep() to call
> that function.
> 
> This should solve your problem without having to alter the semantics
> of pinctrl_select_state() for everyone.

This was exactly what I proposed initially here:

http://patchwork.ozlabs.org/patch/734326/

I really want to get this fixed, but I can't do that if we keep losing
the context of the discussion (pun intended) :).
-- 
Florian

^ permalink raw reply

* Re: [PATCH v4 3/5] staging: Introduce NVIDIA Tegra video decoder driver
From: Dmitry Osipenko @ 2017-12-11  0:12 UTC (permalink / raw)
  To: Nicolas Dufresne, Hans Verkuil, Thierry Reding, Jonathan Hunter,
	Greg Kroah-Hartman, Rob Herring, Mauro Carvalho Chehab,
	Stephen Warren, Vladimir Zapolskiy
  Cc: Dan Carpenter, linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Maxime Ripard,
	Giulio Benetti
In-Reply-To: <1512934179.4281.15.camel-dDhyB4GVkw9AFePFGvp55w@public.gmane.org>

On 10.12.2017 22:29, Nicolas Dufresne wrote:
> Le dimanche 10 décembre 2017 à 21:56 +0300, Dmitry Osipenko a écrit :
>>> I've CC-ed Maxime and Giulio as well: they are looking into adding support for
>>> the stateless allwinner codec based on this code as well. There may well be
>>> opportunities for you to work together, esp. on the userspace side. Note that
>>> Rockchip has the same issue, they too have a stateless HW codec.
>>
>> IIUC, we will have to define video decoder parameters in V4L API and then make a
>> V4L driver / userspace prototype (ffmpeg for example) that will use the requests
>> API for video decoding in order to upstream the requests API. Does it sound good?
> 
> Chromium/Chrome already have support for that type of decoder in their
> tree. In theory, it should just work.

Everything is possible, in theory ;)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v9 0/2] media: ov7740: Add a V4L2 sensor-level driver
From: Wenyou Yang @ 2017-12-11  1:31 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Nicolas Ferre,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Sakari Ailus, Jonathan Corbet,
	Hans Verkuil, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Linux Media Mailing List, Wenyou Yang

Add a Video4Linux2 sensor-level driver for the OmniVision OV7740
VGA camera image sensor.

Changes in v9:
 - Use the new SPDX ids.

Changes in v8:
 - As the registers are written at stream start, remove the written
   code from the set fmt function.

Changes in v7:
 - Add Acked-by tag.
 - Fix the wrong handle of default register configuration.
 - Add the missed assignment of ov7740->frmsize.

Changes in v6:
 - Remove unnecessary #include <linux/init>.
 - Remove unnecessary comments and extra newline.
 - Add const for some structures.
 - Add the check of the return value from regmap_write().
 - Simplify the calling of __v4l2_ctrl_handler_setup().
 - Add the default format initialization function.
 - Integrate the set_power() and enable/disable the clock into
   one function.

Changes in v5:
 - Squash the driver and MAINTAINERS entry patches to one.
 - Precede the driver patch with the bindings patch.

Changes in v4:
 - Assign 'val' a initial value to avoid warning: 'val' may be
   used uninitialized.
 - Rename REG_REG15 to avoid warning: "REG_REG15" redefined.

Changes in v3:
 - Explicitly document the "remote-endpoint" property.
 - Put the MAINTAINERS change to a separate patch.

Changes in v2:
 - Split off the bindings into a separate patch.
 - Add a new entry to the MAINTAINERS file.

Wenyou Yang (2):
  media: ov7740: Document device tree bindings
  media: i2c: Add the ov7740 image sensor driver

 .../devicetree/bindings/media/i2c/ov7740.txt       |   47 +
 MAINTAINERS                                        |    8 +
 drivers/media/i2c/Kconfig                          |    8 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/ov7740.c                         | 1216 ++++++++++++++++++++
 5 files changed, 1280 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov7740.txt
 create mode 100644 drivers/media/i2c/ov7740.c

-- 
2.15.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v9 1/2] media: ov7740: Document device tree bindings
From: Wenyou Yang @ 2017-12-11  1:31 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
  Cc: linux-kernel, Nicolas Ferre, devicetree, Sakari Ailus,
	Jonathan Corbet, Hans Verkuil, linux-arm-kernel,
	Linux Media Mailing List, Wenyou Yang
In-Reply-To: <20171211013146.2497-1-wenyou.yang@microchip.com>

Add the device tree binding documentation for the ov7740 sensor driver.

Signed-off-by: Wenyou Yang <wenyou.yang@microchip.com>
Acked-by: Rob Herring <robh@kernel.org>
---

Changes in v9: None
Changes in v8: None
Changes in v7:
 - Add Acked-by tag.

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
 - Explicitly document the "remote-endpoint" property.

Changes in v2: None

 .../devicetree/bindings/media/i2c/ov7740.txt       | 47 ++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov7740.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov7740.txt b/Documentation/devicetree/bindings/media/i2c/ov7740.txt
new file mode 100644
index 000000000000..af781c3a5f0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov7740.txt
@@ -0,0 +1,47 @@
+* Omnivision OV7740 CMOS image sensor
+
+The Omnivision OV7740 image sensor supports multiple output image
+size, such as VGA, and QVGA, CIF and any size smaller. It also
+supports the RAW RGB and YUV output formats.
+
+The common video interfaces bindings (see video-interfaces.txt) should
+be used to specify link to the image data receiver. The OV7740 device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Required Properties:
+- compatible:	"ovti,ov7740".
+- reg:		I2C slave address of the sensor.
+- clocks:	Reference to the xvclk input clock.
+- clock-names:	"xvclk".
+
+Optional Properties:
+- reset-gpios: Rreference to the GPIO connected to the reset_b pin,
+  if any. Active low with pull-ip resistor.
+- powerdown-gpios: Reference to the GPIO connected to the pwdn pin,
+  if any. Active high with pull-down resistor.
+
+Endpoint node mandatory properties:
+- remote-endpoint: A phandle to the bus receiver's endpoint node.
+
+Example:
+
+	i2c1: i2c@fc028000 {
+		ov7740: camera@21 {
+			compatible = "ovti,ov7740";
+			reg = <0x21>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_sensor_power &pinctrl_sensor_reset>;
+			clocks = <&isc>;
+			clock-names = "xvclk";
+			assigned-clocks = <&isc>;
+			assigned-clock-rates = <24000000>;
+			reset-gpios = <&pioA 43 GPIO_ACTIVE_LOW>;
+			powerdown-gpios = <&pioA 44 GPIO_ACTIVE_HIGH>;
+
+			port {
+				ov7740_0: endpoint {
+					remote-endpoint = <&isc_0>;
+				};
+			};
+		};
+	};
-- 
2.15.0

^ permalink raw reply related

* [PATCH v9 2/2] media: i2c: Add the ov7740 image sensor driver
From: Wenyou Yang @ 2017-12-11  1:31 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
  Cc: linux-kernel, Nicolas Ferre, devicetree, Sakari Ailus,
	Jonathan Corbet, Hans Verkuil, linux-arm-kernel,
	Linux Media Mailing List, Wenyou Yang, Songjun Wu
In-Reply-To: <20171211013146.2497-1-wenyou.yang@microchip.com>

The ov7740 (color) image sensor is a high performance VGA CMOS
image snesor, which supports for output formats: RAW RGB and YUV
and image sizes: VGA, and QVGA, CIF and any size smaller.

Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
Signed-off-by: Wenyou Yang <wenyou.yang@microchip.com>
---

Changes in v9:
 - Use the new SPDX ids.

Changes in v8:
 - As the registers are written at stream start, remove the written
   code from the set fmt function.

Changes in v7:
 - Fix the wrong handle of default register configuration.
 - Add the missed assignment of ov7740->frmsize.

Changes in v6:
 - Remove unnecessary #include <linux/init>.
 - Remove unnecessary comments and extra newline.
 - Add const for some structures.
 - Add the check of the return value from regmap_write().
 - Simplify the calling of __v4l2_ctrl_handler_setup().
 - Add the default format initialization function.
 - Integrate the set_power() and enable/disable the clock into
   one function.

Changes in v5:
 - Squash the driver and MAINTAINERS entry patches to one.
 - Precede the driver patch with the bindings patch.

Changes in v4:
 - Assign 'val' a initial value to avoid warning: 'val' may be
   used uninitialized.
 - Rename REG_REG15 to avoid warning: "REG_REG15" redefined.

Changes in v3:
 - Put the MAINTAINERS change to a separate patch.

Changes in v2:
 - Split off the bindings into a separate patch.
 - Add a new entry to the MAINTAINERS file.

 MAINTAINERS                |    8 +
 drivers/media/i2c/Kconfig  |    8 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/ov7740.c | 1216 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1233 insertions(+)
 create mode 100644 drivers/media/i2c/ov7740.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a52a66aa991..1de965009b13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10053,6 +10053,14 @@ S:	Maintained
 F:	drivers/media/i2c/ov7670.c
 F:	Documentation/devicetree/bindings/media/i2c/ov7670.txt
 
+OMNIVISION OV7740 SENSOR DRIVER
+M:	Wenyou Yang <wenyou.yang@microchip.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/ov7740.c
+F:	Documentation/devicetree/bindings/media/i2c/ov7740.txt
+
 ONENAND FLASH DRIVER
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	linux-mtd@lists.infradead.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index cb5d7ff82915..00b1c4c031d4 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -665,6 +665,14 @@ config VIDEO_OV7670
 	  OV7670 VGA camera.  It currently only works with the M88ALP01
 	  controller.
 
+config VIDEO_OV7740
+	tristate "OmniVision OV7740 sensor support"
+	depends on I2C && VIDEO_V4L2
+	depends on MEDIA_CAMERA_SUPPORT
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV7740 VGA camera sensor.
+
 config VIDEO_OV9650
 	tristate "OmniVision OV9650/OV9652 sensor support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 548a9efce966..9b19ec7fcaf4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
 obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
 obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
 obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
 obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
 obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
 obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
new file mode 100644
index 000000000000..0308ba437bbb
--- /dev/null
+++ b/drivers/media/i2c/ov7740.c
@@ -0,0 +1,1216 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Microchip Corporation.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+
+#define REG_OUTSIZE_LSB 0x34
+
+/* OV7740 register tables */
+#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
+#define REG_BGAIN	0x01	/* blue gain */
+#define REG_RGAIN	0x02	/* red gain */
+#define REG_GGAIN	0x03	/* green gain */
+#define REG_REG04	0x04	/* analog setting, dont change*/
+#define REG_BAVG	0x05	/* b channel average */
+#define REG_GAVG	0x06	/* g channel average */
+#define REG_RAVG	0x07	/* r channel average */
+
+#define REG_REG0C	0x0C	/* filp enable */
+#define REG0C_IMG_FLIP		0x80
+#define REG0C_IMG_MIRROR	0x40
+
+#define REG_REG0E	0x0E	/* blc line */
+#define REG_HAEC	0x0F	/* auto exposure cntrl */
+#define REG_AEC		0x10	/* auto exposure cntrl */
+
+#define REG_CLK		0x11	/* Clock control */
+#define REG_REG55	0x55	/* Clock PLL DIV/PreDiv */
+
+#define REG_REG12	0x12
+
+#define REG_REG13	0x13	/* auto/manual AGC, AEC, Write Balance*/
+#define REG13_AEC_EN	0x01
+#define REG13_AGC_EN	0x04
+
+#define REG_REG14	0x14
+#define REG_CTRL15	0x15
+#define REG15_GAIN_MSB	0x03
+
+#define REG_REG16	0x16
+
+#define REG_MIDH	0x1C	/* manufacture id byte */
+#define REG_MIDL	0x1D	/* manufacture id byre */
+#define REG_PIDH	0x0A	/* Product ID MSB */
+#define REG_PIDL	0x0B	/* Product ID LSB */
+
+#define REG_84		0x84	/* lots of stuff */
+#define REG_REG38	0x38	/* sub-addr */
+
+#define REG_AHSTART	0x17	/* Horiz start high bits */
+#define REG_AHSIZE	0x18
+#define REG_AVSTART	0x19	/* Vert start high bits */
+#define REG_AVSIZE	0x1A
+#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
+
+#define REG_HOUTSIZE	0x31
+#define REG_VOUTSIZE	0x32
+#define REG_HVSIZEOFF	0x33
+#define REG_REG34	0x34	/* DSP output size H/V LSB*/
+
+#define REG_ISP_CTRL00	0x80
+#define ISPCTRL00_AWB_EN	0x10
+#define ISPCTRL00_AWB_GAIN_EN	0x04
+
+#define	REG_YGAIN	0xE2	/* ygain for contrast control */
+
+#define	REG_YBRIGHT	  0xE3
+#define	REG_SGNSET	  0xE4
+#define	SGNSET_YBRIGHT_MASK	  0x08
+
+#define REG_USAT	0xDD
+#define REG_VSAT	0xDE
+
+
+struct ov7740 {
+	struct v4l2_subdev subdev;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	struct media_pad pad;
+#endif
+	struct v4l2_mbus_framefmt format;
+	const struct ov7740_pixfmt *fmt;  /* Current format */
+	const struct ov7740_framesize *frmsize;
+	struct regmap *regmap;
+	struct clk *xvclk;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct {
+		/* gain cluster */
+		struct v4l2_ctrl *auto_gain;
+		struct v4l2_ctrl *gain;
+	};
+	struct {
+		struct v4l2_ctrl *auto_wb;
+		struct v4l2_ctrl *blue_balance;
+		struct v4l2_ctrl *red_balance;
+	};
+	struct {
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+	struct {
+		/* exposure cluster */
+		struct v4l2_ctrl *auto_exposure;
+		struct v4l2_ctrl *exposure;
+	};
+	struct {
+		/* saturation/hue cluster */
+		struct v4l2_ctrl *saturation;
+		struct v4l2_ctrl *hue;
+	};
+	struct v4l2_ctrl *brightness;
+	struct v4l2_ctrl *contrast;
+
+	struct mutex mutex;	/* To serialize asynchronus callbacks */
+	bool streaming;		/* Streaming on/off */
+
+	struct gpio_desc *resetb_gpio;
+	struct gpio_desc *pwdn_gpio;
+};
+
+struct ov7740_pixfmt {
+	u32 mbus_code;
+	enum v4l2_colorspace colorspace;
+	const struct reg_sequence *regs;
+	u32 reg_num;
+};
+
+struct ov7740_framesize {
+	u16 width;
+	u16 height;
+	const struct reg_sequence *regs;
+	u32 reg_num;
+};
+
+static const struct reg_sequence ov7740_vga[] = {
+	{0x55, 0x40},
+	{0x11, 0x02},
+
+	{0xd5, 0x10},
+	{0x0c, 0x12},
+	{0x0d, 0x34},
+	{0x17, 0x25},
+	{0x18, 0xa0},
+	{0x19, 0x03},
+	{0x1a, 0xf0},
+	{0x1b, 0x89},
+	{0x22, 0x03},
+	{0x29, 0x18},
+	{0x2b, 0xf8},
+	{0x2c, 0x01},
+	{REG_HOUTSIZE, 0xa0},
+	{REG_VOUTSIZE, 0xf0},
+	{0x33, 0xc4},
+	{REG_OUTSIZE_LSB, 0x0},
+	{0x35, 0x05},
+	{0x04, 0x60},
+	{0x27, 0x80},
+	{0x3d, 0x0f},
+	{0x3e, 0x80},
+	{0x3f, 0x40},
+	{0x40, 0x7f},
+	{0x41, 0x6a},
+	{0x42, 0x29},
+	{0x44, 0x22},
+	{0x45, 0x41},
+	{0x47, 0x02},
+	{0x49, 0x64},
+	{0x4a, 0xa1},
+	{0x4b, 0x40},
+	{0x4c, 0x1a},
+	{0x4d, 0x50},
+	{0x4e, 0x13},
+	{0x64, 0x00},
+	{0x67, 0x88},
+	{0x68, 0x1a},
+
+	{0x14, 0x28},
+	{0x24, 0x3c},
+	{0x25, 0x30},
+	{0x26, 0x72},
+	{0x50, 0x97},
+	{0x51, 0x1f},
+	{0x52, 0x00},
+	{0x53, 0x00},
+	{0x20, 0x00},
+	{0x21, 0xcf},
+	{0x50, 0x4b},
+	{0x38, 0x14},
+	{0xe9, 0x00},
+	{0x56, 0x55},
+	{0x57, 0xff},
+	{0x58, 0xff},
+	{0x59, 0xff},
+	{0x5f, 0x04},
+	{0xec, 0x00},
+	{0x13, 0xff},
+
+	{0x81, 0x3f},
+	{0x82, 0x32},
+	{0x38, 0x11},
+	{0x84, 0x70},
+	{0x85, 0x00},
+	{0x86, 0x03},
+	{0x87, 0x01},
+	{0x88, 0x05},
+	{0x89, 0x30},
+	{0x8d, 0x30},
+	{0x8f, 0x85},
+	{0x93, 0x30},
+	{0x95, 0x85},
+	{0x99, 0x30},
+	{0x9b, 0x85},
+
+	{0x9c, 0x08},
+	{0x9d, 0x12},
+	{0x9e, 0x23},
+	{0x9f, 0x45},
+	{0xa0, 0x55},
+	{0xa1, 0x64},
+	{0xa2, 0x72},
+	{0xa3, 0x7f},
+	{0xa4, 0x8b},
+	{0xa5, 0x95},
+	{0xa6, 0xa7},
+	{0xa7, 0xb5},
+	{0xa8, 0xcb},
+	{0xa9, 0xdd},
+	{0xaa, 0xec},
+	{0xab, 0x1a},
+
+	{0xce, 0x78},
+	{0xcf, 0x6e},
+	{0xd0, 0x0a},
+	{0xd1, 0x0c},
+	{0xd2, 0x84},
+	{0xd3, 0x90},
+	{0xd4, 0x1e},
+
+	{0x5a, 0x24},
+	{0x5b, 0x1f},
+	{0x5c, 0x88},
+	{0x5d, 0x60},
+
+	{0xac, 0x6e},
+	{0xbe, 0xff},
+	{0xbf, 0x00},
+
+	{0x0f, 0x1d},
+	{0x0f, 0x1f},
+};
+
+static const struct ov7740_framesize ov7740_framesizes[] = {
+	{
+		.width		= VGA_WIDTH,
+		.height		= VGA_HEIGHT,
+		.regs		= ov7740_vga,
+		.reg_num	= ARRAY_SIZE(ov7740_vga),
+	},
+};
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov7740_get_register(struct v4l2_subdev *sd,
+			       struct v4l2_dbg_register *reg)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	struct regmap *regmap = ov7740->regmap;
+	unsigned int val = 0;
+	int ret;
+
+	ret = regmap_read(regmap, reg->reg & 0xff, &val);
+	reg->val = val;
+	reg->size = 1;
+
+	return 0;
+}
+
+static int ov7740_set_register(struct v4l2_subdev *sd,
+			       const struct v4l2_dbg_register *reg)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	struct regmap *regmap = ov7740->regmap;
+
+	regmap_write(regmap, reg->reg & 0xff, reg->val & 0xff);
+
+	return 0;
+}
+#endif
+
+static int ov7740_set_power(struct ov7740 *ov7740, int on)
+{
+	int ret;
+
+	if (on) {
+		ret = clk_prepare_enable(ov7740->xvclk);
+		if (ret)
+			return ret;
+
+		if (ov7740->pwdn_gpio)
+			gpiod_direction_output(ov7740->pwdn_gpio, 0);
+
+		if (ov7740->resetb_gpio) {
+			gpiod_set_value(ov7740->resetb_gpio, 1);
+			usleep_range(500, 1000);
+			gpiod_set_value(ov7740->resetb_gpio, 0);
+			usleep_range(3000, 5000);
+		}
+	} else {
+		clk_disable_unprepare(ov7740->xvclk);
+
+		if (ov7740->pwdn_gpio)
+			gpiod_direction_output(ov7740->pwdn_gpio, 0);
+	}
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = {
+	.log_status = v4l2_ctrl_subdev_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ov7740_get_register,
+	.s_register = ov7740_set_register,
+#endif
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static int ov7740_set_white_balance(struct ov7740 *ov7740, int awb)
+{
+	struct regmap *regmap = ov7740->regmap;
+	unsigned int value;
+	int ret;
+
+	ret = regmap_read(regmap, REG_ISP_CTRL00, &value);
+	if (!ret) {
+		if (awb)
+			value |= (ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
+		else
+			value &= ~(ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
+		ret = regmap_write(regmap, REG_ISP_CTRL00, value);
+		if (ret)
+			return ret;
+	}
+
+	if (!awb) {
+		ret = regmap_write(regmap, REG_BGAIN,
+				   ov7740->blue_balance->val);
+		if (ret)
+			return ret;
+
+		ret = regmap_write(regmap, REG_RGAIN, ov7740->red_balance->val);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ov7740_set_saturation(struct regmap *regmap, int value)
+{
+	int ret;
+
+	ret = regmap_write(regmap, REG_USAT, (unsigned char)value);
+	if (ret)
+		return ret;
+
+	return regmap_write(regmap, REG_VSAT, (unsigned char)value);
+}
+
+static int ov7740_set_gain(struct regmap *regmap, int value)
+{
+	int ret;
+
+	ret = regmap_write(regmap, REG_GAIN, value & 0xff);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, REG_CTRL15,
+				 REG15_GAIN_MSB, (value >> 8) & 0x3);
+	if (!ret)
+		ret = regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
+
+	return ret;
+}
+
+static int ov7740_set_autogain(struct regmap *regmap, int value)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(regmap, REG_REG13, &reg);
+	if (ret)
+		return ret;
+	if (value)
+		reg |= REG13_AGC_EN;
+	else
+		reg &= ~REG13_AGC_EN;
+	return regmap_write(regmap, REG_REG13, reg);
+}
+
+static int ov7740_set_brightness(struct regmap *regmap, int value)
+{
+	/* Turn off AEC/AGC */
+	regmap_update_bits(regmap, REG_REG13, REG13_AEC_EN, 0);
+	regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
+
+	if (value >= 0) {
+		regmap_write(regmap, REG_YBRIGHT, (unsigned char)value);
+		regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 0);
+	} else{
+		regmap_write(regmap, REG_YBRIGHT, (unsigned char)(-value));
+		regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 1);
+	}
+
+	return 0;
+}
+
+static int ov7740_set_contrast(struct regmap *regmap, int value)
+{
+	return regmap_write(regmap, REG_YGAIN, (unsigned char)value);
+}
+
+static int ov7740_get_gain(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl)
+{
+	struct regmap *regmap = ov7740->regmap;
+	unsigned int value0, value1;
+	int ret;
+
+	if (!ctrl->val)
+		return 0;
+
+	ret = regmap_read(regmap, REG_GAIN, &value0);
+	if (ret)
+		return ret;
+	ret = regmap_read(regmap, REG_CTRL15, &value1);
+	if (ret)
+		return ret;
+
+	ov7740->gain->val = (value1 << 8) | (value0 & 0xff);
+
+	return 0;
+}
+
+static int ov7740_set_exp(struct regmap *regmap, int value)
+{
+	int ret;
+
+	/* Turn off AEC/AGC */
+	ret = regmap_update_bits(regmap, REG_REG13,
+				 REG13_AEC_EN | REG13_AGC_EN, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(regmap, REG_AEC, (unsigned char)value);
+	if (ret)
+		return ret;
+
+	return regmap_write(regmap, REG_HAEC, (unsigned char)(value >> 8));
+}
+
+static int ov7740_set_autoexp(struct regmap *regmap,
+			      enum v4l2_exposure_auto_type value)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(regmap, REG_REG13, &reg);
+	if (!ret) {
+		if (value == V4L2_EXPOSURE_AUTO)
+			reg |= (REG13_AEC_EN | REG13_AGC_EN);
+		else
+			reg &= ~(REG13_AEC_EN | REG13_AGC_EN);
+		ret = regmap_write(regmap, REG_REG13, reg);
+	}
+
+	return ret;
+}
+
+
+static int ov7740_get_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov7740 *ov7740 = container_of(ctrl->handler,
+					     struct ov7740, ctrl_handler);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		ret = ov7740_get_gain(ov7740, ctrl);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov7740 *ov7740 = container_of(ctrl->handler,
+					     struct ov7740, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
+	struct regmap *regmap = ov7740->regmap;
+	int ret;
+	u8 val = 0;
+
+	if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = ov7740_set_white_balance(ov7740, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		ret = ov7740_set_saturation(regmap, ctrl->val);
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ret = ov7740_set_brightness(regmap, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		ret = ov7740_set_contrast(regmap, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = regmap_update_bits(regmap, REG_REG0C,
+					 REG0C_IMG_FLIP, val);
+		break;
+	case V4L2_CID_HFLIP:
+		val = ctrl->val ? REG0C_IMG_MIRROR : 0x00;
+		ret = regmap_update_bits(regmap, REG_REG0C,
+					 REG0C_IMG_MIRROR, val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (!ctrl->val)
+			return ov7740_set_gain(regmap, ov7740->gain->val);
+
+		ret = ov7740_set_autogain(regmap, ctrl->val);
+		break;
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (ctrl->val == V4L2_EXPOSURE_MANUAL)
+			return ov7740_set_exp(regmap, ov7740->exposure->val);
+
+		ret = ov7740_set_autoexp(regmap, ctrl->val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov7740_ctrl_ops = {
+	.g_volatile_ctrl = ov7740_get_volatile_ctrl,
+	.s_ctrl = ov7740_set_ctrl,
+};
+
+static int ov7740_start_streaming(struct ov7740 *ov7740)
+{
+	int ret;
+
+	if (ov7740->fmt) {
+		ret = regmap_multi_reg_write(ov7740->regmap,
+					     ov7740->fmt->regs,
+					     ov7740->fmt->reg_num);
+		if (ret)
+			return ret;
+	}
+
+	if (ov7740->frmsize) {
+		ret = regmap_multi_reg_write(ov7740->regmap,
+					     ov7740->frmsize->regs,
+					     ov7740->frmsize->reg_num);
+		if (ret)
+			return ret;
+	}
+
+	return __v4l2_ctrl_handler_setup(ov7740->subdev.ctrl_handler);
+}
+
+static int ov7740_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	mutex_lock(&ov7740->mutex);
+	if (ov7740->streaming == enable) {
+		mutex_unlock(&ov7740->mutex);
+		return 0;
+	}
+
+	if (enable) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto err_unlock;
+		}
+
+		ret = ov7740_start_streaming(ov7740);
+		if (ret)
+			goto err_rpm_put;
+	} else {
+		pm_runtime_put(&client->dev);
+	}
+
+	ov7740->streaming = enable;
+
+	mutex_unlock(&ov7740->mutex);
+	return ret;
+
+err_rpm_put:
+	pm_runtime_put(&client->dev);
+err_unlock:
+	mutex_unlock(&ov7740->mutex);
+	return ret;
+}
+
+static int ov7740_get_parm(struct v4l2_subdev *sd,
+			   struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(cp, 0, sizeof(struct v4l2_captureparm));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+
+	tpf->numerator = 1;
+	tpf->denominator = 60;
+
+	return 0;
+}
+
+static int ov7740_set_parm(struct v4l2_subdev *sd,
+			   struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+
+	tpf->numerator = 1;
+	tpf->denominator = 60;
+
+	return 0;
+}
+
+static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = {
+	.s_stream = ov7740_set_stream,
+	.s_parm = ov7740_set_parm,
+	.g_parm = ov7740_get_parm,
+};
+
+static const struct reg_sequence ov7740_format_yuyv[] = {
+	{0x12, 0x00},
+	{0x36, 0x3f},
+	{0x80, 0x7f},
+	{0x83, 0x01},
+};
+
+static const struct reg_sequence ov7740_format_bggr8[] = {
+	{0x36, 0x2f},
+	{0x80, 0x01},
+	{0x83, 0x04},
+};
+
+static const struct ov7740_pixfmt ov7740_formats[] = {
+	{
+		.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.regs = ov7740_format_yuyv,
+		.reg_num = ARRAY_SIZE(ov7740_format_yuyv),
+	},
+	{
+		.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.regs = ov7740_format_bggr8,
+		.reg_num = ARRAY_SIZE(ov7740_format_bggr8),
+	}
+};
+#define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats)
+
+static int ov7740_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index >= N_OV7740_FMTS)
+		return -EINVAL;
+
+	code->code = ov7740_formats[code->index].mbus_code;
+
+	return 0;
+}
+
+static int ov7740_enum_frame_interval(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_frame_interval_enum *fie)
+{
+	if (fie->pad)
+		return -EINVAL;
+
+	if (fie->index >= 1)
+		return -EINVAL;
+
+	if ((fie->width != VGA_WIDTH) || (fie->height != VGA_HEIGHT))
+		return -EINVAL;
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = 60;
+
+	return 0;
+}
+
+static int ov7740_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->pad)
+		return -EINVAL;
+
+	if (fse->index > 0)
+		return -EINVAL;
+
+	fse->min_width = fse->max_width = VGA_WIDTH;
+	fse->min_height = fse->max_height = VGA_HEIGHT;
+
+	return 0;
+}
+
+static int ov7740_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   const struct ov7740_pixfmt **ret_fmt,
+				   const struct ov7740_framesize **ret_frmsize)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	const struct ov7740_framesize *fsize = &ov7740_framesizes[0];
+	int index, i;
+
+	for (index = 0; index < N_OV7740_FMTS; index++) {
+		if (ov7740_formats[index].mbus_code == fmt->code)
+			break;
+	}
+	if (index >= N_OV7740_FMTS) {
+		/* default to first format */
+		index = 0;
+		fmt->code = ov7740_formats[0].mbus_code;
+	}
+	if (ret_fmt != NULL)
+		*ret_fmt = ov7740_formats + index;
+
+	for (i = 0; i < ARRAY_SIZE(ov7740_framesizes); i++) {
+		if ((fsize->width >= fmt->width) &&
+		    (fsize->height >= fmt->height)) {
+			fmt->width = fsize->width;
+			fmt->height = fsize->height;
+			break;
+		}
+
+		fsize++;
+	}
+
+	if (ret_frmsize != NULL)
+		*ret_frmsize = fsize;
+
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->colorspace = ov7740_formats[index].colorspace;
+
+	ov7740->format = *fmt;
+
+	return 0;
+}
+
+static int ov7740_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	const struct ov7740_pixfmt *ovfmt;
+	const struct ov7740_framesize *fsize;
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	struct v4l2_mbus_framefmt *mbus_fmt;
+#endif
+	int ret;
+
+	mutex_lock(&ov7740->mutex);
+	if (format->pad) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL);
+		if (ret)
+			goto error;
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+		*mbus_fmt = format->format;
+
+		mutex_unlock(&ov7740->mutex);
+		return 0;
+#else
+		ret = -ENOTTY;
+		goto error;
+#endif
+	}
+
+	ret = ov7740_try_fmt_internal(sd, &format->format, &ovfmt, &fsize);
+	if (ret)
+		goto error;
+
+	ov7740->fmt = ovfmt;
+	ov7740->frmsize = fsize;
+
+	mutex_unlock(&ov7740->mutex);
+	return 0;
+
+error:
+	mutex_unlock(&ov7740->mutex);
+	return ret;
+}
+
+static int ov7740_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	struct v4l2_mbus_framefmt *mbus_fmt;
+#endif
+	int ret = 0;
+
+	mutex_lock(&ov7740->mutex);
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+		format->format = *mbus_fmt;
+		ret = 0;
+#else
+		ret = -ENOTTY;
+#endif
+	} else {
+		format->format = ov7740->format;
+	}
+	mutex_unlock(&ov7740->mutex);
+
+	return ret;
+}
+
+static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = {
+	.enum_frame_interval = ov7740_enum_frame_interval,
+	.enum_frame_size = ov7740_enum_frame_size,
+	.enum_mbus_code = ov7740_enum_mbus_code,
+	.get_fmt = ov7740_get_fmt,
+	.set_fmt = ov7740_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov7740_subdev_ops = {
+	.core	= &ov7740_subdev_core_ops,
+	.video	= &ov7740_subdev_video_ops,
+	.pad	= &ov7740_subdev_pad_ops,
+};
+
+static void ov7740_get_default_format(struct v4l2_subdev *sd,
+				      struct v4l2_mbus_framefmt *format)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+	format->width = ov7740->frmsize->width;
+	format->height = ov7740->frmsize->height;
+	format->colorspace = ov7740->fmt->colorspace;
+	format->code = ov7740->fmt->mbus_code;
+	format->field = V4L2_FIELD_NONE;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+	struct v4l2_mbus_framefmt *format =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+	mutex_lock(&ov7740->mutex);
+	ov7740_get_default_format(sd, format);
+	mutex_unlock(&ov7740->mutex);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = {
+	.open = ov7740_open,
+};
+#endif
+
+static int ov7740_probe_dt(struct i2c_client *client,
+			   struct ov7740 *ov7740)
+{
+	ov7740->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+			GPIOD_OUT_HIGH);
+	if (IS_ERR(ov7740->resetb_gpio)) {
+		dev_info(&client->dev, "can't get %s GPIO\n", "reset");
+		return PTR_ERR(ov7740->resetb_gpio);
+	}
+
+	ov7740->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+			GPIOD_OUT_LOW);
+	if (IS_ERR(ov7740->pwdn_gpio)) {
+		dev_info(&client->dev, "can't get %s GPIO\n", "powerdown");
+		return PTR_ERR(ov7740->pwdn_gpio);
+	}
+
+	return 0;
+}
+
+static int ov7740_detect(struct ov7740 *ov7740)
+{
+	struct regmap *regmap = ov7740->regmap;
+	unsigned int midh, midl, pidh, pidl;
+	int ret;
+
+	ret = regmap_read(regmap, REG_MIDH, &midh);
+	if (ret)
+		return ret;
+	if (midh != 0x7f)
+		return -ENODEV;
+
+	ret = regmap_read(regmap, REG_MIDL, &midl);
+	if (ret)
+		return ret;
+	if (midl != 0xa2)
+		return -ENODEV;
+
+	ret = regmap_read(regmap, REG_PIDH, &pidh);
+	if (ret)
+		return ret;
+	if (pidh != 0x77)
+		return -ENODEV;
+
+	ret = regmap_read(regmap, REG_PIDL, &pidl);
+	if (ret)
+		return ret;
+	if ((pidl != 0x40) && (pidl != 0x41) && (pidl != 0x42))
+		return -ENODEV;
+
+	return 0;
+}
+
+static int ov7740_init_controls(struct ov7740 *ov7740)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
+	struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler;
+	int ret;
+
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2);
+	if (ret < 0)
+		return ret;
+
+	ctrl_hdlr->lock = &ov7740->mutex;
+	ov7740->auto_wb = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					  V4L2_CID_AUTO_WHITE_BALANCE,
+					  0, 1, 1, 1);
+	ov7740->blue_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					       V4L2_CID_BLUE_BALANCE,
+					       0, 0xff, 1, 0x80);
+	ov7740->red_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					      V4L2_CID_RED_BALANCE,
+					      0, 0xff, 1, 0x80);
+
+	ov7740->brightness = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					     V4L2_CID_BRIGHTNESS,
+					     -255, 255, 1, 0);
+	ov7740->contrast = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					   V4L2_CID_CONTRAST,
+					   0, 127, 1, 0x20);
+	ov7740->saturation = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 256, 1, 0x80);
+	ov7740->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					V4L2_CID_HFLIP, 0, 1, 1, 0);
+	ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+				       V4L2_CID_GAIN, 0, 1023, 1, 500);
+	ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					    V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+					   V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
+	ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr,
+					&ov7740_ctrl_ops,
+					V4L2_CID_EXPOSURE_AUTO,
+					V4L2_EXPOSURE_MANUAL, 0,
+					V4L2_EXPOSURE_AUTO);
+
+	ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+	v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false);
+	v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true);
+	v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure,
+			       V4L2_EXPOSURE_MANUAL, false);
+	v4l2_ctrl_cluster(2, &ov7740->hflip);
+
+	ret = v4l2_ctrl_handler_setup(ctrl_hdlr);
+	if (ret) {
+		dev_err(&client->dev, "%s control init failed (%d)\n",
+			__func__, ret);
+		goto error;
+	}
+
+	ov7740->subdev.ctrl_handler = ctrl_hdlr;
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+	mutex_destroy(&ov7740->mutex);
+	return ret;
+}
+
+static void ov7740_free_controls(struct ov7740 *ov7740)
+{
+	v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+	mutex_destroy(&ov7740->mutex);
+}
+
+#define OV7740_MAX_REGISTER     0xff
+static const struct regmap_config ov7740_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.max_register	= OV7740_MAX_REGISTER,
+};
+
+static int ov7740_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct ov7740 *ov7740;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev,
+			"OV7740: I2C-Adapter doesn't support SMBUS\n");
+		return -EIO;
+	}
+
+	ov7740 = devm_kzalloc(&client->dev, sizeof(*ov7740), GFP_KERNEL);
+	if (!ov7740)
+		return -ENOMEM;
+
+	ov7740->xvclk = devm_clk_get(&client->dev, "xvclk");
+	if (IS_ERR(ov7740->xvclk)) {
+		ret = PTR_ERR(ov7740->xvclk);
+		dev_err(&client->dev,
+			"OV7740: fail to get xvclk: %d\n", ret);
+		return ret;
+	}
+
+	ret = ov7740_probe_dt(client, ov7740);
+	if (ret)
+		return ret;
+
+	ov7740->regmap = devm_regmap_init_i2c(client, &ov7740_regmap_config);
+	if (IS_ERR(ov7740->regmap)) {
+		ret = PTR_ERR(ov7740->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	sd = &ov7740->subdev;
+	client->flags |= I2C_CLIENT_SCCB;
+	v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops);
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &ov7740_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	ov7740->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad);
+	if (ret)
+		return ret;
+#endif
+
+	ret = ov7740_set_power(ov7740, 1);
+	if (ret)
+		return ret;
+
+	ret = ov7740_detect(ov7740);
+	if (ret)
+		goto error_detect;
+
+	mutex_init(&ov7740->mutex);
+
+	ret = ov7740_init_controls(ov7740);
+	if (ret)
+		goto error_init_controls;
+
+	v4l_info(client, "chip found @ 0x%02x (%s)\n",
+			client->addr << 1, client->adapter->name);
+
+	ov7740->fmt = &ov7740_formats[0];
+	ov7740->frmsize = &ov7740_framesizes[0];
+
+	ov7740_get_default_format(sd, &ov7740->format);
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret)
+		goto error_async_register;
+
+	pm_runtime_set_active(&client->dev);
+	pm_runtime_enable(&client->dev);
+	pm_runtime_idle(&client->dev);
+
+	return 0;
+
+error_async_register:
+	v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+error_init_controls:
+	ov7740_free_controls(ov7740);
+error_detect:
+	ov7740_set_power(ov7740, 0);
+	media_entity_cleanup(&ov7740->subdev.entity);
+
+	return ret;
+}
+
+static int ov7740_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+	mutex_destroy(&ov7740->mutex);
+	v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&ov7740->subdev.entity);
+#endif
+	v4l2_async_unregister_subdev(sd);
+	ov7740_free_controls(ov7740);
+
+	pm_runtime_get_sync(&client->dev);
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
+	ov7740_set_power(ov7740, 0);
+	return 0;
+}
+
+static int __maybe_unused ov7740_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+	ov7740_set_power(ov7740, 0);
+
+	return 0;
+}
+
+static int __maybe_unused ov7740_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+	return ov7740_set_power(ov7740, 1);
+}
+
+static const struct i2c_device_id ov7740_id[] = {
+	{ "ov7740", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ov7740_id);
+
+static const struct dev_pm_ops ov7740_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov7740_runtime_suspend, ov7740_runtime_resume, NULL)
+};
+
+static const struct of_device_id ov7740_of_match[] = {
+	{.compatible = "ovti,ov7740", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov7740_of_match);
+
+static struct i2c_driver ov7740_i2c_driver = {
+	.driver = {
+		.name = "ov7740",
+		.pm = &ov7740_pm_ops,
+		.of_match_table = of_match_ptr(ov7740_of_match),
+	},
+	.probe    = ov7740_probe,
+	.remove   = ov7740_remove,
+	.id_table = ov7740_id,
+};
+module_i2c_driver(ov7740_i2c_driver);
+
+MODULE_DESCRIPTION("The V4L2 driver for Omnivision 7740 sensor");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.15.0

^ permalink raw reply related

* Re: [PATCH v8 2/2] media: i2c: Add the ov7740 image sensor driver
From: Yang, Wenyou @ 2017-12-11  1:33 UTC (permalink / raw)
  To: Philippe Ombredanne
  Cc: Mauro Carvalho Chehab, Rob Herring, Mark Rutland, LKML,
	Nicolas Ferre,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Sakari Ailus, Jonathan Corbet, Hans Verkuil,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	Linux Media Mailing List, Songjun Wu
In-Reply-To: <CAOFm3uFASPpuMtGSetcYME0pQEmuzoLqY=Yhv5aaFu5AzJwaew-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Hi Philippe,


On 2017/12/8 21:14, Philippe Ombredanne wrote:
> Wenyou,
>
> On Fri, Dec 8, 2017 at 2:55 AM, Wenyou Yang <wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org> wrote:
>> The ov7740 (color) image sensor is a high performance VGA CMOS
>> image snesor, which supports for output formats: RAW RGB and YUV
>> and image sizes: VGA, and QVGA, CIF and any size smaller.
>>
>> Signed-off-by: Songjun Wu <songjun.wu-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
>> Signed-off-by: Wenyou Yang <wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> []
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov7740.c
>> @@ -0,0 +1,1226 @@
>> +/*
>> + * Copyright (c) 2017 Microchip Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License version
>> + * 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + */
> Have you considered using the new SPDX ids instead of this fine legalese?
> e.g.:
>
> // SPDX-License-Identifier: GPL-2.0
> // Copyright (c) 2017 Microchip Corporation.
>
> Short and neat! Check also Thomas doc patches and Linus comments on
> why he prefers the C++ comment style for these.
Thank you for your suggestion and information.


Best Regards,
Wenyou Yang
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 4/4] PCI: dwc: pci-dra7xx: Fix legacy IRQ handling
From: Vignesh R @ 2017-12-11  4:42 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Bjorn Helgaas, Rob Herring, Tony Lindgren, KISHON VIJAY ABRAHAM,
	linux-omap-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-pci-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <20171208183523.GB1885-4tUPXFaYRHv6sAKXYmQ0tx/iLCjYCKR+VpNB7YpNyf8@public.gmane.org>



On Saturday 09 December 2017 12:05 AM, Lorenzo Pieralisi wrote:
> On Fri, Dec 01, 2017 at 11:43:11AM +0530, Vignesh R wrote:
>> Legacy INTD IRQ handling is broken on dra7xx due to fact that driver
>> uses hwirq in range of 1-4 for INTA, INTD whereas IRQ domain is of size
>> 4 which is numbered 0-3. Therefore when INTD IRQ line is used with
>> pci-dra7xx driver following warning is seen:
>>
>>        WARNING: CPU: 0 PID: 1 at kernel/irq/irqdomain.c:342 irq_domain_associate+0x12c/0x1c4
>>        error: hwirq 0x4 is too large for dummy
>>
>> Fix this by using pci_irqd_intx_xlate() helper to translate the INTx 1-4
>> range into the 0-3 as done in other PCIe drivers.
>>
>> Also, iterate over all the INTx bits and call their respective IRQ
>> handlers before clearing the status register.
> 
> It seems to me that you are fixing two bugs with one patch and therefore
> I would ask you to split it in two or explain to me why we should
> consider lumping them together.
> 

Ok, I will split the patch into two in v2.


-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH 00/20] ARM: dts: aspeed: updates and new machines
From: Joel Stanley @ 2017-12-11  5:06 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Arnd Bergmann, Andrew Jeffery,
	Patrick Venture, Xo Wang, Lei YU
  Cc: Cédric Le Goater, Benjamin Herrenschmidt, Jeremy Kerr,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-aspeed-uLR06cmDAlY/bJ5BZ2RsiQ

This series of device tree patches for the ASPEED BMC machines
moves all systems to use the soon to be merged clk driver, and
updates machines to use all of the drivers we have upstream.

In addition it adds three new OpenBMC systems that have been developed
in the OpenBMC kernel tree over the past year: two Power9 OpenPower
systems, and a port by Google to a Quanta x86 server.

I have boot tested these on Romulus and Palmetto, as well as boot tested
all device trees in Qemu.

Please review the boards you are familiar with. I will merge these in to
the ASPEED ARM SoC tree for inclusion in 4.16.

Andrew Jeffery (1):
  ARM: dts: aspeed: Add LPC and child devices

Joel Stanley (17):
  dt-bindings: clock: Add ASPEED constants
  dt-bindings: gpio: Add ASPEED constants
  ARM: dts: aspeed-g4: Correct VUART IRQ number
  ARM: dts: aspeed: Add proper clock references
  ARM: dts: aspeed: Add MAC clocks
  ARM: dts: aspeed: Add watchdog clocks
  ARM: dts: aspeed: Add flash controller clocks
  ARM: dts: aspeed: Add clock phandle to GPIO
  ARM: dts: aspeed: Add PWM and tachometer node
  ARM: dts: aspeed: Add LPC Snoop device
  ARM: dts: aspeed: Remove skeleton.dtsi
  ARM: dts: aspeed: Update license headers
  ARM: dts: Add OpenBMC flash layout
  ARM: dts: aspeed: Sort ASPEED entries in makefile
  ARM: dts: aspeed: Add Witherspoon BMC machine
  ARM: dts: aspeed-romulus: Update Romulus system
  ARM: dts: aspeed-plametto: Add flash layout

Rick Altherr (1):
  ARM: dts: aspeed: Add Qanta Q71L BMC machine

Xo Wang (1):
  ARM: dts: aspeed: Add Ingrasys Zaius BMC machine

 arch/arm/boot/dts/Makefile                       |   8 +-
 arch/arm/boot/dts/aspeed-ast2500-evb.dts         |   2 +-
 arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts    |   3 +-
 arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts     | 155 ++++++-
 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 547 +++++++++++++++++++++++
 arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts       | 427 ++++++++++++++++++
 arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts     | 458 +++++++++++++++++++
 arch/arm/boot/dts/aspeed-g4.dtsi                 | 164 ++++---
 arch/arm/boot/dts/aspeed-g5.dtsi                 | 155 ++++---
 arch/arm/boot/dts/openbmc-flash-layout.dtsi      |  32 ++
 include/dt-bindings/clock/aspeed-clock.h         |  54 +++
 include/dt-bindings/gpio/aspeed-gpio.h           |  49 ++
 12 files changed, 1908 insertions(+), 146 deletions(-)
 create mode 100644 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
 create mode 100644 arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
 create mode 100644 arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
 create mode 100644 arch/arm/boot/dts/openbmc-flash-layout.dtsi
 create mode 100644 include/dt-bindings/clock/aspeed-clock.h
 create mode 100644 include/dt-bindings/gpio/aspeed-gpio.h

-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH 01/20] dt-bindings: clock: Add ASPEED constants
From: Joel Stanley @ 2017-12-11  5:06 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Arnd Bergmann, Andrew Jeffery,
	Patrick Venture, Xo Wang, Lei YU
  Cc: Cédric Le Goater, Benjamin Herrenschmidt, Jeremy Kerr,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-aspeed-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <20171211050704.20621-1-joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>

These will be merged as part of the clock driver. This commit is
included so the tree will build without the clock series being applied.

Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
---
 include/dt-bindings/clock/aspeed-clock.h | 54 ++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 include/dt-bindings/clock/aspeed-clock.h

diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h
new file mode 100644
index 000000000000..fe46ab69da5c
--- /dev/null
+++ b/include/dt-bindings/clock/aspeed-clock.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+
+#ifndef DT_BINDINGS_ASPEED_CLOCK_H
+#define DT_BINDINGS_ASPEED_CLOCK_H
+
+#define ASPEED_CLK_GATE_ECLK		0
+#define ASPEED_CLK_GATE_GCLK		1
+#define ASPEED_CLK_GATE_MCLK		2
+#define ASPEED_CLK_GATE_VCLK		3
+#define ASPEED_CLK_GATE_BCLK		4
+#define ASPEED_CLK_GATE_DCLK		5
+#define ASPEED_CLK_GATE_REFCLK		6
+#define ASPEED_CLK_GATE_USBPORT2CLK	7
+#define ASPEED_CLK_GATE_LCLK		8
+#define ASPEED_CLK_GATE_USBUHCICLK	9
+#define ASPEED_CLK_GATE_D1CLK		10
+#define ASPEED_CLK_GATE_YCLK		11
+#define ASPEED_CLK_GATE_USBPORT1CLK	12
+#define ASPEED_CLK_GATE_UART1CLK	13
+#define ASPEED_CLK_GATE_UART2CLK	14
+#define ASPEED_CLK_GATE_UART5CLK	15
+#define ASPEED_CLK_GATE_ESPICLK		16
+#define ASPEED_CLK_GATE_MAC1CLK		17
+#define ASPEED_CLK_GATE_MAC2CLK		18
+#define ASPEED_CLK_GATE_RSACLK		19
+#define ASPEED_CLK_GATE_UART3CLK	20
+#define ASPEED_CLK_GATE_UART4CLK	21
+#define ASPEED_CLK_GATE_SDCLKCLK	22
+#define ASPEED_CLK_GATE_LHCCLK		23
+#define ASPEED_CLK_HPLL			24
+#define ASPEED_CLK_AHB			25
+#define ASPEED_CLK_APB			26
+#define ASPEED_CLK_UART			27
+#define ASPEED_CLK_SDIO			28
+#define ASPEED_CLK_ECLK			29
+#define ASPEED_CLK_ECLK_MUX		30
+#define ASPEED_CLK_LHCLK		31
+#define ASPEED_CLK_MAC			32
+#define ASPEED_CLK_BCLK			33
+#define ASPEED_CLK_MPLL			34
+
+#define ASPEED_NUM_CLKS			35
+
+#define ASPEED_RESET_XDMA		0
+#define ASPEED_RESET_MCTP		1
+#define ASPEED_RESET_ADC		2
+#define ASPEED_RESET_JTAG_MASTER	3
+#define ASPEED_RESET_MIC		4
+#define ASPEED_RESET_PWM		5
+#define ASPEED_RESET_PCIVGA		6
+#define ASPEED_RESET_I2C		7
+#define ASPEED_RESET_AHB		8
+
+#endif
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox