Re: [PATCH v9 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

From: Luca Ceresoli
Date: Sun Jun 30 2019 - 17:45:13 EST


Hi Vishal,

a few questions below about the SLBF error management.

On 11/06/19 12:10, Vishal Sagar wrote:
> The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
> from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
> for image processing. Please refer to PG232 for details.
>
> The driver is used to set the number of active lanes, if enabled
> in hardware. The CSI2 Rx controller filters out all packets except for
> the packets with data type fixed in hardware. RAW8 packets are always
> allowed to pass through.
>
> It is also used to setup and handle interrupts and enable the core. It
> logs all the events in respective counters between streaming on and off.
>
> The driver supports only the video format bridge enabled configuration.
> Some data types like YUV 422 10bpc, RAW16, RAW20 are supported when the
> CSI v2.0 feature is enabled in design. When the VCX feature is enabled,
> the maximum number of virtual channels becomes 16 from 4.
>
> Signed-off-by: Vishal Sagar <vishal.sagar@xxxxxxxxxx>
> Reviewed-by: Hyun Kwon <hyun.kwon@xxxxxxxxxx>

...

> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c

...
> +/**
> + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * In the interrupt handler, a list of event counters are updated for
> + * corresponding interrupts. This is useful to get status / debug.
> + *
> + * In case of stream line buffer full condition, the IP is reset, stopped and
> + * an event is raised.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + * IRQ_NONE is no interrupts
> + */
> +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
> +{
> + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
> + struct xcsi2rxss_core *core = &state->core;
> + u32 status;
> +
> + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK;
> + dev_dbg_ratelimited(core->dev, "interrupt status = 0x%08x\n", status);
> +
> + if (!status)
> + return IRQ_NONE;
> +
> + /* Received a short packet */
> + if (status & XCSI_ISR_SPFIFONE) {
> + dev_dbg_ratelimited(core->dev, "Short packet = 0x%08x\n",
> + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET));
> + }
> +
> + /* Short packet FIFO overflow */
> + if (status & XCSI_ISR_SPFIFOF)
> + dev_alert_ratelimited(core->dev, "Short packet FIFO overflowed\n");
> +
> + /*
> + * Stream line buffer full
> + * This means there is a backpressure from downstream IP
> + */
> + if (status & XCSI_ISR_SLBF) {
> + dev_alert_ratelimited(core->dev, "Stream Line Buffer Full!\n");
> + if (core->rst_gpio) {
> + gpiod_set_value(core->rst_gpio, 1);
> + /* minimum 40 dphy_clk_200M cycles */
> + ndelay(250);
> + gpiod_set_value(core->rst_gpio, 0);
> + }
> + xcsi2rxss_stop_stream(state);

I've been hit by the dreadful "Stream Line Buffer Full" error, getting
the CSI-2 RX completely stuck in SLBF and not transmitting any frames
sporadically after glitches in the incoming MIPI stream. And I found
that adding xcsi2rxss_start_stream() here just after
xcsi2rxss_stop_stream() allows to continue the stream with almost no
interruption and without userspace intervention.

Do you think this is a reliable solution, or does it have side-effects I
didn't encounter? Note I'm not using pm nor the ctrls, so register
writes are limited to the enable/disable code paths.

Does video_aresetn also reset registers?

BTW in my code I also moved xcsi2rxss_stop_stream() before the if
(core->rst_gpio) {}. There is no strong reason for this, I didn't
observe any functional difference, it just looks
more logical to me to stop the IP before resetting it.

...

> +static int xcsi2rxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xcsi2rxss_state *xcsi2rxss;
> + struct xcsi2rxss_core *core;
> + struct resource *res;
> + int ret, num_ctrls, i;
> +
> + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
> + if (!xcsi2rxss)
> + return -ENOMEM;
> +
> + core = &xcsi2rxss->core;
> + core->dev = &pdev->dev;
> +
> + core->clks = devm_kmemdup(core->dev, xcsi2rxss_clks,
> + sizeof(xcsi2rxss_clks), GFP_KERNEL);
> + if (!core->clks)
> + return -ENOMEM;
> +
> + /* Reset GPIO */
> + core->rst_gpio = devm_gpiod_get_optional(core->dev, "reset",
> + GPIOD_OUT_HIGH);

Is GPIOD_OUT_HIGH correct? video_aresetn is active low.

> + if (IS_ERR(core->rst_gpio)) {
> + if (PTR_ERR(core->rst_gpio) != -EPROBE_DEFER)
> + dev_err(core->dev, "Video Reset GPIO not setup in DT");
> + return PTR_ERR(core->rst_gpio);
> + }
> +
> + mutex_init(&xcsi2rxss->lock);
> +
> + ret = xcsi2rxss_parse_of(xcsi2rxss);
> + if (ret < 0)
> + return ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + core->iomem = devm_ioremap_resource(core->dev, res);
> + if (IS_ERR(core->iomem))
> + return PTR_ERR(core->iomem);
> +
> + core->num_clks = ARRAY_SIZE(xcsi2rxss_clks);
> +
> + ret = clk_bulk_get(core->dev, core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> + if (ret)
> + goto err_clk_put;
> +
> + if (xcsi2rxss->core.rst_gpio) {
> + gpiod_set_value_cansleep(xcsi2rxss->core.rst_gpio, 1);
> + /* minimum of 40 dphy_clk_200M cycles */
> + usleep_range(1, 2);
> + gpiod_set_value_cansleep(xcsi2rxss->core.rst_gpio, 0);
> + }

"xcsi2rxss->core" -> "core" in these lines.

Thanks,
--
Luca