Re: [PATCH 2/2] media: nxp: add driver for i.MX93 MIPI CSI-2 controller and D-PHY

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Guoniu,

thanks for the fast response.

Am Mittwoch, 5. Juli 2023, 05:52:05 CEST schrieb G.N. Zhou (OSS):
> Hi Alexander,
> 
> Thanks for your comments.
> 
> [snip]
> > > +
> > > +/* Set default high speed frequency range to 1.5Gbps  */
> > > +#define DPHY_DEFAULT_FREQRANGE               0x2c
> > > +
> > > +enum imx93_csi_clks {
> > > +     PER,
> > > +     PIXEL,
> > > +     PHY_CFG,
> > > +};
> > > +
> > > +enum model {
> > > +     DWC_CSI2RX_IMX93,
> > > +};
> > > +
> > > +enum dwc_csi2rx_intf {
> > > +     DWC_CSI2RX_INTF_IDI,
> > 
> > 
> > This is unused, what is it intented for?
> 
> 
> DesignWare Core MIPI CSI-2 support both IDI and IPI interface. For i.MX93 it
> use IPI as interface with ISI(gasket) and I reserved IDI here on the one
> hand support full features of the MIPI CSI-2 IP as more as possible, on the
> other hand, NXP i.MX95 MIPI CSI-2 remove IPI and use IDI as the interface. 

I don't know about the differences on IPI and IDI, but it looks like both 
i.MX93 and i.MX95 use the same MIPI-CSI2 IP core, but have a different glue 
layer. So IPI and IDI specifics seem to be SoC specific as well. Did I get 
something wrong?

> [snip]

> > > ------------------------------------------------------------------------
> > > ---
 -- + * Debug
> > > + */
> > > +
> > > +static void dwc_csi_clear_counters(struct dwc_csi_device *csidev)
> > > +{
> > > +     unsigned long flags;
> > > +     unsigned int i;
> > > +
> > > +     spin_lock_irqsave(&csidev->slock, flags);
> > > +
> > > +     for (i = 0; i < csidev->pdata->num_events; ++i)
> > > +             csidev->events[i].counter = 0;
> > > +
> > > +     spin_unlock_irqrestore(&csidev->slock, flags);
> > > +}
> > > +
> > > +static void dwc_csi_log_counters(struct dwc_csi_device *csidev)
> > > +{
> > > +     unsigned int num_events = csidev->pdata->num_events;
> > > +     unsigned long flags;
> > > +     unsigned int i;
> > > +
> > > +     spin_lock_irqsave(&csidev->slock, flags);
> > > +
> > > +     for (i = 0; i < num_events; ++i) {
> > > +             if (csidev->events[i].counter > 0)
> > > +                     dev_info(csidev->dev, "%s events: %d\n",
> > > +                              csidev->events[i].name,
> > > +                              csidev->events[i].counter);
> > > +     }
> > > +
> > > +     spin_unlock_irqrestore(&csidev->slock, flags);
> > > +}
> > > +
> > > +static void dwc_csi_dump_regs(struct dwc_csi_device *csidev)
> > > +{
> > > +#define DWC_MIPI_CSIS_DEBUG_REG(name)                {name,
> > 
> > #name}
> > 
> > > +     static const struct {
> > > +             u32 offset;
> > > +             const char * const name;
> > > +     } registers[] = {
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_VERSION),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_N_LANES),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_HOST_RESETN),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_INT_ST_MAIN),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_SHUTDOWNZ),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_RSTZ),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_RX_STATUS),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_STOPSTATE),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_TEST_CTRL0),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_DPHY_TEST_CTRL1),
> > > +
> > 
> > DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_PPI_PG_PATTERN_VRES),
> > 
> > > +
> > 
> > DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_PPI_PG_PATTERN_HRES),
> > 
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_PPI_PG_CONFIG),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_PPI_PG_ENABLE),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_PPI_PG_STATUS),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_MODE),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_VCID),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_DATA_TYPE),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_MEM_FLUSH),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_SOFTRSTN),
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_IPI_ADV_FEATURES),
> > > +
> > 
> > DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_INT_ST_DPHY_FATAL),
> > 
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_INT_ST_PKT_FATAL),
> > > +
> > 
> > DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_INT_ST_DPHY_FATAL),
> > 
> > > +             DWC_MIPI_CSIS_DEBUG_REG(CSI2RX_INT_ST_IPI_FATAL),
> > > +     };
> > > +
> > > +     unsigned int i;
> > > +     u32 cfg;
> > > +
> > > +     dev_dbg(csidev->dev, "--- REGISTERS ---\n");
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(registers); i++) {
> > > +             cfg = dwc_csi_read(csidev, registers[i].offset);
> > > +             dev_dbg(csidev->dev, "%14s[0x%02x]: 0x%08x\n",
> > > +                     registers[i].name, registers[i].offset, cfg);
> > > +     }
> > > +}

