Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH 1/4] OMAP: DSS2: Remove suspicous and unused TAAL regulator API usage
From: Mark Brown @ 2012-05-03 13:21 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: Florian Tobias Schandinat, linux-fbdev, linux-omap
In-Reply-To: <1336050660.14378.12.camel@deskari>

[-- Attachment #1: Type: text/plain, Size: 281 bytes --]

On Thu, May 03, 2012 at 04:11:00PM +0300, Tomi Valkeinen wrote:

> I've already applied this and the three other patches that you sent in
> March to my omapdss tree. Have there been any changes?

No, it's a resend - if you've applied these changes they're not showing
up in -next.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 1/4] OMAP: DSS2: Remove suspicous and unused TAAL regulator API usage
From: Tomi Valkeinen @ 2012-05-03 13:11 UTC (permalink / raw)
  To: Mark Brown; +Cc: Florian Tobias Schandinat, linux-fbdev, linux-omap
In-Reply-To: <1336042634-28486-1-git-send-email-broonie@opensource.wolfsonmicro.com>

[-- Attachment #1: Type: text/plain, Size: 1084 bytes --]

Hi,

On Thu, 2012-05-03 at 11:57 +0100, Mark Brown wrote:
> The TAAL driver contains some regulator support which is currently unused
> (the code is there but the one panel supported by the driver doesn't have
> any regulators provided). This code mostly looks like an open coded
> version of the regulator core bulk API.
> 
> The only additional feature is that a voltage range can be set once when
> the device is opened, though this is never varied at runtime. The general
> expectation is that if the device is not actively managing the voltage of
> the device (eg, doing DVFS) then any configuration will be done using the
> constraints rather than by drivers, saving them code and ensuring that
> they work well with systems where the voltage is not configurable.
> 
> If systems are added needing regulator support this can be added back in,
> though it should be based on core features rather than open coding things.

I've already applied this and the three other patches that you sent in
March to my omapdss tree. Have there been any changes?

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH v2 3/4] leds: add LM3533 LED driver
From: Johan Hovold @ 2012-05-03 11:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat, Arnd Bergmann,
	Andrew Morton, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <20120503104344.GC3955@opensource.wolfsonmicro.com>

On Thu, May 03, 2012 at 11:43:44AM +0100, Mark Brown wrote:
> On Thu, May 03, 2012 at 12:26:38PM +0200, Johan Hovold wrote:
> 
> > +What:		/sys/class/leds/<led>/risetime
> > +Date:		April 2012
> > +KernelVersion:	3.5
> > +Contact:	Johan Hovold <jhovold@gmail.com>
> > +Description:
> > +		Set the pattern generator fall and rise times (0..7), where
> > +
> > +		0 - 2048 us
> > +		1 - 262 ms
> > +		2 - 524 ms
> > +		3 - 1.049 s
> > +		4 - 2.097 s
> > +		5 - 4.194 s
> > +		6 - 8.389 s
> > +		7 - 16.78 s
> > +
> 
> Shouldn't these be controlled by led_blink_set() rather than a custom
> ABI?

led_blink_set controls the on/off times, but the LM3533 has the two
additional rise and fall-time settings which determine the transition
time between these states.

> > +What:		/sys/class/leds/<led>/id
> > +Date:		April 2012
> > +KernelVersion:	3.5
> > +Contact:	Johan Hovold <jhovold@gmail.com>
> > +Description:
> > +		Get the id of this led (0..3).
> > +
> 
> This should just be a generic LED subsystem thing?

It's related to the output mapping discussed in my previous mail. The
four logical LEDs (0..3) can be used to control either (or all) of the
five low-voltage output. This attribute provides the identity of the
class devices (logical LEDs) which can then be used in the output
mapping (done in the parent device). These id's have been chosen to
correspond to the MFD-id's, but are really device specific.
 
> > +What:		/sys/class/leds/<led>/max_current
> > +Date:		April 2012
> > +KernelVersion:	3.5
> > +Contact:	Johan Hovold <jhovold@gmail.com>
> > +Description:
> > +		Set the full-scale current I_{LED_FULLSCALE} (0..31), where
> > +
> > +		I_{LED_FULLSCALE} = 5mA + max_current * 0.8mA
> > +
> 
> Shouldn't this be set by platform data, the maximum current you can push
> through the LEDs seems like a board dependant thing which won't change
> dynamically at runtime.  The brightness can already be varied.

I fully agree and it is possible to set via the platform data for that
reason. The end-customer, however, insisted that even this setting be
available through sysfs to facilitate their integration and testing.

I'd be willing drop this attribute if requested, as it would only be used
during integration and could easily be added back by the end-customer if
needed.

> It'd also be nicer if the kernel did the calculation for the user.

If it was something that was going to be changed a lot, then yes,
perhaps. But as you point out above, this is generally a fixed setting
set using platform data once by integrators that will have access to
the datasheet and it's current table.

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH v2 2/4] iio: add LM3533 ambient light sensor driver
From: Jonathan Cameron @ 2012-05-03 11:40 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Greg Kroah-Hartman,
	Florian Tobias Schandinat, Arnd Bergmann, Andrew Morton,
	Mark Brown, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <1336040799-18433-3-git-send-email-jhovold@gmail.com>

