public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC v2 0/3] V4L2 API for flash devices and the adp1653 flash controller driver
@ 2011-05-17 15:13 Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 1/3] v4l: Add a class and a set of controls for flash devices Sakari Ailus
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Sakari Ailus @ 2011-05-17 15:13 UTC (permalink / raw)
  To: linux-media@vger.kernel.org
  Cc: Nayden Kanchev, Guennadi Liakhovetski, Laurent Pinchart,
	Hans Verkuil, David Cohen, Kim HeungJun, andrew.b.adams,
	Sung Hee Park

Hi,


This patchset implements RFC v4 of V4L2 API for flash devices [1], with
minor modifications, and adds the adp1653 flash controller driver. There
have been changes since v1 [2] of this patchset:


- Faults on the flash LED are allowed to make the LED unusable before
the faults are read. This is implemented in the adp1653 driver.

- Intensities are using standard units; mA for flash / torch and uA for
the indicator.


Thanks to those who have given their feedback so far in the process!


[1] http://www.spinics.net/lists/linux-media/msg32030.html

[2] http://www.spinics.net/lists/linux-media/msg32396.html

Cheers,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC v2 1/3] v4l: Add a class and a set of controls for flash devices.
  2011-05-17 15:13 [RFC v2 0/3] V4L2 API for flash devices and the adp1653 flash controller driver Sakari Ailus
@ 2011-05-17 15:14 ` Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 2/3] v4l: Add flash control documentation Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 3/3] adp1653: Add driver for LED flash controller Sakari Ailus
  2 siblings, 0 replies; 7+ messages in thread
From: Sakari Ailus @ 2011-05-17 15:14 UTC (permalink / raw)
  To: linux-media
  Cc: laurent.pinchart, nkanchev, g.liakhovetski, hverkuil, dacohen,
	riverful, andrew.b.adams, shpark7

From: Sakari Ailus <sakari.ailus@iki.fi>

Add a control class and a set of controls to support LED and Xenon flash
devices. An example of such a device is the adp1653.

Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
---
 drivers/media/video/v4l2-ctrls.c |   45 ++++++++++++++++++++++++++++++++++++++
 include/linux/videodev2.h        |   36 ++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 2412f08..74aae36 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -216,6 +216,17 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
 		"75 useconds",
 		NULL,
 	};
+	static const char * const flash_led_mode[] = {
+		"Off",
+		"Flash",
+		"Torch",
+		NULL,
+	};
+	static const char * const flash_strobe_source[] = {
+		"Software",
+		"External",
+		NULL,
+	};
 
 	switch (id) {
 	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -256,6 +267,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
 		return colorfx;
 	case V4L2_CID_TUNE_PREEMPHASIS:
 		return tune_preemphasis;
+	case V4L2_CID_FLASH_LED_MODE:
+		return flash_led_mode;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		return flash_strobe_source;
 	default:
 		return NULL;
 	}
@@ -389,6 +404,21 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_TUNE_POWER_LEVEL:		return "Tune Power Level";
 	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:	return "Tune Antenna Capacitor";
 
+	/* Flash controls */
+	case V4L2_CID_FLASH_CLASS:		return "Flash controls";
+	case V4L2_CID_FLASH_LED_MODE:		return "LED mode";
+	case V4L2_CID_FLASH_STROBE_SOURCE:	return "Strobe source";
+	case V4L2_CID_FLASH_STROBE:		return "Strobe";
+	case V4L2_CID_FLASH_STROBE_STOP:	return "Stop strobe";
+	case V4L2_CID_FLASH_STROBE_STATUS:	return "Strobe status";
+	case V4L2_CID_FLASH_TIMEOUT:		return "Strobe timeout";
+	case V4L2_CID_FLASH_INTENSITY:		return "Intensity, flash mode";
+	case V4L2_CID_FLASH_TORCH_INTENSITY:	return "Intensity, torch mode";
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, indicator";
+	case V4L2_CID_FLASH_FAULT:		return "Faults";
+	case V4L2_CID_FLASH_CHARGE:		return "Charge";
+	case V4L2_CID_FLASH_READY:		return "Ready to strobe";
+
 	default:
 		return NULL;
 	}
@@ -423,12 +453,17 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_PILOT_TONE_ENABLED:
 	case V4L2_CID_ILLUMINATORS_1:
 	case V4L2_CID_ILLUMINATORS_2:
+	case V4L2_CID_FLASH_STROBE_STATUS:
+	case V4L2_CID_FLASH_CHARGE:
+	case V4L2_CID_FLASH_READY:
 		*type = V4L2_CTRL_TYPE_BOOLEAN;
 		*min = 0;
 		*max = *step = 1;
 		break;
 	case V4L2_CID_PAN_RESET:
 	case V4L2_CID_TILT_RESET:
+	case V4L2_CID_FLASH_STROBE:
+	case V4L2_CID_FLASH_STROBE_STOP:
 		*type = V4L2_CTRL_TYPE_BUTTON;
 		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
 		*min = *max = *step = *def = 0;
@@ -452,6 +487,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_EXPOSURE_AUTO:
 	case V4L2_CID_COLORFX:
 	case V4L2_CID_TUNE_PREEMPHASIS:
+	case V4L2_CID_FLASH_LED_MODE:
+	case V4L2_CID_FLASH_STROBE_SOURCE:
 		*type = V4L2_CTRL_TYPE_MENU;
 		break;
 	case V4L2_CID_RDS_TX_PS_NAME:
@@ -462,6 +499,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_CAMERA_CLASS:
 	case V4L2_CID_MPEG_CLASS:
 	case V4L2_CID_FM_TX_CLASS:
+	case V4L2_CID_FLASH_CLASS:
 		*type = V4L2_CTRL_TYPE_CTRL_CLASS;
 		/* You can neither read not write these */
 		*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -474,6 +512,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 		/* Max is calculated as RGB888 that is 2^24 */
 		*max = 0xFFFFFF;
 		break;
+	case V4L2_CID_FLASH_FAULT:
+		*type = V4L2_CTRL_TYPE_BITMASK;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -519,6 +560,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_ZOOM_RELATIVE:
 		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
 		break;
+	case V4L2_CID_FLASH_STROBE_STATUS:
+	case V4L2_CID_FLASH_READY:
+		*flags |= V4L2_CTRL_FLAG_READ_ONLY;
+		break;
 	}
 }
 EXPORT_SYMBOL(v4l2_ctrl_fill);
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index be82c8e..e364350 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1022,6 +1022,7 @@ struct v4l2_ext_controls {
 #define V4L2_CTRL_CLASS_MPEG 0x00990000	/* MPEG-compression controls */
 #define V4L2_CTRL_CLASS_CAMERA 0x009a0000	/* Camera class controls */
 #define V4L2_CTRL_CLASS_FM_TX 0x009b0000	/* FM Modulator control class */
+#define V4L2_CTRL_CLASS_FLASH 0x009c0000	/* Camera flash controls */
 
 #define V4L2_CTRL_ID_MASK      	  (0x0fffffff)
 #define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
@@ -1423,6 +1424,41 @@ enum v4l2_preemphasis {
 #define V4L2_CID_TUNE_POWER_LEVEL		(V4L2_CID_FM_TX_CLASS_BASE + 113)
 #define V4L2_CID_TUNE_ANTENNA_CAPACITOR		(V4L2_CID_FM_TX_CLASS_BASE + 114)
 
+/* Flash and privacy (indicator) light controls */
+#define V4L2_CID_FLASH_CLASS_BASE		(V4L2_CTRL_CLASS_FLASH | 0x900)
+#define V4L2_CID_FLASH_CLASS			(V4L2_CTRL_CLASS_FLASH | 1)
+
+#define V4L2_CID_FLASH_LED_MODE			(V4L2_CID_FLASH_CLASS_BASE + 1)
+enum v4l2_flash_led_mode {
+	V4L2_FLASH_LED_MODE_NONE,
+	V4L2_FLASH_LED_MODE_FLASH,
+	V4L2_FLASH_LED_MODE_TORCH,
+};
+
+#define V4L2_CID_FLASH_STROBE_SOURCE		(V4L2_CID_FLASH_CLASS_BASE + 2)
+enum v4l2_flash_strobe_source {
+	V4L2_FLASH_STROBE_SOURCE_SOFTWARE,
+	V4L2_FLASH_STROBE_SOURCE_EXTERNAL,
+};
+
+#define V4L2_CID_FLASH_STROBE			(V4L2_CID_FLASH_CLASS_BASE + 3)
+#define V4L2_CID_FLASH_STROBE_STOP		(V4L2_CID_FLASH_CLASS_BASE + 4)
+#define V4L2_CID_FLASH_STROBE_STATUS		(V4L2_CID_FLASH_CLASS_BASE + 5)
+
+#define V4L2_CID_FLASH_TIMEOUT			(V4L2_CID_FLASH_CLASS_BASE + 6)
+#define V4L2_CID_FLASH_INTENSITY		(V4L2_CID_FLASH_CLASS_BASE + 7)
+#define V4L2_CID_FLASH_TORCH_INTENSITY		(V4L2_CID_FLASH_CLASS_BASE + 8)
+#define V4L2_CID_FLASH_INDICATOR_INTENSITY	(V4L2_CID_FLASH_CLASS_BASE + 9)
+
+#define V4L2_CID_FLASH_FAULT			(V4L2_CID_FLASH_CLASS_BASE + 10)
+#define V4L2_FLASH_FAULT_OVER_VOLTAGE		(1 << 0)
+#define V4L2_FLASH_FAULT_TIMEOUT		(1 << 1)
+#define V4L2_FLASH_FAULT_OVER_TEMPERATURE	(1 << 2)
+#define V4L2_FLASH_FAULT_SHORT_CIRCUIT		(1 << 3)
+
+#define V4L2_CID_FLASH_CHARGE			(V4L2_CID_FLASH_CLASS_BASE + 11)
+#define V4L2_CID_FLASH_READY			(V4L2_CID_FLASH_CLASS_BASE + 12)
+
 /*
  *	T U N I N G
  */
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC v2 2/3] v4l: Add flash control documentation
  2011-05-17 15:13 [RFC v2 0/3] V4L2 API for flash devices and the adp1653 flash controller driver Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 1/3] v4l: Add a class and a set of controls for flash devices Sakari Ailus
@ 2011-05-17 15:14 ` Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 3/3] adp1653: Add driver for LED flash controller Sakari Ailus
  2 siblings, 0 replies; 7+ messages in thread