These register dumps also look like a good candidate for 
v4l2_subdev_core_ops.log_status callback. VIDIOC_LOG_STATUS ioctl that is.

> [snip]

> > > +static int dwc_csi_subdev_set_fmt(struct v4l2_subdev *sd,
> > > +                                struct v4l2_subdev_state *sd_state,
> > > +                                struct v4l2_subdev_format
> > 
> > *sdformat)
> > 
> > > +{
> > > +     struct dwc_csi_device *csidev = sd_to_dwc_csi_device(sd);
> > > +     struct dwc_csi_pix_format const *csi_fmt;
> > > +     struct v4l2_mbus_framefmt *fmt;
> > > +     unsigned int align;
> > > +
> > > +     /*
> > > +      * The CSIS can't transcode in any way, the source format can't
> > > be
> > > +      * modified.
> > > +      */
> > > +     if (sdformat->pad == DWC_CSI2RX_PAD_SOURCE)
> > > +             return dwc_csi_subdev_get_fmt(sd, sd_state, sdformat);
> > > +
> > > +     if (sdformat->pad != DWC_CSI2RX_PAD_SINK)
> > > +             return -EINVAL;
> > > +
> > > +     /*
> > > +      * Validate the media bus code and clamp and align the size.
> > > +      *
> > > +      * The total number of bits per line must be a multiple of 8. We
> > 
> > thus
> > 
> > > +      * need to align the width for formats that are not multiples of
> > > 8
> > > +      * bits.
> > > +      */
> > > +     csi_fmt = find_csi_format(sdformat->format.code);
> > > +     if (!csi_fmt)
> > > +             csi_fmt = &dwc_csi_formats[0];
> > > +
> > > +     switch (csi_fmt->width % 8) {
> > > +     case 0:
> > > +             align = 0;
> > > +             break;
> > > +     case 4:
> > > +             align = 1;
> > > +             break;
> > > +     case 2:
> > > +     case 6:
> > > +             align = 2;
> > > +             break;
> > > +     default:
> > > +             /* 1, 3, 5, 7 */
> > > +             align = 3;
> > > +             break;
> > > +     }
> > 
> > 
> > Is this switch-case actually necessary? If the bits per line have to be a
> > multiple of 8, IMHO calling v4l_bound_align_image() with walign=3 should
> > be enough for all cases.
> 
> 
> Yes it's. walign=3 can cover all cases as you said but can't handle precise
> control and cause unnecessary memory waste.

Right, it's about _bits_ per line, not pixel per line. So your calculation 
seems sensible.

> [snip]

> > > +static int dwc_csi_async_register(struct dwc_csi_device *csidev)
> > > +{
> > > +     struct v4l2_fwnode_endpoint vep = {
> > > +             .bus_type = V4L2_MBUS_CSI2_DPHY,
> > > +     };
> > > +     struct v4l2_async_subdev *asd;
> > > +     struct fwnode_handle *ep;
> > > +     unsigned int i;
> > > +     int ret;
> > > +
> > > +     v4l2_async_nf_init(&csidev->notifier);
> > > +
> > > +     ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csidev->dev), 0,
> > 
> > 0,
> > 
> > > +
> > 
> > FWNODE_GRAPH_ENDPOINT_NEXT);
> > 
> > > +     if (!ep)
> > > +             return -ENOTCONN;
> > > +
> > > +     ret = v4l2_fwnode_endpoint_parse(ep, &vep);
> > > +     if (ret)
> > > +             goto err_parse;
> > > +
> > > +     for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
> > > +             if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
> > > +                     dev_err(csidev->dev,
> > > +                             "data lanes reordering is not
> > 
> > supported");
> > 
> > > +                     ret = -EINVAL;
> > > +                     goto err_parse;
> > > +             }
> > > +     }
> > > +
> > > +     csidev->bus = vep.bus.mipi_csi2;
> > > +
> > > +     if (fwnode_property_read_u32(ep, "fsl,hsfreqrange",
> > > +                                  &csidev->hsfreqrange))
> > > +             csidev->hsfreqrange = DPHY_DEFAULT_FREQRANGE;
> > > +
> > > +     dev_dbg(csidev->dev, "data lanes: %d\n", csidev-
> > >
> > >bus.num_data_lanes);
> > >
> > > +     dev_dbg(csidev->dev, "flags: 0x%08x\n", csidev->bus.flags);
> > > +     dev_dbg(csidev->dev, "high speed frequency range: 0x%X\n",
> > > csidev->hsfreqrange); +
> > > +     asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep,
> > > +                                           struct
> > 
> > v4l2_async_subdev);
> > 
> > > +     if (IS_ERR(asd)) {
> > > +             ret = PTR_ERR(asd);
> > > +             goto err_parse;
> > > +     }
> > > +
> > > +     fwnode_handle_put(ep);
> > > +
> > > +     csidev->notifier.ops = &dwc_csi_notify_ops;
> > > +
> > > +     ret = v4l2_async_subdev_nf_register(&csidev->sd,
> > > &csidev->notifier);
> > > +     if (ret)
> > > +             return ret;
> > 
> > 
> > I'm not sure which part causes the following message:
> > 
> > > dwc-mipi-csi2 4ae00000.mipi-csi: Consider updating driver dwc-mipi-csi2
> > > to
> > 
> > match on endpoints
> > 
> > But as this is a new driver, this should be addressed.
> 
> 
> Sure. Could you help to share the steps about how to reproduce it?

