I'm using SDK 9.0 on the SK-AM62A-LP board with the TI provided SD card image "tisdk-edgeai-image-am62axx-evm_SDK9-0.wic.xz". I'm trying to debug a new image sensor (Device Under Test) and therefore collecting CSI-2 RX register information for debug (potentially along with D-Phy register info as well). I have tried using the devmem2 application while streaming from a working OV5647 in 640x480, 8-bpp mode (with modified driver code) but the corresponding CSI-2 RX register values don't seem to be reporting all of the information. I'm getting pretty much identical results using the IMX219 sensor as well. I've additionally gone as far as creating a driver attribute in the cdns-csi2rx.c driver to read register 0x30101060 to check that the register value wasn't being blocked from userspace in some way but it is producing the same results as using the devmem2 application. From the TRM, I've additionally edited the cdns-csi2rx.c driver file to ignore ECC, CRC and Data_ID errors in hopes of seeing some sign of life from the DUT but so far I'm not able to see a video window open on my attached monitor when using gstreamer or capture images using the following command: "v4l2-ctl --device /dev/video 3 --set-fmt-video=width=640,height=480pixelformat=BA81 --stream-mmap --stream-to =v4l2Test1.bin --stream-count=6"
cdns_csi2rx.c edits:
--- a/Desktop/AM62A/Driver_Files/Original_Files/cdns-csi2rx.c +++ b/ti/AM62A/SDK-9_0/board-support/linux-6.1.33+gitAUTOINC+8f7f371be2-g8f7f371be2/drivers/media/platform/cadence/cdns-csi2rx.c @@ -21,6 +21,9 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> +#define DEBUG +#define ENABLE_ATTRS 1 + #define CSI2RX_DEVICE_CFG_REG 0x000 #define CSI2RX_SOFT_RESET_REG 0x004 @@ -31,6 +34,18 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +// ST added +#define CSI2RX_ERROR_BYPASS 1 +#if CSI2RX_ERROR_BYPASS +#define CSI2RX_ERROR_BYPASS_REG 0x010 +#define CSI2RX_ERROR_BYPASS_DATA_ID BIT(2) +#define CSI2RX_ERROR_BYPASS_ECC BIT(1) +#define CSI2RX_ERROR_BYPASS_CRC BIT(0) +#endif + +#define CSI2RX_INTEGRATION_DEBUG 0x060 +// ST added end + #define CSI2RX_DPHY_LANE_CTRL_REG 0x40 #define CSI2RX_DPHY_CL_RST BIT(16) #define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) @@ -275,8 +290,13 @@ static int csi2rx_configure_external_dphy(struct csi2rx_priv *csi2rx) link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler, fmt->bpp, 2 * csi2rx->num_lanes); - if (link_freq < 0) + if (link_freq < 0) { + dev_info(csi2rx->dev, "Link Frequency < 0: %lld\n", link_freq); + // printk("csi2rx: Link Frequency < 0: %lld\n", link_freq); return link_freq; + } + dev_info(csi2rx->dev, "Link Frequency: %lld\n", link_freq); + // printk("csi2rx: Link Frequency: %lld\n", link_freq); ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq, csi2rx->num_lanes, cfg); @@ -328,6 +348,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); + // ST added +#if CSI2RX_ERROR_BYPASS + reg = CSI2RX_ERROR_BYPASS_DATA_ID | CSI2RX_ERROR_BYPASS_ECC | CSI2RX_ERROR_BYPASS_CRC; + writel(reg, csi2rx->base + CSI2RX_ERROR_BYPASS_REG); +#endif + // ST added end + /* Enable DPHY clk and data lanes. */ if (csi2rx->dphy) { reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; @@ -363,7 +390,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); - + // ST_edits start + // writel(0, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); + // ST_edits end + /* Let all virtual channels through. */ writel(CSI2RX_STREAM_DATA_CFG_VC_ALL, csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); @@ -418,6 +448,19 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) clk_disable_unprepare(csi2rx->pixel_clk[i]); } + // ST_edits start + ret = readl_relaxed_poll_timeout(csi2rx->base + + CSI2RX_INTEGRATION_DEBUG, + val, + (val & BIT(28)), + 10, 10000); + if (ret == 0) { + dev_info(csi2rx->dev,"Stream stopped, Register 0x30101060 = 0x%x", val); + } else { + dev_warn(csi2rx->dev,"Stream stopped, Couldn't read Register 0x30101060"); + } + // ST_edits end + clk_disable_unprepare(csi2rx->p_clk); if (csi2rx->dphy) { @@ -840,6 +883,74 @@ static int csi2rx_resume(struct device *dev) return 0; } +// ST_edits start +#if ENABLE_ATTRS +static ssize_t csi2rx_intdebugreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 val; + int ret; + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + + /* + for (i = 0; i < csi2rx->max_streams; i++) { + writel(CSI2RX_STREAM_CTRL_STOP, + csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + + ret = readl_relaxed_poll_timeout(csi2rx->base + + CSI2RX_STREAM_STATUS_REG(i), + val, + (val & CSI2RX_STREAM_STATUS_RDY), + 10, 10000); + if (ret) + dev_warn(csi2rx->dev, "Failed to stop stream%u\n", i); + + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + */ + + ret = readl_relaxed_poll_timeout(csi2rx->base + + CSI2RX_INTEGRATION_DEBUG, + val, val, 10, 100); + if (ret == 0) { + dev_info(csi2rx->dev,"Register 0x30101060 = 0x%x", val); + printk("csi2rx_dev_attr: Register 0x30101060 = 0x%x", val); + } else { + dev_warn(csi2rx->dev,"Couldn't read Register 0x30101060"); + printk("csi2rx_dev_attr: Couldn't read Register 0x30101060"); + } + + return 1; +} + +static DEVICE_ATTR(intdebugreg, S_IRUGO, csi2rx_intdebugreg_show, NULL); + +static struct attribute *csi2rx_dev_attrs[] = { + &dev_attr_intdebugreg.attr, + NULL +}; + +static struct attribute_group csi2rx_dev_attr_group = { + .name = "csi2rx_dev", + .attrs = csi2rx_dev_attrs, +}; + +/* +static const struct attribute_group *csi2rx_dev_attr_groups[] = { + &csi2rx_dev_attr_group, + NULL +}; + +static struct device_driver csi2rx_dev = { + .bus = &phy_bus_type, + .dev_groups = csi2rx_dev_attr_groups, + .groups = csi2rx_dev_attr_group, + .release = &csi2rx_dev_release, +}; +*/ +#endif +// ST_edits end + + static int csi2rx_probe(struct platform_device *pdev) { struct csi2rx_priv *csi2rx; @@ -891,6 +1002,16 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret < 0) goto err_free_subdev; + // ST_edits start +#if ENABLE_ATTRS + // pdev->dev.groups = csi2rx_dev_attr_groups; + ret = sysfs_create_group(&pdev->dev.kobj, &csi2rx_dev_attr_group); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot register csi2rx Driver attributes: %d\n", ret); + } +#endif + // ST_edits end + dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, @@ -915,6 +1036,13 @@ static int csi2rx_remove(struct platform_device *pdev) { struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); + + // ST_edits start +#if ENABLE_ATTRS + sysfs_remove_group(&pdev->dev.kobj, &csi2rx_dev_attr_group); +#endif + // ST_edits end + v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); v4l2_async_unregister_subdev(&csi2rx->subdev);
From looking at Figure 12-388 in the TRM and reading the CSI peripheral section, I'm under the impression that CSI-2 RX Stream0 is the one to examine. Here are the results of reading the registers using devmem2 (e.g. "devmem2 0x30101074 w"):
(Note: A couple registers are read repeatedly as they are expected to change frequently):
I'm not understanding why the Protocol Datatype or Protocol Word Type aren't being reported in 0x30101060.
I would expect to see an error at some point in 0x30101074, but haven't observed one yet even when the bits are set in register 0x30101010 to not ignore them.
The field values are always 0 in the Stream0 Monitor registers 0x30101114 and 0x30101118. Do these have to be enabled by register 0x30101110 perhaps and what is the procedure for doing that?
The COUNT field in FIFO_FILL_LVL register 0x30101128 is always 0, which seems suspicious.
For the DUT, register 0x30101104 is always 0x80000111 which indicates it is always in a waiting state.
Adding a print of the time in the dma callback function in j721e-csi2rx, I observed it print the time multiple times a second on the OV5647 and IMX219 but it has yet to be observed to be printed on the DUT setup. (Note: I've also added a few other prints during debug):
--- a/Desktop/AM62A/Driver_Files/Original_Files/j721e-csi2rx.c +++ b/ti/AM62A/SDK-9_0/board-support/linux-6.1.33+gitAUTOINC+8f7f371be2-g8f7f371be2/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -7,6 +7,8 @@ * Author: Pratyush Yadav <p.yadav@ti.com> */ +#define DEBUG + #include <linux/bitfield.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -334,6 +336,13 @@ static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt, bpl = (pix->width * ALIGN(bpp, 8)) >> 3; pix->bytesperline = ALIGN(bpl, 16); + + // ST_edits start + // printk("j721e-csi2rx: Pix width is %d", pix->width); + // printk("j721e-csi2rx: Pix height is %d", pix->height); + // printk("j721e-csi2rx: Pix image size is %d", pix->sizeimage); + // printk("j721e-csi2rx: Pix bytes per line is %d", pix->bytesperline); + // ST_edits end } static int ti_csi2rx_querycap(struct file *file, void *priv, @@ -682,6 +691,9 @@ static void ti_csi2rx_dma_callback(void *param) */ buf->vb.vb2_buf.timestamp = ktime_get_ns(); buf->vb.sequence = ctx->sequence++; + // ST_edits start + dev_info(ctx->csi->dev,"dma_callback buffer timestamp %lld", buf->vb.vb2_buf.timestamp); + // ST_edits end spin_lock_irqsave(&dma->lock, flags); @@ -735,6 +747,10 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, return ret; dma_async_issue_pending(ctx->dma.chan); + + // ST_edits start + dev_info(ctx->csi->dev,"DMA start completed.\n"); + // ST_edits end return 0; }
Is there a way to read the contents of the DMA buffer to see the stream data?
I have access to a differential probe and a high speed oscilloscope and I've been examining the signals going over the FFC on the OV5647 and the DUT but haven't identified anything that would prevent the buffer from filling up so far.
Any other info or steps for debugging the CSI-2 RX and MIPI D-PHY peripherals would be appreciated at this point.
Thank you,
Steve T