On 5/3/2012 11:26 AM, Johan Hovold wrote:
> Add sub-driver for the ambient light sensor interface on National
> Semiconductor / TI LM3533 lighting power chips.
>
> The sensor interface can be used to control the LEDs and backlights of
> the chip through defining five light zones and three sets of
> corresponding brightness target levels.
>
> The driver provides raw and mean adc readings along with the current
> light zone through sysfs. A threshold event can be generated on zone
> changes.
Code is fine.  Pretty much all my comments are to do with the interface.
>
> Signed-off-by: Johan Hovold<jhovold@gmail.com>
> ---
>
> v2:
>   - reimplement using iio
>   - add sysfs-ABI documentation
>
>
>   .../Documentation/sysfs-bus-iio-light-lm3533-als   |   62 ++
>   drivers/staging/iio/light/Kconfig                  |   16 +
>   drivers/staging/iio/light/Makefile                 |    1 +
>   drivers/staging/iio/light/lm3533-als.c             |  617 ++++++++++++++++++++
>   4 files changed, 696 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
>   create mode 100644 drivers/staging/iio/light/lm3533-als.c
>
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
> new file mode 100644
> index 0000000..9849d14
> --- /dev/null
> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
> @@ -0,0 +1,62 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/gain
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold<jhovold@gmail.com>
> +Description:
> +		Set the ALS gain-resistor setting (0..127) for analog input
> +		mode, where
> +
> +		0000000 - ALS input is high impedance
> +		0000001 - 200kOhm (10uA at 2V full-scale)
> +		0000010 - 100kOhm (20uA at 2V full-scale)
> +		...
> +		1111110 - 1.587kOhm (1.26mA at 2V full-scale)
> +		1111111 - 1.575kOhm (1.27mA at 2V full-scale)
> +
> +		R_als = 2V / (10uA * gain)	(gain>  0)
Firstly, no magic numbers.  These are definitely magic.  Secondly see
in_illuminance0_scale for a suitable existing attribute.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/illuminance_zone
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold<jhovold@gmail.com>
> +Description:
> +		Get the current light zone (0..4) as defined by the
> +		in_illuminance_thresh[n]_{falling,rising} thresholds.
Hmm.. definitely have an in prefix, beyond that I'm not sure what the 
cleanest
interface will be for this.   Could extend the event codes to deal with the
zone index.  Slightly tricky as the channel could already be modified so
chan2 isn't necesarily available.
> +
> +What:		/sys/.../events/in_illuminance_thresh_either_en
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold<jhovold@gmail.com>
> +Description:
> +		Event generated when channel passes one of the four threshold
> +		in either direction (rising|falling) and a zone change occurs.
> +		The corresponding light zone can be read from
> +		illuminance_zone.
> +
> +What:		/sys/.../events/illuminance_thresh0_falling_value
hmm.. every time you think you are making progress a new and exciting 
device comes
along requiring the abi to be extended.
in_illuminanceX_threshY_rising_value
in_illuminanceX_threshY_falling_value
should do with appropriate description.
> +What:		/sys/.../events/illuminance_thresh0_raising_value
> +What:		/sys/.../events/illuminance_thresh1_falling_value
> +What:		/sys/.../events/illuminance_thresh1_raising_value
> +What:		/sys/.../events/illuminance_thresh2_falling_value
> +What:		/sys/.../events/illuminance_thresh2_raising_value
> +What:		/sys/.../events/illuminance_thresh3_falling_value
> +What:		/sys/.../events/illuminance_thresh3_raising_value
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold<jhovold@gmail.com>
> +Description:
> +		Specifies the value of threshold that the device is comparing
> +		against for the events enabled by
> +		in_illuminance_thresh_either_en, and defines the
> +		the five light zones.
> +
> +		These thresholds correspond to the eight zone-boundary
> +		registers (boundary[n]_{low,high}).
> +
This interface is going to take some thought.  We have 
in_illuminance0_target at the
moment, so I guess we can add a zoning concept to that...
> +What:		/sys/bus/iio/devices/iio:deviceX/target[m]_[n]
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold<jhovold@gmail.com>
> +Description:
> +		Set the target brightness for ALS-mapper m in light zone n
> +		(0..255), where m in 1..3 and n in 0..4.
Don't suppose you could do a quick summary of what these zones are and 
why there
are 3 ALS-mappers?  I'm not getting terribly far on a quick look at the 
datasheet!
> diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
> index e7e9159..263e44a 100644
> --- a/drivers/staging/iio/light/Kconfig
> +++ b/drivers/staging/iio/light/Kconfig
> @@ -24,6 +24,22 @@ config SENSORS_TSL2563
>   	 This driver can also be built as a module.  If so, the module
>   	 will be called tsl2563.
>
> +config SENSORS_LM3533
> +	tristate "LM3533 ambient light sensor"
> +	depends on MFD_LM3533
> +	help
> +	  If you say yes here you get support for the ambient light sensor
> +	  interface on National Semiconductor / TI LM3533 Lighting Power
> +	  chips.
> +
> +	  The sensor interface can be used to control the LEDs and backlights
> +	  of the chip through defining five light zones and three sets of
> +	  corresponding brightness target levels.
> +
> +	  The driver provides raw and mean adc readings along with the current
> +	  light zone through sysfs. A threshold event can be generated on zone
> +	  changes.
> +
>   config TSL2583
>   	tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
>   	depends on I2C
> diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
> index 3011fbf..16a60a2 100644
> --- a/drivers/staging/iio/light/Makefile
> +++ b/drivers/staging/iio/light/Makefile
> @@ -4,4 +4,5 @@
>
>   obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
>   obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
> +obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
>   obj-$(CONFIG_TSL2583)	+= tsl2583.o
> diff --git a/drivers/staging/iio/light/lm3533-als.c b/drivers/staging/iio/light/lm3533-als.c
> new file mode 100644
> index 0000000..e2c9be6
> --- /dev/null
> +++ b/drivers/staging/iio/light/lm3533-als.c
> @@ -0,0 +1,617 @@
> +/*
> + * lm3533-als.c -- LM3533 Ambient Light Sensor driver
> + *
> + * Copyright (C) 2011-2012 Texas Instruments
> + *
> + * Author: Johan Hovold<jhovold@gmail.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/atomic.h>
> +#include<linux/fs.h>
> +#include<linux/interrupt.h>
> +#include<linux/io.h>
> +#include<linux/module.h>
> +#include<linux/mfd/core.h>
> +#include<linux/platform_device.h>
> +#include<linux/slab.h>
> +#include<linux/uaccess.h>
> +
> +#include<linux/mfd/lm3533.h>
> +
> +#include "../events.h"
> +#include "../iio.h"
This will need to go through the staging-next tree.  In there these
headers have moved.
> +
> +
> +#define LM3533_ALS_RESISTOR_MAX			0x7f
> +#define LM3533_ALS_ADC_MAX			0xff
> +#define LM3533_ALS_BOUNDARY_MAX			LM3533_ALS_ADC_MAX
> +#define LM3533_ALS_TARGET_MAX			LM3533_ALS_ADC_MAX
> +#define LM3533_ALS_ZONE_MAX			4
> +
> +#define LM3533_REG_ALS_RESISTOR_SELECT		0x30
> +#define LM3533_REG_ALS_CONF			0x31
> +#define LM3533_REG_ALS_ZONE_INFO		0x34
> +#define LM3533_REG_ALS_READ_ADC_AVERAGE		0x37
> +#define LM3533_REG_ALS_READ_ADC_RAW		0x38
> +#define LM3533_REG_ALS_BOUNDARY0_HIGH		0x50
> +#define LM3533_REG_ALS_BOUNDARY0_LOW		0x51
> +#define LM3533_REG_ALS_BOUNDARY1_HIGH		0x52
> +#define LM3533_REG_ALS_BOUNDARY1_LOW		0x53
> +#define LM3533_REG_ALS_BOUNDARY2_HIGH		0x54
> +#define LM3533_REG_ALS_BOUNDARY2_LOW		0x55
> +#define LM3533_REG_ALS_BOUNDARY3_HIGH		0x56
> +#define LM3533_REG_ALS_BOUNDARY3_LOW		0x57
> +#define LM3533_REG_ALS_M1_TARGET_0		0x60
> +#define LM3533_REG_ALS_M1_TARGET_1		0x61
> +#define LM3533_REG_ALS_M1_TARGET_2		0x62
> +#define LM3533_REG_ALS_M1_TARGET_3		0x63
> +#define LM3533_REG_ALS_M1_TARGET_4		0x64
> +#define LM3533_REG_ALS_M2_TARGET_0		0x65
> +#define LM3533_REG_ALS_M2_TARGET_1		0x66
> +#define LM3533_REG_ALS_M2_TARGET_2		0x67
> +#define LM3533_REG_ALS_M2_TARGET_3		0x68
> +#define LM3533_REG_ALS_M2_TARGET_4		0x69
> +#define LM3533_REG_ALS_M3_TARGET_0		0x6a
> +#define LM3533_REG_ALS_M3_TARGET_1		0x6b
> +#define LM3533_REG_ALS_M3_TARGET_2		0x6c
> +#define LM3533_REG_ALS_M3_TARGET_3		0x6d
> +#define LM3533_REG_ALS_M3_TARGET_4		0x6e
> +
> +#define LM3533_ALS_ENABLE_MASK			0x01
> +#define LM3533_ALS_INPUT_MODE_MASK		0x02
> +#define LM3533_ALS_INT_ENABLE_MASK		0x01
> +
> +#define LM3533_ALS_ZONE_SHIFT			2
> +#define LM3533_ALS_ZONE_MASK			0x1c
> +
> +#define LM3533_ALS_FLAG_INT_ENABLED		1
> +
> +
> +struct lm3533_als {
> +	struct lm3533 *lm3533;
> +
> +	unsigned long flags;
> +	int irq;
> +
> +	atomic_t zone;
> +};
> +
> +
> +static int lm3533_als_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan,
> +				int *val1, int *val2, long mask)
> +{
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 reg;
> +	u8 val;
> +	int ret;
> +
> +	switch (mask) {
> +	case 0:
> +		reg = LM3533_REG_ALS_READ_ADC_RAW;
> +		break;
> +	case IIO_CHAN_INFO_AVERAGE_RAW:
> +		reg = LM3533_REG_ALS_READ_ADC_AVERAGE;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = lm3533_read(als->lm3533, reg,&val);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "failed to read adc\n");
> +		return ret;
> +	}
> +
> +	*val1 = val;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static const struct iio_chan_spec lm3533_als_channels[] = {
> +	{
> +		.type = IIO_LIGHT,
> +		.info_mask = IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT,
> +		.channel = 0,
channel doesn't get used unless you also set indexed = 1.
> +	}
> +};
> +
> +static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
> +{
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 val;
> +	int ret;
> +
> +	ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO,&val);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "failed to read zone\n");
> +		return ret;
> +	}
> +
> +	val = (val&  LM3533_ALS_ZONE_MASK)>>  LM3533_ALS_ZONE_SHIFT;
> +	*zone = min_t(u8, val, LM3533_ALS_ZONE_MAX);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t lm3533_als_isr(int irq, void *dev_id)
> +{
> +
> +	struct iio_dev *indio_dev = dev_id;
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 zone;
> +	int ret;
> +
> +	/* Clear interrupt by reading the ALS zone register. */
> +	ret = lm3533_als_get_zone(indio_dev,&zone);
> +	if (ret)
> +		goto out;
> +
> +	atomic_set(&als->zone, zone);
> +
> +	iio_push_event(indio_dev,
> +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
> +					    0,
> +					    IIO_EV_TYPE_THRESH,
> +					    IIO_EV_DIR_EITHER),
> +		       iio_get_time_ns());
> +out:
> +	return IRQ_HANDLED;
> +}
> +
> +static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable)
> +{
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 mask = LM3533_ALS_INT_ENABLE_MASK;
> +	u8 val;
> +	int ret;
> +
> +	if (enable)
> +		val = mask;
> +	else
> +		val = 0;
> +
> +	ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "failed to set int mode %d\n",
> +								enable);
extra brackets.
> +	}
> +
> +	return ret;
> +}
> +
> +static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable)
> +{
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 mask = LM3533_ALS_INT_ENABLE_MASK;
> +	u8 val;
> +	int ret;
> +
> +	ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO,&val);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "failed to get int mode\n");
> +		return ret;
> +	}
> +
> +	*enable = !!(val&  mask);
> +
> +	return 0;
> +}
> +
> +static int show_thresh_either_en(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	int enable;
> +	int ret;
> +
> +	if (als->irq) {
> +		ret = lm3533_als_get_int_mode(indio_dev,&enable);
> +		if (ret)
> +			return ret;
> +	} else {
> +		enable = 0;
> +	}
> +
> +	return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
> +}
> +
> +static int store_thresh_either_en(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	unsigned long enable;
> +	bool int_enabled;
> +	u8 zone;
> +	int ret;
> +
> +	if (!als->irq)
> +		return -EBUSY;
> +
> +	if (kstrtoul(buf, 0,&enable))
> +		return -EINVAL;
> +
> +	int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED,&als->flags);
> +
> +	if (enable&&  !int_enabled) {
> +		ret = lm3533_als_get_zone(indio_dev,&zone);
> +		if (ret)
> +			return ret;
> +
> +		atomic_set(&als->zone, zone);
> +
> +		set_bit(LM3533_ALS_FLAG_INT_ENABLED,&als->flags);
> +	}
> +
> +	ret = lm3533_als_set_int_mode(indio_dev, enable);
> +	if (ret) {
> +		if (!int_enabled)
> +			clear_bit(LM3533_ALS_FLAG_INT_ENABLED,&als->flags);
> +
> +		return ret;
> +	}
> +
> +	if (!enable)
> +		clear_bit(LM3533_ALS_FLAG_INT_ENABLED,&als->flags);
> +
> +	return len;
> +}
> +
> +static ssize_t show_zone(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	u8 zone;
> +	int ret;
> +
> +	if (test_bit(LM3533_ALS_FLAG_INT_ENABLED,&als->flags)) {
> +		zone = atomic_read(&als->zone);
> +	} else {
> +		ret = lm3533_als_get_zone(indio_dev,&zone);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return scnprintf(buf, PAGE_SIZE, "%u\n", zone);
> +}
> +
> +struct lm3533_device_attribute {
> +	struct device_attribute dev_attr;
> +	u8 reg;
> +	u8 max;
> +};
> +
> +#define to_lm3533_dev_attr(_dev_attr) \
> +	container_of(_dev_attr, struct lm3533_device_attribute, dev_attr)
> +
> +static ssize_t show_lm3533_als_reg(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	struct lm3533_device_attribute *lm3533_attr = to_lm3533_dev_attr(attr);
> +	u8 val;
> +	int ret;
> +
> +	ret = lm3533_read(als->lm3533, lm3533_attr->reg,&val);
> +	if (ret)
> +		return ret;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
> +}
> +
> +static ssize_t store_lm3533_als_reg(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +	struct lm3533_device_attribute *lm3533_attr = to_lm3533_dev_attr(attr);
> +	u8 val;
> +	int ret;
> +
> +	if (kstrtou8(buf, 0,&val) || val>  lm3533_attr->max)
> +		return -EINVAL;
> +
> +	ret = lm3533_write(als->lm3533, lm3533_attr->reg, val);
> +	if (ret)
> +		return ret;
> +
> +	return len;
> +}
> +
> +#define REG_ATTR(_name, _mode, _show, _store, _reg, _max) \
> +	{ .dev_attr = __ATTR(_name, _mode, _show, _store), \
> +	  .reg = _reg, \
> +	  .max = _max }
> +
> +#define LM3533_REG_ATTR(_name, _mode, _show, _store, _reg, _max) \
> +	struct lm3533_device_attribute lm3533_dev_attr_##_name \
> +		= REG_ATTR(_name, _mode, _show, _store, _reg, _max)
> +
> +#define LM3533_REG_ATTR_RW(_name, _reg, _max) \
> +	LM3533_REG_ATTR(_name, S_IRUGO | S_IWUSR, show_lm3533_als_reg, \
> +					store_lm3533_als_reg, _reg, _max)
> +
> +#define ALS_THRESH_FALLING_ATTR_RW(_nr) \
> +	LM3533_REG_ATTR_RW(in_illuminance_thresh##_nr##_falling_value, \
> +		LM3533_REG_ALS_BOUNDARY##_nr##_LOW, LM3533_ALS_BOUNDARY_MAX)
> +
> +#define ALS_THRESH_RAISING_ATTR_RW(_nr) \
> +	LM3533_REG_ATTR_RW(in_illuminance_thresh##_nr##_raising_value, \
> +		LM3533_REG_ALS_BOUNDARY##_nr##_HIGH, LM3533_ALS_BOUNDARY_MAX)
> +
> +/* ALS Zone thresholds (boundaries)
> + *
> + * in_illuminance_thresh[0-3]_falling_value	0-255
> + * in_illuminance_thresh[0-3]_raising_value	0-255
> + */
> +static ALS_THRESH_FALLING_ATTR_RW(0);
> +static ALS_THRESH_FALLING_ATTR_RW(1);
> +static ALS_THRESH_FALLING_ATTR_RW(2);
> +static ALS_THRESH_FALLING_ATTR_RW(3);
> +
> +static ALS_THRESH_RAISING_ATTR_RW(0);
> +static ALS_THRESH_RAISING_ATTR_RW(1);
> +static ALS_THRESH_RAISING_ATTR_RW(2);
> +static ALS_THRESH_RAISING_ATTR_RW(3);
> +
> +#define LM3533_ALS_ATTR_RO(_name) \
> +	DEVICE_ATTR(in_illuminance_##_name, S_IRUGO, show_##_name, NULL)
> +#define LM3533_ALS_ATTR_RW(_name) \
> +	DEVICE_ATTR(in_illuminance_##_name, S_IRUGO | S_IWUSR , \
> +						show_##_name, store_##_name)
> +
> +/* ALS Zone threshold-event enable
> + *
> + * in_illuminance_thresh_either_en		0,1
> + */
> +static LM3533_ALS_ATTR_RW(thresh_either_en);
> +
> +/* ALS Current Zone
> + *
> + * in_illuminance_zone		0-4
> + */
> +static LM3533_ALS_ATTR_RO(zone);
> +
> +#define ALS_TARGET_ATTR_RW(_mapper, _nr) \
> +	LM3533_REG_ATTR_RW(target##_mapper##_##_nr, \
> +		LM3533_REG_ALS_M##_mapper##_TARGET_##_nr, LM3533_ALS_TARGET_MAX)
> +
> +/* ALS Mapper targets
> + *
> + * target[1-3]_[0-4]		0-255
> + */
> +static ALS_TARGET_ATTR_RW(1, 0);
> +static ALS_TARGET_ATTR_RW(1, 1);
> +static ALS_TARGET_ATTR_RW(1, 2);
> +static ALS_TARGET_ATTR_RW(1, 3);
> +static ALS_TARGET_ATTR_RW(1, 4);
> +
> +static ALS_TARGET_ATTR_RW(2, 0);
> +static ALS_TARGET_ATTR_RW(2, 1);
> +static ALS_TARGET_ATTR_RW(2, 2);
> +static ALS_TARGET_ATTR_RW(2, 3);
> +static ALS_TARGET_ATTR_RW(2, 4);
> +
> +static ALS_TARGET_ATTR_RW(3, 0);
> +static ALS_TARGET_ATTR_RW(3, 1);
> +static ALS_TARGET_ATTR_RW(3, 2);
> +static ALS_TARGET_ATTR_RW(3, 3);
> +static ALS_TARGET_ATTR_RW(3, 4);
> +
> +/* ALS Gain resistor setting
> + *
> + * gain		0-127
> + */
> +static LM3533_REG_ATTR_RW(gain, LM3533_REG_ALS_RESISTOR_SELECT,
> +						LM3533_ALS_RESISTOR_MAX);
> +
> +static struct attribute *lm3533_als_event_attributes[] = {
> +	&dev_attr_in_illuminance_thresh_either_en.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh0_falling_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh0_raising_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh1_falling_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh1_raising_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh2_falling_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh2_raising_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh3_falling_value.dev_attr.attr,
> +	&lm3533_dev_attr_in_illuminance_thresh3_raising_value.dev_attr.attr,
> +	NULL
> +};
> +
> +static struct attribute_group lm3533_als_event_attribute_group = {
> +	.attrs = lm3533_als_event_attributes
> +};
> +
> +static struct attribute *lm3533_als_attributes[] = {
> +	&lm3533_dev_attr_target1_0.dev_attr.attr,
> +	&lm3533_dev_attr_target1_1.dev_attr.attr,
> +	&lm3533_dev_attr_target1_2.dev_attr.attr,
> +	&lm3533_dev_attr_target1_3.dev_attr.attr,
> +	&lm3533_dev_attr_target1_4.dev_attr.attr,
> +	&lm3533_dev_attr_target2_0.dev_attr.attr,
> +	&lm3533_dev_attr_target2_1.dev_attr.attr,
> +	&lm3533_dev_attr_target2_2.dev_attr.attr,
> +	&lm3533_dev_attr_target2_3.dev_attr.attr,
> +	&lm3533_dev_attr_target2_4.dev_attr.attr,
> +	&lm3533_dev_attr_target3_0.dev_attr.attr,
> +	&lm3533_dev_attr_target3_1.dev_attr.attr,
> +	&lm3533_dev_attr_target3_2.dev_attr.attr,
> +	&lm3533_dev_attr_target3_3.dev_attr.attr,
> +	&lm3533_dev_attr_target3_4.dev_attr.attr,
> +	&lm3533_dev_attr_gain.dev_attr.attr,
> +	&dev_attr_in_illuminance_zone.attr,
> +	NULL
> +};
> +
> +static struct attribute_group lm3533_als_attribute_group = {
> +	.attrs = lm3533_als_attributes
> +};
> +
> +static int __devinit lm3533_als_set_input_mode(struct lm3533 *lm3533,
> +								int pwm_mode)
> +{
> +	u8 mask = LM3533_ALS_INPUT_MODE_MASK;
> +	u8 val;
> +	int ret;
> +
> +	if (pwm_mode)
> +		val = mask;	/* pwm input */
> +	else
> +		val = 0;	/* analog input */
> +
> +	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, mask, mask);
> +	if (ret) {
> +		dev_err(lm3533->dev,
> +				"failed to set input mode %d\n", pwm_mode);
> +	}
> +
> +	return ret;
> +}
> +
> +static int __devinit lm3533_als_enable(struct lm3533 *lm3533)
> +{
> +	u8 mask = LM3533_ALS_ENABLE_MASK;
> +	int ret;
> +
> +	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, mask, mask);
> +	if (ret)
> +		dev_err(lm3533->dev, "failed to enable ALS\n");
> +
> +	return ret;
> +}
> +
> +static int lm3533_als_disable(struct lm3533 *lm3533)
> +{
> +	u8 mask = LM3533_ALS_ENABLE_MASK;
> +	int ret;
> +
> +	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, 0, mask);
> +	if (ret)
> +		dev_err(lm3533->dev, "failed to disable ALS\n");
> +
> +	return ret;
> +}
> +
> +static const struct iio_info lm3533_als_info = {
> +	.attrs		=&lm3533_als_attribute_group,
> +	.event_attrs	=&lm3533_als_event_attribute_group,
> +	.driver_module	= THIS_MODULE,
> +	.read_raw	=&lm3533_als_read_raw,
> +};
> +
> +static int __devinit lm3533_als_probe(struct platform_device *pdev)
> +{
> +	struct lm3533 *lm3533;
> +	struct lm3533_als_platform_data *pdata;
> +	struct lm3533_als *als;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +
> +	dev_dbg(&pdev->dev, "%s\n", __func__);
> +
> +	lm3533 = dev_get_drvdata(pdev->dev.parent);
> +	if (!lm3533)
> +		return -EINVAL;
> +
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "no platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	indio_dev = iio_allocate_device(sizeof(*als));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	indio_dev->info =&lm3533_als_info;
> +	indio_dev->channels = lm3533_als_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels);
> +	indio_dev->name = "lm3533-als";
> +	indio_dev->dev.parent = pdev->dev.parent;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	als = iio_priv(indio_dev);
> +	als->lm3533 = lm3533;
> +	als->irq = lm3533->irq;
> +	atomic_set(&als->zone, 0);
> +
> +	if (als->irq) {
> +		ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr,
> +					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +					indio_dev->name, indio_dev);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "failed to request irq %d\n",
> +								lm3533->irq);
> +			goto err_free;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ret = lm3533_als_set_input_mode(lm3533, pdata->pwm_mode);
> +	if (ret)
> +		goto err_free;
> +
> +	ret = lm3533_als_enable(lm3533);
> +	if (ret)
> +		goto err_free;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register ALS\n");
> +		goto err_disable;
> +	}
> +
> +	return 0;
> +
> +err_disable:
> +	lm3533_als_disable(lm3533);
> +err_free:
> +	iio_free_device(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int __devexit lm3533_als_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct lm3533_als *als = iio_priv(indio_dev);
> +
> +	dev_dbg(&pdev->dev, "%s\n", __func__);
> +
> +	iio_device_unregister(indio_dev);
> +	lm3533_als_disable(als->lm3533);
> +	if (als->irq)
> +		free_irq(als->irq, indio_dev);
> +	iio_free_device(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver lm3533_als_driver = {
> +	.driver = {
> +		.name = "lm3533-als",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe		= lm3533_als_probe,
> +	.remove		= __devexit_p(lm3533_als_remove),
> +};
> +module_platform_driver(lm3533_als_driver);
> +
> +MODULE_AUTHOR("Johan Hovold<jhovold@gmail.com>");
> +MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:lm3533-als");


^ permalink raw reply

* Re: [PATCH v2 1/4] mfd: add LM3533 lighting-power core driver
From: Mark Brown @ 2012-05-03 11:38 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat, Arnd Bergmann,
	Andrew Morton, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <20120503112803.GA15752@localhost>

[-- Attachment #1: Type: text/plain, Size: 2134 bytes --]

On Thu, May 03, 2012 at 01:28:03PM +0200, Johan Hovold wrote:
> On Thu, May 03, 2012 at 11:38:48AM +0100, Mark Brown wrote:

> > I'd expect you can drop these log messages, if there's stuff like this
> > missing we should add it to regmap.  At the minute the regmap logging is
> > via trace points rather than debug logs as you can leave them enabled
> > all the time.

> If such debugging is added to regmap we still need a way to enable them
> per driver (or rather regmap) to not clutter the logs.

This is one of the reasons why we currently use tracepoints (they just
don't have this issue as they're trivial to filter), though
adding some sort of infrastructure for it ought not to be too difficult
even if it's just at the regmap level.

> These three dev_dbg statements are extremely useful during debugging /
> development especially in combination with the other dynamic printks in
> these drivers.

> I'd actually prefer just keeping them for now.

OTOH the whole point in having stuff like this is to factor out repeated
code like this so if the infrastructure isn't working we should fix
that.

> > Might also be worth moving some of the sysfs stuff to live with the
> > relevant drivers.

> Which attributes do you have in mind?

Pretty much all of those on the MFD.

> The boost_freq and boost_ovp affect both backlight devices (i.e. cannot
> be set separately) and as such belong in the parent driver IMO.

> Same with the output_hvled{1..2}, output_lvled{1..5} attributes. The
> chip has four logical LEDs ("control banks") but five low-voltage output
> sinks. The five output_lvled attributes determine the mapping and as
> such belong in the parent driver. The two logical backlight devices can
> likewise be used to control either or both high-voltage outputs and
> belong in the parent driver for the same reasons.

Actually, the other question I had but forgot to ask (or I think punted
on for your response) was why these are in sysfs at all - things like
which things are connected to the backlight are going to be a property
of the board design so should be defined by the machine not tweaked from
userspace.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Wolfram Sang @ 2012-05-03 11:35 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1336042416-28330-1-git-send-email-broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 921 bytes --]

On Thu, May 03, 2012 at 11:53:36AM +0100, Mark Brown wrote:
> Since there are uses for I2C_M_NOSTART which are much more sensible and
> standard than most of the protocol mangling functionality (the main one
> being gather writes to devices where something like a register address
> needs to be inserted before a block of data) create a new I2C_FUNC_NOSTART
> for this feature and update all the users to use it.
> 
> In the case of regmap-i2c we remove the requirement for mangling as
> I2C_M_NOSTART is the only mangling feature which is being used.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

Applied to next, thanks! Jean, let me know if you prefer to take it.
Acks from input and fbdev maintainers still appreciated.

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH v2 1/4] mfd: add LM3533 lighting-power core driver
From: Johan Hovold @ 2012-05-03 11:28 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat, Arnd Bergmann,
	Andrew Morton, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <20120503103848.GB3955@opensource.wolfsonmicro.com>

On Thu, May 03, 2012 at 11:38:48AM +0100, Mark Brown wrote:
> On Thu, May 03, 2012 at 12:26:36PM +0200, Johan Hovold wrote:
> > Add support for National Semiconductor / TI LM3533 lighting power chips.
> > 
> > This is the core driver which provides register access over I2C and
> > registers the ambient-light-sensor, LED and backlight sub-drivers.
> 
> Reviwed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> 
> though
> 
> > +	dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);
> 
> I'd expect you can drop these log messages, if there's stuff like this
> missing we should add it to regmap.  At the minute the regmap logging is
> via trace points rather than debug logs as you can leave them enabled
> all the time.

If such debugging is added to regmap we still need a way to enable them
per driver (or rather regmap) to not clutter the logs.

These three dev_dbg statements are extremely useful during debugging /
development especially in combination with the other dynamic printks in
these drivers.

I'd actually prefer just keeping them for now.
 
> Might also be worth moving some of the sysfs stuff to live with the
> relevant drivers.

Which attributes do you have in mind?

The boost_freq and boost_ovp affect both backlight devices (i.e. cannot
be set separately) and as such belong in the parent driver IMO.

Same with the output_hvled{1..2}, output_lvled{1..5} attributes. The
chip has four logical LEDs ("control banks") but five low-voltage output
sinks. The five output_lvled attributes determine the mapping and as
such belong in the parent driver. The two logical backlight devices can
likewise be used to control either or both high-voltage outputs and
belong in the parent driver for the same reasons.

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Wolfram Sang @ 2012-05-03 11:13 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20120503095814.GA3955-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 706 bytes --]

On Thu, May 03, 2012 at 10:58:15AM +0100, Mark Brown wrote:
> On Thu, May 03, 2012 at 11:52:11AM +0200, Wolfram Sang wrote:
> 
> > Also, I'd think the FUNC_NOSTART bit should be 0x08 and SMBUS_PEC 0x4000. This
> > will be more intuitive, probably?
> 
> What is the value in renumbering everything?  It just seems like it
> makes the diff less clear and has no practical value.

Not everything, only those 2. The result would be having one block dealing
with I2C and one block with SMBUS. But Jean's comment is an ultimate
"no" anyway.

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of  I2C_FUNC_PROTOCOL_MANGLING
From: Jean Delvare @ 2012-05-03 10:58 UTC (permalink / raw)
  To: Mark Brown
  Cc: Wolfram Sang, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20120503095814.GA3955-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

On Thu, 3 May 2012 10:58:15 +0100, Mark Brown wrote:
> On Thu, May 03, 2012 at 11:52:11AM +0200, Wolfram Sang wrote:
> 
> > Also, I'd think the FUNC_NOSTART bit should be 0x08 and SMBUS_PEC 0x4000. This
> > will be more intuitive, probably?
> 
> What is the value in renumbering everything?  It just seems like it
> makes the diff less clear and has no practical value.

You actually just can't renumber them, as they are part of the ABI to
user-space (through i2c-dev's ioctl I2C_FUNCS.)

-- 
Jean Delvare

^ permalink raw reply

* [PATCH 4/4] OMAPDSS: TPO-TD03MTEA1: Correct comment for power on delay
From: Mark Brown @ 2012-05-03 10:57 UTC (permalink / raw)
  To: Tomi Valkeinen, Florian Tobias Schandinat
  Cc: linux-fbdev, linux-omap, Mark Brown
In-Reply-To: <1336042634-28486-1-git-send-email-broonie@opensource.wolfsonmicro.com>

Since any power on stabilisation delay for the supply itself should be
taken care of transparently by the regulator API when the regulator is
enabled the additional delay that the TPO-TD03MTEA1 driver adds after
that returned should be due to the requirements of the device itself
rather than the supply (the delay is also suspicously long for one for
a regulator to ramp). Correct the comment to avoid misleading people
taking this code as a reference.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Grazvydas Ignotas <notasas@gmail.com>
---
 drivers/video/omap2/displays/panel-tpo-td043mtea1.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index 74c6b87..987cb84 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -281,7 +281,7 @@ static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
 	if (r != 0)
 		return r;
 
-	/* wait for regulator to stabilize */
+	/* wait for panel to stabilize */
 	msleep(160);
 
 	if (gpio_is_valid(nreset_gpio))
-- 
1.7.10


^ permalink raw reply related

* [PATCH 3/4] OMAPDSS: TPO-TD03MTEA1: Check for errors from regulator_enable()
From: Mark Brown @ 2012-05-03 10:57 UTC (permalink / raw)
  To: Tomi Valkeinen, Florian Tobias Schandinat
  Cc: linux-fbdev, linux-omap, Mark Brown
In-Reply-To: <1336042634-28486-1-git-send-email-broonie@opensource.wolfsonmicro.com>

It is possible for regulator_enable() to fail and if it does fail that's
generally a bad sign for anything we try to do with the hardware afterwards
so check for and immediately return an error if regulator_enable() fails.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Grazvydas Ignotas <notasas@gmail.com>
---
 drivers/video/omap2/displays/panel-tpo-td043mtea1.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index 32f3fcd..74c6b87 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -272,11 +272,14 @@ static const struct omap_video_timings tpo_td043_timings = {
 static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
 {
 	int nreset_gpio = tpo_td043->nreset_gpio;
+	int r;
 
 	if (tpo_td043->powered_on)
 		return 0;
 
-	regulator_enable(tpo_td043->vcc_reg);
+	r = regulator_enable(tpo_td043->vcc_reg);
+	if (r != 0)
+		return r;
 
 	/* wait for regulator to stabilize */
 	msleep(160);
-- 
1.7.10


^ permalink raw reply related

* [PATCH 2/4] OMAPDSS: VENC: Check for errors from regulator_enable()
From: Mark Brown @ 2012-05-03 10:57 UTC (permalink / raw)
  To: Tomi Valkeinen, Florian Tobias Schandinat
  Cc: linux-fbdev, linux-omap, Mark Brown
In-Reply-To: <1336042634-28486-1-git-send-email-broonie@opensource.wolfsonmicro.com>

It is possible for regulator_enable() to fail and if it does fail that's
generally a bad sign for anything we try to do with the hardware afterwards
so check for and immediately return an error if regulator_enable() fails.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 drivers/video/omap2/dss/venc.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 9c3daf7..abfbd4a 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -443,7 +443,9 @@ static int venc_power_on(struct omap_dss_device *dssdev)
 	dispc_set_digit_size(dssdev->panel.timings.x_res,
 			dssdev->panel.timings.y_res/2);
 
-	regulator_enable(venc.vdda_dac_reg);
+	r = regulator_enable(venc.vdda_dac_reg);
+	if (r)
+		goto err;
 
 	if (dssdev->platform_enable)
 		dssdev->platform_enable(dssdev);
-- 
1.7.10


^ permalink raw reply related

* [PATCH 1/4] OMAP: DSS2: Remove suspicous and unused TAAL regulator API usage
From: Mark Brown @ 2012-05-03 10:57 UTC (permalink / raw)
  To: Tomi Valkeinen, Florian Tobias Schandinat
  Cc: linux-fbdev, linux-omap, Mark Brown

The TAAL driver contains some regulator support which is currently unused
(the code is there but the one panel supported by the driver doesn't have
any regulators provided). This code mostly looks like an open coded
version of the regulator core bulk API.

The only additional feature is that a voltage range can be set once when
the device is opened, though this is never varied at runtime. The general
expectation is that if the device is not actively managing the voltage of
the device (eg, doing DVFS) then any configuration will be done using the
constraints rather than by drivers, saving them code and ensuring that
they work well with systems where the voltage is not configurable.

If systems are added needing regulator support this can be added back in,
though it should be based on core features rather than open coding things.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 drivers/video/omap2/displays/panel-taal.c |   80 -----------------------------
 1 file changed, 80 deletions(-)

diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 0f21fa5..72d6307 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -30,7 +30,6 @@
 #include <linux/gpio.h>
 #include <linux/workqueue.h>
 #include <linux/slab.h>
-#include <linux/regulator/consumer.h>
 #include <linux/mutex.h>
 
 #include <video/omapdss.h>
@@ -55,73 +54,6 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
 
 static int taal_panel_reset(struct omap_dss_device *dssdev);
 
-struct panel_regulator {
-	struct regulator *regulator;
-	const char *name;
-	int min_uV;
-	int max_uV;
-};
-
-static void free_regulators(struct panel_regulator *regulators, int n)
-{
-	int i;
-
-	for (i = 0; i < n; i++) {
-		/* disable/put in reverse order */
-		regulator_disable(regulators[n - i - 1].regulator);
-		regulator_put(regulators[n - i - 1].regulator);
-	}
-}
-
-static int init_regulators(struct omap_dss_device *dssdev,
-			struct panel_regulator *regulators, int n)
-{
-	int r, i, v;
-
-	for (i = 0; i < n; i++) {
-		struct regulator *reg;
-
-		reg = regulator_get(&dssdev->dev, regulators[i].name);
-		if (IS_ERR(reg)) {
-			dev_err(&dssdev->dev, "failed to get regulator %s\n",
-				regulators[i].name);
-			r = PTR_ERR(reg);
-			goto err;
-		}
-
-		/* FIXME: better handling of fixed vs. variable regulators */
-		v = regulator_get_voltage(reg);
-		if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
-			r = regulator_set_voltage(reg, regulators[i].min_uV,
-						regulators[i].max_uV);
-			if (r) {
-				dev_err(&dssdev->dev,
-					"failed to set regulator %s voltage\n",
-					regulators[i].name);
-				regulator_put(reg);
-				goto err;
-			}
-		}
-
-		r = regulator_enable(reg);
-		if (r) {
-			dev_err(&dssdev->dev, "failed to enable regulator %s\n",
-				regulators[i].name);
-			regulator_put(reg);
-			goto err;
-		}
-
-		regulators[i].regulator = reg;
-	}
-
-	return 0;
-
-err:
-	free_regulators(regulators, i);
-
-	return r;
-}
-
 /**
  * struct panel_config - panel configuration
  * @name: panel name
@@ -150,8 +82,6 @@ struct panel_config {
 		unsigned int low;
 	} reset_sequence;
 
-	struct panel_regulator *regulators;
-	int num_regulators;
 };
 
 enum {
@@ -977,11 +907,6 @@ static int taal_probe(struct omap_dss_device *dssdev)
 
 	atomic_set(&td->do_update, 0);
 
-	r = init_regulators(dssdev, panel_config->regulators,
-			panel_config->num_regulators);
-	if (r)
-		goto err_reg;
-
 	td->workqueue = create_singlethread_workqueue("taal_esd");
 	if (td->workqueue = NULL) {
 		dev_err(&dssdev->dev, "can't create ESD workqueue\n");
@@ -1075,8 +1000,6 @@ err_gpio:
 err_bl:
 	destroy_workqueue(td->workqueue);
 err_wq:
-	free_regulators(panel_config->regulators, panel_config->num_regulators);
-err_reg:
 	kfree(td);
 err:
 	return r;
@@ -1113,9 +1036,6 @@ static void __exit taal_remove(struct omap_dss_device *dssdev)
 	/* reset, to be sure that the panel is in a valid state */
 	taal_hw_reset(dssdev);
 
-	free_regulators(td->panel_config->regulators,
-			td->panel_config->num_regulators);
-
 	kfree(td);
 }
 
-- 
1.7.10


^ permalink raw reply related

* [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Mark Brown @ 2012-05-03 10:53 UTC (permalink / raw)
  To: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	Wolfram Sang
  Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Mark Brown
In-Reply-To: <1335443839-22872-1-git-send-email-broonie@opensource.wolfsonmicro.com>

Since there are uses for I2C_M_NOSTART which are much more sensible and
standard than most of the protocol mangling functionality (the main one
being gather writes to devices where something like a register address
needs to be inserted before a block of data) create a new I2C_FUNC_NOSTART
for this feature and update all the users to use it.

In the case of regmap-i2c we remove the requirement for mangling as
I2C_M_NOSTART is the only mangling feature which is being used.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 Documentation/i2c/functionality       |    1 +
 drivers/base/regmap/regmap-i2c.c      |    2 +-
 drivers/i2c/algos/i2c-algo-bit.c      |    2 +-
 drivers/i2c/algos/i2c-algo-pcf.c      |    2 +-
 drivers/i2c/busses/i2c-nuc900.c       |    3 ++-
 drivers/i2c/busses/i2c-s3c2410.c      |    3 ++-
 drivers/input/joystick/as5011.c       |    1 +
 drivers/video/matrox/matroxfb_maven.c |    1 +
 include/linux/i2c.h                   |    5 +++--
 9 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/Documentation/i2c/functionality b/Documentation/i2c/functionality
index 42c17c1..fb2e77e 100644
--- a/Documentation/i2c/functionality
+++ b/Documentation/i2c/functionality
@@ -33,6 +33,7 @@ For the most up-to-date list of functionality constants, please check
   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA Handles the SMBus write_block_data command
   I2C_FUNC_SMBUS_READ_I2C_BLOCK   Handles the SMBus read_i2c_block_data command
   I2C_FUNC_SMBUS_WRITE_I2C_BLOCK  Handles the SMBus write_i2c_block_data command
+  I2C_FUNC_NOSTART                Transfers can be sent without a start
 
 A few combinations of the above flags are also defined for your convenience:
 
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 5f6b247..fa6bf52 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -42,7 +42,7 @@ static int regmap_i2c_gather_write(void *context,
 	/* If the I2C controller can't do a gather tell the core, it
 	 * will substitute in a linear write for us.
 	 */
-	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_PROTOCOL_MANGLING))
+	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
 		return -ENOTSUPP;
 
 	xfer[0].addr = i2c->addr;
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 7f0b832..fad22b0 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -608,7 +608,7 @@ bailout:
 
 static u32 bit_func(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+	return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
 	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
 	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
 	       I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 5c23795..8b38986 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -401,7 +401,7 @@ out:
 
 static u32 pcf_func(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+	return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
 	       I2C_FUNC_PROTOCOL_MANGLING;
 }
 
diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c
index 03b6157..a26dfb8 100644
--- a/drivers/i2c/busses/i2c-nuc900.c
+++ b/drivers/i2c/busses/i2c-nuc900.c
@@ -502,7 +502,8 @@ static int nuc900_i2c_xfer(struct i2c_adapter *adap,
 /* declare our i2c functionality */
 static u32 nuc900_i2c_func(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
+		I2C_FUNC_PROTOCOL_MANGLING;
 }
 
 /* i2c bus registration info */
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index fa0b134..0195915 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -626,7 +626,8 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
 /* declare our i2c functionality */
 static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
+		I2C_FUNC_PROTOCOL_MANGLING;
 }
 
 /* i2c bus registration info */
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
index 3063464..57d19d4 100644
--- a/drivers/input/joystick/as5011.c
+++ b/drivers/input/joystick/as5011.c
@@ -231,6 +231,7 @@ static int __devinit as5011_probe(struct i2c_client *client,
 	}
 
 	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_NOSTART |
 				     I2C_FUNC_PROTOCOL_MANGLING)) {
 		dev_err(&client->dev,
 			"need i2c bus that supports protocol mangling\n");
diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c
index 31b8f67..217678e 100644
--- a/drivers/video/matrox/matroxfb_maven.c
+++ b/drivers/video/matrox/matroxfb_maven.c
@@ -1243,6 +1243,7 @@ static int maven_probe(struct i2c_client *client,
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA |
 					      I2C_FUNC_SMBUS_BYTE_DATA |
+					      I2C_FUNC_NOSTART |
 					      I2C_FUNC_PROTOCOL_MANGLING))
 		goto ERROR0;
 	if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) {
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index b66cb60..da46925 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -541,7 +541,7 @@ struct i2c_msg {
 	__u16 flags;
 #define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
 #define I2C_M_RD		0x0001	/* read data, from slave to master */
-#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
 #define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
 #define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
 #define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
@@ -554,7 +554,7 @@ struct i2c_msg {
 
 #define I2C_FUNC_I2C			0x00000001
 #define I2C_FUNC_10BIT_ADDR		0x00000002
-#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_NOSTART etc. */
+#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_IGNORE_NAK etc. */
 #define I2C_FUNC_SMBUS_PEC		0x00000008
 #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_QUICK		0x00010000
@@ -569,6 +569,7 @@ struct i2c_msg {
 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
 #define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
 #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_NOSTART		0x10000000 /* I2C_M_NOSTART */
 
 #define I2C_FUNC_SMBUS_BYTE		(I2C_FUNC_SMBUS_READ_BYTE | \
 					 I2C_FUNC_SMBUS_WRITE_BYTE)
-- 
1.7.10


^ permalink raw reply related

* Re: [PATCH v2 3/4] leds: add LM3533 LED driver
From: Mark Brown @ 2012-05-03 10:43 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat, Arnd Bergmann,
	Andrew Morton, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <1336040799-18433-4-git-send-email-jhovold@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1272 bytes --]

On Thu, May 03, 2012 at 12:26:38PM +0200, Johan Hovold wrote:

> +What:		/sys/class/leds/<led>/risetime
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold <jhovold@gmail.com>
> +Description:
> +		Set the pattern generator fall and rise times (0..7), where
> +
> +		0 - 2048 us
> +		1 - 262 ms
> +		2 - 524 ms
> +		3 - 1.049 s
> +		4 - 2.097 s
> +		5 - 4.194 s
> +		6 - 8.389 s
> +		7 - 16.78 s
> +

Shouldn't these be controlled by led_blink_set() rather than a custom
ABI?

> +What:		/sys/class/leds/<led>/id
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold <jhovold@gmail.com>
> +Description:
> +		Get the id of this led (0..3).
> +

This should just be a generic LED subsystem thing?

> +What:		/sys/class/leds/<led>/max_current
> +Date:		April 2012
> +KernelVersion:	3.5
> +Contact:	Johan Hovold <jhovold@gmail.com>
> +Description:
> +		Set the full-scale current I_{LED_FULLSCALE} (0..31), where
> +
> +		I_{LED_FULLSCALE} = 5mA + max_current * 0.8mA
> +

Shouldn't this be set by platform data, the maximum current you can push
through the LEDs seems like a board dependant thing which won't change
dynamically at runtime.  The brightness can already be varied.

It'd also be nicer if the kernel did the calculation for the user.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH v2 1/4] mfd: add LM3533 lighting-power core driver
From: Mark Brown @ 2012-05-03 10:38 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat, Arnd Bergmann,
	Andrew Morton, linux-doc, linux-kernel, linux-iio, devel,
	linux-fbdev
In-Reply-To: <1336040799-18433-2-git-send-email-jhovold@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 745 bytes --]

On Thu, May 03, 2012 at 12:26:36PM +0200, Johan Hovold wrote:
> Add support for National Semiconductor / TI LM3533 lighting power chips.
> 
> This is the core driver which provides register access over I2C and
> registers the ambient-light-sensor, LED and backlight sub-drivers.

Reviwed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

though

> +	dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);