Sure, I assume this depends how your OF graph looks like. This is the one I 
used, stripped some of the (irrelevant) camera properties.

&lpi2c5 {
  camera@1a {
    compatible = "sony,imx327lqr";
    reg = <0x1a>;

    port {
      sony_imx327: endpoint {
        remote-endpoint = <&mipi_to_isi>;
        data-lanes = <1 2>;
        clock-lanes = <0>;
        clock-noncontinuous = <1>;
        link-frequencies = /bits/ 64 <445500000 297000000>;
      };
    };
  };
};

/ {
  soc@0 {
    mipi_csi: mipi-csi@4ae00000 {
      compatible = "fsl,imx93-mipi-csi2";
      reg = <0x4ae00000 0x10000>;
      interrupts = <GIC_SPI 175 IRQ_TYPE_LEVEL_HIGH>;
      clocks = <&clk IMX93_CLK_MIPI_CSI_GATE>,
         <&clk IMX93_CLK_CAM_PIX>,
         <&clk IMX93_CLK_MIPI_PHY_CFG>;
      clock-names = "per", "pixel", "phy_cfg";
      power-domains = <&media_blk_ctrl IMX93_MEDIABLK_PD_MIPI_CSI>;

      ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@0 {
          reg = <0>;

          mipi_from_sensor: endpoint {
            data-lanes = <1 2>;
            fsl,hsfreqrange = <0x2c>;
          };
        };

        port@1 {
          reg = <1>;

          mipi_to_isi: endpoint {
            remote-endpoint = <&isi_in>;
          };
        };
      };
    };
  };
};

As you can see the link speed is either 445.5MHz or 297Mhz. Does this mean I 
have to set fsl,hsfreqrange to 0x16 or 0x14?