From: Sakari Ailus @ 2011-05-17 15:14 UTC (permalink / raw)
  To: linux-media
  Cc: laurent.pinchart, nkanchev, g.liakhovetski, hverkuil, dacohen,
	riverful, andrew.b.adams, shpark7

Add documentation for V4L2 flash controls.

Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
---
 Documentation/DocBook/v4l/controls.xml           |  221 ++++++++++++++++++++++
 Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml |    7 +
 2 files changed, 228 insertions(+), 0 deletions(-)

diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml
index a920ee8..a4e4542 100644
--- a/Documentation/DocBook/v4l/controls.xml
+++ b/Documentation/DocBook/v4l/controls.xml
@@ -2092,6 +2092,227 @@ manually or automatically if set to zero. Unit, range and step are driver-specif
 <para>For more details about RDS specification, refer to
 <xref linkend="en50067" /> document, from CENELEC.</para>
     </section>
+
+    <section id="flash-controls">
+      <title>Flash Control Reference</title>
+
+      <para>
+	The V4L2 flash controls are intended to provide generic access
+	to flash controller devices. Flash controller devices are
+	typically used in digital cameras.
+      </para>
+
+      <para>
+	The interface can support both LED and xenon flash devices.
+      </para>
+
+      <table pgwide="1" frame="none" id="flash-control-id">
+      <title>Flash Control IDs</title>
+
+      <tgroup cols="4">
+	<colspec colname="c1" colwidth="1*" />
+	<colspec colname="c2" colwidth="6*" />
+	<colspec colname="c3" colwidth="2*" />
+	<colspec colname="c4" colwidth="6*" />
+	<spanspec namest="c1" nameend="c2" spanname="id" />
+	<spanspec namest="c2" nameend="c4" spanname="descr" />
+	<thead>
+	  <row>
+	    <entry spanname="id" align="left">ID</entry>
+	    <entry align="left">Type</entry>
+	  </row><row rowsep="1"><entry spanname="descr" align="left">Description</entry>
+	  </row>
+	</thead>
+	<tbody valign="top">
+	  <row><entry></entry></row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_CLASS</constant></entry>
+	    <entry>class</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">The FLASH class descriptor.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_LED_MODE</constant></entry>
+	    <entry>menu</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Defines the mode of the flash LED,
+	    the high-power white LED attached to the flash
+	    controller.</entry>
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_NONE</constant></entry>
+		  <entry>Off.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_FLASH</constant></entry>
+		  <entry>Flash mode.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_TORCH</constant></entry>
+		  <entry>Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_SOURCE</constant></entry>
+	    <entry>menu</entry>
+	  </row>
+	  <row><entry spanname="descr">Defines the mode of the
+	  flash LED strobe.</entry>
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_STROBE_SOURCE_SOFTWARE</constant></entry>
+		  <entry>The flash strobe is triggered by using
+		  the V4L2_CID_FLASH_STROBE control.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_STROBE_SOURCE_EXTERNAL</constant></entry>
+		  <entry>The flash strobe is triggered by an
+		  external source. Typically this is a sensor,
+		  which makes it possible to synchronises the
+		  flash strobe start to exposure start.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE</constant></entry>
+	    <entry>button</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Strobe flash. Valid when
+	    V4L2_CID_FLASH_LED_MODE is set to
+	    V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE
+	    is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STOP</constant></entry>
+	    <entry>button</entry>
+	  </row>
+          <row><entry spanname="descr">Stop flash strobe immediately.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STATUS</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Strobe status: whether the flash
+	    is strobing at the moment or not. This is a read-only
+	    control.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Hardware timeout for flash. The
+	    flash strobe is stopped after this period of time has
+	    passed from the start of the strobe.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Intensity of the flash strobe when
+	    the flash LED is in flash mode
+	    (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps
+	    (mA) if possible.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_TORCH_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Intensity of the flash LED in
+	    torch mode (V4L2_FLASH_LED_MODE_TORCH). The unit should be
+	    milliamps (mA) if possible.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_INDICATOR_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Intensity of the indicator LED.
+	    The indicator LED may be fully independent of the flash
+	    LED. The unit should be microamps (uA) if possible.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_FAULT</constant></entry>
+	    <entry>bitmask</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Faults related to the flash. The
+	    faults tell about specific problems in the flash chip
+	    itself or the LEDs attached to it. Faults related to the
+	    flash LED may set the V4L2_CID_FLASH_LED_MODE to
+	    V4L2_FLASH_LED_MODE_NONE, and prevent further use of the
+	    LED before reading the faults. Exactly which faults have
+	    such an effect is chip dependent.</entry>
+
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_OVER_VOLTAGE</constant></entry>
+		  <entry>Flash controller voltage to the flash LED
+		  has exceeded the limit specific to the flash
+		  controller.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_TIMEOUT</constant></entry>
+		  <entry>The flash strobe was still on when
+		  the timeout set by the user ---
+		  V4L2_CID_FLASH_TIMEOUT control --- has expired.
+		  Not all flash controllers may set this in all
+		  such conditions.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_OVER_TEMPERATURE</constant></entry>
+		  <entry>The flash controller has overheated.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_SHORT_CIRCUIT</constant></entry>
+		  <entry>The short circuit protection of the flash
+		  controller has been triggered.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_CHARGE</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+          <row><entry spanname="descr">Enable or disable charging of the xenon
+          flash capacitor.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_READY</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+          <row>
+	    <entry spanname="descr">Is the flash ready to strobe?
+	    Xenon flashes require their capacitors charged before
+	    strobing. LED flashes often require a cooldown period
+	    after strobe during which another strobe will not be
+	    possible. This is a read-only control.</entry>
+	  </row>
+	  <row><entry></entry></row>
+	</tbody>
+      </tgroup>
+      </table>
+
+    </section>
 </section>
 
   <!--
diff --git a/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml
index 3aa7f8f..c37bd86 100644
--- a/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml
+++ b/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml
@@ -250,6 +250,13 @@ These controls are described in <xref
 These controls are described in <xref
 		linkend="fm-tx-controls" />.</entry>
 	  </row>
+	  <row>
+	    <entry><constant>V4L2_CTRL_CLASS_FLASH</constant></entry>
+	    <entry>0x9c0000</entry>
+	    <entry>The class containing flash device controls.
+These controls are described in <xref
+		linkend="flash-controls" />.</entry>
+	  </row>
 	</tbody>
       </tgroup>
     </table>
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC v2 3/3] adp1653: Add driver for LED flash controller
  2011-05-17 15:13 [RFC v2 0/3] V4L2 API for flash devices and the adp1653 flash controller driver Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 1/3] v4l: Add a class and a set of controls for flash devices Sakari Ailus
  2011-05-17 15:14 ` [RFC v2 2/3] v4l: Add flash control documentation Sakari Ailus
@ 2011-05-17 15:14 ` Sakari Ailus
  2011-05-18  7:31   ` Laurent Pinchart
  2 siblings, 1 reply; 7+ messages in thread
From: Sakari Ailus @ 2011-05-17 15:14 UTC (permalink / raw)
  To: linux-media
  Cc: laurent.pinchart, nkanchev, g.liakhovetski, hverkuil, dacohen,
	riverful, andrew.b.adams, shpark7

This patch adds the driver for the adp1653 LED flash controller. This
controller supports a high power led in flash and torch modes and an
indicator light, sometimes also called privacy light.

The adp1653 is used on the Nokia N900.

Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
Signed-off-by: Tuukka Toivonen <tuukkat76@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: David Cohen <dacohen@gmail.com>
---
 drivers/media/video/Kconfig   |    7 +
 drivers/media/video/Makefile  |    2 +
 drivers/media/video/adp1653.c |  485 +++++++++++++++++++++++++++++++++++++++++
 include/media/adp1653.h       |  126 +++++++++++
 4 files changed, 620 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/adp1653.c
 create mode 100644 include/media/adp1653.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 00f51dd..c004dbb 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -344,6 +344,13 @@ config VIDEO_TCM825X
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+config VIDEO_ADP1653
+	tristate "ADP1653 flash support"
+	depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+	---help---
+	  This is a driver for the ADP1653 flash controller. It is used for
+	  example in Nokia N900.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ace5d8b..abdf021 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -131,6 +131,8 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 
 obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
 
+obj-$(CONFIG_VIDEO_ADP1653)	+= adp1653.o
+
 obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
 obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
 
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
new file mode 100644
index 0000000..1679707
--- /dev/null
+++ b/drivers/media/video/adp1653.c
@@ -0,0 +1,485 @@
+/*
+ * drivers/media/video/adp1653.c
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ *	Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *	Tuukka Toivonen <tuukkat76@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - fault interrupt handling
+ * - faster strobe (use i/o pin instead of i2c)
+ *   - should ensure that the pin is in some sane state even if not used
+ * - power doesn't need to be ON if all lights are off
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/adp1653.h>
+#include <media/v4l2-device.h>
+
+#define TIMEOUT_MAX		820000
+#define TIMEOUT_STEP		54600
+#define TIMEOUT_MIN		(TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
+				 * TIMEOUT_STEP)
+#define TIMEOUT_US_TO_CODE(t)	((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
+				 / TIMEOUT_STEP)
+#define TIMEOUT_CODE_TO_US(c)	(TIMEOUT_MAX - (c) * TIMEOUT_STEP)
+
+static int adp1653_get_fault(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	int fault;
+	int rval;
+
+	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+	if (IS_ERR_VALUE(fault))
+		return fault;
+
+	flash->fault |= fault;
+
+	if (!flash->fault)
+		return 0;
+
+	/* Clear faults. */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+
+	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
+
+	return flash->fault;
+}
+
+/* Write values into ADP1653 registers. */
+static int adp1653_update_hw(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	u8 out_sel;
+	u8 config = 0;
+	int rval;
+
+	out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+		flash->indicator_intensity->val)
+		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
+
+	switch (flash->led_mode->val) {
+	case V4L2_FLASH_LED_MODE_NONE:
+		break;
+	case V4L2_FLASH_LED_MODE_FLASH:
+		/* Flash mode, light on with strobe, duration from timer */
+		config = ADP1653_REG_CONFIG_TMR_CFG;
+		config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
+			  << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
+		break;
+	case V4L2_FLASH_LED_MODE_TORCH:
+		/* Torch mode, light immediately on, duration indefinite */
+		out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+			flash->torch_intensity->val)
+			<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+		break;
+	}
+
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+	if (rval < 0)
+		return rval;
+
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
+	if (rval < 0)
+		return rval;
+
+	return 0;
+}
+
+static int adp1653_strobe(struct adp1653_flash *flash, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+		flash->indicator_intensity->val)
+		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
+	int rval;
+
+	if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
+		return -EBUSY;
+
+	if (!enable)
+		return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
+						 out_sel);
+
+	out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+		flash->flash_intensity->val)
+		<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+	if (rval)
+		return rval;
+
+	/* Software strobe using i2c */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
+		ADP1653_REG_SW_STROBE_SW_STROBE);
+	if (rval)
+		return rval;
+	return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct adp1653_flash *flash =
+		container_of(ctrl->handler, struct adp1653_flash, ctrls);
+
+	adp1653_get_fault(flash);
+	if (IS_ERR_VALUE(flash->fault))
+		return flash->fault;
+
+	ctrl->cur.val = 0;
+
+	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+	flash->fault = 0;
+
+	return 0;
+}
+
+static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct adp1653_flash *flash =
+		container_of(ctrl->handler, struct adp1653_flash, ctrls);
+	int rval;
+
+	rval = adp1653_get_fault(flash);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+	if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
+		     ADP1653_REG_FAULT_FLT_OT |
+		     ADP1653_REG_FAULT_FLT_OV)) &&
+	    (ctrl->id == V4L2_CID_FLASH_STROBE ||
+	     ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
+	     ctrl->id == V4L2_CID_FLASH_LED_MODE))
+		return -EBUSY;
+
+	switch (ctrl->id) {
+	case V4L2_CID_FLASH_STROBE:
+		return adp1653_strobe(flash, 1);
+	case V4L2_CID_FLASH_STROBE_STOP:
+		return adp1653_strobe(flash, 0);
+	}
+
+	return adp1653_update_hw(flash);
+}
+
+static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
+	.g_volatile_ctrl = adp1653_get_ctrl,
+	.s_ctrl = adp1653_set_ctrl,
+};
+
+static int adp1653_init_controls(struct adp1653_flash *flash)
+{
+	v4l2_ctrl_handler_init(&flash->ctrls, 9);
+
+	flash->led_mode =
+		v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+				       V4L2_CID_FLASH_LED_MODE,
+				       V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
+	v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+			       V4L2_CID_FLASH_STROBE_SOURCE,
+			       V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
+	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+			  V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+			  V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+	flash->flash_timeout =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
+				  flash->platform_data->max_flash_timeout,
+				  TIMEOUT_STEP,
+				  flash->platform_data->max_flash_timeout);
+	flash->flash_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_INTENSITY,
+				  ADP1653_FLASH_INTENSITY_MIN,
+				  flash->platform_data->max_flash_intensity,
+				  1, flash->platform_data->max_flash_intensity);
+	flash->torch_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_TORCH_INTENSITY,
+				  ADP1653_TORCH_INTENSITY_MIN,
+				  flash->platform_data->max_torch_intensity,
+				  ADP1653_FLASH_INTENSITY_STEP,
+				  flash->platform_data->max_torch_intensity);
+	flash->indicator_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_INDICATOR_INTENSITY,
+				  ADP1653_INDICATOR_INTENSITY_MIN,
+				  flash->platform_data->max_indicator_intensity,
+				  ADP1653_INDICATOR_INTENSITY_STEP,
+				  ADP1653_INDICATOR_INTENSITY_MIN);
+	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+			  V4L2_CID_FLASH_FAULT, 0, V4L2_FLASH_FAULT_OVER_VOLTAGE
+			  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+			  | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+
+	if (flash->ctrls.error)
+		return flash->ctrls.error;
+
+	flash->subdev.ctrl_handler = &flash->ctrls;
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int
+adp1653_registered(struct v4l2_subdev *subdev)
+{
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	return adp1653_init_controls(flash);
+}
+
+static int
+adp1653_init_device(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	int rval;
+
+	/* Clear FAULT register by writing zero to OUT_SEL */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+	if (rval < 0) {
+		dev_err(&client->dev, "failed writing fault register\n");
+		return -EIO;
+	}
+
+	/* Read FAULT register */
+	rval = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+	if (rval < 0) {
+		dev_err(&client->dev, "failed reading fault register\n");
+		return -EIO;
+	}
+
+	if ((rval & 0x0f) != 0) {
+		dev_err(&client->dev, "device fault\n");
+		return -EIO;
+	}
+
+	mutex_lock(&flash->ctrls.lock);
+	rval = adp1653_update_hw(flash);
+	mutex_unlock(&flash->ctrls.lock);
+	if (rval) {
+		dev_err(&client->dev,
+			"adp1653_update_hw failed at %s\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int
+__adp1653_set_power(struct adp1653_flash *flash, int on)
+{
+	int ret;
+
+	ret = flash->platform_data->power(&flash->subdev, on);
+	if (ret < 0)
+		return ret;
+
+	if (!on)
+		return 0;
+
+	ret = adp1653_init_device(flash);
+	if (ret < 0)
+		flash->platform_data->power(&flash->subdev, 0);
+
+	return ret;
+}
+
+static int
+adp1653_set_power(struct v4l2_subdev *subdev, int on)
+{
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+	int ret = 0;
+
+	mutex_lock(&flash->power_lock);
+
+	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
+	 * update the power state.
+	 */
+	if (flash->power_count == !on) {
+		ret = __adp1653_set_power(flash, !!on);
+		if (ret < 0)
+			goto done;
+	}
+
+	/* Update the power count. */
+	flash->power_count += on ? 1 : -1;
+	WARN_ON(flash->power_count < 0);
+
+done:
+	mutex_unlock(&flash->power_lock);
+	return ret;
+}
+
+static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return adp1653_set_power(sd, 1);
+}
+
+static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return adp1653_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops adp1653_core_ops = {
+	.s_power = adp1653_set_power,
+};
+
+static const struct v4l2_subdev_ops adp1653_ops = {
+	.core = &adp1653_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
+	.registered = adp1653_registered,
+	.open = adp1653_open,
+	.close = adp1653_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int adp1653_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	if (!flash->power_count)
+		return 0;
+
+	return __adp1653_set_power(flash, 0);
+}
+
+static int adp1653_resume(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	if (!flash->power_count)
+		return 0;
+
+	return __adp1653_set_power(flash, 1);
+}
+
+#else
+
+#define adp1653_suspend	NULL
+#define adp1653_resume	NULL
+
+#endif /* CONFIG_PM */
+
+static int adp1653_probe(struct i2c_client *client,
+			 const struct i2c_device_id *devid)
+{
+	struct adp1653_flash *flash;
+	int ret;
+
+	flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+	if (flash == NULL)
+		return -ENOMEM;
+
+	flash->platform_data = client->dev.platform_data;
+
+	mutex_init(&flash->power_lock);
+
+	v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
+	flash->subdev.internal_ops = &adp1653_internal_ops;
+	flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+	if (ret < 0)
+		kfree(flash);
+
+	return ret;
+}
+
+static int __exit adp1653_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	v4l2_device_unregister_subdev(&flash->subdev);
+	v4l2_ctrl_handler_free(&flash->ctrls);
+	media_entity_cleanup(&flash->subdev.entity);
+	kfree(flash);
+	return 0;
+}
+
+static const struct i2c_device_id adp1653_id_table[] = {
+	{ ADP1653_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
+
+static struct i2c_driver adp1653_i2c_driver = {
+	.driver		= {
+		.name	= ADP1653_NAME,
+	},
+	.probe		= adp1653_probe,
+	.remove		= __exit_p(adp1653_remove),
+	.suspend	= adp1653_suspend,
+	.resume		= adp1653_resume,
+	.id_table	= adp1653_id_table,
+};
+
+static int __init adp1653_init(void)
+{
+	int rval;
+
+	rval = i2c_add_driver(&adp1653_i2c_driver);
+	if (rval)
+		printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
+
+	return rval;
+}
+
+static void __exit adp1653_exit(void)
+{
+	i2c_del_driver(&adp1653_i2c_driver);
+}
+
+module_init(adp1653_init);
+module_exit(adp1653_exit);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
+MODULE_LICENSE("GPL");
diff --git a/include/media/adp1653.h b/include/media/adp1653.h
new file mode 100644
index 0000000..50a1af8
--- /dev/null
+++ b/include/media/adp1653.h
@@ -0,0 +1,126 @@
+/*
+ * include/media/adp1653.h
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ *	Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *	Tuukka Toivonen <tuukkat76@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef ADP1653_H
+#define ADP1653_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define ADP1653_NAME				"adp1653"
+#define ADP1653_I2C_ADDR			(0x60 >> 1)
+
+/* Register definitions */
+#define ADP1653_REG_OUT_SEL			0x00
+#define ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN	0x01
+#define ADP1653_REG_OUT_SEL_HPLED_TORCH_MAX	0x0b
+#define ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN	0x0c
+#define ADP1653_REG_OUT_SEL_HPLED_FLASH_MAX	0x1f
+#define ADP1653_REG_OUT_SEL_HPLED_SHIFT		3
+#define ADP1653_REG_OUT_SEL_ILED_MAX		0x07
+#define ADP1653_REG_OUT_SEL_ILED_SHIFT		0
+
+#define ADP1653_REG_CONFIG			0x01
+#define ADP1653_REG_CONFIG_TMR_CFG		(1 << 4)
+#define ADP1653_REG_CONFIG_TMR_SET_MAX		0x0f
+#define ADP1653_REG_CONFIG_TMR_SET_SHIFT	0
+
+#define ADP1653_REG_SW_STROBE			0x02
+#define ADP1653_REG_SW_STROBE_SW_STROBE		(1 << 0)
+
+#define ADP1653_REG_FAULT			0x03
+#define ADP1653_REG_FAULT_FLT_SCP		(1 << 3)
+#define ADP1653_REG_FAULT_FLT_OT		(1 << 2)
+#define ADP1653_REG_FAULT_FLT_TMR		(1 << 1)
+#define ADP1653_REG_FAULT_FLT_OV		(1 << 0)
+
+#define ADP1653_INDICATOR_INTENSITY_MIN		0
+#define ADP1653_INDICATOR_INTENSITY_STEP	2500
+#define ADP1653_INDICATOR_INTENSITY_MAX		\
+	(ADP1653_REG_OUT_SEL_ILED_MAX * ADP1653_INDICATOR_INTENSITY_STEP)
+#define ADP1653_INDICATOR_INTENSITY_uA_TO_REG(a) \
+	((a) / ADP1653_INDICATOR_INTENSITY_STEP)
+#define ADP1653_INDICATOR_INTENSITY_REG_TO_uA(a) \
+	((a) * ADP1653_INDICATOR_INTENSITY_STEP)
+
+#define ADP1653_FLASH_INTENSITY_BASE		35
+#define ADP1653_FLASH_INTENSITY_STEP		15
+#define ADP1653_FLASH_INTENSITY_MIN					\
+	(ADP1653_FLASH_INTENSITY_BASE					\
+	 + ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN * ADP1653_FLASH_INTENSITY_STEP)
+#define ADP1653_FLASH_INTENSITY_MAX			\
+	(ADP1653_FLASH_INTENSITY_MIN +			\
+	 (ADP1653_REG_OUT_SEL_HPLED_FLASH_MAX -		\
+	  ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN + 1) *	\
+	 ADP1653_FLASH_INTENSITY_STEP)
+
+#define ADP1653_FLASH_INTENSITY_mA_TO_REG(a)				\
+	((a) < ADP1653_FLASH_INTENSITY_BASE ? 0 :			\
+	 (((a) - ADP1653_FLASH_INTENSITY_BASE) / ADP1653_FLASH_INTENSITY_STEP))
+#define ADP1653_FLASH_INTENSITY_REG_TO_mA(a)		\
+	((a) * ADP1653_FLASH_INTENSITY_STEP + ADP1653_FLASH_INTENSITY_BASE)
+
+#define ADP1653_TORCH_INTENSITY_MIN					\
+	(ADP1653_FLASH_INTENSITY_BASE					\
+	 + ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN * ADP1653_FLASH_INTENSITY_STEP)
+#define ADP1653_TORCH_INTENSITY_MAX			\
+	(ADP1653_TORCH_INTENSITY_MIN +			\
+	 (ADP1653_REG_OUT_SEL_HPLED_TORCH_MAX -		\
+	  ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN + 1) *	\
+	 ADP1653_FLASH_INTENSITY_STEP)
+
+struct adp1653_platform_data {
+	int (*power)(struct v4l2_subdev *sd, int on);
+
+	u32 max_flash_timeout;		/* flash light timeout in us */
+	u32 max_flash_intensity;	/* led intensity, flash mode */
+	u32 max_torch_intensity;	/* led intensity, torch mode */
+	u32 max_indicator_intensity;	/* indicator led intensity */
+};
+
+#define to_adp1653_flash(sd)	container_of(sd, struct adp1653_flash, subdev)
+
+struct adp1653_flash {
+	struct v4l2_subdev subdev;
+	struct adp1653_platform_data *platform_data;
+
+	struct v4l2_ctrl_handler ctrls;
+	struct v4l2_ctrl *led_mode;
+	struct v4l2_ctrl *flash_timeout;
+	struct v4l2_ctrl *flash_intensity;
+	struct v4l2_ctrl *torch_intensity;
+	struct v4l2_ctrl *indicator_intensity;
+
+	struct mutex power_lock;
+	int power_count;
+	int fault;
+};
+
+#endif /* ADP1653_H */
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [RFC v2 3/3] adp1653: Add driver for LED flash controller
  2011-05-17 15:14 ` [RFC v2 3/3] adp1653: Add driver for LED flash controller Sakari Ailus
@ 2011-05-18  7:31   ` Laurent Pinchart
  2011-05-18 11:29     ` Sakari Ailus
  0 siblings, 1 reply; 7+ messages in thread
From: Laurent Pinchart @ 2011-05-18  7:31 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, nkanchev, g.liakhovetski, hverkuil, dacohen,
	riverful, andrew.b.adams, shpark7

Hi Sakari,

On Tuesday 17 May 2011 17:14:04 Sakari Ailus wrote:
> This patch adds the driver for the adp1653 LED flash controller. This
> controller supports a high power led in flash and torch modes and an
> indicator light, sometimes also called privacy light.
> 
> The adp1653 is used on the Nokia N900.

[snip]

> diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
> new file mode 100644
> index 0000000..1679707
> --- /dev/null
> +++ b/drivers/media/video/adp1653.c

[snip]

> +static int adp1653_get_fault(struct adp1653_flash *flash)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> +	int fault;
> +	int rval;
> +
> +	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> +	if (IS_ERR_VALUE(fault))
> +		return fault;
> +
> +	flash->fault |= fault;
> +
> +	if (!flash->fault)
> +		return 0;
> +
> +	/* Clear faults. */
> +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> +	if (IS_ERR_VALUE(rval))
> +		return rval;

Should the faults be cleared right away, instead of when the user reads the 
faults control ?

