From 0b3f646bdf111996fb4362d80e66bf91e12d13fd Mon Sep 17 00:00:00 2001 From: "Goh, Wei Khang1" Date: Thu, 5 Feb 2026 15:00:00 +0800 Subject: [PATCH] ECG Camera Scaling Release 2026 Q1.1.1a This patch consolidates multiple improvements across the driver codebase: Driver Refactoring: - Refactor isx031 I2C driver for upstream submission with improved code structure - Fix gpio read operation by adding read wait time Configuration Updates: - Add AR0233 IPU6 XML configuration with complete GMSL pipeline setup - Update AR0820 IPU6 XML descriptions for better clarity - Fix incorrect whitespace in ISX031 IPU6 max9x device names Documentation Improvements: - Add comprehensive AR0233 userspace GMSL documentation with BIOS configuration - Update AR0820 userspace GMSL documentation - Update ISX031 sample GStreamer commands in userspace documentation - Fix kernelspace.md for AR0233, AR0820, ISX031 - Improve README format and DKMS build instructions Changes included: - media: i2c: isx031: Refactor driver for upstream submission - config: isx031: ipu6: Fix incorrect whitespace in max9x device names - config: isx031: Update sample commands in userspace-gmsl.md - config: ar0233: Add IPU6 XML config & Update userspace-gmsl.md - config: ar0820: Update IPU6 XML description & Update userspace-gmsl.md - doc: Fix repo path info in kernelspace.md - Update README format and DKMS build instructions Signed-off-by: Goh, Wei Khang1 --- README.md | 88 ++-- config/ar0233/ipu6/ar0820-ar0233-1.xml | 46 +++ config/ar0233/userspace-gmsl.md | 81 +++- config/ar0820/ipu6/ar0820-1.xml | 2 +- config/ar0820/userspace-gmsl.md | 40 +- config/isx031/ipu6/isx031-2.xml | 10 +- config/isx031/ipu6/isx031-3.xml | 16 +- config/isx031/ipu6/isx031-4.xml | 126 +++--- config/isx031/userspace-gmsl.md | 24 +- doc/ar0233/kernelspace.md | 8 +- doc/ar0820/kernelspace.md | 6 +- doc/isx031/kernelspace.md | 29 +- drivers/media/i2c/isx031.c | 545 ++++++++++++++----------- 13 files changed, 614 insertions(+), 407 deletions(-) create mode 100644 config/ar0233/ipu6/ar0820-ar0233-1.xml diff --git a/README.md b/README.md index 574386d..4a04f4c 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ This repository contains reference drivers and configurations for Intel MIPI CSI ## Getting Started with Reference Camera 1. Install Intel BKC image: - - Go to rdc.intel.com - - Download IPU Master Collateral (Document #817101) + - Go to [rdc.intel.com](https://www.intel.com/content/www/us/en/resources-documentation/developer.html) + - Download IPU Master Collateral (Document [#817101](https://www.intel.com/content/www/us/en/secure/content-details/817101/intel-ipu6-enabling-partners-technical-collaterals-advisory.html?DocID=817101)) - In Document #817101 - Go to **Linux Getting Started Guide (GSG) Documentation** section - Search for your platform under **Platform** column @@ -47,7 +47,7 @@ cd Intel-MIPI-CSI-Camera-Reference-Driver git checkout ``` -3. Clone ipu repositories and checkout to below commits +3. Clone IPU repositories and checkout to below commits ```bash cd $HOME git clone -b iotg_ipu6 https://github.com/intel/ipu6-camera-bins.git @@ -60,7 +60,7 @@ cd ipu6-camera-hal git checkout a647a0a0c660c1e43b00ae9e06c0a74428120f3a cd $HOME -git clone -b icmaerasrc_slim_api https://github.com/intel/icamerasrc.git +git clone -b icamerasrc_slim_api https://github.com/intel/icamerasrc.git cd icamerasrc git checkout 4fb31db76b618aae72184c59314b839dedb42689 @@ -70,15 +70,12 @@ cd ipu6-drivers git checkout 71e2e426f3e16f8bc42bf2f88a21fa95eeca5923 ``` -4. Deploy and build IPU userspace components +4. Deploy ipu6-camera-bins runtime & development files ```bash -# ipu6-camera-bins: Deploy files using below steps: -# Runtime Files mkdir -p /lib/firmware/intel/ipu sudo cp -r $HOME/ipu6-camera-bins/lib/lib* /usr/lib/ sudo cp -r $HOME/ipu6-camera-bins/lib/firmware/intel/ipu/*.bin /lib/firmware/intel/ipu -# Development files mkdir -p /usr/include /usr/lib/pkgconfig sudo cp -r $HOME/ipu6-camera-bins/include/* /usr/include/ sudo cp -r $HOME/ipu6-camera-bins/lib/pkgconfig/* /usr/lib/pkgconfig/ @@ -87,60 +84,79 @@ for lib in $HOME/ipu6-camera-bins/lib/lib*.so.*; do \ lib=${lib##*/}; \ sudo ln -s $lib /usr/lib/${lib%.*}; \ done +``` -# ipu6-camera-hal and icamerasrc: Build using below steps: -# Make sure ipu6-camera-hal & icamerasrc are both in same directory, i.e. $HOME. +5. Build ipu6-camera-hal (ipu6-camera-hal & icamerasrc repository must be in same directory, i.e. $HOME) +```bash cd $HOME cp $HOME/ipu6-camera-hal/build.sh . -# To build IPU6EPMTL with DMA (recommended) -./build.sh -d --board ipu_mtl -# To build IPU6EPMTL without DMA -./build.sh --board ipu_mtl +./build.sh -d --board ipu_mtl +``` -# Install built libraries to Target -sudo cp -r $HOME/out//install/etc/* /etc/ -sudo cp -r $HOME/out//install/usr/include/* /usr/include/ -sudo cp -r $HOME/out//install/usr/lib/* /usr/lib/ +6. Install built libraries to target system +```bash +sudo cp -r $HOME/out/ipu_mtl/install/etc/* /etc/ +sudo cp -r $HOME/out/ipu_mtl/install/usr/include/* /usr/include/ +sudo cp -r $HOME/out/ipu_mtl/install/usr/lib/* /usr/lib/ ``` -5. (optional) Setup media-ctl to Version 1.30 for debugging +7. (optional) Setup media-ctl to Version 1.30 for debugging ```bash -# Install dependencies -sudo apt-get install debhelper doxygen gcc git graphviz libasound2-dev libjpeg-dev libqt5opengl5-dev libudev-dev libx11-dev meson pkg-config qtbase5-dev udev libsdl2-dev libbpf-dev llvm clang libjson-c-dev +sudo apt-get install \ + debhelper doxygen gcc git graphviz \ + libasound2-dev libjpeg-dev libqt5opengl5-dev libudev-dev libx11-dev \ + meson pkg-config qtbase5-dev udev libsdl2-dev libbpf-dev llvm clang \ + libjson-c-dev -# Build and install v4l-utils 1.30 git clone https://github.com/gjasny/v4l-utils.git -b stable-1.30 cd v4l-utils meson build/ sudo ninja -C build/ install ``` -6. (If needed) Refer to `doc/\/kernelspace.md`, if there are dependency patches to enable the sensor, apply the patch to **ipu6-drivers**. +8. Configure isys_freq value in `/etc/modprobe.d/ipu.conf` +```bash +sudo bash -c 'echo "options intel-ipu6 isys_freq_override=400" >> /etc/modprobe.d/ipu.conf' +``` + +9. Apply kernel patches (if required) + +Refer to `doc//kernelspace.md` to check if dependency patches are needed for your sensor. +If patches are required, apply them to the **ipu6-drivers** repository: + ```bash cd $HOME/ipu6-drivers -git am $HOME/Intel-MIPI-CSI-Camera-Reference-Driver/.../XXX.patch +git am $HOME/Intel-MIPI-CSI-Camera-Reference-Driver/patch//XXX.patch ``` -7. Dkms build ipu6-drivers +10. Build and install ipu6-drivers using DKMS ```bash cd $HOME/ipu6-drivers + +sudo dkms remove ipu6-drivers/0.0.0 +sudo rm -rf /usr/src/ipu6-drivers-0.0.0/ + sudo dkms add . sudo dkms build -m ipu6-drivers -v 0.0.0 sudo dkms install -m ipu6-drivers -v 0.0.0 ``` -8. Dkms build this repository +11. Build and install Intel-MIPI-CSI-Camera-Reference-Driver using DKMS ```bash cd $HOME/Intel-MIPI-CSI-Camera-Reference-Driver + +sudo dkms remove ipu-camera-sensor/0.1 +sudo rm -rf /usr/src/ipu-camera-sensor-0.1/ + sudo dkms add . sudo dkms build -m ipu-camera-sensor -v 0.1 sudo dkms install -m ipu-camera-sensor -v 0.1 ``` -9. Power off Board. Connect Sensor Hardware. Power On Board and Sensor. +12. Power off the board. Connect sensor hardware. Power on the board and sensor (if external power required). -10. Boot into BIOS menu. Configure BIOS option according to config/\/userspace-\.md, +13. Boot into BIOS menu. Configure BIOS options according to `config//userspace-.md` - Follow Section **BIOS Configuration Table** - **\ Camera Option** and/or - **\ Control Logic** @@ -149,14 +165,16 @@ sudo dkms install -m ipu-camera-sensor -v 0.1 - Follow `config/isx031/userspace-gmsl.md` - Follow **BIOS Configuration Table** >> **IPU6EPMTL Camera Option** -11. Boot into OS. Check if sensor is enumerated correctly in media-ctl - - If sensor is not listed, re-check hardware connection and BIOS configuration +14. Boot into OS. Check if sensor is enumerated correctly in media-ctl + - If sensor is not listed + - Recheck hardware connection (Step 12) + - Recheck BIOS configuration (Step 13) - If issue persists, refer to Intel IPU Team for support. ```bash media-ctl -p | grep -ie ``` -12. Setup XML file according to config/\/userspace-\.md, +15. Setup XML file according to `config//userspace-.md` - Follow Section **Camera XML File Setup** - **\ Configuration** @@ -164,7 +182,7 @@ media-ctl -p | grep -ie - Follow `config/isx031/userspace-gmsl.md` - Follow **Camera XML File Setup** >> **IPU6EPMTL Configuration** section -13. Export below environment variables or add to shell profile (e.g., `~/.bashrc` ) +16. Export below environment variables or add to shell profile (e.g. `~/.bashrc` ) ```bash export DISPLAY=:0; xhost + export GST_PLUGIN_PATH=/usr/lib/gstreamer-1.0 @@ -178,7 +196,7 @@ export logSink=terminal rm -rf ~/.cache/gstreamer-1.0 ``` -14. Run Gstreamer command according to config/\/userspace-\.md, +17. Run GStreamer command according to `config//userspace-.md` - Follow Section **Sample Userspace Command** - Copy and run command based on your use case. @@ -186,11 +204,11 @@ rm -rf ~/.cache/gstreamer-1.0 - Follow `config/isx031/userspace-gmsl.md` - Follow **Sample Userspace Command** >> **Number of Stream (Single Stream / Multi Stream) Selection** >> command for x1 stream -15. Enjoy your camera stream! +18. Enjoy your camera stream! ## Documentation -Detailed documentation for each sensor can be found in the `doc/` directory, organized by sensor model. +Detailed documentation for each sensor can be found in the [doc/](doc) directory, organized by sensor model. ## Contributing diff --git a/config/ar0233/ipu6/ar0820-ar0233-1.xml b/config/ar0233/ipu6/ar0820-ar0233-1.xml new file mode 100644 index 0000000..5f4e60b --- /dev/null +++ b/config/ar0233/ipu6/ar0820-ar0233-1.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/ar0233/userspace-gmsl.md b/config/ar0233/userspace-gmsl.md index ad8f5e6..4a67d42 100644 --- a/config/ar0233/userspace-gmsl.md +++ b/config/ar0233/userspace-gmsl.md @@ -1,8 +1,62 @@ ## Description -This document provides configuration details for **ar0233** sensor. The BIOS configuration, camera XML file setup, and userspace commands for this sensor is using the **AR0820 GMSL sensor's** setup. Please refer to `config/ar0820/userspace-gmsl.md` for complete configuration details. +This document details the configuration settings for the AR0233 GMSL sensor, providing essential information for system integration. The table below presents the key parameters and their respective values used during system setup and validation. -**Important:** Before using the AR0233 sensor, modify the sensor's XML file resolution value to **1920x1080** to match the AR0233's supported resolution. +## BIOS Configuration Table + +**Note:** No External Clock required. + +#### IPU6EPMTL Camera Option + +**Note:** AR0233 sensor reuses AR0820 sensor Custom HID + +| | Camera1 Link options | +|--- |--- | +| Sensor Model | User Custom | +| Custom HID | AR0820 | +| Lanes Clock division | 4 4 2 2 | +| CRD Version | CRD-D | +| GPIO control | No Control Logic | +| Camera position | Front | +| Flash Support | Driver default | +| Privacy LED | Driver default | +| Rotation | 90 | +| PPR Value | 4 | +| PPR Unit | 2 | +| Camera module name | | +| MIPI port | 0 | +| LaneUsed | x4 | +| MCLK | 19200000 | +| EEPROM Type | ROM_NONE | +| VCM Type | VCM_NONE | +| Number of I2C Components | 3 | +| I2C Channel | I2C1 | +| Device 0 | | +| I2C Address | 48 | +| Device Type | Sensor | +| Device 1 | | +| I2C Address | 44 | +| Device Type | Sensor | +| Device 2 | | +| I2C Address | 50 | +| Device Type | Sensor | +| Customize Device ID List | | +| Customize Device ID Number | 17 | +| Customize Device ID Number | 18 | +| Customize Device ID Number | 19 | +| Flash Driver Selection | Disabled | + +## Camera XML File Setup + +#### IPU6EPMTL Configuration + +1. Import files below to `/etc/camera/ipu6epmtl/sensor` + - ar0820-ar0233-1.xml + +2. Append new sensors into `/etc/camera/ipu6epmtl/libcamhal_profile.xml` + ```xml + + ``` ## Sample Userspace Command @@ -10,8 +64,7 @@ This document provides configuration details for **ar0233** sensor. The BIOS con | Sensor Number | Command Pipeline | |----------|----------| -| 1 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**ar0820-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1080' ! glimagesink sync=false | -| 2 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**ar0820-2** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1080' ! glimagesink sync=false | +| 1 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**ar0233-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1080' ! glimagesink sync=false | **Note**: Refer to icamerasrc device-name property for more sensor details. @@ -19,20 +72,32 @@ This document provides configuration details for **ar0233** sensor. The BIOS con | IO Mode | Command Pipeline | |----------|----------| -| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=**1** ! '**video/x-raw,format=UYVY**,width=1920,height=1080' ! glimagesink sync=false | -| DMA MODE | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=**4** ! '**video/x-raw(memory:DMABuf),drm-format=UYVY**,width=1920,height=1080' ! glimagesink sync=false | +| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0233-1 printfps=true io-mode=**mmap** ! '**video/x-raw,format=UYVY**,width=1920,height=1080' ! glimagesink sync=false | +| DMA MODE | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0233-1 printfps=true io-mode=**dma_mode** ! '**video/x-raw(memory:DMABuf),drm-format=UYVY**,width=1920,height=1080' ! glimagesink sync=false | **Note**: Refer to icamerasrc io-mode property for more sensor details. +#### Sensor Resolution Selection + +| Resolution | Command Pipeline | +|----------|----------| +| 1920x1080 | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=ar0233-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,**width=1920,height=1080**' ! glimagesink sync=false | + +#### Sensor Format Selection + +| Format | Command Pipeline | +|----------|----------| +| UYVY | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=ar0233-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),**drm-format=UYVY**,width=1920,height=1080' ! glimagesink sync=false | + #### Number of Stream (Single Stream / Multi Stream) Selection | Number of Stream | Command Pipeline | |----------|----------| -| x1 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=1** scene-mode=normal device-name=**ar0820-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1080' ! glimagesink sync=false | +| x1 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=1** scene-mode=normal device-name=**ar0233-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1080' ! glimagesink sync=false | #### FPS Result | Number of Stream | IO Mode | FPS Result | |----------|----------|----------| | x1 | MMAP | 30 | -| x1 | DMA MODE | 30 | \ No newline at end of file +| x1 | DMA MODE | 30 | diff --git a/config/ar0820/ipu6/ar0820-1.xml b/config/ar0820/ipu6/ar0820-1.xml index c00f6e9..6b4429b 100644 --- a/config/ar0820/ipu6/ar0820-1.xml +++ b/config/ar0820/ipu6/ar0820-1.xml @@ -15,7 +15,7 @@ --> - + diff --git a/config/ar0820/userspace-gmsl.md b/config/ar0820/userspace-gmsl.md index 85d9959..b14cb87 100644 --- a/config/ar0820/userspace-gmsl.md +++ b/config/ar0820/userspace-gmsl.md @@ -58,15 +58,41 @@ This document details the configuration settings for the AR0820 GMSL sensor, pro ## Sample Userspace Command +#### Sensor Device Selection + +| Sensor Number | Command Pipeline | +|----------|----------| +| 1 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**ar0820-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=3840,height=2160' ! glimagesink sync=false | + #### Frame Buffer Memory Type (IO Mode) Selection + | IO Mode | Command Pipeline | -|---|---| -| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 device-name=ar0820-1 printfps=true io-mode=**1** ! '**video/x-raw,format=UYVY**, width=3840,height=2160' ! glimagesink sync=false| -| DMA MODE| gst-launch-1.0 icamerasrc num-buffers=$num_buffers device-name=ar0820-1 printfps=true io-mode=**4** ! '**video/x-raw(memory:DMABuf)**, drm-format=UYVY, width=3840,height=2160' ! glimagesink sync=false | +|----------|----------| +| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=**mmap** ! '**video/x-raw,format=UYVY**,width=3840,height=2160' ! glimagesink sync=false | +| DMA MODE | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=**dma_mode** ! '**video/x-raw(memory:DMABuf),drm-format=UYVY**,width=3840,height=2160' ! glimagesink sync=false | + +#### Sensor Resolution Selection + +| Resolution | Command Pipeline | +|----------|----------| +| 3840x2160 | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,**width=3840,height=2160**' ! glimagesink sync=false | + +#### Sensor Format Selection + +| Format | Command Pipeline | +|----------|----------| +| UYVY | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=ar0820-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),**drm-format=UYVY**,width=3840,height=2160' ! glimagesink sync=false | + +#### Number of Stream (Single Stream / Multi Stream) Selection + +| Number of Stream | Command Pipeline | +|----------|----------| +| x1 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=1** scene-mode=normal device-name=**ar0820-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=3840,height=2160' ! glimagesink sync=false | #### FPS Result -| IO Mode | FPS Result | -|---|---| -| MMAP | 30 | -| DMA MODE | 30| + +| Number of Stream | IO Mode | FPS Result | +|----------|----------|----------| +| x1 | MMAP | 30 | +| x1 | DMA MODE | 30 | diff --git a/config/isx031/ipu6/isx031-2.xml b/config/isx031/ipu6/isx031-2.xml index 85a91a9..05ccb3d 100644 --- a/config/isx031/ipu6/isx031-2.xml +++ b/config/isx031/ipu6/isx031-2.xml @@ -47,8 +47,8 @@ - - + + @@ -60,14 +60,14 @@ - - + + - + diff --git a/config/isx031/ipu6/isx031-3.xml b/config/isx031/ipu6/isx031-3.xml index 6867ae1..34c1c3c 100644 --- a/config/isx031/ipu6/isx031-3.xml +++ b/config/isx031/ipu6/isx031-3.xml @@ -20,16 +20,16 @@ - - + + - - + + - + @@ -40,7 +40,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -70,7 +70,7 @@ - + diff --git a/config/isx031/ipu6/isx031-4.xml b/config/isx031/ipu6/isx031-4.xml index 066a32e..13dbcbf 100644 --- a/config/isx031/ipu6/isx031-4.xml +++ b/config/isx031/ipu6/isx031-4.xml @@ -18,18 +18,18 @@ - - - - + + + + - - - + + + - - + + @@ -40,38 +40,38 @@ - + - - - - + + + + - - - - + + + + - - - - - + + + + + - - + + - - + + @@ -82,65 +82,65 @@ - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - + + - - + + - - + + - - + + @@ -151,7 +151,7 @@ - + diff --git a/config/isx031/userspace-gmsl.md b/config/isx031/userspace-gmsl.md index d9891bf..01fa28d 100644 --- a/config/isx031/userspace-gmsl.md +++ b/config/isx031/userspace-gmsl.md @@ -116,10 +116,10 @@ This document details the configuration settings for the ISX031 GMSL sensor, pro | Sensor Number | Command Pipeline | |----------|----------| -| 1 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | -| 2 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-2** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | -| 3 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-3** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | -| 4 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-4** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| 1 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| 2 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-2** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| 3 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-3** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| 4 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=**isx031-4** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | **Note**: Refer to icamerasrc device-name property for more sensor details. @@ -127,28 +127,30 @@ This document details the configuration settings for the ISX031 GMSL sensor, pro | IO Mode | Command Pipeline | |----------|----------| -| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=**1** ! '**video/x-raw,format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | -| DMA MODE | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=**4** ! '**video/x-raw(memory:DMABuf),drm-format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | +| MMAP | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=**mmap** ! '**video/x-raw,format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | +| DMA MODE | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=**dma_mode** ! '**video/x-raw(memory:DMABuf),drm-format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | **Note**: Refer to icamerasrc io-mode property for more sensor details. #### Sensor Resolution Selection + | Resolution | Command Pipeline | |----------|----------| -| 1920x1536 | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,**width=1920,height=1536**' ! glimagesink sync=false | +| 1920x1536 | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,**width=1920,height=1536**' ! glimagesink sync=false | #### Sensor Format Selection + | Format | Command Pipeline | |----------|----------| -| UYVY | gst-launch-1.0 icamerasrc num-buffers=-1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),**drm-format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | +| UYVY | gst-launch-1.0 icamerasrc num-buffers=-1 num-vc=1 scene-mode=normal device-name=isx031-1 printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),**drm-format=UYVY**,width=1920,height=1536' ! glimagesink sync=false | #### Number of Stream (Single Stream / Multi Stream) Selection | Number of Stream | Command Pipeline | |----------|----------| -| x1 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=1** scene-mode=normal device-name=**isx031-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | -| x2 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=2** scene-mode=normal device-name=**isx031x2-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink icamerasrc num-buffers=-1 **num-vc=2** scene-mode=normal device-name=**isx031x2-2** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | -| x4 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-1** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-2** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-3** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-4** printfps=true io-mode=4 ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| x1 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=1** scene-mode=normal device-name=**isx031-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| x2 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=2** scene-mode=normal device-name=**isx031x2-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false icamerasrc num-buffers=-1 **num-vc=2** scene-mode=normal device-name=**isx031x2-2** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | +| x4 | gst-launch-1.0 icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-1** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-2** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-3** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false icamerasrc num-buffers=-1 **num-vc=4** scene-mode=normal device-name=**isx031x4-4** printfps=true io-mode=dma_mode ! 'video/x-raw(memory:DMABuf),drm-format=UYVY,width=1920,height=1536' ! glimagesink sync=false | #### FPS Result diff --git a/doc/ar0233/kernelspace.md b/doc/ar0233/kernelspace.md index 8e043c3..0132cfb 100644 --- a/doc/ar0233/kernelspace.md +++ b/doc/ar0233/kernelspace.md @@ -11,8 +11,8 @@ This document outlines the configuration parameters for Sensor AR0233, including 2. Apply the following patches from this repository: ```bash - git am drivers.camera.scaling.sensor/patch/v6.12/0040-media-platform-intel-Add-AR0820-sensor-support-to-IPU.patch - git am drivers.camera.scaling.sensor/patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch + git am /patch/v6.12/0040-media-platform-intel-Add-AR0820-sensor-support-to-IPU.patch + git am /patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch ``` 3. Build and install the DKMS modules: @@ -35,7 +35,7 @@ This document outlines the configuration parameters for Sensor AR0233, including 2. Sensor ISP Physical Address Configuration: - The sensor I2C address is defined in `ipu6-drivers/include/media/i2c/ar0820.h`: + The sensor I2C address is defined in `/include/media/i2c/ar0820.h`: ```c #define AR0820_I2C_ADDRESS 0x6D // Sensing ISP I2C Address is 0xDA >> 1 ``` @@ -49,4 +49,4 @@ This document outlines the configuration parameters for Sensor AR0233, including ## Validated with: - Kernel version 6.12: [linux-intel-lts](https://github.com/intel/linux-intel-lts/tree/lts-v6.12.48-linux-250924T142248Z) - - IPU6 drivers: [ipu6-drivers](https://github.com/intel/ipu6-drivers/tree/iotg_ipu6) (commit `71e2e426`) \ No newline at end of file + - IPU6 drivers: [ipu6-drivers](https://github.com/intel/ipu6-drivers/tree/iotg_ipu6) (commit `71e2e426`) diff --git a/doc/ar0820/kernelspace.md b/doc/ar0820/kernelspace.md index 26e6f38..078b3ab 100644 --- a/doc/ar0820/kernelspace.md +++ b/doc/ar0820/kernelspace.md @@ -11,8 +11,8 @@ This document outlines the configuration parameters for Sensor AR0820, including 2. Apply the following patches from this repository: ```bash - git am drivers.camera.scaling.sensor/patch/v6.12/0040-media-platform-intel-Add-AR0820-sensor-support-to-IPU.patch - git am drivers.camera.scaling.sensor/patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch + git am /patch/v6.12/0040-media-platform-intel-Add-AR0820-sensor-support-to-IPU.patch + git am /patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch ``` 3. Build and install the DKMS modules: @@ -35,7 +35,7 @@ This document outlines the configuration parameters for Sensor AR0820, including 2. Sensor ISP Physical Address Configuration: - The sensor I2C address is defined in `ipu6-drivers/include/media/i2c/ar0820.h`: + The sensor I2C address is defined in `/include/media/i2c/ar0820.h`: ```c #define AR0820_I2C_ADDRESS 0x6D // Sensing ISP I2C Address is 0xDA >> 1 ``` diff --git a/doc/isx031/kernelspace.md b/doc/isx031/kernelspace.md index f31173e..376332a 100644 --- a/doc/isx031/kernelspace.md +++ b/doc/isx031/kernelspace.md @@ -4,27 +4,27 @@ This document outlines the configuration parameters for Sensor ISX031, including ## For Sensor Type: MIPI CSI-2 -1. Verify if line below in ipu_supported_sensors[]** in `drivers.camera.scaling.sensor/drivers/media/pci/intel/ipu-bridge.c` +1. Verify if line below in ipu_supported_sensors[]** in `/drivers/media/pci/intel/ipu-bridge.c` ```c IPU_SENSOR_CONFIG("INTC113C", 1, 300000000) ``` -2. **Sensor ISP Physical Address Configuration:** +2. Sensor ISP Physical Address Configuration: - The sensor I2C address is defined in `ipu6-drivers/include/media/i2c/isx031.h`: + The sensor I2C address is defined in `/include/media/i2c/isx031.h`: ```c #define ISX031_I2C_ADDRESS 0x1a ``` - | Vendor | Physical Address | - |--------|------------------| - | All | 0x1a | + | Vendor | Physical Address | + |--------------------|------------------| + | D3 Embedded | 0x1a | ## For Sensor Type: GMSL ### I2C Address Configuration -1. **MAX9295 Serializer Physical Address:** +1. MAX9295 Serializer Physical Address: The serializer address is configured in `ipu6-drivers/drivers/media/platform/intel/ipu-acpi-pdata.c`: ```c @@ -37,9 +37,9 @@ This document outlines the configuration parameters for Sensor ISX031, including | Leopard Imaging | 0x62 | | Sensing | 0x40 | -2. **Sensor ISP Physical Address:** +2. Sensor ISP Physical Address: - The sensor I2C address is defined in `ipu6-drivers/include/media/i2c/isx031.h`: + The sensor I2C address is defined in `/include/media/i2c/isx031.h`: ```c #define ISX031_I2C_ADDRESS 0x1a ``` @@ -61,10 +61,11 @@ This document outlines the configuration parameters for Sensor ISX031, including 2. Apply the following patches from this repository: ```bash - git am drivers.camera.scaling.sensor/patch/v6.12/0042-modify-physical-address-of-serializer.patch + git am /patch/v6.12/0042-modify-physical-address-of-serializer.patch ``` - > **Note:** After applying, the serializer address is set to `0x62` for LI sensors. Sensors using the default `0x40` will not work unless reverted. + > **Note:** After applying, the serializer address is set to `0x62` for LI sensors. \ +Sensors using the default `0x40` will not work unless reverted. 3. Build and install the DKMS modules: - Refer to the main [README.md](../../README.md) for complete DKMS build and installation instructions. @@ -78,10 +79,10 @@ This document outlines the configuration parameters for Sensor ISX031, including 2. Apply the following patches from this repository: ```bash - git am drivers.camera.scaling.sensor/patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch + git am /patch/v6.12/0041-media-i2c-max9x-Add-FSIN-GPIO-support-for-sensor-syn.patch ``` - > **Note:** This patch enables FSIN GPIO support + > **Note:** This patch enables FSIN GPIO support. 3. Build and install the DKMS modules: - Refer to the main [README.md](../../README.md) for complete DKMS build and installation instructions. @@ -90,4 +91,4 @@ This document outlines the configuration parameters for Sensor ISX031, including ## Validated with: - Kernel version 6.12: [linux-intel-lts](https://github.com/intel/linux-intel-lts/tree/lts-v6.12.48-linux-250924T142248Z) - - IPU6 drivers: [ipu6-drivers](https://github.com/intel/ipu6-drivers/tree/iotg_ipu6) (commit `71e2e426`) \ No newline at end of file + - IPU6 drivers: [ipu6-drivers](https://github.com/intel/ipu6-drivers/tree/iotg_ipu6) (commit `71e2e426`) diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c index 91b6a89..73328f3 100644 --- a/drivers/media/i2c/isx031.c +++ b/drivers/media/i2c/isx031.c @@ -3,25 +3,27 @@ #include #include +#include #include +#include #include #include -#include -#include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0) #include #else #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) +#include +#endif #include #include #include + #include "media/i2c/isx031.h" -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) -#include -#endif -#define to_isx031(_sd) container_of(_sd, struct isx031, sd) + +#define to_isx031(_sd) container_of(_sd, struct isx031, sd) #define ISX031_OTP_TYPE_NAME_L 0x7E8A #define ISX031_OTP_TYPE_NAME_H 0x7E8B @@ -44,7 +46,14 @@ #define ISX031_MODE_4LANES_30FPS 0x17 #define ISX031_MODE_2LANES_30FPS 0x18 -/* To serialize asynchronus callbacks */ +#define ISX031_READ_REG_RETRY_TIMEOUT 50 +#define ISX031_WRITE_REG_RETRY_TIMEOUT 100 +#define ISX031_PM_RETRY_TIMEOUT 10 +#define ISX031_REG_SLEEP_10000US 10000 /* 10ms */ +#define ISX031_REG_SLEEP_20MS 20 /* 20ms */ +#define ISX031_REG_SLEEP_200MS 200 /* 200ms */ + +/* To serialize asynchronous callbacks */ static DEFINE_MUTEX(isx031_mutex); struct isx031_reg { @@ -76,6 +85,7 @@ static const struct isx031_drive_mode isx031_drive_modes[] = { { 4, 60, ISX031_MODE_4LANES_60FPS }, { 4, 30, ISX031_MODE_4LANES_30FPS }, { 2, 30, ISX031_MODE_2LANES_30FPS }, + {} }; struct isx031_mode { @@ -85,50 +95,53 @@ struct isx031_mode { #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) u8 datatype; /* CSI-2 data type ID */ #endif - u32 fps; /* MODE_FPS*/ + u32 fps; /* MODE_FPS */ - const struct isx031_reg_list reg_list; /* Sensor register settings for this resolution */ + /* Sensor register settings for a specific resolution */ + const struct isx031_reg_list reg_list; }; struct isx031 { struct v4l2_subdev sd; - struct media_pad pad; - - const struct isx031_mode *cur_mode; /* Current mode */ - const struct isx031_mode *pre_mode; /* Previous mode */ - u8 lanes; + struct v4l2_ctrl_handler ctrls; - struct i2c_client *client; /* i2c client */ struct isx031_platform_data *platform_data; + struct i2c_client *client; + struct gpio_desc *reset_gpio; struct gpio_desc *fsin_gpio; - struct v4l2_ctrl_handler ctrls; + struct media_pad pad; + + const struct isx031_mode *cur_mode; /* Current mode */ + const struct isx031_mode *pre_mode; /* Previous mode */ + u8 lanes; bool streaming; /* Streaming on/off */ }; static const s64 isx031_link_frequencies[] = { - 300000000ULL + 300000000ULL, }; static const struct isx031_reg isx031_init_reg[] = { - {ISX031_REG_LEN_08BIT, 0xFFFF, 0x00}, /* select mode */ - {ISX031_REG_LEN_08BIT, 0x0171, 0x00}, /* close F_EBD */ - {ISX031_REG_LEN_08BIT, 0x0172, 0x00}, /* close R_EBD */ + {ISX031_REG_LEN_08BIT, 0xFFFF, 0x00}, /* Select mode */ + {ISX031_REG_LEN_08BIT, 0x0171, 0x00}, /* Close F_EBD */ + {ISX031_REG_LEN_08BIT, 0x0172, 0x00}, /* Close R_EBD */ + {} }; static const struct isx031_reg isx031_framesync_reg[] = { - /* External sync */ {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, - {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ + {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* External pulse-based sync */ {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, {ISX031_REG_LEN_08BIT, 0x8AF1, 0x00}, + {} }; static const struct isx031_reg isx031_1920_1536_30fps_reg[] = { - {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* crop enable */ + {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* Crop enable */ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, /* H size = 1920 */ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, /* H croped 0 */ @@ -147,10 +160,11 @@ static const struct isx031_reg isx031_1920_1536_30fps_reg[] = { {ISX031_REG_LEN_08BIT, 0xBF0B, 0x06}, {ISX031_REG_LEN_08BIT, 0xBF0C, 0x00}, {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, + {} }; static const struct isx031_reg isx031_1920_1080_30fps_reg[] = { - {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* crop enable */ + {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* Crop enable */ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, /* H size = 1920 */ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, /* H croped 0 */ @@ -169,10 +183,11 @@ static const struct isx031_reg isx031_1920_1080_30fps_reg[] = { {ISX031_REG_LEN_08BIT, 0xBF0B, 0x04}, {ISX031_REG_LEN_08BIT, 0xBF0C, 0xE4}, {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, + {} }; static const struct isx031_reg isx031_1280_720_30fps_reg[] = { - {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* crop enable */ + {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, /* Crop enable */ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x00}, /* H size = 1280 */ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x05}, {ISX031_REG_LEN_08BIT, 0x8AAC, 0x40}, /* H croped 320*2 */ @@ -191,6 +206,7 @@ static const struct isx031_reg isx031_1280_720_30fps_reg[] = { {ISX031_REG_LEN_08BIT, 0xBF0B, 0x02}, {ISX031_REG_LEN_08BIT, 0xBF0C, 0x98}, {ISX031_REG_LEN_08BIT, 0xBF0D, 0x01}, + {} }; static const struct isx031_reg_list isx031_init_reg_list = { @@ -220,34 +236,34 @@ static const struct isx031_reg_list isx031_1280_720_30fps_reg_list = { static const struct isx031_mode supported_modes[] = { { - .width = 1920, - .height = 1536, - .code = MEDIA_BUS_FMT_UYVY8_1X16, + .width = 1920, + .height = 1536, + .code = MEDIA_BUS_FMT_UYVY8_1X16, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) - .datatype = MIPI_CSI2_DT_YUV422_8B, + .datatype = MIPI_CSI2_DT_YUV422_8B, #endif - .fps = 30, - .reg_list = isx031_1920_1536_30fps_reg_list, + .fps = 30, + .reg_list = isx031_1920_1536_30fps_reg_list, }, { - .width = 1920, - .height = 1080, - .code = MEDIA_BUS_FMT_UYVY8_1X16, + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_UYVY8_1X16, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) - .datatype = MIPI_CSI2_DT_YUV422_8B, + .datatype = MIPI_CSI2_DT_YUV422_8B, #endif - .fps = 30, - .reg_list = isx031_1920_1080_30fps_reg_list, + .fps = 30, + .reg_list = isx031_1920_1080_30fps_reg_list, }, { - .width = 1280, - .height = 720, - .code = MEDIA_BUS_FMT_UYVY8_1X16, + .width = 1280, + .height = 720, + .code = MEDIA_BUS_FMT_UYVY8_1X16, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) - .datatype = MIPI_CSI2_DT_YUV422_8B, + .datatype = MIPI_CSI2_DT_YUV422_8B, #endif - .fps = 30, - .reg_list = isx031_1280_720_30fps_reg_list, + .fps = 30, + .reg_list = isx031_1280_720_30fps_reg_list, }, }; @@ -255,17 +271,19 @@ static int isx031_read_reg(struct i2c_client *client, u16 reg, u16 len, u32 *val { struct i2c_msg msgs[2]; u8 addr_buf[2]; - u8 data_buf[4] = {0}; + u8 data_buf[4] = { 0 }; int ret; if (len > 4) return -EINVAL; put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = sizeof(addr_buf); msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; @@ -283,80 +301,100 @@ static int isx031_read_reg(struct i2c_client *client, u16 reg, u16 len, u32 *val static int isx031_read_reg_state(struct i2c_client *client, u32 *val) { int ret; - int i = 0; - int retry = 50; + int i; - for (i = 0; i < retry; i++) { + for (i = 0; i < ISX031_READ_REG_RETRY_TIMEOUT; i++) { ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, val); - if (ret == 0) - break; - usleep_range(10000, 10500); + ISX031_REG_LEN_08BIT, val); + if (!ret) + return 0; + + usleep_range(ISX031_REG_SLEEP_10000US, + ISX031_REG_SLEEP_10000US + 500); } return ret; } -static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) +static int isx031_read_reg_otp(struct i2c_client *client, u16 reg, u32 *val) +{ + int ret; + int i; + + for (i = 0; i < ISX031_READ_REG_RETRY_TIMEOUT; i++) { + ret = isx031_read_reg(client, reg, ISX031_REG_LEN_08BIT, val); + if (!ret) + return 0; + + usleep_range(ISX031_REG_SLEEP_10000US, + ISX031_REG_SLEEP_10000US + 500); + } + + return ret; +} + +static int isx031_write_reg(struct i2c_client *client, u16 reg, u16 len, u32 val) { - struct i2c_client *client = isx031->client; u8 buf[6]; + int ret; if (len > 4) return -EINVAL; - dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, val); put_unaligned_be16(reg, buf); - put_unaligned_be32(val << 8 * (4 - len), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) { - dev_err(&client->dev, "%s:failed: reg=%2x\n", __func__, reg); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) return -EIO; - } return 0; } -static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) +static int isx031_write_reg_retry(struct i2c_client *client, u16 reg, u16 len, u32 val) { int ret; - int i = 0; - int retry = 100; + int i; - for (i = 0; i < retry; i++) { - ret = isx031_write_reg(isx031, reg, len, val); + for (i = 0; i < ISX031_WRITE_REG_RETRY_TIMEOUT; i++) { + ret = isx031_write_reg(client, reg, len, val); if (!ret) - break; - msleep(20); + return 0; + + msleep(ISX031_REG_SLEEP_20MS); } return ret; } -static int isx031_write_reg_list(struct isx031 *isx031, +static int isx031_write_reg_list(struct i2c_client *client, const struct isx031_reg_list *r_list, - bool isRetry) + bool is_retry) { - struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); unsigned int i; int ret; for (i = 0; i < r_list->num_of_regs; i++) { - if (r_list->regs[i].mode == ISX031_REG_LEN_DELAY) { - msleep(r_list->regs[i].val); + const struct isx031_reg *reg = &r_list->regs[i]; + + if (reg->mode == ISX031_REG_LEN_DELAY) { + msleep(reg->val); continue; } - ret = isRetry ? - isx031_write_reg_retry(isx031, - r_list->regs[i].address, + + if (is_retry) + ret = isx031_write_reg_retry(client, reg->address, ISX031_REG_LEN_08BIT, - r_list->regs[i].val) : - isx031_write_reg(isx031, r_list->regs[i].address, + reg->val); + else + ret = isx031_write_reg(client, reg->address, ISX031_REG_LEN_08BIT, - r_list->regs[i].val); + reg->val); + if (ret) { dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", - r_list->regs[i].address, ret); + "write reg failed (addr=0x%04x, err=%d)\n", + reg->address, ret); return ret; } } @@ -369,7 +407,8 @@ static int isx031_find_drive_mode(int lanes, int fps) int i; for (i = 0; i < ARRAY_SIZE(isx031_drive_modes); i++) { - if (isx031_drive_modes[i].lanes == lanes && isx031_drive_modes[i].fps == fps) + if (isx031_drive_modes[i].lanes == lanes && + isx031_drive_modes[i].fps == fps) return isx031_drive_modes[i].mode; } @@ -378,14 +417,17 @@ static int isx031_find_drive_mode(int lanes, int fps) static int isx031_set_drive_mode(struct isx031 *isx031) { - int ret; - int mode; + struct i2c_client *client = isx031->client; + int mode, ret; mode = isx031_find_drive_mode(isx031->lanes, isx031->cur_mode->fps); - if (mode < 0) + if (mode < 0) { + dev_err(&client->dev, "Failed to find drive mode\n"); return mode; + } + + ret = isx031_write_reg(client, ISX031_REG_MODE_SELECT, 1, mode); - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); return ret; } @@ -400,43 +442,39 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) mode = ISX031_MODE_STANDBY; else if (state == ISX031_STATE_STREAMING) mode = ISX031_MODE_STREAMING; + else + return -EINVAL; ret = isx031_read_reg_state(client, &val); if (ret) { - dev_err(&client->dev, "failed to read sensor state"); + dev_err(&client->dev, "Failed to read sensor state\n"); return ret; } cur_mode = val; - /* Note: Ideally, drive mode should only be set if isx031->lanes != 0, - * which would mean the number of lanes is obtained from platform data. - * Currently, drive mode is always set. - */ ret = isx031_set_drive_mode(isx031); if (ret) { - dev_err(&client->dev, "failed to set drive mode"); + dev_err(&client->dev, "Failed to set drive mode\n"); return ret; } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F_LOCK, 1, - ISX031_MODE_UNLOCK); + ret = isx031_write_reg(client, ISX031_REG_MODE_SET_F_LOCK, 1, + ISX031_MODE_UNLOCK); if (ret) { - dev_err(&client->dev, "failed to unlock mode"); + dev_err(&client->dev, "Failed to unlock mode\n"); return ret; } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, - mode); + ret = isx031_write_reg(client, ISX031_REG_MODE_SET_F, 1, mode); if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", + dev_err(&client->dev, "Failed to transit mode from 0x%x to 0x%x\n", cur_mode, mode); return ret; } - /* streaming transit to standby need 1 frame+5ms */ ret = isx031_read_reg_state(client, &val); if (ret) { - dev_err(&client->dev, "failed to read sensor state"); + dev_err(&client->dev, "Failed to read sensor state\n"); return ret; } @@ -449,28 +487,29 @@ static int isx031_initialize_module(struct isx031 *isx031) int ret; u32 val = 0; - /* read sensor current mode */ + /* Read sensor current state */ ret = isx031_read_reg_state(client, &val); if (ret) { - dev_err(&client->dev, "failed to read sensor state"); + dev_err(&client->dev, "Failed to read sensor state\n"); return ret; } - dev_dbg(&client->dev, "sensor in mode 0x%x", val); - - /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly */ + /* If sensor is streaming, transition to startup before initialization */ if (val == ISX031_STATE_STREAMING) { - if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) + ret = isx031_mode_transit(isx031, ISX031_STATE_STARTUP); + if (ret) return ret; } - ret = isx031_write_reg_list(isx031, &isx031_init_reg_list, true); + ret = isx031_write_reg_list(client, &isx031_init_reg_list, true); if (ret) return ret; - if (isx031->platform_data != NULL && !isx031->platform_data->irq_pin_flags) { - ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list, false); + + if (isx031->platform_data && + !isx031->platform_data->irq_pin_flags) { + ret = isx031_write_reg_list(client, &isx031_framesync_reg_list, false); if (ret) { - dev_err(&client->dev, "failed in set framesync."); + dev_err(&client->dev, "Failed to set framesync\n"); return ret; } } @@ -480,45 +519,32 @@ static int isx031_initialize_module(struct isx031 *isx031) static int isx031_identify_module(struct i2c_client *client) { - u32 NAME_L = 0; - u32 NAME_H = 0; - int ret = 0; - int i = 0; - int retry = 50; - - for (i = 0; i < retry; i++) { - ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_L, - ISX031_REG_LEN_08BIT, &NAME_L); - if (!ret) - break; - } + u32 name_l = 0; + u32 name_h = 0; + u16 module_id; + int ret; - if (i == retry) { - dev_err(&client->dev, "isx031 read NAME_L failed"); + ret = isx031_read_reg_otp(client, ISX031_OTP_TYPE_NAME_L, &name_l); + if (ret) { + dev_err(&client->dev, "Failed to read OTP NAME_L register\n"); return ret; } - for (i = 0; i < retry; i++) { - ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_H, - ISX031_REG_LEN_08BIT, &NAME_H); - if (!ret) - break; - } - - if (i == retry) { - dev_err(&client->dev, "isx031 read NAME_H failed"); + ret = isx031_read_reg_otp(client, ISX031_OTP_TYPE_NAME_H, &name_h); + if (ret) { + dev_err(&client->dev, "Failed to read OTP NAME_H register\n"); return ret; } - if (((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | NAME_L) != - ISX031_OTP_MODULE_ID_L) { - dev_err(&client->dev, "isx031 module id mismatch: 0x%4.4x\n", - ((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | - NAME_L)); + module_id = ((name_h & ISX031_OTP_TYPE_NAME_H_FIELD) << 8) | name_l; + if (module_id != ISX031_OTP_MODULE_ID_L) { + dev_err(&client->dev, + "Invalid module ID: expected 0x%04x, got 0x%04x\n", + ISX031_OTP_MODULE_ID_L, module_id); return -ENODEV; } - return ret; + return 0; } static void isx031_update_pad_format(const struct isx031_mode *mode, @@ -536,27 +562,27 @@ static int isx031_get_num_lane(struct isx031 *isx031, struct device *dev) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - int ret; endpoint = - fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); if (!endpoint) { - dev_err(dev, "endpoint node not found"); + dev_err(dev, "No endpoint node found\n"); return -EPROBE_DEFER; } ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); if (ret) { - dev_err(dev, "parsing endpoint node fail"); + dev_err(dev, "Failed to parse endpoint node: %d\n", ret); goto out_err; } - /* Check the number of MIPI CSI2 data lanes */ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && - bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { - dev_err(dev, "only 2 or 4 data lanes are currently supported"); + bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + dev_err(dev, "Only 2 or 4 data lanes are supported\n"); + ret = -EINVAL; goto out_err; } @@ -565,37 +591,35 @@ static int isx031_get_num_lane(struct isx031 *isx031, struct device *dev) out_err: v4l2_fwnode_endpoint_free(&bus_cfg); fwnode_handle_put(endpoint); - return ret; } static int isx031_start_streaming(struct isx031 *isx031) { - int ret; struct i2c_client *client = isx031->client; const struct isx031_reg_list *reg_list; + int ret; + /* Apply mode registers only if mode changed */ if (isx031->cur_mode != isx031->pre_mode) { reg_list = &isx031->cur_mode->reg_list; - ret = isx031_write_reg_list(isx031, reg_list, true); + ret = isx031_write_reg_list(client, reg_list, true); if (ret) { - dev_err(&client->dev, "failed to set stream mode"); + dev_err(&client->dev, "Failed to set stream mode\n"); return ret; } isx031->pre_mode = isx031->cur_mode; - } else { - dev_dbg(&client->dev, "same mode, skip write reg list"); } ret = __v4l2_ctrl_handler_setup(&isx031->ctrls); if (ret) { - dev_err(&client->dev, "failed to setup ctrls"); + dev_err(&client->dev, "Failed to setup controls\n"); return ret; } ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to start streaming"); + dev_err(&client->dev, "Failed to start streaming\n"); return ret; } @@ -605,8 +629,11 @@ static int isx031_start_streaming(struct isx031 *isx031) static void isx031_stop_streaming(struct isx031 *isx031) { struct i2c_client *client = isx031->client; - if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) - dev_err(&client->dev, "failed to stop streaming"); + int ret; + + ret = isx031_mode_transit(isx031, ISX031_STATE_STARTUP); + if (ret) + dev_err(&client->dev, "Failed to stop streaming: %d\n", ret); } static int isx031_set_stream(struct v4l2_subdev *sd, int enable) @@ -615,46 +642,47 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) struct i2c_client *client = isx031->client; int ret = 0; + mutex_lock(&isx031_mutex); + if (isx031->streaming == enable) - return 0; + goto unlock; - mutex_lock(&isx031_mutex); if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto unlock; ret = isx031_start_streaming(isx031); if (ret) { - enable = 0; isx031_stop_streaming(isx031); pm_runtime_put(&client->dev); + goto unlock; } + + isx031->streaming = true; + } else { isx031_stop_streaming(isx031); pm_runtime_put(&client->dev); + isx031->streaming = false; } - isx031->streaming = enable; - -err_unlock: +unlock: mutex_unlock(&isx031_mutex); return ret; } static int isx031_enable_streams(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state, - u32 pad, u64 streams_mask) + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { return isx031_set_stream(subdev, true); } static int isx031_disable_streams(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state, - u32 pad, u64 streams_mask) + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { return isx031_set_stream(subdev, false); } @@ -666,13 +694,15 @@ static int __maybe_unused isx031_suspend(struct device *dev) struct isx031 *isx031 = to_isx031(sd); mutex_lock(&isx031_mutex); + if (isx031->streaming) isx031_stop_streaming(isx031); mutex_unlock(&isx031_mutex); /* Active low gpio reset, set 1 to power off sensor */ - gpiod_set_value_cansleep(isx031->reset_gpio, 1); + if (isx031->reset_gpio) + gpiod_set_value_cansleep(isx031->reset_gpio, 1); return 0; } @@ -683,64 +713,68 @@ static int __maybe_unused isx031_resume(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct isx031 *isx031 = to_isx031(sd); const struct isx031_reg_list *reg_list; - int ret = 0; + int ret; + int count; mutex_lock(&isx031_mutex); - int count = 0; - /* Active low gpio reset, set 0 to power on sensor, - * sensor must on back before start resume + * sensor must be on before resume */ - if (isx031->reset_gpio != NULL) { - do { + if (isx031->reset_gpio) { + for (count = 0; count < ISX031_PM_RETRY_TIMEOUT; count++) { gpiod_set_value_cansleep(isx031->reset_gpio, 0); - ret = gpiod_get_value_cansleep(isx031->reset_gpio); - usleep_range(200 * 1000, 200 * 1000 + 500); + msleep(ISX031_REG_SLEEP_200MS); - if (++count >= 10) { - dev_err(&client->dev, "%s: failed to power on reset gpio, reset gpio is %d", __func__, ret); + ret = gpiod_get_value_cansleep(isx031->reset_gpio); + if (ret == 0) break; - } + } - } while (ret != 0); + if (ret != 0) { + dev_err(&client->dev, "Failed to power on sensor in pm resume\n"); + mutex_unlock(&isx031_mutex); + return -ETIMEDOUT; + } } - ret = isx031_identify_module(isx031->client); + ret = isx031_identify_module(client); if (ret) { - dev_err(&client->dev, "isx031 identify module failed"); - goto err_unlock; + dev_err(&client->dev, "Failed to identify sensor module: %d\n", ret); + goto unlock; } ret = isx031_initialize_module(isx031); - if (ret == 0) { - reg_list = &isx031->cur_mode->reg_list; - ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); - goto err_unlock; - } - } else { - dev_err(&client->dev, "isx031 resume initialization failed"); - goto err_unlock; + if (ret) { + dev_err(&client->dev, "Failed to initialize sensor module: %d\n", ret); + goto unlock; + } + + reg_list = &isx031->cur_mode->reg_list; + ret = isx031_write_reg_list(client, reg_list, true); + if (ret) { + dev_err(&client->dev, "Failed to apply cur mode in resume: %d\n", ret); + goto unlock; } + if (isx031->streaming) { ret = isx031_start_streaming(isx031); if (ret) { isx031->streaming = false; isx031_stop_streaming(isx031); - goto err_unlock; + goto unlock; } } -err_unlock: +unlock: mutex_unlock(&isx031_mutex); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) -static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, +static int isx031_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) { struct isx031 *isx031 = to_isx031(sd); @@ -758,7 +792,8 @@ static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, } #else static int isx031_get_frame_desc(struct v4l2_subdev *sd, - unsigned int pad, struct v4l2_mbus_frame_desc *desc) + unsigned int pad, + struct v4l2_mbus_frame_desc *desc) { unsigned int i; @@ -783,24 +818,28 @@ static int isx031_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct isx031 *isx031 = to_isx031(sd); - const struct isx031_mode *mode; - int i; + const struct isx031_mode *mode = NULL; + unsigned int i; - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) + mutex_lock(&isx031_mutex); + + /* Find the best matching mode */ + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { if (supported_modes[i].code == fmt->format.code && - supported_modes[i].width == fmt->format.width && - supported_modes[i].height == fmt->format.height) { + supported_modes[i].width == fmt->format.width && + supported_modes[i].height == fmt->format.height) { mode = &supported_modes[i]; break; } + } - if (i >= ARRAY_SIZE(supported_modes)) + /* If no exact match, use the default mode */ + if (!mode) mode = &supported_modes[0]; - mutex_lock(&isx031_mutex); - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; #elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) @@ -808,9 +847,8 @@ static int isx031_set_format(struct v4l2_subdev *sd, #else *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; #endif - } else { + else isx031->cur_mode = mode; - } mutex_unlock(&isx031_mutex); @@ -828,6 +866,7 @@ static int isx031_get_format(struct v4l2_subdev *sd, struct isx031 *isx031 = to_isx031(sd); mutex_lock(&isx031_mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) fmt->format = *v4l2_subdev_get_try_format(&isx031->sd, cfg, @@ -836,8 +875,7 @@ static int isx031_get_format(struct v4l2_subdev *sd, fmt->format = *v4l2_subdev_get_try_format(&isx031->sd, sd_state, fmt->pad); #else - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); #endif else isx031_update_pad_format(isx031->cur_mode, &fmt->format); @@ -850,6 +888,7 @@ static int isx031_get_format(struct v4l2_subdev *sd, static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { mutex_lock(&isx031_mutex); + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) isx031_update_pad_format(&supported_modes[0], v4l2_subdev_get_try_format(sd, fh->pad, 0)); @@ -860,6 +899,7 @@ static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) isx031_update_pad_format(&supported_modes[0], v4l2_subdev_state_get_format(fh->state, 0)); #endif + mutex_unlock(&isx031_mutex); return 0; @@ -901,22 +941,28 @@ static const struct v4l2_ctrl_ops isx031_ctrl_ops = { static int isx031_ctrls_init(struct isx031 *sensor) { - int ret = 0; struct v4l2_ctrl *ctrl; + struct v4l2_ctrl_handler *hdl = &sensor->ctrls; - v4l2_ctrl_handler_init(&sensor->ctrls, 10); + v4l2_ctrl_handler_init(hdl, 10); /* There's a need to set the link frequency because IPU6 dictates it. */ - ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &isx031_ctrl_ops, + ctrl = v4l2_ctrl_new_int_menu(hdl, &isx031_ctrl_ops, V4L2_CID_LINK_FREQ, ARRAY_SIZE(isx031_link_frequencies) - 1, 0, isx031_link_frequencies); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; + } + if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - sensor->sd.ctrl_handler = &sensor->ctrls; - return ret; + sensor->sd.ctrl_handler = hdl; + + return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) @@ -949,27 +995,32 @@ static int isx031_probe(struct i2c_client *client) isx031->client = client; isx031->platform_data = client->dev.platform_data; - if (isx031->platform_data == NULL) - dev_warn(&client->dev, "no platform data provided\n"); + if (!isx031->platform_data) + dev_warn(&client->dev, "No platform data provided\n"); isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", - GPIOD_OUT_LOW); - isx031->fsin_gpio = devm_gpiod_get_optional(&client->dev, "fsin", - GPIOD_OUT_LOW); - + GPIOD_OUT_LOW); if (IS_ERR(isx031->reset_gpio)) return -EPROBE_DEFER; - else if (isx031->reset_gpio == NULL) - dev_warn(&client->dev, "Reset GPIO not found"); + if (isx031->reset_gpio) + dev_info(&client->dev, "Reset gpio found\n"); else - dev_info(&client->dev, "Reset GPIO found"); + dev_warn(&client->dev, "Reset gpio not found\n"); - /* initialize subdevice */ + isx031->fsin_gpio = devm_gpiod_get_optional(&client->dev, "fsin", + GPIOD_OUT_LOW); + if (isx031->fsin_gpio) + dev_info(&client->dev, "Fsin gpio found\n"); + else + dev_warn(&client->dev, "Fsin gpio not found\n"); + + /* Initialize subdevice */ sd = &isx031->sd; v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); + ret = isx031_ctrls_init(isx031); if (ret) { - dev_err(&client->dev, "failed to init sensor ctrls: %d", ret); + dev_err(&client->dev, "Failed to init sensor ctrls: %d\n", ret); return ret; } @@ -982,12 +1033,12 @@ static int isx031_probe(struct i2c_client *client) sd->entity.ops = &isx031_subdev_entity_ops; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* initialize subdev media pad */ + /* Initialize subdev media pad */ isx031->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); - if (ret < 0) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); - goto probe_error_v4l2_ctrl_handler_free; + if (ret) { + dev_err(&client->dev, "Failed to init entity pads: %d\n", ret); + goto err_ctrl_free; } isx031->sd.state_lock = isx031->sd.ctrl_handler->lock; @@ -999,20 +1050,19 @@ static int isx031_probe(struct i2c_client *client) if (isx031->platform_data && isx031->platform_data->lanes) isx031->lanes = isx031->platform_data->lanes; - else { /* Read info from fwnode entrypoint bus cfg if no platform data */ ret = isx031_get_num_lane(isx031, &client->dev); if (ret) { - dev_err(&client->dev, "failed to get MIPI lane configuration"); - goto probe_error_media_entity_cleanup; + dev_err(&client->dev, "Failed to get mipi lane configuration\n"); + goto err_media_cleanup; } } ret = isx031_identify_module(client); if (ret) { - dev_err(&client->dev, "isx031 identify module failed"); - return ret; + dev_err(&client->dev, "Failed to identify sensor module: %d\n", ret); + goto err_media_cleanup; } /* 1920x1536 default */ @@ -1020,14 +1070,15 @@ static int isx031_probe(struct i2c_client *client) isx031->cur_mode = &supported_modes[0]; ret = isx031_initialize_module(isx031); if (ret) { - dev_err(&client->dev, "failed to initialize sensor: %d", ret); - return ret; + dev_err(&client->dev, "Failed to initialize sensor: %d\n", ret); + goto err_media_cleanup; } + reg_list = &isx031->cur_mode->reg_list; - ret = isx031_write_reg_list(isx031, reg_list, true); + ret = isx031_write_reg_list(client, reg_list, true); if (ret) { - dev_err(&client->dev, "failed to apply preset mode"); - goto probe_error_media_entity_cleanup; + dev_err(&client->dev, "Failed to apply preset mode\n"); + goto err_media_cleanup; } isx031->pre_mode = isx031->cur_mode; @@ -1036,10 +1087,9 @@ static int isx031_probe(struct i2c_client *client) #else ret = v4l2_async_register_subdev_sensor(&isx031->sd); #endif - if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", - ret); - goto probe_error_media_entity_cleanup; + if (ret) { + dev_err(&client->dev, "Failed to register V4L2 subdev: %d\n", ret); + goto err_media_cleanup; } /* @@ -1052,10 +1102,9 @@ static int isx031_probe(struct i2c_client *client) return 0; -probe_error_media_entity_cleanup: +err_media_cleanup: media_entity_cleanup(&isx031->sd.entity); - -probe_error_v4l2_ctrl_handler_free: +err_ctrl_free: v4l2_ctrl_handler_free(isx031->sd.ctrl_handler); return ret;