Friday 30 July 2010 13:07:42 Guennadi Liakhovetski napisaĆ(a):
Hi Janusz
Thanks once more for the patches, from your patches it is obvious, that you've spent quite a bit of time on them and you have not just copy-pasted various bits and pieces from other drivers, just filling your hardware details, but you also actually understand a lot of what is actually going on in there. So, I think, fixing remaining mostly minor issues shouldn't be too difficult for you either.
Hi Guennadi, Thanks for your review time. I only tried to get the driver really working, not only looking good ;).
On Sun, 18 Jul 2010, Janusz Krzysztofik wrote:
This is a V4L2 driver for TI OMAP1 SoC camera interface.
Two versions of the driver are provided, using either videobuf-dma-contig or videobuf-dma-sg. 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.
Hm, would it be difficult to first try contig, and if it fails - fall back to sg?
Hmm, let me think about it.
In its present state buffer management in your driver is pretty difficult to follow. At the VERY least you have to extensively document your buffer manipulations. Better yet clean it up. AFAIU, your "ready" pointer is pretty much unused in the contiguous case. Or you can easily get rid of it.
I was sure the "ready" pointer was essential for both paths. However, things tend to change as more and more optimizations / cleanups are applied, so I have to verify it again.
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.
You are right. My approach was probably more adequate at development time, when I was trying to aviod error-prone code duplication. As soon as both paths get stable, they can be split for better readability. I'll take care of this.
So, I will do a pretty formal review this time and wait for v2. And in v2 I'd like to at the very least see detailed buffer-management documentation, or - better yet - a rework with a clean separation.
OK.
...
--- linux-2.6.35-rc3.orig/include/media/omap1_camera.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.35-rc3/include/media/omap1_camera.h 2010-07-18 01:31:57.000000000 +0200 @@ -0,0 +1,16 @@
A copyright / licence header here, please
OK.
+#ifndef __MEDIA_OMAP1_CAMERA_H_ +#define __MEDIA_OMAP1_CAMERA_H_
+#define OMAP1_CAMERA_IOSIZE 0x1c
+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)
Then you need to #include <linux/bitops.h>
OK. Since I always tend to optimise out redundant includes of header files that are otherwise included indirectly, please verify if there are more of them that you would prefere included explicitly.
+#endif /* __MEDIA_OMAP1_CAMERA_H_ */ --- linux-2.6.35-rc3.orig/drivers/media/video/Kconfig 2010-06-26 15:55:29.000000000 +0200 +++ linux-2.6.35-rc3/drivers/media/video/Kconfig 2010-07-02 04:12:02.000000000 +0200 @@ -962,6 +962,20 @@ 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 if !VIDEO_OMAP1_SG
- ---help---
This is a v4l2 driver for the TI OMAP1 camera interface
+if VIDEO_OMAP1
Don't think you need this "if," the "depends on" below should suffice.
OK. My intention was to get it indented, and now I see it works like this even if not enclosed with if-endif. Will drop the if-endif if the compile time SG option survives.
+config VIDEO_OMAP1_SG
- bool "Scatter-gather mode"
- depends on VIDEO_OMAP1 && EXPERIMENTAL
- select VIDEOBUF_DMA_SG
+endif
...
+#ifndef CONFIG_VIDEO_OMAP1_SG +#include <media/videobuf-dma-contig.h> +#else +#include <media/videobuf-dma-sg.h> +#endif
I think, you can just include both headers.
OK.
...
+#define CAM_EXCLK_6MHz 6000000 +#define CAM_EXCLK_8MHz 8000000 +#define CAM_EXCLK_9_6MHz 9600000 +#define CAM_EXCLK_12MHz 12000000 +#define CAM_EXCLK_24MHz 24000000
Why do you need this? Your platform specifies the EXCLK frequency directly in kHz, then you multiplly it by 1000 to get pcdev->camexclk in Hz, then you compare it to these macros. Don't think you need them - just use numbers directly.
OK. My only reason for defines here was thees numbers being used more than once.
...
+/*
- Structures
- */
+/* buffer for one video frame */ +struct omap1_cam_buf {
- struct videobuf_buffer vb;
- enum v4l2_mbus_pixelcode code;
- int inwork;
+#ifdef CONFIG_VIDEO_OMAP1_SG
- struct scatterlist *sgbuf;
- int sgcount;
- int bytes_left;
- enum videobuf_state result;
+#endif +};
+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;
- spinlock_t lock;
- /* Pointers to DMA buffers */
- struct omap1_cam_buf *active;
- struct omap1_cam_buf *ready;
Well, I must confess, as it stands, you managed to confuse me with your "active" and "ready" buffer management. Is the reason for these two pointers some hardware feature? Maybe that while one buffer is being filled by DMA you can already configure the DMA engine with the next one?
Exactly. Both dma-contig and dma-sg paths relay on this hardware feature, making use of this "ready" pointing to a not yet active but already dma-preprogrammed videobuf.
- 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=commitdif... 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.
+};
+void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
static
Sure.
+{
- pcdev->reg_cache[reg / sizeof(u32)] = val;
- __raw_writel(val, pcdev->base + reg);
+}
+int cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
static u32
OK.
+{
- return !from_cache ? __raw_readl(pcdev->base + reg) :
pcdev->reg_cache[reg / sizeof(u32)];
hmmm.... it's never simple with caches;) Some registers (e.g., CTRLCLOCK) you read both from the cache and from the hardware directly.
I read the CTRLCLOCK from the hardware in omap1_cam_add_device() for one reason: the cache is (can be) not initialized (written with the register copy) yet, and there are reserved bits that I'm trying to keep untouched.
The read from the hardware in omap1_cam_remove_device() is not intentional and should be replaced with cache read if we don't drop register caching.
Other than that, reads from cache are used throughout other functions, I believe.
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.
+}
+#define CAM_READ(pcdev, reg) \
cam_read(pcdev, REG_##reg, 0)
last parameter is a bool, so, use "false"
Right.
+#define CAM_WRITE(pcdev, reg, val) \
cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
cam_read(pcdev, REG_##reg, 1)
"true"
Of course.
...
+static void start_capture(struct omap1_cam_dev *pcdev) +{
- struct omap1_cam_buf *buf = pcdev->active;
- unsigned long ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- unsigned long mode = CAM_READ_CACHE(pcdev, MODE);
I would make all variables, that are used specifically for various registers u32.
OK.
- if (WARN_ON(!buf))
return;
+#ifndef CONFIG_VIDEO_OMAP1_SG
- /* don't enable end of frame interrupts before capture autostart */
- mode &= ~EN_V_DOWN;
+#endif
- if (WARN_ON(mode & RAZ_FIFO))
/* clean up possibly insane reset condition */
CAM_WRITE(pcdev, MODE, mode &= ~RAZ_FIFO);
It is usually better to avoid assigning variables inside other constructs, please split into two lines.
OK, I'll review and rearrange all similiar constructs.
Besides, in omap1_cam_probe() you initialise MODE to "THRESHOLD_LEVEL << THRESHOLD_SHIFT," i.e., RAZ_FIFO is cleared. So, don't think you need that test at all.
Right. What you think is better, drop it, or replace with BUG_ON(mode & RAZ_FIFO)?
...
+static void suspend_capture(struct omap1_cam_dev *pcdev) +{
- unsigned long ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
u32
Yes.
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- omap_stop_dma(pcdev->dma_ch);
+}
+static void disable_capture(struct omap1_cam_dev *pcdev) +{
- unsigned long mode = CAM_READ_CACHE(pcdev, MODE);
u32 (etc.)
Mmh.
...
- dev_dbg(icd->dev.parent, "%s: capture not active, setup FIFO, start
DMA"
"\n", __func__);
No need to split "\n" to a new line, don't worry about > 80 characters...
OK, for this one and all similiar cases.
...
+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)) {
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.
if (!pcdev->ready && result != VIDEOBUF_ERROR)
suspend_capture(pcdev);
vb->state = result;
do_gettimeofday(&vb->ts);
vb->field_count++;
I don't think you're supposed to increment field_count if you've got a VIDEOBUF_ERROR.
OK, I'll correct it.
wake_up(&vb->done);
pcdev->active = buf = pcdev->ready;
Self comment: this construct occured to not satisfy "checkpatch --strict". I'm going to correct all similiar cases.
pcdev->ready = NULL;
if (!buf) {
result = VIDEOBUF_ERROR;
prepare_next_vb(pcdev);
pcdev->active = buf = pcdev->ready;
pcdev->ready = NULL;
}
- } 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.
pcdev->active = pcdev->ready;
+#ifdef CONFIG_VIDEO_OMAP1_SG
buf->sgbuf = NULL;
+#endif
pcdev->ready = buf;
buf = pcdev->active;
- } else {
+#ifndef CONFIG_VIDEO_OMAP1_SG
dev_dbg(dev, "%s: nobody waiting on videobuf, reuse it\n",
__func__);
+#else
if (result != VIDEOBUF_ERROR) {
suspend_capture(pcdev);
result = VIDEOBUF_ERROR;
}
prepare_next_vb(pcdev);
+#endif
- }
- if (!buf) {
dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
disable_capture(pcdev);
return;
- }
+#ifdef CONFIG_VIDEO_OMAP1_SG
- if (result == VIDEOBUF_ERROR)
buf->sgbuf = NULL;
- try_next_sgbuf(pcdev->dma_ch, buf);
+#endif
- if (result == VIDEOBUF_ERROR) {
dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
__func__);
start_capture(pcdev);
- }
This all doesn't look quite right, but I'll wait for documentation, before commenting.
In short: in case of any error, I assume not being able to ensure that next frame still starts at next videobuf start, that's why I restart capture.
start_capture() doesn't actually start anything, only initializes it. After FIFO reset, the hardware stays idle until first frame start (as indicated by both VSYNC and HSYNC active), then starts filling its FIFO on LCLK edges selected with POLCLK and requesting DMA automatically every THRESHOLD_LEVEL words.
- 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;
- enum videobuf_state result;
- unsigned long flags;
- spin_lock_irqsave(&pcdev->lock, flags);
- if (WARN_ON(!buf)) {
suspend_capture(pcdev);
disable_capture(pcdev);
goto out;
- }
+#ifndef CONFIG_VIDEO_OMAP1_SG
- /* videobuf complete, disable end of frame interrupt for this frame */
- CAM_WRITE(pcdev, MODE, CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
- result = VIDEOBUF_DONE;
+#else
- if (buf->sgbuf) {
/* current sglist not complete yet */
try_next_sgbuf(pcdev->dma_ch, buf);
if (buf->sgbuf)
goto out;
if (buf->result != VIDEOBUF_ERROR) {
buf = prepare_next_vb(pcdev);
if (!buf)
goto out;
try_next_sgbuf(pcdev->dma_ch, buf);
goto out;
}
- }
- /* end of videobuf */
- result = buf->result;
+#endif
- videobuf_done(pcdev, 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;
- unsigned long 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)) {
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) {
+#ifdef CONFIG_VIDEO_OMAP1_SG
/*
* if exactly 2 sgbufs of the next sglist has be used,
* then we are in sync
*/
You mean the hardware is supposed to interrupt you after 2 buffers?
No. This interrupt occures on every frame end. By this moment, DMA, that is set up to restart automatically on every DMA block end, should already be waiting for first data to put into the first sgbuf area of the next sglist, pointed out by the DMA working registers, and the second sgbuf from this list should already be entered by the dma_isr() into the DMA programming registers, ready for DMA auto restart on current sgbuf (DMA block) end.
if (buf && buf->sgcount == 2)
goto out;
+#endif
dev_notice(dev, "%s: unexpected end of video frame\n",
__func__);
+#ifndef CONFIG_VIDEO_OMAP1_SG
- } else if (it_status & V_UP) {
unsigned long mode = CAM_READ_CACHE(pcdev, MODE);
if (!(mode & EN_V_DOWN)) {
/* enable end of frame interrupt for current videobuf */
CAM_WRITE(pcdev, MODE, mode | EN_V_DOWN);
}
goto out;
+#endif
- } else {
dev_warn(pcdev->soc_host.v4l2_dev.dev, "%s: "
"unhandled camera interrupt, status == 0x%lx\n",
__func__, it_status);
goto out;
- }
- videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return IRQ_HANDLED;
+}
...
+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;
- unsigned int ctrlclock;
u32
Sure.
- int ret = 0;
- if (pcdev->icd) {
ret = -EBUSY;
goto 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 CAM_EXCLK_6MHz:
just use constants for these
You mean numbers (6000000 in this case) instead of predifined symbols, don't you?
ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
break;
- case CAM_EXCLK_8MHz:
ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
break;
- case CAM_EXCLK_9_6MHz:
ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
break;
- case CAM_EXCLK_12MHz:
ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
break;
- case CAM_EXCLK_24MHz:
ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
- default:
This is the case of "Not providing sensor clock," do you still have to enable the clock at all below (MCLK_EN)?
I'm not absolutely sure, but I think I had already tried without it and it looked like FIFO didn't work. Will recheck.
break;
- }
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
- /* enable clock */
- ctrlclock |= MCLK_EN;
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
- sensor_reset(pcdev, 0);
use "false" for the second parameter
OK.
...
+/* Duplicate standard formats based on host capability of byte swapping */ +static const struct soc_mbus_pixelfmt omap1_cam_formats[] = {
- [V4L2_MBUS_FMT_YUYV8_2X8_BE] = {
You'll have to adjust these to the new names. Basically, for all YUYV8 and YVYU8 codes, "_LE" just gets dropped, and for "_BE" you swap chrominance-luminance pairs, i.e., UYVY8_2X8 instead of YUYV8_2X8_BE and VYUY8_2X8 instead of YVYU8_2X8_BE.
OK.
...
+static int is_dma_aligned(s32 bytes_per_line, unsigned int height)
make it a bool
OK.
+{
- int size = bytes_per_line * height;
- return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
IS_ALIGNED(size, DMA_FRAME_SIZE * 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 + DMA_ELEMENT_SHIFT - pxalign;
unsigned int incr = enlarge << salign;
v4l_bound_align_image(width, DMA_ELEMENT_SIZE >> pxalign,
*width + incr, DMA_ELEMENT_SHIFT - pxalign,
The above means, that lines must consist of an integer number of DMA elements, right? Is this really necessary?
Perhaps not really :). I'll recheck and remove the constraint if not.
height, 1, *height + incr, 0, salign);
return 0;
- }
- return 1;
+}
...
dev_dbg(dev, "%s: g_crop() missing, trying cropcap() instead"
"\n", __func__);
Put "\n" back in the string
OK.
...
- *rect = crop.c;
superfluous space
Mhm.
...
dev_dbg(dev, "%s: g_mbus_fmt() missing, trying g_crop() instead"
"\n", __func__);
Put "\n" back in the string
Yes.
ret = get_crop(icd, &c);
if (ret < 0)
return ret;
/* REVISIT:
* Should cropcap() obtained defrect reflect last s_crop()?
* Can we use it here for s_crop() result verification?
*/
multiline comment style
Could be dropped completely if you know the answer and share it with me :).
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(ret, function, args...) \ +{ \
- struct soc_camera_sense sense = { \
.master_clock = pcdev->camexclk, \
.pixel_clock_max = 0, \
- }; \
+\
- 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); \
"\n" back, please
Sure.
return -EIO; \
} \
- } \
+}
Ok, I'd prefer to follow some good-style rules here too. (1) please align your backslashes with TABs. (2) please, don't use implicitly passed variables, in this case "sd," "pcdev," "dev," and "icd." (3) since this is a macro, you don't need to pass ret as a pointer. (4) don't "return" from a macro - in your case it's enough to "ret = -EIO." Personally, I wouldn't worry about adding 4 more parameters to the macro. I would also remove "ret" from parameters and "return" it from the macro per
#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \ ({ \ int __ret; \ ... \ __ret = v4l2_subdev_call(sd, video, function, ##args); \ ... \ __ret = -EINVAL; \ ... \ __ret; \ })
Thanks! :)
+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(&ret, 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__);
- }
Superfluous braces
Yes.
- 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 < 0) {
Doesn't look like this is possible.
It was once :/. I'll correct it.
dev_err(dev, "%s: is_dma_aligned() failed\n", __func__);
return ret;
- }
- if (!ret) {
dev_err(dev, "%s: resulting geometry %dx%d not DMA aligned\n",
__func__, rect->width, rect->height);
return -EINVAL;
- }
- 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.
- 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;
- }
- bytes_per_line = soc_mbus_bytes_per_line(pix->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;
- }
- if (pix->bytesperline && pix->bytesperline != bytes_per_line) {
dev_err(dev, "%s: bytes per line mismatch\n", __func__);
return -EINVAL;
- }
- ret = is_dma_aligned(bytes_per_line, pix->height);
- if (ret < 0) {
ditto - that's just a bool.
Sure.
dev_err(dev, "%s: is_dma_aligned() failed\n", __func__);
return ret;
- }
- if (!ret) {
dev_err(dev, "%s: image size %dx%d not DMA aligned\n",
__func__, pix->width, pix->height);
return -EINVAL;
- }
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
- subdev_call_with_sense(&ret, 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;
- }
- icd->current_fmt = xlate;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
- if (mf.width == pix->width && mf.height == pix->height)
return 0;
- dev_notice(dev, "%s: sensor geometry differs, trying to crop to %dx%d"
"\n", __func__, pix->width, pix->height);
No, you shouldn't do this. It is normal, when S_FMT sets a format different from what the user has requested. Just leave it as is, only adjust if you cannot handle it or fail.
With my, maybe weird, try_fmt() implementation, I think I can be sure here that the geometry is not DMA aligned, unless a user can request a different geometry than she obtained from try_fmt(), that I assumed should not happen. Please see below for more exhausitve explanation.
- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- 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;
- subdev_call_with_sense(&ret, 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, 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 verified\n", __func__);
return 0;
- }
- if (pix->width != rect->width || pix->height != rect->height) {
dev_err(dev, "%s: tried to set %dx%d, got %dx%d\n", __func__,
pix->width, pix->height,
rect->width, rect->height);
return -EINVAL;
- }
No, that's not an error. Just use whatever you managed to configure.
Let me explain and maybe reimplement correctly my try_fmt() first :).
- 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 device *dev = icd->dev.parent;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf, testmf;
- const struct soc_mbus_pixelfmt *fmt;
- 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;
- }
- fmt = xlate->host_fmt;
- ret = dma_align(&pix->width, &pix->height, fmt, true);
- if (ret < 0) {
dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
__func__, pix->width, pix->height, fmt->name);
return ret;
- }
- 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) {
dev_err(dev, "%s: try_mbus_fmt() failed\n", __func__);
return ret;
- }
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
- if (mf.width == pix->width && mf.height == pix->height &&
mf.code == xlate->code)
return 0;
- dev_dbg(dev, "%s: geometry changed, recheck alignment\n", __func__);
- pix->width = mf.width;
- pix->height = mf.height;
This is what the sensor has returned, you put it back into the user frmat.
I hope it's OK.
- fmt = soc_mbus_get_fmtdesc(mf.code);
- ret = dma_align(&pix->width, &pix->height, fmt, false);
And you DMA-align those sizes.
Shouldn't I?
- if (ret < 0) {
dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
__func__, pix->width, pix->height, fmt->name);
return ret;
- }
- if (ret)
return 0;
Maybe the above is not clear enough: ret == 1 means that what we got from the sensor is already aligned. If not, we keep processing.
- testmf.width = pix->width;
- testmf.height = pix->height;
And use them again for try_fmt
Yes. For rationale, please see my explanation below.
- testmf.field = mf.field;
- testmf.colorspace = mf.colorspace;
- testmf.code = mf.code;
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &testmf);
- if (ret < 0) {
dev_err(dev, "%s: try_mbus_fmt() failed\n", __func__);
return ret;
- }
- if (testmf.code != mf.code || testmf.width != mf.width ||
testmf.height != mf.height) {
dev_err(dev, "%s: sensor format inconsistency, giving up\n",
__func__);
return -EINVAL;
As a general rule, try_fmt shouldn't fail... Only in very weird cases, really.
Not being able to negotiate a DMA aligned geometry with the sensor - would it be weird enough?
- }
And you verified, the sensor kept them.
The sensor kept previously returend (not aligned) values (stored in mf), not those DMA aligned (stored under pix).
- dev_notice(dev, "%s: "
"sensor frame not DMA aligned, will try to crop from set_fmt()"
Hm? You did align them above? /me confused...
Let me explain how it is all supposed to work:
try_fmt(): 1. DMA align a user requested frame geometry. 2. Let the sensor adjust it to its capabilities. 3. If the sensor didn't change the (DMA aligned) geometry, we are done. 4. Otherwise, DMA align it again. 5. If no DMA alignment was necessary on the sensor proposed geometry, we are done as well. 6. Otherwise, check if we still get the same (not aligned) geometry from the sensor if now asking for its DMA alignment result. 7. If the now returned sensor geometry is different from what we got before, we can either restart from 3 (how many iterations?) or fail, I believe. 8. Otherwise, we can accept the sensor geometry that is not DMA aligned, but should return the DMA aligned one, not the sensor provided. We believe to crop the sensor geometry to DMA aligned one during S_FMT.
set_fmt(): 1. Since we always return DMA aligned geometry from try_fmt(), expecting it being accepted by a user, we start from checking if what user have requested now is DMA aligned or not. 2. If not DMA aligned, we fail. Should we be more polite and try to do our best to accept a geometry that differs from what our try_fmt() could ever return? 3. If DMA aligned, then OK, let the sensor adjust it to its capabilities. 4. If the sensor didn't change the (DMA aligned) geometry, we are done. 5. Otherwise, we assume the sensor returned geometry is not DMA aligned (if it were, we would accept it during try_fmt() before), so it has to be cropped. Should we rather retry with S_FMT instead of cropping? 6. If crop gives the geometry requested, we are done. 7. Otherwise, we fail. Should we be more cooperative here and accept any valid (ie. DMA aligned) crop result?
"\n", __func__);
- return 0;
+}
...
- /* apply reset to camera sensor if requested by platform data */
- if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
CAM_WRITE(pcdev, GPIO, 0x1);
- else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
CAM_WRITE(pcdev, GPIO, 0x0);
Why not use sensor_reset()?
Why not... ;)
...
+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;
- soc_camera_host_unregister(soc_host);
- free_irq(pcdev->irq, pcdev);
- omap_free_dma(pcdev->dma_ch);
It is probably safer to first shut down the hardware, and only then unconfigure software. So, free_irq and free_dma should go first, I think.
OK.
Thanks, Janusz