> +	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;

Does the hardware switch back to "none" mode when a fault occurs ? The 
datasheet just states that "the ADP1653 is disabled". Does that mean 
temporarily disabled until the faults are cleared ? If so you should update 
the registers to turn the LED off.

> +	return flash->fault;
> +}

[snip]

> +static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct adp1653_flash *flash =
> +		container_of(ctrl->handler, struct adp1653_flash, ctrls);
> +
> +	adp1653_get_fault(flash);
> +	if (IS_ERR_VALUE(flash->fault))

Shouldn't you check the adp1653_get_fault() return value instead ?

> +		return flash->fault;
> +
> +	ctrl->cur.val = 0;
> +
> +	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
> +		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
> +	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
> +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
> +	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
> +		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
> +	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
> +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
> +
> +	flash->fault = 0;
> +
> +	return 0;
> +}

[snip]

> +static int
> +adp1653_registered(struct v4l2_subdev *subdev)
> +{
> +	struct adp1653_flash *flash = to_adp1653_flash(subdev);
> +
> +	return adp1653_init_controls(flash);

Can't this be moved to adp1653_probe() ? You could then get rid of the 
registered callback.

> +}
> +
> +static int
> +adp1653_init_device(struct adp1653_flash *flash)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> +	int rval;
> +
> +	/* Clear FAULT register by writing zero to OUT_SEL */
> +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> +	if (rval < 0) {
> +		dev_err(&client->dev, "failed writing fault register\n");
> +		return -EIO;
> +	}
> +
> +	/* Read FAULT register */
> +	rval = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> +	if (rval < 0) {
> +		dev_err(&client->dev, "failed reading fault register\n");
> +		return -EIO;
> +	}
> +
> +	if ((rval & 0x0f) != 0) {
> +		dev_err(&client->dev, "device fault\n");

Same comment as last time :-)