I'd expect you can drop these log messages, if there's stuff like this
missing we should add it to regmap.  At the minute the regmap logging is
via trace points rather than debug logs as you can leave them enabled
all the time.

Might also be worth moving some of the sysfs stuff to live with the
relevant drivers.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* [PATCH v2 4/4] backlight: add LM3533 backlight driver
From: Johan Hovold @ 2012-05-03 10:26 UTC (permalink / raw)
  To: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat
  Cc: Arnd Bergmann, Andrew Morton, Mark Brown, linux-doc, linux-kernel,
	linux-iio, devel, linux-fbdev, Johan Hovold
In-Reply-To: <1336040799-18433-1-git-send-email-jhovold@gmail.com>

Add sub-driver for the backlights on National Semiconductor / TI LM3533
lighting power chips.

The chip provides 256 brightness levels and ambient-light-sensor and pwm
input control.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
---

v2:
 - add sysfs-ABI documentation
 - open code max_current/pwm macros


 .../testing/sysfs-class-backlight-driver-lm3533    |   50 +++
 drivers/video/backlight/Kconfig                    |   12 +
 drivers/video/backlight/Makefile                   |    1 +
 drivers/video/backlight/lm3533_bl.c                |  458 ++++++++++++++++++++
 4 files changed, 521 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
 create mode 100644 drivers/video/backlight/lm3533_bl.c

