[PATCH 1/6] v4l: omap4iss: Add support for OMAP4 camera interface - Core
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Sun Nov 3 23:43:43 UTC 2013
Hi Sakari,
Thank you for the review.
On Sunday 06 October 2013 00:49:28 Sakari Ailus wrote:
> Hi Laurent,
>
> Thanks for the patch! Some comments below.
>
> On Thu, Oct 03, 2013 at 01:55:28AM +0200, Laurent Pinchart wrote:
> ...
>
> > +int omap4iss_get_external_info(struct iss_pipeline *pipe,
> > + struct media_link *link)
> > +{
> > + struct iss_device *iss =
> > + container_of(pipe, struct iss_video, pipe)->iss;
> > + struct v4l2_subdev_format fmt;
> > + struct v4l2_ext_controls ctrls;
> > + struct v4l2_ext_control ctrl;
> > + int ret;
> > +
> > + if (!pipe->external)
> > + return 0;
> > +
> > + if (pipe->external_rate)
> > + return 0;
> > +
> > + memset(&fmt, 0, sizeof(fmt));
> > +
> > + fmt.pad = link->source->index;
> > + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(link->sink-
>entity),
> > + pad, get_fmt, NULL, &fmt);
> > + if (ret < 0)
> > + return -EPIPE;
> > +
> > + pipe->external_bpp = omap4iss_video_format_info(fmt.format.code)-
>bpp;
> > +
> > + memset(&ctrls, 0, sizeof(ctrls));
> > + memset(&ctrl, 0, sizeof(ctrl));
> > +
>
> As a general note, you can replace memsets of local structs and arrays by
> assingning them as {0}. No need to worry about size for instance.
Good point. That code is going away though :-)
> > + ctrl.id = V4L2_CID_PIXEL_RATE;
> > +
> > + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id);
> > + ctrls.count = 1;
> > + ctrls.controls = &ctrl;
> > +
> > + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
> > + if (ret < 0) {
> > + dev_warn(iss->dev, "no pixel rate control in subdev %s\n",
> > + pipe->external->name);
> > + return ret;
> > + }
> > +
> > + pipe->external_rate = ctrl.value64;
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * Configure the bridge. Valid inputs are
> > + *
> > + * IPIPEIF_INPUT_CSI2A: CSI2a receiver
> > + * IPIPEIF_INPUT_CSI2B: CSI2b receiver
> > + *
> > + * The bridge and lane shifter are configured according to the selected
> > input + * and the ISP platform data.
> > + */
> > +void omap4iss_configure_bridge(struct iss_device *iss,
> > + enum ipipeif_input_entity input)
> > +{
> > + u32 issctrl_val;
> > + u32 isp5ctrl_val;
> > +
> > + issctrl_val = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL);
> > + issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK;
> > + issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK;
> > +
> > + isp5ctrl_val = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL);
> > +
> > + switch (input) {
> > + case IPIPEIF_INPUT_CSI2A:
> > + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A;
> > + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT;
> > + break;
> > +
> > + case IPIPEIF_INPUT_CSI2B:
> > + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B;
> > + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT;
>
> This assignment is independent of the case. You could do that a little later
> just once.
I'll fix that.
> > + break;
> > +
> > + default:
> > + return;
>
> Isn't this an error?
This should never happen, it would be a driver bug. If the input isn't
configured the pipeline isn't valid, which should be caught when starting the
stream before calling this function.
> > + }
> > +
> > + issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING;
> > +
> > + isp5ctrl_val |= ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE;
> > +
> > + writel(issctrl_val, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL);
> > + writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL);
> > +}
>
> ...
>
> > +
> > +/*
> > + * iss_pipeline_enable - Enable streaming on a pipeline
> > + * @pipe: ISS pipeline
> > + * @mode: Stream mode (single shot or continuous)
> > + *
> > + * Walk the entities chain starting at the pipeline output video node and
> > start + * all modules in the chain in the given mode.
> > + *
> > + * Return 0 if successful, or the return value of the failed
> > video::s_stream + * operation otherwise.
> > + */
> > +static int iss_pipeline_enable(struct iss_pipeline *pipe,
> > + enum iss_pipeline_stream_state mode)
> > +{
> > + struct media_entity *entity;
> > + struct media_pad *pad;
> > + struct v4l2_subdev *subdev;
> > + unsigned long flags;
> > + int ret;
> > +
> > + spin_lock_irqsave(&pipe->lock, flags);
> > + pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
> > + spin_unlock_irqrestore(&pipe->lock, flags);
> > +
> > + pipe->do_propagation = false;
> > +
> > + entity = &pipe->output->video.entity;
> > + while (1) {
> > + pad = &entity->pads[0];
> > + if (!(pad->flags & MEDIA_PAD_FL_SINK))
> > + break;
> > +
> > + pad = media_entity_remote_pad(pad);
> > + if (pad == NULL ||
> > + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> > + break;
> > +
> > + entity = pad->entity;
> > + subdev = media_entity_to_v4l2_subdev(entity);
> > +
> > + ret = v4l2_subdev_call(subdev, video, s_stream, mode);
> > + if (ret < 0 && ret != -ENOIOCTLCMD)
> > + return ret;
>
> This loop looks very similar to that in the omap3isp driver.
>
> It'd be nice to do this in a generic way. Something to think about in the
> future perhaps. This is still queite easy; it gets somewhat more difficult
> once the pipeline isn't linear.
That's a good idea. I'll check whether we have other common needs.
> > + }
> > + iss_print_status(pipe->output->iss);
> > + return 0;
> > +}
> > +
> > +/*
> > + * iss_pipeline_disable - Disable streaming on a pipeline
> > + * @pipe: ISS pipeline
> > + *
> > + * Walk the entities chain starting at the pipeline output video node and
> > stop + * all modules in the chain. Wait synchronously for the modules to
> > be stopped if + * necessary.
> > + */
> > +static int iss_pipeline_disable(struct iss_pipeline *pipe)
> > +{
> > + struct media_entity *entity;
> > + struct media_pad *pad;
> > + struct v4l2_subdev *subdev;
> > + int failure = 0;
> > +
> > + entity = &pipe->output->video.entity;
> > + while (1) {
> > + pad = &entity->pads[0];
> > + if (!(pad->flags & MEDIA_PAD_FL_SINK))
> > + break;
> > +
> > + pad = media_entity_remote_pad(pad);
> > + if (pad == NULL ||
> > + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> > + break;
> > +
> > + entity = pad->entity;
> > + subdev = media_entity_to_v4l2_subdev(entity);
> > +
> > + v4l2_subdev_call(subdev, video, s_stream, 0);
>
> What would you think about combining the loop with enabling the pipeline?
> The only difference is that you're disabling streaming here, not enabling
> it, and no error handling is done either. Just and idea.
I'll try this when factorizing common code between the omap3isp and omap4iss
drivers.
> > + }
> > +
> > + return failure;
>
> You always return zero here.
I'll fix that.
> > +}
[snip]
> > +/*
> > + * iss_pipeline_is_last - Verify if entity has an enabled link to the
> > output + * video node
> > + * @me: ISS module's media entity
> > + *
> > + * Returns 1 if the entity has an enabled link to the output video node
> > or 0 + * otherwise. It's true only while pipeline can have no more than
> > one output + * node.
> > + */
> > +static int iss_pipeline_is_last(struct media_entity *me)
> > +{
> > + struct iss_pipeline *pipe;
> > + struct media_pad *pad;
> > +
> > + if (!me->pipe)
> > + return 0;
> > + pipe = to_iss_pipeline(me);
> > + if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
> > + return 0;
> > + pad = media_entity_remote_pad(&pipe->output->pad);
> > + return pad->entity == me;
> > +}
> > +
> > +static int iss_reset(struct iss_device *iss)
> > +{
> > + unsigned long timeout = 0;
> > +
> > + writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
> > + ISS_HL_SYSCONFIG_SOFTRESET,
> > + iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
> > +
> > + while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
> > + ISS_HL_SYSCONFIG_SOFTRESET) {
> > + if (timeout++ > 10000) {
> > + dev_alert(iss->dev, "cannot reset ISS\n");
> > + return -ETIMEDOUT;
> > + }
> > + udelay(1);
>
> You only seem to do this in probe. How about e.g. usleep_range(10, 10) and
> timeout of 1000?
Good point, I'll fix that.
>
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int iss_isp_reset(struct iss_device *iss)
> > +{
> > + unsigned long timeout = 0;
> > +
> > + /* Fist, ensure that the ISP is IDLE (no transactions happening) */
> > + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) &
> > + ~ISP5_SYSCONFIG_STANDBYMODE_MASK) |
> > + ISP5_SYSCONFIG_STANDBYMODE_SMART,
> > + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG);
> > +
> > + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) |
> > + ISP5_CTRL_MSTANDBY,
> > + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL);
> > +
> > + for (;;) {
> > + if (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) &
> > + ISP5_CTRL_MSTANDBY_WAIT)
> > + break;
> > + if (timeout++ > 1000) {
> > + dev_alert(iss->dev, "cannot set ISP5 to standby\n");
> > + return -ETIMEDOUT;
> > + }
> > + msleep(1);
>
> Is this time critical in any way? msleep(1) may end up sleeping quite a lot
> longer. I propose usleep_range().
I'll fix that.
> > + }
> > +
> > + /* Now finally, do the reset */
> > + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) |
> > + ISP5_SYSCONFIG_SOFTRESET,
> > + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG);
> > +
> > + timeout = 0;
> > + while (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) &
> > + ISP5_SYSCONFIG_SOFTRESET) {
> > + if (timeout++ > 1000) {
> > + dev_alert(iss->dev, "cannot reset ISP5\n");
> > + return -ETIMEDOUT;
> > + }
> > + msleep(1);
>
> Wow. :) Remember msleep() is usually notoriously imprecise, too. E.g.
> CONFIG_HZ at 100 could mean msleep(1) sleeps up to 20 ms, and 1000 that can
> be up to 20 seconds. Is this intended?
I doubt it :-)
> > + }
> > +
> > + return 0;
> > +}
[snip]
> > diff --git a/include/media/omap4iss.h b/include/media/omap4iss.h
> > new file mode 100644
> > index 0000000..0d7620d
> > --- /dev/null
> > +++ b/include/media/omap4iss.h
> > @@ -0,0 +1,65 @@
>
> There's no copyright notice in this file. Is it intentional? I don't demand
> it; just wondering. :)
It's not my code so I don't know :-)
> > +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
> > +#define ARCH_ARM_PLAT_OMAP4_ISS_H
> > +
> > +#include <linux/i2c.h>
> > +
> > +struct iss_device;
> > +
> > +enum iss_interface_type {
> > + ISS_INTERFACE_CSI2A_PHY1,
> > + ISS_INTERFACE_CSI2B_PHY2,
> > +};
> > +
> > +/**
> > + * struct iss_csiphy_lane: CSI2 lane position and polarity
> > + * @pos: position of the lane
> > + * @pol: polarity of the lane
> > + */
> > +struct iss_csiphy_lane {
> > + u8 pos;
> > + u8 pol;
> > +};
> > +
> > +#define ISS_CSIPHY1_NUM_DATA_LANES 4
> > +#define ISS_CSIPHY2_NUM_DATA_LANES 1
> > +
> > +/**
> > + * struct iss_csiphy_lanes_cfg - CSI2 lane configuration
> > + * @data: Configuration of one or two data lanes
> > + * @clk: Clock lane configuration
> > + */
> > +struct iss_csiphy_lanes_cfg {
> > + struct iss_csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
> > + struct iss_csiphy_lane clk;
> > +};
> > +
> > +/**
> > + * struct iss_csi2_platform_data - CSI2 interface platform data
> > + * @crc: Enable the cyclic redundancy check
> > + * @vpclk_div: Video port output clock control
> > + */
> > +struct iss_csi2_platform_data {
> > + unsigned crc:1;
> > + unsigned vpclk_div:2;
> > + struct iss_csiphy_lanes_cfg lanecfg;
> > +};
> > +
> > +struct iss_subdev_i2c_board_info {
> > + struct i2c_board_info *board_info;
> > + int i2c_adapter_id;
> > +};
> > +
> > +struct iss_v4l2_subdevs_group {
> > + struct iss_subdev_i2c_board_info *subdevs;
> > + enum iss_interface_type interface;
> > + union {
> > + struct iss_csi2_platform_data csi2;
> > + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
> > +};
> > +
> > +struct iss_platform_data {
> > + struct iss_v4l2_subdevs_group *subdevs;
> > + void (*set_constraints)(struct iss_device *iss, bool enable);
> > +};
> > +
> > +#endif
--
Regards,
Laurent Pinchart
More information about the devel
mailing list