> +		return -EIO;
> +	}
> +
> +	mutex_lock(&flash->ctrls.lock);
> +	rval = adp1653_update_hw(flash);
> +	mutex_unlock(&flash->ctrls.lock);
> +	if (rval) {
> +		dev_err(&client->dev,
> +			"adp1653_update_hw failed at %s\n", __func__);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}

[snip]

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC v2 3/3] adp1653: Add driver for LED flash controller
  2011-05-18  7:31   ` Laurent Pinchart
@ 2011-05-18 11:29     ` Sakari Ailus
  2011-05-18 12:40       ` Laurent Pinchart
  0 siblings, 1 reply; 7+ messages in thread
From: Sakari Ailus @ 2011-05-18 11:29 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, linux-media, nkanchev, g.liakhovetski, hverkuil,
	dacohen, riverful, andrew.b.adams, shpark7

On Wed, May 18, 2011 at 09:31:24AM +0200, Laurent Pinchart wrote:
> Hi Sakari,

Hi Laurent,

> On Tuesday 17 May 2011 17:14:04 Sakari Ailus wrote:
> > This patch adds the driver for the adp1653 LED flash controller. This
> > controller supports a high power led in flash and torch modes and an
> > indicator light, sometimes also called privacy light.
> > 
> > The adp1653 is used on the Nokia N900.
> 
> [snip]
> 
> > diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
> > new file mode 100644
> > index 0000000..1679707
> > --- /dev/null
> > +++ b/drivers/media/video/adp1653.c
> 
> [snip]
> 
> > +static int adp1653_get_fault(struct adp1653_flash *flash)
> > +{
> > +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> > +	int fault;
> > +	int rval;
> > +
> > +	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> > +	if (IS_ERR_VALUE(fault))
> > +		return fault;
> > +
> > +	flash->fault |= fault;
> > +
> > +	if (!flash->fault)
> > +		return 0;
> > +
> > +	/* Clear faults. */
> > +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> > +	if (IS_ERR_VALUE(rval))
> > +		return rval;
> 
> Should the faults be cleared right away, instead of when the user reads the 
> faults control ?
> 
> > +	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
> 
> Does the hardware switch back to "none" mode when a fault occurs ? The 
> datasheet just states that "the ADP1653 is disabled". Does that mean 
> temporarily disabled until the faults are cleared ? If so you should update 
> the registers to turn the LED off.

My understanding is that this is temporary until the faults are cleared.
However, this is difficult to find out since the faults don't just occur
that easily.

OUT_SEL register controls the current to LEDs so this turned everything off.
The indicator should still remain on, I guess, since it's not affected by
the faults, except possibly the over-temperature one.

> > +	return flash->fault;
> > +}
> 
> [snip]
> 
> > +static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	struct adp1653_flash *flash =
> > +		container_of(ctrl->handler, struct adp1653_flash, ctrls);
> > +
> > +	adp1653_get_fault(flash);
> > +	if (IS_ERR_VALUE(flash->fault))
> 
> Shouldn't you check the adp1653_get_fault() return value instead ?

Yes. Good catch. I've tried to simulate this code a bit but as always, error
handling tends not to be one of the best parts of the driver, especially
those errors that generally do not happen. :-I

> > +		return flash->fault;
> > +
> > +	ctrl->cur.val = 0;
> > +
> > +	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
> > +		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
> > +	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
> > +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
> > +	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
> > +		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
> > +	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
> > +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
> > +
> > +	flash->fault = 0;
> > +
> > +	return 0;
> > +}
> 
> [snip]
> 
> > +static int
> > +adp1653_registered(struct v4l2_subdev *subdev)
> > +{
> > +	struct adp1653_flash *flash = to_adp1653_flash(subdev);
> > +
> > +	return adp1653_init_controls(flash);
> 
> Can't this be moved to adp1653_probe() ? You could then get rid of the 
> registered callback.

Good point. I'll look into this.

> > +}
> > +
> > +static int
> > +adp1653_init_device(struct adp1653_flash *flash)
> > +{
> > +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> > +	int rval;
> > +
> > +	/* Clear FAULT register by writing zero to OUT_SEL */
> > +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> > +	if (rval < 0) {
> > +		dev_err(&client->dev, "failed writing fault register\n");
> > +		return -EIO;
> > +	}
> > +
> > +	/* Read FAULT register */
> > +	rval = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> > +	if (rval < 0) {
> > +		dev_err(&client->dev, "failed reading fault register\n");
> > +		return -EIO;
> > +	}
> > +
> > +	if ((rval & 0x0f) != 0) {
> > +		dev_err(&client->dev, "device fault\n");
> 
> Same comment as last time :-)

Ouch. I'll try to get the fix done for the next time. :-)

Many thanks for the comments.

Regards,

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC v2 3/3] adp1653: Add driver for LED flash controller
  2011-05-18 11:29     ` Sakari Ailus