diff --git a/Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533 b/Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
new file mode 100644
index 0000000..866fd3e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
@@ -0,0 +1,50 @@
+What:		/sys/class/backlight/<backlight>/als
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the ALS-control mode (0,..2), where
+
+		0 - disabled
+		1 - ALS-mapper 1 (backlight 0)
+		2 - ALS-mapper 2 (backlight 1)
+
+What:		/sys/class/backlight/<backlight>/id
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Get the id of this backlight (0, 1).
+
+What:		/sys/class/backlight/<backlight>/linear
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the brightness-mapping mode (0, 1), where
+
+		0 - exponential mode
+		1 - linear mode
+
+What:		/sys/class/backlight/<backlight>/max_current
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the full-scale current I_{LED_FULLSCALE} (0..31), where
+
+		I_{LED_FULLSCALE} = 5mA + max_current * 0.8mA
+
+What:		/sys/class/backlight/<backlight>/pwm
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the PWM-input control mask (5 bits), where
+
+		bit 5 - PWM-input enabled in Zone 4
+		bit 4 - PWM-input enabled in Zone 3
+		bit 3 - PWM-input enabled in Zone 2
+		bit 2 - PWM-input enabled in Zone 1
+		bit 1 - PWM-input enabled in Zone 0
+		bit 0 - PWM-input enabled
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index af16884..fa2b037 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -184,6 +184,18 @@ config BACKLIGHT_GENERIC
 	  known as the Corgi backlight driver. If you have a Sharp Zaurus
 	  SL-C7xx, SL-Cxx00 or SL-6000x say y.
 