> [snip]

> > > +void dphy_rx_test_code_config(struct dwc_csi_device *csidev)
> > > +{
> > > +     u32 val;
> > > +     u8 dphy_val;
> > > +
> > > +     /* Set testclr=1'b0 */
> > > +     val = dwc_csi_read(csidev, CSI2RX_DPHY_TEST_CTRL0);
> > > +     val &= ~CSI2RX_DPHY_TEST_CTRL0_TEST_CLR;
> > > +     dwc_csi_write(csidev, CSI2RX_DPHY_TEST_CTRL0, val);
> > > +
> > > +     /* Enable hsfreqrange_ovr_en and set hsfreqrange */
> > > +     dphy_rx_write(csidev, DPHY_RX_SYS_0,
> > 
> > HSFREQRANGE_OVR_EN_RW);
> > 
> > > +     dphy_rx_write(csidev, DPHY_RX_SYS_1,
> > > +                   HSFREQRANGE_OVR_RW(csidev->hsfreqrange));
> > > +
> > > +     /* Enable timebase_ovr_en */
> > > +     dphy_val = dphy_rx_read(csidev, DPHY_RX_SYS_1);
> > > +     dphy_val |= TIMEBASE_OVR_EN_RW;
> > > +     dphy_rx_write(csidev, DPHY_RX_SYS_1, dphy_val);
> > > +
> > > +     /* Set cfgclkfreqrange */
> > > +     dphy_rx_write(csidev, DPHY_RX_SYS_2,
> > > +                   TIMEBASE_OVR_RW(csidev->cfgclkfreqrange + 0x44));
> > 
> > 
> > RM Rev 2. mentions that depending on cfgclkfreqrange another
> > configuration,
 called counter_for_des_en_config_if, also needs to be
> > set. Is this missing here?
> 
> 
> It isn't needed from my experiment result.

Okay, I'm just doing guesswork trying to figure out why I get those D-PHY 
errors.

> > 
> > 
> > > +}
> > > +
> > > +void dphy_rx_power_off(struct dwc_csi_device *csidev)
> > > +{
> > > +     dwc_csi_write(csidev, CSI2RX_DPHY_RSTZ, 0x0);
> > > +     dwc_csi_write(csidev, CSI2RX_DPHY_SHUTDOWNZ, 0x0);
> > > +}
> > > +
> > > +void dphy_rx_test_code_dump(struct dwc_csi_device *csidev)
> > > +{
> > > +#define DPHY_DEBUG_REG(name)         {name, #name}
> > > +     static const struct {
> > > +             u32 offset;
> > > +             const char * const name;
> > > +     } test_codes[] = {
> > > +             DPHY_DEBUG_REG(DPHY_RX_SYS_0),
> > > +             DPHY_DEBUG_REG(DPHY_RX_SYS_1),
> > > +             DPHY_DEBUG_REG(DPHY_RX_SYS_2),
> > > +     };
> > > +     unsigned int i;
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(test_codes); i++)
> > > +             dev_dbg(csidev->dev, "%14s[0x%02x]: 0x%02x\n",
> > > +                     test_codes[i].name, test_codes[i].offset,
> > > +                     dphy_rx_read(csidev, test_codes[i].offset));
> > > +}
> > 
> > 
> > Could you also provide a complete DT configuration? I tried myself, but I
> > just ended up in getting errors while trying to use a MIPI-CSI camera
> > dwc-mipi-csi2 4ae00000.mipi-csi: IPI Interface Fatal Error events:
> > 2800064
> > dwc-mipi-csi2 4ae00000.mipi-csi: PHY Error events: 2174
> > dwc-mipi-csi2 4ae00000.mipi-csi: IPI Interface Fatal Error events:
> > 2800064
> > dwc-mipi-csi2 4ae00000.mipi-csi: PHY Error events: 2174
> 
> 
> Sure, I can provide full patches that use i.MX93 platform with AP1302 if you
> are interested.
> Will send you in another mails.

Thanks.

Best regards,
Alexander
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/






[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux