* [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, ®);
+ 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, ®);
+ ctrl->value = reg;
+ } else {
+ ctrl->value = priv->blue;
+ }
+ break;
+ case V4L2_CID_RED_BALANCE:
+ if (priv->awb) {
+ ret = ov6650_reg_read(client, REG_RED, ®);
+ 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, ®);
+ 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.