+config BACKLIGHT_LM3533
+	tristate "Backlight Driver for LM3533"
+	depends on BACKLIGHT_CLASS_DEVICE
+	depends on MFD_LM3533
+	help
+	  Say Y to enable the backlight driver for National Semiconductor / TI
+	  LM3533 Lighting Power chips.
+
+	  The backlights can be controlled directly, through PWM input, or by
+	  the ambient-light-sensor interface. The chip supports 256 brightness
+	  levels.
+
 config BACKLIGHT_LOCOMO
 	tristate "Sharp LOCOMO LCD/Backlight Driver"
 	depends on SHARP_LOCOMO
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 36855ae..a2ac9cf 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_BACKLIGHT_EP93XX)	+= ep93xx_bl.o
 obj-$(CONFIG_BACKLIGHT_GENERIC)	+= generic_bl.o
 obj-$(CONFIG_BACKLIGHT_HP700)	+= jornada720_bl.o
 obj-$(CONFIG_BACKLIGHT_HP680)	+= hp680_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3533)	+= lm3533_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)	+= locomolcd.o
 obj-$(CONFIG_BACKLIGHT_LP855X)	+= lp855x_bl.o
 obj-$(CONFIG_BACKLIGHT_OMAP1)	+= omap1_bl.o
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
new file mode 100644
index 0000000..4100c7a
--- /dev/null
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -0,0 +1,458 @@
+/*
+ * lm3533-bl.c -- LM3533 Backlight driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_HVCTRLBANK_COUNT		2
+#define LM3533_BL_MAX_BRIGHTNESS	255
+
+#define LM3533_REG_CTRLBANK_AB_BCONF	0x1a
+
+
+struct lm3533_bl {
+	struct lm3533 *lm3533;
+	struct lm3533_ctrlbank cb;
+	struct backlight_device *bd;
+	int id;
+};
+
+
+static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl)
+{
+	return bl->id;
+}
+
+static int lm3533_bl_update_status(struct backlight_device *bd)
+{
+	struct lm3533_bl *bl = bl_get_data(bd);
+	int brightness = bd->props.brightness;
+
+	if (bd->props.power != FB_BLANK_UNBLANK)
+		brightness = 0;
+	if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+		brightness = 0;
+
+	return lm3533_ctrlbank_set_brightness(&bl->cb, (u8)brightness);
+}
+
+static int lm3533_bl_get_brightness(struct backlight_device *bd)
+{
+	struct lm3533_bl *bl = bl_get_data(bd);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val);
+	if (ret)
+		return ret;
+
+	return val;
+}
+
+static const struct backlight_ops lm3533_bl_ops = {
+	.get_brightness	= lm3533_bl_get_brightness,
+	.update_status	= lm3533_bl_update_status,
+};
+
+static ssize_t show_id(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id);
+}
+
+/*
+ * ALS-control setting:
+ *
+ *   0 - ALS disabled
+ *   1 - ALS-mapper 1 (backlight 0)
+ *   2 - ALS-mapper 2 (backlight 1)
+ */
+static ssize_t show_als(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+	u8 val;
+	u8 mask;
+	int als;
+	int ret;
+
+	ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+	if (ret)
+		return ret;
+
+	mask = 2 * ctrlbank;
+	als = val & mask;
+	if (als)
+		als = ctrlbank + 1;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", als);
+}
+
+static ssize_t store_als(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+	int als;
+	u8 val;
+	u8 mask;
+	int ret;
+
+	if (kstrtoint(buf, 0, &als))
+		return -EINVAL;
+
+	if (als != 0 && (als != ctrlbank + 1))
+		return -EINVAL;
+
+	mask = 1 << (2 * ctrlbank);
+
+	if (als)
+		val = mask;
+	else
+		val = 0;
+
+	ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+									mask);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_linear(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	u8 val;
+	u8 mask;
+	int linear;
+	int ret;
+
+	ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+	if (ret)
+		return ret;
+
+	mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+	if (val & mask)
+		linear = 1;
+	else
+		linear = 0;
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
+}
+
+static ssize_t store_linear(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	unsigned long linear;
+	u8 mask;
+	u8 val;
+	int ret;
+
+	if (kstrtoul(buf, 0, &linear))
+		return -EINVAL;
+
+	mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+	if (linear)
+		val = mask;
+	else
+		val = 0;
+
+	ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+									mask);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_max_current(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_max_current(&bl->cb, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_max_current(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val))
+		return -EINVAL;
+
+	ret = lm3533_ctrlbank_set_max_current(&bl->cb, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_pwm(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_pwm(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val))
+		return -EINVAL;
+
+	ret = lm3533_ctrlbank_set_pwm(&bl->cb, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static LM3533_ATTR_RW(als);
+static LM3533_ATTR_RO(id);
+static LM3533_ATTR_RW(linear);
+static LM3533_ATTR_RW(max_current);
+static LM3533_ATTR_RW(pwm);
+
+static struct attribute *lm3533_bl_attributes[] = {
+	&dev_attr_als.attr,
+	&dev_attr_id.attr,
+	&dev_attr_linear.attr,
+	&dev_attr_max_current.attr,
+	&dev_attr_pwm.attr,
+	NULL,
+};
+
+static mode_t lm3533_bl_attr_is_visible(struct kobject *kobj,
+					     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct lm3533_bl *bl = dev_get_drvdata(dev);
+	mode_t mode = attr->mode;
+
+	if (attr = &dev_attr_als.attr) {
+		if (!bl->lm3533->have_als)
+			mode = 0;
+	}
+
+	return mode;
+};
+
+static struct attribute_group lm3533_bl_attribute_group = {
+	.is_visible	= lm3533_bl_attr_is_visible,
+	.attrs		= lm3533_bl_attributes
+};
+
+static int __devinit lm3533_bl_setup(struct lm3533_bl *bl,
+					struct lm3533_bl_platform_data *pdata)
+{
+	int ret;
+
+	ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current);
+	if (ret)
+		return ret;
+
+	return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm);
+}
+
+static int __devinit lm3533_bl_probe(struct platform_device *pdev)
+{
+	struct lm3533 *lm3533;
+	struct lm3533_bl_platform_data *pdata;
+	struct lm3533_bl *bl;
+	struct backlight_device *bd;
+	struct backlight_properties props;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533 = dev_get_drvdata(pdev->dev.parent);
+	if (!lm3533)
+		return -EINVAL;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) {
+		dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+	if (!bl) {
+		dev_err(&pdev->dev,
+				"failed to allocate memory for backlight\n");
+		return -ENOMEM;
+	}
+
+	bl->lm3533 = lm3533;
+	bl->id = pdev->id;
+
+	bl->cb.lm3533 = lm3533;
+	bl->cb.id = lm3533_bl_get_ctrlbank_id(bl);
+	bl->cb.dev = NULL;			/* until registered */
+
+	memset(&props, 0, sizeof(props));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = LM3533_BL_MAX_BRIGHTNESS;
+	props.brightness = pdata->default_brightness;
+	bd = backlight_device_register(pdata->name, pdev->dev.parent, bl,
+						&lm3533_bl_ops, &props);
+	if (IS_ERR(bd)) {
+		dev_err(&pdev->dev, "failed to register backlight device\n");
+		ret = PTR_ERR(bd);
+		goto err_free;
+	}
+
+	bl->bd = bd;
+	bl->cb.dev = &bl->bd->dev;
+
+	platform_set_drvdata(pdev, bl);
+
+	ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create sysfs attributes\n");
+		goto err_unregister;
+	}
+
+	backlight_update_status(bd);
+
+	ret = lm3533_bl_setup(bl, pdata);
+	if (ret)
+		goto err_sysfs_remove;
+
+	ret = lm3533_ctrlbank_enable(&bl->cb);
+	if (ret)
+		goto err_sysfs_remove;
+
+	return 0;
+
+err_sysfs_remove:
+	sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+err_unregister:
+	backlight_device_unregister(bd);
+err_free:
+	kfree(bl);
+
+	return ret;
+}
+
+static int __devexit lm3533_bl_remove(struct platform_device *pdev)
+{
+	struct lm3533_bl *bl = platform_get_drvdata(pdev);
+	struct backlight_device *bd = bl->bd;
+
+	dev_dbg(&bd->dev, "%s\n", __func__);
+
+	bd->props.power = FB_BLANK_POWERDOWN;
+	bd->props.brightness = 0;
+
+	lm3533_ctrlbank_disable(&bl->cb);
+	sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+	backlight_device_unregister(bd);
+	kfree(bl);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int lm3533_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	return lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static int lm3533_bl_resume(struct platform_device *pdev)
+{
+	struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	return lm3533_ctrlbank_enable(&bl->cb);
+}
+#else
+#define lm3533_bl_suspend	NULL
+#define lm3533_bl_resume	NULL
+#endif
+
+static void lm3533_bl_shutdown(struct platform_device *pdev)
+{
+	struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static struct platform_driver lm3533_bl_driver = {
+	.driver = {
+		.name	= "lm3533-backlight",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= lm3533_bl_probe,
+	.remove		= __devexit_p(lm3533_bl_remove),
+	.shutdown	= lm3533_bl_shutdown,
+	.suspend	= lm3533_bl_suspend,
+	.resume		= lm3533_bl_resume,
+};
+module_platform_driver(lm3533_bl_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Backlight driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-backlight");
-- 
1.7.8.5


^ permalink raw reply related

* [PATCH v2 3/4] leds: add LM3533 LED driver
From: Johan Hovold @ 2012-05-03 10:26 UTC (permalink / raw)
  To: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat
  Cc: Arnd Bergmann, Andrew Morton, Mark Brown, linux-doc, linux-kernel,
	linux-iio, devel, linux-fbdev, Johan Hovold
In-Reply-To: <1336040799-18433-1-git-send-email-jhovold@gmail.com>

Add sub-driver for the LEDs on National Semiconductor / TI LM3533
lighting power chips.

The chip provides 256 brightness levels, hardware accelerated blinking
as well as ambient-light-sensor and pwm input control.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
---

v2:
 - add sysfs-ABI documentation
 - open code max_current/pwm macros


 .../ABI/testing/sysfs-class-led-driver-lm3533      |   67 ++
 drivers/leds/Kconfig                               |   13 +
 drivers/leds/Makefile                              |    1 +
 drivers/leds/leds-lm3533.c                         |  741 ++++++++++++++++++++
 4 files changed, 822 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-led-driver-lm3533
 create mode 100644 drivers/leds/leds-lm3533.c

diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-lm3533 b/Documentation/ABI/testing/sysfs-class-led-driver-lm3533
new file mode 100644
index 0000000..fc1ee04
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-led-driver-lm3533
@@ -0,0 +1,67 @@
+What:		/sys/class/leds/<led>/als
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the ALS-control mode (0, 2, 3), where
+
+		0 - disabled
+		2 - ALS-mapper 2
+		3 - ALS-mapper 3
+
+What:		/sys/class/leds/<led>/falltime
+What:		/sys/class/leds/<led>/risetime
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the pattern generator fall and rise times (0..7), where
+
+		0 - 2048 us
+		1 - 262 ms
+		2 - 524 ms
+		3 - 1.049 s
+		4 - 2.097 s
+		5 - 4.194 s
+		6 - 8.389 s
+		7 - 16.78 s
+
+What:		/sys/class/leds/<led>/id
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Get the id of this led (0..3).
+
+What:		/sys/class/leds/<led>/linear
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the brightness-mapping mode (0, 1), where
+
+		0 - exponential mode
+		1 - linear mode
+
+What:		/sys/class/leds/<led>/max_current
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the full-scale current I_{LED_FULLSCALE} (0..31), where
+
+		I_{LED_FULLSCALE} = 5mA + max_current * 0.8mA
+
+What:		/sys/class/leds/<led>/pwm
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the PWM-input control mask (5 bits), where
+
+		bit 5 - PWM-input enabled in Zone 4
+		bit 4 - PWM-input enabled in Zone 3
+		bit 3 - PWM-input enabled in Zone 2
+		bit 2 - PWM-input enabled in Zone 1
+		bit 1 - PWM-input enabled in Zone 0
+		bit 0 - PWM-input enabled
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ff4b8cf..19bd829 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -50,6 +50,19 @@ config LEDS_LM3530
 	  controlled manually or using PWM input or using ambient
 	  light automatically.
 
+config LEDS_LM3533
+	tristate "LED support for LM3533"
+	depends on LEDS_CLASS
+	depends on MFD_LM3533
+	help
+	  This option enables support for the LEDs on National Semiconductor /
+	  TI LM3533 Lighting Power chips.
+
+	  The LEDs can be controlled directly, through PWM input, or by the
+	  ambient-light-sensor interface. The chip supports
+	  hardware-accelerated blinking with maximum on and off periods of 9.8
+	  and 77 seconds respectively.
+
 config LEDS_LOCOMO
 	tristate "LED Support for Locomo device"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 890481c..f39a526 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_ATMEL_PWM)		+= leds-atmel-pwm.o
 obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
 obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
+obj-$(CONFIG_LEDS_LM3533)		+= leds-lm3533.o
 obj-$(CONFIG_LEDS_MIKROTIK_RB532)	+= leds-rb532.o
 obj-$(CONFIG_LEDS_S3C24XX)		+= leds-s3c24xx.o
 obj-$(CONFIG_LEDS_NET48XX)		+= leds-net48xx.o
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
new file mode 100644
index 0000000..7d02f4b
--- /dev/null
+++ b/drivers/leds/leds-lm3533.c
@@ -0,0 +1,741 @@
+/*
+ * leds-lm3533.c -- LM3533 LED driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/mfd/core.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_LVCTRLBANK_MIN		2
+#define LM3533_LVCTRLBANK_MAX		5
+#define LM3533_LVCTRLBANK_COUNT		4
+#define LM3533_RISEFALLTIME_MAX		7
+#define LM3533_ALS_LV_MIN		2
+#define LM3533_ALS_LV_MAX		3
+
+#define LM3533_REG_CTRLBANK_BCONF_BASE		0x1b
+#define LM3533_REG_PATTERN_ENABLE		0x28
+#define LM3533_REG_PATTERN_LOW_TIME_BASE	0x71
+#define LM3533_REG_PATTERN_HIGH_TIME_BASE	0x72
+#define LM3533_REG_PATTERN_RISETIME_BASE	0x74
+#define LM3533_REG_PATTERN_FALLTIME_BASE	0x75
+
+#define LM3533_REG_PATTERN_STEP			0x10
+
+#define LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK	0x04
+#define LM3533_REG_CTRLBANK_BCONF_ALS_MASK	0x03
+
+#define LM3533_LED_FLAG_PATTERN_ENABLE		1
+
+
+struct lm3533_led {
+	struct lm3533 *lm3533;
+	struct lm3533_ctrlbank cb;
+	struct led_classdev cdev;
+	int id;
+
+	struct mutex mutex;
+	unsigned long flags;
+
+	struct work_struct work;
+	u8 new_brightness;
+};
+
+#define to_lm3533_led(_cdev) \
+	container_of(_cdev, struct lm3533_led, cdev)
+
+
+static inline int lm3533_led_get_ctrlbank_id(struct lm3533_led *led)
+{
+	return led->id + 2;
+}
+
+static inline u8 lm3533_led_get_lv_reg(struct lm3533_led *led, u8 base)
+{
+	return base + led->id;
+}
+
+static inline u8 lm3533_led_get_pattern(struct lm3533_led *led)
+{
+	return led->id;
+}
+
+static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led,
+								u8 base)
+{
+	return base + lm3533_led_get_pattern(led) * LM3533_REG_PATTERN_STEP;
+}
+
+static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable)
+{
+	u8 mask;
+	u8 val;
+	int pattern;
+	int state;
+	int ret = 0;
+
+	dev_dbg(led->cdev.dev, "%s - %d\n", __func__, enable);
+
+	mutex_lock(&led->mutex);
+
+	state = test_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags);
+	if ((enable && state) || (!enable && !state))
+		goto out;
+
+	pattern = lm3533_led_get_pattern(led);
+	mask = 1 << (2 * pattern);
+
+	if (enable)
+		val = mask;
+	else
+		val = 0;
+
+	ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask);
+	if (ret) {
+		dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n",
+							pattern, enable);
+		goto out;
+	}
+
+	__change_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags);
+out:
+	mutex_unlock(&led->mutex);
+
+	return ret;
+}
+
+static void lm3533_led_work(struct work_struct *work)
+{
+	struct lm3533_led *led = container_of(work, struct lm3533_led, work);
+
+	dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness);
+
+	if (led->new_brightness = 0)
+		lm3533_led_pattern_enable(led, 0);	/* disable blink */
+
+	lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness);
+}
+
+static void lm3533_led_set(struct led_classdev *cdev,
+						enum led_brightness value)
+{
+	struct lm3533_led *led = to_lm3533_led(cdev);
+
+	dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value);
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static enum led_brightness lm3533_led_get(struct led_classdev *cdev)
+{
+	struct lm3533_led *led = to_lm3533_led(cdev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_brightness(&led->cb, &val);
+	if (ret)
+		return ret;
+
+	dev_dbg(led->cdev.dev, "%s - %u\n", __func__, val);
+
+	return val;
+}
+
+/* Pattern generator defines -- delays in us */
+#define LM3533_LED_DELAY_GROUP1_BASE	0x00
+#define LM3533_LED_DELAY_GROUP2_BASE	0x3d
+#define LM3533_LED_DELAY_GROUP3_BASE	0x80
+#define LM3533_LED_DELAY_MAX		0xff
+
+#define LM3533_LED_DELAY_GROUP1_STEP	16384
+#define LM3533_LED_DELAY_GROUP2_STEP	131072
+#define LM3533_LED_DELAY_GROUP3_STEP	524288
+#define LM3533_LED_DELAY_GROUP1_MIN	16384
+#define LM3533_LED_DELAY_GROUP2_MIN	1130496
+#define LM3533_LED_DELAY_GROUP3_MIN	10305536
+#define LM3533_LED_DELAY_GROUP1_MAX	999424
+#define LM3533_LED_DELAY_GROUP2_MAX	9781248
+#define LM3533_LED_DELAY_GROUP3_MAX	76890112
+
+/* Delay limits in ms */
+#define LM3533_LED_DELAY_ON_MAX		9845
+#define LM3533_LED_DELAY_OFF_MAX	77140
+
+static int time_to_val(long *t, long t_min, long t_max, long t_step,
+							int v_min, int v_max)
+{
+	int val;
+
+	*t += t_step / 2;
+	val = (*t - t_min) / t_step + v_min;
+	val = clamp(val, v_min, v_max);
+	*t = t_step * (val - v_min) + t_min;
+
+	return val;
+}
+
+static int lm3533_led_get_delay(long *delay)
+{
+	int val;
+
+	*delay *= 1000;
+
+	if (*delay >= LM3533_LED_DELAY_GROUP3_MIN -
+					LM3533_LED_DELAY_GROUP3_STEP / 2) {
+		val = time_to_val(delay, LM3533_LED_DELAY_GROUP3_MIN,
+					LM3533_LED_DELAY_GROUP3_MAX,
+					LM3533_LED_DELAY_GROUP3_STEP,
+					LM3533_LED_DELAY_GROUP3_BASE,
+					0xff);
+	} else if (*delay >= LM3533_LED_DELAY_GROUP2_MIN -
+					LM3533_LED_DELAY_GROUP2_STEP / 2) {
+		val = time_to_val(delay, LM3533_LED_DELAY_GROUP2_MIN,
+					LM3533_LED_DELAY_GROUP2_MAX,
+					LM3533_LED_DELAY_GROUP2_STEP,
+					LM3533_LED_DELAY_GROUP2_BASE,
+					LM3533_LED_DELAY_GROUP3_BASE - 1);
+	} else {
+		val = time_to_val(delay, LM3533_LED_DELAY_GROUP1_MIN,
+					LM3533_LED_DELAY_GROUP1_MAX,
+					LM3533_LED_DELAY_GROUP1_STEP,
+					LM3533_LED_DELAY_GROUP1_BASE,
+					LM3533_LED_DELAY_GROUP2_BASE - 1);
+	}
+
+	*delay /= 1000;
+
+	return val;
+}
+
+static int lm3533_led_delay_set(struct lm3533_led *led, u8 base,
+							unsigned long *delay)
+{
+	u8 val;
+	u8 reg;
+	long t;
+	int ret;
+
+	t = *delay;
+	val = lm3533_led_get_delay(&t);
+
+	dev_dbg(led->cdev.dev, "%s - %lu: %ld (0x%02x)\n", __func__,
+							*delay, t, val);
+	reg = lm3533_led_get_pattern_reg(led, base);
+	ret = lm3533_write(led->lm3533, reg, val);
+	if (ret)
+		dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg);
+
+	*delay = t;
+
+	return ret;
+}
+
+static int lm3533_led_delay_on_set(struct lm3533_led *led, unsigned long *t)
+{
+	*t = min_t(long, *t, LM3533_LED_DELAY_GROUP2_MAX / 1000);
+
+	return lm3533_led_delay_set(led, LM3533_REG_PATTERN_HIGH_TIME_BASE, t);
+}
+
+static int lm3533_led_delay_off_set(struct lm3533_led *led, unsigned long *t)
+{
+	*t = min_t(long, *t, LM3533_LED_DELAY_GROUP3_MAX / 1000);
+
+	return lm3533_led_delay_set(led, LM3533_REG_PATTERN_LOW_TIME_BASE, t);
+}
+
+static int lm3533_led_blink_set(struct led_classdev *cdev,
+				unsigned long *delay_on,
+				unsigned long *delay_off)
+{
+	struct lm3533_led *led = to_lm3533_led(cdev);
+	int ret;
+
+	dev_dbg(led->cdev.dev, "%s - on = %lu, off = %lu\n", __func__,
+							*delay_on, *delay_off);
+
+	if (*delay_on > LM3533_LED_DELAY_ON_MAX ||
+					*delay_off > LM3533_LED_DELAY_OFF_MAX)
+		return -EINVAL;
+
+	if (*delay_on = 0 && *delay_off = 0) {
+		*delay_on = 500;
+		*delay_off = 500;
+	}
+
+	ret = lm3533_led_delay_on_set(led, delay_on);
+	if (ret)
+		return ret;
+
+	ret = lm3533_led_delay_off_set(led, delay_off);
+	if (ret)
+		return ret;
+
+	return lm3533_led_pattern_enable(led, 1);
+}
+
+static ssize_t show_id(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", led->id);
+}
+
+/*
+ * Pattern generator rise/fall times:
+ *
+ *   0 - 2048 us (default)
+ *   1 - 262 ms
+ *   2 - 524 ms
+ *   3 - 1.049 s
+ *   4 - 2.097 s
+ *   5 - 4.194 s
+ *   6 - 8.389 s
+ *   7 - 16.78 s
+ */
+static ssize_t show_risefalltime(struct device *dev,
+					struct device_attribute *attr,
+					char *buf, u8 base)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	ssize_t ret;
+	u8 reg;
+	u8 val;
+
+	reg = lm3533_led_get_pattern_reg(led, base);
+	ret = lm3533_read(led->lm3533, reg, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", val);
+}
+
+static ssize_t show_risetime(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return show_risefalltime(dev, attr, buf,
+					LM3533_REG_PATTERN_RISETIME_BASE);
+}
+
+static ssize_t show_falltime(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return show_risefalltime(dev, attr, buf,
+					LM3533_REG_PATTERN_FALLTIME_BASE);
+}
+
+static ssize_t store_risefalltime(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len, u8 base)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 val;
+	u8 reg;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val) || val > LM3533_RISEFALLTIME_MAX)
+		return -EINVAL;
+
+	reg = lm3533_led_get_pattern_reg(led, base);
+	ret = lm3533_write(led->lm3533, reg, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t store_risetime(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	return store_risefalltime(dev, attr, buf, len,
+					LM3533_REG_PATTERN_RISETIME_BASE);
+}
+
+static ssize_t store_falltime(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	return store_risefalltime(dev, attr, buf, len,
+					LM3533_REG_PATTERN_FALLTIME_BASE);
+}
+
+/*
+ * ALS-control setting:
+ *
+ *   0 - ALS disabled
+ *   2 - ALS-mapper 2
+ *   3 - ALS-mapper 3
+ */
+static ssize_t show_als(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 reg;
+	u8 val;
+	int als;
+	int ret;
+
+	reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+	ret = lm3533_read(led->lm3533, reg, &val);
+	if (ret)
+		return ret;
+
+	als = val & LM3533_REG_CTRLBANK_BCONF_ALS_MASK;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", als);
+}
+
+static ssize_t store_als(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 als;
+	u8 reg;
+	u8 mask;
+	int ret;
+
+	if (kstrtou8(buf, 0, &als))
+		return -EINVAL;
+
+	if (als != 0 && (als < LM3533_ALS_LV_MIN || als > LM3533_ALS_LV_MAX))
+		return -EINVAL;
+
+	reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+	mask = LM3533_REG_CTRLBANK_BCONF_ALS_MASK;
+
+	ret = lm3533_update(led->lm3533, reg, als, mask);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_linear(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 reg;
+	u8 val;
+	int linear;
+	int ret;
+
+	reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+	ret = lm3533_read(led->lm3533, reg, &val);
+	if (ret)
+		return ret;
+
+	if (val & LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK)
+		linear = 1;
+	else
+		linear = 0;
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
+}
+
+static ssize_t store_linear(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	unsigned long linear;
+	u8 reg;
+	u8 mask;
+	u8 val;
+	int ret;
+
+	if (kstrtoul(buf, 0, &linear))
+		return -EINVAL;
+
+	reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+	mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK;
+
+	if (linear)
+		val = mask;
+	else
+		val = 0;
+
+	ret = lm3533_update(led->lm3533, reg, val, mask);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_max_current(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_max_current(&led->cb, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_max_current(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val))
+		return -EINVAL;
+
+	ret = lm3533_ctrlbank_set_max_current(&led->cb, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t show_pwm(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_ctrlbank_get_pwm(&led->cb, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_pwm(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val))
+		return -EINVAL;
+
+	ret = lm3533_ctrlbank_set_pwm(&led->cb, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static LM3533_ATTR_RW(als);
+static LM3533_ATTR_RW(falltime);
+static LM3533_ATTR_RO(id);
+static LM3533_ATTR_RW(linear);
+static LM3533_ATTR_RW(max_current);
+static LM3533_ATTR_RW(pwm);
+static LM3533_ATTR_RW(risetime);
+
+static struct attribute *lm3533_led_attributes[] = {
+	&dev_attr_als.attr,
+	&dev_attr_falltime.attr,
+	&dev_attr_id.attr,
+	&dev_attr_linear.attr,
+	&dev_attr_max_current.attr,
+	&dev_attr_pwm.attr,
+	&dev_attr_risetime.attr,
+	NULL,
+};
+
+static mode_t lm3533_led_attr_is_visible(struct kobject *kobj,
+					     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm3533_led *led = to_lm3533_led(led_cdev);
+	mode_t mode = attr->mode;
+
+	if (attr = &dev_attr_als.attr) {
+		if (!led->lm3533->have_als)
+			mode = 0;
+	}
+
+	return mode;
+};
+
+static struct attribute_group lm3533_led_attribute_group = {
+	.is_visible	= lm3533_led_attr_is_visible,
+	.attrs		= lm3533_led_attributes
+};
+
+static int __devinit lm3533_led_setup(struct lm3533_led *led,
+					struct lm3533_led_platform_data *pdata)
+{
+	int ret;
+
+	ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current);
+	if (ret)
+		return ret;
+
+	return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm);
+}
+
+static int __devinit lm3533_led_probe(struct platform_device *pdev)
+{
+	struct lm3533 *lm3533;
+	struct lm3533_led_platform_data *pdata;
+	struct lm3533_led *led;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533 = dev_get_drvdata(pdev->dev.parent);
+	if (!lm3533)
+		return -EINVAL;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) {
+		dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	led = kzalloc(sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->lm3533 = lm3533;
+	led->cdev.name = pdata->name;
+	led->cdev.default_trigger = pdata->default_trigger;
+	led->cdev.brightness_set = lm3533_led_set;
+	led->cdev.brightness_get = lm3533_led_get;
+	led->cdev.blink_set = lm3533_led_blink_set;
+	led->cdev.brightness = LED_OFF;
+	led->id = pdev->id;
+
+	mutex_init(&led->mutex);
+	INIT_WORK(&led->work, lm3533_led_work);
+
+	/* The class framework makes a callback to get brightness during
+	 * registration so use parent device (for error reporting) until
+	 * registered.
+	 */
+	led->cb.lm3533 = lm3533;
+	led->cb.id = lm3533_led_get_ctrlbank_id(led);
+	led->cb.dev = lm3533->dev;
+
+	platform_set_drvdata(pdev, led);
+
+	ret = led_classdev_register(pdev->dev.parent, &led->cdev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id);
+		goto err_free;
+	}
+
+	led->cb.dev = led->cdev.dev;
+
+	ret = sysfs_create_group(&led->cdev.dev->kobj,
+						&lm3533_led_attribute_group);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create sysfs attributes\n");
+		goto err_unregister;
+	}
+
+	ret = lm3533_led_setup(led, pdata);
+	if (ret)
+		goto err_sysfs_remove;
+
+	ret = lm3533_ctrlbank_enable(&led->cb);
+	if (ret)
+		goto err_sysfs_remove;
+
+	return 0;
+
+err_sysfs_remove:
+	sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group);
+err_unregister:
+	led_classdev_unregister(&led->cdev);
+	flush_work_sync(&led->work);
+err_free:
+	kfree(led);
+
+	return ret;
+}
+
+static int __devexit lm3533_led_remove(struct platform_device *pdev)
+{
+	struct lm3533_led *led = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533_ctrlbank_disable(&led->cb);
+	sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group);
+	led_classdev_unregister(&led->cdev);
+	flush_work_sync(&led->work);
+	kfree(led);
+
+	return 0;
+}
+
+static void lm3533_led_shutdown(struct platform_device *pdev)
+{
+
+	struct lm3533_led *led = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533_ctrlbank_disable(&led->cb);
+	lm3533_led_set(&led->cdev, LED_OFF);		/* disable blink */
+	flush_work_sync(&led->work);
+}
+
+static struct platform_driver lm3533_led_driver = {
+	.driver = {
+		.name = "lm3533-leds",
+		.owner = THIS_MODULE,
+	},
+	.probe		= lm3533_led_probe,
+	.remove		= __devexit_p(lm3533_led_remove),
+	.shutdown	= lm3533_led_shutdown,
+};
+module_platform_driver(lm3533_led_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-leds");
-- 
1.7.8.5


^ permalink raw reply related

* [PATCH v2 2/4] iio: add LM3533 ambient light sensor driver
From: Johan Hovold @ 2012-05-03 10:26 UTC (permalink / raw)
  To: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat
  Cc: Arnd Bergmann, Andrew Morton, Mark Brown, linux-doc, linux-kernel,
	linux-iio, devel, linux-fbdev, Johan Hovold
In-Reply-To: <1336040799-18433-1-git-send-email-jhovold@gmail.com>

Add sub-driver for the ambient light sensor interface on National
Semiconductor / TI LM3533 lighting power chips.

The sensor interface can be used to control the LEDs and backlights of
the chip through defining five light zones and three sets of
corresponding brightness target levels.

The driver provides raw and mean adc readings along with the current
light zone through sysfs. A threshold event can be generated on zone
changes.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
---

v2:
 - reimplement using iio
 - add sysfs-ABI documentation


 .../Documentation/sysfs-bus-iio-light-lm3533-als   |   62 ++
 drivers/staging/iio/light/Kconfig                  |   16 +
 drivers/staging/iio/light/Makefile                 |    1 +
 drivers/staging/iio/light/lm3533-als.c             |  617 ++++++++++++++++++++
 4 files changed, 696 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
 create mode 100644 drivers/staging/iio/light/lm3533-als.c

diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
new file mode 100644
index 0000000..9849d14
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
@@ -0,0 +1,62 @@
+What:		/sys/bus/iio/devices/iio:deviceX/gain
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the ALS gain-resistor setting (0..127) for analog input
+		mode, where
+
+		0000000 - ALS input is high impedance
+		0000001 - 200kOhm (10uA at 2V full-scale)
+		0000010 - 100kOhm (20uA at 2V full-scale)
+		...
+		1111110 - 1.587kOhm (1.26mA at 2V full-scale)
+		1111111 - 1.575kOhm (1.27mA at 2V full-scale)
+
+		R_als = 2V / (10uA * gain)	(gain > 0)
+
+What:		/sys/bus/iio/devices/iio:deviceX/illuminance_zone
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Get the current light zone (0..4) as defined by the
+		in_illuminance_thresh[n]_{falling,rising} thresholds.
+
+What:		/sys/.../events/in_illuminance_thresh_either_en
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Event generated when channel passes one of the four threshold
+		in either direction (rising|falling) and a zone change occurs.
+		The corresponding light zone can be read from
+		illuminance_zone.
+
+What:		/sys/.../events/illuminance_thresh0_falling_value
+What:		/sys/.../events/illuminance_thresh0_raising_value
+What:		/sys/.../events/illuminance_thresh1_falling_value
+What:		/sys/.../events/illuminance_thresh1_raising_value
+What:		/sys/.../events/illuminance_thresh2_falling_value
+What:		/sys/.../events/illuminance_thresh2_raising_value
+What:		/sys/.../events/illuminance_thresh3_falling_value
+What:		/sys/.../events/illuminance_thresh3_raising_value
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Specifies the value of threshold that the device is comparing
+		against for the events enabled by
+		in_illuminance_thresh_either_en, and defines the
+		the five light zones.
+
+		These thresholds correspond to the eight zone-boundary
+		registers (boundary[n]_{low,high}).
+
+What:		/sys/bus/iio/devices/iio:deviceX/target[m]_[n]
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the target brightness for ALS-mapper m in light zone n
+		(0..255), where m in 1..3 and n in 0..4.
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index e7e9159..263e44a 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -24,6 +24,22 @@ config SENSORS_TSL2563
 	 This driver can also be built as a module.  If so, the module
 	 will be called tsl2563.
 
+config SENSORS_LM3533
+	tristate "LM3533 ambient light sensor"
+	depends on MFD_LM3533
+	help
+	  If you say yes here you get support for the ambient light sensor
+	  interface on National Semiconductor / TI LM3533 Lighting Power
+	  chips.
+
+	  The sensor interface can be used to control the LEDs and backlights
+	  of the chip through defining five light zones and three sets of
+	  corresponding brightness target levels.
+
+	  The driver provides raw and mean adc readings along with the current
+	  light zone through sysfs. A threshold event can be generated on zone
+	  changes.
+
 config TSL2583
 	tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
 	depends on I2C
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 3011fbf..16a60a2 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -4,4 +4,5 @@
 
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
+obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
 obj-$(CONFIG_TSL2583)	+= tsl2583.o
diff --git a/drivers/staging/iio/light/lm3533-als.c b/drivers/staging/iio/light/lm3533-als.c
new file mode 100644
index 0000000..e2c9be6
--- /dev/null
+++ b/drivers/staging/iio/light/lm3533-als.c
@@ -0,0 +1,617 @@
+/*
+ * lm3533-als.c -- LM3533 Ambient Light Sensor driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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/atomic.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/mfd/lm3533.h>
+
+#include "../events.h"
+#include "../iio.h"
+
+
+#define LM3533_ALS_RESISTOR_MAX			0x7f
+#define LM3533_ALS_ADC_MAX			0xff
+#define LM3533_ALS_BOUNDARY_MAX			LM3533_ALS_ADC_MAX
+#define LM3533_ALS_TARGET_MAX			LM3533_ALS_ADC_MAX
+#define LM3533_ALS_ZONE_MAX			4
+
+#define LM3533_REG_ALS_RESISTOR_SELECT		0x30
+#define LM3533_REG_ALS_CONF			0x31
+#define LM3533_REG_ALS_ZONE_INFO		0x34
+#define LM3533_REG_ALS_READ_ADC_AVERAGE		0x37
+#define LM3533_REG_ALS_READ_ADC_RAW		0x38
+#define LM3533_REG_ALS_BOUNDARY0_HIGH		0x50
+#define LM3533_REG_ALS_BOUNDARY0_LOW		0x51
+#define LM3533_REG_ALS_BOUNDARY1_HIGH		0x52
+#define LM3533_REG_ALS_BOUNDARY1_LOW		0x53
+#define LM3533_REG_ALS_BOUNDARY2_HIGH		0x54
+#define LM3533_REG_ALS_BOUNDARY2_LOW		0x55
+#define LM3533_REG_ALS_BOUNDARY3_HIGH		0x56
+#define LM3533_REG_ALS_BOUNDARY3_LOW		0x57
+#define LM3533_REG_ALS_M1_TARGET_0		0x60
+#define LM3533_REG_ALS_M1_TARGET_1		0x61
+#define LM3533_REG_ALS_M1_TARGET_2		0x62
+#define LM3533_REG_ALS_M1_TARGET_3		0x63
+#define LM3533_REG_ALS_M1_TARGET_4		0x64
+#define LM3533_REG_ALS_M2_TARGET_0		0x65
+#define LM3533_REG_ALS_M2_TARGET_1		0x66
+#define LM3533_REG_ALS_M2_TARGET_2		0x67
+#define LM3533_REG_ALS_M2_TARGET_3		0x68
+#define LM3533_REG_ALS_M2_TARGET_4		0x69
+#define LM3533_REG_ALS_M3_TARGET_0		0x6a
+#define LM3533_REG_ALS_M3_TARGET_1		0x6b
+#define LM3533_REG_ALS_M3_TARGET_2		0x6c
+#define LM3533_REG_ALS_M3_TARGET_3		0x6d
+#define LM3533_REG_ALS_M3_TARGET_4		0x6e
+
+#define LM3533_ALS_ENABLE_MASK			0x01
+#define LM3533_ALS_INPUT_MODE_MASK		0x02
+#define LM3533_ALS_INT_ENABLE_MASK		0x01
+
+#define LM3533_ALS_ZONE_SHIFT			2
+#define LM3533_ALS_ZONE_MASK			0x1c
+
+#define LM3533_ALS_FLAG_INT_ENABLED		1
+
+
+struct lm3533_als {
+	struct lm3533 *lm3533;
+
+	unsigned long flags;
+	int irq;
+
+	atomic_t zone;
+};
+
+
+static int lm3533_als_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val1, int *val2, long mask)
+{
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 reg;
+	u8 val;
+	int ret;
+
+	switch (mask) {
+	case 0:
+		reg = LM3533_REG_ALS_READ_ADC_RAW;
+		break;
+	case IIO_CHAN_INFO_AVERAGE_RAW:
+		reg = LM3533_REG_ALS_READ_ADC_AVERAGE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = lm3533_read(als->lm3533, reg, &val);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed to read adc\n");
+		return ret;
+	}
+
+	*val1 = val;
+
+	return IIO_VAL_INT;
+}
+
+static const struct iio_chan_spec lm3533_als_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.info_mask = IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT,
+		.channel = 0,
+	}
+};
+
+static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
+{
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 val;
+	int ret;
+
+	ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed to read zone\n");
+		return ret;
+	}
+
+	val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT;
+	*zone = min_t(u8, val, LM3533_ALS_ZONE_MAX);
+
+	return 0;
+}
+
+static irqreturn_t lm3533_als_isr(int irq, void *dev_id)
+{
+
+	struct iio_dev *indio_dev = dev_id;
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 zone;
+	int ret;
+
+	/* Clear interrupt by reading the ALS zone register. */
+	ret = lm3533_als_get_zone(indio_dev, &zone);
+	if (ret)
+		goto out;
+
+	atomic_set(&als->zone, zone);
+
+	iio_push_event(indio_dev,
+		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+					    0,
+					    IIO_EV_TYPE_THRESH,
+					    IIO_EV_DIR_EITHER),
+		       iio_get_time_ns());
+out:
+	return IRQ_HANDLED;
+}
+
+static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable)
+{
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+	u8 val;
+	int ret;
+
+	if (enable)
+		val = mask;
+	else
+		val = 0;
+
+	ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed to set int mode %d\n",
+								enable);
+	}
+
+	return ret;
+}
+
+static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable)
+{
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+	u8 val;
+	int ret;
+
+	ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed to get int mode\n");
+		return ret;
+	}
+
+	*enable = !!(val & mask);
+
+	return 0;
+}
+
+static int show_thresh_either_en(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+	int enable;
+	int ret;
+
+	if (als->irq) {
+		ret = lm3533_als_get_int_mode(indio_dev, &enable);
+		if (ret)
+			return ret;
+	} else {
+		enable = 0;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
+}
+
+static int store_thresh_either_en(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+	unsigned long enable;
+	bool int_enabled;
+	u8 zone;
+	int ret;
+
+	if (!als->irq)
+		return -EBUSY;
+
+	if (kstrtoul(buf, 0, &enable))
+		return -EINVAL;
+
+	int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+	if (enable && !int_enabled) {
+		ret = lm3533_als_get_zone(indio_dev, &zone);
+		if (ret)
+			return ret;
+
+		atomic_set(&als->zone, zone);
+
+		set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+	}
+
+	ret = lm3533_als_set_int_mode(indio_dev, enable);
+	if (ret) {
+		if (!int_enabled)
+			clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+		return ret;
+	}
+
+	if (!enable)
+		clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+	return len;
+}
+
+static ssize_t show_zone(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+	u8 zone;
+	int ret;
+
+	if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) {
+		zone = atomic_read(&als->zone);
+	} else {
+		ret = lm3533_als_get_zone(indio_dev, &zone);
+		if (ret)
+			return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", zone);
+}
+
+struct lm3533_device_attribute {
+	struct device_attribute dev_attr;
+	u8 reg;
+	u8 max;
+};
+
+#define to_lm3533_dev_attr(_dev_attr) \
+	container_of(_dev_attr, struct lm3533_device_attribute, dev_attr)
+
+static ssize_t show_lm3533_als_reg(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+	struct lm3533_device_attribute *lm3533_attr = to_lm3533_dev_attr(attr);
+	u8 val;
+	int ret;
+
+	ret = lm3533_read(als->lm3533, lm3533_attr->reg, &val);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_lm3533_als_reg(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+	struct lm3533_device_attribute *lm3533_attr = to_lm3533_dev_attr(attr);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val) || val > lm3533_attr->max)
+		return -EINVAL;
+
+	ret = lm3533_write(als->lm3533, lm3533_attr->reg, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+#define REG_ATTR(_name, _mode, _show, _store, _reg, _max) \
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store), \
+	  .reg = _reg, \
+	  .max = _max }
+
+#define LM3533_REG_ATTR(_name, _mode, _show, _store, _reg, _max) \
+	struct lm3533_device_attribute lm3533_dev_attr_##_name \
+		= REG_ATTR(_name, _mode, _show, _store, _reg, _max)
+
+#define LM3533_REG_ATTR_RW(_name, _reg, _max) \
+	LM3533_REG_ATTR(_name, S_IRUGO | S_IWUSR, show_lm3533_als_reg, \
+					store_lm3533_als_reg, _reg, _max)
+
+#define ALS_THRESH_FALLING_ATTR_RW(_nr) \
+	LM3533_REG_ATTR_RW(in_illuminance_thresh##_nr##_falling_value, \
+		LM3533_REG_ALS_BOUNDARY##_nr##_LOW, LM3533_ALS_BOUNDARY_MAX)
+
+#define ALS_THRESH_RAISING_ATTR_RW(_nr) \
+	LM3533_REG_ATTR_RW(in_illuminance_thresh##_nr##_raising_value, \
+		LM3533_REG_ALS_BOUNDARY##_nr##_HIGH, LM3533_ALS_BOUNDARY_MAX)
+
+/* ALS Zone thresholds (boundaries)
+ *
+ * in_illuminance_thresh[0-3]_falling_value	0-255
+ * in_illuminance_thresh[0-3]_raising_value	0-255
+ */
+static ALS_THRESH_FALLING_ATTR_RW(0);
+static ALS_THRESH_FALLING_ATTR_RW(1);
+static ALS_THRESH_FALLING_ATTR_RW(2);
+static ALS_THRESH_FALLING_ATTR_RW(3);
+
+static ALS_THRESH_RAISING_ATTR_RW(0);
+static ALS_THRESH_RAISING_ATTR_RW(1);
+static ALS_THRESH_RAISING_ATTR_RW(2);
+static ALS_THRESH_RAISING_ATTR_RW(3);
+
+#define LM3533_ALS_ATTR_RO(_name) \
+	DEVICE_ATTR(in_illuminance_##_name, S_IRUGO, show_##_name, NULL)
+#define LM3533_ALS_ATTR_RW(_name) \
+	DEVICE_ATTR(in_illuminance_##_name, S_IRUGO | S_IWUSR , \
+						show_##_name, store_##_name)
+
+/* ALS Zone threshold-event enable
+ *
+ * in_illuminance_thresh_either_en		0,1
+ */
+static LM3533_ALS_ATTR_RW(thresh_either_en);
+
+/* ALS Current Zone
+ *
+ * in_illuminance_zone		0-4
+ */
+static LM3533_ALS_ATTR_RO(zone);
+
+#define ALS_TARGET_ATTR_RW(_mapper, _nr) \
+	LM3533_REG_ATTR_RW(target##_mapper##_##_nr, \
+		LM3533_REG_ALS_M##_mapper##_TARGET_##_nr, LM3533_ALS_TARGET_MAX)
+
+/* ALS Mapper targets
+ *
+ * target[1-3]_[0-4]		0-255
+ */
+static ALS_TARGET_ATTR_RW(1, 0);
+static ALS_TARGET_ATTR_RW(1, 1);
+static ALS_TARGET_ATTR_RW(1, 2);
+static ALS_TARGET_ATTR_RW(1, 3);
+static ALS_TARGET_ATTR_RW(1, 4);
+
+static ALS_TARGET_ATTR_RW(2, 0);
+static ALS_TARGET_ATTR_RW(2, 1);
+static ALS_TARGET_ATTR_RW(2, 2);
+static ALS_TARGET_ATTR_RW(2, 3);
+static ALS_TARGET_ATTR_RW(2, 4);
+
+static ALS_TARGET_ATTR_RW(3, 0);
+static ALS_TARGET_ATTR_RW(3, 1);
+static ALS_TARGET_ATTR_RW(3, 2);
+static ALS_TARGET_ATTR_RW(3, 3);
+static ALS_TARGET_ATTR_RW(3, 4);
+
+/* ALS Gain resistor setting
+ *
+ * gain		0-127
+ */
+static LM3533_REG_ATTR_RW(gain, LM3533_REG_ALS_RESISTOR_SELECT,
+						LM3533_ALS_RESISTOR_MAX);
+
+static struct attribute *lm3533_als_event_attributes[] = {
+	&dev_attr_in_illuminance_thresh_either_en.attr,
+	&lm3533_dev_attr_in_illuminance_thresh0_falling_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh0_raising_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh1_falling_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh1_raising_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh2_falling_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh2_raising_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh3_falling_value.dev_attr.attr,
+	&lm3533_dev_attr_in_illuminance_thresh3_raising_value.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group lm3533_als_event_attribute_group = {
+	.attrs = lm3533_als_event_attributes
+};
+
+static struct attribute *lm3533_als_attributes[] = {
+	&lm3533_dev_attr_target1_0.dev_attr.attr,
+	&lm3533_dev_attr_target1_1.dev_attr.attr,
+	&lm3533_dev_attr_target1_2.dev_attr.attr,
+	&lm3533_dev_attr_target1_3.dev_attr.attr,
+	&lm3533_dev_attr_target1_4.dev_attr.attr,
+	&lm3533_dev_attr_target2_0.dev_attr.attr,
+	&lm3533_dev_attr_target2_1.dev_attr.attr,
+	&lm3533_dev_attr_target2_2.dev_attr.attr,
+	&lm3533_dev_attr_target2_3.dev_attr.attr,
+	&lm3533_dev_attr_target2_4.dev_attr.attr,
+	&lm3533_dev_attr_target3_0.dev_attr.attr,
+	&lm3533_dev_attr_target3_1.dev_attr.attr,
+	&lm3533_dev_attr_target3_2.dev_attr.attr,
+	&lm3533_dev_attr_target3_3.dev_attr.attr,
+	&lm3533_dev_attr_target3_4.dev_attr.attr,
+	&lm3533_dev_attr_gain.dev_attr.attr,
+	&dev_attr_in_illuminance_zone.attr,
+	NULL
+};
+
+static struct attribute_group lm3533_als_attribute_group = {
+	.attrs = lm3533_als_attributes
+};
+
+static int __devinit lm3533_als_set_input_mode(struct lm3533 *lm3533,
+								int pwm_mode)
+{
+	u8 mask = LM3533_ALS_INPUT_MODE_MASK;
+	u8 val;
+	int ret;
+
+	if (pwm_mode)
+		val = mask;	/* pwm input */
+	else
+		val = 0;	/* analog input */
+
+	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, mask, mask);
+	if (ret) {
+		dev_err(lm3533->dev,
+				"failed to set input mode %d\n", pwm_mode);
+	}
+
+	return ret;
+}
+
+static int __devinit lm3533_als_enable(struct lm3533 *lm3533)
+{
+	u8 mask = LM3533_ALS_ENABLE_MASK;
+	int ret;
+
+	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, mask, mask);
+	if (ret)
+		dev_err(lm3533->dev, "failed to enable ALS\n");
+
+	return ret;
+}
+
+static int lm3533_als_disable(struct lm3533 *lm3533)
+{
+	u8 mask = LM3533_ALS_ENABLE_MASK;
+	int ret;
+
+	ret = lm3533_update(lm3533, LM3533_REG_ALS_CONF, 0, mask);
+	if (ret)
+		dev_err(lm3533->dev, "failed to disable ALS\n");
+
+	return ret;
+}
+
+static const struct iio_info lm3533_als_info = {
+	.attrs		= &lm3533_als_attribute_group,
+	.event_attrs	= &lm3533_als_event_attribute_group,
+	.driver_module	= THIS_MODULE,
+	.read_raw	= &lm3533_als_read_raw,
+};
+
+static int __devinit lm3533_als_probe(struct platform_device *pdev)
+{
+	struct lm3533 *lm3533;
+	struct lm3533_als_platform_data *pdata;
+	struct lm3533_als *als;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	lm3533 = dev_get_drvdata(pdev->dev.parent);
+	if (!lm3533)
+		return -EINVAL;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	indio_dev = iio_allocate_device(sizeof(*als));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	indio_dev->info = &lm3533_als_info;
+	indio_dev->channels = lm3533_als_channels;
+	indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels);
+	indio_dev->name = "lm3533-als";
+	indio_dev->dev.parent = pdev->dev.parent;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	als = iio_priv(indio_dev);
+	als->lm3533 = lm3533;
+	als->irq = lm3533->irq;
+	atomic_set(&als->zone, 0);
+
+	if (als->irq) {
+		ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					indio_dev->name, indio_dev);
+		if (ret) {
+			dev_err(&indio_dev->dev, "failed to request irq %d\n",
+								lm3533->irq);
+			goto err_free;
+		}
+	}
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = lm3533_als_set_input_mode(lm3533, pdata->pwm_mode);
+	if (ret)
+		goto err_free;
+
+	ret = lm3533_als_enable(lm3533);
+	if (ret)
+		goto err_free;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ALS\n");
+		goto err_disable;
+	}
+
+	return 0;
+
+err_disable:
+	lm3533_als_disable(lm3533);
+err_free:
+	iio_free_device(indio_dev);
+
+	return ret;
+}
+
+static int __devexit lm3533_als_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct lm3533_als *als = iio_priv(indio_dev);
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	iio_device_unregister(indio_dev);
+	lm3533_als_disable(als->lm3533);
+	if (als->irq)
+		free_irq(als->irq, indio_dev);
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver lm3533_als_driver = {
+	.driver = {
+		.name = "lm3533-als",
+		.owner = THIS_MODULE,
+	},
+	.probe		= lm3533_als_probe,
+	.remove		= __devexit_p(lm3533_als_remove),
+};
+module_platform_driver(lm3533_als_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-als");
-- 
1.7.8.5


^ permalink raw reply related

* [PATCH v2 1/4] mfd: add LM3533 lighting-power core driver
From: Johan Hovold @ 2012-05-03 10:26 UTC (permalink / raw)
  To: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat
  Cc: Arnd Bergmann, Andrew Morton, Mark Brown, linux-doc, linux-kernel,
	linux-iio, devel, linux-fbdev, Johan Hovold
In-Reply-To: <1336040799-18433-1-git-send-email-jhovold@gmail.com>

Add support for National Semiconductor / TI LM3533 lighting power chips.

This is the core driver which provides register access over I2C and
registers the ambient-light-sensor, LED and backlight sub-drivers.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
---

v2:
 - add sysfs-ABI documentation
 - merge i2c implementation with core
 - use regmap and kill custom debugfs interface


 .../ABI/testing/sysfs-bus-i2c-devices-lm3533       |   38 +
 drivers/mfd/Kconfig                                |   13 +
 drivers/mfd/Makefile                               |    1 +
 drivers/mfd/lm3533-core.c                          |  717 ++++++++++++++++++++
 drivers/mfd/lm3533-ctrlbank.c                      |  134 ++++
 include/linux/mfd/lm3533.h                         |   89 +++
 6 files changed, 992 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
 create mode 100644 drivers/mfd/lm3533-core.c
 create mode 100644 drivers/mfd/lm3533-ctrlbank.c
 create mode 100644 include/linux/mfd/lm3533.h

diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
new file mode 100644
index 0000000..5700721
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
@@ -0,0 +1,38 @@
+What:		/sys/bus/i2c/devices/.../boost_freq
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the boost converter switching frequency (0, 1), where
+
+		0 -  500Hz
+		1 - 1000Hz
+
+What:		/sys/bus/i2c/devices/.../boost_ovp
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the boost converter over-voltage protection threshold
+		(0..3), where
+
+		0 - 16V
+		1 - 24V
+		2 - 32V
+		3 - 40V
+
+What:		/sys/bus/i2c/devices/.../output_hvled[n]
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the controlling backlight device for high-voltage current
+		sink HVLED[n] (n = 1, 2) (0, 1).
+
+What:		/sys/bus/i2c/devices/.../output_lvled[n]
+Date:		April 2012
+KernelVersion:	3.5
+Contact:	Johan Hovold <jhovold@gmail.com>
+Description:
+		Set the controlling led device for low-voltage current sink
+		LVLED[n] (n = 1..5) (0..3).
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e4438..8fe0771 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -106,6 +106,19 @@ config UCB1400_CORE
 	  To compile this driver as a module, choose M here: the
 	  module will be called ucb1400_core.
 
+config MFD_LM3533
+	tristate "LM3533 Lighting Power chip"
+	depends on I2C
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	  Say yes here to enable support for National Semiconductor / TI
+	  LM3533 Lighting Power chips.
+
+	  This driver provides common support for accessing the device;
+	  additional drivers must be enabled in order to use the LED,
+	  backlight or ambient-light-sensor functionality of the device.
+
 config TPS6105X
 	tristate "TPS61050/61052 Boost Converters"
 	depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..b6fe0a5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
 obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
 obj-$(CONFIG_MFD_ANATOP)	+= anatop-mfd.o
+obj-$(CONFIG_MFD_LM3533)	+= lm3533-core.o lm3533-ctrlbank.o
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
new file mode 100644
index 0000000..75f4b7f
--- /dev/null
+++ b/drivers/mfd/lm3533-core.c
@@ -0,0 +1,717 @@
+/*
+ * lm3533-core.c -- LM3533 Core
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_BOOST_OVP_MAX		0x03
+#define LM3533_BOOST_OVP_MASK		0x06
+#define LM3533_BOOST_OVP_SHIFT		1
+
+#define LM3533_BOOST_FREQ_MAX		0x01
+#define LM3533_BOOST_FREQ_MASK		0x01
+#define LM3533_BOOST_FREQ_SHIFT		0
+
+#define LM3533_BL_ID_MASK		1
+#define LM3533_LED_ID_MASK		3
+#define LM3533_BL_ID_MAX		1
+#define LM3533_LED_ID_MAX		3
+
+#define LM3533_HVLED_ID_MAX		2
+#define LM3533_LVLED_ID_MAX		5
+
+#define LM3533_REG_OUTPUT_CONF1		0x10
+#define LM3533_REG_OUTPUT_CONF2		0x11
+#define LM3533_REG_BOOST_PWM		0x2c
+
+#define LM3533_REG_MAX			0xb2
+
+
+static struct mfd_cell lm3533_als_devs[] = {
+	{
+		.name	= "lm3533-als",
+		.id	= -1,
+	},
+};
+
+static struct mfd_cell lm3533_bl_devs[] = {
+	{
+		.name	= "lm3533-backlight",
+		.id	= 0,
+	},
+	{
+		.name	= "lm3533-backlight",
+		.id	= 1,
+	},
+};
+
+static struct mfd_cell lm3533_led_devs[] = {
+	{
+		.name	= "lm3533-leds",
+		.id	= 0,
+	},
+	{
+		.name	= "lm3533-leds",
+		.id	= 1,
+	},
+	{
+		.name	= "lm3533-leds",
+		.id	= 2,
+	},
+	{
+		.name	= "lm3533-leds",
+		.id	= 3,
+	},
+};
+
+int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
+{
+	int tmp;
+	int ret;
+
+	ret = regmap_read(lm3533->regmap, reg, &tmp);
+	if (ret < 0) {
+		dev_err(lm3533->dev, "failed to read register %02x: %d\n",
+								reg, ret);
+		return ret;
+	}
+
+	*val = tmp;
+
+	dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_read);
+
+int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val)
+{
+	int ret;
+
+	dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val);
+
+	ret = regmap_write(lm3533->regmap, reg, val);
+	if (ret < 0) {
+		dev_err(lm3533->dev, "failed to write register %02x: %d\n",
+								reg, ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_write);
+
+int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask)
+{
+	int ret;
+
+	dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask);
+
+	ret = regmap_update_bits(lm3533->regmap, reg, val, mask);
+	if (ret < 0) {
+		dev_err(lm3533->dev, "failed to update register %02x: %d\n",
+								reg, ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_update);
+
+/*
+ * HVLED output config -- output hvled controlled by backlight bl
+ */
+static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl)
+{
+	u8 val;
+	u8 mask;
+	int shift;
+	int ret;
+
+	if (hvled = 0 || hvled > LM3533_HVLED_ID_MAX)
+		return -EINVAL;
+
+	if (bl > LM3533_BL_ID_MAX)
+		return -EINVAL;
+
+	shift = hvled - 1;
+	mask = LM3533_BL_ID_MASK << shift;
+	val = bl << shift;
+
+	ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask);
+	if (ret)
+		dev_err(lm3533->dev, "failed to set hvled config\n");
+
+	return ret;
+}
+
+/*
+ * LVLED output config -- output lvled controlled by LED led
+ */
+static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
+{
+	u8 reg;
+	u8 val;
+	u8 mask;
+	int shift;
+	int ret;
+
+	if (lvled = 0 || lvled > LM3533_LVLED_ID_MAX)
+		return -EINVAL;
+
+	if (led > LM3533_LED_ID_MAX)
+		return -EINVAL;
+
+	if (lvled < 4) {
+		reg = LM3533_REG_OUTPUT_CONF1;
+		shift = 2 * lvled;
+	} else {
+		reg = LM3533_REG_OUTPUT_CONF2;
+		shift = 2 * (lvled - 4);
+	}
+
+	mask = LM3533_LED_ID_MASK << shift;
+	val = led << shift;
+
+	ret = lm3533_update(lm3533, reg, val, mask);
+	if (ret)
+		dev_err(lm3533->dev, "failed to set lvled config\n");
+
+	return ret;
+}
+
+static void lm3533_enable(struct lm3533 *lm3533)
+{
+	if (gpio_is_valid(lm3533->gpio_hwen))
+		gpio_set_value(lm3533->gpio_hwen, 1);
+}
+
+static void lm3533_disable(struct lm3533 *lm3533)
+{
+	if (gpio_is_valid(lm3533->gpio_hwen))
+		gpio_set_value(lm3533->gpio_hwen, 0);
+}
+
+enum lm3533_attribute_type {
+	LM3533_ATTR_TYPE_BACKLIGHT,
+	LM3533_ATTR_TYPE_LED,
+};
+
+struct lm3533_device_attribute {
+	struct device_attribute dev_attr;
+	enum lm3533_attribute_type type;
+	union {
+		struct {
+			u8 id;
+		} output;
+		struct {
+			u8 reg;
+			u8 shift;
+			u8 mask;
+			u8 max;
+		} generic;
+	} u;
+};
+
+#define to_lm3533_dev_attr(_attr) \
+	container_of(_attr, struct lm3533_device_attribute, dev_attr)
+
+static ssize_t show_lm3533_reg(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct lm3533 *lm3533 = dev_get_drvdata(dev);
+	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+	u8 val;
+	int ret;
+
+	ret = lm3533_read(lm3533, lattr->u.generic.reg, &val);
+	if (ret)
+		return ret;
+
+	val = (val & lattr->u.generic.mask) >> lattr->u.generic.shift;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_lm3533_reg(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t len)
+{
+	struct lm3533 *lm3533 = dev_get_drvdata(dev);
+	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val) || val > lattr->u.generic.max)
+		return -EINVAL;
+
+	val = val << lattr->u.generic.shift;
+	ret = lm3533_update(lm3533, lattr->u.generic.reg, val,
+							lattr->u.generic.mask);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+#define GENERIC_ATTR(_reg, _max, _mask, _shift) \
+	{ .reg		= _reg, \
+	  .max		= _max, \
+	  .mask		= _mask, \
+	  .shift	= _shift }
+
+#define LM3533_GENERIC_ATTR(_name, _mode, _show, _store, _type,	\
+						_reg, _max, _mask, _shift) \
+	struct lm3533_device_attribute lm3533_dev_attr_##_name = { \
+		.dev_attr	= __ATTR(_name, _mode, _show, _store), \
+		.type		= _type, \
+		.u.generic	= GENERIC_ATTR(_reg, _max, _mask, _shift) }
+
+#define LM3533_GENERIC_ATTR_RW(_name, _type, _reg, _max, _mask, _shift) \
+	LM3533_GENERIC_ATTR(_name, S_IRUGO | S_IWUSR, \
+					show_lm3533_reg, store_lm3533_reg, \
+					_type, _reg, _max, _mask, _shift)
+
+#define LM3533_BOOST_ATTR_RW(_name, _NAME) \
+	LM3533_GENERIC_ATTR_RW(_name, LM3533_ATTR_TYPE_BACKLIGHT, \
+				LM3533_REG_BOOST_PWM, LM3533_##_NAME##_MAX, \
+				LM3533_##_NAME##_MASK, LM3533_##_NAME##_SHIFT)
+/*
+ * Boost Over Voltage Protection Select
+ *
+ *   0 - 16 V (default)
+ *   1 - 24 V
+ *   2 - 32 V
+ *   3 - 40 V
+ */
+static LM3533_BOOST_ATTR_RW(boost_ovp, BOOST_OVP);
+
+/*
+ * Boost Frequency Select
+ *
+ *   0 - 500 kHz (default)
+ *   1 - 1 MHz
+ */
+static LM3533_BOOST_ATTR_RW(boost_freq, BOOST_FREQ);
+
+static ssize_t show_output(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct lm3533 *lm3533 = dev_get_drvdata(dev);
+	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+	int id = lattr->u.output.id;
+	u8 reg;
+	u8 val;
+	u8 mask;
+	int shift;
+	int ret;
+
+	if (lattr->type = LM3533_ATTR_TYPE_BACKLIGHT) {
+		reg = LM3533_REG_OUTPUT_CONF1;
+		shift = id - 1;
+		mask = LM3533_BL_ID_MASK << shift;
+	} else {
+		if (id < 4) {
+			reg = LM3533_REG_OUTPUT_CONF1;
+			shift = 2 * id;
+		} else {
+			reg = LM3533_REG_OUTPUT_CONF2;
+			shift = 2 * (id - 4);
+		}
+		mask = LM3533_LED_ID_MASK << shift;
+	}
+
+	ret = lm3533_read(lm3533, reg, &val);
+	if (ret)
+		return ret;
+
+	val = (val & mask) >> shift;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_output(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct lm3533 *lm3533 = dev_get_drvdata(dev);
+	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+	int id = lattr->u.output.id;
+	u8 val;
+	int ret;
+
+	if (kstrtou8(buf, 0, &val))
+		return -EINVAL;
+
+	if (lattr->type = LM3533_ATTR_TYPE_BACKLIGHT)
+		ret = lm3533_set_hvled_config(lm3533, id, val);
+	else
+		ret = lm3533_set_lvled_config(lm3533, id, val);
+
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+#define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \
+	struct lm3533_device_attribute lm3533_dev_attr_##_name = \
+		{ .dev_attr	= __ATTR(_name, _mode, _show, _store), \
+		  .type		= _type, \
+		  .u.output	= { .id = _id }, }
+
+#define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \
+	LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \
+					show_output, store_output, _type, _id)
+
+#define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \
+	LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr)
+#define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \
+	LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr)
+/*
+ * Output config:
+ *
+ * output_hvled<nr>	0-1
+ * output_lvled<nr>	0-3
+ */
+static LM3533_OUTPUT_HVLED_ATTR_RW(1);
+static LM3533_OUTPUT_HVLED_ATTR_RW(2);
+static LM3533_OUTPUT_LVLED_ATTR_RW(1);
+static LM3533_OUTPUT_LVLED_ATTR_RW(2);
+static LM3533_OUTPUT_LVLED_ATTR_RW(3);
+static LM3533_OUTPUT_LVLED_ATTR_RW(4);
+static LM3533_OUTPUT_LVLED_ATTR_RW(5);
+
+static struct attribute *lm3533_attributes[] = {
+	&lm3533_dev_attr_boost_freq.dev_attr.attr,
+	&lm3533_dev_attr_boost_ovp.dev_attr.attr,
+	&lm3533_dev_attr_output_hvled1.dev_attr.attr,
+	&lm3533_dev_attr_output_hvled2.dev_attr.attr,
+	&lm3533_dev_attr_output_lvled1.dev_attr.attr,
+	&lm3533_dev_attr_output_lvled2.dev_attr.attr,
+	&lm3533_dev_attr_output_lvled3.dev_attr.attr,
+	&lm3533_dev_attr_output_lvled4.dev_attr.attr,
+	&lm3533_dev_attr_output_lvled5.dev_attr.attr,
+	NULL,
+};
+
+#define to_dev_attr(_attr) \
+	container_of(_attr, struct device_attribute, attr)
+
+static mode_t lm3533_attr_is_visible(struct kobject *kobj,
+					     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct lm3533 *lm3533 = dev_get_drvdata(dev);
+	struct device_attribute *dattr = to_dev_attr(attr);
+	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr);
+	enum lm3533_attribute_type type = lattr->type;
+	mode_t mode = attr->mode;
+
+	if (!lm3533->have_backlights && type = LM3533_ATTR_TYPE_BACKLIGHT)
+		mode = 0;
+	else if (!lm3533->have_leds && type = LM3533_ATTR_TYPE_LED)
+		mode = 0;
+
+	return mode;
+};
+
+static struct attribute_group lm3533_attribute_group = {
+	.is_visible	= lm3533_attr_is_visible,
+	.attrs		= lm3533_attributes
+};
+
+static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
+{
+	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+	int ret;
+
+	if (!pdata->als)
+		return 0;
+
+	lm3533_als_devs[0].platform_data = pdata->als;
+	lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
+
+	ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0);
+	if (ret) {
+		dev_err(lm3533->dev, "failed to add ALS device\n");
+		return ret;
+	}
+
+	lm3533->have_als = 1;
+
+	return 0;
+}
+
+static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
+{
+	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+	int i;
+	int ret;
+
+	if (!pdata->backlights || pdata->num_backlights = 0)
+		return 0;
+
+	if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs))
+		pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs);
+
+	for (i = 0; i < pdata->num_backlights; ++i) {
+		lm3533_bl_devs[i].platform_data = &pdata->backlights[i];
+		lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]);
+	}
+
+	ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
+					pdata->num_backlights, NULL, 0);
+	if (ret) {
+		dev_err(lm3533->dev, "failed to add backlight devices\n");
+		return ret;
+	}
+
+	lm3533->have_backlights = 1;
+
+	return 0;
+}
+
+static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
+{
+	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+	int i;
+	int ret;
+
+	if (!pdata->leds || pdata->num_leds = 0)
+		return 0;
+
+	if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs))
+		pdata->num_leds = ARRAY_SIZE(lm3533_led_devs);
+
+	for (i = 0; i < pdata->num_leds; ++i) {
+		lm3533_led_devs[i].platform_data = &pdata->leds[i];
+		lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]);
+	}
+
+	ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
+						pdata->num_leds, NULL, 0);
+	if (ret) {
+		dev_err(lm3533->dev, "failed to add LED devices\n");
+		return ret;
+	}
+
+	lm3533->have_leds = 1;
+
+	return 0;
+}
+
+static int __devinit lm3533_device_init(struct lm3533 *lm3533)
+{
+	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+	int ret;
+
+	dev_dbg(lm3533->dev, "%s\n", __func__);
+
+	if (!pdata) {
+		dev_err(lm3533->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	lm3533->gpio_hwen = pdata->gpio_hwen;
+
+	dev_set_drvdata(lm3533->dev, lm3533);
+
+	if (gpio_is_valid(lm3533->gpio_hwen)) {
+		ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW,
+								"lm3533-hwen");
+		if (ret < 0) {
+			dev_err(lm3533->dev,
+				"failed to request HWEN GPIO %d\n",
+				lm3533->gpio_hwen);
+			return ret;
+		}
+	}
+
+	lm3533_enable(lm3533);
+
+	lm3533_device_als_init(lm3533);
+	lm3533_device_bl_init(lm3533);
+	lm3533_device_led_init(lm3533);
+
+	ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group);
+	if (ret < 0) {
+		dev_err(lm3533->dev, "failed to create sysfs attributes\n");
+		goto err_unregister;
+	}
+
+	return 0;
+
+err_unregister:
+	mfd_remove_devices(lm3533->dev);
+	lm3533_disable(lm3533);
+	if (gpio_is_valid(lm3533->gpio_hwen))
+		gpio_free(lm3533->gpio_hwen);
+
+	return ret;
+}
+
+static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
+{
+	dev_dbg(lm3533->dev, "%s\n", __func__);
+
+	sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group);
+
+	mfd_remove_devices(lm3533->dev);
+	lm3533_disable(lm3533);
+	if (gpio_is_valid(lm3533->gpio_hwen))
+		gpio_free(lm3533->gpio_hwen);
+}
+
+static bool lm3533_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x10 ... 0x2c:
+	case 0x30 ... 0x38:
+	case 0x40 ... 0x45:
+	case 0x50 ... 0x57:
+	case 0x60 ... 0x6e:
+	case 0x70 ... 0x75:
+	case 0x80 ... 0x85:
+	case 0x90 ... 0x95:
+	case 0xa0 ... 0xa5:
+	case 0xb0 ... 0xb2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool lm3533_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x34:		/* zone */
+	case 0x37 ... 0x38:	/* adc */
+	case 0xb0 ... 0xb1:	/* fault */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool lm3533_precious_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x34:		/* zone */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static struct regmap_config regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.max_register	= LM3533_REG_MAX,
+	.readable_reg	= lm3533_readable_register,
+	.volatile_reg	= lm3533_volatile_register,
+	.precious_reg	= lm3533_precious_register,
+};
+
+static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
+					const struct i2c_device_id *id)
+{
+	struct lm3533 *lm3533;
+	int ret;
+
+	dev_dbg(&i2c->dev, "%s\n", __func__);
+
+	lm3533 = kzalloc(sizeof(*lm3533), GFP_KERNEL);
+	if (!lm3533)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, lm3533);
+
+	lm3533->regmap = regmap_init_i2c(i2c, &regmap_config);
+	if (IS_ERR(lm3533->regmap)) {
+		ret = PTR_ERR(lm3533->regmap);
+		goto err_regmap;
+	}
+
+	lm3533->dev = &i2c->dev;
+	lm3533->irq = i2c->irq;
+
+	ret = lm3533_device_init(lm3533);
+	if (ret)
+		goto err_dev;
+
+	return 0;
+
+err_dev:
+	regmap_exit(lm3533->regmap);
+err_regmap:
+	kfree(lm3533);
+
+	return ret;
+}
+
+static int __devexit lm3533_i2c_remove(struct i2c_client *i2c)
+{
+	struct lm3533 *lm3533 = i2c_get_clientdata(i2c);
+
+	dev_dbg(&i2c->dev, "%s\n", __func__);
+
+	lm3533_device_exit(lm3533);
+	regmap_exit(lm3533->regmap);
+
+	kfree(lm3533);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm3533_i2c_ids[] = {
+	{ "lm3533", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
+
+static struct i2c_driver lm3533_i2c_driver = {
+	.driver = {
+		   .name = "lm3533",
+		   .owner = THIS_MODULE,
+	},
+	.id_table	= lm3533_i2c_ids,
+	.probe		= lm3533_i2c_probe,
+	.remove		= __devexit_p(lm3533_i2c_remove),
+};
+
+static int __init lm3533_i2c_init(void)
+{
+	return i2c_add_driver(&lm3533_i2c_driver);
+}
+subsys_initcall(lm3533_i2c_init);
+
+static void __exit lm3533_i2c_exit(void)
+{
+	i2c_del_driver(&lm3533_i2c_driver);
+}
+module_exit(lm3533_i2c_exit);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c
new file mode 100644
index 0000000..c2732a3
--- /dev/null
+++ b/drivers/mfd/lm3533-ctrlbank.c
@@ -0,0 +1,134 @@
+/*
+ * lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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/device.h>
+#include <linux/module.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_BRIGHTNESS_MAX		255
+#define LM3533_MAX_CURRENT_MAX		31
+#define LM3533_PWM_MAX			0x3f
+
+#define LM3533_REG_PWM_BASE		0x14
+#define LM3533_REG_MAX_CURRENT_BASE	0x1f
+#define LM3533_REG_CTRLBANK_ENABLE	0x27
+#define LM3533_REG_BRIGHTNESS_BASE	0x40
+
+
+static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base)
+{
+	return base + cb->id;
+}
+
+int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb)
+{
+	u8 mask;
+	int ret;
+
+	dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
+
+	mask = 1 << cb->id;
+	ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE,
+								mask, mask);
+	if (ret)
+		dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable);
+
+int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb)
+{
+	u8 mask;
+	int ret;
+
+	dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
+
+	mask = 1 << cb->id;
+	ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask);
+	if (ret)
+		dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable);
+
+#define lm3533_ctrlbank_set(_name, _NAME)				\
+int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val)	\
+{									\
+	u8 reg;								\
+	int ret;							\
+									\
+	if (val > LM3533_##_NAME##_MAX)					\
+		return -EINVAL;						\
+									\
+	reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE);	\
+	ret = lm3533_write(cb->lm3533, reg, val);			\
+	if (ret)							\
+		dev_err(cb->dev, "failed to set " #_name "\n");		\
+									\
+	return ret;							\
+}									\
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name);
+
+#define lm3533_ctrlbank_get(_name, _NAME)				\
+int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val)	\
+{									\
+	u8 reg;								\
+	int ret;							\
+									\
+	reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE);	\
+	ret = lm3533_read(cb->lm3533, reg, val);			\
+	if (ret)							\
+		dev_err(cb->dev, "failed to get " #_name "\n");		\
+									\
+	return ret;							\
+}									\
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name);
+
+lm3533_ctrlbank_set(brightness, BRIGHTNESS);
+lm3533_ctrlbank_get(brightness, BRIGHTNESS);
+
+/*
+ * Full scale current.
+ *
+ * Imax = 5 + val * 0.8 mA, e.g.:
+ *
+ *    0 - 5 mA
+ *     ...
+ *   19 - 20.2 mA (default)
+ *     ...
+ *   31 - 29.8 mA
+ */
+lm3533_ctrlbank_set(max_current, MAX_CURRENT);
+lm3533_ctrlbank_get(max_current, MAX_CURRENT);
+
+/*
+ * PWM-input control mask:
+ *
+ *   bit 5 - PWM-input enabled in Zone 4
+ *   bit 4 - PWM-input enabled in Zone 3
+ *   bit 3 - PWM-input enabled in Zone 2
+ *   bit 2 - PWM-input enabled in Zone 1
+ *   bit 1 - PWM-input enabled in Zone 0
+ *   bit 0 - PWM-input enabled
+ */
+lm3533_ctrlbank_set(pwm, PWM);
+lm3533_ctrlbank_get(pwm, PWM);
+
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Control Bank interface");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h
new file mode 100644
index 0000000..75f85f3
--- /dev/null
+++ b/include/linux/mfd/lm3533.h
@@ -0,0 +1,89 @@
+/*
+ * lm3533.h -- LM3533 interface
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.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.
+ */
+
+#ifndef __LINUX_MFD_LM3533_H
+#define __LINUX_MFD_LM3533_H
+
+#define LM3533_ATTR_RO(_name) \
+	DEVICE_ATTR(_name, S_IRUGO, show_##_name, NULL)
+#define LM3533_ATTR_RW(_name) \
+	DEVICE_ATTR(_name, S_IRUGO | S_IWUSR , show_##_name, store_##_name)
+
+struct device;
+struct regmap;
+
+struct lm3533 {
+	struct device *dev;
+
+	struct regmap *regmap;
+
+	int gpio_hwen;
+	int irq;
+
+	unsigned have_als:1;
+	unsigned have_backlights:1;
+	unsigned have_leds:1;
+};
+
+struct lm3533_ctrlbank {
+	struct lm3533 *lm3533;
+	struct device *dev;
+	int id;
+};
+
+struct lm3533_als_platform_data {
+	unsigned pwm_mode:1;		/* PWM input mode (default analog) */
+};
+
+struct lm3533_bl_platform_data {
+	char *name;
+	u8 default_brightness;		/* 0 - 255 */
+	u8 max_current;			/* 0 - 31 */
+	u8 pwm;				/* 0 - 0x3f */
+};
+
+struct lm3533_led_platform_data {
+	char *name;
+	const char *default_trigger;
+	u8 max_current;			/* 0 - 31 */
+	u8 pwm;				/* 0 - 0x3f */
+};
+
+struct lm3533_platform_data {
+	int gpio_hwen;
+
+	struct lm3533_als_platform_data *als;
+
+	struct lm3533_bl_platform_data *backlights;
+	int num_backlights;
+
+	struct lm3533_led_platform_data *leds;
+	int num_leds;
+};
+
+extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb);
+extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb);
+
+extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val);
+extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val);
+extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u8 val);
+extern int lm3533_ctrlbank_get_max_current(struct lm3533_ctrlbank *cb,
+								u8 *val);
+extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val);
+extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val);
+
+extern int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val);
+extern int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val);
+extern int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask);
+
+#endif	/* __LINUX_MFD_LM3533_H */
-- 
1.7.8.5