@ 2011-05-18 12:40       ` Laurent Pinchart
  0 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2011-05-18 12:40 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Sakari Ailus, linux-media, nkanchev, g.liakhovetski, hverkuil,
	dacohen, riverful, andrew.b.adams, shpark7

Hi Sakari,

On Wednesday 18 May 2011 13:29:56 Sakari Ailus wrote:
> On Wed, May 18, 2011 at 09:31:24AM +0200, Laurent Pinchart wrote:
> > On Tuesday 17 May 2011 17:14:04 Sakari Ailus wrote:
> > > This patch adds the driver for the adp1653 LED flash controller. This
> > > controller supports a high power led in flash and torch modes and an
> > > indicator light, sometimes also called privacy light.
> > > 
> > > The adp1653 is used on the Nokia N900.
> > 
> > [snip]
> > 
> > > diff --git a/drivers/media/video/adp1653.c
> > > b/drivers/media/video/adp1653.c new file mode 100644
> > > index 0000000..1679707
> > > --- /dev/null
> > > +++ b/drivers/media/video/adp1653.c
> > 
> > [snip]
> > 
> > > +static int adp1653_get_fault(struct adp1653_flash *flash)
> > > +{
> > > +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> > > +	int fault;
> > > +	int rval;
> > > +
> > > +	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> > > +	if (IS_ERR_VALUE(fault))
> > > +		return fault;
> > > +
> > > +	flash->fault |= fault;
> > > +
> > > +	if (!flash->fault)
> > > +		return 0;
> > > +
> > > +	/* Clear faults. */
> > > +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> > > +	if (IS_ERR_VALUE(rval))
> > > +		return rval;
> > 
> > Should the faults be cleared right away, instead of when the user reads
> > the faults control ?
> > 
> > > +	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
> > 
> > Does the hardware switch back to "none" mode when a fault occurs ? The
> > datasheet just states that "the ADP1653 is disabled". Does that mean
> > temporarily disabled until the faults are cleared ? If so you should
> > update the registers to turn the LED off.
> 
> My understanding is that this is temporary until the faults are cleared.
> However, this is difficult to find out since the faults don't just occur
> that easily.
> 
> OUT_SEL register controls the current to LEDs so this turned everything
> off. The indicator should still remain on, I guess, since it's not
> affected by the faults, except possibly the over-temperature one.

OK, I'm fine with the code then.

> > > +	return flash->fault;
> > > +}
> > 
> > [snip]
> > 
> > > +static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
> > > +{
> > > +	struct adp1653_flash *flash =
> > > +		container_of(ctrl->handler, struct adp1653_flash, ctrls);
> > > +
> > > +	adp1653_get_fault(flash);
> > > +	if (IS_ERR_VALUE(flash->fault))
> > 
> > Shouldn't you check the adp1653_get_fault() return value instead ?
> 
> Yes. Good catch. I've tried to simulate this code a bit but as always,
> error handling tends not to be one of the best parts of the driver,
> especially those errors that generally do not happen. :-I
> 
> > > +		return flash->fault;
> > > +
> > > +	ctrl->cur.val = 0;
> > > +
> > > +	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
> > > +		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
> > > +	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
> > > +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
> > > +	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
> > > +		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
> > > +	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
> > > +		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
> > > +
> > > +	flash->fault = 0;
> > > +
> > > +	return 0;
> > > +}
> > 
> > [snip]
> > 
> > > +static int
> > > +adp1653_registered(struct v4l2_subdev *subdev)
> > > +{
> > > +	struct adp1653_flash *flash = to_adp1653_flash(subdev);
> > > +
> > > +	return adp1653_init_controls(flash);
> > 
> > Can't this be moved to adp1653_probe() ? You could then get rid of the
> > registered callback.
> 
> Good point. I'll look into this.
> 
> > > +}
> > > +
> > > +static int
> > > +adp1653_init_device(struct adp1653_flash *flash)
> > > +{
> > > +	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
> > > +	int rval;
> > > +
> > > +	/* Clear FAULT register by writing zero to OUT_SEL */
> > > +	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
> > > +	if (rval < 0) {
> > > +		dev_err(&client->dev, "failed writing fault register\n");
> > > +		return -EIO;
> > > +	}
> > > +
> > > +	/* Read FAULT register */
> > > +	rval = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
> > > +	if (rval < 0) {
> > > +		dev_err(&client->dev, "failed reading fault register\n");
> > > +		return -EIO;
> > > +	}
> > > +
> > > +	if ((rval & 0x0f) != 0) {
> > > +		dev_err(&client->dev, "device fault\n");
> > 
> > Same comment as last time :-)
> 
> Ouch. I'll try to get the fix done for the next time. :-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-05-18 12:40 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-17 15:13 [RFC v2 0/3] V4L2 API for flash devices and the adp1653 flash controller driver Sakari Ailus
2011-05-17 15:14 ` [RFC v2 1/3] v4l: Add a class and a set of controls for flash devices Sakari Ailus
2011-05-17 15:14 ` [RFC v2 2/3] v4l: Add flash control documentation Sakari Ailus
2011-05-17 15:14 ` [RFC v2 3/3] adp1653: Add driver for LED flash controller Sakari Ailus
2011-05-18  7:31   ` Laurent Pinchart
2011-05-18 11:29     ` Sakari Ailus
2011-05-18 12:40       ` Laurent Pinchart

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