All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone
@ 2010-09-11  1:17 Janusz Krzysztofik
  2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
                   ` (5 more replies)
  0 siblings, 6 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:17 UTC (permalink / raw)
  To: linux-media
  Cc: Guennadi Liakhovetski, linux-omap, Tony Lindgren, Richard Purdie,
	Discussion of the Amstrad E3 emailer hardware/software

This set consists of the following patches:

  1/6	SoC Camera: add driver for OMAP1 camera interface
  2/6	OMAP1: Add support for SoC camera interface
  3/6	SoC Camera: add driver for OV6650 sensor
  4/6	SoC Camera: add support for g_parm / s_parm operations
  5/6	OMAP1: Amstrad Delta: add support for on-board camera
  6/6	OMAP1: Amstrad Delta: add camera controlled LEDS trigger

 arch/arm/mach-omap1/board-ams-delta.c     |   69 +
 arch/arm/mach-omap1/devices.c             |   43
 arch/arm/mach-omap1/include/mach/camera.h |    8
 drivers/media/video/Kconfig               |   14
 drivers/media/video/Makefile              |    2
 drivers/media/video/omap1_camera.c        | 1781 ++++++++++++++++++++++++++++++
 drivers/media/video/ov6650.c              | 1242 ++++++++++++++++++++
 drivers/media/video/soc_camera.c          |   18
 include/media/omap1_camera.h              |   35
 include/media/v4l2-chip-ident.h           |    1
 10 files changed, 3213 insertions(+)


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

* [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
@ 2010-09-11  1:21 ` Janusz Krzysztofik
  2010-09-11  9:32   ` Janusz Krzysztofik
  2010-09-21 23:23   ` Guennadi Liakhovetski
  2010-09-11  1:23 ` [PATCH v2 2/6] OMAP1: Add support for SoC " Janusz Krzysztofik
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:21 UTC (permalink / raw)
  To: linux-media
  Cc: Guennadi Liakhovetski, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

This is a V4L2 driver for TI OMAP1 SoC camera interface.

Both videobuf-dma versions are supported, contig and sg, selectable with a 
module option. The former uses less processing power, but often fails to 
allocate contignuous buffer memory. The latter is free of this problem, but 
generates tens of DMA interrupts per frame. If contig memory allocation ever 
fails, the driver falls back to sg automatically on next open, but still can 
be switched back to contig manually. Both paths work stable for me, even 
under heavy load, on my OMAP1510 based Amstrad Delta videophone, that is the 
oldest, least powerfull OMAP1 implementation.

The interface generally works in pass-through mode. Since input data byte 
endianess can be swapped, it provides up to two v4l2 pixel formats per each of 
several soc_mbus formats that have their swapped endian counterparts.

Boards using this driver can provide it with the followning information:
- if and what freqency clock is expected by an on-board camera sensor,
- what is the maximum pixel clock that should be accepted from the sensor,
- what is the polarity of the sensor provided pixel clock,
- if the interface GPIO line is connected to a sensor reset/powerdown input 
  and what is the input polarity.

Created and tested against linux-2.6.36-rc3 on Amstrad Delta.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> So, I think, a very welcome improvement to the driver would be a 
> cleaner separation between the two cases. Don't try that much to reuse the
> code as much as possible. Would be much better to have clean separation
> between the two implementations - whether dynamically switchable at
> runtime or at config time - would be best to separate the two
> implementations to the point, where each of them becomes understandable
> and maintainable.

Guennadi,
I've tried to rearrange them spearated, as you requested, but finally decided 
to keep them together, as before, only better documented and cleaned up as 
much as possible. I'm rather satisfied with the result, but if you think it 
is still not enough understandable and maintainable, I'll take one more 
iteration and split both paths.

Besides, there are a few my not yet answered questions or suggestions (see 
http://www.spinics.net/lists/linux-media/msg21615.html for original message):

Friday 30 July 2010 20:49:05 Janusz Krzysztofik wrote:
> Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> > On Sun, 18 Jul 2010, Janusz Krzysztofik wrote:
> > > This is a V4L2 driver for TI OMAP1 SoC camera interface.

...
> > > +
> > > +	u32			reg_cache[OMAP1_CAMERA_IOSIZE / sizeof(u32)];
> >
> > Don't think you'd lose much performance without cache, for that the code
> > would become less error-prone.
>
> The reason is my bad experience with my hardware audio interface. For my
> rationale regarding OMAP McBSP register caching, please look at this
> commit:
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=96fbd74551e9cae8fd5e9cb62a0a19336a3064fa 
> or the foregoing discussions (links both to my initial and final submitions):
> http://thread.gmane.org/gmane.linux.alsa.devel/65675
> http://thread.gmane.org/gmane.linux.ports.arm.omap/29708
>
> However, I haven't noticed any similiar flaws in camera interface yet, so
> if you think that caching would not be helpful in any possible future
> (context save/restore) enhancements here, then I can drop it.
...
> > Don't you have to update the cache on a direct read?
>
> If I do it unconditionally, I'd loose the read error prevention function of
> register caching idea.

I've kept register caching for now, only replaced a few not intentional reads 
from hardware with more sensible reads from cache. Please confirm if you 
prefere me dropping register caching anyways.

...
> > > +static void videobuf_done(struct omap1_cam_dev *pcdev,
> > > +		enum videobuf_state result)
> > > +{
> > > +	...
> > > +	if (waitqueue_active(&vb->done)) {
> >
> > I'm not sure this is a good idea. Why are you reusing the buffer, if
> > noone is waiting for it _now_? It can well be, that the task is doing
> > something else atm. Like processing the previous frame. Or it has been
> > preempted by another process, before it called poll() or select() on your
> > device?
>
> I've introduced this way of videobuf handling after learning, form the
> Documentation/video4linux/videobuf, what the first step of filling a buffer
> should be:
>
> "- Obtain the next available buffer and make sure that somebody is actually
>    waiting for it."
>
> Since I had no idea how I could implement it, I decided to do it a bit
> differently: start filling a buffer unconditionally, then drop its contents
> if not awaited by any user when done
> 
> > > ...
> > > +	} else if (pcdev->ready) {
> > > +		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
> > > +				__func__);
> >
> > Not sure this is a wise decision... If there are more buffers in the
> > queue, they stay there waiting. Best remove this waitqueue_active() check
> > entirely, just complete buffers and wake up the user regardless. If no
> > more buffers - go to sleep.
>
> If you think I don't have to follow the above mentioned requirement for not
> servicing a buffer unless someone is waiting for it, then yes, I'll drop
> these overcomplicated bits. Please confirm.

One more reference to Documentation/video4linux/videobuf:

"It is equally possible that nobody is yet interested in the
buffer; the driver should not remove it from the list or fill it until a
process is waiting on it."

Please review my now better commented code again, and confirm if you still 
expect me to rearrange it, ignoring the documentation provided requirements.

...
> > > +static int omap1_cam_set_crop(struct soc_camera_device *icd,
> > > +			       struct v4l2_crop *crop)
> > > +{
> > > ...
> > > +	icd->user_width	 = rect->width;
> > > +	icd->user_height = rect->height;
> >
> > No, this is wrong. user_width and user_height are _user_ sizes, i.e., a
> > result sensor cropping and scaling and host cropping and scaling. Whereas
> > set_crop sets sensor input rectangle.
>
> I'm not sure if I understand what you mean. Should I drop these
> assignments? Or correct them? If yes, how they should be calculated?
>
> In Documentation/video4linux/soc-camera.txt, you say:
> "The core updates these fields upon successful completion of a .s_fmt()
> call, but if these fields change elsewhere, e.g., during .s_crop()
> processing, the host driver is responsible for updating them."
>
> I thought this is the case we're dealing with here.

Please confirm if I my understanding of what the documentation says is wrong 
and I should really correct the above.

Thanks,
Janusz


v1->v2 changes:

requested by Guennadi Liakhovetski (thanks!):
- first try contig, and if it fails - fall back to sg; invalidates next two,
- invalidated: Kconfig: VIDEO_OMAP1_SG: not need "if", the "depends on" should 
  suffice,
- invalidated: include both <media/videobuf-dma-contig.h> and 
  <media/videobuf-dma-sg.h> headers,
- extensively document buffer manipulations, better yet clean it up,
- a copyright / licence header was missing form a header file, 
- need to #include <linux/bitops.h> if using BIT() macro,
- don't need macros representing frequencies - use numbers directly,
- add a few missing "static" qualifiers,
- use u32 type for register content handling,
- some cached registers were unnecessarily read from the hardware directly,
- use true/false constants instead of 0/1 for booleans,
- avoid assigning variables inside other constructs,
- don't need to test if RAZ_FIFO is cleared,
- no need to split "\n" to a new line, don't worry about > 80 characters,
- don't increment field_count in case of a VIDEOBUF_ERROR,
- adjust mbus format codes to the new names,
- make is_dma_aligned() return value a bool,
- no need to align frame line lenghts to integer number of DMA elements,
- drop a few superflous whitespace, braces, etc.,
- use correct multiline comment style,
- follow some good-style rules when defining a multi-line function-like macro,
- drop tests for impossible bool < 0,
- that's not an error when S_FMT sets a format different from what the user
  has requested, just use whatever you managed to configure,
- as a general rule, try_fmt shouldn't fail,
- use sensor_reset() instead of duplicating its code,
- first shut down the hardware, and only then unconfigure software,

suggested by Ralph Corderoy (thanks!):
- include vb->state's value in the debug messages instead of just "unknown",
- correct a few print formats,
- don't use goto if return is all that needs to be done,

other:
- correct a few issues reported with "checkpatch.pl --strict".
- refreshed against linux-2.6.36-rc3


 drivers/media/video/Kconfig        |    8
 drivers/media/video/Makefile       |    1
 drivers/media/video/omap1_camera.c | 1781 +++++++++++++++++++++++++++++++++++++
 include/media/omap1_camera.h       |   35
 4 files changed, 1825 insertions(+)


diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Kconfig linux-2.6.36-rc3/drivers/media/video/Kconfig
--- linux-2.6.36-rc3.orig/drivers/media/video/Kconfig	2010-09-03 22:29:37.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/Kconfig	2010-09-09 17:55:52.000000000 +0200
@@ -890,6 +890,14 @@ config VIDEO_SH_MOBILE_CEU
 	---help---
 	  This is a v4l2 driver for the SuperH Mobile CEU Interface
 
+config VIDEO_OMAP1
+	tristate "OMAP1 Camera Interface driver"
+	depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+	select VIDEOBUF_DMA_CONTIG
+	select VIDEOBUF_DMA_SG
+	---help---
+	  This is a v4l2 driver for the TI OMAP1 camera interface
+
 config VIDEO_OMAP2
 	tristate "OMAP2 Camera Capture Interface driver"
 	depends on VIDEO_DEV && ARCH_OMAP2
diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Makefile linux-2.6.36-rc3/drivers/media/video/Makefile
--- linux-2.6.36-rc3.orig/drivers/media/video/Makefile	2010-09-03 22:29:37.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/Makefile	2010-09-09 17:55:52.000000000 +0200
@@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3)			+= mx3_camera.
 obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP1)		+= omap1_camera.o
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
 
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
diff -upr linux-2.6.36-rc3.orig/drivers/media/video/omap1_camera.c linux-2.6.36-rc3/drivers/media/video/omap1_camera.c
--- linux-2.6.36-rc3.orig/drivers/media/video/omap1_camera.c	2010-09-03 22:34:02.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/omap1_camera.c	2010-09-09 15:58:22.000000000 +0200
@@ -0,0 +1,1781 @@
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * 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.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include <media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <plat/dma.h>
+
+
+#define DRIVER_NAME		"omap1-camera"
+#define VERSION_CODE		KERNEL_VERSION(0, 0, 1)
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *  OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK		0x00
+#define REG_IT_STATUS		0x04
+#define REG_MODE		0x08
+#define REG_STATUS		0x0C
+#define REG_CAMDATA		0x10
+#define REG_GPIO		0x14
+#define REG_PEAK_COUNTER	0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN			BIT(7)
+#define DPLL_EN			BIT(6)
+#define MCLK_EN			BIT(5)
+#define CAMEXCLK_EN		BIT(4)
+#define POLCLK			BIT(3)
+#define FOSCMOD_SHIFT		0
+#define FOSCMOD_MASK		(0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz		0x0
+#define FOSCMOD_6MHz		0x2
+#define FOSCMOD_9_6MHz		0x4
+#define FOSCMOD_24MHz		0x5
+#define FOSCMOD_8MHz		0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER		BIT(5)
+#define FIFO_FULL		BIT(4)
+#define H_DOWN			BIT(3)
+#define H_UP			BIT(2)
+#define V_DOWN			BIT(1)
+#define V_UP			BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO		BIT(18)
+#define EN_FIFO_FULL		BIT(17)
+#define EN_NIRQ			BIT(16)
+#define THRESHOLD_SHIFT		9
+#define THRESHOLD_MASK		(0x7f << THRESHOLD_SHIFT)
+#define DMA			BIT(8)
+#define EN_H_DOWN		BIT(7)
+#define EN_H_UP			BIT(6)
+#define EN_V_DOWN		BIT(5)
+#define EN_V_UP			BIT(4)
+#define ORDERCAMD		BIT(3)
+
+#define IRQ_MASK		(EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+				 EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS			BIT(1)
+#define VSTATUS			BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST			BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS	(SOCAM_MASTER | \
+			SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
+			SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
+			SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
+
+
+#define FIFO_SIZE		((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT		__fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT		(1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE		(1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT	OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE	(1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG	(FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG	DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x)	(x ? DMA_FRAME_SHIFT_SG : \
+						DMA_FRAME_SHIFT_CONTIG)
+#define DMA_FRAME_SIZE(x)	(1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC		OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL		DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM		4	/* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+	struct videobuf_buffer		vb;
+	enum v4l2_mbus_pixelcode	code;
+	int				inwork;
+	struct scatterlist		*sgbuf;
+	int				sgcount;
+	int				bytes_left;
+	enum videobuf_state		result;
+};
+
+struct omap1_cam_dev {
+	struct soc_camera_host		soc_host;
+	struct soc_camera_device	*icd;
+	struct clk			*clk;
+
+	unsigned int			irq;
+	void __iomem			*base;
+
+	int				dma_ch;
+
+	struct omap1_cam_platform_data	*pdata;
+	struct resource			*res;
+	unsigned long			pflags;
+	unsigned long			camexclk;
+
+	struct list_head		capture;
+
+	/* lock used to protect videobuf */
+	spinlock_t			lock;
+
+	u32				reg_cache[OMAP1_CAMERA_IOSIZE /
+							sizeof(u32)];
+
+	/* Pointers to DMA buffers */
+	struct omap1_cam_buf		*active;
+	struct omap1_cam_buf		*ready;
+
+	enum omap1_cam_vb_mode		vb_mode;
+	int				(*mmap_mapper)(struct videobuf_queue *q,
+						struct videobuf_buffer *buf,
+						struct vm_area_struct *vma);
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+	pcdev->reg_cache[reg / sizeof(u32)] = val;
+	__raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+	return !from_cache ? __raw_readl(pcdev->base + reg) :
+			pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+		cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+		cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+		cam_read(pcdev, REG_##reg, true)
+
+/*
+ *  Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+		unsigned int *size)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+			icd->current_fmt->host_fmt);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	*size = bytes_per_line * icd->user_height;
+
+	if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+		*count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+	if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+		*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+	dev_dbg(icd->dev.parent,
+			"%s: count=%d, size=%d\n", __func__, *count, *size);
+
+	return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+		enum omap1_cam_vb_mode vb_mode)
+{
+	struct videobuf_buffer *vb = &buf->vb;
+
+	BUG_ON(in_interrupt());
+
+	videobuf_waiton(vb, 0, 0);
+
+	if (vb_mode == CONTIG) {
+		videobuf_dma_contig_free(vq, vb);
+	} else {
+		struct soc_camera_device *icd = vq->priv_data;
+		struct device *dev = icd->dev.parent;
+		struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+		videobuf_dma_unmap(dev, dma);
+		videobuf_dma_free(dma);
+	}
+
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+		struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+			icd->current_fmt->host_fmt);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	int ret;
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	WARN_ON(!list_empty(&vb->queue));
+
+	BUG_ON(NULL == icd->current_fmt);
+
+	buf->inwork = 1;
+
+	if (buf->code != icd->current_fmt->code || vb->field != field ||
+			vb->width  != icd->user_width ||
+			vb->height != icd->user_height) {
+		buf->code  = icd->current_fmt->code;
+		vb->width  = icd->user_width;
+		vb->height = icd->user_height;
+		vb->field  = field;
+		vb->state  = VIDEOBUF_NEEDS_INIT;
+	}
+
+	vb->size = bytes_per_line * vb->height;
+
+	if (vb->baddr && vb->bsize < vb->size) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (vb->state == VIDEOBUF_NEEDS_INIT) {
+		ret = videobuf_iolock(vq, vb, NULL);
+		if (ret)
+			goto fail;
+
+		vb->state = VIDEOBUF_PREPARED;
+	}
+	buf->inwork = 0;
+
+	return 0;
+fail:
+	free_buffer(vq, buf, pcdev->vb_mode);
+out:
+	buf->inwork = 0;
+	return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+		enum omap1_cam_vb_mode vb_mode)
+{
+	dma_addr_t dma_addr;
+	unsigned int block_size;
+
+	if (vb_mode == CONTIG) {
+		dma_addr = videobuf_to_dma_contig(&buf->vb);
+		block_size = buf->vb.size;
+	} else {
+		if (WARN_ON(!buf->sgbuf)) {
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		dma_addr = sg_dma_address(buf->sgbuf);
+		if (WARN_ON(!dma_addr)) {
+			buf->sgbuf = NULL;
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		block_size = sg_dma_len(buf->sgbuf);
+		if (WARN_ON(!block_size)) {
+			buf->sgbuf = NULL;
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		if (unlikely(buf->bytes_left < block_size))
+			block_size = buf->bytes_left;
+		if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+				DMA_ELEMENT_SIZE - 1))) {
+			dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+					DMA_ELEMENT_SIZE);
+			block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+					DMA_ELEMENT_SIZE - 1);
+		}
+		buf->bytes_left -= block_size;
+		buf->sgcount++;
+	}
+
+	omap_set_dma_dest_params(dma_ch,
+		OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+	omap_set_dma_transfer_params(dma_ch,
+		OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+		block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+		DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+	struct omap1_cam_buf *buf;
+
+	/*
+	 * If there is already a buffer pointed out by the pcdev->ready,
+	 * (re)use it, otherwise try to fetch and configure a new one.
+	 */
+	buf = pcdev->ready;
+	if (!buf) {
+		if (list_empty(&pcdev->capture))
+			return buf;
+		buf = list_entry(pcdev->capture.next,
+				struct omap1_cam_buf, vb.queue);
+		buf->vb.state = VIDEOBUF_ACTIVE;
+		pcdev->ready = buf;
+		list_del_init(&buf->vb.queue);
+	}
+
+	if (pcdev->vb_mode == CONTIG) {
+		/*
+		 * In CONTIG mode, we can safely enter next buffer parameters
+		 * into the DMA programming register set after the DMA
+		 * has already been activated on the previous buffer
+		 */
+		set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+	} else {
+		/*
+		 * In SG mode, the above is not safe since there are probably
+		 * a bunch of sgbufs from previous sglist still pending.
+		 * Instead, mark the sglist fresh for the upcoming
+		 * try_next_sgbuf().
+		 */
+		buf->sgbuf = NULL;
+	}
+
+	return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+	struct scatterlist *sgbuf;
+
+	if (likely(buf->sgbuf)) {
+		/* current sglist is active */
+		if (unlikely(!buf->bytes_left)) {
+			/* indicate sglist complete */
+			sgbuf = NULL;
+		} else {
+			/* process next sgbuf */
+			sgbuf = sg_next(buf->sgbuf);
+			if (WARN_ON(!sgbuf)) {
+				buf->result = VIDEOBUF_ERROR;
+			} else if (WARN_ON(!sg_dma_len(sgbuf))) {
+				sgbuf = NULL;
+				buf->result = VIDEOBUF_ERROR;
+			}
+		}
+		buf->sgbuf = sgbuf;
+	} else {
+		/* sglist is fresh, initialize it before using */
+		struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+		sgbuf = dma->sglist;
+		if (!(WARN_ON(!sgbuf))) {
+			buf->sgbuf = sgbuf;
+			buf->sgcount = 0;
+			buf->bytes_left = buf->vb.size;
+			buf->result = VIDEOBUF_DONE;
+		}
+	}
+	if (sgbuf)
+		/*
+		 * Put our next sgbuf parameters (address, size)
+		 * into the DMA programming register set.
+		 */
+		set_dma_dest_params(dma_ch, buf, SG);
+
+	return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+	struct omap1_cam_buf *buf = pcdev->active;
+	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+	if (WARN_ON(!buf))
+		return;
+
+	/*
+	 * Enable start of frame interrupt, which we will use for activating
+	 * our end of frame watchdog when capture actually starts.
+	 */
+	mode |= EN_V_UP;
+
+	if (unlikely(ctrlclock & LCLK_EN))
+		/* stop pixel clock before FIFO reset */
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+	/* reset FIFO */
+	CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+	omap_start_dma(pcdev->dma_ch);
+
+	if (pcdev->vb_mode == SG) {
+		/*
+		 * In SG mode, it's a good moment for fetching next sgbuf
+		 * from the current sglist and, if available, already putting
+		 * its parameters into the DMA programming register set.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	/* (re)enable pixel clock */
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+	/* release FIFO reset */
+	CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+	omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+	u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+	CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+						struct videobuf_buffer *vb)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct omap1_cam_buf *buf;
+	u32 mode;
+
+	list_add_tail(&vb->queue, &pcdev->capture);
+	vb->state = VIDEOBUF_QUEUED;
+
+	if (pcdev->active)
+		return;
+
+	WARN_ON(pcdev->ready);
+
+	buf = prepare_next_vb(pcdev);
+	if (WARN_ON(!buf))
+		return;
+
+	pcdev->active = buf;
+	pcdev->ready = NULL;
+
+	dev_dbg(icd->dev.parent,
+		"%s: capture not active, setup FIFO, start DMA\n", __func__);
+	mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+	mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+	CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+	if (pcdev->vb_mode == SG) {
+		/*
+		 * In SG mode, the above prepare_next_vb() didn't actually
+		 * put anything into the DMA programming register set,
+		 * so we have to do it now, before activating DMA.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+				 struct videobuf_buffer *vb)
+{
+	struct omap1_cam_buf *buf =
+			container_of(vb, struct omap1_cam_buf, vb);
+	struct soc_camera_device *icd = vq->priv_data;
+	struct device *dev = icd->dev.parent;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	switch (vb->state) {
+	case VIDEOBUF_DONE:
+		dev_dbg(dev, "%s (done)\n", __func__);
+		break;
+	case VIDEOBUF_ACTIVE:
+		dev_dbg(dev, "%s (active)\n", __func__);
+		break;
+	case VIDEOBUF_QUEUED:
+		dev_dbg(dev, "%s (queued)\n", __func__);
+		break;
+	case VIDEOBUF_PREPARED:
+		dev_dbg(dev, "%s (prepared)\n", __func__);
+		break;
+	default:
+		dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+		break;
+	}
+
+	free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+		enum videobuf_state result)
+{
+	struct omap1_cam_buf *buf = pcdev->active;
+	struct videobuf_buffer *vb;
+	struct device *dev = pcdev->icd->dev.parent;
+
+	if (WARN_ON(!buf)) {
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		return;
+	}
+
+	if (result == VIDEOBUF_ERROR)
+		suspend_capture(pcdev);
+
+	vb = &buf->vb;
+	if (waitqueue_active(&vb->done)) {
+		if (!pcdev->ready && result != VIDEOBUF_ERROR)
+			/*
+			 * No next buffer has been entered into the DMA
+			 * programming register set on time, so best we can do
+			 * is stopping the capture before last DMA block,
+			 * whether our CONTIG mode whole buffer or its last
+			 * sgbuf in SG mode, gets overwritten by next frame.
+			 */
+			suspend_capture(pcdev);
+		vb->state = result;
+		do_gettimeofday(&vb->ts);
+		if (result != VIDEOBUF_ERROR)
+			vb->field_count++;
+		wake_up(&vb->done);
+
+		/* shift in next buffer */
+		buf = pcdev->ready;
+		pcdev->active = buf;
+		pcdev->ready = NULL;
+
+		if (!buf) {
+			/*
+			 * No next buffer was ready on time (see above), so
+			 * indicate error condition to force capture restart or
+			 * stop, depending on next buffer already queued or not.
+			 */
+			result = VIDEOBUF_ERROR;
+			prepare_next_vb(pcdev);
+
+			buf = pcdev->ready;
+			pcdev->active = buf;
+			pcdev->ready = NULL;
+		}
+	} else if (pcdev->ready) {
+		/*
+		 * In both CONTIG and SG mode, the DMA engine has (might)
+		 * already been autoreinitialized with the preprogrammed
+		 * pcdev->ready buffer.  We can either accept this fact
+		 * and just swap the buffers, or provoke an error condition
+		 * and restart capture.  The former seems less intrusive.
+		 */
+		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+				__func__);
+		pcdev->active = pcdev->ready;
+
+		if (pcdev->vb_mode == SG) {
+			/*
+			 * In SG mode, we have to make sure that the buffer we
+			 * are putting back into the pcdev->ready is marked
+			 * fresh.
+			 */
+			buf->sgbuf = NULL;
+		}
+		pcdev->ready = buf;
+
+		buf = pcdev->active;
+	} else {
+		/*
+		 * No next buffer has been entered into
+		 * the DMA programming register set on time.
+		 */
+		if (pcdev->vb_mode == CONTIG) {
+			/*
+			 * In CONTIG mode, the DMA engine has already been
+			 * reinitialized with the current buffer. Best we can do
+			 * is not touching it.
+			 */
+			dev_dbg(dev,
+				"%s: nobody waiting on videobuf, reuse it\n",
+				__func__);
+		} else {
+			/*
+			 * In SG mode, the DMA engine has just been
+			 * autoreinitialized with the last sgbuf from the
+			 * current list. Restart capture in order to transfer
+			 * next frame start into the first sgbuf, not the last
+			 * one.
+			 */
+			if (result != VIDEOBUF_ERROR) {
+				suspend_capture(pcdev);
+				result = VIDEOBUF_ERROR;
+			}
+		}
+	}
+
+	if (!buf) {
+		dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+		disable_capture(pcdev);
+		return;
+	}
+
+	if (pcdev->vb_mode == CONTIG) {
+		/*
+		 * In CONTIG mode, the current buffer parameters had already
+		 * been entered into the DMA programming register set while the
+		 * buffer was fetched with prepare_next_vb(), they may have also
+		 * been transfered into the runtime set and already active if
+		 * the DMA still running.
+		 */
+	} else {
+		/* In SG mode, extra steps are required */
+		if (result == VIDEOBUF_ERROR)
+			/* make sure we (re)use sglist from start on error */
+			buf->sgbuf = NULL;
+
+		/*
+		 * In any case, enter the next sgbuf parameters into the DMA
+		 * programming register set.  They will be used either during
+		 * nearest DMA autoreinitialization or, in case of an error,
+		 * on DMA startup below.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	if (result == VIDEOBUF_ERROR) {
+		dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+				__func__);
+		start_capture(pcdev);
+		/*
+		 * In SG mode, the above also resulted in the next sgbuf
+		 * parameters being entered into the DMA programming register
+		 * set, making them ready for next DMA autoreinitialization.
+		 */
+	}
+
+	/*
+	 * Finally, try fetching next buffer.  In CONTIG mode, it will also
+	 * get entered it into the DMA programming register set,
+	 * making it ready for next DMA autoreinitialization.
+	 */
+	prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+	struct omap1_cam_dev *pcdev = data;
+	struct omap1_cam_buf *buf = pcdev->active;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pcdev->lock, flags);
+
+	if (WARN_ON(!buf)) {
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		goto out;
+	}
+
+	if (pcdev->vb_mode == CONTIG) {
+		/*
+		 * In CONTIG mode, assume we have just managed to collect the
+		 * whole frame, hopefully before our end of frame watchdog is
+		 * triggered. Then, all we have to do is disabling the watchdog
+		 * for this frame, and calling videobuf_done() with success
+		 * indicated.
+		 */
+		CAM_WRITE(pcdev, MODE,
+				CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+		videobuf_done(pcdev, VIDEOBUF_DONE);
+	} else {
+		/*
+		 * In SG mode, we have to process every sgbuf from the current
+		 * sglist, one after another.
+		 */
+		if (buf->sgbuf) {
+			/*
+			 * Current sglist not completed yet, try fetching next
+			 * sgbuf, hopefully putting it into the DMA programming
+			 * register set, making it ready for next DMA
+			 * autoreinitialization.
+			 */
+			try_next_sgbuf(pcdev->dma_ch, buf);
+			if (buf->sgbuf)
+				goto out;
+
+			/* no more sgbufs in the current sglist */
+			if (buf->result != VIDEOBUF_ERROR) {
+				/*
+				 * Video frame collected without errors, we can
+				 * prepare for collecting a next one as soon as
+				 * DMA gets autoreinitialized after the currennt
+				 * (last) sgbuf is completed.
+				 */
+				buf = prepare_next_vb(pcdev);
+				if (!buf)
+					goto out;
+
+				try_next_sgbuf(pcdev->dma_ch, buf);
+				goto out;
+			}
+		}
+		/* end of videobuf */
+		videobuf_done(pcdev, buf->result);
+	}
+
+out:
+	spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+	struct omap1_cam_dev *pcdev = data;
+	struct device *dev = pcdev->icd->dev.parent;
+	struct omap1_cam_buf *buf = pcdev->active;
+	u32 it_status;
+	unsigned long flags;
+
+	it_status = CAM_READ(pcdev, IT_STATUS);
+	if (!it_status)
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&pcdev->lock, flags);
+
+	if (WARN_ON(!buf)) {
+		dev_warn(dev, "%s: unhandled camera interrupt, status == "
+				"0x%0x\n", __func__, it_status);
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		goto out;
+	}
+
+	if (unlikely(it_status & FIFO_FULL)) {
+		dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+	} else if (it_status & V_DOWN) {
+		/* end of video frame watchdog */
+		if (pcdev->vb_mode == CONTIG) {
+			/*
+			 * In CONTIG mode, the watchdog is disabled with
+			 * successful DMA end of block interrupt, and reenabled
+			 * on next frame start. If we get here, there is nothing
+			 * to check, we must be out of sync.
+			 */
+		} else {
+			if (buf->sgcount == 2) {
+				/*
+				 * If exactly 2 sgbufs from the next sglist has
+				 * been programmed into the DMA engine (the
+				 * frist one already transfered into the DMA
+				 * runtime register set, the second one still
+				 * in the programming set), then we are in sync.
+				 */
+				goto out;
+			}
+		}
+		dev_notice(dev, "%s: unexpected end of video frame\n",
+				__func__);
+
+	} else if (it_status & V_UP) {
+		u32 mode;
+
+		if (pcdev->vb_mode == CONTIG) {
+			/*
+			 * In CONTIG mode, we need this interrupt every frame
+			 * in oredr to reenable our end of frame watchdog.
+			 */
+			mode = CAM_READ_CACHE(pcdev, MODE);
+		} else {
+			/*
+			 * In SG mode, the below enabled end of frame watchdog
+			 * is kept on permanently, so we can turn this one shot
+			 * setup off.
+			 */
+			mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+		}
+
+		if (!(mode & EN_V_DOWN)) {
+			/* (re)enable end of frame watchdog interrupt */
+			mode |= EN_V_DOWN;
+		}
+		CAM_WRITE(pcdev, MODE, mode);
+		goto out;
+
+	} else {
+		dev_warn(dev, "%s: unhandled camera interrupt, status == "
+				"0x%0x\n", __func__, it_status);
+		goto out;
+	}
+
+	videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+	spin_unlock_irqrestore(&pcdev->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+	.buf_setup	= omap1_videobuf_setup,
+	.buf_prepare	= omap1_videobuf_prepare,
+	.buf_queue	= omap1_videobuf_queue,
+	.buf_release	= omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+	/* apply/release camera sensor reset if requested by platform data */
+	if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+		CAM_WRITE(pcdev, GPIO, reset);
+	else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+		CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	u32 ctrlclock;
+
+	if (pcdev->icd)
+		return -EBUSY;
+
+	clk_enable(pcdev->clk);
+
+	/* setup sensor clock */
+	ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+	ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	ctrlclock &= ~FOSCMOD_MASK;
+	switch (pcdev->camexclk) {
+	case 6000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+		break;
+	case 8000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+		break;
+	case 9600000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+		break;
+	case 12000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+		break;
+	case 24000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+	default:
+		break;
+	}
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+	/* enable internal clock */
+	ctrlclock |= MCLK_EN;
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	sensor_reset(pcdev, false);
+
+	pcdev->icd = icd;
+
+	dev_info(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n",
+			icd->devnum);
+	return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	u32 ctrlclock;
+
+	BUG_ON(icd != pcdev->icd);
+
+	suspend_capture(pcdev);
+	disable_capture(pcdev);
+
+	sensor_reset(pcdev, true);
+
+	/* disable and release system clocks */
+	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+	clk_disable(pcdev->clk);
+
+	pcdev->icd = NULL;
+
+	dev_info(icd->dev.parent,
+		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_pixelfmt omap1_cam_formats[] = {
+	[V4L2_MBUS_FMT_UYVY8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_YUYV,
+		.name			= "YUYV",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_VYUY8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_YVYU,
+		.name			= "YVYU",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_YUYV8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_UYVY,
+		.name			= "UYVY",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_YVYU8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_VYUY,
+		.name			= "VYUY",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB555,
+		.name			= "RGB555",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB555X,
+		.name			= "RGB555X",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB565_2X8_BE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB565,
+		.name			= "RGB565",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB565_2X8_LE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB565X,
+		.name			= "RGB565X",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+		unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct device *dev = icd->dev.parent;
+	int formats = 0, ret;
+	enum v4l2_mbus_pixelcode code;
+	const struct soc_mbus_pixelfmt *fmt;
+
+	ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+	if (ret < 0)
+		/* No more formats */
+		return 0;
+
+	fmt = soc_mbus_get_fmtdesc(code);
+	if (!fmt) {
+		dev_err(dev, "%s: invalid format code #%d: %d\n", __func__,
+				idx, code);
+		return 0;
+	}
+
+	/* Check support for the requested bits-per-sample */
+	if (fmt->bits_per_sample != 8)
+		return 0;
+
+	switch (code) {
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+	case V4L2_MBUS_FMT_RGB565_2X8_BE:
+	case V4L2_MBUS_FMT_RGB565_2X8_LE:
+		formats++;
+		if (xlate) {
+			xlate->host_fmt	= &omap1_cam_formats[code];
+			xlate->code	= code;
+			xlate++;
+			dev_dbg(dev, "%s: providing format %s "
+					"as byte swapped code #%d\n", __func__,
+					omap1_cam_formats[code].name, code);
+		}
+	default:
+		if (xlate)
+			dev_dbg(dev, "%s: providing format %s "
+					"in pass-through mode\n", __func__,
+					fmt->name);
+	}
+	formats++;
+	if (xlate) {
+		xlate->host_fmt	= fmt;
+		xlate->code	= code;
+		xlate++;
+	}
+
+	return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height)
+{
+	int size = bytes_per_line * height;
+
+	return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+			IS_ALIGNED(size, DMA_FRAME_SIZE(CONTIG) *
+			DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+		const struct soc_mbus_pixelfmt *fmt, bool enlarge)
+{
+	s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	if (!is_dma_aligned(bytes_per_line, *height)) {
+		unsigned int pxalign = __fls(bytes_per_line / *width);
+		unsigned int salign  = DMA_FRAME_SHIFT(CONTIG) +
+				DMA_ELEMENT_SHIFT - pxalign;
+		unsigned int incr    = enlarge << salign;
+
+		v4l_bound_align_image(width, 1, *width + incr, 0,
+				height, 1, *height + incr, 0, salign);
+		return 0;
+	}
+	return 1;
+}
+
+/* returns 1 on g_crop() success, 0 on cropcap() success, <0 on error */
+static int get_crop(struct soc_camera_device *icd, struct v4l2_rect *rect)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct device *dev = icd->dev.parent;
+	struct v4l2_crop crop;
+	int ret;
+
+	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+	if (ret == -ENOIOCTLCMD) {
+		struct v4l2_cropcap cc;
+
+		dev_dbg(dev, "%s: g_crop() missing, trying cropcap() instead\n",
+				__func__);
+		cc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		ret = v4l2_subdev_call(sd, video, cropcap, &cc);
+		if (ret < 0)
+			return ret;
+		*rect = cc.defrect;
+		return 0;
+	} else if (ret < 0) {
+		return ret;
+	}
+	*rect = crop.c;
+	return 1;
+}
+
+/*
+ * returns 1 on g_mbus_fmt() or g_crop() success, 0 on cropcap() success,
+ * <0 on error
+ */
+static int get_geometry(struct soc_camera_device *icd, struct v4l2_rect *rect,
+		enum v4l2_mbus_pixelcode code)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct device *dev = icd->dev.parent;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+	if (ret == -ENOIOCTLCMD) {
+		struct v4l2_rect c;
+
+		dev_dbg(dev,
+			"%s: g_mbus_fmt() missing, trying g_crop() instead\n",
+			__func__);
+		ret = get_crop(icd, &c);
+		if (ret < 0)
+			return ret;
+
+		if (ret) {
+			*rect = c;	/* use g_crop() result */
+		} else {
+			dev_warn(dev, "%s: current geometry not available\n",
+					__func__);
+			return 0;
+		}
+	} else if (ret < 0) {
+		return ret;
+	} else if (mf.code != code) {
+		return -EINVAL;
+	} else {
+		rect->width  = mf.width;
+		rect->height = mf.height;
+	}
+	return 1;
+}
+
+#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...)	   \
+({									   \
+	struct soc_camera_sense sense = {				   \
+		.master_clock		= pcdev->camexclk,		   \
+		.pixel_clock_max	= 0,				   \
+	};								   \
+	int __ret;							   \
+									   \
+	if (pcdev->pdata)						   \
+		sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+	icd->sense = &sense;						   \
+	__ret = v4l2_subdev_call(sd, video, function, ##args);		   \
+	icd->sense = NULL;						   \
+									   \
+	if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {			   \
+		if (sense.pixel_clock > sense.pixel_clock_max) {	   \
+			dev_err(dev, "%s: pixel clock %lu "		   \
+					"set by the camera too high!\n",   \
+					__func__, sense.pixel_clock);	   \
+			__ret = -EINVAL;				   \
+		}							   \
+	}								   \
+	__ret;								   \
+})
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+			       struct v4l2_crop *crop)
+{
+	struct v4l2_rect *rect = &crop->c;
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct device *dev = icd->dev.parent;
+	s32 bytes_per_line;
+	int ret;
+
+	ret = dma_align(&rect->width, &rect->height, icd->current_fmt->host_fmt,
+			false);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+				__func__, rect->width, rect->height,
+				icd->current_fmt->host_fmt->name);
+		return ret;
+	}
+
+	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
+	if (ret < 0) {
+		dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+			 rect->width, rect->height, rect->left, rect->top);
+		return ret;
+	}
+
+	ret = get_geometry(icd, rect, icd->current_fmt->code);
+	if (ret < 0) {
+		dev_err(dev, "%s: get_geometry() failed\n", __func__);
+		return ret;
+	}
+	if (!ret)
+		dev_warn(dev, "%s: unable to verify s_crop() results\n",
+				__func__);
+
+	bytes_per_line = soc_mbus_bytes_per_line(rect->width,
+			icd->current_fmt->host_fmt);
+	if (bytes_per_line < 0) {
+		dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+				__func__);
+		return bytes_per_line;
+	}
+
+	ret = is_dma_aligned(bytes_per_line, rect->height);
+	if (!ret) {
+		dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+				__func__, rect->width, rect->height);
+		return -EINVAL;
+	}
+
+	icd->user_width	 = rect->width;
+	icd->user_height = rect->height;
+
+	return ret;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct device *dev = icd->dev.parent;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	struct v4l2_crop crop;
+	struct v4l2_rect *rect = &crop.c;
+	int bytes_per_line;
+	int ret;
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+	if (!xlate) {
+		dev_warn(dev, "%s: format %#x not found\n", __func__,
+				pix->pixelformat);
+		return -EINVAL;
+	}
+
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, &mf);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to set format\n", __func__);
+		return ret;
+	}
+
+	if (mf.code != xlate->code) {
+		dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+		return -EINVAL;
+	}
+
+	pix->width		= mf.width;
+	pix->height		= mf.height;
+	pix->field		= mf.field;
+	pix->colorspace		= mf.colorspace;
+	icd->current_fmt	= xlate;
+
+	ret = dma_align(&pix->width, &pix->height, xlate->host_fmt, false);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+				__func__, pix->width, pix->height,
+				xlate->host_fmt->name);
+		return ret;
+	}
+
+	if (ret == 1) {
+		/* sensor returned geometry was already DMA aligned */
+		return 0;
+	}
+
+	dev_notice(dev, "%s: sensor geometry not DMA aligned, trying to crop to"
+			" %ux%u\n", __func__, pix->width, pix->height);
+	ret = get_crop(icd, rect);
+	if (ret < 0) {
+		dev_err(dev, "%s: get_crop() failed\n", __func__);
+		return ret;
+	}
+
+	rect->width  = pix->width;
+	rect->height = pix->height;
+
+	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, &crop);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+			 rect->width, rect->height, rect->left, rect->top);
+		return ret;
+	}
+
+	ret = get_geometry(icd, rect, xlate->code);
+	if (ret < 0) {
+		dev_err(dev, "%s: get_geometry() failed\n", __func__);
+		return ret;
+	}
+
+	if (!ret)
+		dev_warn(dev, "%s: s_crop() results not confirmed\n", __func__);
+
+	bytes_per_line = soc_mbus_bytes_per_line(rect->width, xlate->host_fmt);
+	if (bytes_per_line < 0) {
+		dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+				__func__);
+		return bytes_per_line;
+	}
+
+	ret = is_dma_aligned(bytes_per_line, rect->height);
+	if (!ret) {
+		dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+				__func__, rect->width, rect->height);
+		return -EINVAL;
+	}
+
+	pix->width  = rect->width;
+	pix->height = rect->height;
+
+	return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+	/* TODO: limit to mx1 hardware capabilities */
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+	if (!xlate) {
+		dev_warn(icd->dev.parent, "Format %#x not found\n",
+			 pix->pixelformat);
+		return -EINVAL;
+	}
+
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	/* limit to sensor capabilities */
+	ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+	if (ret < 0)
+		return ret;
+
+	pix->width	= mf.width;
+	pix->height	= mf.height;
+	pix->field	= mf.field;
+	pix->colorspace	= mf.colorspace;
+
+	return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+				  struct videobuf_buffer *buf,
+				  struct vm_area_struct *vma)
+{
+	struct soc_camera_device *icd = q->priv_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	int ret;
+
+	ret = pcdev->mmap_mapper(q, buf, vma);
+
+	if (ret == -ENOMEM)
+		sg_mode = 1;
+
+	return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+				     struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	if (!sg_mode)
+		videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+				icd->dev.parent, &pcdev->lock,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+				sizeof(struct omap1_cam_buf), icd);
+	else
+		videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+				icd->dev.parent, &pcdev->lock,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+				sizeof(struct omap1_cam_buf), icd);
+
+	/* use videobuf mode (auto)selected with the module parameter */
+	pcdev->vb_mode = sg_mode ? SG : CONTIG;
+
+	/*
+	 * Ensure we substitute the videobuf-dma-contig version of the
+	 * mmap_mapper() callback with our own wrapper, used for switching
+	 * automatically to videobuf-dma-sg on buffer allocation failure.
+	 */
+	if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+		pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+		q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+	}
+}
+
+static int omap1_cam_reqbufs(struct soc_camera_file *icf,
+			      struct v4l2_requestbuffers *p)
+{
+	int i;
+
+	/*
+	 * This is for locking debugging only. I removed spinlocks and now I
+	 * check whether .prepare is ever called on a linked buffer, or whether
+	 * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+	 * it hadn't triggered
+	 */
+	for (i = 0; i < p->count; i++) {
+		struct omap1_cam_buf *buf = container_of(icf->vb_vidq.bufs[i],
+						      struct omap1_cam_buf, vb);
+		buf->inwork = 0;
+		INIT_LIST_HEAD(&buf->vb.queue);
+	}
+
+	return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+			       struct v4l2_capability *cap)
+{
+	/* cap->name is set by the friendly caller:-> */
+	strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+	cap->version = VERSION_CODE;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
+		__u32 pixfmt)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct device *dev = icd->dev.parent;
+	const struct soc_camera_format_xlate *xlate;
+	const struct soc_mbus_pixelfmt *fmt;
+	unsigned long camera_flags, common_flags;
+	u32 ctrlclock, mode;
+	int ret;
+
+	camera_flags = icd->ops->query_bus_param(icd);
+
+	common_flags = soc_camera_bus_param_compatible(camera_flags,
+			SOCAM_BUS_FLAGS);
+	if (!common_flags)
+		return -EINVAL;
+
+	/* Make choices, possibly based on platform configuration */
+	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
+			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+		if (!pcdev->pdata ||
+				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+		else
+			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+	}
+
+	ret = icd->ops->set_bus_param(icd, common_flags);
+	if (ret < 0)
+		return ret;
+
+	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	if (ctrlclock & LCLK_EN)
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
+		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+		ctrlclock |= POLCLK;
+	} else {
+		dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+		ctrlclock &= ~POLCLK;
+	}
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+	if (ctrlclock & LCLK_EN)
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	/* select bus endianess */
+	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+	fmt = xlate->host_fmt;
+
+	mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+	if (fmt->order == SOC_MBUS_ORDER_LE) {
+		dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+		CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+	} else {
+		dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+		CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+	}
+
+	return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+	struct soc_camera_file *icf = file->private_data;
+	struct omap1_cam_buf *buf;
+
+	buf = list_entry(icf->vb_vidq.stream.next, struct omap1_cam_buf,
+			 vb.stream);
+
+	poll_wait(file, &buf->vb.done, pt);
+
+	if (buf->vb.state == VIDEOBUF_DONE ||
+	    buf->vb.state == VIDEOBUF_ERROR)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+	.owner		= THIS_MODULE,
+	.add		= omap1_cam_add_device,
+	.remove		= omap1_cam_remove_device,
+	.get_formats	= omap1_cam_get_formats,
+	.set_crop	= omap1_cam_set_crop,
+	.set_fmt	= omap1_cam_set_fmt,
+	.try_fmt	= omap1_cam_try_fmt,
+	.init_videobuf	= omap1_cam_init_videobuf,
+	.reqbufs	= omap1_cam_reqbufs,
+	.querycap	= omap1_cam_querycap,
+	.set_bus_param	= omap1_cam_set_bus_param,
+	.poll		= omap1_cam_poll,
+};
+
+static int __init omap1_cam_probe(struct platform_device *pdev)
+{
+	struct omap1_cam_dev *pcdev;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	unsigned int irq;
+	int err = 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!res || (int)irq <= 0) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	clk = clk_get(&pdev->dev, "armper_ck");
+	if (IS_ERR(clk)) {
+		err = PTR_ERR(clk);
+		goto exit;
+	}
+
+	pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+	if (!pcdev) {
+		dev_err(&pdev->dev, "Could not allocate pcdev\n");
+		err = -ENOMEM;
+		goto exit_put_clk;
+	}
+
+	pcdev->res = res;
+	pcdev->clk = clk;
+
+	pcdev->pdata = pdev->dev.platform_data;
+	pcdev->pflags = pcdev->pdata->flags;
+
+	if (pcdev->pdata)
+		pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+
+	switch (pcdev->camexclk) {
+	case 6000000:
+	case 8000000:
+	case 9600000:
+	case 12000000:
+	case 24000000:
+		break;
+	default:
+		dev_warn(&pdev->dev,
+				"Incorrect sensor clock frequency %ld kHz, "
+				"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+				"please correct your platform data\n",
+				pcdev->pdata->camexclk_khz);
+		pcdev->camexclk = 0;
+	case 0:
+		dev_info(&pdev->dev,
+				"Not providing sensor clock\n");
+	}
+
+	INIT_LIST_HEAD(&pcdev->capture);
+	spin_lock_init(&pcdev->lock);
+
+	/*
+	 * Request the region.
+	 */
+	if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+		err = -EBUSY;
+		goto exit_kfree;
+	}
+
+	base = ioremap(res->start, resource_size(res));
+	if (!base) {
+		err = -ENOMEM;
+		goto exit_release;
+	}
+	pcdev->irq = irq;
+	pcdev->base = base;
+
+	sensor_reset(pcdev, true);
+
+	err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+			dma_isr, (void *)pcdev, &pcdev->dma_ch);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+		err = -EBUSY;
+		goto exit_iounmap;
+	}
+	dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+	/* preconfigure DMA */
+	omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+			OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+			0, 0);
+	omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+	/* setup DMA autoinitialization */
+	omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+	err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+	if (err) {
+		dev_err(&pdev->dev, "Camera interrupt register failed\n");
+		goto exit_free_dma;
+	}
+
+	pcdev->soc_host.drv_name	= DRIVER_NAME;
+	pcdev->soc_host.ops		= &omap1_host_ops;
+	pcdev->soc_host.priv		= pcdev;
+	pcdev->soc_host.v4l2_dev.dev	= &pdev->dev;
+	pcdev->soc_host.nr		= pdev->id;
+
+	err = soc_camera_host_register(&pcdev->soc_host);
+	if (err)
+		goto exit_free_irq;
+
+	dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+	return 0;
+
+exit_free_irq:
+	free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+	omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+	iounmap(base);
+exit_release:
+	release_mem_region(res->start, resource_size(res));
+exit_kfree:
+	kfree(pcdev);
+exit_put_clk:
+	clk_put(clk);
+exit:
+	return err;
+}
+
+static int __exit omap1_cam_remove(struct platform_device *pdev)
+{
+	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+	struct omap1_cam_dev *pcdev = container_of(soc_host,
+					struct omap1_cam_dev, soc_host);
+	struct resource *res;
+
+	free_irq(pcdev->irq, pcdev);
+
+	omap_free_dma(pcdev->dma_ch);
+
+	soc_camera_host_unregister(soc_host);
+
+	iounmap(pcdev->base);
+
+	res = pcdev->res;
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(pcdev);
+
+	clk_put(pcdev->clk);
+
+	dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+	return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= omap1_cam_probe,
+	.remove		= __exit_p(omap1_cam_remove),
+};
+
+static int __init omap1_cam_init(void)
+{
+	return platform_driver_register(&omap1_cam_driver);
+}
+module_init(omap1_cam_init);
+
+static void __exit omap1_cam_exit(void)
+{
+	platform_driver_unregister(&omap1_cam_driver);
+}
+module_exit(omap1_cam_exit);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff -upr linux-2.6.36-rc3.orig/include/media/omap1_camera.h linux-2.6.36-rc3/include/media/omap1_camera.h
--- linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03 22:34:02.000000000 +0200
+++ linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08 23:41:12.000000000 +0200
@@ -0,0 +1,35 @@
+/*
+ * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * 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.
+ */
+
+#ifndef __MEDIA_OMAP1_CAMERA_H_
+#define __MEDIA_OMAP1_CAMERA_H_
+
+#include <linux/bitops.h>
+
+#define OMAP1_CAMERA_IOSIZE		0x1c
+
+enum omap1_cam_vb_mode {
+	CONTIG = 0,
+	SG,
+};
+
+#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
+
+struct omap1_cam_platform_data {
+	unsigned long	camexclk_khz;
+	unsigned long	lclk_khz_max;
+	unsigned long	flags;
+};
+
+#define OMAP1_CAMERA_LCLK_RISING	BIT(0)
+#define OMAP1_CAMERA_RST_LOW		BIT(1)
+#define OMAP1_CAMERA_RST_HIGH		BIT(2)
+
+#endif /* __MEDIA_OMAP1_CAMERA_H_ */

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

* [PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
  2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
@ 2010-09-11  1:23 ` Janusz Krzysztofik
  2010-09-11  1:34   ` [RESEND][PATCH " Janusz Krzysztofik
  2010-09-22  6:53   ` [PATCH " Guennadi Liakhovetski
  2010-09-11  1:25 ` [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor Janusz Krzysztofik
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:23 UTC (permalink / raw)
  To: linux-omap
  Cc: linux-media, Guennadi Liakhovetski, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

This patch adds support for SoC camera interface to OMAP1 devices.

Created and tested against linux-2.6.36-rc3 on Amstrad Delta.

For successfull compilation, requires a header file provided by PATCH 1/6 from 
this series, "SoC Camera: add driver for OMAP1 camera interface".

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
v1->v2 changes:
- no functional changes,
- refreshed against linux-2.6.36-rc3


 arch/arm/mach-omap1/devices.c             |   43 
++++++++++++++++++++++++++++++
 arch/arm/mach-omap1/include/mach/camera.h |    8 +++++
 2 files changed, 51 insertions(+)


diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c 
linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c	2010-09-03 
22:29:00.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c	2010-09-09 
18:42:30.000000000 +0200
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/spi/spi.h>
+#include <linux/dma-mapping.h>
 
 #include <mach/hardware.h>
 #include <asm/mach/map.h>
@@ -25,6 +26,7 @@
 #include <mach/gpio.h>
 #include <plat/mmc.h>
 #include <plat/omap7xx.h>
+#include <mach/camera.h>
 
 /*-------------------------------------------------------------------------*/
 
@@ -191,6 +193,47 @@ static inline void omap_init_spi100k(voi
 }
 #endif
 
+
+#define OMAP1_CAMERA_BASE	0xfffb6800
+
+static struct resource omap1_camera_resources[] = {
+	[0] = {
+		.start	= OMAP1_CAMERA_BASE,
+		.end	= OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= INT_CAMERA,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device omap1_camera_device = {
+	.name		= "omap1-camera",
+	.id		= 0, /* This is used to put cameras on this interface */
+	.dev		= {
+		.dma_mask		= &omap1_camera_dma_mask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(omap1_camera_resources),
+	.resource	= omap1_camera_resources,
+};
+
+void __init omap1_set_camera_info(struct omap1_cam_platform_data *info)
+{
+	struct platform_device *dev = &omap1_camera_device;
+	int ret;
+
+	dev->dev.platform_data = info;
+
+	ret = platform_device_register(dev);
+	if (ret)
+		dev_err(&dev->dev, "unable to register device: %d\n", ret);
+}
+
+
 /*-------------------------------------------------------------------------*/
 
 static inline void omap_init_sti(void) {}
diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h 
linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-03 
22:34:03.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09 
18:42:30.000000000 +0200
@@ -0,0 +1,8 @@
+#ifndef __ASM_ARCH_CAMERA_H_
+#define __ASM_ARCH_CAMERA_H_
+
+#include <media/omap1_camera.h>
+
+extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
+
+#endif /* __ASM_ARCH_CAMERA_H_ */

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

* [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
  2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
  2010-09-11  1:23 ` [PATCH v2 2/6] OMAP1: Add support for SoC " Janusz Krzysztofik
@ 2010-09-11  1:25 ` Janusz Krzysztofik
  2010-09-22  9:12   ` Guennadi Liakhovetski
  2010-09-11  1:26 ` [PATCH v2 4/6] SoC Camera: add support for g_parm / s_parm operations Janusz Krzysztofik
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:25 UTC (permalink / raw)
  To: linux-media
  Cc: Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

This patch provides a V4L2 SoC Camera driver for OV6650 camera sensor, found 
on OMAP1 SoC based Amstrad Delta videophone.

Since I have no experience with camera sensors, and the sensor documentation I 
was able to find was not very comprehensive, I left most settings at their 
default (reset) values, except for:
- those required for proper mediabus parameters and picture format setup,
- those used by controls.
Resulting picture quality is far from perfect, but better than nothing.

In order to be able to get / set the sensor frame rate from userspace, I 
decided to provide two not yet SoC camera supported operations, g_parm and 
s_parm. These can be used after applying patch 4/6 from this series, 
"SoC Camera: add support for g_parm / s_parm operations".

Created and tested against linux-2.6.36-rc3 on Amstrad Delta.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
v1->v2 changes:

requested by Guennadi Liakhovetski (thanks!):
- include <linux/bitops.h> if using BIT() macro,
- sort headers alphabetically,
- don't mix tabs with spaces (preferred) when separating symbols from 
  #define keywords,
- drop unused NUM_REGS definition,
- optimize SET_SAT() and SAT_MASK macros,
- reuse no longer needed function argument instead of declaring a new local 
  variable,
- don't touch auto controls when changing their correspondig manual settings, 
  and vice versa,
- drop probably unsupported auto-hue control,
- initialize sensor by writing registers explicitly instead of using a "magic" 
  initialization array,
- avoid gotos, don't use them other than in failure cases,
- make pclk computation more readable,
- implement g_mbus_fmt() callback,
- correct a few obvious mistakes,
- remove a few extra whitespaces,

suggested by Ralph Corderoy (thanks!):
- use one common format when hex printing register addresses and values,
- optimize if(ret) vs. if(!ret) constructs usage,
- replace a few if-else constructs with more compact, conditional 
  expression based, when translating controls to register bits,
- optimize ov6650_res_roundup(),
- drop redundant cast of index from ov6650_enum_fmt(),
- use variable identifiers rather than their types as sizeof() arguments,

other:
- disable band filter, auto exposure control seems working more effectively 
  without it,
- refresh against linux-2.6.36-rc3.


 drivers/media/video/Kconfig     |    6
 drivers/media/video/Makefile    |    1
 drivers/media/video/ov6650.c    | 1242 ++++++++++++++++++++++++++++++++++++++++
 include/media/v4l2-chip-ident.h |    1
 4 files changed, 1250 insertions(+)

diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Kconfig linux-2.6.36-rc3/drivers/media/video/Kconfig
--- linux-2.6.36-rc3.orig/drivers/media/video/Kconfig	2010-09-03 22:34:02.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/Kconfig	2010-09-03 22:34:02.000000000 +0200
@@ -835,6 +835,12 @@ config SOC_CAMERA_PLATFORM
 	help
 	  This is a generic SoC camera platform driver, useful for testing
 
+config SOC_CAMERA_OV6650
+	tristate "ov6650 sensor support"
+	depends on SOC_CAMERA && I2C
+	---help---
+	  This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
+
 config SOC_CAMERA_OV772X
 	tristate "ov772x camera support"
 	depends on SOC_CAMERA && I2C
diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Makefile linux-2.6.36-rc3/drivers/media/video/Makefile
--- linux-2.6.36-rc3.orig/drivers/media/video/Makefile	2010-09-03 22:34:02.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/Makefile	2010-09-03 22:34:02.000000000 +0200
@@ -79,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m
 obj-$(CONFIG_SOC_CAMERA_MT9T031)	+= mt9t031.o
 obj-$(CONFIG_SOC_CAMERA_MT9T112)	+= mt9t112.o
 obj-$(CONFIG_SOC_CAMERA_MT9V022)	+= mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV6650)		+= ov6650.o
 obj-$(CONFIG_SOC_CAMERA_OV772X)		+= ov772x.o
 obj-$(CONFIG_SOC_CAMERA_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA_RJ54N1)		+= rj54n1cb0c.o
diff -upr linux-2.6.36-rc3.orig/drivers/media/video/ov6650.c linux-2.6.36-rc3/drivers/media/video/ov6650.c
--- linux-2.6.36-rc3.orig/drivers/media/video/ov6650.c	2010-09-03 22:34:02.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/ov6650.c	2010-09-04 17:50:32.000000000 +0200
@@ -0,0 +1,1242 @@
+/*
+ * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
+ *
+ * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on OmniVision OV96xx Camera Driver
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/sensor_ov6650.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+
+
+/* Register definitions */
+#define REG_GAIN		0x00	/* range 00 - 3F */
+#define REG_BLUE		0x01
+#define REG_RED			0x02
+#define REG_SAT			0x03	/* [7:4] saturation [0:3] reserved */
+#define REG_HUE			0x04	/* [7:6] rsrvd [5] hue en [4:0] hue */
+
+#define REG_BRT			0x06
+
+#define REG_PIDH		0x0a
+#define REG_PIDL		0x0b
+
+#define REG_AECH		0x10
+#define REG_CLKRC		0x11	/* Data Format and Internal Clock */
+					/* [7:6] Input system clock (MHz)*/
+					/*   00=8, 01=12, 10=16, 11=24 */
+					/* [5:0]: Internal Clock Pre-Scaler */
+#define REG_COMA		0x12	/* [7] Reset */
+#define REG_COMB		0x13
+#define REG_COMC		0x14
+#define REG_COMD		0x15
+#define REG_COML		0x16
+#define REG_HSTRT		0x17
+#define REG_HSTOP		0x18
+#define REG_VSTRT		0x19
+#define REG_VSTOP		0x1a
+#define REG_PSHFT		0x1b
+#define REG_MIDH		0x1c
+#define REG_MIDL		0x1d
+#define REG_HSYNS		0x1e
+#define REG_HSYNE		0x1f
+#define REG_COME		0x20
+#define REG_YOFF		0x21
+#define REG_UOFF		0x22
+#define REG_VOFF		0x23
+#define REG_AEW			0x24
+#define REG_AEB			0x25
+#define REG_COMF		0x26
+#define REG_COMG		0x27
+#define REG_COMH		0x28
+#define REG_COMI		0x29
+
+#define REG_FRARL		0x2b
+#define REG_COMJ		0x2c
+#define REG_COMK		0x2d
+#define REG_AVGY		0x2e
+#define REG_REF0		0x2f
+#define REG_REF1		0x30
+#define REG_REF2		0x31
+#define REG_FRAJH		0x32
+#define REG_FRAJL		0x33
+#define REG_FACT		0x34
+#define REG_L1AEC		0x35
+#define REG_AVGU		0x36
+#define REG_AVGV		0x37
+
+#define REG_SPCB		0x60
+#define REG_SPCC		0x61
+#define REG_GAM1		0x62
+#define REG_GAM2		0x63
+#define REG_GAM3		0x64
+#define REG_SPCD		0x65
+
+#define REG_SPCE		0x68
+#define REG_ADCL		0x69
+
+#define REG_RMCO		0x6c
+#define REG_GMCO		0x6d
+#define REG_BMCO		0x6e
+
+
+/* Register bits, values, etc. */
+#define OV6650_PIDH		0x66	/* high byte of product ID number */
+#define OV6650_PIDL		0x50	/* low byte of product ID number */
+#define OV6650_MIDH		0x7F	/* high byte of mfg ID */
+#define OV6650_MIDL		0xA2	/* low byte of mfg ID */
+
+#define DEF_GAIN		0x00
+#define DEF_BLUE		0x80
+#define DEF_RED			0x80
+
+#define SAT_SHIFT		4
+#define SAT_MASK		(0x0f << SAT_SHIFT)
+#define SET_SAT(x)		(((x) << SAT_SHIFT) & SAT_MASK)
+
+#define HUE_EN			BIT(5)
+#define HUE_MASK		0x1f
+#define DEF_HUE			0x10
+#define SET_HUE(x)		(HUE_EN | ((x) & HUE_MASK))
+
+#define DEF_AECH		0x4D
+
+#define CLKRC_6MHz		0x00
+#define CLKRC_12MHz		0x40
+#define CLKRC_16MHz		0x80
+#define CLKRC_24MHz		0xc0
+#define CLKRC_DIV_MASK		0x3f
+#define GET_CLKRC_DIV(x)	(((x) & CLKRC_DIV_MASK) + 1)
+
+#define COMA_RESET		BIT(7)
+#define COMA_QCIF		BIT(5)
+#define COMA_RAW_RGB		BIT(4)
+#define COMA_RGB		BIT(3)
+#define COMA_BW			BIT(2)
+#define COMA_WORD_SWAP		BIT(1)
+#define COMA_BYTE_SWAP		BIT(0)
+#define DEF_COMA		0x00
+
+#define COMB_FLIP_V		BIT(7)
+#define COMB_FLIP_H		BIT(5)
+#define COMB_BAND_FILTER	BIT(4)
+#define COMB_AWB		BIT(2)
+#define COMB_AGC		BIT(1)
+#define COMB_AEC		BIT(0)
+#define DEF_COMB		0x5f
+
+#define COML_ONE_CHANNEL	BIT(7)
+
+#define DEF_HSTRT		0x24
+#define DEF_HSTOP		0xd4
+#define DEF_VSTRT		0x04
+#define DEF_VSTOP		0x94
+
+#define COMF_HREF_LOW		BIT(4)
+
+#define COMJ_PCLK_RISING	BIT(4)
+#define COMJ_VSYNC_HIGH		BIT(0)
+
+/* supported resolutions */
+#define W_QCIF			(DEF_HSTOP - DEF_HSTRT)
+#define W_CIF			(W_QCIF << 1)
+#define H_QCIF			(DEF_VSTOP - DEF_VSTRT)
+#define H_CIF			(H_QCIF << 1)
+
+#define FRAME_RATE_MAX		30
+
+
+struct ov6650_reg {
+	u8	reg;
+	u8	val;
+};
+
+struct ov6650 {
+	struct v4l2_subdev	subdev;
+
+	int			gain;
+	int			blue;
+	int			red;
+	int			saturation;
+	int			hue;
+	int			brightness;
+	int			exposure;
+	int			gamma;
+	bool			vflip;
+	bool			hflip;
+	bool			awb;
+	bool			agc;
+	int			aec;
+	bool			qcif;
+	unsigned long		pclk_max;	/* from resolution and format */
+	unsigned long		pclk_limit;	/* from host */
+	struct v4l2_fract	timeperframe;	/* as requested with s_parm */
+	struct v4l2_rect	rect;
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace	colorspace;
+};
+
+
+static enum v4l2_mbus_pixelcode ov6650_codes[] = {
+	V4L2_MBUS_FMT_YUYV8_2X8,
+	V4L2_MBUS_FMT_UYVY8_2X8,
+	V4L2_MBUS_FMT_YVYU8_2X8,
+	V4L2_MBUS_FMT_VYUY8_2X8,
+	V4L2_MBUS_FMT_SBGGR8_1X8,
+	V4L2_MBUS_FMT_GREY8_1X8,
+};
+
+static const struct v4l2_queryctrl ov6650_controls[] = {
+	{
+		.id		= V4L2_CID_AUTOGAIN,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "AGC",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	},
+	{
+		.id		= V4L2_CID_GAIN,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Gain",
+		.minimum	= 0,
+		.maximum	= 0x3f,
+		.step		= 1,
+		.default_value	= DEF_GAIN,
+	},
+	{
+		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "AWB",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	},
+	{
+		.id		= V4L2_CID_BLUE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Blue",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_BLUE,
+	},
+	{
+		.id		= V4L2_CID_RED_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Red",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_RED,
+	},
+	{
+		.id		= V4L2_CID_SATURATION,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Saturation",
+		.minimum	= 0,
+		.maximum	= 0xf,
+		.step		= 1,
+		.default_value	= 0x8,
+	},
+	{
+		.id		= V4L2_CID_HUE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Hue",
+		.minimum	= 0,
+		.maximum	= 0x1f,
+		.step		= 1,
+		.default_value	= DEF_HUE,
+	},
+	{
+		.id		= V4L2_CID_BRIGHTNESS,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Brightness",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= 0x80,
+	},
+	{
+		.id		= V4L2_CID_EXPOSURE_AUTO,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "AEC",
+		.minimum	= 0,
+		.maximum	= 3,
+		.step		= 1,
+		.default_value	= 0,
+	},
+	{
+		.id		= V4L2_CID_EXPOSURE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Exposure",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_AECH,
+	},
+	{
+		.id		= V4L2_CID_GAMMA,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Gamma",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= 0x12,
+	},
+	{
+		.id		= V4L2_CID_VFLIP,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "Flip Vertically",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 0,
+	},
+	{
+		.id		= V4L2_CID_HFLIP,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "Flip Horizontally",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 0,
+	},
+};
+
+/* read a register */
+static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret;
+	u8 data = reg;
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 1,
+		.buf	= &data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data;
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/* write a register */
+static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+	unsigned char data[2] = { reg, val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	msleep_interruptible(1);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+	return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
+{
+	u8 val;
+	int ret;
+
+	ret = ov6650_reg_read(client, reg, &val);
+	if (ret) {
+		dev_err(&client->dev,
+			"[Read]-Modify-Write of register 0x%02x failed!\n",
+			reg);
+		return ret;
+	}
+
+	val &= ~mask;
+	val |= set;
+
+	ret = ov6650_reg_write(client, reg, val);
+	if (ret)
+		dev_err(&client->dev,
+			"Read-Modify-[Write] of register 0x%02x failed!\n",
+			reg);
+
+	return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov6650_reset(struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "reset\n");
+
+	ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
+	if (ret)
+		dev_err(&client->dev,
+			"An error occured while entering soft reset!\n");
+
+	return ret;
+}
+
+static struct ov6650 *to_ov6650(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
+}
+
+/* Start/Stop streaming from the device */
+static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_set_bus_param(struct soc_camera_device *icd,
+				unsigned long flags)
+{
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+	int ret;
+
+	flags = soc_camera_apply_sensor_flags(icl, flags);
+
+	if (flags & SOCAM_PCLK_SAMPLE_RISING)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+	if (ret)
+		return ret;
+
+	if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+		ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+	if (ret)
+		return ret;
+
+	if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+	return ret;
+}
+
+/* Request bus settings on camera side */
+static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
+{
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+	unsigned long flags = SOCAM_MASTER | \
+		SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
+		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | \
+		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \
+		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+	return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/* Get status of additional camera capabilities */
+static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	uint8_t reg;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		ctrl->value = priv->agc;
+		break;
+	case V4L2_CID_GAIN:
+		if (priv->agc) {
+			ret = ov6650_reg_read(client, REG_GAIN, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->gain;
+		}
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrl->value = priv->awb;
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		if (priv->awb) {
+			ret = ov6650_reg_read(client, REG_BLUE, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->blue;
+		}
+		break;
+	case V4L2_CID_RED_BALANCE:
+		if (priv->awb) {
+			ret = ov6650_reg_read(client, REG_RED, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->red;
+		}
+		break;
+	case V4L2_CID_SATURATION:
+		ctrl->value = priv->saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctrl->value = priv->hue;
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ctrl->value = priv->brightness;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		ctrl->value = priv->aec;
+		break;
+	case V4L2_CID_EXPOSURE:
+		if (priv->aec) {
+			ret = ov6650_reg_read(client, REG_AECH, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->exposure;
+		}
+		break;
+	case V4L2_CID_GAMMA:
+		ctrl->value = priv->gamma;
+		break;
+	case V4L2_CID_VFLIP:
+		ctrl->value = priv->vflip;
+		break;
+	case V4L2_CID_HFLIP:
+		ctrl->value = priv->hflip;
+		break;
+	}
+	return ret;
+}
+
+/* Set status of additional camera capabilities */
+static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_AGC : 0, COMB_AGC);
+		if (!ret)
+			priv->agc = ctrl->value;
+		break;
+	case V4L2_CID_GAIN:
+		ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
+		if (!ret)
+			priv->gain = ctrl->value;
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_AWB : 0, COMB_AWB);
+		if (!ret)
+			priv->awb = ctrl->value;
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
+		if (!ret)
+			priv->blue = ctrl->value;
+		break;
+	case V4L2_CID_RED_BALANCE:
+		ret = ov6650_reg_write(client, REG_RED, ctrl->value);
+		if (!ret)
+			priv->red = ctrl->value;
+		break;
+	case V4L2_CID_SATURATION:
+		ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
+				SAT_MASK);
+		if (!ret)
+			priv->saturation = ctrl->value;
+		break;
+	case V4L2_CID_HUE:
+		ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
+				HUE_MASK);
+		if (!ret)
+			priv->hue = ctrl->value;
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
+		if (!ret)
+			priv->brightness = ctrl->value;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		switch (ctrl->value) {
+		case V4L2_EXPOSURE_AUTO:
+			ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
+			break;
+		default:
+			ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
+			break;
+		}
+		if (!ret)
+			priv->aec = ctrl->value;
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
+		if (!ret)
+			priv->exposure = ctrl->value;
+		break;
+	case V4L2_CID_GAMMA:
+		ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
+		if (!ret)
+			priv->gamma = ctrl->value;
+		break;
+	case V4L2_CID_VFLIP:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
+		if (!ret)
+			priv->vflip = ctrl->value;
+		break;
+	case V4L2_CID_HFLIP:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
+		if (!ret)
+			priv->hflip = ctrl->value;
+		break;
+	}
+
+	return ret;
+}
+
+/* Get chip identification */
+static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
+				struct v4l2_dbg_chip_ident *id)
+{
+	id->ident	= V4L2_IDENT_OV6650;
+	id->revision	= 0;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov6650_get_register(struct v4l2_subdev *sd,
+				struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = sd->priv;
+	int ret;
+	u8 val;
+
+	if (reg->reg & ~0xff)
+		return -EINVAL;
+
+	reg->size = 1;
+
+	ret = ov6650_reg_read(client, reg->reg, &val);
+	if (!ret)
+		reg->val = (__u64)val;
+
+	return ret;
+}
+
+static int ov6650_set_register(struct v4l2_subdev *sd,
+				struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = sd->priv;
+
+	if (reg->reg & ~0xff || reg->val & ~0xff)
+		return -EINVAL;
+
+	return ov6650_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+/*
+ * Select nearest higher resolution for capture.
+ * To be dropped once a common function for doing this is implemented.
+ */
+static void ov6650_res_roundup(u32 *width, u32 *height)
+{
+	int i, lasti;
+	int res_x[] = { W_QCIF, W_CIF };
+	int res_y[] = { H_QCIF, H_CIF };
+
+	for (i = 0; i < ARRAY_SIZE(res_x); i++) {
+		lasti = i;
+		if (res_x[i] >= *width && res_y[i] >= *height)
+			break;
+	}
+
+	*width = res_x[lasti];
+	*height = res_y[lasti];
+}
+
+/* program default register values */
+static int ov6650_prog_dflt(struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "reinitializing\n");
+
+	ret = ov6650_reg_write(client, REG_COMA, 0);	/* ~COMA_RESET */
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
+
+	return ret;
+}
+
+static int ov6650_g_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+
+	mf->width	= priv->rect.width;
+	mf->height	= priv->rect.height;
+	mf->code	= priv->code;
+	mf->colorspace	= priv->colorspace;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+/* set the format we will capture in */
+static int ov6650_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = sd->priv;
+	struct soc_camera_device *icd	= client->dev.platform_data;
+	struct soc_camera_sense *sense = icd->sense;
+	struct ov6650 *priv = to_ov6650(client);
+	enum v4l2_mbus_pixelcode code = mf->code;
+	unsigned long pclk;
+	u8 coma_set = 0, coma_mask = 0, coml_set = 0, coml_mask = 0;
+	u8 clkrc, clkrc_div;
+	int ret;
+
+	/* select color matrix configuration for given color encoding */
+	switch (code) {
+	case V4L2_MBUS_FMT_GREY8_1X8:
+		dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
+		coma_set |= COMA_BW;
+		coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
+		break;
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+		dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
+		coma_set |= COMA_WORD_SWAP;
+		coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
+		break;
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+		dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
+		coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
+				COMA_BYTE_SWAP;
+		break;
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
+		if (mf->width == W_CIF) {
+			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+			coma_mask |= COMA_RGB | COMA_BW;
+		} else {
+			coma_set |= COMA_BYTE_SWAP;
+			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+		}
+		break;
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+		dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
+		if (mf->width == W_CIF) {
+			coma_set |= COMA_BYTE_SWAP;
+			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+		} else {
+			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+			coma_mask |= COMA_RGB | COMA_BW;
+		}
+		break;
+	case V4L2_MBUS_FMT_SBGGR8_1X8:
+		dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
+		coma_set |= COMA_RAW_RGB | COMA_RGB;
+		coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
+		break;
+	default:
+		dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
+		return -EINVAL;
+	}
+	priv->code = code;
+
+	if ((code == V4L2_MBUS_FMT_GREY8_1X8) ||
+			(code == V4L2_MBUS_FMT_SBGGR8_1X8)) {
+		coml_mask |= COML_ONE_CHANNEL;
+		priv->pclk_max = 4000000;
+	} else {
+		coml_set |= COML_ONE_CHANNEL;
+		priv->pclk_max = 8000000;
+	}
+
+	if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
+		priv->colorspace = V4L2_COLORSPACE_SRGB;
+	else
+		priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+	/*
+	 * Select register configuration for given resolution.
+	 * To be replaced with a common function that does it, once available.
+	 */
+	ov6650_res_roundup(&mf->width, &mf->height);
+
+	switch (mf->width) {
+	case W_QCIF:
+		dev_dbg(&client->dev, "resolution QCIF\n");
+		priv->qcif = 1;
+		coma_set |= COMA_QCIF;
+		priv->pclk_max /= 2;
+		break;
+	case W_CIF:
+		dev_dbg(&client->dev, "resolution CIF\n");
+		priv->qcif = 0;
+		coma_mask |= COMA_QCIF;
+		break;
+	default:
+		dev_err(&client->dev, "unspported resolution!\n");
+		return -EINVAL;
+	}
+
+	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
+	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
+	priv->rect.width  = mf->width;
+	priv->rect.height = mf->height;
+
+	if (priv->timeperframe.numerator && priv->timeperframe.denominator)
+		pclk = priv->pclk_max * priv->timeperframe.denominator /
+				(FRAME_RATE_MAX * priv->timeperframe.numerator);
+	else
+		pclk = priv->pclk_max;
+
+	if (sense) {
+		if (sense->master_clock == 8000000) {
+			dev_dbg(&client->dev, "8MHz input clock\n");
+			clkrc = CLKRC_6MHz;
+		} else if (sense->master_clock == 12000000) {
+			dev_dbg(&client->dev, "12MHz input clock\n");
+			clkrc = CLKRC_12MHz;
+		} else if (sense->master_clock == 16000000) {
+			dev_dbg(&client->dev, "16MHz input clock\n");
+			clkrc = CLKRC_16MHz;
+		} else if (sense->master_clock == 24000000) {
+			dev_dbg(&client->dev, "24MHz input clock\n");
+			clkrc = CLKRC_24MHz;
+		} else {
+			dev_err(&client->dev,
+				"unspported input clock, check platform data\n");
+			return -EINVAL;
+		}
+		priv->pclk_limit = sense->pixel_clock_max;
+		if (priv->pclk_limit && (priv->pclk_limit < pclk))
+			pclk = priv->pclk_limit;
+	} else {
+		priv->pclk_limit = 0;
+		clkrc = 0xc0;
+		dev_dbg(&client->dev, "using default 24MHz input clock\n");
+	}
+
+	clkrc_div = (priv->pclk_max - 1) / pclk;
+	clkrc |= clkrc_div;
+	pclk = priv->pclk_max / clkrc_div;
+	dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
+			sense->master_clock / pclk,
+			10 * sense->master_clock % pclk / pclk);
+
+	ov6650_reset(client);
+
+	ret = ov6650_prog_dflt(client);
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
+	if (!ret)
+		ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
+
+	if (!ret) {
+		mf->code	= code;
+		mf->colorspace	= priv->colorspace;
+	}
+
+	return ret;
+}
+
+static int ov6650_try_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *mf)
+{
+	/*
+	 * Select register configuration for given resolution.
+	 * To be replaced with a common function that does it, once available.
+	 */
+	ov6650_res_roundup(&mf->width, &mf->height);
+
+	mf->field = V4L2_FIELD_NONE;
+
+	switch (mf->code) {
+	case V4L2_MBUS_FMT_Y10_1X10:
+		mf->code = V4L2_MBUS_FMT_GREY8_1X8;
+	case V4L2_MBUS_FMT_GREY8_1X8:
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		mf->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	default:
+		mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+	case V4L2_MBUS_FMT_SBGGR8_1X8:
+		mf->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+
+	return 0;
+}
+
+static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	if (index >= ARRAY_SIZE(ov6650_codes))
+		return -EINVAL;
+
+	*code = ov6650_codes[index];
+	return 0;
+}
+
+static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	int shift = !priv->qcif;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* Crop limits depend on selected frame format (CIF/QCIF) */
+	a->bounds.left			= DEF_HSTRT << shift;
+	a->bounds.top			= DEF_VSTRT << shift;
+	a->bounds.width			= W_QCIF << shift;
+	a->bounds.height		= H_QCIF << shift;
+	a->defrect			= a->bounds;
+	a->pixelaspect.numerator	= 1;
+	a->pixelaspect.denominator	= 1;
+
+	return 0;
+}
+
+static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_rect *rect = &a->c;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	rect->left   = priv->rect.left;
+	rect->top    = priv->rect.top;
+	rect->width  = priv->rect.width;
+	rect->height = priv->rect.height;
+
+	return 0;
+}
+
+static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_rect *rect = &a->c;
+	int shift = !priv->qcif;
+	u8 hstrt, vstrt, hstop, vstop;
+	int ret;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	hstrt = rect->left >> shift;
+	vstrt = rect->top >> shift;
+	hstop = hstrt + (rect->width >> shift);
+	vstop = vstrt + (rect->height >> shift);
+
+	if ((hstop > DEF_HSTOP) || (vstop > DEF_VSTOP)) {
+		dev_err(&client->dev, "Invalid window geometry!\n");
+		return -EINVAL;
+	}
+
+	ret = ov6650_reg_write(client, REG_HSTRT, hstrt);
+	if (!ret) {
+		priv->rect.left = rect->left;
+		ret = ov6650_reg_write(client, REG_HSTOP, hstop);
+	}
+	if (!ret) {
+		priv->rect.width = rect->width;
+		ret = ov6650_reg_write(client, REG_VSTRT, vstrt);
+	}
+	if (!ret) {
+		priv->rect.top = rect->top;
+		ret = ov6650_reg_write(client, REG_VSTOP, vstop);
+	}
+	if (!ret)
+		priv->rect.height = rect->height;
+
+	return ret;
+}
+
+static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = sd->priv;
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	u8 clkrc;
+	int ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	ret = ov6650_reg_read(client, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+
+	memset(cp, 0, sizeof(*cp));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = GET_CLKRC_DIV(clkrc);
+	cp->timeperframe.denominator = FRAME_RATE_MAX;
+
+	dev_dbg(&client->dev, "Frame interval: %u/%u\n",
+		cp->timeperframe.numerator, cp->timeperframe.denominator);
+
+	return 0;
+}
+
+static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = sd->priv;
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	int div, ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		div = 1;  /* Reset to full rate */
+	else
+		div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
+
+	if (div == 0)
+		div = 1;
+	else if (div > CLKRC_DIV_MASK + 1)
+		div = CLKRC_DIV_MASK + 1;
+
+	if (priv->pclk_max && priv->pclk_limit) {
+		ret = (priv->pclk_max - 1) / priv->pclk_limit;
+		if (div < ret)
+			div = ret;
+	}
+
+	ret = ov6650_reg_rmw(client, REG_CLKRC, div - 1, CLKRC_DIV_MASK);
+	if (!ret) {
+		tpf->numerator = FRAME_RATE_MAX;
+		tpf->denominator = div;
+		priv->timeperframe = *tpf;
+	}
+
+	return ret;
+}
+
+static int ov6650_video_probe(struct soc_camera_device *icd,
+				struct i2c_client *client)
+{
+	u8		pidh, pidl, midh, midl;
+	int		ret = 0;
+
+	/*
+	 * check and show product ID and manufacturer ID
+	 */
+	ret = ov6650_reg_read(client, REG_PIDH, &pidh);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_PIDL, &pidl);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_MIDH, &midh);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_MIDL, &midl);
+
+	if (ret)
+		return ret;
+
+	if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
+		dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
+				pidh, pidl);
+		return -ENODEV;
+	}
+
+	dev_info(&client->dev,
+		"ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
+		pidh, pidl, midh, midl);
+
+	return 0;
+}
+
+static struct soc_camera_ops ov6650_ops = {
+	.set_bus_param		= ov6650_set_bus_param,
+	.query_bus_param	= ov6650_query_bus_param,
+	.controls		= ov6650_controls,
+	.num_controls		= ARRAY_SIZE(ov6650_controls),
+};
+
+static struct v4l2_subdev_core_ops ov6650_core_ops = {
+	.g_ctrl			= ov6650_g_ctrl,
+	.s_ctrl			= ov6650_s_ctrl,
+	.g_chip_ident		= ov6650_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= ov6650_get_register,
+	.s_register		= ov6650_set_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops ov6650_video_ops = {
+	.s_stream	= ov6650_s_stream,
+	.g_mbus_fmt	= ov6650_g_fmt,
+	.s_mbus_fmt	= ov6650_s_fmt,
+	.try_mbus_fmt	= ov6650_try_fmt,
+	.enum_mbus_fmt	= ov6650_enum_fmt,
+	.cropcap	= ov6650_cropcap,
+	.g_crop		= ov6650_g_crop,
+	.s_crop		= ov6650_s_crop,
+	.g_parm		= ov6650_g_parm,
+	.s_parm		= ov6650_s_parm,
+};
+
+static struct v4l2_subdev_ops ov6650_subdev_ops = {
+	.core	= &ov6650_core_ops,
+	.video	= &ov6650_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov6650_probe(struct i2c_client *client,
+			const struct i2c_device_id *did)
+{
+	struct ov6650 *priv;
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl;
+	int ret;
+
+	if (!icd) {
+		dev_err(&client->dev, "Missing soc-camera data!\n");
+		return -EINVAL;
+	}
+
+	icl = to_soc_camera_link(icd);
+	if (!icl) {
+		dev_err(&client->dev, "Missing platform_data for driver\n");
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&client->dev,
+			"Failed to allocate memory for private data!\n");
+		return -ENOMEM;
+	}
+
+	v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+
+	icd->ops = &ov6650_ops;
+
+	priv->qcif	  = false;
+	priv->rect.left	  = DEF_HSTRT << !false;
+	priv->rect.top	  = DEF_VSTRT << !false;
+	priv->rect.width  = W_CIF;
+	priv->rect.height = H_CIF;
+	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8;
+	priv->colorspace  = V4L2_COLORSPACE_JPEG;
+
+	ret = ov6650_video_probe(icd, client);
+
+	if (ret) {
+		icd->ops = NULL;
+		i2c_set_clientdata(client, NULL);
+		kfree(priv);
+	}
+
+	return ret;
+}
+
+static int ov6650_remove(struct i2c_client *client)
+{
+	struct ov6650 *priv = to_ov6650(client);
+
+	i2c_set_clientdata(client, NULL);
+	kfree(priv);
+	return 0;
+}
+
+static const struct i2c_device_id ov6650_id[] = {
+	{ "ov6650", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov6650_id);
+
+static struct i2c_driver ov6650_i2c_driver = {
+	.driver = {
+		.name = "ov6650",
+	},
+	.probe    = ov6650_probe,
+	.remove   = ov6650_remove,
+	.id_table = ov6650_id,
+};
+
+static int __init ov6650_module_init(void)
+{
+	return i2c_add_driver(&ov6650_i2c_driver);
+}
+
+static void __exit ov6650_module_exit(void)
+{
+	i2c_del_driver(&ov6650_i2c_driver);
+}
+
+module_init(ov6650_module_init);
+module_exit(ov6650_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
diff -upr linux-2.6.36-rc3.orig/include/media/v4l2-chip-ident.h linux-2.6.36-rc3/include/media/v4l2-chip-ident.h
--- linux-2.6.36-rc3.orig/include/media/v4l2-chip-ident.h	2010-09-03 22:30:25.000000000 +0200
+++ linux-2.6.36-rc3/include/media/v4l2-chip-ident.h	2010-09-03 22:34:02.000000000 +0200
@@ -70,6 +70,7 @@ enum {
 	V4L2_IDENT_OV9655 = 255,
 	V4L2_IDENT_SOI968 = 256,
 	V4L2_IDENT_OV9640 = 257,
+	V4L2_IDENT_OV6650 = 258,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,


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

* [PATCH v2 4/6] SoC Camera: add support for g_parm / s_parm operations
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
                   ` (2 preceding siblings ...)
  2010-09-11  1:25 ` [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor Janusz Krzysztofik
@ 2010-09-11  1:26 ` Janusz Krzysztofik
  2010-09-11  1:27 ` [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera Janusz Krzysztofik
  2010-09-11  1:28 ` [PATCH v2 6/6] OMAP1: Amstrad Delta: add camera controlled LEDS trigger Janusz Krzysztofik
  5 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:26 UTC (permalink / raw)
  To: linux-media
  Cc: Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

This patch adds support for g_parm / s_parm operations to the SoC Camera 
framework. It is usefull for checking/setting camera frame rate.

Example usage can be found in the previous patch from this series, 
"SoC Camera: add driver for OV6650 sensor".

Created and tested against linux-2.6.36-rc3 on Amstrad Delta.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
v1->v2 changes:
- no functional changes,
- refreshed against linux-2.6.36-rc3.


 drivers/media/video/soc_camera.c |   18 ++++++++++++++++++
 1 file changed, 18 insertions(+)


diff -upr linux-2.6.36-rc3.orig/drivers/media/video/soc_camera.c linux-2.6.36-rc3/drivers/media/video/soc_camera.c
--- linux-2.6.36-rc3.orig/drivers/media/video/soc_camera.c	2010-09-03 22:29:44.000000000 +0200
+++ linux-2.6.36-rc3/drivers/media/video/soc_camera.c	2010-09-03 22:34:03.000000000 +0200
@@ -1148,6 +1148,20 @@ static int default_s_crop(struct soc_cam
 	return v4l2_subdev_call(sd, video, s_crop, a);
 }
 
+static int default_g_parm(struct soc_camera_device *icd,
+			  struct v4l2_streamparm *parm)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	return v4l2_subdev_call(sd, video, g_parm, parm);
+}
+
+static int default_s_parm(struct soc_camera_device *icd,
+			  struct v4l2_streamparm *parm)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	return v4l2_subdev_call(sd, video, s_parm, parm);
+}
+
 static void soc_camera_device_init(struct device *dev, void *pdata)
 {
 	dev->platform_data	= pdata;
@@ -1179,6 +1193,10 @@ int soc_camera_host_register(struct soc_
 		ici->ops->get_crop = default_g_crop;
 	if (!ici->ops->cropcap)
 		ici->ops->cropcap = default_cropcap;
+	if (!ici->ops->set_parm)
+		ici->ops->set_parm = default_s_parm;
+	if (!ici->ops->get_parm)
+		ici->ops->get_parm = default_g_parm;
 
 	mutex_lock(&list_lock);
 	list_for_each_entry(ix, &hosts, list) {

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

* [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
                   ` (3 preceding siblings ...)
  2010-09-11  1:26 ` [PATCH v2 4/6] SoC Camera: add support for g_parm / s_parm operations Janusz Krzysztofik
@ 2010-09-11  1:27 ` Janusz Krzysztofik
  2010-09-23 23:14   ` Tony Lindgren
  2010-09-11  1:28 ` [PATCH v2 6/6] OMAP1: Amstrad Delta: add camera controlled LEDS trigger Janusz Krzysztofik
  5 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:27 UTC (permalink / raw)
  To: linux-media
  Cc: Guennadi Liakhovetski, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

This patch adds configuration data and initialization code required for camera 
support to the Amstrad Delta board.

Three devices are declared: SoC camera, OMAP1 camera interface and OV6650 
sensor.

Default 12MHz clock has been selected for driving the sensor. Pixel clock has 
been limited to get reasonable frame rates, not exceeding the board 
capabilities. Since both devices (interface and sensor) support both pixel 
clock polarities, decision on polarity selection has been left to drivers.
Interface GPIO line has been found not functional, thus not configured.

Created and tested against linux-2.6.36-rc3.

Works on top of previous patches from the series, at least 1/6, 2/6 and 3/6.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
v1->v2 changes:
- no functional changes,
- refreshed against linux-2.6.36-rc3


 arch/arm/mach-omap1/board-ams-delta.c |   45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)


diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/board-ams-delta.c 
linux-2.6.36-rc3/arch/arm/mach-omap1/board-ams-delta.c
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/board-ams-delta.c	2010-09-03 22:29:00.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/board-ams-delta.c	2010-09-10 22:01:24.000000000 +0200
@@ -19,6 +19,8 @@
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 
+#include <media/soc_camera.h>
+
 #include <asm/serial.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -32,6 +34,7 @@
 #include <plat/usb.h>
 #include <plat/board.h>
 #include <plat/common.h>
+#include <mach/camera.h>
 
 #include <mach/ams-delta-fiq.h>
 
@@ -213,10 +216,37 @@ static struct platform_device ams_delta_
 	.id	= -1
 };
 
+static struct i2c_board_info ams_delta_camera_board_info[] = {
+	{
+		I2C_BOARD_INFO("ov6650", 0x60),
+	},
+};
+
+static struct soc_camera_link __initdata ams_delta_iclink = {
+	.bus_id         = 0,	/* OMAP1 SoC camera bus */
+	.i2c_adapter_id = 1,
+	.board_info     = &ams_delta_camera_board_info[0],
+	.module_name    = "ov6650",
+};
+
+static struct platform_device ams_delta_camera_device = {
+	.name   = "soc-camera-pdrv",
+	.id     = 0,
+	.dev    = {
+		.platform_data = &ams_delta_iclink,
+	},
+};
+
+static struct omap1_cam_platform_data ams_delta_camera_platform_data = {
+	.camexclk_khz	= 12000,	/* default 12MHz clock, no extra DPLL */
+	.lclk_khz_max	= 1334,		/* results in 5fps CIF, 10fps QCIF */
+};
+
 static struct platform_device *ams_delta_devices[] __initdata = {
 	&ams_delta_kp_device,
 	&ams_delta_lcd_device,
 	&ams_delta_led_device,
+	&ams_delta_camera_device,
 };
 
 static void __init ams_delta_init(void)
@@ -225,6 +255,20 @@ static void __init ams_delta_init(void)
 	omap_cfg_reg(UART1_TX);
 	omap_cfg_reg(UART1_RTS);
 
+	/* parallel camera interface */
+	omap_cfg_reg(H19_1610_CAM_EXCLK);
+	omap_cfg_reg(J15_1610_CAM_LCLK);
+	omap_cfg_reg(L18_1610_CAM_VS);
+	omap_cfg_reg(L15_1610_CAM_HS);
+	omap_cfg_reg(L19_1610_CAM_D0);
+	omap_cfg_reg(K14_1610_CAM_D1);
+	omap_cfg_reg(K15_1610_CAM_D2);
+	omap_cfg_reg(K19_1610_CAM_D3);
+	omap_cfg_reg(K18_1610_CAM_D4);
+	omap_cfg_reg(J14_1610_CAM_D5);
+	omap_cfg_reg(J19_1610_CAM_D6);
+	omap_cfg_reg(J18_1610_CAM_D7);
+
 	iotable_init(ams_delta_io_desc, ARRAY_SIZE(ams_delta_io_desc));
 
 	omap_board_config = ams_delta_config;
@@ -236,6 +280,7 @@ static void __init ams_delta_init(void)
 	ams_delta_latch2_write(~0, 0);
 
 	omap1_usb_init(&ams_delta_usb_config);
+	omap1_set_camera_info(&ams_delta_camera_platform_data);
 	platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices));
 
 #ifdef CONFIG_AMS_DELTA_FIQ

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

* [PATCH v2 6/6] OMAP1: Amstrad Delta: add camera controlled LEDS trigger
  2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
                   ` (4 preceding siblings ...)
  2010-09-11  1:27 ` [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera Janusz Krzysztofik
@ 2010-09-11  1:28 ` Janusz Krzysztofik
  5 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:28 UTC (permalink / raw)
  To: linux-media, linux-omap
  Cc: Guennadi Liakhovetski, Tony Lindgren, Richard Purdie,
	Discussion of the Amstrad E3 emailer hardware/software

This patch extends the Amstrad Delta camera support with LEDS trigger that can 
be used for automatic control of the on-board camera LED. The led turns on 
automatically on camera device open and turns off on camera device close.

Created and tested against linux-2.6.36-rc3.

Works on top of patch 5/6, "OMAP1: Amstrad Delta: add support for on-board 
camera"

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
Having no comments received on v1 of this patch, I assume nobody could see any 
benefit if I implemented this idea at the SoC camera or OMAP1 camera level, 
so I'm leaveing it as an Amstrad Delta only feature, as it originally was for 
v1.

Thanks,
Janusz


v1->v2 changes:
- no functional changes,
- refreshed against linux-2.6.36-rc3.


 arch/arm/mach-omap1/board-ams-delta.c |   24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)


diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/board-ams-delta.c 
linux-2.6.36-rc3/arch/arm/mach-omap1/board-ams-delta.c
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/board-ams-delta.c	2010-09-10 22:01:24.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/board-ams-delta.c	2010-09-10 22:08:29.000000000 +0200
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
+#include <linux/leds.h>
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 
@@ -222,11 +223,30 @@ static struct i2c_board_info ams_delta_c
 	},
 };
 
+#ifdef CONFIG_LEDS_TRIGGERS
+DEFINE_LED_TRIGGER(ams_delta_camera_led_trigger);
+
+static int ams_delta_camera_power(struct device *dev, int power)
+{
+	/*
+	 * turn on camera LED
+	 */
+	if (power)
+		led_trigger_event(ams_delta_camera_led_trigger, LED_FULL);
+	else
+		led_trigger_event(ams_delta_camera_led_trigger, LED_OFF);
+	return 0;
+}
+#else
+#define ams_delta_camera_power	NULL
+#endif
+
 static struct soc_camera_link __initdata ams_delta_iclink = {
 	.bus_id         = 0,	/* OMAP1 SoC camera bus */
 	.i2c_adapter_id = 1,
 	.board_info     = &ams_delta_camera_board_info[0],
 	.module_name    = "ov6650",
+	.power		= ams_delta_camera_power,
 };
 
 static struct platform_device ams_delta_camera_device = {
@@ -281,6 +301,10 @@ static void __init ams_delta_init(void)
 
 	omap1_usb_init(&ams_delta_usb_config);
 	omap1_set_camera_info(&ams_delta_camera_platform_data);
+#ifdef CONFIG_LEDS_TRIGGERS
+	led_trigger_register_simple("ams_delta_camera",
+			&ams_delta_camera_led_trigger);
+#endif
 	platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices));
 
 #ifdef CONFIG_AMS_DELTA_FIQ

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

* [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-11  1:23 ` [PATCH v2 2/6] OMAP1: Add support for SoC " Janusz Krzysztofik
@ 2010-09-11  1:34   ` Janusz Krzysztofik
  2010-09-23 23:23     ` Tony Lindgren
  2010-09-22  6:53   ` [PATCH " Guennadi Liakhovetski
  1 sibling, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  1:34 UTC (permalink / raw)
  To: linux-omap
  Cc: linux-media, Guennadi Liakhovetski, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

This patch adds support for SoC camera interface to OMAP1 devices.

Created and tested against linux-2.6.36-rc3 on Amstrad Delta.

For successfull compilation, requires a header file provided by PATCH 1/6 from 
this series, "SoC Camera: add driver for OMAP1 camera interface".

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
---
Resend because of wrapped lines, sorry.
Janusz


v1->v2 changes:
- no functional changes,
- refreshed against linux-2.6.36-rc3


 arch/arm/mach-omap1/devices.c             |   43 ++++++++++++++++++++++++++++++
 arch/arm/mach-omap1/include/mach/camera.h |    8 +++++
 2 files changed, 51 insertions(+)


diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c	2010-09-03 22:29:00.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c	2010-09-09 18:42:30.000000000 +0200
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/spi/spi.h>
+#include <linux/dma-mapping.h>
 
 #include <mach/hardware.h>
 #include <asm/mach/map.h>
@@ -25,6 +26,7 @@
 #include <mach/gpio.h>
 #include <plat/mmc.h>
 #include <plat/omap7xx.h>
+#include <mach/camera.h>
 
 /*-------------------------------------------------------------------------*/
 
@@ -191,6 +193,47 @@ static inline void omap_init_spi100k(voi
 }
 #endif
 
+
+#define OMAP1_CAMERA_BASE	0xfffb6800
+
+static struct resource omap1_camera_resources[] = {
+	[0] = {
+		.start	= OMAP1_CAMERA_BASE,
+		.end	= OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= INT_CAMERA,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device omap1_camera_device = {
+	.name		= "omap1-camera",
+	.id		= 0, /* This is used to put cameras on this interface */
+	.dev		= {
+		.dma_mask		= &omap1_camera_dma_mask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(omap1_camera_resources),
+	.resource	= omap1_camera_resources,
+};
+
+void __init omap1_set_camera_info(struct omap1_cam_platform_data *info)
+{
+	struct platform_device *dev = &omap1_camera_device;
+	int ret;
+
+	dev->dev.platform_data = info;
+
+	ret = platform_device_register(dev);
+	if (ret)
+		dev_err(&dev->dev, "unable to register device: %d\n", ret);
+}
+
+
 /*-------------------------------------------------------------------------*/
 
 static inline void omap_init_sti(void) {}
diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h 
linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
--- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-03 22:34:03.000000000 +0200
+++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09 18:42:30.000000000 +0200
@@ -0,0 +1,8 @@
+#ifndef __ASM_ARCH_CAMERA_H_
+#define __ASM_ARCH_CAMERA_H_
+
+#include <media/omap1_camera.h>
+
+extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
+
+#endif /* __ASM_ARCH_CAMERA_H_ */

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
@ 2010-09-11  9:32   ` Janusz Krzysztofik
  2010-09-21 23:23   ` Guennadi Liakhovetski
  1 sibling, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-11  9:32 UTC (permalink / raw)
  To: Jasmine Strong
  Cc: linux-media, Guennadi Liakhovetski, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Saturday 11 September 2010 04:06:02 Jasmine Strong wrote:
> On 10 Sep 2010, at 18:21, Janusz Krzysztofik wrote:
> >  Both paths work stable for me, even
> > under heavy load, on my OMAP1510 based Amstrad Delta videophone, that is
> > the oldest, least powerfull OMAP1 implementation.
>
> You say that, but the ARM925 in OMAP1510 is known not to exhibit the bug 

Then, lucky me ;)

> in  
> OMAP1610, which causes severe slowdown to DRAM writes when the first
> address of an STM instruction is not aligned to a d-cache line boundary. 
> This means that at least last time I looked, the Linux ARM memcpy()
> implementation is often faster on 1510 than 1610.

This sounds like a problem that should be solved at a machine support level 
rather than a camera driver. I don't follow general OMAP development closely 
enough to know if this bug has ever been addressed or is going to be.

Unfortunatelly, I have no access to any OMAP machine other than Amstrad Delta, 
so I'm not able to test the driver, including its performance, on other OMAP1 
implementations.

Anyways, I think there is always a room for improvement, either in my 
omap1_camera or maybe in the omap24xxcam driver, if someone tries to add 
support for a camera to an OMAP1 board other than 1510, and identifies a more 
optimal, 1610 or higher specific way of handling the OMAP camera interface.

Do you think I should rename the driver to something like "omap1510cam" rather 
than "omap1_camera" then?

Thanks,
Janusz

>
> -J.
> _______________________________________________
> e3-hacking mailing list
> e3-hacking@earth.li
> http://www.earth.li/cgi-bin/mailman/listinfo/e3-hacking


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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
  2010-09-11  9:32   ` Janusz Krzysztofik
@ 2010-09-21 23:23   ` Guennadi Liakhovetski
  2010-09-22  0:10     ` hermann pitton
  2010-09-22 18:13       ` Janusz Krzysztofik
  1 sibling, 2 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-21 23:23 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:

> This is a V4L2 driver for TI OMAP1 SoC camera interface.
> 
> Both videobuf-dma versions are supported, contig and sg, selectable with a 
> module option. The former uses less processing power, but often fails to 
> allocate contignuous buffer memory. The latter is free of this problem, but 
> generates tens of DMA interrupts per frame. If contig memory allocation ever 
> fails, the driver falls back to sg automatically on next open, but still can 
> be switched back to contig manually. Both paths work stable for me, even 
> under heavy load, on my OMAP1510 based Amstrad Delta videophone, that is the 
> oldest, least powerfull OMAP1 implementation.
> 
> The interface generally works in pass-through mode. Since input data byte 
> endianess can be swapped, it provides up to two v4l2 pixel formats per each of 
> several soc_mbus formats that have their swapped endian counterparts.
> 
> Boards using this driver can provide it with the followning information:
> - if and what freqency clock is expected by an on-board camera sensor,
> - what is the maximum pixel clock that should be accepted from the sensor,
> - what is the polarity of the sensor provided pixel clock,
> - if the interface GPIO line is connected to a sensor reset/powerdown input 
>   and what is the input polarity.
> 
> Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> 
> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> ---
> Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> > So, I think, a very welcome improvement to the driver would be a 
> > cleaner separation between the two cases. Don't try that much to reuse the
> > code as much as possible. Would be much better to have clean separation
> > between the two implementations - whether dynamically switchable at
> > runtime or at config time - would be best to separate the two
> > implementations to the point, where each of them becomes understandable
> > and maintainable.
> 
> Guennadi,
> I've tried to rearrange them spearated, as you requested, but finally decided 
> to keep them together, as before, only better documented and cleaned up as 
> much as possible. I'm rather satisfied with the result, but if you think it 
> is still not enough understandable and maintainable, I'll take one more 
> iteration and split both paths.

Well, I think, I'll move a bit towards the "if it breaks - someone gets to 
fix it, or it gets dropped" policy, i.e., I'll give you more freedom 
(don't know what's wrong with me today;))

> 
> Besides, there are a few my not yet answered questions or suggestions (see 
> http://www.spinics.net/lists/linux-media/msg21615.html for original message):
> 
> Friday 30 July 2010 20:49:05 Janusz Krzysztofik wrote:
> > Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> > > On Sun, 18 Jul 2010, Janusz Krzysztofik wrote:
> > > > This is a V4L2 driver for TI OMAP1 SoC camera interface.
> 
> ...
> > > > +
> > > > +	u32			reg_cache[OMAP1_CAMERA_IOSIZE / sizeof(u32)];
> > >
> > > Don't think you'd lose much performance without cache, for that the code
> > > would become less error-prone.
> >
> > The reason is my bad experience with my hardware audio interface. For my
> > rationale regarding OMAP McBSP register caching, please look at this
> > commit:
> > http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=96fbd74551e9cae8fd5e9cb62a0a19336a3064fa 
> > or the foregoing discussions (links both to my initial and final submitions):
> > http://thread.gmane.org/gmane.linux.alsa.devel/65675
> > http://thread.gmane.org/gmane.linux.ports.arm.omap/29708
> >
> > However, I haven't noticed any similiar flaws in camera interface yet, so
> > if you think that caching would not be helpful in any possible future
> > (context save/restore) enhancements here, then I can drop it.
> ...
> > > Don't you have to update the cache on a direct read?
> >
> > If I do it unconditionally, I'd loose the read error prevention function of
> > register caching idea.
> 
> I've kept register caching for now, only replaced a few not intentional reads 
> from hardware with more sensible reads from cache. Please confirm if you 
> prefere me dropping register caching anyways.
> 
> ...
> > > > +static void videobuf_done(struct omap1_cam_dev *pcdev,
> > > > +		enum videobuf_state result)
> > > > +{
> > > > +	...
> > > > +	if (waitqueue_active(&vb->done)) {
> > >
> > > I'm not sure this is a good idea. Why are you reusing the buffer, if
> > > noone is waiting for it _now_? It can well be, that the task is doing
> > > something else atm. Like processing the previous frame. Or it has been
> > > preempted by another process, before it called poll() or select() on your
> > > device?
> >
> > I've introduced this way of videobuf handling after learning, form the
> > Documentation/video4linux/videobuf, what the first step of filling a buffer
> > should be:
> >
> > "- Obtain the next available buffer and make sure that somebody is actually
> >    waiting for it."
> >
> > Since I had no idea how I could implement it, I decided to do it a bit
> > differently: start filling a buffer unconditionally, then drop its contents
> > if not awaited by any user when done
> > 
> > > > ...
> > > > +	} else if (pcdev->ready) {
> > > > +		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
> > > > +				__func__);
> > >
> > > Not sure this is a wise decision... If there are more buffers in the
> > > queue, they stay there waiting. Best remove this waitqueue_active() check
> > > entirely, just complete buffers and wake up the user regardless. If no
> > > more buffers - go to sleep.
> >
> > If you think I don't have to follow the above mentioned requirement for not
> > servicing a buffer unless someone is waiting for it, then yes, I'll drop
> > these overcomplicated bits. Please confirm.
> 
> One more reference to Documentation/video4linux/videobuf:
> 
> "It is equally possible that nobody is yet interested in the
> buffer; the driver should not remove it from the list or fill it until a
> process is waiting on it."
> 
> Please review my now better commented code again, and confirm if you still 
> expect me to rearrange it, ignoring the documentation provided requirements.
> 
> ...
> > > > +static int omap1_cam_set_crop(struct soc_camera_device *icd,
> > > > +			       struct v4l2_crop *crop)
> > > > +{
> > > > ...
> > > > +	icd->user_width	 = rect->width;
> > > > +	icd->user_height = rect->height;
> > >
> > > No, this is wrong. user_width and user_height are _user_ sizes, i.e., a
> > > result sensor cropping and scaling and host cropping and scaling. Whereas
> > > set_crop sets sensor input rectangle.
> >
> > I'm not sure if I understand what you mean. Should I drop these
> > assignments? Or correct them? If yes, how they should be calculated?
> >
> > In Documentation/video4linux/soc-camera.txt, you say:
> > "The core updates these fields upon successful completion of a .s_fmt()
> > call, but if these fields change elsewhere, e.g., during .s_crop()
> > processing, the host driver is responsible for updating them."
> >
> > I thought this is the case we're dealing with here.
> 
> Please confirm if I my understanding of what the documentation says is wrong 
> and I should really correct the above.
> 
> Thanks,
> Janusz
> 
> 
> v1->v2 changes:
> 
> requested by Guennadi Liakhovetski (thanks!):
> - first try contig, and if it fails - fall back to sg; invalidates next two,
> - invalidated: Kconfig: VIDEO_OMAP1_SG: not need "if", the "depends on" should 
>   suffice,
> - invalidated: include both <media/videobuf-dma-contig.h> and 
>   <media/videobuf-dma-sg.h> headers,
> - extensively document buffer manipulations, better yet clean it up,
> - a copyright / licence header was missing form a header file, 
> - need to #include <linux/bitops.h> if using BIT() macro,
> - don't need macros representing frequencies - use numbers directly,
> - add a few missing "static" qualifiers,
> - use u32 type for register content handling,
> - some cached registers were unnecessarily read from the hardware directly,
> - use true/false constants instead of 0/1 for booleans,
> - avoid assigning variables inside other constructs,
> - don't need to test if RAZ_FIFO is cleared,
> - no need to split "\n" to a new line, don't worry about > 80 characters,
> - don't increment field_count in case of a VIDEOBUF_ERROR,
> - adjust mbus format codes to the new names,
> - make is_dma_aligned() return value a bool,
> - no need to align frame line lenghts to integer number of DMA elements,
> - drop a few superflous whitespace, braces, etc.,
> - use correct multiline comment style,
> - follow some good-style rules when defining a multi-line function-like macro,
> - drop tests for impossible bool < 0,
> - that's not an error when S_FMT sets a format different from what the user
>   has requested, just use whatever you managed to configure,
> - as a general rule, try_fmt shouldn't fail,
> - use sensor_reset() instead of duplicating its code,
> - first shut down the hardware, and only then unconfigure software,
> 
> suggested by Ralph Corderoy (thanks!):
> - include vb->state's value in the debug messages instead of just "unknown",
> - correct a few print formats,
> - don't use goto if return is all that needs to be done,
> 
> other:
> - correct a few issues reported with "checkpatch.pl --strict".
> - refreshed against linux-2.6.36-rc3
> 
> 
>  drivers/media/video/Kconfig        |    8
>  drivers/media/video/Makefile       |    1
>  drivers/media/video/omap1_camera.c | 1781 +++++++++++++++++++++++++++++++++++++
>  include/media/omap1_camera.h       |   35
>  4 files changed, 1825 insertions(+)
> 
> 
> diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Kconfig linux-2.6.36-rc3/drivers/media/video/Kconfig
> --- linux-2.6.36-rc3.orig/drivers/media/video/Kconfig	2010-09-03 22:29:37.000000000 +0200
> +++ linux-2.6.36-rc3/drivers/media/video/Kconfig	2010-09-09 17:55:52.000000000 +0200
> @@ -890,6 +890,14 @@ config VIDEO_SH_MOBILE_CEU
>  	---help---
>  	  This is a v4l2 driver for the SuperH Mobile CEU Interface
>  
> +config VIDEO_OMAP1
> +	tristate "OMAP1 Camera Interface driver"
> +	depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
> +	select VIDEOBUF_DMA_CONTIG
> +	select VIDEOBUF_DMA_SG
> +	---help---
> +	  This is a v4l2 driver for the TI OMAP1 camera interface
> +
>  config VIDEO_OMAP2
>  	tristate "OMAP2 Camera Capture Interface driver"
>  	depends on VIDEO_DEV && ARCH_OMAP2
> diff -upr linux-2.6.36-rc3.orig/drivers/media/video/Makefile linux-2.6.36-rc3/drivers/media/video/Makefile
> --- linux-2.6.36-rc3.orig/drivers/media/video/Makefile	2010-09-03 22:29:37.000000000 +0200
> +++ linux-2.6.36-rc3/drivers/media/video/Makefile	2010-09-09 17:55:52.000000000 +0200
> @@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3)			+= mx3_camera.
>  obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
> +obj-$(CONFIG_VIDEO_OMAP1)		+= omap1_camera.o
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
>  
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
> diff -upr linux-2.6.36-rc3.orig/drivers/media/video/omap1_camera.c linux-2.6.36-rc3/drivers/media/video/omap1_camera.c
> --- linux-2.6.36-rc3.orig/drivers/media/video/omap1_camera.c	2010-09-03 22:34:02.000000000 +0200
> +++ linux-2.6.36-rc3/drivers/media/video/omap1_camera.c	2010-09-09 15:58:22.000000000 +0200
> @@ -0,0 +1,1781 @@
> +/*
> + * V4L2 SoC Camera driver for OMAP1 Camera Interface
> + *
> + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> + *
> + * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
> + * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
> + * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
> + *
> + * Based on PXA SoC camera driver
> + * Copyright (C) 2006, Sascha Hauer, Pengutronix
> + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
> + *
> + * Hardware specific bits initialy based on former work by Matt Callow
> + * drivers/media/video/omap/omap1510cam.c
> + * Copyright (C) 2006 Matt Callow
> + *
> + * 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.
> + */
> +
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +
> +#include <media/omap1_camera.h>
> +#include <media/soc_camera.h>
> +#include <media/soc_mediabus.h>
> +#include <media/videobuf-dma-contig.h>
> +#include <media/videobuf-dma-sg.h>
> +
> +#include <plat/dma.h>
> +
> +
> +#define DRIVER_NAME		"omap1-camera"
> +#define VERSION_CODE		KERNEL_VERSION(0, 0, 1)
> +
> +
> +/*
> + * ---------------------------------------------------------------------------
> + *  OMAP1 Camera Interface registers
> + * ---------------------------------------------------------------------------
> + */
> +
> +#define REG_CTRLCLOCK		0x00
> +#define REG_IT_STATUS		0x04
> +#define REG_MODE		0x08
> +#define REG_STATUS		0x0C
> +#define REG_CAMDATA		0x10
> +#define REG_GPIO		0x14
> +#define REG_PEAK_COUNTER	0x18
> +
> +/* CTRLCLOCK bit shifts */
> +#define LCLK_EN			BIT(7)
> +#define DPLL_EN			BIT(6)
> +#define MCLK_EN			BIT(5)
> +#define CAMEXCLK_EN		BIT(4)
> +#define POLCLK			BIT(3)
> +#define FOSCMOD_SHIFT		0
> +#define FOSCMOD_MASK		(0x7 << FOSCMOD_SHIFT)
> +#define FOSCMOD_12MHz		0x0
> +#define FOSCMOD_6MHz		0x2
> +#define FOSCMOD_9_6MHz		0x4
> +#define FOSCMOD_24MHz		0x5
> +#define FOSCMOD_8MHz		0x6
> +
> +/* IT_STATUS bit shifts */
> +#define DATA_TRANSFER		BIT(5)
> +#define FIFO_FULL		BIT(4)
> +#define H_DOWN			BIT(3)
> +#define H_UP			BIT(2)
> +#define V_DOWN			BIT(1)
> +#define V_UP			BIT(0)
> +
> +/* MODE bit shifts */
> +#define RAZ_FIFO		BIT(18)
> +#define EN_FIFO_FULL		BIT(17)
> +#define EN_NIRQ			BIT(16)
> +#define THRESHOLD_SHIFT		9
> +#define THRESHOLD_MASK		(0x7f << THRESHOLD_SHIFT)
> +#define DMA			BIT(8)
> +#define EN_H_DOWN		BIT(7)
> +#define EN_H_UP			BIT(6)
> +#define EN_V_DOWN		BIT(5)
> +#define EN_V_UP			BIT(4)
> +#define ORDERCAMD		BIT(3)
> +
> +#define IRQ_MASK		(EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
> +				 EN_NIRQ | EN_FIFO_FULL)
> +
> +/* STATUS bit shifts */
> +#define HSTATUS			BIT(1)
> +#define VSTATUS			BIT(0)
> +
> +/* GPIO bit shifts */
> +#define CAM_RST			BIT(0)
> +
> +/* end of OMAP1 Camera Interface registers */
> +
> +
> +#define SOCAM_BUS_FLAGS	(SOCAM_MASTER | \
> +			SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
> +			SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
> +			SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
> +
> +
> +#define FIFO_SIZE		((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
> +#define FIFO_SHIFT		__fls(FIFO_SIZE)
> +
> +#define DMA_BURST_SHIFT		(1 + OMAP_DMA_DATA_BURST_4)
> +#define DMA_BURST_SIZE		(1 << DMA_BURST_SHIFT)
> +
> +#define DMA_ELEMENT_SHIFT	OMAP_DMA_DATA_TYPE_S32
> +#define DMA_ELEMENT_SIZE	(1 << DMA_ELEMENT_SHIFT)
> +
> +#define DMA_FRAME_SHIFT_CONTIG	(FIFO_SHIFT - 1)
> +#define DMA_FRAME_SHIFT_SG	DMA_BURST_SHIFT
> +
> +#define DMA_FRAME_SHIFT(x)	(x ? DMA_FRAME_SHIFT_SG : \
> +						DMA_FRAME_SHIFT_CONTIG)

Don't you want to compare (x) against CONTIG and you want to put x in 
parenthesis in the DMA_FRAME_SHIFT macro. Besides, CONTIG and SG are not 
good enough names to be defined in a header under include/... Looks like 
you don't need them at all in the header? You only use them in this file, 
so, just move them inside.

> +#define DMA_FRAME_SIZE(x)	(1 << DMA_FRAME_SHIFT(x))
> +#define DMA_SYNC		OMAP_DMA_SYNC_FRAME
> +#define THRESHOLD_LEVEL		DMA_FRAME_SIZE
> +
> +
> +#define MAX_VIDEO_MEM		4	/* arbitrary video memory limit in MB */
> +
> +
> +/*
> + * Structures
> + */
> +
> +/* buffer for one video frame */
> +struct omap1_cam_buf {
> +	struct videobuf_buffer		vb;
> +	enum v4l2_mbus_pixelcode	code;
> +	int				inwork;
> +	struct scatterlist		*sgbuf;
> +	int				sgcount;
> +	int				bytes_left;
> +	enum videobuf_state		result;
> +};
> +
> +struct omap1_cam_dev {
> +	struct soc_camera_host		soc_host;
> +	struct soc_camera_device	*icd;
> +	struct clk			*clk;
> +
> +	unsigned int			irq;
> +	void __iomem			*base;
> +
> +	int				dma_ch;
> +
> +	struct omap1_cam_platform_data	*pdata;
> +	struct resource			*res;
> +	unsigned long			pflags;
> +	unsigned long			camexclk;
> +
> +	struct list_head		capture;
> +
> +	/* lock used to protect videobuf */
> +	spinlock_t			lock;
> +
> +	u32				reg_cache[OMAP1_CAMERA_IOSIZE /
> +							sizeof(u32)];
> +
> +	/* Pointers to DMA buffers */
> +	struct omap1_cam_buf		*active;
> +	struct omap1_cam_buf		*ready;
> +
> +	enum omap1_cam_vb_mode		vb_mode;
> +	int				(*mmap_mapper)(struct videobuf_queue *q,
> +						struct videobuf_buffer *buf,
> +						struct vm_area_struct *vma);
> +};
> +
> +
> +static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
> +{
> +	pcdev->reg_cache[reg / sizeof(u32)] = val;
> +	__raw_writel(val, pcdev->base + reg);
> +}
> +
> +static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
> +{
> +	return !from_cache ? __raw_readl(pcdev->base + reg) :
> +			pcdev->reg_cache[reg / sizeof(u32)];
> +}
> +
> +#define CAM_READ(pcdev, reg) \
> +		cam_read(pcdev, REG_##reg, false)
> +#define CAM_WRITE(pcdev, reg, val) \
> +		cam_write(pcdev, REG_##reg, val)
> +#define CAM_READ_CACHE(pcdev, reg) \
> +		cam_read(pcdev, REG_##reg, true)
> +
> +/*
> + *  Videobuf operations
> + */
> +static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
> +		unsigned int *size)
> +{
> +	struct soc_camera_device *icd = vq->priv_data;
> +	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
> +			icd->current_fmt->host_fmt);
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +
> +	if (bytes_per_line < 0)
> +		return bytes_per_line;
> +
> +	*size = bytes_per_line * icd->user_height;
> +
> +	if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
> +		*count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
> +
> +	if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
> +		*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
> +
> +	dev_dbg(icd->dev.parent,
> +			"%s: count=%d, size=%d\n", __func__, *count, *size);
> +
> +	return 0;
> +}
> +
> +static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
> +		enum omap1_cam_vb_mode vb_mode)
> +{
> +	struct videobuf_buffer *vb = &buf->vb;
> +
> +	BUG_ON(in_interrupt());
> +
> +	videobuf_waiton(vb, 0, 0);
> +
> +	if (vb_mode == CONTIG) {
> +		videobuf_dma_contig_free(vq, vb);
> +	} else {
> +		struct soc_camera_device *icd = vq->priv_data;
> +		struct device *dev = icd->dev.parent;
> +		struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
> +
> +		videobuf_dma_unmap(dev, dma);
> +		videobuf_dma_free(dma);
> +	}
> +
> +	vb->state = VIDEOBUF_NEEDS_INIT;
> +}
> +
> +static int omap1_videobuf_prepare(struct videobuf_queue *vq,
> +		struct videobuf_buffer *vb, enum v4l2_field field)
> +{
> +	struct soc_camera_device *icd = vq->priv_data;
> +	struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
> +	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
> +			icd->current_fmt->host_fmt);
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	int ret;
> +
> +	if (bytes_per_line < 0)
> +		return bytes_per_line;
> +
> +	WARN_ON(!list_empty(&vb->queue));
> +
> +	BUG_ON(NULL == icd->current_fmt);
> +
> +	buf->inwork = 1;
> +
> +	if (buf->code != icd->current_fmt->code || vb->field != field ||
> +			vb->width  != icd->user_width ||
> +			vb->height != icd->user_height) {
> +		buf->code  = icd->current_fmt->code;
> +		vb->width  = icd->user_width;
> +		vb->height = icd->user_height;
> +		vb->field  = field;
> +		vb->state  = VIDEOBUF_NEEDS_INIT;
> +	}
> +
> +	vb->size = bytes_per_line * vb->height;
> +
> +	if (vb->baddr && vb->bsize < vb->size) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (vb->state == VIDEOBUF_NEEDS_INIT) {
> +		ret = videobuf_iolock(vq, vb, NULL);
> +		if (ret)
> +			goto fail;
> +
> +		vb->state = VIDEOBUF_PREPARED;
> +	}
> +	buf->inwork = 0;
> +
> +	return 0;
> +fail:
> +	free_buffer(vq, buf, pcdev->vb_mode);
> +out:
> +	buf->inwork = 0;
> +	return ret;
> +}
> +
> +static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
> +		enum omap1_cam_vb_mode vb_mode)
> +{
> +	dma_addr_t dma_addr;
> +	unsigned int block_size;
> +
> +	if (vb_mode == CONTIG) {
> +		dma_addr = videobuf_to_dma_contig(&buf->vb);
> +		block_size = buf->vb.size;
> +	} else {
> +		if (WARN_ON(!buf->sgbuf)) {
> +			buf->result = VIDEOBUF_ERROR;
> +			return;
> +		}
> +		dma_addr = sg_dma_address(buf->sgbuf);
> +		if (WARN_ON(!dma_addr)) {
> +			buf->sgbuf = NULL;
> +			buf->result = VIDEOBUF_ERROR;
> +			return;
> +		}
> +		block_size = sg_dma_len(buf->sgbuf);
> +		if (WARN_ON(!block_size)) {
> +			buf->sgbuf = NULL;
> +			buf->result = VIDEOBUF_ERROR;
> +			return;
> +		}
> +		if (unlikely(buf->bytes_left < block_size))
> +			block_size = buf->bytes_left;
> +		if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
> +				DMA_ELEMENT_SIZE - 1))) {
> +			dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
> +					DMA_ELEMENT_SIZE);
> +			block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
> +					DMA_ELEMENT_SIZE - 1);
> +		}
> +		buf->bytes_left -= block_size;
> +		buf->sgcount++;
> +	}
> +
> +	omap_set_dma_dest_params(dma_ch,
> +		OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
> +	omap_set_dma_transfer_params(dma_ch,
> +		OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
> +		block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
> +		DMA_SYNC, 0, 0);
> +}
> +
> +static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
> +{
> +	struct omap1_cam_buf *buf;
> +
> +	/*
> +	 * If there is already a buffer pointed out by the pcdev->ready,
> +	 * (re)use it, otherwise try to fetch and configure a new one.
> +	 */
> +	buf = pcdev->ready;
> +	if (!buf) {
> +		if (list_empty(&pcdev->capture))
> +			return buf;
> +		buf = list_entry(pcdev->capture.next,
> +				struct omap1_cam_buf, vb.queue);
> +		buf->vb.state = VIDEOBUF_ACTIVE;
> +		pcdev->ready = buf;
> +		list_del_init(&buf->vb.queue);
> +	}
> +
> +	if (pcdev->vb_mode == CONTIG) {
> +		/*
> +		 * In CONTIG mode, we can safely enter next buffer parameters
> +		 * into the DMA programming register set after the DMA
> +		 * has already been activated on the previous buffer
> +		 */
> +		set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
> +	} else {
> +		/*
> +		 * In SG mode, the above is not safe since there are probably
> +		 * a bunch of sgbufs from previous sglist still pending.
> +		 * Instead, mark the sglist fresh for the upcoming
> +		 * try_next_sgbuf().
> +		 */
> +		buf->sgbuf = NULL;
> +	}
> +
> +	return buf;
> +}
> +
> +static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
> +{
> +	struct scatterlist *sgbuf;
> +
> +	if (likely(buf->sgbuf)) {
> +		/* current sglist is active */
> +		if (unlikely(!buf->bytes_left)) {
> +			/* indicate sglist complete */
> +			sgbuf = NULL;
> +		} else {
> +			/* process next sgbuf */
> +			sgbuf = sg_next(buf->sgbuf);
> +			if (WARN_ON(!sgbuf)) {
> +				buf->result = VIDEOBUF_ERROR;
> +			} else if (WARN_ON(!sg_dma_len(sgbuf))) {
> +				sgbuf = NULL;
> +				buf->result = VIDEOBUF_ERROR;
> +			}
> +		}
> +		buf->sgbuf = sgbuf;
> +	} else {
> +		/* sglist is fresh, initialize it before using */
> +		struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
> +
> +		sgbuf = dma->sglist;
> +		if (!(WARN_ON(!sgbuf))) {
> +			buf->sgbuf = sgbuf;
> +			buf->sgcount = 0;
> +			buf->bytes_left = buf->vb.size;
> +			buf->result = VIDEOBUF_DONE;
> +		}
> +	}
> +	if (sgbuf)
> +		/*
> +		 * Put our next sgbuf parameters (address, size)
> +		 * into the DMA programming register set.
> +		 */
> +		set_dma_dest_params(dma_ch, buf, SG);
> +
> +	return sgbuf;
> +}
> +
> +static void start_capture(struct omap1_cam_dev *pcdev)
> +{
> +	struct omap1_cam_buf *buf = pcdev->active;
> +	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> +	u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
> +
> +	if (WARN_ON(!buf))
> +		return;
> +
> +	/*
> +	 * Enable start of frame interrupt, which we will use for activating
> +	 * our end of frame watchdog when capture actually starts.
> +	 */
> +	mode |= EN_V_UP;
> +
> +	if (unlikely(ctrlclock & LCLK_EN))
> +		/* stop pixel clock before FIFO reset */
> +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> +	/* reset FIFO */
> +	CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
> +
> +	omap_start_dma(pcdev->dma_ch);
> +
> +	if (pcdev->vb_mode == SG) {
> +		/*
> +		 * In SG mode, it's a good moment for fetching next sgbuf
> +		 * from the current sglist and, if available, already putting
> +		 * its parameters into the DMA programming register set.
> +		 */
> +		try_next_sgbuf(pcdev->dma_ch, buf);
> +	}
> +
> +	/* (re)enable pixel clock */
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
> +	/* release FIFO reset */
> +	CAM_WRITE(pcdev, MODE, mode);
> +}
> +
> +static void suspend_capture(struct omap1_cam_dev *pcdev)
> +{
> +	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> +
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> +	omap_stop_dma(pcdev->dma_ch);
> +}
> +
> +static void disable_capture(struct omap1_cam_dev *pcdev)
> +{
> +	u32 mode = CAM_READ_CACHE(pcdev, MODE);
> +
> +	CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
> +}
> +
> +static void omap1_videobuf_queue(struct videobuf_queue *vq,
> +						struct videobuf_buffer *vb)
> +{
> +	struct soc_camera_device *icd = vq->priv_data;
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	struct omap1_cam_buf *buf;
> +	u32 mode;
> +
> +	list_add_tail(&vb->queue, &pcdev->capture);
> +	vb->state = VIDEOBUF_QUEUED;
> +
> +	if (pcdev->active)
> +		return;
> +
> +	WARN_ON(pcdev->ready);
> +
> +	buf = prepare_next_vb(pcdev);
> +	if (WARN_ON(!buf))
> +		return;
> +
> +	pcdev->active = buf;
> +	pcdev->ready = NULL;
> +
> +	dev_dbg(icd->dev.parent,
> +		"%s: capture not active, setup FIFO, start DMA\n", __func__);
> +	mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
> +	mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
> +	CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
> +
> +	if (pcdev->vb_mode == SG) {
> +		/*
> +		 * In SG mode, the above prepare_next_vb() didn't actually
> +		 * put anything into the DMA programming register set,
> +		 * so we have to do it now, before activating DMA.
> +		 */
> +		try_next_sgbuf(pcdev->dma_ch, buf);
> +	}
> +
> +	start_capture(pcdev);
> +}
> +
> +static void omap1_videobuf_release(struct videobuf_queue *vq,
> +				 struct videobuf_buffer *vb)
> +{
> +	struct omap1_cam_buf *buf =
> +			container_of(vb, struct omap1_cam_buf, vb);
> +	struct soc_camera_device *icd = vq->priv_data;
> +	struct device *dev = icd->dev.parent;
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +
> +	switch (vb->state) {
> +	case VIDEOBUF_DONE:
> +		dev_dbg(dev, "%s (done)\n", __func__);
> +		break;
> +	case VIDEOBUF_ACTIVE:
> +		dev_dbg(dev, "%s (active)\n", __func__);
> +		break;
> +	case VIDEOBUF_QUEUED:
> +		dev_dbg(dev, "%s (queued)\n", __func__);
> +		break;
> +	case VIDEOBUF_PREPARED:
> +		dev_dbg(dev, "%s (prepared)\n", __func__);
> +		break;
> +	default:
> +		dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
> +		break;
> +	}
> +
> +	free_buffer(vq, buf, pcdev->vb_mode);
> +}
> +
> +static void videobuf_done(struct omap1_cam_dev *pcdev,
> +		enum videobuf_state result)
> +{
> +	struct omap1_cam_buf *buf = pcdev->active;
> +	struct videobuf_buffer *vb;
> +	struct device *dev = pcdev->icd->dev.parent;
> +
> +	if (WARN_ON(!buf)) {
> +		suspend_capture(pcdev);
> +		disable_capture(pcdev);
> +		return;
> +	}
> +
> +	if (result == VIDEOBUF_ERROR)
> +		suspend_capture(pcdev);
> +
> +	vb = &buf->vb;
> +	if (waitqueue_active(&vb->done)) {
> +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> +			/*
> +			 * No next buffer has been entered into the DMA
> +			 * programming register set on time, so best we can do
> +			 * is stopping the capture before last DMA block,
> +			 * whether our CONTIG mode whole buffer or its last
> +			 * sgbuf in SG mode, gets overwritten by next frame.
> +			 */

Hm, why do you think it's a good idea? This specific buffer completed 
successfully, but you want to fail it just because the next buffer is 
missing? Any specific reason for this? Besides, you seem to also be 
considering the possibility of your ->ready == NULL, but the queue 
non-empty, in which case you just take the next buffer from the queue and 
continue with it. Why error out in this case? And even if also the queue 
is empty - still not sure, why.

> +			suspend_capture(pcdev);
> +		vb->state = result;
> +		do_gettimeofday(&vb->ts);
> +		if (result != VIDEOBUF_ERROR)
> +			vb->field_count++;
> +		wake_up(&vb->done);
> +
> +		/* shift in next buffer */
> +		buf = pcdev->ready;
> +		pcdev->active = buf;
> +		pcdev->ready = NULL;
> +
> +		if (!buf) {
> +			/*
> +			 * No next buffer was ready on time (see above), so
> +			 * indicate error condition to force capture restart or
> +			 * stop, depending on next buffer already queued or not.
> +			 */
> +			result = VIDEOBUF_ERROR;
> +			prepare_next_vb(pcdev);
> +
> +			buf = pcdev->ready;
> +			pcdev->active = buf;
> +			pcdev->ready = NULL;
> +		}
> +	} else if (pcdev->ready) {
> +		/*
> +		 * In both CONTIG and SG mode, the DMA engine has (might)

s/might/possibly/

> +		 * already been autoreinitialized with the preprogrammed
> +		 * pcdev->ready buffer.  We can either accept this fact
> +		 * and just swap the buffers, or provoke an error condition
> +		 * and restart capture.  The former seems less intrusive.
> +		 */
> +		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
> +				__func__);
> +		pcdev->active = pcdev->ready;
> +
> +		if (pcdev->vb_mode == SG) {
> +			/*
> +			 * In SG mode, we have to make sure that the buffer we
> +			 * are putting back into the pcdev->ready is marked
> +			 * fresh.
> +			 */
> +			buf->sgbuf = NULL;
> +		}
> +		pcdev->ready = buf;
> +
> +		buf = pcdev->active;
> +	} else {
> +		/*
> +		 * No next buffer has been entered into
> +		 * the DMA programming register set on time.
> +		 */
> +		if (pcdev->vb_mode == CONTIG) {
> +			/*
> +			 * In CONTIG mode, the DMA engine has already been
> +			 * reinitialized with the current buffer. Best we can do
> +			 * is not touching it.
> +			 */
> +			dev_dbg(dev,
> +				"%s: nobody waiting on videobuf, reuse it\n",
> +				__func__);
> +		} else {
> +			/*
> +			 * In SG mode, the DMA engine has just been
> +			 * autoreinitialized with the last sgbuf from the
> +			 * current list. Restart capture in order to transfer
> +			 * next frame start into the first sgbuf, not the last
> +			 * one.
> +			 */
> +			if (result != VIDEOBUF_ERROR) {
> +				suspend_capture(pcdev);
> +				result = VIDEOBUF_ERROR;
> +			}
> +		}
> +	}
> +
> +	if (!buf) {
> +		dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
> +		disable_capture(pcdev);
> +		return;
> +	}
> +
> +	if (pcdev->vb_mode == CONTIG) {
> +		/*
> +		 * In CONTIG mode, the current buffer parameters had already
> +		 * been entered into the DMA programming register set while the
> +		 * buffer was fetched with prepare_next_vb(), they may have also
> +		 * been transfered into the runtime set and already active if
> +		 * the DMA still running.
> +		 */
> +	} else {
> +		/* In SG mode, extra steps are required */
> +		if (result == VIDEOBUF_ERROR)
> +			/* make sure we (re)use sglist from start on error */
> +			buf->sgbuf = NULL;
> +
> +		/*
> +		 * In any case, enter the next sgbuf parameters into the DMA
> +		 * programming register set.  They will be used either during
> +		 * nearest DMA autoreinitialization or, in case of an error,
> +		 * on DMA startup below.
> +		 */
> +		try_next_sgbuf(pcdev->dma_ch, buf);
> +	}
> +
> +	if (result == VIDEOBUF_ERROR) {
> +		dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
> +				__func__);
> +		start_capture(pcdev);
> +		/*
> +		 * In SG mode, the above also resulted in the next sgbuf
> +		 * parameters being entered into the DMA programming register
> +		 * set, making them ready for next DMA autoreinitialization.
> +		 */
> +	}
> +
> +	/*
> +	 * Finally, try fetching next buffer.  In CONTIG mode, it will also
> +	 * get entered it into the DMA programming register set,

s/get entered/enter/

> +	 * making it ready for next DMA autoreinitialization.
> +	 */
> +	prepare_next_vb(pcdev);
> +}
> +
> +static void dma_isr(int channel, unsigned short status, void *data)
> +{
> +	struct omap1_cam_dev *pcdev = data;
> +	struct omap1_cam_buf *buf = pcdev->active;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&pcdev->lock, flags);
> +
> +	if (WARN_ON(!buf)) {
> +		suspend_capture(pcdev);
> +		disable_capture(pcdev);
> +		goto out;
> +	}
> +
> +	if (pcdev->vb_mode == CONTIG) {
> +		/*
> +		 * In CONTIG mode, assume we have just managed to collect the
> +		 * whole frame, hopefully before our end of frame watchdog is
> +		 * triggered. Then, all we have to do is disabling the watchdog
> +		 * for this frame, and calling videobuf_done() with success
> +		 * indicated.
> +		 */
> +		CAM_WRITE(pcdev, MODE,
> +				CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
> +		videobuf_done(pcdev, VIDEOBUF_DONE);
> +	} else {
> +		/*
> +		 * In SG mode, we have to process every sgbuf from the current
> +		 * sglist, one after another.
> +		 */
> +		if (buf->sgbuf) {
> +			/*
> +			 * Current sglist not completed yet, try fetching next
> +			 * sgbuf, hopefully putting it into the DMA programming
> +			 * register set, making it ready for next DMA
> +			 * autoreinitialization.
> +			 */
> +			try_next_sgbuf(pcdev->dma_ch, buf);
> +			if (buf->sgbuf)
> +				goto out;
> +
> +			/* no more sgbufs in the current sglist */
> +			if (buf->result != VIDEOBUF_ERROR) {
> +				/*
> +				 * Video frame collected without errors, we can
> +				 * prepare for collecting a next one as soon as
> +				 * DMA gets autoreinitialized after the currennt
> +				 * (last) sgbuf is completed.
> +				 */
> +				buf = prepare_next_vb(pcdev);
> +				if (!buf)
> +					goto out;
> +
> +				try_next_sgbuf(pcdev->dma_ch, buf);
> +				goto out;

Uh, sorry, maybe I cannot quite follow the logic here, but it doesn't seem 
quite correct to me. Looks like you do not complete the current 
(successfully completed) buffer, if you failed to prepare the next one?

> +			}
> +		}
> +		/* end of videobuf */
> +		videobuf_done(pcdev, buf->result);
> +	}
> +
> +out:
> +	spin_unlock_irqrestore(&pcdev->lock, flags);
> +}
> +
> +static irqreturn_t cam_isr(int irq, void *data)
> +{
> +	struct omap1_cam_dev *pcdev = data;
> +	struct device *dev = pcdev->icd->dev.parent;
> +	struct omap1_cam_buf *buf = pcdev->active;
> +	u32 it_status;
> +	unsigned long flags;
> +
> +	it_status = CAM_READ(pcdev, IT_STATUS);
> +	if (!it_status)
> +		return IRQ_NONE;
> +
> +	spin_lock_irqsave(&pcdev->lock, flags);
> +
> +	if (WARN_ON(!buf)) {
> +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> +				"0x%0x\n", __func__, it_status);
> +		suspend_capture(pcdev);
> +		disable_capture(pcdev);
> +		goto out;
> +	}
> +
> +	if (unlikely(it_status & FIFO_FULL)) {
> +		dev_warn(dev, "%s: FIFO overflow\n", __func__);
> +
> +	} else if (it_status & V_DOWN) {
> +		/* end of video frame watchdog */
> +		if (pcdev->vb_mode == CONTIG) {
> +			/*
> +			 * In CONTIG mode, the watchdog is disabled with
> +			 * successful DMA end of block interrupt, and reenabled
> +			 * on next frame start. If we get here, there is nothing
> +			 * to check, we must be out of sync.
> +			 */
> +		} else {
> +			if (buf->sgcount == 2) {
> +				/*
> +				 * If exactly 2 sgbufs from the next sglist has

s/has/have/

> +				 * been programmed into the DMA engine (the
> +				 * frist one already transfered into the DMA
> +				 * runtime register set, the second one still
> +				 * in the programming set), then we are in sync.
> +				 */
> +				goto out;
> +			}
> +		}
> +		dev_notice(dev, "%s: unexpected end of video frame\n",
> +				__func__);
> +
> +	} else if (it_status & V_UP) {
> +		u32 mode;
> +
> +		if (pcdev->vb_mode == CONTIG) {
> +			/*
> +			 * In CONTIG mode, we need this interrupt every frame
> +			 * in oredr to reenable our end of frame watchdog.
> +			 */
> +			mode = CAM_READ_CACHE(pcdev, MODE);
> +		} else {
> +			/*
> +			 * In SG mode, the below enabled end of frame watchdog
> +			 * is kept on permanently, so we can turn this one shot
> +			 * setup off.
> +			 */
> +			mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
> +		}
> +
> +		if (!(mode & EN_V_DOWN)) {
> +			/* (re)enable end of frame watchdog interrupt */
> +			mode |= EN_V_DOWN;
> +		}
> +		CAM_WRITE(pcdev, MODE, mode);
> +		goto out;
> +
> +	} else {
> +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> +				"0x%0x\n", __func__, it_status);

Please, don't split strings

> +		goto out;
> +	}
> +
> +	videobuf_done(pcdev, VIDEOBUF_ERROR);
> +out:
> +	spin_unlock_irqrestore(&pcdev->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static struct videobuf_queue_ops omap1_videobuf_ops = {
> +	.buf_setup	= omap1_videobuf_setup,
> +	.buf_prepare	= omap1_videobuf_prepare,
> +	.buf_queue	= omap1_videobuf_queue,
> +	.buf_release	= omap1_videobuf_release,
> +};
> +
> +
> +/*
> + * SOC Camera host operations
> + */
> +
> +static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
> +{
> +	/* apply/release camera sensor reset if requested by platform data */
> +	if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
> +		CAM_WRITE(pcdev, GPIO, reset);
> +	else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
> +		CAM_WRITE(pcdev, GPIO, !reset);
> +}
> +
> +/*
> + * The following two functions absolutely depend on the fact, that
> + * there can be only one camera on OMAP1 camera sensor interface
> + */
> +static int omap1_cam_add_device(struct soc_camera_device *icd)
> +{
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	u32 ctrlclock;
> +
> +	if (pcdev->icd)
> +		return -EBUSY;
> +
> +	clk_enable(pcdev->clk);
> +
> +	/* setup sensor clock */
> +	ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
> +	ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> +
> +	ctrlclock &= ~FOSCMOD_MASK;
> +	switch (pcdev->camexclk) {
> +	case 6000000:
> +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
> +		break;
> +	case 8000000:
> +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
> +		break;
> +	case 9600000:
> +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
> +		break;
> +	case 12000000:
> +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
> +		break;
> +	case 24000000:
> +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
> +	default:

This default doesn't make much sense here, maybe put it to one of these 
values, that you think is a reasonable fall back. Ah, right, you wanted to 
check, whether this can work...

> +		break;
> +	}
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
> +
> +	/* enable internal clock */
> +	ctrlclock |= MCLK_EN;
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> +
> +	sensor_reset(pcdev, false);
> +
> +	pcdev->icd = icd;
> +
> +	dev_info(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n",
> +			icd->devnum);
> +	return 0;
> +}
> +
> +static void omap1_cam_remove_device(struct soc_camera_device *icd)
> +{
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	u32 ctrlclock;
> +
> +	BUG_ON(icd != pcdev->icd);
> +
> +	suspend_capture(pcdev);
> +	disable_capture(pcdev);
> +
> +	sensor_reset(pcdev, true);
> +
> +	/* disable and release system clocks */
> +	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> +	ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> +
> +	ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
> +
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
> +
> +	clk_disable(pcdev->clk);
> +
> +	pcdev->icd = NULL;
> +
> +	dev_info(icd->dev.parent,
> +		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
> +}
> +
> +/* Duplicate standard formats based on host capability of byte swapping */
> +static const struct soc_mbus_pixelfmt omap1_cam_formats[] = {
> +	[V4L2_MBUS_FMT_UYVY8_2X8] = {
> +		.fourcc			= V4L2_PIX_FMT_YUYV,
> +		.name			= "YUYV",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_VYUY8_2X8] = {
> +		.fourcc			= V4L2_PIX_FMT_YVYU,
> +		.name			= "YVYU",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_YUYV8_2X8] = {
> +		.fourcc			= V4L2_PIX_FMT_UYVY,
> +		.name			= "UYVY",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_YVYU8_2X8] = {
> +		.fourcc			= V4L2_PIX_FMT_VYUY,
> +		.name			= "VYUY",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE] = {
> +		.fourcc			= V4L2_PIX_FMT_RGB555,
> +		.name			= "RGB555",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE] = {
> +		.fourcc			= V4L2_PIX_FMT_RGB555X,
> +		.name			= "RGB555X",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_RGB565_2X8_BE] = {
> +		.fourcc			= V4L2_PIX_FMT_RGB565,
> +		.name			= "RGB565",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +	[V4L2_MBUS_FMT_RGB565_2X8_LE] = {
> +		.fourcc			= V4L2_PIX_FMT_RGB565X,
> +		.name			= "RGB565X",
> +		.bits_per_sample	= 8,
> +		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
> +		.order			= SOC_MBUS_ORDER_BE,
> +	},
> +};
> +
> +static int omap1_cam_get_formats(struct soc_camera_device *icd,
> +		unsigned int idx, struct soc_camera_format_xlate *xlate)
> +{
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	struct device *dev = icd->dev.parent;
> +	int formats = 0, ret;
> +	enum v4l2_mbus_pixelcode code;
> +	const struct soc_mbus_pixelfmt *fmt;
> +
> +	ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
> +	if (ret < 0)
> +		/* No more formats */
> +		return 0;
> +
> +	fmt = soc_mbus_get_fmtdesc(code);
> +	if (!fmt) {
> +		dev_err(dev, "%s: invalid format code #%d: %d\n", __func__,
> +				idx, code);
> +		return 0;
> +	}
> +
> +	/* Check support for the requested bits-per-sample */
> +	if (fmt->bits_per_sample != 8)
> +		return 0;
> +
> +	switch (code) {
> +	case V4L2_MBUS_FMT_YUYV8_2X8:
> +	case V4L2_MBUS_FMT_YVYU8_2X8:
> +	case V4L2_MBUS_FMT_UYVY8_2X8:
> +	case V4L2_MBUS_FMT_VYUY8_2X8:
> +	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
> +	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
> +	case V4L2_MBUS_FMT_RGB565_2X8_BE:
> +	case V4L2_MBUS_FMT_RGB565_2X8_LE:
> +		formats++;
> +		if (xlate) {
> +			xlate->host_fmt	= &omap1_cam_formats[code];
> +			xlate->code	= code;
> +			xlate++;
> +			dev_dbg(dev, "%s: providing format %s "
> +					"as byte swapped code #%d\n", __func__,
> +					omap1_cam_formats[code].name, code);
> +		}
> +	default:
> +		if (xlate)
> +			dev_dbg(dev, "%s: providing format %s "
> +					"in pass-through mode\n", __func__,
> +					fmt->name);
> +	}
> +	formats++;
> +	if (xlate) {
> +		xlate->host_fmt	= fmt;
> +		xlate->code	= code;
> +		xlate++;
> +	}
> +
> +	return formats;
> +}
> +
> +static bool is_dma_aligned(s32 bytes_per_line, unsigned int height)
> +{
> +	int size = bytes_per_line * height;
> +
> +	return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
> +			IS_ALIGNED(size, DMA_FRAME_SIZE(CONTIG) *
> +			DMA_ELEMENT_SIZE);
> +}
> +
> +static int dma_align(int *width, int *height,
> +		const struct soc_mbus_pixelfmt *fmt, bool enlarge)
> +{
> +	s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
> +
> +	if (bytes_per_line < 0)
> +		return bytes_per_line;
> +
> +	if (!is_dma_aligned(bytes_per_line, *height)) {
> +		unsigned int pxalign = __fls(bytes_per_line / *width);
> +		unsigned int salign  = DMA_FRAME_SHIFT(CONTIG) +
> +				DMA_ELEMENT_SHIFT - pxalign;
> +		unsigned int incr    = enlarge << salign;
> +
> +		v4l_bound_align_image(width, 1, *width + incr, 0,
> +				height, 1, *height + incr, 0, salign);
> +		return 0;
> +	}
> +	return 1;
> +}
> +
> +/* returns 1 on g_crop() success, 0 on cropcap() success, <0 on error */
> +static int get_crop(struct soc_camera_device *icd, struct v4l2_rect *rect)
> +{
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	struct device *dev = icd->dev.parent;
> +	struct v4l2_crop crop;
> +	int ret;
> +
> +	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	ret = v4l2_subdev_call(sd, video, g_crop, &crop);
> +	if (ret == -ENOIOCTLCMD) {
> +		struct v4l2_cropcap cc;
> +
> +		dev_dbg(dev, "%s: g_crop() missing, trying cropcap() instead\n",
> +				__func__);
> +		cc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		ret = v4l2_subdev_call(sd, video, cropcap, &cc);
> +		if (ret < 0)
> +			return ret;
> +		*rect = cc.defrect;
> +		return 0;
> +	} else if (ret < 0) {
> +		return ret;
> +	}
> +	*rect = crop.c;
> +	return 1;
> +}
> +
> +/*
> + * returns 1 on g_mbus_fmt() or g_crop() success, 0 on cropcap() success,
> + * <0 on error
> + */
> +static int get_geometry(struct soc_camera_device *icd, struct v4l2_rect *rect,
> +		enum v4l2_mbus_pixelcode code)
> +{
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	struct device *dev = icd->dev.parent;
> +	struct v4l2_mbus_framefmt mf;
> +	int ret;
> +
> +	ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
> +	if (ret == -ENOIOCTLCMD) {
> +		struct v4l2_rect c;
> +
> +		dev_dbg(dev,
> +			"%s: g_mbus_fmt() missing, trying g_crop() instead\n",
> +			__func__);
> +		ret = get_crop(icd, &c);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret) {
> +			*rect = c;	/* use g_crop() result */
> +		} else {
> +			dev_warn(dev, "%s: current geometry not available\n",
> +					__func__);
> +			return 0;
> +		}
> +	} else if (ret < 0) {
> +		return ret;
> +	} else if (mf.code != code) {
> +		return -EINVAL;
> +	} else {
> +		rect->width  = mf.width;
> +		rect->height = mf.height;
> +	}
> +	return 1;
> +}
> +
> +#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...)	   \
> +({									   \
> +	struct soc_camera_sense sense = {				   \
> +		.master_clock		= pcdev->camexclk,		   \
> +		.pixel_clock_max	= 0,				   \
> +	};								   \
> +	int __ret;							   \
> +									   \
> +	if (pcdev->pdata)						   \
> +		sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
> +	icd->sense = &sense;						   \
> +	__ret = v4l2_subdev_call(sd, video, function, ##args);		   \
> +	icd->sense = NULL;						   \
> +									   \
> +	if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {			   \
> +		if (sense.pixel_clock > sense.pixel_clock_max) {	   \
> +			dev_err(dev, "%s: pixel clock %lu "		   \
> +					"set by the camera too high!\n",   \
> +					__func__, sense.pixel_clock);	   \
> +			__ret = -EINVAL;				   \
> +		}							   \
> +	}								   \
> +	__ret;								   \
> +})
> +
> +static int omap1_cam_set_crop(struct soc_camera_device *icd,
> +			       struct v4l2_crop *crop)
> +{
> +	struct v4l2_rect *rect = &crop->c;
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	struct device *dev = icd->dev.parent;
> +	s32 bytes_per_line;
> +	int ret;
> +
> +	ret = dma_align(&rect->width, &rect->height, icd->current_fmt->host_fmt,
> +			false);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
> +				__func__, rect->width, rect->height,
> +				icd->current_fmt->host_fmt->name);
> +		return ret;
> +	}
> +
> +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);

Missing "ret = "?

> +	if (ret < 0) {
> +		dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> +			 rect->width, rect->height, rect->left, rect->top);
> +		return ret;
> +	}
> +
> +	ret = get_geometry(icd, rect, icd->current_fmt->code);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: get_geometry() failed\n", __func__);
> +		return ret;
> +	}
> +	if (!ret)
> +		dev_warn(dev, "%s: unable to verify s_crop() results\n",
> +				__func__);
> +
> +	bytes_per_line = soc_mbus_bytes_per_line(rect->width,
> +			icd->current_fmt->host_fmt);
> +	if (bytes_per_line < 0) {
> +		dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
> +				__func__);
> +		return bytes_per_line;
> +	}
> +
> +	ret = is_dma_aligned(bytes_per_line, rect->height);
> +	if (!ret) {
> +		dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
> +				__func__, rect->width, rect->height);
> +		return -EINVAL;
> +	}
> +
> +	icd->user_width	 = rect->width;
> +	icd->user_height = rect->height;
> +
> +	return ret;
> +}
> +
> +static int omap1_cam_set_fmt(struct soc_camera_device *icd,
> +			      struct v4l2_format *f)
> +{
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	const struct soc_camera_format_xlate *xlate;
> +	struct device *dev = icd->dev.parent;
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	struct v4l2_mbus_framefmt mf;
> +	struct v4l2_crop crop;
> +	struct v4l2_rect *rect = &crop.c;
> +	int bytes_per_line;
> +	int ret;
> +
> +	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
> +	if (!xlate) {
> +		dev_warn(dev, "%s: format %#x not found\n", __func__,
> +				pix->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	mf.width	= pix->width;
> +	mf.height	= pix->height;
> +	mf.field	= pix->field;
> +	mf.colorspace	= pix->colorspace;
> +	mf.code		= xlate->code;
> +
> +	ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, &mf);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: failed to set format\n", __func__);
> +		return ret;
> +	}
> +
> +	if (mf.code != xlate->code) {
> +		dev_err(dev, "%s: unexpected pixel code change\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	pix->width		= mf.width;
> +	pix->height		= mf.height;
> +	pix->field		= mf.field;
> +	pix->colorspace		= mf.colorspace;
> +	icd->current_fmt	= xlate;
> +
> +	ret = dma_align(&pix->width, &pix->height, xlate->host_fmt, false);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
> +				__func__, pix->width, pix->height,
> +				xlate->host_fmt->name);
> +		return ret;
> +	}
> +
> +	if (ret == 1) {
> +		/* sensor returned geometry was already DMA aligned */
> +		return 0;
> +	}
> +
> +	dev_notice(dev, "%s: sensor geometry not DMA aligned, trying to crop to"
> +			" %ux%u\n", __func__, pix->width, pix->height);

One line

> +	ret = get_crop(icd, rect);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: get_crop() failed\n", __func__);
> +		return ret;
> +	}
> +
> +	rect->width  = pix->width;
> +	rect->height = pix->height;
> +
> +	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, &crop);

'ret = '?

> +	if (ret < 0) {
> +		dev_err(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> +			 rect->width, rect->height, rect->left, rect->top);
> +		return ret;
> +	}
> +
> +	ret = get_geometry(icd, rect, xlate->code);
> +	if (ret < 0) {
> +		dev_err(dev, "%s: get_geometry() failed\n", __func__);
> +		return ret;
> +	}
> +
> +	if (!ret)
> +		dev_warn(dev, "%s: s_crop() results not confirmed\n", __func__);
> +
> +	bytes_per_line = soc_mbus_bytes_per_line(rect->width, xlate->host_fmt);
> +	if (bytes_per_line < 0) {
> +		dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
> +				__func__);
> +		return bytes_per_line;
> +	}
> +
> +	ret = is_dma_aligned(bytes_per_line, rect->height);
> +	if (!ret) {
> +		dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
> +				__func__, rect->width, rect->height);
> +		return -EINVAL;
> +	}
> +
> +	pix->width  = rect->width;
> +	pix->height = rect->height;
> +
> +	return 0;
> +}
> +
> +static int omap1_cam_try_fmt(struct soc_camera_device *icd,
> +			      struct v4l2_format *f)
> +{
> +	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
> +	const struct soc_camera_format_xlate *xlate;
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	struct v4l2_mbus_framefmt mf;
> +	int ret;
> +	/* TODO: limit to mx1 hardware capabilities */
> +
> +	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
> +	if (!xlate) {
> +		dev_warn(icd->dev.parent, "Format %#x not found\n",
> +			 pix->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	mf.width	= pix->width;
> +	mf.height	= pix->height;
> +	mf.field	= pix->field;
> +	mf.colorspace	= pix->colorspace;
> +	mf.code		= xlate->code;
> +
> +	/* limit to sensor capabilities */
> +	ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
> +	if (ret < 0)
> +		return ret;
> +
> +	pix->width	= mf.width;
> +	pix->height	= mf.height;
> +	pix->field	= mf.field;
> +	pix->colorspace	= mf.colorspace;
> +
> +	return 0;
> +}
> +
> +static bool sg_mode;
> +
> +/*
> + * Local mmap_mapper wrapper,
> + * used for detecting videobuf-dma-contig buffer allocation failures
> + * and switching to videobuf-dma-sg automatically for future attempts.
> + */
> +static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
> +				  struct videobuf_buffer *buf,
> +				  struct vm_area_struct *vma)
> +{
> +	struct soc_camera_device *icd = q->priv_data;
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	int ret;
> +
> +	ret = pcdev->mmap_mapper(q, buf, vma);
> +
> +	if (ret == -ENOMEM)
> +		sg_mode = 1;

"true"

> +
> +	return ret;
> +}
> +
> +static void omap1_cam_init_videobuf(struct videobuf_queue *q,
> +				     struct soc_camera_device *icd)
> +{
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +
> +	if (!sg_mode)
> +		videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
> +				icd->dev.parent, &pcdev->lock,
> +				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
> +				sizeof(struct omap1_cam_buf), icd);
> +	else
> +		videobuf_queue_sg_init(q, &omap1_videobuf_ops,
> +				icd->dev.parent, &pcdev->lock,
> +				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
> +				sizeof(struct omap1_cam_buf), icd);
> +
> +	/* use videobuf mode (auto)selected with the module parameter */
> +	pcdev->vb_mode = sg_mode ? SG : CONTIG;
> +
> +	/*
> +	 * Ensure we substitute the videobuf-dma-contig version of the
> +	 * mmap_mapper() callback with our own wrapper, used for switching
> +	 * automatically to videobuf-dma-sg on buffer allocation failure.
> +	 */
> +	if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
> +		pcdev->mmap_mapper = q->int_ops->mmap_mapper;
> +		q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
> +	}
> +}
> +
> +static int omap1_cam_reqbufs(struct soc_camera_file *icf,
> +			      struct v4l2_requestbuffers *p)
> +{
> +	int i;
> +
> +	/*
> +	 * This is for locking debugging only. I removed spinlocks and now I
> +	 * check whether .prepare is ever called on a linked buffer, or whether
> +	 * a dma IRQ can occur for an in-work or unlinked buffer. Until now
> +	 * it hadn't triggered
> +	 */
> +	for (i = 0; i < p->count; i++) {
> +		struct omap1_cam_buf *buf = container_of(icf->vb_vidq.bufs[i],
> +						      struct omap1_cam_buf, vb);
> +		buf->inwork = 0;
> +		INIT_LIST_HEAD(&buf->vb.queue);
> +	}
> +
> +	return 0;
> +}
> +
> +static int omap1_cam_querycap(struct soc_camera_host *ici,
> +			       struct v4l2_capability *cap)
> +{
> +	/* cap->name is set by the friendly caller:-> */
> +	strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
> +	cap->version = VERSION_CODE;
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +
> +	return 0;
> +}
> +
> +static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
> +		__u32 pixfmt)
> +{
> +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> +	struct omap1_cam_dev *pcdev = ici->priv;
> +	struct device *dev = icd->dev.parent;
> +	const struct soc_camera_format_xlate *xlate;
> +	const struct soc_mbus_pixelfmt *fmt;
> +	unsigned long camera_flags, common_flags;
> +	u32 ctrlclock, mode;
> +	int ret;
> +
> +	camera_flags = icd->ops->query_bus_param(icd);
> +
> +	common_flags = soc_camera_bus_param_compatible(camera_flags,
> +			SOCAM_BUS_FLAGS);
> +	if (!common_flags)
> +		return -EINVAL;
> +
> +	/* Make choices, possibly based on platform configuration */
> +	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
> +			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
> +		if (!pcdev->pdata ||
> +				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)

Out of curiosity - do you need to force a specific clock polarity on your 
platform or do you know of any, where this is necessary (beyond the 
natural restriction, coming from the sensor - if any). I.e., I'm not at 
all sure you need this flag, but if you do - I would apply it earlier - to 
mask the respective bit from SOCAM_BUS_FLAGS.

> +			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
> +		else
> +			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
> +	}
> +
> +	ret = icd->ops->set_bus_param(icd, common_flags);
> +	if (ret < 0)
> +		return ret;
> +
> +	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> +	if (ctrlclock & LCLK_EN)
> +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> +
> +	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
> +		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
> +		ctrlclock |= POLCLK;
> +	} else {
> +		dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
> +		ctrlclock &= ~POLCLK;
> +	}
> +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> +
> +	if (ctrlclock & LCLK_EN)
> +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> +
> +	/* select bus endianess */
> +	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
> +	fmt = xlate->host_fmt;
> +
> +	mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
> +	if (fmt->order == SOC_MBUS_ORDER_LE) {
> +		dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
> +		CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
> +	} else {
> +		dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
> +		CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
> +	}
> +
> +	return 0;
> +}
> +
> +static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
> +{
> +	struct soc_camera_file *icf = file->private_data;
> +	struct omap1_cam_buf *buf;
> +
> +	buf = list_entry(icf->vb_vidq.stream.next, struct omap1_cam_buf,
> +			 vb.stream);
> +
> +	poll_wait(file, &buf->vb.done, pt);
> +
> +	if (buf->vb.state == VIDEOBUF_DONE ||
> +	    buf->vb.state == VIDEOBUF_ERROR)
> +		return POLLIN | POLLRDNORM;
> +
> +	return 0;
> +}
> +
> +static struct soc_camera_host_ops omap1_host_ops = {
> +	.owner		= THIS_MODULE,
> +	.add		= omap1_cam_add_device,
> +	.remove		= omap1_cam_remove_device,
> +	.get_formats	= omap1_cam_get_formats,
> +	.set_crop	= omap1_cam_set_crop,
> +	.set_fmt	= omap1_cam_set_fmt,
> +	.try_fmt	= omap1_cam_try_fmt,
> +	.init_videobuf	= omap1_cam_init_videobuf,
> +	.reqbufs	= omap1_cam_reqbufs,
> +	.querycap	= omap1_cam_querycap,
> +	.set_bus_param	= omap1_cam_set_bus_param,
> +	.poll		= omap1_cam_poll,
> +};
> +
> +static int __init omap1_cam_probe(struct platform_device *pdev)
> +{
> +	struct omap1_cam_dev *pcdev;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	unsigned int irq;
> +	int err = 0;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	irq = platform_get_irq(pdev, 0);
> +	if (!res || (int)irq <= 0) {
> +		err = -ENODEV;
> +		goto exit;
> +	}
> +
> +	clk = clk_get(&pdev->dev, "armper_ck");
> +	if (IS_ERR(clk)) {
> +		err = PTR_ERR(clk);
> +		goto exit;
> +	}
> +
> +	pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
> +	if (!pcdev) {
> +		dev_err(&pdev->dev, "Could not allocate pcdev\n");
> +		err = -ENOMEM;
> +		goto exit_put_clk;
> +	}
> +
> +	pcdev->res = res;
> +	pcdev->clk = clk;
> +
> +	pcdev->pdata = pdev->dev.platform_data;
> +	pcdev->pflags = pcdev->pdata->flags;
> +
> +	if (pcdev->pdata)
> +		pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
> +
> +	switch (pcdev->camexclk) {
> +	case 6000000:
> +	case 8000000:
> +	case 9600000:
> +	case 12000000:
> +	case 24000000:
> +		break;
> +	default:
> +		dev_warn(&pdev->dev,
> +				"Incorrect sensor clock frequency %ld kHz, "
> +				"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
> +				"please correct your platform data\n",
> +				pcdev->pdata->camexclk_khz);
> +		pcdev->camexclk = 0;
> +	case 0:
> +		dev_info(&pdev->dev,
> +				"Not providing sensor clock\n");
> +	}
> +
> +	INIT_LIST_HEAD(&pcdev->capture);
> +	spin_lock_init(&pcdev->lock);
> +
> +	/*
> +	 * Request the region.
> +	 */
> +	if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
> +		err = -EBUSY;
> +		goto exit_kfree;
> +	}
> +
> +	base = ioremap(res->start, resource_size(res));
> +	if (!base) {
> +		err = -ENOMEM;
> +		goto exit_release;
> +	}
> +	pcdev->irq = irq;
> +	pcdev->base = base;
> +
> +	sensor_reset(pcdev, true);
> +
> +	err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
> +			dma_isr, (void *)pcdev, &pcdev->dma_ch);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
> +		err = -EBUSY;
> +		goto exit_iounmap;
> +	}
> +	dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
> +
> +	/* preconfigure DMA */
> +	omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
> +			OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
> +			0, 0);
> +	omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
> +	/* setup DMA autoinitialization */
> +	omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
> +
> +	err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Camera interrupt register failed\n");
> +		goto exit_free_dma;
> +	}
> +
> +	pcdev->soc_host.drv_name	= DRIVER_NAME;
> +	pcdev->soc_host.ops		= &omap1_host_ops;
> +	pcdev->soc_host.priv		= pcdev;
> +	pcdev->soc_host.v4l2_dev.dev	= &pdev->dev;
> +	pcdev->soc_host.nr		= pdev->id;
> +
> +	err = soc_camera_host_register(&pcdev->soc_host);
> +	if (err)
> +		goto exit_free_irq;
> +
> +	dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
> +
> +	return 0;
> +
> +exit_free_irq:
> +	free_irq(pcdev->irq, pcdev);
> +exit_free_dma:
> +	omap_free_dma(pcdev->dma_ch);
> +exit_iounmap:
> +	iounmap(base);
> +exit_release:
> +	release_mem_region(res->start, resource_size(res));
> +exit_kfree:
> +	kfree(pcdev);
> +exit_put_clk:
> +	clk_put(clk);
> +exit:
> +	return err;
> +}
> +
> +static int __exit omap1_cam_remove(struct platform_device *pdev)
> +{
> +	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
> +	struct omap1_cam_dev *pcdev = container_of(soc_host,
> +					struct omap1_cam_dev, soc_host);
> +	struct resource *res;
> +
> +	free_irq(pcdev->irq, pcdev);
> +
> +	omap_free_dma(pcdev->dma_ch);
> +
> +	soc_camera_host_unregister(soc_host);
> +
> +	iounmap(pcdev->base);
> +
> +	res = pcdev->res;
> +	release_mem_region(res->start, resource_size(res));
> +
> +	kfree(pcdev);
> +
> +	clk_put(pcdev->clk);
> +
> +	dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
> +
> +	return 0;
> +}
> +
> +static struct platform_driver omap1_cam_driver = {
> +	.driver		= {
> +		.name	= DRIVER_NAME,
> +	},
> +	.probe		= omap1_cam_probe,
> +	.remove		= __exit_p(omap1_cam_remove),
> +};
> +
> +static int __init omap1_cam_init(void)
> +{
> +	return platform_driver_register(&omap1_cam_driver);
> +}
> +module_init(omap1_cam_init);
> +
> +static void __exit omap1_cam_exit(void)
> +{
> +	platform_driver_unregister(&omap1_cam_driver);
> +}
> +module_exit(omap1_cam_exit);
> +
> +module_param(sg_mode, bool, 0644);
> +MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
> +
> +MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
> +MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff -upr linux-2.6.36-rc3.orig/include/media/omap1_camera.h linux-2.6.36-rc3/include/media/omap1_camera.h
> --- linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03 22:34:02.000000000 +0200
> +++ linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08 23:41:12.000000000 +0200
> @@ -0,0 +1,35 @@
> +/*
> + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> + *
> + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> + *
> + * 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.
> + */
> +
> +#ifndef __MEDIA_OMAP1_CAMERA_H_
> +#define __MEDIA_OMAP1_CAMERA_H_
> +
> +#include <linux/bitops.h>
> +
> +#define OMAP1_CAMERA_IOSIZE		0x1c
> +
> +enum omap1_cam_vb_mode {
> +	CONTIG = 0,
> +	SG,
> +};

See above - are these needed here?

> +
> +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)

ditto

> +
> +struct omap1_cam_platform_data {
> +	unsigned long	camexclk_khz;
> +	unsigned long	lclk_khz_max;
> +	unsigned long	flags;
> +};
> +
> +#define OMAP1_CAMERA_LCLK_RISING	BIT(0)
> +#define OMAP1_CAMERA_RST_LOW		BIT(1)
> +#define OMAP1_CAMERA_RST_HIGH		BIT(2)
> +
> +#endif /* __MEDIA_OMAP1_CAMERA_H_ */
> 

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-21 23:23   ` Guennadi Liakhovetski
@ 2010-09-22  0:10     ` hermann pitton
  2010-09-22  6:08       ` Guennadi Liakhovetski
  2010-09-22 18:13       ` Janusz Krzysztofik
  1 sibling, 1 reply; 48+ messages in thread
From: hermann pitton @ 2010-09-22  0:10 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Janusz Krzysztofik, Linux Media Mailing List, linux-omap,
	Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Hi,

Am Mittwoch, den 22.09.2010, 01:23 +0200 schrieb Guennadi Liakhovetski:
> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> 
> > This is a V4L2 driver for TI OMAP1 SoC camera interface.

[snip]

> > +
> > +	} else {
> > +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> > +				"0x%0x\n", __func__, it_status);
> 
> Please, don't split strings

sorry for any OT interference.

But, are there any new rules out?

Maybe I missed them.

Either way, the above was forced during the last three years.

Not at all ?

Cheers,
Hermann




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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-22  0:10     ` hermann pitton
@ 2010-09-22  6:08       ` Guennadi Liakhovetski
  2010-09-22 23:31         ` hermann pitton
  0 siblings, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-22  6:08 UTC (permalink / raw)
  To: hermann pitton
  Cc: Janusz Krzysztofik, Linux Media Mailing List, linux-omap,
	Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Wed, 22 Sep 2010, hermann pitton wrote:

> Am Mittwoch, den 22.09.2010, 01:23 +0200 schrieb Guennadi Liakhovetski:
> > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > 
> > > This is a V4L2 driver for TI OMAP1 SoC camera interface.
> 
> [snip]
> 
> > > +
> > > +	} else {
> > > +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> > > +				"0x%0x\n", __func__, it_status);
> > 
> > Please, don't split strings
> 
> sorry for any OT interference.
> 
> But, are there any new rules out?
> 
> Maybe I missed them.
> 
> Either way, the above was forced during the last three years.
> 
> Not at all ?

No. Splitting print strings has always been discouraged, because it makes 
tracking back kernel logs difficult. The reason for this has been the 80 
character line length limit, which has now been effectively lifted. I'm 
sure you can find enough links on the internet for any of these topics.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-11  1:23 ` [PATCH v2 2/6] OMAP1: Add support for SoC " Janusz Krzysztofik
  2010-09-11  1:34   ` [RESEND][PATCH " Janusz Krzysztofik
@ 2010-09-22  6:53   ` Guennadi Liakhovetski
  2010-09-22 18:20       ` Janusz Krzysztofik
  1 sibling, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-22  6:53 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-omap, Linux Media Mailing List, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

That's up to the platform maintainer to review / apply this one, but if 
you like, a couple of nit-picks from me:

On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:

> This patch adds support for SoC camera interface to OMAP1 devices.
> 
> Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> 
> For successfull compilation, requires a header file provided by PATCH 1/6 from 
> this series, "SoC Camera: add driver for OMAP1 camera interface".
> 
> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> ---
> v1->v2 changes:
> - no functional changes,
> - refreshed against linux-2.6.36-rc3
> 
> 
>  arch/arm/mach-omap1/devices.c             |   43 
> ++++++++++++++++++++++++++++++
>  arch/arm/mach-omap1/include/mach/camera.h |    8 +++++
>  2 files changed, 51 insertions(+)
> 
> 
> diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c 
> linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c
> --- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c	2010-09-03 
> 22:29:00.000000000 +0200
> +++ linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c	2010-09-09 
> 18:42:30.000000000 +0200
> @@ -15,6 +15,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/io.h>
>  #include <linux/spi/spi.h>
> +#include <linux/dma-mapping.h>
>  
>  #include <mach/hardware.h>
>  #include <asm/mach/map.h>
> @@ -25,6 +26,7 @@
>  #include <mach/gpio.h>
>  #include <plat/mmc.h>
>  #include <plat/omap7xx.h>
> +#include <mach/camera.h>

You might want to try to group headers according to their location, i.e., 
put <mach/...> higher - together with <mach/gpio.h>, also it helps to have 
headers alphabetically ordered.

>  
>  /*-------------------------------------------------------------------------*/
>  
> @@ -191,6 +193,47 @@ static inline void omap_init_spi100k(voi
>  }
>  #endif
>  
> +
> +#define OMAP1_CAMERA_BASE	0xfffb6800
> +
> +static struct resource omap1_camera_resources[] = {
> +	[0] = {
> +		.start	= OMAP1_CAMERA_BASE,
> +		.end	= OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= INT_CAMERA,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32);
> +
> +static struct platform_device omap1_camera_device = {
> +	.name		= "omap1-camera",
> +	.id		= 0, /* This is used to put cameras on this interface */
> +	.dev		= {
> +		.dma_mask		= &omap1_camera_dma_mask,
> +		.coherent_dma_mask	= DMA_BIT_MASK(32),
> +	},
> +	.num_resources	= ARRAY_SIZE(omap1_camera_resources),
> +	.resource	= omap1_camera_resources,
> +};
> +
> +void __init omap1_set_camera_info(struct omap1_cam_platform_data *info)
> +{
> +	struct platform_device *dev = &omap1_camera_device;
> +	int ret;
> +
> +	dev->dev.platform_data = info;
> +
> +	ret = platform_device_register(dev);
> +	if (ret)
> +		dev_err(&dev->dev, "unable to register device: %d\n", ret);
> +}
> +
> +
>  /*-------------------------------------------------------------------------*/
>  
>  static inline void omap_init_sti(void) {}
> diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h 
> linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> --- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-03 
> 22:34:03.000000000 +0200
> +++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09 
> 18:42:30.000000000 +0200
> @@ -0,0 +1,8 @@
> +#ifndef __ASM_ARCH_CAMERA_H_
> +#define __ASM_ARCH_CAMERA_H_
> +
> +#include <media/omap1_camera.h>
> +
> +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);

function declarations don't need an "extern" - something I've also been 
doing needlessly for a few years...

> +
> +#endif /* __ASM_ARCH_CAMERA_H_ */

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-11  1:25 ` [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor Janusz Krzysztofik
@ 2010-09-22  9:12   ` Guennadi Liakhovetski
  2010-09-22 18:23     ` Janusz Krzysztofik
  0 siblings, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-22  9:12 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

Ok, just a couple more comments, all looking quite good so far, if we get 
a new version soon enough, we still might manage it for 2.6.37

On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:

[snip]

> +/* write a register */
> +static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
> +{
> +	int ret;
> +	unsigned char data[2] = { reg, val };
> +	struct i2c_msg msg = {
> +		.addr	= client->addr,
> +		.flags	= 0,
> +		.len	= 2,
> +		.buf	= data,
> +	};
> +
> +	ret = i2c_transfer(client->adapter, &msg, 1);
> +	msleep_interruptible(1);

Why do you want _interruptible here? Firstly it's just 1ms, secondly - 
why?...

> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
> +		return ret;
> +	}
> +	return 0;
> +}

...

> +/* set the format we will capture in */
> +static int ov6650_s_fmt(struct v4l2_subdev *sd,
> +			struct v4l2_mbus_framefmt *mf)
> +{
> +	struct i2c_client *client = sd->priv;
> +	struct soc_camera_device *icd	= client->dev.platform_data;
> +	struct soc_camera_sense *sense = icd->sense;
> +	struct ov6650 *priv = to_ov6650(client);
> +	enum v4l2_mbus_pixelcode code = mf->code;
> +	unsigned long pclk;
> +	u8 coma_set = 0, coma_mask = 0, coml_set = 0, coml_mask = 0;
> +	u8 clkrc, clkrc_div;
> +	int ret;
> +
> +	/* select color matrix configuration for given color encoding */
> +	switch (code) {
> +	case V4L2_MBUS_FMT_GREY8_1X8:
> +		dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
> +		coma_set |= COMA_BW;
> +		coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
> +		break;
> +	case V4L2_MBUS_FMT_YUYV8_2X8:
> +		dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
> +		coma_set |= COMA_WORD_SWAP;
> +		coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
> +		break;
> +	case V4L2_MBUS_FMT_YVYU8_2X8:
> +		dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
> +		coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
> +				COMA_BYTE_SWAP;
> +		break;
> +	case V4L2_MBUS_FMT_UYVY8_2X8:
> +		dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
> +		if (mf->width == W_CIF) {
> +			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
> +			coma_mask |= COMA_RGB | COMA_BW;
> +		} else {
> +			coma_set |= COMA_BYTE_SWAP;
> +			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
> +		}
> +		break;
> +	case V4L2_MBUS_FMT_VYUY8_2X8:
> +		dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
> +		if (mf->width == W_CIF) {
> +			coma_set |= COMA_BYTE_SWAP;
> +			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
> +		} else {
> +			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
> +			coma_mask |= COMA_RGB | COMA_BW;
> +		}
> +		break;
> +	case V4L2_MBUS_FMT_SBGGR8_1X8:
> +		dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
> +		coma_set |= COMA_RAW_RGB | COMA_RGB;
> +		coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
> +		break;
> +	default:
> +		dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
> +		return -EINVAL;
> +	}
> +	priv->code = code;
> +
> +	if ((code == V4L2_MBUS_FMT_GREY8_1X8) ||
> +			(code == V4L2_MBUS_FMT_SBGGR8_1X8)) {

Superfluous parenthesis

> +		coml_mask |= COML_ONE_CHANNEL;
> +		priv->pclk_max = 4000000;
> +	} else {
> +		coml_set |= COML_ONE_CHANNEL;
> +		priv->pclk_max = 8000000;
> +	}

coml_mask and coml_set are only set here and only used once below, so, 
dropping initialisation to 0 in variable definitions and just doing

+	if (code == V4L2_MBUS_FMT_GREY8_1X8 ||
+			code == V4L2_MBUS_FMT_SBGGR8_1X8) {
+		coml_mask = COML_ONE_CHANNEL;
+		coml_set = 0;
+		priv->pclk_max = 4000000;
+	} else {
+		coml_mask = 0;
+		coml_set = COML_ONE_CHANNEL;
+		priv->pclk_max = 8000000;
+	}

would work too.

> +
> +	if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
> +		priv->colorspace = V4L2_COLORSPACE_SRGB;
> +	else
> +		priv->colorspace = V4L2_COLORSPACE_JPEG;
> +
> +	/*
> +	 * Select register configuration for given resolution.
> +	 * To be replaced with a common function that does it, once available.
> +	 */
> +	ov6650_res_roundup(&mf->width, &mf->height);
> +
> +	switch (mf->width) {
> +	case W_QCIF:
> +		dev_dbg(&client->dev, "resolution QCIF\n");
> +		priv->qcif = 1;
> +		coma_set |= COMA_QCIF;
> +		priv->pclk_max /= 2;
> +		break;
> +	case W_CIF:
> +		dev_dbg(&client->dev, "resolution CIF\n");
> +		priv->qcif = 0;
> +		coma_mask |= COMA_QCIF;
> +		break;
> +	default:
> +		dev_err(&client->dev, "unspported resolution!\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
> +	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
> +	priv->rect.width  = mf->width;
> +	priv->rect.height = mf->height;

Sorry, don't understand. The sensor can do both - cropping per HSTRT, 
HSTOP, VSTRT and VSTOP and scaling per COMA_CIF / COMA_QCIF, right? But 
which of them is stored in your priv->rect? Is this the input window 
(cropping) or the output one (scaling)? You overwrite it in .s_fmt and 
.s_crop...

> +
> +	if (priv->timeperframe.numerator && priv->timeperframe.denominator)
> +		pclk = priv->pclk_max * priv->timeperframe.denominator /
> +				(FRAME_RATE_MAX * priv->timeperframe.numerator);
> +	else
> +		pclk = priv->pclk_max;
> +
> +	if (sense) {
> +		if (sense->master_clock == 8000000) {
> +			dev_dbg(&client->dev, "8MHz input clock\n");
> +			clkrc = CLKRC_6MHz;
> +		} else if (sense->master_clock == 12000000) {
> +			dev_dbg(&client->dev, "12MHz input clock\n");
> +			clkrc = CLKRC_12MHz;
> +		} else if (sense->master_clock == 16000000) {
> +			dev_dbg(&client->dev, "16MHz input clock\n");
> +			clkrc = CLKRC_16MHz;
> +		} else if (sense->master_clock == 24000000) {
> +			dev_dbg(&client->dev, "24MHz input clock\n");
> +			clkrc = CLKRC_24MHz;
> +		} else {
> +			dev_err(&client->dev,
> +				"unspported input clock, check platform data\n");
> +			return -EINVAL;
> +		}
> +		priv->pclk_limit = sense->pixel_clock_max;
> +		if (priv->pclk_limit && (priv->pclk_limit < pclk))

Don't think the compiler would complain without the internal parenthesis 
here?

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-21 23:23   ` Guennadi Liakhovetski
@ 2010-09-22 18:13       ` Janusz Krzysztofik
  2010-09-22 18:13       ` Janusz Krzysztofik
  1 sibling, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-22 18:13 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > This is a V4L2 driver for TI OMAP1 SoC camera interface.
> >
> > Both videobuf-dma versions are supported, contig and sg, selectable with
> > a module option. The former uses less processing power, but often fails
> > to allocate contignuous buffer memory. The latter is free of this
> > problem, but generates tens of DMA interrupts per frame. If contig memory
> > allocation ever fails, the driver falls back to sg automatically on next
> > open, but still can be switched back to contig manually. Both paths work
> > stable for me, even under heavy load, on my OMAP1510 based Amstrad Delta
> > videophone, that is the oldest, least powerfull OMAP1 implementation.
> >
> > The interface generally works in pass-through mode. Since input data byte
> > endianess can be swapped, it provides up to two v4l2 pixel formats per
> > each of several soc_mbus formats that have their swapped endian
> > counterparts.
> >
> > Boards using this driver can provide it with the followning information:
> > - if and what freqency clock is expected by an on-board camera sensor,
> > - what is the maximum pixel clock that should be accepted from the
> > sensor, - what is the polarity of the sensor provided pixel clock,
> > - if the interface GPIO line is connected to a sensor reset/powerdown
> > input and what is the input polarity.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > ---
> >
> > Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> > > So, I think, a very welcome improvement to the driver would be a
> > > cleaner separation between the two cases. Don't try that much to reuse
> > > the code as much as possible. Would be much better to have clean
> > > separation between the two implementations - whether dynamically
> > > switchable at runtime or at config time - would be best to separate the
> > > two
> > > implementations to the point, where each of them becomes understandable
> > > and maintainable.
> >
> > Guennadi,
> > I've tried to rearrange them spearated, as you requested, but finally
> > decided to keep them together, as before, only better documented and
> > cleaned up as much as possible. I'm rather satisfied with the result, but
> > if you think it is still not enough understandable and maintainable, I'll
> > take one more iteration and split both paths.
>
> Well, I think, I'll move a bit towards the "if it breaks - someone gets to
> fix it, or it gets dropped" policy, i.e., I'll give you more freedom
> (don't know what's wrong with me today;))

Hi Guennadi,
Thanks!

...
> > +#define DMA_FRAME_SHIFT(x)	(x ? DMA_FRAME_SHIFT_SG : \
> > +						DMA_FRAME_SHIFT_CONTIG)
>
> Don't you want to compare (x) against CONTIG and you want to put x in
> parenthesis in the DMA_FRAME_SHIFT macro. 

Sure.

> Besides, CONTIG and SG are not 
> good enough names to be defined in a header under include/... Looks like
> you don't need them at all in the header? You only use them in this file,
> so, just move them inside.

Please see my very last comment below.

...
> > +static void videobuf_done(struct omap1_cam_dev *pcdev,
> > +		enum videobuf_state result)
> > +{
> > +	struct omap1_cam_buf *buf = pcdev->active;
> > +	struct videobuf_buffer *vb;
> > +	struct device *dev = pcdev->icd->dev.parent;
> > +
> > +	if (WARN_ON(!buf)) {
> > +		suspend_capture(pcdev);
> > +		disable_capture(pcdev);
> > +		return;
> > +	}
> > +
> > +	if (result == VIDEOBUF_ERROR)
> > +		suspend_capture(pcdev);
> > +
> > +	vb = &buf->vb;
> > +	if (waitqueue_active(&vb->done)) {
> > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > +			/*
> > +			 * No next buffer has been entered into the DMA
> > +			 * programming register set on time, so best we can do
> > +			 * is stopping the capture before last DMA block,
> > +			 * whether our CONTIG mode whole buffer or its last
> > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > +			 */
>
> Hm, why do you think it's a good idea? This specific buffer completed
> successfully, but you want to fail it just because the next buffer is
> missing? Any specific reason for this? 

Maybe my comment is not clear enough, but the below suspend_capture() doesn't 
indicate any failure on a frame just captured. It only prevents the frame 
from being overwritten by the already autoreinitialized DMA engine, pointing 
back to the same buffer once again.

> Besides, you seem to also be 
> considering the possibility of your ->ready == NULL, but the queue
> non-empty, in which case you just take the next buffer from the queue and
> continue with it. Why error out in this case? 

pcdev->ready == NULL means no buffer was available when it was time to put it 
into the DMA programming register set. As a result, a next DMA transfer has 
just been autoreinitialized with the same buffer parameters as before. To 
protect the buffer from being overwriten unintentionally, we have to stop the 
DMA transfer as soon as possible, hopefully before the sensor starts sending 
out next frame data.

If a new buffer has been queued meanwhile, best we can do is stopping 
everything, programming the DMA with the new buffer, and setting up for a new 
transfer hardware auto startup on nearest frame start, be it the next one if 
we are lucky enough, or one after the next if we are too slow.

> And even if also the queue 
> is empty - still not sure, why.

I hope the above explanation clarifies why.

I'll try to rework the above comment to be more clear, OK? Any hints?

> > +			suspend_capture(pcdev);
> > +		vb->state = result;
> > +		do_gettimeofday(&vb->ts);
> > +		if (result != VIDEOBUF_ERROR)
> > +			vb->field_count++;
> > +		wake_up(&vb->done);
> > +
> > +		/* shift in next buffer */
> > +		buf = pcdev->ready;
> > +		pcdev->active = buf;
> > +		pcdev->ready = NULL;
> > +
> > +		if (!buf) {
> > +			/*
> > +			 * No next buffer was ready on time (see above), so
> > +			 * indicate error condition to force capture restart or
> > +			 * stop, depending on next buffer already queued or not.
> > +			 */
> > +			result = VIDEOBUF_ERROR;
> > +			prepare_next_vb(pcdev);
> > +
> > +			buf = pcdev->ready;
> > +			pcdev->active = buf;
> > +			pcdev->ready = NULL;
> > +		}
> > +	} else if (pcdev->ready) {
> > +		/*
> > +		 * In both CONTIG and SG mode, the DMA engine has (might)
>
> s/might/possibly/

OK

> > +		 * already been autoreinitialized with the preprogrammed
> > +		 * pcdev->ready buffer.  We can either accept this fact
> > +		 * and just swap the buffers, or provoke an error condition
> > +		 * and restart capture.  The former seems less intrusive.
> > +		 */
> > +		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
> > +				__func__);
> > +		pcdev->active = pcdev->ready;
> > +
> > +		if (pcdev->vb_mode == SG) {
> > +			/*
> > +			 * In SG mode, we have to make sure that the buffer we
> > +			 * are putting back into the pcdev->ready is marked
> > +			 * fresh.
> > +			 */
> > +			buf->sgbuf = NULL;
> > +		}
> > +		pcdev->ready = buf;
> > +
> > +		buf = pcdev->active;
> > +	} else {
> > +		/*
> > +		 * No next buffer has been entered into
> > +		 * the DMA programming register set on time.
> > +		 */
> > +		if (pcdev->vb_mode == CONTIG) {
> > +			/*
> > +			 * In CONTIG mode, the DMA engine has already been
> > +			 * reinitialized with the current buffer. Best we can do
> > +			 * is not touching it.
> > +			 */
> > +			dev_dbg(dev,
> > +				"%s: nobody waiting on videobuf, reuse it\n",
> > +				__func__);
> > +		} else {
> > +			/*
> > +			 * In SG mode, the DMA engine has just been
> > +			 * autoreinitialized with the last sgbuf from the
> > +			 * current list. Restart capture in order to transfer
> > +			 * next frame start into the first sgbuf, not the last
> > +			 * one.
> > +			 */
> > +			if (result != VIDEOBUF_ERROR) {
> > +				suspend_capture(pcdev);
> > +				result = VIDEOBUF_ERROR;
> > +			}
> > +		}
> > +	}
> > +
> > +	if (!buf) {
> > +		dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
> > +		disable_capture(pcdev);
> > +		return;
> > +	}
> > +
> > +	if (pcdev->vb_mode == CONTIG) {
> > +		/*
> > +		 * In CONTIG mode, the current buffer parameters had already
> > +		 * been entered into the DMA programming register set while the
> > +		 * buffer was fetched with prepare_next_vb(), they may have also
> > +		 * been transfered into the runtime set and already active if
> > +		 * the DMA still running.
> > +		 */
> > +	} else {
> > +		/* In SG mode, extra steps are required */
> > +		if (result == VIDEOBUF_ERROR)
> > +			/* make sure we (re)use sglist from start on error */
> > +			buf->sgbuf = NULL;
> > +
> > +		/*
> > +		 * In any case, enter the next sgbuf parameters into the DMA
> > +		 * programming register set.  They will be used either during
> > +		 * nearest DMA autoreinitialization or, in case of an error,
> > +		 * on DMA startup below.
> > +		 */
> > +		try_next_sgbuf(pcdev->dma_ch, buf);
> > +	}
> > +
> > +	if (result == VIDEOBUF_ERROR) {
> > +		dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
> > +				__func__);
> > +		start_capture(pcdev);
> > +		/*
> > +		 * In SG mode, the above also resulted in the next sgbuf
> > +		 * parameters being entered into the DMA programming register
> > +		 * set, making them ready for next DMA autoreinitialization.
> > +		 */
> > +	}
> > +
> > +	/*
> > +	 * Finally, try fetching next buffer.  In CONTIG mode, it will also
> > +	 * get entered it into the DMA programming register set,
>
> s/get entered/enter/

OK.

> > +	 * making it ready for next DMA autoreinitialization.
> > +	 */
> > +	prepare_next_vb(pcdev);
> > +}
> > +
> > +static void dma_isr(int channel, unsigned short status, void *data)
> > +{
> > +	struct omap1_cam_dev *pcdev = data;
> > +	struct omap1_cam_buf *buf = pcdev->active;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&pcdev->lock, flags);
> > +
> > +	if (WARN_ON(!buf)) {
> > +		suspend_capture(pcdev);
> > +		disable_capture(pcdev);
> > +		goto out;
> > +	}
> > +
> > +	if (pcdev->vb_mode == CONTIG) {
> > +		/*
> > +		 * In CONTIG mode, assume we have just managed to collect the
> > +		 * whole frame, hopefully before our end of frame watchdog is
> > +		 * triggered. Then, all we have to do is disabling the watchdog
> > +		 * for this frame, and calling videobuf_done() with success
> > +		 * indicated.
> > +		 */
> > +		CAM_WRITE(pcdev, MODE,
> > +				CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
> > +		videobuf_done(pcdev, VIDEOBUF_DONE);
> > +	} else {
> > +		/*
> > +		 * In SG mode, we have to process every sgbuf from the current
> > +		 * sglist, one after another.
> > +		 */
> > +		if (buf->sgbuf) {
> > +			/*
> > +			 * Current sglist not completed yet, try fetching next
> > +			 * sgbuf, hopefully putting it into the DMA programming
> > +			 * register set, making it ready for next DMA
> > +			 * autoreinitialization.
> > +			 */
> > +			try_next_sgbuf(pcdev->dma_ch, buf);
> > +			if (buf->sgbuf)
> > +				goto out;
> > +
> > +			/* no more sgbufs in the current sglist */
> > +			if (buf->result != VIDEOBUF_ERROR) {
> > +				/*
> > +				 * Video frame collected without errors, we can
> > +				 * prepare for collecting a next one as soon as
> > +				 * DMA gets autoreinitialized after the currennt
> > +				 * (last) sgbuf is completed.
> > +				 */
> > +				buf = prepare_next_vb(pcdev);
> > +				if (!buf)
> > +					goto out;
> > +
> > +				try_next_sgbuf(pcdev->dma_ch, buf);
> > +				goto out;
>
> Uh, sorry, maybe I cannot quite follow the logic here, but it doesn't seem
> quite correct to me. Looks like you do not complete the current
> (successfully completed) buffer, if you failed to prepare the next one?

Looks like there is also some space for improvement in my documentation here.

In short, until the DMA ISR is called with the buf->sgbuf reset back to NULL, 
which should happen while the previous DMA ISR's called try_next_sgbuf() has 
retuned NULL, the frame is still not ready, ie. still being filled with DMA. 
IOW, only the DMA interrupt with buf->sgbuf == NULL means that the last sgbuf 
DMA transfer has just been completed.

Meanwhile, a next sglist could already be put in advanvce into the DMA 
programming register set and just activated (copied to the DMA working 
register set) automatically.

> > +			}
> > +		}
> > +		/* end of videobuf */
> > +		videobuf_done(pcdev, buf->result);
> > +	}
> > +
> > +out:
> > +	spin_unlock_irqrestore(&pcdev->lock, flags);
> > +}

...
> > +				 * If exactly 2 sgbufs from the next sglist has
>
> s/has/have/

Thanks.

...
> > +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> > +				"0x%0x\n", __func__, it_status);
>
> Please, don't split strings

OK.

...
> > +static int omap1_cam_add_device(struct soc_camera_device *icd)
> > +{
> > +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> > +	struct omap1_cam_dev *pcdev = ici->priv;
> > +	u32 ctrlclock;
> > +
> > +	if (pcdev->icd)
> > +		return -EBUSY;
> > +
> > +	clk_enable(pcdev->clk);
> > +
> > +	/* setup sensor clock */
> > +	ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
> > +	ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	ctrlclock &= ~FOSCMOD_MASK;
> > +	switch (pcdev->camexclk) {
> > +	case 6000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
> > +		break;
> > +	case 8000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
> > +		break;
> > +	case 9600000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
> > +		break;
> > +	case 12000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
> > +		break;
> > +	case 24000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
> > +	default:
>
> This default doesn't make much sense here, maybe put it to one of these
> values, that you think is a reasonable fall back. Ah, right, you wanted to
> check, whether this can work...

Sorry, I forgot to answer your previously asked question about the MCLK_EN 
(master clock enable) requirement if not rising the CAMEXCLK_EN (sensor clock 
enable).

The documentation (http://focus.ti.com/lit/ug/spru684/spru684.pdf) says:

	"the MCLK_EN bit must first be set before any camera-interface registers 
	 access"

so the answer is yes, the MCLK is required not only for providing a sensor 
with a clock, but also for the host interface itself to do its job.

The default case above (no CAMEXCLK_EN) is intended to be used on a board 
whith a sensor that is driven from a clock source other than the interface's 
CAM_EXCLK pin, which is keept idle in this case. Am I missing something?

My general assumption was to provide support for all interface hardware 
features which are supported by the existing soc_camera framework, not only 
those required by my board or sensor. Could it be that this approach breaks a 
general rule of not putting any code that is not (yet) used by any consumer?

> > +		break;
> > +	}
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
> > +
> > +	/* enable internal clock */
> > +	ctrlclock |= MCLK_EN;
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	sensor_reset(pcdev, false);
> > +
> > +	pcdev->icd = icd;
> > +
> > +	dev_info(icd->dev.parent, "OMAP1 Camera driver attached to camera
> > %d\n", +			icd->devnum);
> > +	return 0;
> > +}

...
> > +	ret = dma_align(&rect->width, &rect->height,
> > icd->current_fmt->host_fmt, +			false);
> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
> > +				__func__, rect->width, rect->height,
> > +				icd->current_fmt->host_fmt->name);
> > +		return ret;
> > +	}
> > +
> > +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
>
> Missing "ret = "?

Yes, thanks!

> > +	if (ret < 0) {
> > +		dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> > +			 rect->width, rect->height, rect->left, rect->top);
> > +		return ret;
> > +	}

...
> > +	dev_notice(dev, "%s: sensor geometry not DMA aligned, trying to crop
> > to" +			" %ux%u\n", __func__, pix->width, pix->height);
>
> One line

OK.

> > +	ret = get_crop(icd, rect);
> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: get_crop() failed\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	rect->width  = pix->width;
> > +	rect->height = pix->height;
> > +
> > +	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, &crop);
>
> 'ret = '?

Of course.

> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> > +			 rect->width, rect->height, rect->left, rect->top);
> > +		return ret;
> > +	}

...
> > +		sg_mode = 1;
>
> "true"

Right.

...
> > +static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
> > +		__u32 pixfmt)
> > +{
> > +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> > +	struct omap1_cam_dev *pcdev = ici->priv;
> > +	struct device *dev = icd->dev.parent;
> > +	const struct soc_camera_format_xlate *xlate;
> > +	const struct soc_mbus_pixelfmt *fmt;
> > +	unsigned long camera_flags, common_flags;
> > +	u32 ctrlclock, mode;
> > +	int ret;
> > +
> > +	camera_flags = icd->ops->query_bus_param(icd);
> > +
> > +	common_flags = soc_camera_bus_param_compatible(camera_flags,
> > +			SOCAM_BUS_FLAGS);
> > +	if (!common_flags)
> > +		return -EINVAL;
> > +
> > +	/* Make choices, possibly based on platform configuration */
> > +	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
> > +			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
> > +		if (!pcdev->pdata ||
> > +				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
>
> Out of curiosity - do you need to force a specific clock polarity on your
> platform 

If you mean my board - no, I don't think so.

> or do you know of any, where this is necessary (beyond the 
> natural restriction, coming from the sensor - if any). 

No, neither.

> I.e., I'm not at 
> all sure you need this flag, but if you do - I would apply it earlier - to
> mask the respective bit from SOCAM_BUS_FLAGS.

Maybe my understanding of what is actually going on here is not enough. TBH, 
I've just copy-pasted this piece of code from mx1_camera.c in order to 
provide support for a hardware feature that is available on my platform, 
regardles of any boards actually using it or not. Almost all existing 
soc_camera host drivers, ie. the pxa_camera.c and all of mx?_camera.c, follow 
the same or very similiar pattern, and the only exception, 
sh_mobile_ceu_camera.c, looks for me like just missing this hardware feature. 
Then, having no example implementation to follow and not ehough understanding 
of the matter, I'm not sure if I'm able to make you happy. Please give me 
more details if you think this is important.

> > +			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
> > +		else
> > +			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
> > +	}
> > +
> > +	ret = icd->ops->set_bus_param(icd, common_flags);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> > +	if (ctrlclock & LCLK_EN)
> > +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> > +
> > +	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
> > +		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
> > +		ctrlclock |= POLCLK;
> > +	} else {
> > +		dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
> > +		ctrlclock &= ~POLCLK;
> > +	}
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> > +
> > +	if (ctrlclock & LCLK_EN)
> > +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	/* select bus endianess */
> > +	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
> > +	fmt = xlate->host_fmt;
> > +
> > +	mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
> > +	if (fmt->order == SOC_MBUS_ORDER_LE) {
> > +		dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
> > +		CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
> > +	} else {
> > +		dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
> > +		CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
> > +	}
> > +
> > +	return 0;
> > +}

...
> > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > 22:34:02.000000000 +0200 +++
> > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > +/*
> > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > + *
> > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > +#define __MEDIA_OMAP1_CAMERA_H_
> > +
> > +#include <linux/bitops.h>
> > +
> > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > +
> > +enum omap1_cam_vb_mode {
> > +	CONTIG = 0,
> > +	SG,
> > +};
>
> See above - are these needed here?
>
> > +
> > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
>
> ditto

I moved them both over to the header file because I was using the 
OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in order 
to calculate the buffer size when calling the then NAKed 
dma_preallocate_coherent_memory(). Now I could put them back into the driver 
code, but if we ever get back to the concept of preallocating a contignuos 
piece of memory from the platform init code, we might need them back here, so 
maybe I should rather keep them, only rename the two enum values using a 
distinct name space. What do you think is better for now?

Thanks,
Janusz

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
@ 2010-09-22 18:13       ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-22 18:13 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > This is a V4L2 driver for TI OMAP1 SoC camera interface.
> >
> > Both videobuf-dma versions are supported, contig and sg, selectable with
> > a module option. The former uses less processing power, but often fails
> > to allocate contignuous buffer memory. The latter is free of this
> > problem, but generates tens of DMA interrupts per frame. If contig memory
> > allocation ever fails, the driver falls back to sg automatically on next
> > open, but still can be switched back to contig manually. Both paths work
> > stable for me, even under heavy load, on my OMAP1510 based Amstrad Delta
> > videophone, that is the oldest, least powerfull OMAP1 implementation.
> >
> > The interface generally works in pass-through mode. Since input data byte
> > endianess can be swapped, it provides up to two v4l2 pixel formats per
> > each of several soc_mbus formats that have their swapped endian
> > counterparts.
> >
> > Boards using this driver can provide it with the followning information:
> > - if and what freqency clock is expected by an on-board camera sensor,
> > - what is the maximum pixel clock that should be accepted from the
> > sensor, - what is the polarity of the sensor provided pixel clock,
> > - if the interface GPIO line is connected to a sensor reset/powerdown
> > input and what is the input polarity.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > ---
> >
> > Friday 30 July 2010 13:07:42 Guennadi Liakhovetski wrote:
> > > So, I think, a very welcome improvement to the driver would be a
> > > cleaner separation between the two cases. Don't try that much to reuse
> > > the code as much as possible. Would be much better to have clean
> > > separation between the two implementations - whether dynamically
> > > switchable at runtime or at config time - would be best to separate the
> > > two
> > > implementations to the point, where each of them becomes understandable
> > > and maintainable.
> >
> > Guennadi,
> > I've tried to rearrange them spearated, as you requested, but finally
> > decided to keep them together, as before, only better documented and
> > cleaned up as much as possible. I'm rather satisfied with the result, but
> > if you think it is still not enough understandable and maintainable, I'll
> > take one more iteration and split both paths.
>
> Well, I think, I'll move a bit towards the "if it breaks - someone gets to
> fix it, or it gets dropped" policy, i.e., I'll give you more freedom
> (don't know what's wrong with me today;))

Hi Guennadi,
Thanks!

...
> > +#define DMA_FRAME_SHIFT(x)	(x ? DMA_FRAME_SHIFT_SG : \
> > +						DMA_FRAME_SHIFT_CONTIG)
>
> Don't you want to compare (x) against CONTIG and you want to put x in
> parenthesis in the DMA_FRAME_SHIFT macro. 

Sure.

> Besides, CONTIG and SG are not 
> good enough names to be defined in a header under include/... Looks like
> you don't need them at all in the header? You only use them in this file,
> so, just move them inside.

Please see my very last comment below.

...
> > +static void videobuf_done(struct omap1_cam_dev *pcdev,
> > +		enum videobuf_state result)
> > +{
> > +	struct omap1_cam_buf *buf = pcdev->active;
> > +	struct videobuf_buffer *vb;
> > +	struct device *dev = pcdev->icd->dev.parent;
> > +
> > +	if (WARN_ON(!buf)) {
> > +		suspend_capture(pcdev);
> > +		disable_capture(pcdev);
> > +		return;
> > +	}
> > +
> > +	if (result == VIDEOBUF_ERROR)
> > +		suspend_capture(pcdev);
> > +
> > +	vb = &buf->vb;
> > +	if (waitqueue_active(&vb->done)) {
> > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > +			/*
> > +			 * No next buffer has been entered into the DMA
> > +			 * programming register set on time, so best we can do
> > +			 * is stopping the capture before last DMA block,
> > +			 * whether our CONTIG mode whole buffer or its last
> > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > +			 */
>
> Hm, why do you think it's a good idea? This specific buffer completed
> successfully, but you want to fail it just because the next buffer is
> missing? Any specific reason for this? 

Maybe my comment is not clear enough, but the below suspend_capture() doesn't 
indicate any failure on a frame just captured. It only prevents the frame 
from being overwritten by the already autoreinitialized DMA engine, pointing 
back to the same buffer once again.

> Besides, you seem to also be 
> considering the possibility of your ->ready == NULL, but the queue
> non-empty, in which case you just take the next buffer from the queue and
> continue with it. Why error out in this case? 

pcdev->ready == NULL means no buffer was available when it was time to put it 
into the DMA programming register set. As a result, a next DMA transfer has 
just been autoreinitialized with the same buffer parameters as before. To 
protect the buffer from being overwriten unintentionally, we have to stop the 
DMA transfer as soon as possible, hopefully before the sensor starts sending 
out next frame data.

If a new buffer has been queued meanwhile, best we can do is stopping 
everything, programming the DMA with the new buffer, and setting up for a new 
transfer hardware auto startup on nearest frame start, be it the next one if 
we are lucky enough, or one after the next if we are too slow.

> And even if also the queue 
> is empty - still not sure, why.

I hope the above explanation clarifies why.

I'll try to rework the above comment to be more clear, OK? Any hints?

> > +			suspend_capture(pcdev);
> > +		vb->state = result;
> > +		do_gettimeofday(&vb->ts);
> > +		if (result != VIDEOBUF_ERROR)
> > +			vb->field_count++;
> > +		wake_up(&vb->done);
> > +
> > +		/* shift in next buffer */
> > +		buf = pcdev->ready;
> > +		pcdev->active = buf;
> > +		pcdev->ready = NULL;
> > +
> > +		if (!buf) {
> > +			/*
> > +			 * No next buffer was ready on time (see above), so
> > +			 * indicate error condition to force capture restart or
> > +			 * stop, depending on next buffer already queued or not.
> > +			 */
> > +			result = VIDEOBUF_ERROR;
> > +			prepare_next_vb(pcdev);
> > +
> > +			buf = pcdev->ready;
> > +			pcdev->active = buf;
> > +			pcdev->ready = NULL;
> > +		}
> > +	} else if (pcdev->ready) {
> > +		/*
> > +		 * In both CONTIG and SG mode, the DMA engine has (might)
>
> s/might/possibly/

OK

> > +		 * already been autoreinitialized with the preprogrammed
> > +		 * pcdev->ready buffer.  We can either accept this fact
> > +		 * and just swap the buffers, or provoke an error condition
> > +		 * and restart capture.  The former seems less intrusive.
> > +		 */
> > +		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
> > +				__func__);
> > +		pcdev->active = pcdev->ready;
> > +
> > +		if (pcdev->vb_mode == SG) {
> > +			/*
> > +			 * In SG mode, we have to make sure that the buffer we
> > +			 * are putting back into the pcdev->ready is marked
> > +			 * fresh.
> > +			 */
> > +			buf->sgbuf = NULL;
> > +		}
> > +		pcdev->ready = buf;
> > +
> > +		buf = pcdev->active;
> > +	} else {
> > +		/*
> > +		 * No next buffer has been entered into
> > +		 * the DMA programming register set on time.
> > +		 */
> > +		if (pcdev->vb_mode == CONTIG) {
> > +			/*
> > +			 * In CONTIG mode, the DMA engine has already been
> > +			 * reinitialized with the current buffer. Best we can do
> > +			 * is not touching it.
> > +			 */
> > +			dev_dbg(dev,
> > +				"%s: nobody waiting on videobuf, reuse it\n",
> > +				__func__);
> > +		} else {
> > +			/*
> > +			 * In SG mode, the DMA engine has just been
> > +			 * autoreinitialized with the last sgbuf from the
> > +			 * current list. Restart capture in order to transfer
> > +			 * next frame start into the first sgbuf, not the last
> > +			 * one.
> > +			 */
> > +			if (result != VIDEOBUF_ERROR) {
> > +				suspend_capture(pcdev);
> > +				result = VIDEOBUF_ERROR;
> > +			}
> > +		}
> > +	}
> > +
> > +	if (!buf) {
> > +		dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
> > +		disable_capture(pcdev);
> > +		return;
> > +	}
> > +
> > +	if (pcdev->vb_mode == CONTIG) {
> > +		/*
> > +		 * In CONTIG mode, the current buffer parameters had already
> > +		 * been entered into the DMA programming register set while the
> > +		 * buffer was fetched with prepare_next_vb(), they may have also
> > +		 * been transfered into the runtime set and already active if
> > +		 * the DMA still running.
> > +		 */
> > +	} else {
> > +		/* In SG mode, extra steps are required */
> > +		if (result == VIDEOBUF_ERROR)
> > +			/* make sure we (re)use sglist from start on error */
> > +			buf->sgbuf = NULL;
> > +
> > +		/*
> > +		 * In any case, enter the next sgbuf parameters into the DMA
> > +		 * programming register set.  They will be used either during
> > +		 * nearest DMA autoreinitialization or, in case of an error,
> > +		 * on DMA startup below.
> > +		 */
> > +		try_next_sgbuf(pcdev->dma_ch, buf);
> > +	}
> > +
> > +	if (result == VIDEOBUF_ERROR) {
> > +		dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
> > +				__func__);
> > +		start_capture(pcdev);
> > +		/*
> > +		 * In SG mode, the above also resulted in the next sgbuf
> > +		 * parameters being entered into the DMA programming register
> > +		 * set, making them ready for next DMA autoreinitialization.
> > +		 */
> > +	}
> > +
> > +	/*
> > +	 * Finally, try fetching next buffer.  In CONTIG mode, it will also
> > +	 * get entered it into the DMA programming register set,
>
> s/get entered/enter/

OK.

> > +	 * making it ready for next DMA autoreinitialization.
> > +	 */
> > +	prepare_next_vb(pcdev);
> > +}
> > +
> > +static void dma_isr(int channel, unsigned short status, void *data)
> > +{
> > +	struct omap1_cam_dev *pcdev = data;
> > +	struct omap1_cam_buf *buf = pcdev->active;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&pcdev->lock, flags);
> > +
> > +	if (WARN_ON(!buf)) {
> > +		suspend_capture(pcdev);
> > +		disable_capture(pcdev);
> > +		goto out;
> > +	}
> > +
> > +	if (pcdev->vb_mode == CONTIG) {
> > +		/*
> > +		 * In CONTIG mode, assume we have just managed to collect the
> > +		 * whole frame, hopefully before our end of frame watchdog is
> > +		 * triggered. Then, all we have to do is disabling the watchdog
> > +		 * for this frame, and calling videobuf_done() with success
> > +		 * indicated.
> > +		 */
> > +		CAM_WRITE(pcdev, MODE,
> > +				CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
> > +		videobuf_done(pcdev, VIDEOBUF_DONE);
> > +	} else {
> > +		/*
> > +		 * In SG mode, we have to process every sgbuf from the current
> > +		 * sglist, one after another.
> > +		 */
> > +		if (buf->sgbuf) {
> > +			/*
> > +			 * Current sglist not completed yet, try fetching next
> > +			 * sgbuf, hopefully putting it into the DMA programming
> > +			 * register set, making it ready for next DMA
> > +			 * autoreinitialization.
> > +			 */
> > +			try_next_sgbuf(pcdev->dma_ch, buf);
> > +			if (buf->sgbuf)
> > +				goto out;
> > +
> > +			/* no more sgbufs in the current sglist */
> > +			if (buf->result != VIDEOBUF_ERROR) {
> > +				/*
> > +				 * Video frame collected without errors, we can
> > +				 * prepare for collecting a next one as soon as
> > +				 * DMA gets autoreinitialized after the currennt
> > +				 * (last) sgbuf is completed.
> > +				 */
> > +				buf = prepare_next_vb(pcdev);
> > +				if (!buf)
> > +					goto out;
> > +
> > +				try_next_sgbuf(pcdev->dma_ch, buf);
> > +				goto out;
>
> Uh, sorry, maybe I cannot quite follow the logic here, but it doesn't seem
> quite correct to me. Looks like you do not complete the current
> (successfully completed) buffer, if you failed to prepare the next one?

Looks like there is also some space for improvement in my documentation here.

In short, until the DMA ISR is called with the buf->sgbuf reset back to NULL, 
which should happen while the previous DMA ISR's called try_next_sgbuf() has 
retuned NULL, the frame is still not ready, ie. still being filled with DMA. 
IOW, only the DMA interrupt with buf->sgbuf == NULL means that the last sgbuf 
DMA transfer has just been completed.

Meanwhile, a next sglist could already be put in advanvce into the DMA 
programming register set and just activated (copied to the DMA working 
register set) automatically.

> > +			}
> > +		}
> > +		/* end of videobuf */
> > +		videobuf_done(pcdev, buf->result);
> > +	}
> > +
> > +out:
> > +	spin_unlock_irqrestore(&pcdev->lock, flags);
> > +}

...
> > +				 * If exactly 2 sgbufs from the next sglist has
>
> s/has/have/

Thanks.

...
> > +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> > +				"0x%0x\n", __func__, it_status);
>
> Please, don't split strings

OK.

...
> > +static int omap1_cam_add_device(struct soc_camera_device *icd)
> > +{
> > +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> > +	struct omap1_cam_dev *pcdev = ici->priv;
> > +	u32 ctrlclock;
> > +
> > +	if (pcdev->icd)
> > +		return -EBUSY;
> > +
> > +	clk_enable(pcdev->clk);
> > +
> > +	/* setup sensor clock */
> > +	ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
> > +	ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	ctrlclock &= ~FOSCMOD_MASK;
> > +	switch (pcdev->camexclk) {
> > +	case 6000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
> > +		break;
> > +	case 8000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
> > +		break;
> > +	case 9600000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
> > +		break;
> > +	case 12000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
> > +		break;
> > +	case 24000000:
> > +		ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
> > +	default:
>
> This default doesn't make much sense here, maybe put it to one of these
> values, that you think is a reasonable fall back. Ah, right, you wanted to
> check, whether this can work...

Sorry, I forgot to answer your previously asked question about the MCLK_EN 
(master clock enable) requirement if not rising the CAMEXCLK_EN (sensor clock 
enable).

The documentation (http://focus.ti.com/lit/ug/spru684/spru684.pdf) says:

	"the MCLK_EN bit must first be set before any camera-interface registers 
	 access"

so the answer is yes, the MCLK is required not only for providing a sensor 
with a clock, but also for the host interface itself to do its job.

The default case above (no CAMEXCLK_EN) is intended to be used on a board 
whith a sensor that is driven from a clock source other than the interface's 
CAM_EXCLK pin, which is keept idle in this case. Am I missing something?

My general assumption was to provide support for all interface hardware 
features which are supported by the existing soc_camera framework, not only 
those required by my board or sensor. Could it be that this approach breaks a 
general rule of not putting any code that is not (yet) used by any consumer?

> > +		break;
> > +	}
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
> > +
> > +	/* enable internal clock */
> > +	ctrlclock |= MCLK_EN;
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	sensor_reset(pcdev, false);
> > +
> > +	pcdev->icd = icd;
> > +
> > +	dev_info(icd->dev.parent, "OMAP1 Camera driver attached to camera
> > %d\n", +			icd->devnum);
> > +	return 0;
> > +}

...
> > +	ret = dma_align(&rect->width, &rect->height,
> > icd->current_fmt->host_fmt, +			false);
> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
> > +				__func__, rect->width, rect->height,
> > +				icd->current_fmt->host_fmt->name);
> > +		return ret;
> > +	}
> > +
> > +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
>
> Missing "ret = "?

Yes, thanks!

> > +	if (ret < 0) {
> > +		dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> > +			 rect->width, rect->height, rect->left, rect->top);
> > +		return ret;
> > +	}

...
> > +	dev_notice(dev, "%s: sensor geometry not DMA aligned, trying to crop
> > to" +			" %ux%u\n", __func__, pix->width, pix->height);
>
> One line

OK.

> > +	ret = get_crop(icd, rect);
> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: get_crop() failed\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	rect->width  = pix->width;
> > +	rect->height = pix->height;
> > +
> > +	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > +	subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, &crop);
>
> 'ret = '?

Of course.

> > +	if (ret < 0) {
> > +		dev_err(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
> > +			 rect->width, rect->height, rect->left, rect->top);
> > +		return ret;
> > +	}

...
> > +		sg_mode = 1;
>
> "true"

Right.

...
> > +static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
> > +		__u32 pixfmt)
> > +{
> > +	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
> > +	struct omap1_cam_dev *pcdev = ici->priv;
> > +	struct device *dev = icd->dev.parent;
> > +	const struct soc_camera_format_xlate *xlate;
> > +	const struct soc_mbus_pixelfmt *fmt;
> > +	unsigned long camera_flags, common_flags;
> > +	u32 ctrlclock, mode;
> > +	int ret;
> > +
> > +	camera_flags = icd->ops->query_bus_param(icd);
> > +
> > +	common_flags = soc_camera_bus_param_compatible(camera_flags,
> > +			SOCAM_BUS_FLAGS);
> > +	if (!common_flags)
> > +		return -EINVAL;
> > +
> > +	/* Make choices, possibly based on platform configuration */
> > +	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
> > +			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
> > +		if (!pcdev->pdata ||
> > +				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
>
> Out of curiosity - do you need to force a specific clock polarity on your
> platform 

If you mean my board - no, I don't think so.

> or do you know of any, where this is necessary (beyond the 
> natural restriction, coming from the sensor - if any). 

No, neither.

> I.e., I'm not at 
> all sure you need this flag, but if you do - I would apply it earlier - to
> mask the respective bit from SOCAM_BUS_FLAGS.

Maybe my understanding of what is actually going on here is not enough. TBH, 
I've just copy-pasted this piece of code from mx1_camera.c in order to 
provide support for a hardware feature that is available on my platform, 
regardles of any boards actually using it or not. Almost all existing 
soc_camera host drivers, ie. the pxa_camera.c and all of mx?_camera.c, follow 
the same or very similiar pattern, and the only exception, 
sh_mobile_ceu_camera.c, looks for me like just missing this hardware feature. 
Then, having no example implementation to follow and not ehough understanding 
of the matter, I'm not sure if I'm able to make you happy. Please give me 
more details if you think this is important.

> > +			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
> > +		else
> > +			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
> > +	}
> > +
> > +	ret = icd->ops->set_bus_param(icd, common_flags);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
> > +	if (ctrlclock & LCLK_EN)
> > +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> > +
> > +	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
> > +		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
> > +		ctrlclock |= POLCLK;
> > +	} else {
> > +		dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
> > +		ctrlclock &= ~POLCLK;
> > +	}
> > +	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
> > +
> > +	if (ctrlclock & LCLK_EN)
> > +		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
> > +
> > +	/* select bus endianess */
> > +	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
> > +	fmt = xlate->host_fmt;
> > +
> > +	mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
> > +	if (fmt->order == SOC_MBUS_ORDER_LE) {
> > +		dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
> > +		CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
> > +	} else {
> > +		dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
> > +		CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
> > +	}
> > +
> > +	return 0;
> > +}

...
> > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > 22:34:02.000000000 +0200 +++
> > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > +/*
> > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > + *
> > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > +#define __MEDIA_OMAP1_CAMERA_H_
> > +
> > +#include <linux/bitops.h>
> > +
> > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > +
> > +enum omap1_cam_vb_mode {
> > +	CONTIG = 0,
> > +	SG,
> > +};
>
> See above - are these needed here?
>
> > +
> > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
>
> ditto

I moved them both over to the header file because I was using the 
OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in order 
to calculate the buffer size when calling the then NAKed 
dma_preallocate_coherent_memory(). Now I could put them back into the driver 
code, but if we ever get back to the concept of preallocating a contignuos 
piece of memory from the platform init code, we might need them back here, so 
maybe I should rather keep them, only rename the two enum values using a 
distinct name space. What do you think is better for now?

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

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

* Re: [PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-22  6:53   ` [PATCH " Guennadi Liakhovetski
@ 2010-09-22 18:20       ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-22 18:20 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-omap, Linux Media Mailing List, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Wednesday 22 September 2010 08:53:19 Guennadi Liakhovetski napisał(a):
> That's up to the platform maintainer to review / apply this one, but if
> you like, a couple of nit-picks from me:

Guennadi,
Thanks for also looking at this!

> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > This patch adds support for SoC camera interface to OMAP1 devices.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > For successfull compilation, requires a header file provided by PATCH 1/6
> > from this series, "SoC Camera: add driver for OMAP1 camera interface".
> >
> > Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > ---
> > v1->v2 changes:
> > - no functional changes,
> > - refreshed against linux-2.6.36-rc3
> >
> >
> >  arch/arm/mach-omap1/devices.c             |   43
> > ++++++++++++++++++++++++++++++
> >  arch/arm/mach-omap1/include/mach/camera.h |    8 +++++
> >  2 files changed, 51 insertions(+)
> >
> >
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c
> > linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c
> > --- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c	2010-09-03
> > 22:29:00.000000000 +0200
> > +++ linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c	2010-09-09
> > 18:42:30.000000000 +0200
> > @@ -15,6 +15,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/io.h>
> >  #include <linux/spi/spi.h>
> > +#include <linux/dma-mapping.h>
> >
> >  #include <mach/hardware.h>
> >  #include <asm/mach/map.h>
> > @@ -25,6 +26,7 @@
> >  #include <mach/gpio.h>
> >  #include <plat/mmc.h>
> >  #include <plat/omap7xx.h>
> > +#include <mach/camera.h>
>
> You might want to try to group headers according to their location, i.e.,
> put <mach/...> higher - together with <mach/gpio.h>, also it helps to have
> headers alphabetically ordered.

Yes, I will, thank you.

> > 
> > /*-----------------------------------------------------------------------
> >--*/
> >
> > @@ -191,6 +193,47 @@ static inline void omap_init_spi100k(voi
> >  }
> >  #endif
> >
> > +
> > +#define OMAP1_CAMERA_BASE	0xfffb6800
> > +
> > +static struct resource omap1_camera_resources[] = {
> > +	[0] = {
> > +		.start	= OMAP1_CAMERA_BASE,
> > +		.end	= OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1,
> > +		.flags	= IORESOURCE_MEM,
> > +	},
> > +	[1] = {
> > +		.start	= INT_CAMERA,
> > +		.flags	= IORESOURCE_IRQ,
> > +	},
> > +};
> > +
> > +static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32);
> > +
> > +static struct platform_device omap1_camera_device = {
> > +	.name		= "omap1-camera",
> > +	.id		= 0, /* This is used to put cameras on this interface */
> > +	.dev		= {
> > +		.dma_mask		= &omap1_camera_dma_mask,
> > +		.coherent_dma_mask	= DMA_BIT_MASK(32),
> > +	},
> > +	.num_resources	= ARRAY_SIZE(omap1_camera_resources),
> > +	.resource	= omap1_camera_resources,
> > +};
> > +
> > +void __init omap1_set_camera_info(struct omap1_cam_platform_data *info)
> > +{
> > +	struct platform_device *dev = &omap1_camera_device;
> > +	int ret;
> > +
> > +	dev->dev.platform_data = info;
> > +
> > +	ret = platform_device_register(dev);
> > +	if (ret)
> > +		dev_err(&dev->dev, "unable to register device: %d\n", ret);
> > +}
> > +
> > +
> > 
> > /*-----------------------------------------------------------------------
> >--*/
> >
> >  static inline void omap_init_sti(void) {}
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> > ---
> > linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-0
> >3 22:34:03.000000000 +0200
> > +++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09
> > 18:42:30.000000000 +0200
> > @@ -0,0 +1,8 @@
> > +#ifndef __ASM_ARCH_CAMERA_H_
> > +#define __ASM_ARCH_CAMERA_H_
> > +
> > +#include <media/omap1_camera.h>
> > +
> > +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
>
> function declarations don't need an "extern" - something I've also been
> doing needlessly for a few years...

Good to know. I'll drop it.

Tony,
Any comments from you before I submit a hopefully final version?

Thanks,
Janusz

> > +
> > +#endif /* __ASM_ARCH_CAMERA_H_ */
>
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



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

* Re: [PATCH v2 2/6] OMAP1: Add support for SoC camera interface
@ 2010-09-22 18:20       ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-22 18:20 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software,
	linux-omap, Linux Media Mailing List

Wednesday 22 September 2010 08:53:19 Guennadi Liakhovetski napisał(a):
> That's up to the platform maintainer to review / apply this one, but if
> you like, a couple of nit-picks from me:

Guennadi,
Thanks for also looking at this!

> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > This patch adds support for SoC camera interface to OMAP1 devices.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > For successfull compilation, requires a header file provided by PATCH 1/6
> > from this series, "SoC Camera: add driver for OMAP1 camera interface".
> >
> > Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > ---
> > v1->v2 changes:
> > - no functional changes,
> > - refreshed against linux-2.6.36-rc3
> >
> >
> >  arch/arm/mach-omap1/devices.c             |   43
> > ++++++++++++++++++++++++++++++
> >  arch/arm/mach-omap1/include/mach/camera.h |    8 +++++
> >  2 files changed, 51 insertions(+)
> >
> >
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c
> > linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c
> > --- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/devices.c	2010-09-03
> > 22:29:00.000000000 +0200
> > +++ linux-2.6.36-rc3/arch/arm/mach-omap1/devices.c	2010-09-09
> > 18:42:30.000000000 +0200
> > @@ -15,6 +15,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/io.h>
> >  #include <linux/spi/spi.h>
> > +#include <linux/dma-mapping.h>
> >
> >  #include <mach/hardware.h>
> >  #include <asm/mach/map.h>
> > @@ -25,6 +26,7 @@
> >  #include <mach/gpio.h>
> >  #include <plat/mmc.h>
> >  #include <plat/omap7xx.h>
> > +#include <mach/camera.h>
>
> You might want to try to group headers according to their location, i.e.,
> put <mach/...> higher - together with <mach/gpio.h>, also it helps to have
> headers alphabetically ordered.

Yes, I will, thank you.

> > 
> > /*-----------------------------------------------------------------------
> >--*/
> >
> > @@ -191,6 +193,47 @@ static inline void omap_init_spi100k(voi
> >  }
> >  #endif
> >
> > +
> > +#define OMAP1_CAMERA_BASE	0xfffb6800
> > +
> > +static struct resource omap1_camera_resources[] = {
> > +	[0] = {
> > +		.start	= OMAP1_CAMERA_BASE,
> > +		.end	= OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1,
> > +		.flags	= IORESOURCE_MEM,
> > +	},
> > +	[1] = {
> > +		.start	= INT_CAMERA,
> > +		.flags	= IORESOURCE_IRQ,
> > +	},
> > +};
> > +
> > +static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32);
> > +
> > +static struct platform_device omap1_camera_device = {
> > +	.name		= "omap1-camera",
> > +	.id		= 0, /* This is used to put cameras on this interface */
> > +	.dev		= {
> > +		.dma_mask		= &omap1_camera_dma_mask,
> > +		.coherent_dma_mask	= DMA_BIT_MASK(32),
> > +	},
> > +	.num_resources	= ARRAY_SIZE(omap1_camera_resources),
> > +	.resource	= omap1_camera_resources,
> > +};
> > +
> > +void __init omap1_set_camera_info(struct omap1_cam_platform_data *info)
> > +{
> > +	struct platform_device *dev = &omap1_camera_device;
> > +	int ret;
> > +
> > +	dev->dev.platform_data = info;
> > +
> > +	ret = platform_device_register(dev);
> > +	if (ret)
> > +		dev_err(&dev->dev, "unable to register device: %d\n", ret);
> > +}
> > +
> > +
> > 
> > /*-----------------------------------------------------------------------
> >--*/
> >
> >  static inline void omap_init_sti(void) {}
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> > ---
> > linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-0
> >3 22:34:03.000000000 +0200
> > +++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09
> > 18:42:30.000000000 +0200
> > @@ -0,0 +1,8 @@
> > +#ifndef __ASM_ARCH_CAMERA_H_
> > +#define __ASM_ARCH_CAMERA_H_
> > +
> > +#include <media/omap1_camera.h>
> > +
> > +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
>
> function declarations don't need an "extern" - something I've also been
> doing needlessly for a few years...

Good to know. I'll drop it.

Tony,
Any comments from you before I submit a hopefully final version?

Thanks,
Janusz

> > +
> > +#endif /* __ASM_ARCH_CAMERA_H_ */
>
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



_______________________________________________
e3-hacking mailing list
e3-hacking@earth.li
http://www.earth.li/cgi-bin/mailman/listinfo/e3-hacking

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-22  9:12   ` Guennadi Liakhovetski
@ 2010-09-22 18:23     ` Janusz Krzysztofik
  2010-09-23 16:06       ` Guennadi Liakhovetski
  0 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-22 18:23 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał(a):
> Ok, just a couple more comments, all looking quite good so far, if we get
> a new version soon enough, we still might manage it for 2.6.37
>
> On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
>
> [snip]
>
> > +/* write a register */
> > +static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
> > +{
> > +	int ret;
> > +	unsigned char data[2] = { reg, val };
> > +	struct i2c_msg msg = {
> > +		.addr	= client->addr,
> > +		.flags	= 0,
> > +		.len	= 2,
> > +		.buf	= data,
> > +	};
> > +
> > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > +	msleep_interruptible(1);
>
> Why do you want _interruptible here? Firstly it's just 1ms, secondly -
> why?...

My bad. I didn't verified what a real difference between msleep() and 
msleep_interruptible() is, only found that msleep_interruptible(1) makes 
checkpatch.pl more happy than msleep(1), sorry.

What I can be sure is that a short delay is required here, otherwise the 
driver doesn't work correctly. To prevent the checkpatch.pl from complying 
against msleep(1), I think I could just replace it with msleep(20). What do 
you think?

> > +
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
> > +		return ret;
> > +	}
> > +	return 0;
> > +}
>
> ...
>
> > +/* set the format we will capture in */
> > +static int ov6650_s_fmt(struct v4l2_subdev *sd,
> > +			struct v4l2_mbus_framefmt *mf)
> > +{
> > +	struct i2c_client *client = sd->priv;
> > +	struct soc_camera_device *icd	= client->dev.platform_data;
> > +	struct soc_camera_sense *sense = icd->sense;
> > +	struct ov6650 *priv = to_ov6650(client);
> > +	enum v4l2_mbus_pixelcode code = mf->code;
> > +	unsigned long pclk;
> > +	u8 coma_set = 0, coma_mask = 0, coml_set = 0, coml_mask = 0;
> > +	u8 clkrc, clkrc_div;
> > +	int ret;
> > +
> > +	/* select color matrix configuration for given color encoding */
> > +	switch (code) {
> > +	case V4L2_MBUS_FMT_GREY8_1X8:
> > +		dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
> > +		coma_set |= COMA_BW;
> > +		coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
> > +		break;
> > +	case V4L2_MBUS_FMT_YUYV8_2X8:
> > +		dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
> > +		coma_set |= COMA_WORD_SWAP;
> > +		coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
> > +		break;
> > +	case V4L2_MBUS_FMT_YVYU8_2X8:
> > +		dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
> > +		coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
> > +				COMA_BYTE_SWAP;
> > +		break;
> > +	case V4L2_MBUS_FMT_UYVY8_2X8:
> > +		dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
> > +		if (mf->width == W_CIF) {
> > +			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
> > +			coma_mask |= COMA_RGB | COMA_BW;
> > +		} else {
> > +			coma_set |= COMA_BYTE_SWAP;
> > +			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
> > +		}
> > +		break;
> > +	case V4L2_MBUS_FMT_VYUY8_2X8:
> > +		dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
> > +		if (mf->width == W_CIF) {
> > +			coma_set |= COMA_BYTE_SWAP;
> > +			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
> > +		} else {
> > +			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
> > +			coma_mask |= COMA_RGB | COMA_BW;
> > +		}
> > +		break;
> > +	case V4L2_MBUS_FMT_SBGGR8_1X8:
> > +		dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
> > +		coma_set |= COMA_RAW_RGB | COMA_RGB;
> > +		coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
> > +		break;
> > +	default:
> > +		dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
> > +		return -EINVAL;
> > +	}
> > +	priv->code = code;
> > +
> > +	if ((code == V4L2_MBUS_FMT_GREY8_1X8) ||
> > +			(code == V4L2_MBUS_FMT_SBGGR8_1X8)) {
>
> Superfluous parenthesis

Will be dropped.

> > +		coml_mask |= COML_ONE_CHANNEL;
> > +		priv->pclk_max = 4000000;
> > +	} else {
> > +		coml_set |= COML_ONE_CHANNEL;
> > +		priv->pclk_max = 8000000;
> > +	}
>
> coml_mask and coml_set are only set here and only used once below, so,
> dropping initialisation to 0 in variable definitions and just doing
>
> +	if (code == V4L2_MBUS_FMT_GREY8_1X8 ||
> +			code == V4L2_MBUS_FMT_SBGGR8_1X8) {
> +		coml_mask = COML_ONE_CHANNEL;
> +		coml_set = 0;
> +		priv->pclk_max = 4000000;
> +	} else {
> +		coml_mask = 0;
> +		coml_set = COML_ONE_CHANNEL;
> +		priv->pclk_max = 8000000;
> +	}
>
> would work too.

OK, I'll use your prefered pattern.

> > +
> > +	if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
> > +		priv->colorspace = V4L2_COLORSPACE_SRGB;
> > +	else
> > +		priv->colorspace = V4L2_COLORSPACE_JPEG;
> > +
> > +	/*
> > +	 * Select register configuration for given resolution.
> > +	 * To be replaced with a common function that does it, once available.
> > +	 */
> > +	ov6650_res_roundup(&mf->width, &mf->height);
> > +
> > +	switch (mf->width) {
> > +	case W_QCIF:
> > +		dev_dbg(&client->dev, "resolution QCIF\n");
> > +		priv->qcif = 1;
> > +		coma_set |= COMA_QCIF;
> > +		priv->pclk_max /= 2;
> > +		break;
> > +	case W_CIF:
> > +		dev_dbg(&client->dev, "resolution CIF\n");
> > +		priv->qcif = 0;
> > +		coma_mask |= COMA_QCIF;
> > +		break;
> > +	default:
> > +		dev_err(&client->dev, "unspported resolution!\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
> > +	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
> > +	priv->rect.width  = mf->width;
> > +	priv->rect.height = mf->height;
>
> Sorry, don't understand. The sensor can do both - cropping per HSTRT,
> HSTOP, VSTRT and VSTOP and scaling per COMA_CIF / COMA_QCIF, right? 

Right.

> But 
> which of them is stored in your priv->rect? Is this the input window
> (cropping) or the output one (scaling)? 

I'm not sure how I can follow your input/output concept here.
Default (reset) values of HSTRT, HSTOP, VSTRT and VSTOP registers are the same 
for both CIF and QCIF, giving a 176x144 picture area in both cases. Than, 
when in CIF (reset default) mode, which actual size is double of that 
(352x288), I scale them by 2 when converting to priv->rect elements.

> You overwrite it in .s_fmt and 
> .s_crop...

I added the priv->rect to be returned by g_crop() instead of recalculating it 
from the register values. Then, I think I have to overwrite it on every 
geometry change, whether s_crop or s_fmt caused. Am I missing something?

> > +
> > +	if (priv->timeperframe.numerator && priv->timeperframe.denominator)
> > +		pclk = priv->pclk_max * priv->timeperframe.denominator /
> > +				(FRAME_RATE_MAX * priv->timeperframe.numerator);
> > +	else
> > +		pclk = priv->pclk_max;
> > +
> > +	if (sense) {
> > +		if (sense->master_clock == 8000000) {
> > +			dev_dbg(&client->dev, "8MHz input clock\n");
> > +			clkrc = CLKRC_6MHz;
> > +		} else if (sense->master_clock == 12000000) {
> > +			dev_dbg(&client->dev, "12MHz input clock\n");
> > +			clkrc = CLKRC_12MHz;
> > +		} else if (sense->master_clock == 16000000) {
> > +			dev_dbg(&client->dev, "16MHz input clock\n");
> > +			clkrc = CLKRC_16MHz;
> > +		} else if (sense->master_clock == 24000000) {
> > +			dev_dbg(&client->dev, "24MHz input clock\n");
> > +			clkrc = CLKRC_24MHz;
> > +		} else {
> > +			dev_err(&client->dev,
> > +				"unspported input clock, check platform data\n");
> > +			return -EINVAL;
> > +		}
> > +		priv->pclk_limit = sense->pixel_clock_max;
> > +		if (priv->pclk_limit && (priv->pclk_limit < pclk))
>
> Don't think the compiler would complain without the internal parenthesis
> here?

OK, I'll drop them.

Thanks,
Janusz

> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-22  6:08       ` Guennadi Liakhovetski
@ 2010-09-22 23:31         ` hermann pitton
  0 siblings, 0 replies; 48+ messages in thread
From: hermann pitton @ 2010-09-22 23:31 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Janusz Krzysztofik, Linux Media Mailing List, linux-omap,
	Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software


Am Mittwoch, den 22.09.2010, 08:08 +0200 schrieb Guennadi Liakhovetski:
> On Wed, 22 Sep 2010, hermann pitton wrote:
> 
> > Am Mittwoch, den 22.09.2010, 01:23 +0200 schrieb Guennadi Liakhovetski:
> > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > 
> > > > This is a V4L2 driver for TI OMAP1 SoC camera interface.
> > 
> > [snip]
> > 
> > > > +
> > > > +	} else {
> > > > +		dev_warn(dev, "%s: unhandled camera interrupt, status == "
> > > > +				"0x%0x\n", __func__, it_status);
> > > 
> > > Please, don't split strings
> > 
> > sorry for any OT interference.
> > 
> > But, are there any new rules out?
> > 
> > Maybe I missed them.
> > 
> > Either way, the above was forced during the last three years.
> > 
> > Not at all ?
> 
> No. Splitting print strings has always been discouraged, because it makes 
> tracking back kernel logs difficult. The reason for this has been the 80 
> character line length limit, which has now been effectively lifted. I'm 
> sure you can find enough links on the internet for any of these topics.
> 
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/

Guennadi,

thanks for the update!

If somebody ever would like to waste time on it, lots of patches and
whole drivers have been forced into this limitations for strings too.

In fact, fixing only a few lines, including the offset, you almost
always did hit it.

I'm pleased to hear now, that this problem never did exist ;))

Cheers,
Hermann








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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-22 18:13       ` Janusz Krzysztofik
@ 2010-09-23 13:33         ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-23 13:33 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:

> Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > +
> > > +	vb = &buf->vb;
> > > +	if (waitqueue_active(&vb->done)) {
> > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > +			/*
> > > +			 * No next buffer has been entered into the DMA
> > > +			 * programming register set on time, so best we can do
> > > +			 * is stopping the capture before last DMA block,
> > > +			 * whether our CONTIG mode whole buffer or its last
> > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > +			 */
> >
> > Hm, why do you think it's a good idea? This specific buffer completed
> > successfully, but you want to fail it just because the next buffer is
> > missing? Any specific reason for this? 
> 
> Maybe my comment is not clear enough, but the below suspend_capture() doesn't 
> indicate any failure on a frame just captured. It only prevents the frame 
> from being overwritten by the already autoreinitialized DMA engine, pointing 
> back to the same buffer once again.
> 
> > Besides, you seem to also be 
> > considering the possibility of your ->ready == NULL, but the queue
> > non-empty, in which case you just take the next buffer from the queue and
> > continue with it. Why error out in this case? 
> 
> pcdev->ready == NULL means no buffer was available when it was time to put it 
> into the DMA programming register set.

But how? Buffers are added onto the list in omap1_videobuf_queue() under 
spin_lock_irqsave(); and there you also check ->ready and fill it in. In 
your completion you set ->ready = NULL, but then also call 
prepare_next_vb() to get the next buffer from the list - if there are any, 
so, how can it be NULL with a non-empty list?

> As a result, a next DMA transfer has 
> just been autoreinitialized with the same buffer parameters as before. To 
> protect the buffer from being overwriten unintentionally, we have to stop the 
> DMA transfer as soon as possible, hopefully before the sensor starts sending 
> out next frame data.
> 
> If a new buffer has been queued meanwhile, best we can do is stopping 
> everything, programming the DMA with the new buffer, and setting up for a new 
> transfer hardware auto startup on nearest frame start, be it the next one if 
> we are lucky enough, or one after the next if we are too slow.
> 
> > And even if also the queue 
> > is empty - still not sure, why.
> 
> I hope the above explanation clarifies why.
> 
> I'll try to rework the above comment to be more clear, OK? Any hints?

> > > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > > 22:34:02.000000000 +0200 +++
> > > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > > +/*
> > > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > > + *
> > > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > > + *
> > > + * 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.
> > > + */
> > > +
> > > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > > +#define __MEDIA_OMAP1_CAMERA_H_
> > > +
> > > +#include <linux/bitops.h>
> > > +
> > > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > > +
> > > +enum omap1_cam_vb_mode {
> > > +	CONTIG = 0,
> > > +	SG,
> > > +};
> >
> > See above - are these needed here?
> >
> > > +
> > > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
> >
> > ditto
> 
> I moved them both over to the header file because I was using the 
> OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in order 
> to calculate the buffer size when calling the then NAKed 
> dma_preallocate_coherent_memory(). Now I could put them back into the driver 
> code, but if we ever get back to the concept of preallocating a contignuos 
> piece of memory from the platform init code, we might need them back here, so 
> maybe I should rather keep them, only rename the two enum values using a 
> distinct name space. What do you think is better for now?

Yeah, up to you, I'd say, but if you decide to keep them in the header, 
please, use a namespace.

I'm satisfied with your answers to the rest of my questions / comments:)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
@ 2010-09-23 13:33         ` Guennadi Liakhovetski
  0 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-23 13:33 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:

> Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > +
> > > +	vb = &buf->vb;
> > > +	if (waitqueue_active(&vb->done)) {
> > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > +			/*
> > > +			 * No next buffer has been entered into the DMA
> > > +			 * programming register set on time, so best we can do
> > > +			 * is stopping the capture before last DMA block,
> > > +			 * whether our CONTIG mode whole buffer or its last
> > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > +			 */
> >
> > Hm, why do you think it's a good idea? This specific buffer completed
> > successfully, but you want to fail it just because the next buffer is
> > missing? Any specific reason for this? 
> 
> Maybe my comment is not clear enough, but the below suspend_capture() doesn't 
> indicate any failure on a frame just captured. It only prevents the frame 
> from being overwritten by the already autoreinitialized DMA engine, pointing 
> back to the same buffer once again.
> 
> > Besides, you seem to also be 
> > considering the possibility of your ->ready == NULL, but the queue
> > non-empty, in which case you just take the next buffer from the queue and
> > continue with it. Why error out in this case? 
> 
> pcdev->ready == NULL means no buffer was available when it was time to put it 
> into the DMA programming register set.

But how? Buffers are added onto the list in omap1_videobuf_queue() under 
spin_lock_irqsave(); and there you also check ->ready and fill it in. In 
your completion you set ->ready = NULL, but then also call 
prepare_next_vb() to get the next buffer from the list - if there are any, 
so, how can it be NULL with a non-empty list?

> As a result, a next DMA transfer has 
> just been autoreinitialized with the same buffer parameters as before. To 
> protect the buffer from being overwriten unintentionally, we have to stop the 
> DMA transfer as soon as possible, hopefully before the sensor starts sending 
> out next frame data.
> 
> If a new buffer has been queued meanwhile, best we can do is stopping 
> everything, programming the DMA with the new buffer, and setting up for a new 
> transfer hardware auto startup on nearest frame start, be it the next one if 
> we are lucky enough, or one after the next if we are too slow.
> 
> > And even if also the queue 
> > is empty - still not sure, why.
> 
> I hope the above explanation clarifies why.
> 
> I'll try to rework the above comment to be more clear, OK? Any hints?

> > > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > > 22:34:02.000000000 +0200 +++
> > > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > > +/*
> > > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > > + *
> > > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > > + *
> > > + * 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.
> > > + */
> > > +
> > > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > > +#define __MEDIA_OMAP1_CAMERA_H_
> > > +
> > > +#include <linux/bitops.h>
> > > +
> > > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > > +
> > > +enum omap1_cam_vb_mode {
> > > +	CONTIG = 0,
> > > +	SG,
> > > +};
> >
> > See above - are these needed here?
> >
> > > +
> > > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
> >
> > ditto
> 
> I moved them both over to the header file because I was using the 
> OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in order 
> to calculate the buffer size when calling the then NAKed 
> dma_preallocate_coherent_memory(). Now I could put them back into the driver 
> code, but if we ever get back to the concept of preallocating a contignuos 
> piece of memory from the platform init code, we might need them back here, so 
> maybe I should rather keep them, only rename the two enum values using a 
> distinct name space. What do you think is better for now?

Yeah, up to you, I'd say, but if you decide to keep them in the header, 
please, use a namespace.

I'm satisfied with your answers to the rest of my questions / comments:)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-23 13:33         ` Guennadi Liakhovetski
@ 2010-09-23 14:51           ` Janusz Krzysztofik
  -1 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-23 14:51 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Thursday 23 September 2010 15:33:54 Guennadi Liakhovetski napisał(a):
> On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > > +
> > > > +	vb = &buf->vb;
> > > > +	if (waitqueue_active(&vb->done)) {
> > > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > > +			/*
> > > > +			 * No next buffer has been entered into the DMA
> > > > +			 * programming register set on time, so best we can do
> > > > +			 * is stopping the capture before last DMA block,
> > > > +			 * whether our CONTIG mode whole buffer or its last
> > > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > > +			 */
> > >
> > > Hm, why do you think it's a good idea? This specific buffer completed
> > > successfully, but you want to fail it just because the next buffer is
> > > missing? Any specific reason for this?
> >
> > Maybe my comment is not clear enough, but the below suspend_capture()
> > doesn't indicate any failure on a frame just captured. It only prevents
> > the frame from being overwritten by the already autoreinitialized DMA
> > engine, pointing back to the same buffer once again.
> >
> > > Besides, you seem to also be
> > > considering the possibility of your ->ready == NULL, but the queue
> > > non-empty, in which case you just take the next buffer from the queue
> > > and continue with it. Why error out in this case?
> >
> > pcdev->ready == NULL means no buffer was available when it was time to
> > put it into the DMA programming register set.
>
> But how? Buffers are added onto the list in omap1_videobuf_queue() under
> spin_lock_irqsave(); and there you also check ->ready and fill it in. 

Guennadi,
Yes, but only if pcdev->active is NULL, ie. both DMA and FIFO are idle, never 
if active:

+	list_add_tail(&vb->queue, &pcdev->capture);
+	vb->state = VIDEOBUF_QUEUED;
+
+	if (pcdev->active)
+		return;

Since the transfer of the DMA programming register set content to the DMA 
working register set is done automatically by the DMA hardware, this can 
pretty well happen while I keep the lock here, so I can't be sure if it's not 
too late for entering new data into the programming register set. Then, I 
decided that this operation should be done only just after the DMA interrupt 
occured, ie. the current DMA programming register set content has just been 
used and can be overwriten.

I'll emphasize the above return; with a comment.

> In 
> your completion you set ->ready = NULL, but then also call
> prepare_next_vb() to get the next buffer from the list - if there are any,
> so, how can it be NULL with a non-empty list?

It happens after the above mentioned prepare_next_vb() gets nothing from an 
empty queue, so nothing is entered into the DMA programming register set, 
only the last, just activated, buffer is processed, then 
omap1_videobuf_queue() puts a new buffer into the queue while the active 
buffer is still filled in, and finally the DMA ISR is called on this last 
active buffer completion.

I hope this helps.

> > As a result, a next DMA transfer has
> > just been autoreinitialized with the same buffer parameters as before. To
> > protect the buffer from being overwriten unintentionally, we have to stop
> > the DMA transfer as soon as possible, hopefully before the sensor starts
> > sending out next frame data.
> >
> > If a new buffer has been queued meanwhile, best we can do is stopping
> > everything, programming the DMA with the new buffer, and setting up for a
> > new transfer hardware auto startup on nearest frame start, be it the next
> > one if we are lucky enough, or one after the next if we are too slow.
> >
> > > And even if also the queue
> > > is empty - still not sure, why.
> >
> > I hope the above explanation clarifies why.
> >
> > I'll try to rework the above comment to be more clear, OK? Any hints?
> >
> > > > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > > > 22:34:02.000000000 +0200 +++
> > > > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > > > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > > > +/*
> > > > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > > > + *
> > > > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > > > + *
> > > > + * 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.
> > > > + */
> > > > +
> > > > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > > > +#define __MEDIA_OMAP1_CAMERA_H_
> > > > +
> > > > +#include <linux/bitops.h>
> > > > +
> > > > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > > > +
> > > > +enum omap1_cam_vb_mode {
> > > > +	CONTIG = 0,
> > > > +	SG,
> > > > +};
> > >
> > > See above - are these needed here?
> > >
> > > > +
> > > > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
> > >
> > > ditto
> >
> > I moved them both over to the header file because I was using the
> > OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in
> > order to calculate the buffer size when calling the then NAKed
> > dma_preallocate_coherent_memory(). Now I could put them back into the
> > driver code, but if we ever get back to the concept of preallocating a
> > contignuos piece of memory from the platform init code, we might need
> > them back here, so maybe I should rather keep them, only rename the two
> > enum values using a distinct name space. What do you think is better for
> > now?
>
> Yeah, up to you, I'd say, but if you decide to keep them in the header,
> please, use a namespace.

OK, I'll use a namespace then.

> I'm satisfied with your answers to the rest of my questions / comments:)

Glad to hear :)

Thanks,
Janusz

> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
@ 2010-09-23 14:51           ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-23 14:51 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

Thursday 23 September 2010 15:33:54 Guennadi Liakhovetski napisał(a):
> On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > > +
> > > > +	vb = &buf->vb;
> > > > +	if (waitqueue_active(&vb->done)) {
> > > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > > +			/*
> > > > +			 * No next buffer has been entered into the DMA
> > > > +			 * programming register set on time, so best we can do
> > > > +			 * is stopping the capture before last DMA block,
> > > > +			 * whether our CONTIG mode whole buffer or its last
> > > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > > +			 */
> > >
> > > Hm, why do you think it's a good idea? This specific buffer completed
> > > successfully, but you want to fail it just because the next buffer is
> > > missing? Any specific reason for this?
> >
> > Maybe my comment is not clear enough, but the below suspend_capture()
> > doesn't indicate any failure on a frame just captured. It only prevents
> > the frame from being overwritten by the already autoreinitialized DMA
> > engine, pointing back to the same buffer once again.
> >
> > > Besides, you seem to also be
> > > considering the possibility of your ->ready == NULL, but the queue
> > > non-empty, in which case you just take the next buffer from the queue
> > > and continue with it. Why error out in this case?
> >
> > pcdev->ready == NULL means no buffer was available when it was time to
> > put it into the DMA programming register set.
>
> But how? Buffers are added onto the list in omap1_videobuf_queue() under
> spin_lock_irqsave(); and there you also check ->ready and fill it in. 

Guennadi,
Yes, but only if pcdev->active is NULL, ie. both DMA and FIFO are idle, never 
if active:

+	list_add_tail(&vb->queue, &pcdev->capture);
+	vb->state = VIDEOBUF_QUEUED;
+
+	if (pcdev->active)
+		return;

Since the transfer of the DMA programming register set content to the DMA 
working register set is done automatically by the DMA hardware, this can 
pretty well happen while I keep the lock here, so I can't be sure if it's not 
too late for entering new data into the programming register set. Then, I 
decided that this operation should be done only just after the DMA interrupt 
occured, ie. the current DMA programming register set content has just been 
used and can be overwriten.

I'll emphasize the above return; with a comment.

> In 
> your completion you set ->ready = NULL, but then also call
> prepare_next_vb() to get the next buffer from the list - if there are any,
> so, how can it be NULL with a non-empty list?

It happens after the above mentioned prepare_next_vb() gets nothing from an 
empty queue, so nothing is entered into the DMA programming register set, 
only the last, just activated, buffer is processed, then 
omap1_videobuf_queue() puts a new buffer into the queue while the active 
buffer is still filled in, and finally the DMA ISR is called on this last 
active buffer completion.

I hope this helps.

> > As a result, a next DMA transfer has
> > just been autoreinitialized with the same buffer parameters as before. To
> > protect the buffer from being overwriten unintentionally, we have to stop
> > the DMA transfer as soon as possible, hopefully before the sensor starts
> > sending out next frame data.
> >
> > If a new buffer has been queued meanwhile, best we can do is stopping
> > everything, programming the DMA with the new buffer, and setting up for a
> > new transfer hardware auto startup on nearest frame start, be it the next
> > one if we are lucky enough, or one after the next if we are too slow.
> >
> > > And even if also the queue
> > > is empty - still not sure, why.
> >
> > I hope the above explanation clarifies why.
> >
> > I'll try to rework the above comment to be more clear, OK? Any hints?
> >
> > > > linux-2.6.36-rc3.orig/include/media/omap1_camera.h	2010-09-03
> > > > 22:34:02.000000000 +0200 +++
> > > > linux-2.6.36-rc3/include/media/omap1_camera.h	2010-09-08
> > > > 23:41:12.000000000 +0200 @@ -0,0 +1,35 @@
> > > > +/*
> > > > + * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
> > > > + *
> > > > + * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
> > > > + *
> > > > + * 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.
> > > > + */
> > > > +
> > > > +#ifndef __MEDIA_OMAP1_CAMERA_H_
> > > > +#define __MEDIA_OMAP1_CAMERA_H_
> > > > +
> > > > +#include <linux/bitops.h>
> > > > +
> > > > +#define OMAP1_CAMERA_IOSIZE		0x1c
> > > > +
> > > > +enum omap1_cam_vb_mode {
> > > > +	CONTIG = 0,
> > > > +	SG,
> > > > +};
> > >
> > > See above - are these needed here?
> > >
> > > > +
> > > > +#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == CONTIG ? 3 : 2)
> > >
> > > ditto
> >
> > I moved them both over to the header file because I was using the
> > OMAP1_CAMERA_MIN_BUF_COUNT(CONTIG) macro once from the platform code in
> > order to calculate the buffer size when calling the then NAKed
> > dma_preallocate_coherent_memory(). Now I could put them back into the
> > driver code, but if we ever get back to the concept of preallocating a
> > contignuos piece of memory from the platform init code, we might need
> > them back here, so maybe I should rather keep them, only rename the two
> > enum values using a distinct name space. What do you think is better for
> > now?
>
> Yeah, up to you, I'd say, but if you decide to keep them in the header,
> please, use a namespace.

OK, I'll use a namespace then.

> I'm satisfied with your answers to the rest of my questions / comments:)

Glad to hear :)

Thanks,
Janusz

> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-22 18:23     ` Janusz Krzysztofik
@ 2010-09-23 16:06       ` Guennadi Liakhovetski
  2010-09-23 22:45         ` Janusz Krzysztofik
  0 siblings, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-23 16:06 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:

> Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał(a):
> > Ok, just a couple more comments, all looking quite good so far, if we get
> > a new version soon enough, we still might manage it for 2.6.37
> >
> > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> >
> > [snip]
> >
> > > +/* write a register */
> > > +static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
> > > +{
> > > +	int ret;
> > > +	unsigned char data[2] = { reg, val };
> > > +	struct i2c_msg msg = {
> > > +		.addr	= client->addr,
> > > +		.flags	= 0,
> > > +		.len	= 2,
> > > +		.buf	= data,
> > > +	};
> > > +
> > > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > > +	msleep_interruptible(1);
> >
> > Why do you want _interruptible here? Firstly it's just 1ms, secondly -
> > why?...
> 
> My bad. I didn't verified what a real difference between msleep() and 
> msleep_interruptible() is, only found that msleep_interruptible(1) makes 
> checkpatch.pl more happy than msleep(1), sorry.
> 
> What I can be sure is that a short delay is required here, otherwise the 
> driver doesn't work correctly. To prevent the checkpatch.pl from complying 
> against msleep(1), I think I could just replace it with msleep(20). What do 
> you think?

oh, no, don't think replacing msleep(1) with msleep(20) just to silence a 
compiler warning is a god idea...;) Well, use a udelay(1000). Or maybe 
try, whether a udelay(100) suffices too.

> > > +	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
> > > +	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
> > > +	priv->rect.width  = mf->width;
> > > +	priv->rect.height = mf->height;
> >
> > Sorry, don't understand. The sensor can do both - cropping per HSTRT,
> > HSTOP, VSTRT and VSTOP and scaling per COMA_CIF / COMA_QCIF, right? 
> 
> Right.
> 
> > But 
> > which of them is stored in your priv->rect? Is this the input window
> > (cropping) or the output one (scaling)? 
> 
> I'm not sure how I can follow your input/output concept here.
> Default (reset) values of HSTRT, HSTOP, VSTRT and VSTOP registers are the same 
> for both CIF and QCIF, giving a 176x144 picture area in both cases. Than, 
> when in CIF (reset default) mode, which actual size is double of that 
> (352x288), I scale them by 2 when converting to priv->rect elements.
> 
> > You overwrite it in .s_fmt and 
> > .s_crop...
> 
> I added the priv->rect to be returned by g_crop() instead of recalculating it 
> from the register values. Then, I think I have to overwrite it on every 
> geometry change, whether s_crop or s_fmt caused. Am I missing something?

If I understand your sensor correctly, HSTRT etc. registers configure 
pretty much any (input) sensor window, whereas COMA and COML select 
whether to scale it to a CIF or to a QCIF output. So, these are two 
different things. Hence your ->rect can hold only one of the two - the 
sensor window or the output image. Since output image has only two options 
- CIF or QCIF, you don't need to store it in rect, you already have 
priv->qcif.


Oh, and one more thing - didn't notice before: in your cropcap you do

+	int shift = !priv->qcif;
...
+	a->bounds.left			= DEF_HSTRT << shift;
+	a->bounds.top			= DEF_VSTRT << shift;
+	a->bounds.width			= W_QCIF << shift;
+	a->bounds.height		= H_QCIF << shift;

Don't think this is right. cropcap shouldn't depend on any dynamic 
(configured by S_FMT) setting, it contains absolute limits of your 
hardware. I know I might have produced a couple of bad examples in the 
past - before I eventually settled with this definition... So, I think, 
it's best to put the full sensor resolution in cropcap.

...and, please, replace

+		priv->qcif = 1;
with
+		priv->qcif = true;
and
+		priv->qcif = 0;
with
+		priv->qcif = false;
in ov6650_s_fmt().

But I think your driver might have a problem with its cropping / scaling 
handling. Let's see if I understand it right:

1. as cropcap you currently return QCIF or CIF, depending on the last 
S_FMT, but let's say, you fix it to always return CIF.

2. in your s_fmt you accept only two output sizes: CIF or QCIF, that's ok, 
if that's all you can configure with your driver.

3. in s_crop you accept anything with left + width <= HSTOP and top + 
height <= VSTOP. This I don't understand. Your HSTOP and VSTOP are fixed 
values, based on QCIF plus some offsets. So, you would accept widths up to 
"a little larger than QCIF width" and similar heights. Then, without 
changing COMA and COML you assume, that the output size changed equally, 
because that's what you return in g_fmt.

Anyway, I think, there is some misunderstanding of the v4l2 cropping and 
scaling procedures. Please, have a look here: 
http://v4l2spec.bytesex.org/spec/x1904.htm. Do you agree, that what your 
driver is implementing doesn't reflect that correctly?;)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
  2010-09-23 14:51           ` Janusz Krzysztofik
@ 2010-09-23 16:10             ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-23 16:10 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Thu, 23 Sep 2010, Janusz Krzysztofik wrote:

> Thursday 23 September 2010 15:33:54 Guennadi Liakhovetski napisał(a):
> > On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > > Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > > > +
> > > > > +	vb = &buf->vb;
> > > > > +	if (waitqueue_active(&vb->done)) {
> > > > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > > > +			/*
> > > > > +			 * No next buffer has been entered into the DMA
> > > > > +			 * programming register set on time, so best we can do
> > > > > +			 * is stopping the capture before last DMA block,
> > > > > +			 * whether our CONTIG mode whole buffer or its last
> > > > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > > > +			 */
> > > >
> > > > Hm, why do you think it's a good idea? This specific buffer completed
> > > > successfully, but you want to fail it just because the next buffer is
> > > > missing? Any specific reason for this?
> > >
> > > Maybe my comment is not clear enough, but the below suspend_capture()
> > > doesn't indicate any failure on a frame just captured. It only prevents
> > > the frame from being overwritten by the already autoreinitialized DMA
> > > engine, pointing back to the same buffer once again.
> > >
> > > > Besides, you seem to also be
> > > > considering the possibility of your ->ready == NULL, but the queue
> > > > non-empty, in which case you just take the next buffer from the queue
> > > > and continue with it. Why error out in this case?
> > >
> > > pcdev->ready == NULL means no buffer was available when it was time to
> > > put it into the DMA programming register set.
> >
> > But how? Buffers are added onto the list in omap1_videobuf_queue() under
> > spin_lock_irqsave(); and there you also check ->ready and fill it in. 
> 
> Guennadi,
> Yes, but only if pcdev->active is NULL, ie. both DMA and FIFO are idle, never 
> if active:
> 
> +	list_add_tail(&vb->queue, &pcdev->capture);
> +	vb->state = VIDEOBUF_QUEUED;
> +
> +	if (pcdev->active)
> +		return;
> 
> Since the transfer of the DMA programming register set content to the DMA 
> working register set is done automatically by the DMA hardware, this can 
> pretty well happen while I keep the lock here, so I can't be sure if it's not 
> too late for entering new data into the programming register set. Then, I 
> decided that this operation should be done only just after the DMA interrupt 
> occured, ie. the current DMA programming register set content has just been 
> used and can be overwriten.
> 
> I'll emphasize the above return; with a comment.

Ok

> > In 
> > your completion you set ->ready = NULL, but then also call
> > prepare_next_vb() to get the next buffer from the list - if there are any,
> > so, how can it be NULL with a non-empty list?
> 
> It happens after the above mentioned prepare_next_vb() gets nothing from an 
> empty queue, so nothing is entered into the DMA programming register set, 
> only the last, just activated, buffer is processed, then 
> omap1_videobuf_queue() puts a new buffer into the queue while the active 
> buffer is still filled in, and finally the DMA ISR is called on this last 
> active buffer completion.
> 
> I hope this helps.

Let's assume it does:) You seem to really understand how this is working 
and even be willing to document the driver, thus making it possibly the 
best documented soc-camera related piece of software;)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface
@ 2010-09-23 16:10             ` Guennadi Liakhovetski
  0 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-23 16:10 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List, linux-omap, Tony Lindgren,
	Discussion of the Amstrad E3 emailer hardware/software

On Thu, 23 Sep 2010, Janusz Krzysztofik wrote:

> Thursday 23 September 2010 15:33:54 Guennadi Liakhovetski napisał(a):
> > On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > > Wednesday 22 September 2010 01:23:22 Guennadi Liakhovetski napisał(a):
> > > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > > > +
> > > > > +	vb = &buf->vb;
> > > > > +	if (waitqueue_active(&vb->done)) {
> > > > > +		if (!pcdev->ready && result != VIDEOBUF_ERROR)
> > > > > +			/*
> > > > > +			 * No next buffer has been entered into the DMA
> > > > > +			 * programming register set on time, so best we can do
> > > > > +			 * is stopping the capture before last DMA block,
> > > > > +			 * whether our CONTIG mode whole buffer or its last
> > > > > +			 * sgbuf in SG mode, gets overwritten by next frame.
> > > > > +			 */
> > > >
> > > > Hm, why do you think it's a good idea? This specific buffer completed
> > > > successfully, but you want to fail it just because the next buffer is
> > > > missing? Any specific reason for this?
> > >
> > > Maybe my comment is not clear enough, but the below suspend_capture()
> > > doesn't indicate any failure on a frame just captured. It only prevents
> > > the frame from being overwritten by the already autoreinitialized DMA
> > > engine, pointing back to the same buffer once again.
> > >
> > > > Besides, you seem to also be
> > > > considering the possibility of your ->ready == NULL, but the queue
> > > > non-empty, in which case you just take the next buffer from the queue
> > > > and continue with it. Why error out in this case?
> > >
> > > pcdev->ready == NULL means no buffer was available when it was time to
> > > put it into the DMA programming register set.
> >
> > But how? Buffers are added onto the list in omap1_videobuf_queue() under
> > spin_lock_irqsave(); and there you also check ->ready and fill it in. 
> 
> Guennadi,
> Yes, but only if pcdev->active is NULL, ie. both DMA and FIFO are idle, never 
> if active:
> 
> +	list_add_tail(&vb->queue, &pcdev->capture);
> +	vb->state = VIDEOBUF_QUEUED;
> +
> +	if (pcdev->active)
> +		return;
> 
> Since the transfer of the DMA programming register set content to the DMA 
> working register set is done automatically by the DMA hardware, this can 
> pretty well happen while I keep the lock here, so I can't be sure if it's not 
> too late for entering new data into the programming register set. Then, I 
> decided that this operation should be done only just after the DMA interrupt 
> occured, ie. the current DMA programming register set content has just been 
> used and can be overwriten.
> 
> I'll emphasize the above return; with a comment.

Ok

> > In 
> > your completion you set ->ready = NULL, but then also call
> > prepare_next_vb() to get the next buffer from the list - if there are any,
> > so, how can it be NULL with a non-empty list?
> 
> It happens after the above mentioned prepare_next_vb() gets nothing from an 
> empty queue, so nothing is entered into the DMA programming register set, 
> only the last, just activated, buffer is processed, then 
> omap1_videobuf_queue() puts a new buffer into the queue while the active 
> buffer is still filled in, and finally the DMA ISR is called on this last 
> active buffer completion.
> 
> I hope this helps.

Let's assume it does:) You seem to really understand how this is working 
and even be willing to document the driver, thus making it possibly the 
best documented soc-camera related piece of software;)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-23 16:06       ` Guennadi Liakhovetski
@ 2010-09-23 22:45         ` Janusz Krzysztofik
  2010-09-24  6:52           ` Guennadi Liakhovetski
  0 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-23 22:45 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

Thursday 23 September 2010 18:06:15 Guennadi Liakhovetski napisał(a):
> On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał(a):
> > > Ok, just a couple more comments, all looking quite good so far, if we
> > > get a new version soon enough, we still might manage it for 2.6.37
> > >
> > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > >
> > > [snip]
> > >
> > > > +/* write a register */
> > > > +static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8
> > > > val) +{
> > > > +	int ret;
> > > > +	unsigned char data[2] = { reg, val };
> > > > +	struct i2c_msg msg = {
> > > > +		.addr	= client->addr,
> > > > +		.flags	= 0,
> > > > +		.len	= 2,
> > > > +		.buf	= data,
> > > > +	};
> > > > +
> > > > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > > > +	msleep_interruptible(1);
> > >
> > > Why do you want _interruptible here? Firstly it's just 1ms, secondly -
> > > why?...
> >
> > My bad. I didn't verified what a real difference between msleep() and
> > msleep_interruptible() is, only found that msleep_interruptible(1) makes
> > checkpatch.pl more happy than msleep(1), sorry.
> >
> > What I can be sure is that a short delay is required here, otherwise the
> > driver doesn't work correctly. To prevent the checkpatch.pl from
> > complying against msleep(1), I think I could just replace it with
> > msleep(20). What do you think?
>
> oh, no, don't think replacing msleep(1) with msleep(20) just to silence a
> compiler warning is a god idea...;) Well, use a udelay(1000). Or maybe
> try, whether a udelay(100) suffices too.

OK. Thanks for the hints.

> > > > +	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
> > > > +	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
> > > > +	priv->rect.width  = mf->width;
> > > > +	priv->rect.height = mf->height;
> > >
> > > Sorry, don't understand. The sensor can do both - cropping per HSTRT,
> > > HSTOP, VSTRT and VSTOP and scaling per COMA_CIF / COMA_QCIF, right?
> >
> > Right.
> >
> > > But
> > > which of them is stored in your priv->rect? Is this the input window
> > > (cropping) or the output one (scaling)?
> >
> > I'm not sure how I can follow your input/output concept here.
> > Default (reset) values of HSTRT, HSTOP, VSTRT and VSTOP registers are the
> > same for both CIF and QCIF, giving a 176x144 picture area in both cases.
> > Than, when in CIF (reset default) mode, which actual size is double of
> > that (352x288), I scale them by 2 when converting to priv->rect elements.
> >
> > > You overwrite it in .s_fmt and
> > > .s_crop...
> >
> > I added the priv->rect to be returned by g_crop() instead of
> > recalculating it from the register values. Then, I think I have to
> > overwrite it on every geometry change, whether s_crop or s_fmt caused. Am
> > I missing something?
>
> If I understand your sensor correctly, HSTRT etc. registers configure
> pretty much any (input) sensor window, 

Let's say, not exceeding CIF geometry (352x288).

> whereas COMA and COML select 
> whether to scale it to a CIF or to a QCIF output. 

I think the answer is: not exactly. AFAICT, the COMA_QCIF bit selects whether 
to scale it down by 2 (QCIF selection) or not (CIF selection).

> So, these are two 
> different things. Hence your ->rect can hold only one of the two - the
> sensor window or the output image. Since output image has only two options
> - CIF or QCIF, you don't need to store it in rect, you already have
> priv->qcif.

With the above reservation - yes, I could use priv->qcif to scale priv->rect 
down by 2 or not in g_fmt.

> Oh, and one more thing - didn't notice before: in your cropcap you do
>
> +	int shift = !priv->qcif;
> ...
> +	a->bounds.left			= DEF_HSTRT << shift;
> +	a->bounds.top			= DEF_VSTRT << shift;
> +	a->bounds.width			= W_QCIF << shift;
> +	a->bounds.height		= H_QCIF << shift;
>
> Don't think this is right. cropcap shouldn't depend on any dynamic
> (configured by S_FMT) setting, it contains absolute limits of your
> hardware. I know I might have produced a couple of bad examples in the
> past - before I eventually settled with this definition... So, I think,
> it's best to put the full sensor resolution in cropcap.

OK.

> ...and, please, replace
>
> +		priv->qcif = 1;
> with
> +		priv->qcif = true;
> and
> +		priv->qcif = 0;
> with
> +		priv->qcif = false;
> in ov6650_s_fmt().

Sure.

> But I think your driver might have a problem with its cropping / scaling
> handling. Let's see if I understand it right:
>
> 1. as cropcap you currently return QCIF or CIF, depending on the last
> S_FMT, 

Yes.

BTW, my S_FMT always calls ov6650_reset(), which resets the current crop to 
defaults. This behaviour doesn't follow the requirement of this operation 
being done only once, when the driver is first loaded, but not later. Should 
I restore the last crop after every reset? If yes, what is the purpose of 
defrect if applied only at driver first load?

> but let's say, you fix it to always return CIF. 

OK.

> 2. in your s_fmt you accept only two output sizes: CIF or QCIF, that's ok,
> if that's all you can configure with your driver.

Not any longer :). I'm able to configure using current crop geometry only, 
either unscaled or scaled down by 2. I'm not able to configure neither exact 
CIF nor QCIF if my current crop window doesn't match, unless I'm allowed to 
change the crop from here.

> 3. in s_crop you accept anything with left + width <= HSTOP and top +
> height <= VSTOP. This I don't understand. Your HSTOP and VSTOP are fixed
> values, based on QCIF plus some offsets. So, you would accept widths up to
> "a little larger than QCIF width" and similar heights. 

Do you mean I should also verify if (rect->left >= DEF_HSTRT) and (rect->top 
>= DEF_VSTRT)? I should probably.

> Then, without 
> changing COMA and COML you assume, that the output size changed equally,
> because that's what you return in g_fmt.

I think I've verified, to the extent possible using v4l2-dbg, that it works 
like this. If I change the input height by overwriting VSTOP with a slightly 
higher value, the output seems to change proportionally and starts rolling 
down, since the host is not updated to handle the so changed frame size.

> Anyway, I think, there is some misunderstanding of the v4l2 cropping and
> scaling procedures. Please, have a look here:
> http://v4l2spec.bytesex.org/spec/x1904.htm. Do you agree, that what your
> driver is implementing doesn't reflect that correctly?;)

Yes, I do. And I think that that the reason of this misunderstanding is my 
sensor just not matching the v4l2 model with its limited, probably uncommon 
scaling capability, or me still not being able to map the sensor to the v4l2 
model correctly. What do you think?

Thanks,
Janusz

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-11  1:27 ` [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera Janusz Krzysztofik
@ 2010-09-23 23:14   ` Tony Lindgren
  2010-09-23 23:26     ` Tony Lindgren
  0 siblings, 1 reply; 48+ messages in thread
From: Tony Lindgren @ 2010-09-23 23:14 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-media, Guennadi Liakhovetski, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> This patch adds configuration data and initialization code required for camera 
> support to the Amstrad Delta board.
> 
> Three devices are declared: SoC camera, OMAP1 camera interface and OV6650 
> sensor.
> 
> Default 12MHz clock has been selected for driving the sensor. Pixel clock has 
> been limited to get reasonable frame rates, not exceeding the board 
> capabilities. Since both devices (interface and sensor) support both pixel 
> clock polarities, decision on polarity selection has been left to drivers.
> Interface GPIO line has been found not functional, thus not configured.
> 
> Created and tested against linux-2.6.36-rc3.
> 
> Works on top of previous patches from the series, at least 1/6, 2/6 and 3/6.

Queuing these last two patches of the series (5/6 and 6/6) for the upcoming
merge window.

Tony

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-11  1:34   ` [RESEND][PATCH " Janusz Krzysztofik
@ 2010-09-23 23:23     ` Tony Lindgren
  2010-09-23 23:44         ` Janusz Krzysztofik
  0 siblings, 1 reply; 48+ messages in thread
From: Tony Lindgren @ 2010-09-23 23:23 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-omap, linux-media, Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:26]:
> This patch adds support for SoC camera interface to OMAP1 devices.
> 
> Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> 
> For successfull compilation, requires a header file provided by PATCH 1/6 from 
> this series, "SoC Camera: add driver for OMAP1 camera interface".

<snip>

> diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h 
> linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> --- linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-03 22:34:03.000000000 +0200
> +++ linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09 18:42:30.000000000 +0200
> @@ -0,0 +1,8 @@
> +#ifndef __ASM_ARCH_CAMERA_H_
> +#define __ASM_ARCH_CAMERA_H_
> +
> +#include <media/omap1_camera.h>
> +
> +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
> +
> +#endif /* __ASM_ARCH_CAMERA_H_ */

Care to refresh this patch so it does not include media/omap1_camera.h?

That way things keep building if I merge this one along with the omap
patches and the drivers/media patches can get merged their via media.

I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
someplace like that?

Regards,

Tony

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-23 23:14   ` Tony Lindgren
@ 2010-09-23 23:26     ` Tony Lindgren
  2010-09-24  0:00         ` Janusz Krzysztofik
  0 siblings, 1 reply; 48+ messages in thread
From: Tony Lindgren @ 2010-09-23 23:26 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-media, Guennadi Liakhovetski, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

* Tony Lindgren <tony@atomide.com> [100923 16:06]:
> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > This patch adds configuration data and initialization code required for camera 
> > support to the Amstrad Delta board.
> > 
> > Three devices are declared: SoC camera, OMAP1 camera interface and OV6650 
> > sensor.
> > 
> > Default 12MHz clock has been selected for driving the sensor. Pixel clock has 
> > been limited to get reasonable frame rates, not exceeding the board 
> > capabilities. Since both devices (interface and sensor) support both pixel 
> > clock polarities, decision on polarity selection has been left to drivers.
> > Interface GPIO line has been found not functional, thus not configured.
> > 
> > Created and tested against linux-2.6.36-rc3.
> > 
> > Works on top of previous patches from the series, at least 1/6, 2/6 and 3/6.
> 
> Queuing these last two patches of the series (5/6 and 6/6) for the upcoming
> merge window.

BTW, these still depend on updated 2/6 to make compile happy.

Tony

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-23 23:23     ` Tony Lindgren
@ 2010-09-23 23:44         ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-23 23:44 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-media, Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:26]:
> > This patch adds support for SoC camera interface to OMAP1 devices.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > For successfull compilation, requires a header file provided by PATCH 1/6
> > from this series, "SoC Camera: add driver for OMAP1 camera interface".
>
> <snip>
>
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> > ---
> > linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-0
> >3 22:34:03.000000000 +0200 +++
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09
> > 18:42:30.000000000 +0200 @@ -0,0 +1,8 @@
> > +#ifndef __ASM_ARCH_CAMERA_H_
> > +#define __ASM_ARCH_CAMERA_H_
> > +
> > +#include <media/omap1_camera.h>
> > +
> > +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
> > +
> > +#endif /* __ASM_ARCH_CAMERA_H_ */
>
> Care to refresh this patch so it does not include media/omap1_camera.h?
>
> That way things keep building if I merge this one along with the omap
> patches and the drivers/media patches can get merged their via media.
>
> I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> someplace like that?

Tony,
Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when reserving 
space for register cache.

I think that I could just duplicate its definition in the devices.c for now, 
than clean things up with a folloup patch when both parts already get merged. 
Would this be acceptable?

Thanks,
Janusz

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

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
@ 2010-09-23 23:44         ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-23 23:44 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-media, Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:26]:
> > This patch adds support for SoC camera interface to OMAP1 devices.
> >
> > Created and tested against linux-2.6.36-rc3 on Amstrad Delta.
> >
> > For successfull compilation, requires a header file provided by PATCH 1/6
> > from this series, "SoC Camera: add driver for OMAP1 camera interface".
>
> <snip>
>
> > diff -upr linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h
> > ---
> > linux-2.6.36-rc3.orig/arch/arm/mach-omap1/include/mach/camera.h	2010-09-0
> >3 22:34:03.000000000 +0200 +++
> > linux-2.6.36-rc3/arch/arm/mach-omap1/include/mach/camera.h	2010-09-09
> > 18:42:30.000000000 +0200 @@ -0,0 +1,8 @@
> > +#ifndef __ASM_ARCH_CAMERA_H_
> > +#define __ASM_ARCH_CAMERA_H_
> > +
> > +#include <media/omap1_camera.h>
> > +
> > +extern void omap1_set_camera_info(struct omap1_cam_platform_data *);
> > +
> > +#endif /* __ASM_ARCH_CAMERA_H_ */
>
> Care to refresh this patch so it does not include media/omap1_camera.h?
>
> That way things keep building if I merge this one along with the omap
> patches and the drivers/media patches can get merged their via media.
>
> I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> someplace like that?

Tony,
Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when reserving 
space for register cache.

I think that I could just duplicate its definition in the devices.c for now, 
than clean things up with a folloup patch when both parts already get merged. 
Would this be acceptable?

Thanks,
Janusz

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

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-23 23:44         ` Janusz Krzysztofik
@ 2010-09-23 23:54           ` Tony Lindgren
  -1 siblings, 0 replies; 48+ messages in thread
From: Tony Lindgren @ 2010-09-23 23:54 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-omap, linux-media, Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:37]:
> Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> >
> > I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> > someplace like that?
> 
> Tony,
> Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when reserving 
> space for register cache.
> 
> I think that I could just duplicate its definition in the devices.c for now, 
> than clean things up with a folloup patch when both parts already get merged. 
> Would this be acceptable?

Yeah, that sounds good to me.

Tony

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
@ 2010-09-23 23:54           ` Tony Lindgren
  0 siblings, 0 replies; 48+ messages in thread
From: Tony Lindgren @ 2010-09-23 23:54 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-omap, linux-media, Guennadi Liakhovetski,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:37]:
> Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> >
> > I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> > someplace like that?
> 
> Tony,
> Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when reserving 
> space for register cache.
> 
> I think that I could just duplicate its definition in the devices.c for now, 
> than clean things up with a folloup patch when both parts already get merged. 
> Would this be acceptable?

Yeah, that sounds good to me.

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

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-23 23:26     ` Tony Lindgren
@ 2010-09-24  0:00         ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24  0:00 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-media, Guennadi Liakhovetski, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > This patch adds configuration data and initialization code required for
> > > camera support to the Amstrad Delta board.
> > >
> > > Three devices are declared: SoC camera, OMAP1 camera interface and
> > > OV6650 sensor.
> > >
> > > Default 12MHz clock has been selected for driving the sensor. Pixel
> > > clock has been limited to get reasonable frame rates, not exceeding the
> > > board capabilities. Since both devices (interface and sensor) support
> > > both pixel clock polarities, decision on polarity selection has been
> > > left to drivers. Interface GPIO line has been found not functional,
> > > thus not configured.
> > >
> > > Created and tested against linux-2.6.36-rc3.
> > >
> > > Works on top of previous patches from the series, at least 1/6, 2/6 and
> > > 3/6.
> >
> > Queuing these last two patches of the series (5/6 and 6/6) for the
> > upcoming merge window.
>
> BTW, these still depend on updated 2/6 to make compile happy.

Not so simple: still depends on struct omap1_cam_platform_data definition from 
<media/omap1_camera.h>, included from <mach/camera.h>. Are you ready to 
accept another temporary workaround?

Thanks,
Janusz


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



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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
@ 2010-09-24  0:00         ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24  0:00 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-media, Guennadi Liakhovetski, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > This patch adds configuration data and initialization code required for
> > > camera support to the Amstrad Delta board.
> > >
> > > Three devices are declared: SoC camera, OMAP1 camera interface and
> > > OV6650 sensor.
> > >
> > > Default 12MHz clock has been selected for driving the sensor. Pixel
> > > clock has been limited to get reasonable frame rates, not exceeding the
> > > board capabilities. Since both devices (interface and sensor) support
> > > both pixel clock polarities, decision on polarity selection has been
> > > left to drivers. Interface GPIO line has been found not functional,
> > > thus not configured.
> > >
> > > Created and tested against linux-2.6.36-rc3.
> > >
> > > Works on top of previous patches from the series, at least 1/6, 2/6 and
> > > 3/6.
> >
> > Queuing these last two patches of the series (5/6 and 6/6) for the
> > upcoming merge window.
>
> BTW, these still depend on updated 2/6 to make compile happy.

Not so simple: still depends on struct omap1_cam_platform_data definition from 
<media/omap1_camera.h>, included from <mach/camera.h>. Are you ready to 
accept another temporary workaround?

Thanks,
Janusz


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


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

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-24  0:00         ` Janusz Krzysztofik
  (?)
@ 2010-09-24  0:49         ` Tony Lindgren
  2010-09-24  6:57             ` Guennadi Liakhovetski
  -1 siblings, 1 reply; 48+ messages in thread
From: Tony Lindgren @ 2010-09-24  0:49 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: linux-media, Guennadi Liakhovetski, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:52]:
> Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> > * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > > This patch adds configuration data and initialization code required for
> > > > camera support to the Amstrad Delta board.
> > > >
> > > > Three devices are declared: SoC camera, OMAP1 camera interface and
> > > > OV6650 sensor.
> > > >
> > > > Default 12MHz clock has been selected for driving the sensor. Pixel
> > > > clock has been limited to get reasonable frame rates, not exceeding the
> > > > board capabilities. Since both devices (interface and sensor) support
> > > > both pixel clock polarities, decision on polarity selection has been
> > > > left to drivers. Interface GPIO line has been found not functional,
> > > > thus not configured.
> > > >
> > > > Created and tested against linux-2.6.36-rc3.
> > > >
> > > > Works on top of previous patches from the series, at least 1/6, 2/6 and
> > > > 3/6.
> > >
> > > Queuing these last two patches of the series (5/6 and 6/6) for the
> > > upcoming merge window.
> >
> > BTW, these still depend on updated 2/6 to make compile happy.
> 
> Not so simple: still depends on struct omap1_cam_platform_data definition from 
> <media/omap1_camera.h>, included from <mach/camera.h>. Are you ready to 
> accept another temporary workaround?

Heh I guess so. Or do you want to queue everything via linux-media?

Tony

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-23 22:45         ` Janusz Krzysztofik
@ 2010-09-24  6:52           ` Guennadi Liakhovetski
  2010-09-24 11:36             ` Janusz Krzysztofik
  0 siblings, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-24  6:52 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

On Fri, 24 Sep 2010, Janusz Krzysztofik wrote:

> Thursday 23 September 2010 18:06:15 Guennadi Liakhovetski napisał(a):
> > On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > > Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał(a):
> > > >
> > > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> > > > > +	priv->rect.left	  = DEF_HSTRT << !priv->qcif;
> > > > > +	priv->rect.top	  = DEF_VSTRT << !priv->qcif;
> > > > > +	priv->rect.width  = mf->width;
> > > > > +	priv->rect.height = mf->height;
> > > >
> > > > Sorry, don't understand. The sensor can do both - cropping per HSTRT,
> > > > HSTOP, VSTRT and VSTOP and scaling per COMA_CIF / COMA_QCIF, right?
> > >
> > > Right.
> > >
> > > > But
> > > > which of them is stored in your priv->rect? Is this the input window
> > > > (cropping) or the output one (scaling)?
> > >
> > > I'm not sure how I can follow your input/output concept here.
> > > Default (reset) values of HSTRT, HSTOP, VSTRT and VSTOP registers are the
> > > same for both CIF and QCIF, giving a 176x144 picture area in both cases.
> > > Than, when in CIF (reset default) mode, which actual size is double of
> > > that (352x288), I scale them by 2 when converting to priv->rect elements.
> > >
> > > > You overwrite it in .s_fmt and
> > > > .s_crop...
> > >
> > > I added the priv->rect to be returned by g_crop() instead of
> > > recalculating it from the register values. Then, I think I have to
> > > overwrite it on every geometry change, whether s_crop or s_fmt caused. Am
> > > I missing something?
> >
> > If I understand your sensor correctly, HSTRT etc. registers configure
> > pretty much any (input) sensor window, 
> 
> Let's say, not exceeding CIF geometry (352x288).

Yes, sorry, forgot to mention that.

> > whereas COMA and COML select 
> > whether to scale it to a CIF or to a QCIF output. 
> 
> I think the answer is: not exactly. AFAICT, the COMA_QCIF bit selects whether 
> to scale it down by 2 (QCIF selection) or not (CIF selection).

Ah! Ok, that it would be better to select different names for those bits.

> > So, these are two 
> > different things. Hence your ->rect can hold only one of the two - the
> > sensor window or the output image. Since output image has only two options
> > - CIF or QCIF, you don't need to store it in rect, you already have
> > priv->qcif.
> 
> With the above reservation - yes, I could use priv->qcif to scale priv->rect 
> down by 2 or not in g_fmt.

...and for the ->qcif member.

> > Oh, and one more thing - didn't notice before: in your cropcap you do
> >
> > +	int shift = !priv->qcif;
> > ...
> > +	a->bounds.left			= DEF_HSTRT << shift;
> > +	a->bounds.top			= DEF_VSTRT << shift;
> > +	a->bounds.width			= W_QCIF << shift;
> > +	a->bounds.height		= H_QCIF << shift;
> >
> > Don't think this is right. cropcap shouldn't depend on any dynamic
> > (configured by S_FMT) setting, it contains absolute limits of your
> > hardware. I know I might have produced a couple of bad examples in the
> > past - before I eventually settled with this definition... So, I think,
> > it's best to put the full sensor resolution in cropcap.
> 
> OK.
> 
> > ...and, please, replace
> >
> > +		priv->qcif = 1;
> > with
> > +		priv->qcif = true;
> > and
> > +		priv->qcif = 0;
> > with
> > +		priv->qcif = false;
> > in ov6650_s_fmt().
> 
> Sure.
> 
> > But I think your driver might have a problem with its cropping / scaling
> > handling. Let's see if I understand it right:
> >
> > 1. as cropcap you currently return QCIF or CIF, depending on the last
> > S_FMT, 
> 
> Yes.
> 
> BTW, my S_FMT always calls ov6650_reset(), which resets the current crop to 
> defaults.

Oh, does it mean all registers are reset to their defaults? That'd be not 
good - no v4l(2) ioctl, AFAIK, should affect parameters, not directly 
related to it. Even closing and reopening the video device node shouldn't 
reset values. So, maybe you should drop that reset completely.

> This behaviour doesn't follow the requirement of this operation 
> being done only once, when the driver is first loaded, but not later. Should 
> I restore the last crop after every reset? If yes, what is the purpose of 
> defrect if applied only at driver first load?

that's exactly the purpose, I think.

> > but let's say, you fix it to always return CIF. 
> 
> OK.
> 
> > 2. in your s_fmt you accept only two output sizes: CIF or QCIF, that's ok,
> > if that's all you can configure with your driver.
> 
> Not any longer :). I'm able to configure using current crop geometry only, 
> either unscaled or scaled down by 2. I'm not able to configure neither exact 
> CIF nor QCIF if my current crop window doesn't match, unless I'm allowed to 
> change the crop from here.

Hm, but in your s_fmt you do:

+	switch (mf->width) {
+	case W_QCIF:
+		dev_dbg(&client->dev, "resolution QCIF\n");
+		priv->qcif = 1;
+		coma_set |= COMA_QCIF;
+		priv->pclk_max /= 2;
+		break;
+	case W_CIF:
+		dev_dbg(&client->dev, "resolution CIF\n");
+		priv->qcif = 0;
+		coma_mask |= COMA_QCIF;
+		break;
+	default:
+		dev_err(&client->dev, "unspported resolution!\n");
+		return -EINVAL;
+	}

So, you accept only CIF or QCIF as your output window. Or do you mean a v3 
by your "not any longer?" And yes, you are allowed to change your input 
sensor window, if that lets you configure your output format more 
precisely. And v.v. The rule is - the most recent command wins.

> > 3. in s_crop you accept anything with left + width <= HSTOP and top +
> > height <= VSTOP. This I don't understand. Your HSTOP and VSTOP are fixed
> > values, based on QCIF plus some offsets. So, you would accept widths up to
> > "a little larger than QCIF width" and similar heights. 
> 
> Do you mean I should also verify if (rect->left >= DEF_HSTRT) and (rect->top 
> >= DEF_VSTRT)? I should probably.

Yes, if you cannot handle smaller values.

> > Then, without 
> > changing COMA and COML you assume, that the output size changed equally,
> > because that's what you return in g_fmt.
> 
> I think I've verified, to the extent possible using v4l2-dbg, that it works 
> like this. If I change the input height by overwriting VSTOP with a slightly 
> higher value, the output seems to change proportionally and starts rolling 
> down, since the host is not updated to handle the so changed frame size.

Yes, now that I know, that that flag is actually a scale-down-by-2 switch.

> > Anyway, I think, there is some misunderstanding of the v4l2 cropping and
> > scaling procedures. Please, have a look here:
> > http://v4l2spec.bytesex.org/spec/x1904.htm. Do you agree, that what your
> > driver is implementing doesn't reflect that correctly?;)
> 
> Yes, I do. And I think that that the reason of this misunderstanding is my 
> sensor just not matching the v4l2 model with its limited, probably uncommon 
> scaling capability, or me still not being able to map the sensor to the v4l2 
> model correctly. What do you think?

No, there's nothing wrong with your sensor:) So, what I would do is:

1. in your struct ov6650:

+	struct v4l2_rect	rect;		/* sensor cropping window */
+	bool			half_scale;	/* scale down output by 2 */

2. in s_crop verify left, width, top, height, program them into the chip 
and store in ->rect

3. in g_crop just return values from ->rect

4. in s_fmt you have to select an input rectangle, that would allow you to 
possibly exactly configure the requested output format. Say, if you have a 
320x240 cropping configured and you get an s_fmt request for 120x90. You 
can either set your input rectangle to 240x180 and scale it down by 2, or 
set the rectangle directly to 120x90. Obviously, it's better to use 
240x180 and scale down, because that's closer to the current cropping of 
320x240. So, in s_fmt you select a new input rectangle _closest_ to the 
currently configured one, that would allow you to configure the correct 
output format. Then you set your ->rect with the new values and your 
->half_scale

5. in g_fmt you return ->rect scaled with ->half_scale

Makes sense?

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-23 23:54           ` Tony Lindgren
  (?)
@ 2010-09-24  6:54           ` Guennadi Liakhovetski
  2010-09-24 10:28             ` Janusz Krzysztofik
  -1 siblings, 1 reply; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-24  6:54 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Janusz Krzysztofik, linux-omap, Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

On Thu, 23 Sep 2010, Tony Lindgren wrote:

> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:37]:
> > Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> > >
> > > I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> > > someplace like that?
> > 
> > Tony,
> > Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when reserving 
> > space for register cache.
> > 
> > I think that I could just duplicate its definition in the devices.c for now, 
> > than clean things up with a folloup patch when both parts already get merged. 
> > Would this be acceptable?
> 
> Yeah, that sounds good to me.

...better yet put a zero-length cache array at the end of your struct 
omap1_cam_dev and allocate it dynamically, calculated from the resource 
size.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-24  0:49         ` Tony Lindgren
@ 2010-09-24  6:57             ` Guennadi Liakhovetski
  0 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-24  6:57 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Janusz Krzysztofik, Linux Media Mailing List, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

On Thu, 23 Sep 2010, Tony Lindgren wrote:

> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:52]:
> > Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> > > * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > > > This patch adds configuration data and initialization code required for
> > > > > camera support to the Amstrad Delta board.
> > > > >
> > > > > Three devices are declared: SoC camera, OMAP1 camera interface and
> > > > > OV6650 sensor.
> > > > >
> > > > > Default 12MHz clock has been selected for driving the sensor. Pixel
> > > > > clock has been limited to get reasonable frame rates, not exceeding the
> > > > > board capabilities. Since both devices (interface and sensor) support
> > > > > both pixel clock polarities, decision on polarity selection has been
> > > > > left to drivers. Interface GPIO line has been found not functional,
> > > > > thus not configured.
> > > > >
> > > > > Created and tested against linux-2.6.36-rc3.
> > > > >
> > > > > Works on top of previous patches from the series, at least 1/6, 2/6 and
> > > > > 3/6.
> > > >
> > > > Queuing these last two patches of the series (5/6 and 6/6) for the
> > > > upcoming merge window.
> > >
> > > BTW, these still depend on updated 2/6 to make compile happy.
> > 
> > Not so simple: still depends on struct omap1_cam_platform_data definition from 
> > <media/omap1_camera.h>, included from <mach/camera.h>. Are you ready to 
> > accept another temporary workaround?
> 
> Heh I guess so. Or do you want to queue everything via linux-media?

Yes, we often have to select via which tree to go, then the maintainer of 
the other tree just acks the patches. Sometimes we push them via different 
trees and try to enforce a specific merge order...

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
@ 2010-09-24  6:57             ` Guennadi Liakhovetski
  0 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-24  6:57 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Janusz Krzysztofik, Linux Media Mailing List, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

On Thu, 23 Sep 2010, Tony Lindgren wrote:

> * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:52]:
> > Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> > > * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > > > This patch adds configuration data and initialization code required for
> > > > > camera support to the Amstrad Delta board.
> > > > >
> > > > > Three devices are declared: SoC camera, OMAP1 camera interface and
> > > > > OV6650 sensor.
> > > > >
> > > > > Default 12MHz clock has been selected for driving the sensor. Pixel
> > > > > clock has been limited to get reasonable frame rates, not exceeding the
> > > > > board capabilities. Since both devices (interface and sensor) support
> > > > > both pixel clock polarities, decision on polarity selection has been
> > > > > left to drivers. Interface GPIO line has been found not functional,
> > > > > thus not configured.
> > > > >
> > > > > Created and tested against linux-2.6.36-rc3.
> > > > >
> > > > > Works on top of previous patches from the series, at least 1/6, 2/6 and
> > > > > 3/6.
> > > >
> > > > Queuing these last two patches of the series (5/6 and 6/6) for the
> > > > upcoming merge window.
> > >
> > > BTW, these still depend on updated 2/6 to make compile happy.
> > 
> > Not so simple: still depends on struct omap1_cam_platform_data definition from 
> > <media/omap1_camera.h>, included from <mach/camera.h>. Are you ready to 
> > accept another temporary workaround?
> 
> Heh I guess so. Or do you want to queue everything via linux-media?

Yes, we often have to select via which tree to go, then the maintainer of 
the other tree just acks the patches. Sometimes we push them via different 
trees and try to enforce a specific merge order...

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-24  6:54           ` Guennadi Liakhovetski
@ 2010-09-24 10:28             ` Janusz Krzysztofik
  2010-09-24 17:43               ` Tony Lindgren
  0 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24 10:28 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Tony Lindgren, linux-omap, Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 08:54:20 Guennadi Liakhovetski napisał(a):
> On Thu, 23 Sep 2010, Tony Lindgren wrote:
> > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:37]:
> > > Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> > > > I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> > > > someplace like that?
> > >
> > > Tony,
> > > Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when
> > > reserving space for register cache.
> > >
> > > I think that I could just duplicate its definition in the devices.c for
> > > now, than clean things up with a folloup patch when both parts already
> > > get merged. Would this be acceptable?
> >
> > Yeah, that sounds good to me.
>
> ...better yet put a zero-length cache array at the end of your struct
> omap1_cam_dev and allocate it dynamically, calculated from the resource
> size.

Guennadi,
Yes, this seems the best solution, thank you.

Tony,
You'll soon get it as you ask: <media/camera.h> no longer included from 
<mach/camera.h>.

Thanks,
Janusz

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
  2010-09-24  6:57             ` Guennadi Liakhovetski
@ 2010-09-24 11:08               ` Janusz Krzysztofik
  -1 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24 11:08 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Tony Lindgren, Linux Media Mailing List, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 08:57:06 Guennadi Liakhovetski napisał(a):
> On Thu, 23 Sep 2010, Tony Lindgren wrote:
> > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:52]:
> > > Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> > > > * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > > > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > > > > This patch adds configuration data and initialization code
> > > > > > required for camera support to the Amstrad Delta board.
> > > > > >
> > > > > > Three devices are declared: SoC camera, OMAP1 camera interface
> > > > > > and OV6650 sensor.
> > > > > >
> > > > > > Default 12MHz clock has been selected for driving the sensor.
> > > > > > Pixel clock has been limited to get reasonable frame rates, not
> > > > > > exceeding the board capabilities. Since both devices (interface
> > > > > > and sensor) support both pixel clock polarities, decision on
> > > > > > polarity selection has been left to drivers. Interface GPIO line
> > > > > > has been found not functional, thus not configured.
> > > > > >
> > > > > > Created and tested against linux-2.6.36-rc3.
> > > > > >
> > > > > > Works on top of previous patches from the series, at least 1/6,
> > > > > > 2/6 and 3/6.
> > > > >
> > > > > Queuing these last two patches of the series (5/6 and 6/6) for the
> > > > > upcoming merge window.
> > > >
> > > > BTW, these still depend on updated 2/6 to make compile happy.
> > >
> > > Not so simple: still depends on struct omap1_cam_platform_data
> > > definition from <media/omap1_camera.h>, included from <mach/camera.h>.
> > > Are you ready to accept another temporary workaround?
> >
> > Heh I guess so. Or do you want to queue everything via linux-media?

AFAIK we can expect my arch/arm/mach-omap1/devices.c changes already resulting 
in a confilct with some ASoC OMAP related changes going via the sound tree, 
so the 2/6 should be better queued via the OMAP tree for Tony to keep control 
over it, with the rest of the seriers going either way.

> Yes, we often have to select via which tree to go, then the maintainer of
> the other tree just acks the patches. Sometimes we push them via different
> trees and try to enforce a specific merge order...

What about

+ void omap1_set_camera_info(struct omap1_cam_platform_data *);

put temporarily into to the arch/arm/mach-omap1/board-ams-delta.c instead of 
including <mach/camera.h>, that could be replaced with <media/omap1_camera.h> 
then? May sound better than redefining struct omap1_cam_platform_data there, 
and should be safe to push everything except 2/6 via the media tree.

Then, replace the above hack back with #include <mach/camera.h> as a fix after 
both are merged.

Thanks,
Janusz

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

* Re: [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera
@ 2010-09-24 11:08               ` Janusz Krzysztofik
  0 siblings, 0 replies; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24 11:08 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Tony Lindgren, Linux Media Mailing List, linux-omap,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 08:57:06 Guennadi Liakhovetski napisał(a):
> On Thu, 23 Sep 2010, Tony Lindgren wrote:
> > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:52]:
> > > Friday 24 September 2010 01:26:17 Tony Lindgren napisał(a):
> > > > * Tony Lindgren <tony@atomide.com> [100923 16:06]:
> > > > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100910 18:20]:
> > > > > > This patch adds configuration data and initialization code
> > > > > > required for camera support to the Amstrad Delta board.
> > > > > >
> > > > > > Three devices are declared: SoC camera, OMAP1 camera interface
> > > > > > and OV6650 sensor.
> > > > > >
> > > > > > Default 12MHz clock has been selected for driving the sensor.
> > > > > > Pixel clock has been limited to get reasonable frame rates, not
> > > > > > exceeding the board capabilities. Since both devices (interface
> > > > > > and sensor) support both pixel clock polarities, decision on
> > > > > > polarity selection has been left to drivers. Interface GPIO line
> > > > > > has been found not functional, thus not configured.
> > > > > >
> > > > > > Created and tested against linux-2.6.36-rc3.
> > > > > >
> > > > > > Works on top of previous patches from the series, at least 1/6,
> > > > > > 2/6 and 3/6.
> > > > >
> > > > > Queuing these last two patches of the series (5/6 and 6/6) for the
> > > > > upcoming merge window.
> > > >
> > > > BTW, these still depend on updated 2/6 to make compile happy.
> > >
> > > Not so simple: still depends on struct omap1_cam_platform_data
> > > definition from <media/omap1_camera.h>, included from <mach/camera.h>.
> > > Are you ready to accept another temporary workaround?
> >
> > Heh I guess so. Or do you want to queue everything via linux-media?

AFAIK we can expect my arch/arm/mach-omap1/devices.c changes already resulting 
in a confilct with some ASoC OMAP related changes going via the sound tree, 
so the 2/6 should be better queued via the OMAP tree for Tony to keep control 
over it, with the rest of the seriers going either way.

> Yes, we often have to select via which tree to go, then the maintainer of
> the other tree just acks the patches. Sometimes we push them via different
> trees and try to enforce a specific merge order...

What about

+ void omap1_set_camera_info(struct omap1_cam_platform_data *);

put temporarily into to the arch/arm/mach-omap1/board-ams-delta.c instead of 
including <mach/camera.h>, that could be replaced with <media/omap1_camera.h> 
then? May sound better than redefining struct omap1_cam_platform_data there, 
and should be safe to push everything except 2/6 via the media tree.

Then, replace the above hack back with #include <mach/camera.h> as a fix after 
both are merged.

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

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-24  6:52           ` Guennadi Liakhovetski
@ 2010-09-24 11:36             ` Janusz Krzysztofik
  2010-09-24 11:51               ` Guennadi Liakhovetski
  0 siblings, 1 reply; 48+ messages in thread
From: Janusz Krzysztofik @ 2010-09-24 11:36 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

Friday 24 September 2010 08:52:32 Guennadi Liakhovetski napisał(a):
> On Fri, 24 Sep 2010, Janusz Krzysztofik wrote:
> > Thursday 23 September 2010 18:06:15 Guennadi Liakhovetski napisał(a):
> > > On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > > > Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał
(a):
> > > > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:

...
> > > whereas COMA and COML select
> > > whether to scale it to a CIF or to a QCIF output.
> >
> > I think the answer is: not exactly. AFAICT, the COMA_QCIF bit selects
> > whether to scale it down by 2 (QCIF selection) or not (CIF selection).
>
> Ah! Ok, that it would be better to select different names for those bits.

I was trying to keep all names more or less consistent with the wording used 
in the sensor documentation (which doesn't follow the v4l2 specification 
unfortunately :). In this case we have:

COMA  Common Control A
	...
	Bit[5]: Output format – resolution
		0: CIF (352 x 288)
		1: QCIF (176 x 144)

So I could rename it to something like COMA_OUTFMT or COMA_RESOLUTION. Which 
one sounds better for you?

...
> > > But I think your driver might have a problem with its cropping /
> > > scaling handling. Let's see if I understand it right:
> > >
> > > 1. as cropcap you currently return QCIF or CIF, depending on the last
> > > S_FMT,
> >
> > Yes.
> >
> > BTW, my S_FMT always calls ov6650_reset(), which resets the current crop
> > to defaults.
>
> Oh, does it mean all registers are reset to their defaults? That'd be not
> good - no v4l(2) ioctl, AFAIK, should affect parameters, not directly
> related to it. Even closing and reopening the video device node shouldn't
> reset values. So, maybe you should drop that reset completely.

Shouldn't I rather move it over into the ov6650_video_probe()?

...
> > > 2. in your s_fmt you accept only two output sizes: CIF or QCIF, that's
> > > ok, if that's all you can configure with your driver.
> >
> > Not any longer :). I'm able to configure using current crop geometry
> > only, either unscaled or scaled down by 2. I'm not able to configure
> > neither exact CIF nor QCIF if my current crop window doesn't match,
> > unless I'm allowed to change the crop from here.
>
> Hm, but in your s_fmt you do:
>
> +	switch (mf->width) {
> +	case W_QCIF:
> +		dev_dbg(&client->dev, "resolution QCIF\n");
> +		priv->qcif = 1;
> +		coma_set |= COMA_QCIF;
> +		priv->pclk_max /= 2;
> +		break;
> +	case W_CIF:
> +		dev_dbg(&client->dev, "resolution CIF\n");
> +		priv->qcif = 0;
> +		coma_mask |= COMA_QCIF;
> +		break;
> +	default:
> +		dev_err(&client->dev, "unspported resolution!\n");
> +		return -EINVAL;
> +	}
>
> So, you accept only CIF or QCIF as your output window. Or do you mean a v3
> by your "not any longer?" 

Exactly!

> And yes, you are allowed to change your input 
> sensor window, if that lets you configure your output format more
> precisely. And v.v. The rule is - the most recent command wins.

I see.

...
> No, there's nothing wrong with your sensor:) So, what I would do is:
>
> 1. in your struct ov6650:
>
> +	struct v4l2_rect	rect;		/* sensor cropping window */
> +	bool			half_scale;	/* scale down output by 2 */
>
> 2. in s_crop verify left, width, top, height, program them into the chip
> and store in ->rect
>
> 3. in g_crop just return values from ->rect
>
> 4. in s_fmt you have to select an input rectangle, that would allow you to
> possibly exactly configure the requested output format. Say, if you have a
> 320x240 cropping configured and you get an s_fmt request for 120x90. You
> can either set your input rectangle to 240x180 and scale it down by 2, or
> set the rectangle directly to 120x90. Obviously, it's better to use
> 240x180 and scale down, because that's closer to the current cropping of
> 320x240. So, in s_fmt you select a new input rectangle _closest_ to the
> currently configured one, that would allow you to configure the correct
> output format. Then you set your ->rect with the new values and your
> ->half_scale
>
> 5. in g_fmt you return ->rect scaled with ->half_scale
>
> Makes sense?

Absolutely. Hope to submit v3 soon.

Thanks,
Janusz

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

* Re: [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor
  2010-09-24 11:36             ` Janusz Krzysztofik
@ 2010-09-24 11:51               ` Guennadi Liakhovetski
  0 siblings, 0 replies; 48+ messages in thread
From: Guennadi Liakhovetski @ 2010-09-24 11:51 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

On Fri, 24 Sep 2010, Janusz Krzysztofik wrote:

> Friday 24 September 2010 08:52:32 Guennadi Liakhovetski napisał(a):
> > On Fri, 24 Sep 2010, Janusz Krzysztofik wrote:
> > > Thursday 23 September 2010 18:06:15 Guennadi Liakhovetski napisał(a):
> > > > On Wed, 22 Sep 2010, Janusz Krzysztofik wrote:
> > > > > Wednesday 22 September 2010 11:12:46 Guennadi Liakhovetski napisał
> (a):
> > > > > > On Sat, 11 Sep 2010, Janusz Krzysztofik wrote:
> 
> ...
> > > > whereas COMA and COML select
> > > > whether to scale it to a CIF or to a QCIF output.
> > >
> > > I think the answer is: not exactly. AFAICT, the COMA_QCIF bit selects
> > > whether to scale it down by 2 (QCIF selection) or not (CIF selection).
> >
> > Ah! Ok, that it would be better to select different names for those bits.
> 
> I was trying to keep all names more or less consistent with the wording used 
> in the sensor documentation (which doesn't follow the v4l2 specification 
> unfortunately :). In this case we have:
> 
> COMA  Common Control A
> 	...
> 	Bit[5]: Output format – resolution
> 		0: CIF (352 x 288)
> 		1: QCIF (176 x 144)
> 
> So I could rename it to something like COMA_OUTFMT or COMA_RESOLUTION. Which 
> one sounds better for you?

ok, so, it means to which output window the _complete_ sensor area is 
mapped. And if you get a smaller sensor rectangle, you get a smaller 
output image, right? Ok, then you can just leave it as is.

> ...
> > > > But I think your driver might have a problem with its cropping /
> > > > scaling handling. Let's see if I understand it right:
> > > >
> > > > 1. as cropcap you currently return QCIF or CIF, depending on the last
> > > > S_FMT,
> > >
> > > Yes.
> > >
> > > BTW, my S_FMT always calls ov6650_reset(), which resets the current crop
> > > to defaults.
> >
> > Oh, does it mean all registers are reset to their defaults? That'd be not
> > good - no v4l(2) ioctl, AFAIK, should affect parameters, not directly
> > related to it. Even closing and reopening the video device node shouldn't
> > reset values. So, maybe you should drop that reset completely.
> 
> Shouldn't I rather move it over into the ov6650_video_probe()?

Good idea!:)

> ...
> > > > 2. in your s_fmt you accept only two output sizes: CIF or QCIF, that's
> > > > ok, if that's all you can configure with your driver.
> > >
> > > Not any longer :). I'm able to configure using current crop geometry
> > > only, either unscaled or scaled down by 2. I'm not able to configure
> > > neither exact CIF nor QCIF if my current crop window doesn't match,
> > > unless I'm allowed to change the crop from here.
> >
> > Hm, but in your s_fmt you do:
> >
> > +	switch (mf->width) {
> > +	case W_QCIF:
> > +		dev_dbg(&client->dev, "resolution QCIF\n");
> > +		priv->qcif = 1;
> > +		coma_set |= COMA_QCIF;
> > +		priv->pclk_max /= 2;
> > +		break;
> > +	case W_CIF:
> > +		dev_dbg(&client->dev, "resolution CIF\n");
> > +		priv->qcif = 0;
> > +		coma_mask |= COMA_QCIF;
> > +		break;
> > +	default:
> > +		dev_err(&client->dev, "unspported resolution!\n");
> > +		return -EINVAL;
> > +	}
> >
> > So, you accept only CIF or QCIF as your output window. Or do you mean a v3
> > by your "not any longer?" 
> 
> Exactly!
> 
> > And yes, you are allowed to change your input 
> > sensor window, if that lets you configure your output format more
> > precisely. And v.v. The rule is - the most recent command wins.
> 
> I see.
> 
> ...
> > No, there's nothing wrong with your sensor:) So, what I would do is:
> >
> > 1. in your struct ov6650:
> >
> > +	struct v4l2_rect	rect;		/* sensor cropping window */
> > +	bool			half_scale;	/* scale down output by 2 */
> >
> > 2. in s_crop verify left, width, top, height, program them into the chip
> > and store in ->rect
> >
> > 3. in g_crop just return values from ->rect
> >
> > 4. in s_fmt you have to select an input rectangle, that would allow you to
> > possibly exactly configure the requested output format. Say, if you have a
> > 320x240 cropping configured and you get an s_fmt request for 120x90. You
> > can either set your input rectangle to 240x180 and scale it down by 2, or
> > set the rectangle directly to 120x90. Obviously, it's better to use
> > 240x180 and scale down, because that's closer to the current cropping of
> > 320x240. So, in s_fmt you select a new input rectangle _closest_ to the
> > currently configured one, that would allow you to configure the correct
> > output format. Then you set your ->rect with the new values and your
> > ->half_scale
> >
> > 5. in g_fmt you return ->rect scaled with ->half_scale
> >
> > Makes sense?
> 
> Absolutely. Hope to submit v3 soon.

Good! Looking forward:)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [RESEND][PATCH v2 2/6] OMAP1: Add support for SoC camera interface
  2010-09-24 10:28             ` Janusz Krzysztofik
@ 2010-09-24 17:43               ` Tony Lindgren
  0 siblings, 0 replies; 48+ messages in thread
From: Tony Lindgren @ 2010-09-24 17:43 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Guennadi Liakhovetski, linux-omap, Linux Media Mailing List,
	Discussion of the Amstrad E3 emailer hardware/software

* Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100924 03:20]:
> Friday 24 September 2010 08:54:20 Guennadi Liakhovetski napisał(a):
> > On Thu, 23 Sep 2010, Tony Lindgren wrote:
> > > * Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> [100923 16:37]:
> > > > Friday 24 September 2010 01:23:10 Tony Lindgren napisał(a):
> > > > > I think you can just move the OMAP1_CAMERA_IOSIZE to the devices.c or
> > > > > someplace like that?
> > > >
> > > > Tony,
> > > > Not exactly. I use the OMAP1_CAMERA_IOSIZE inside the driver when
> > > > reserving space for register cache.
> > > >
> > > > I think that I could just duplicate its definition in the devices.c for
> > > > now, than clean things up with a folloup patch when both parts already
> > > > get merged. Would this be acceptable?
> > >
> > > Yeah, that sounds good to me.
> >
> > ...better yet put a zero-length cache array at the end of your struct
> > omap1_cam_dev and allocate it dynamically, calculated from the resource
> > size.
> 
> Guennadi,
> Yes, this seems the best solution, thank you.
> 
> Tony,
> You'll soon get it as you ask: <media/camera.h> no longer included from 
> <mach/camera.h>.

OK, sounds good to me.

Tony

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

end of thread, other threads:[~2010-09-24 17:43 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-11  1:17 [PATCH v2 0/6] Add camera support to the OMAP1 Amstrad Delta videophone Janusz Krzysztofik
2010-09-11  1:21 ` [PATCH v2 1/6] SoC Camera: add driver for OMAP1 camera interface Janusz Krzysztofik
2010-09-11  9:32   ` Janusz Krzysztofik
2010-09-21 23:23   ` Guennadi Liakhovetski
2010-09-22  0:10     ` hermann pitton
2010-09-22  6:08       ` Guennadi Liakhovetski
2010-09-22 23:31         ` hermann pitton
2010-09-22 18:13     ` Janusz Krzysztofik
2010-09-22 18:13       ` Janusz Krzysztofik
2010-09-23 13:33       ` Guennadi Liakhovetski
2010-09-23 13:33         ` Guennadi Liakhovetski
2010-09-23 14:51         ` Janusz Krzysztofik
2010-09-23 14:51           ` Janusz Krzysztofik
2010-09-23 16:10           ` Guennadi Liakhovetski
2010-09-23 16:10             ` Guennadi Liakhovetski
2010-09-11  1:23 ` [PATCH v2 2/6] OMAP1: Add support for SoC " Janusz Krzysztofik
2010-09-11  1:34   ` [RESEND][PATCH " Janusz Krzysztofik
2010-09-23 23:23     ` Tony Lindgren
2010-09-23 23:44       ` Janusz Krzysztofik
2010-09-23 23:44         ` Janusz Krzysztofik
2010-09-23 23:54         ` Tony Lindgren
2010-09-23 23:54           ` Tony Lindgren
2010-09-24  6:54           ` Guennadi Liakhovetski
2010-09-24 10:28             ` Janusz Krzysztofik
2010-09-24 17:43               ` Tony Lindgren
2010-09-22  6:53   ` [PATCH " Guennadi Liakhovetski
2010-09-22 18:20     ` Janusz Krzysztofik
2010-09-22 18:20       ` Janusz Krzysztofik
2010-09-11  1:25 ` [PATCH v2 3/6] SoC Camera: add driver for OV6650 sensor Janusz Krzysztofik
2010-09-22  9:12   ` Guennadi Liakhovetski
2010-09-22 18:23     ` Janusz Krzysztofik
2010-09-23 16:06       ` Guennadi Liakhovetski
2010-09-23 22:45         ` Janusz Krzysztofik
2010-09-24  6:52           ` Guennadi Liakhovetski
2010-09-24 11:36             ` Janusz Krzysztofik
2010-09-24 11:51               ` Guennadi Liakhovetski
2010-09-11  1:26 ` [PATCH v2 4/6] SoC Camera: add support for g_parm / s_parm operations Janusz Krzysztofik
2010-09-11  1:27 ` [PATCH v2 5/6] OMAP1: Amstrad Delta: add support for camera Janusz Krzysztofik
2010-09-23 23:14   ` Tony Lindgren
2010-09-23 23:26     ` Tony Lindgren
2010-09-24  0:00       ` Janusz Krzysztofik
2010-09-24  0:00         ` Janusz Krzysztofik
2010-09-24  0:49         ` Tony Lindgren
2010-09-24  6:57           ` Guennadi Liakhovetski
2010-09-24  6:57             ` Guennadi Liakhovetski
2010-09-24 11:08             ` Janusz Krzysztofik
2010-09-24 11:08               ` Janusz Krzysztofik
2010-09-11  1:28 ` [PATCH v2 6/6] OMAP1: Amstrad Delta: add camera controlled LEDS trigger Janusz Krzysztofik

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.