^ permalink raw reply related

* [PATCH v2 0/4] mfd: add LM3533 lighting-power chip driver
From: Johan Hovold @ 2012-05-03 10:26 UTC (permalink / raw)
  To: Rob Landley, Richard Purdie, Samuel Ortiz, Jonathan Cameron,
	Greg Kroah-Hartman, Florian Tobias Schandinat
  Cc: Arnd Bergmann, Andrew Morton, Mark Brown, linux-doc, linux-kernel,
	linux-iio, devel, linux-fbdev, Johan Hovold
In-Reply-To: <1334935826-12527-1-git-send-email-jhovold@gmail.com>

These patches (against v3.4-rc5) add support for the National Semiconductor /
Texas Instruments LM3533 lighting-power chip.

This multi-function device has four LEDs, two backlights and an
ambient-light-sensor interface.

The LEDs and backlights can be controlled directly, through PWM input,
or by the ambient-light-sensor interface. Hardware-accelerated blinking is
provided for the LEDs.

ALS control is done through defining five light zones and three sets of
corresponding brightness target levels. The ALS iio-driver provides raw and
mean adc readings along with the current light zone through sysfs. A threshold
event can be generated on zone changes.

Further details and specifications are now available from:

        http://www.ti.com/product/lm3533

Changes since v1 includes a rewrite of the ambient-light-sensor driver against
iio, the addition of sysfs-ABI documentation, and a switch to regmap for
register io.

This work has been done on behalf of National Semiconductor / Texas
Instruments.

Thanks,
Johan


Johan Hovold (4):
  mfd: add LM3533 lighting-power core driver
  iio: add LM3533 ambient light sensor driver
  leds: add LM3533 LED driver
  backlight: add LM3533 backlight driver

 .../ABI/testing/sysfs-bus-i2c-devices-lm3533       |   38 +
 .../testing/sysfs-class-backlight-driver-lm3533    |   50 ++
 .../ABI/testing/sysfs-class-led-driver-lm3533      |   67 ++
 drivers/leds/Kconfig                               |   13 +
 drivers/leds/Makefile                              |    1 +
 drivers/leds/leds-lm3533.c                         |  741 ++++++++++++++++++++
 drivers/mfd/Kconfig                                |   13 +
 drivers/mfd/Makefile                               |    1 +
 drivers/mfd/lm3533-core.c                          |  717 +++++++++++++++++++
 drivers/mfd/lm3533-ctrlbank.c                      |  134 ++++
 .../Documentation/sysfs-bus-iio-light-lm3533-als   |   62 ++
 drivers/staging/iio/light/Kconfig                  |   16 +
 drivers/staging/iio/light/Makefile                 |    1 +
 drivers/staging/iio/light/lm3533-als.c             |  617 ++++++++++++++++
 drivers/video/backlight/Kconfig                    |   12 +
 drivers/video/backlight/Makefile                   |    1 +
 drivers/video/backlight/lm3533_bl.c                |  458 ++++++++++++
 include/linux/mfd/lm3533.h                         |   89 +++
 18 files changed, 3031 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
 create mode 100644 Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
 create mode 100644 Documentation/ABI/testing/sysfs-class-led-driver-lm3533
 create mode 100644 drivers/leds/leds-lm3533.c
 create mode 100644 drivers/mfd/lm3533-core.c
 create mode 100644 drivers/mfd/lm3533-ctrlbank.c
 create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
 create mode 100644 drivers/staging/iio/light/lm3533-als.c
 create mode 100644 drivers/video/backlight/lm3533_bl.c
 create mode 100644 include/linux/mfd/lm3533.h

-- 
1.7.8.5


^ permalink raw reply

* Re: [PATCH 1/4] mfd: add LM3533 lighting-power core driver
From: Johan Hovold @ 2012-05-03 10:22 UTC (permalink / raw)
  To: Mark Brown
  Cc: Samuel Ortiz, Florian Tobias Schandinat, Arnd Bergmann,
	Greg Kroah-Hartman, linux-fbdev, linux-kernel, Andrew Morton,
	Richard Purdie, Johan Hovold
In-Reply-To: <20120426124122.GA26692@sirena.org.uk>

[ Sorry for the resend -- Android/gmail apparently added HTML to my previous
  attempt. ] 

On Apr 26, 2012 2:41 PM, "Mark Brown" <broonie@opensource.wolfsonmicro.com>
wrote:
> On Fri, Apr 20, 2012 at 05:30:23PM +0200, Johan Hovold wrote:
> > +static int __lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
> > +{
> > +     int ret;
> > +
> > +     ret = lm3533->read(lm3533, reg, val);
> > +     if (ret < 0) {
>
> Looks like you could save a bunch of code by using regmap for the
> register I/O.  This would also give you access to the cache and
> diagnostic infrastructure it has.

Using regmap only saves about ten lines for the actual io implementation,
but it does allow me to get rid of the custom debugfs interface. I'm not
enabling caching at this point but it'll probably come in handy later.

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH 1/4] mfd: add LM3533 lighting-power core driver
From: Johan Hovold @ 2012-05-03 10:15 UTC (permalink / raw)
  To: Mark Brown
  Cc: Samuel Ortiz, Florian Tobias Schandinat, Arnd Bergmann,
	Greg Kroah-Hartman, linux-fbdev, linux-kernel, Andrew Morton,
	Richard Purdie, Johan Hovold
In-Reply-To: <20120426124122.GA26692@sirena.org.uk>

[ Sorry for the resend -- Android/gmail apparently added HTML to my previous
  attempt. ] 

On Apr 26, 2012 2:41 PM, "Mark Brown" <broonie@opensource.wolfsonmicro.com>
wrote:
> On Fri, Apr 20, 2012 at 05:30:23PM +0200, Johan Hovold wrote:
> > +static int __lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
> > +{
> > +     int ret;
> > +
> > +     ret = lm3533->read(lm3533, reg, val);
> > +     if (ret < 0) {
>
> Looks like you could save a bunch of code by using regmap for the
> register I/O.  This would also give you access to the cache and
> diagnostic infrastructure it has.

Using regmap only saves about ten lines for the actual io implementation,
but it does allow me to get rid of the custom debugfs interface. I'm not
enabling caching at this point but it'll probably come in handy later.

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Mark Brown @ 2012-05-03  9:58 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20120503095211.GC9574-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 303 bytes --]

On Thu, May 03, 2012 at 11:52:11AM +0200, Wolfram Sang wrote:

> Also, I'd think the FUNC_NOSTART bit should be 0x08 and SMBUS_PEC 0x4000. This
> will be more intuitive, probably?

What is the value in renumbering everything?  It just seems like it
makes the diff less clear and has no practical value.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Wolfram Sang @ 2012-05-03  9:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1335443839-22872-1-git-send-email-broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 2093 bytes --]

On Thu, Apr 26, 2012 at 01:37:19PM +0100, Mark Brown wrote:
> Since there are uses for I2C_M_NOSTART which are much more sensible and
> standard than most of the protocol mangling functionality (the main one
> being gather writes to devices where something like a register address
> needs to be inserted before a block of data) create a new I2C_FUNC_NOSTART
> for this feature and update all the users to use it.
> 
> In the case of regmap-i2c we remove the requirement for mangling as
> I2C_M_NOSTART is the only mangling feature which is being used.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

In general, I like it. One thing below:

> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -540,7 +540,7 @@ struct i2c_msg {
>  	__u16 flags;
>  #define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
>  #define I2C_M_RD		0x0001	/* read data, from slave to master */
> -#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
> +#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
>  #define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
>  #define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
>  #define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
> @@ -568,6 +568,7 @@ struct i2c_msg {
>  #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
>  #define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
>  #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
> +#define I2C_FUNC_NOSTART		0x10000000 /* I2C_M_NOSTART */

In this file, the comment for MANGLING should be adapted, too. It says
currently:

#define I2C_FUNC_PROTOCOL_MANGLING      0x00000004 /* I2C_M_NOSTART etc. */

NOSTART is now out of MANGLING.

Also, I'd think the FUNC_NOSTART bit should be 0x08 and SMBUS_PEC 0x4000. This
will be more intuitive, probably?

Thanks,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply


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