From ec98cd2b3225c05b71bb3876435111e951f6ee36 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 28 Apr 2026 03:25:37 -0700 Subject: [PATCH 01/72] v4l2-core, ipu7-isys, ipu6-isys: add dkms apply d4xx patch quilt * ipu6-isys, ipu7-isys: reindex d4xx patch quilt * v4l2core, ipu-bridge: reindex patch quilt * ipu7-isys: fetch csi-phy frame desc walking through all active routes * ipu7-isys: clear csi phy frame desc * ipu7-isys, ipu6-isys: fix Y8I css pixelformat * ipu7-isys: workaround csi2 unsupported YUYV8_1X16 css pixelformat * ipu6-isys, ipu7-isys: add d4xx video VIDIOC_S_EXT_CTRLS by streamid, add d4xx frame fmt * ipu6-isys, ipu7-isys : add V4L2_CID_IPU_ENUMERATE_LINK definitions * dkms: avoid dh_source issues * isys-video: ignore default raw colorspace that can not be overritten by v4l2src gst plugin ``` gst-launch-1.0 -e -v v4l2src device=/dev/video-rs-color-0 ! 'video/x-raw, width=640, height=480, format=YUY2, pixel-aspect-ratio=1/1' ! videoconvert ! fakesink ``` * ipu6-isys, ipu7-isys: fix librealsense2 v4l2 backend pipeline config `rs2_pipeline_start_with_config` call returns an error in rs-depth Z16 pixel-format ``` There are 1 connected RealSense devices. Using device 0, an Intel RealSense D457 Serial number: 242422303456 Firmware version: 5.13.1.53 The connected device doesn't support depth streaming! ``` * ipu7-isys : ignore HW_ERR_BAD_FRAME_DIM led isys-queue expected buffers noise Signed-off-by: florent pirou --- .gitignore | 14 + Makefile | 23 +- dkms-kernel-source.sh | 44 +- dkms.conf | 127 +- ...s-v4l2-core-makefile-adaptation-6.12.patch | 37 + ...isable-tuner-build-in-v4l2-core-6.12.patch | 24 + ...t-v4l2_subdev_enable_streams_api-tru.patch | 28 + ...s-v4l2-core-makefile-adaptation-6.17.patch | 8 +- ...sable-tuner-build-in-v4l2-core-6.17.patch} | 13 +- ...-v4l2_subdev_enable_streams_api-tru.patch} | 6 +- ...s-v4l2-core-makefile-adaptation-6.18.patch | 37 + ...isable-tuner-build-in-v4l2-core-6.18.patch | 24 + ...2-subdev-Make-struct-v4l2_subdev_str.patch | 95 + ...t-v4l2_subdev_enable_streams_api-tru.patch | 27 + ...ms-v4l2-core-makefile-adaptation-7.0.patch | 37 + ...disable-tuner-build-in-v4l2-core-7.0.patch | 24 + ...t-v4l2_subdev_enable_streams_api-tru.patch | 27 + patches/0040-ipu-bridge-6.12-makefile.patch | 37 - patches/0040-ipu-bridge-6.17-makefile.patch | 38 - patches/0042-ipu-bridge-6.12-add-sensor.patch | 19 - ...ideo-let-v4l2-set-default-colorspace.patch | 25 + ...ms-add-isys-makefile-adaptation-6.12.patch | 73 + ...disable-src_pad-validation-for-dkms.patch} | 14 +- ...-lt6911uxc-2-pads-linked-to-ipu-2-po.patch | 80 + ...n-conditional-for-symbol-namespace-.patch} | 163 +- ...virtual-channels-on-16-csi2-input-vi.patch | 27 + ...6-isys-add-d4xx-pixel-format-support.patch | 144 ++ ...deo-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch | 1206 ++++++++++++ ...force-START-then-CAPTURE-distinct-st.patch | 294 +++ ...ideo-let-v4l2-set-default-colorspace.patch | 25 + ...odules-suffix-versioning-same-as-dkm.patch | 73 + ...virtual-channels-on-16-csi2-input-vi.patch | 29 + ...7-isys-add-d4xx-pixel-format-support.patch | 139 ++ ...deo-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch | 1621 +++++++++++++++++ ...e-HW_ERR_BAD_FRAME_DIM-led-expected-.patch | 28 + ... 0200-ipu-bridge-port-6.12.0-to-bkc.patch} | 17 +- patches/0201-ipu-bridge-6.12-makefile.patch | 42 + ...> 0202-ipu-bridge-6.12-bkc-backport.patch} | 29 +- .../0203-ipu-bridge-6.12-add-sensors.patch | 30 + patches/0204-ipu-bridge-6.17-makefile.patch | 42 + ...> 0205-ipu-bridge-6.17-bkc-backport.patch} | 13 + ...=> 0206-ipu-bridge-6.17-add-sensors.patch} | 27 +- patches/0207-ipu-bridge-6.18-makefile.patch | 42 + .../0208-ipu-bridge-6.18-bkc-backport.patch | 86 + .../0209-ipu-bridge-6.18-add-sensors.patch | 30 + patches/0210-ipu-bridge-7.0-makefile.patch | 42 + .../0211-ipu-bridge-7.0-bkc-backport.patch | 86 + patches/0212-ipu-bridge-7.0-add-sensors.patch | 30 + 48 files changed, 4837 insertions(+), 309 deletions(-) create mode 100644 .gitignore create mode 100644 patches/0001-dkms-v4l2-core-makefile-adaptation-6.12.patch create mode 100644 patches/0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch create mode 100644 patches/0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch rename patches/{0009-dkms-disable-tuner-build-in-v4l2-core-6.17.patch => 0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch} (68%) rename patches/{0005-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch => 0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch} (83%) create mode 100644 patches/0007-dkms-v4l2-core-makefile-adaptation-6.18.patch create mode 100644 patches/0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch create mode 100644 patches/0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch create mode 100644 patches/0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch create mode 100644 patches/0011-dkms-v4l2-core-makefile-adaptation-7.0.patch create mode 100644 patches/0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch create mode 100644 patches/0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch delete mode 100644 patches/0040-ipu-bridge-6.12-makefile.patch delete mode 100644 patches/0040-ipu-bridge-6.17-makefile.patch delete mode 100644 patches/0042-ipu-bridge-6.12-add-sensor.patch create mode 100644 patches/0050-isys-video-let-v4l2-set-default-colorspace.patch create mode 100644 patches/0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch rename patches/{0053-ipu6-isys-disable-src_pad-validation-for-dkms.patch => 0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch} (76%) create mode 100644 patches/0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch rename patches/{0052-module-version-conditional-symbol-namespace.patch => 0054-module-Add-version-conditional-for-symbol-namespace-.patch} (74%) create mode 100644 patches/0055-ipu6-isys-allow-virtual-channels-on-16-csi2-input-vi.patch create mode 100644 patches/0056-ipu6-isys-add-d4xx-pixel-format-support.patch create mode 100644 patches/0057-ipu6-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch create mode 100644 patches/0058-ipu6-isys-video-force-START-then-CAPTURE-distinct-st.patch create mode 100644 patches/0100-isys-video-let-v4l2-set-default-colorspace.patch create mode 100644 patches/0101-ipu7-isys-make-modules-suffix-versioning-same-as-dkm.patch create mode 100644 patches/0102-ipu7-isys-allow-virtual-channels-on-16-csi2-input-vi.patch create mode 100644 patches/0103-ipu7-isys-add-d4xx-pixel-format-support.patch create mode 100644 patches/0104-ipu7-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch create mode 100644 patches/0105-isys-queue-ignore-HW_ERR_BAD_FRAME_DIM-led-expected-.patch rename patches/{0039-ipu-bridge-port-6.12.0-to-bkc.patch => 0200-ipu-bridge-port-6.12.0-to-bkc.patch} (53%) create mode 100644 patches/0201-ipu-bridge-6.12-makefile.patch rename patches/{0041-ipu-bridge-6.12-bkc-backport.patch => 0202-ipu-bridge-6.12-bkc-backport.patch} (85%) create mode 100644 patches/0203-ipu-bridge-6.12-add-sensors.patch create mode 100644 patches/0204-ipu-bridge-6.17-makefile.patch rename patches/{0041-ipu-bridge-6.17-bkc-backport.patch => 0205-ipu-bridge-6.17-bkc-backport.patch} (86%) rename patches/{0042-ipu-bridge-6.17-add-sensor.patch => 0206-ipu-bridge-6.17-add-sensors.patch} (50%) create mode 100644 patches/0207-ipu-bridge-6.18-makefile.patch create mode 100644 patches/0208-ipu-bridge-6.18-bkc-backport.patch create mode 100644 patches/0209-ipu-bridge-6.18-add-sensors.patch create mode 100644 patches/0210-ipu-bridge-7.0-makefile.patch create mode 100644 patches/0211-ipu-bridge-7.0-bkc-backport.patch create mode 100644 patches/0212-ipu-bridge-7.0-add-sensors.patch diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..75286947 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.deb +*.changes +*.buildinfo +debian/.debhelper/* +debian/debhelper-build-stamp +debian/*.substvars +debian/*.debhelper* +debian/files +debian/dkms.conf +debian/intel-mipi-gmsl-dkms.config +debian/intel-mipi-gmsl-dkms.install +debian/intel-mipi-gmsl-dkms.postinst +debian/intel-mipi-gmsl-dkms.templates +debian/intel-mipi-gmsl-dkms/ \ No newline at end of file diff --git a/Makefile b/Makefile index 0236e98f..8a3de41c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ KERNELRELEASE ?= $(shell uname -r) KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build -KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | cut -d- -f1 | sed -r 's/([0-9]+\.[0-9]+).*/\1.0/g') +KERNEL_REV := $(shell echo $(KERNELRELEASE) | cut -d- -f1 | sed -r 's/([0-9]+\.[0-9]+).*/\1.0/g') BUILD_EXCLUSIVE_KERNEL="^(6\.(1[278])\.)" MODSRC := $(shell pwd) @@ -52,7 +52,7 @@ subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU LINUXINCLUDE := -I$(src)/include $(LINUXINCLUDE) ccflags-y := -I$(src)/include -ifeq ($(KERNEL_EQ_6_17),1) + # IPU7 driver configs export CONFIG_VIDEO_INTEL_IPU7=m export CONFIG_VIDEO_INTEL_IPU6=m @@ -68,25 +68,10 @@ obj-m += ipu7-drivers/drivers/media/pci/intel/ipu7/ obj-m += ipu6-drivers/drivers/media/pci/intel/ipu6/ # Build V4L2 core module -obj-m += 6.17.0/drivers/media/v4l2-core/ - -# Build ipu-bridge module -obj-m += 6.17.0/drivers/media/pci/intel/ - -else ifeq ($(KERNEL_EQ_6_12),1) -# IPU6 driver configs -export CONFIG_VIDEO_INTEL_IPU6=m -export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y - -subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU6 - -# Build IPU6 drivers from submodule -obj-m += ipu6-drivers/drivers/media/pci/intel/ipu6/ +obj-m += $(KERNEL_REV)/drivers/media/v4l2-core/ # Build ipu-bridge module -obj-m += 6.12.0/drivers/media/pci/intel/ - -endif +obj-m += $(KERNEL_REV)/drivers/media/pci/intel/ obj-y += drivers/media/platform/intel/ obj-m += drivers/media/i2c/ diff --git a/dkms-kernel-source.sh b/dkms-kernel-source.sh index 9fdcec44..da0a08a0 100755 --- a/dkms-kernel-source.sh +++ b/dkms-kernel-source.sh @@ -1,27 +1,31 @@ #!/bin/bash -if [[ -z "${kernelver:-}" ]]; then - kernelver="$(uname -r)" -fi +KVERS=("6.12.0" "6.17.0" "6.18.0" "7.0.0") -major=$(echo "$kernelver" | cut -d- -f1| cut -d. -f1) -minor=$(echo "$kernelver" | cut -d- -f1| cut -d. -f2) -patch=$(echo "$kernelver" | cut -d- -f1| cut -d. -f3) +for KVER in "${KVERS[@]}"; do -if ! [[ "$major" =~ ^[0-9]+$ ]]; then major=0; fi -if ! [[ "$minor" =~ ^[0-9]+$ ]]; then minor=0; fi -if ! [[ "$patch" =~ ^[0-9]+$ ]]; then patch=0; fi + [[ -d $KVER ]] && continue -echo "Downloading major $major minor $minor patch $patch" -if (( patch != 0 )); then - kernelprefix="linux-$major.$minor.$patch" -else - kernelprefix="linux-$major.$minor" -fi -wget --no-check-certificate https://mirrors.edge.kernel.org/pub/linux/kernel/v$major.x/$kernelprefix.tar.xz -O $kernelprefix.tar.xz + major=$(echo "$KVER" | cut -d- -f1| cut -d. -f1) + minor=$(echo "$KVER" | cut -d- -f1| cut -d. -f2) + patch=$(echo "$KVER" | cut -d- -f1| cut -d. -f3) -for arg in "$@"; do - echo "Extracting: $kernelprefix/$arg" - tar -xvf "$kernelprefix.tar.xz" "$kernelprefix/$arg" \ - --xform="s,^${kernelprefix//./\\.}/,$major.$minor.0/," + if ! [[ "$major" =~ ^[0-9]+$ ]]; then major=0; fi + if ! [[ "$minor" =~ ^[0-9]+$ ]]; then minor=0; fi + if ! [[ "$patch" =~ ^[0-9]+$ ]]; then patch=0; fi + + echo "Downloading major $major minor $minor patch $patch" + if (( patch != 0 )); then + kernelprefix="linux-$major.$minor.$patch" + else + kernelprefix="linux-$major.$minor" + fi + wget --no-check-certificate https://mirrors.edge.kernel.org/pub/linux/kernel/v$major.x/$kernelprefix.tar.xz -O $kernelprefix.tar.xz + echo $@ + for arg in "$@"; do + echo "Extracting: $kernelprefix/$arg" + tar -xvf "$kernelprefix.tar.xz" "$kernelprefix/$arg" \ + --xform="s,^${kernelprefix//./\\.}/,$major.$minor.0/," + done + rm -rf "$kernelprefix.tar.xz" done diff --git a/dkms.conf b/dkms.conf index 2df3cd9b..465e400e 100644 --- a/dkms.conf +++ b/dkms.conf @@ -7,7 +7,14 @@ CLEAN="make KERNELRELEASE=$kernelver KERNEL_SRC=$kernel_source_dir clean" AUTOINSTALL="yes" -BUILD_EXCLUSIVE_KERNEL="^(6\.(1[278])\.)|^6\.(1[27])rt-intel|^6\.(1[27])-intel" +BUILD_EXCLUSIVE_KERNEL="^(7\.([0)\.)|^(6\.(1[278])\.)|^6\.(1[27])rt-intel|^6\.(1[27])-intel" + +#Extract kernel version substring that is used to identify source directory for kernel modules +# i.e 6.12.8-intel-ese-experimental-lts-rt ==> 6.12.0 +# i.e 6.17.0-oem-1011 ==> 6.17.0 +# i.e 6.17rt-intel ==> 6.17.0 +# i.e 6.18.6-intel-ese-experimental-lts-rt ==> 6.18.0 +SELECTED_KERNEL=$(echo $kernelver | cut -d- -f1 | sed -r 's/([0-9]+\.[0-9]+).*/\1.0/g') KERNEL_VER=$(echo $kernelver | cut -d. -f1) KERNEL_PATCH=$(echo $kernelver | cut -d. -f2 | cut -d- -f1) @@ -18,22 +25,54 @@ POST_ADD="dkms-kernel-source.sh" j=0 -# Patches for 6.17 kernel - -# V4l2 core patches +# V4l2 core patches OoT patches POST_ADD+=" drivers/media/v4l2-core" -PATCH[$j]="0004-dkms-v4l2-core-makefile-adaptation-6.17.patch" -PATCH_MATCH[$j]="^(6.1[7])" -PATCH[$((++j))]="0005-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +# kernel patches for Linux 6.12 +PATCH[$j]="0001-dkms-v4l2-core-makefile-adaptation-6.12.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(6.1[2])" +# kernel patches for Linux 6.17 +PATCH[$((++j))]="0004-dkms-v4l2-core-makefile-adaptation-6.17.patch" PATCH_MATCH[$j]="^(6.1[7])" -PATCH[$((++j))]="0009-dkms-disable-tuner-build-in-v4l2-core-6.17.patch" +PATCH[$((++j))]="0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch" PATCH_MATCH[$j]="^(6.1[7])" - -# IPU6 drivers -PATCH[$((++j))]="0052-module-version-conditional-symbol-namespace.patch" -PATCH_MATCH[$j]="^(6.1[27])" -PATCH[$((++j))]="0053-ipu6-isys-disable-src_pad-validation-for-dkms.patch" +PATCH[$((++j))]="0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" PATCH_MATCH[$j]="^(6.1[7])" +# kernel patches for Linux 6.18 +PATCH[$((++j))]="0007-dkms-v4l2-core-makefile-adaptation-6.18.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(6.1[8])" +# kernel patches for Linux 7.0 +PATCH[$((++j))]="0011-dkms-v4l2-core-makefile-adaptation-7.0.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(7.[0])" + +# IPU6 drivers OoT patches +PATCH[$((++j))]="0050-isys-video-let-v4l2-set-default-colorspace.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0054-module-Add-version-conditional-for-symbol-namespace-.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" + +# IPU7 drivers OoT patches +PATCH[$((++j))]="0100-isys-video-let-v4l2-set-default-colorspace.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" i=0 @@ -113,27 +152,50 @@ BUILT_MODULE_LOCATION[$i]="ipu6-drivers/drivers/media/pci/intel/ipu6/psys" DEST_MODULE_LOCATION[$i]="/updates" STRIP[$i]=no -if [ "$KERNEL_VER" -eq 6 ] && [ "$KERNEL_PATCH" -eq 12 ]; then - POST_ADD+=" drivers/media/pci/intel/Kconfig drivers/media/pci/intel/Makefile drivers/media/pci/intel/ipu-bridge.c" -PATCH[$((++j))]="0039-ipu-bridge-port-6.12.0-to-bkc.patch" +# kernel patches for Linux 6.12 +PATCH[$((++j))]="0200-ipu-bridge-port-6.12.0-to-bkc.patch" PATCH_MATCH[$j]="^(6.1[2]-intel)" - -PATCH[$((++j))]="0040-ipu-bridge-6.12-makefile.patch" +PATCH[$((++j))]="0201-ipu-bridge-6.12-makefile.patch" PATCH_MATCH[$j]="^(6.1[2])" -PATCH[$((++j))]="0041-ipu-bridge-6.12-bkc-backport.patch" +PATCH[$((++j))]="0202-ipu-bridge-6.12-bkc-backport.patch" PATCH_MATCH[$j]="^(6.1[2])" -PATCH[$((++j))]="0042-ipu-bridge-6.12-add-sensor.patch" +PATCH[$((++j))]="0203-ipu-bridge-6.12-add-sensors.patch" PATCH_MATCH[$j]="^(6.1[2])" +# kernel patches for Linux 6.17 +PATCH[$((++j))]="0204-ipu-bridge-6.17-makefile.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0205-ipu-bridge-6.17-bkc-backport.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0206-ipu-bridge-6.17-add-sensors.patch" +PATCH_MATCH[$j]="^(6.1[7])" +# kernel patches for Linux 6.18 +PATCH[$((++j))]="0207-ipu-bridge-6.18-makefile.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0208-ipu-bridge-6.18-bkc-backport.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0209-ipu-bridge-6.18-add-sensors.patch" +PATCH_MATCH[$j]="^(6.1[8])" +# kernel patches for Linux 7.0 +PATCH[$((++j))]="0210-ipu-bridge-7.0-makefile.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0211-ipu-bridge-7.0-bkc-backport.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0212-ipu-bridge-7.0-add-sensors.patch" +PATCH_MATCH[$j]="^(7.[0])" BUILT_MODULE_NAME[$((++i))]="ipu-bridge" -BUILT_MODULE_LOCATION[$i]="6.12.0/drivers/media/pci/intel" +BUILT_MODULE_LOCATION[$i]="${SELECTED_KERNEL}/drivers/media/pci/intel" DEST_MODULE_LOCATION[$i]="/updates" STRIP[$i]=no -fi +BUILT_MODULE_NAME[$((++i))]="videodev" +BUILT_MODULE_LOCATION[$i]="${SELECTED_KERNEL}/drivers/media/v4l2-core/" +DEST_MODULE_NAME[$i]="videodev" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no -if [ "$KERNEL_VER" -eq 6 ] && [ "$KERNEL_PATCH" -eq 17 ]; then +if [ "$KERNEL_VER" -ge 6 ] && [ "$KERNEL_PATCH" -ge 17 ]; then # IPU7 drivers from submodule BUILT_MODULE_NAME[$((++i))]="intel-ipu7" BUILT_MODULE_LOCATION[$i]="ipu7-drivers/drivers/media/pci/intel/ipu7" @@ -150,23 +212,4 @@ BUILT_MODULE_LOCATION[$i]="ipu7-drivers/drivers/media/pci/intel/ipu7/psys" DEST_MODULE_LOCATION[$i]="/updates" STRIP[$i]=no -POST_ADD+=" drivers/media/pci/intel/Kconfig drivers/media/pci/intel/Makefile drivers/media/pci/intel/ipu-bridge.c" -PATCH[$((++j))]="0040-ipu-bridge-6.17-makefile.patch" -PATCH_MATCH[$j]="^(6.1[7])" -PATCH[$((++j))]="0041-ipu-bridge-6.17-bkc-backport.patch" -PATCH_MATCH[$j]="^(6.1[7])" -PATCH[$((++j))]="0042-ipu-bridge-6.17-add-sensor.patch" -PATCH_MATCH[$j]="^(6.1[7])" - -BUILT_MODULE_NAME[$((++i))]="ipu-bridge" -BUILT_MODULE_LOCATION[$i]="6.17.0/drivers/media/pci/intel" -DEST_MODULE_LOCATION[$i]="/updates" -STRIP[$i]=no - -BUILT_MODULE_NAME[$((++i))]="videodev" -BUILT_MODULE_LOCATION[$i]="6.17.0/drivers/media/v4l2-core/" -DEST_MODULE_NAME[$i]="videodev" -DEST_MODULE_LOCATION[$i]="/updates" -STRIP[$i]=no - fi diff --git a/patches/0001-dkms-v4l2-core-makefile-adaptation-6.12.patch b/patches/0001-dkms-v4l2-core-makefile-adaptation-6.12.patch new file mode 100644 index 00000000..fffdc07d --- /dev/null +++ b/patches/0001-dkms-v4l2-core-makefile-adaptation-6.12.patch @@ -0,0 +1,37 @@ +From 0484b5aa022b5e44012cf14ffce6537752742f03 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Mon, 17 Nov 2025 07:44:33 -0700 +Subject: [PATCH 01/13] dkms : v4l2-core makefile adaptation 6.12 + +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/v4l2-core/Makefile | 3 +++ + 6.12.0/drivers/media/v4l2-core/v4l2-dev.c | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/6.12.0/drivers/media/v4l2-core/Makefile b/6.12.0/drivers/media/v4l2-core/Makefile +index 2177b9d..82c235f 100644 +--- a/6.12.0/drivers/media/v4l2-core/Makefile ++++ b/6.12.0/drivers/media/v4l2-core/Makefile +@@ -2,6 +2,9 @@ + # + # Makefile for the V4L2 core + # ++# ipu7-dkms appendix ++CC := ${CC} -I ${M}/../../../include-overrides -I ${M}/../tuners -I ${M}/../dvb-core -I ${M}/../dvb-frontends ++ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" + + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends + ccflags-y += -I$(srctree)/drivers/media/tuners +diff --git a/6.12.0/drivers/media/v4l2-core/v4l2-dev.c b/6.12.0/drivers/media/v4l2-core/v4l2-dev.c +index 3d7711c..7b3e462 100644 +--- a/6.12.0/drivers/media/v4l2-core/v4l2-dev.c ++++ b/6.12.0/drivers/media/v4l2-core/v4l2-dev.c +@@ -1231,3 +1231,4 @@ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab , Bill Dirks, + MODULE_DESCRIPTION("Video4Linux2 core driver"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +-- +2.25.1 + diff --git a/patches/0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch b/patches/0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch new file mode 100644 index 00000000..d72e300f --- /dev/null +++ b/patches/0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch @@ -0,0 +1,24 @@ +From 2a5b0ab4e4310821d1c2948adf90c8e8cb14ae79 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:14:15 -0700 +Subject: [PATCH 02/13] dkms: disable tuner build in v4l2-core 6.12 + +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/v4l2-core/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/6.12.0/drivers/media/v4l2-core/Makefile b/6.12.0/drivers/media/v4l2-core/Makefile +index 82c235f..e5880ff 100644 +--- a/6.12.0/drivers/media/v4l2-core/Makefile ++++ b/6.12.0/drivers/media/v4l2-core/Makefile +@@ -36,5 +36,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o + obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o + obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o + +-obj-$(CONFIG_VIDEO_TUNER) += tuner.o ++# obj-$(CONFIG_VIDEO_TUNER) += tuner.o + obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o +-- +2.25.1 + diff --git a/patches/0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patches/0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch new file mode 100644 index 00000000..129c4e03 --- /dev/null +++ b/patches/0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -0,0 +1,28 @@ +From c58afe775ccf6d9e6e53ae05d0ed9fc4d1927154 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Mon, 17 Nov 2025 07:45:23 -0700 +Subject: [PATCH 03/13] drivers: media: set v4l2_subdev_enable_streams_api=true + for WA 6.12 + +Signed-off-by: hepengpx +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/v4l2-core/v4l2-subdev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/6.12.0/drivers/media/v4l2-core/v4l2-subdev.c b/6.12.0/drivers/media/v4l2-core/v4l2-subdev.c +index 3a4ba08..f535929 100644 +--- a/6.12.0/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/6.12.0/drivers/media/v4l2-core/v4l2-subdev.c +@@ -32,7 +32,7 @@ + * 'v4l2_subdev_enable_streams_api' to 1 below. + */ + +-static bool v4l2_subdev_enable_streams_api; ++static bool v4l2_subdev_enable_streams_api = true; + #endif + + /* +-- +2.25.1 + diff --git a/patches/0004-dkms-v4l2-core-makefile-adaptation-6.17.patch b/patches/0004-dkms-v4l2-core-makefile-adaptation-6.17.patch index 9db07e09..0d3ecaae 100644 --- a/patches/0004-dkms-v4l2-core-makefile-adaptation-6.17.patch +++ b/patches/0004-dkms-v4l2-core-makefile-adaptation-6.17.patch @@ -1,7 +1,7 @@ -From 9097a7b8bcb983c7afc812af26eb7d313200f849 Mon Sep 17 00:00:00 2001 +From c23ae9d6a748cf8a44d2a22b16aa09fb1a800a77 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Mon, 17 Nov 2025 07:44:33 -0700 -Subject: [PATCH 4/8] dkms : v4l2-core makefile adaptation 6.17 +Subject: [PATCH 04/13] dkms : v4l2-core makefile adaptation 6.17 Signed-off-by: florent pirou --- @@ -10,7 +10,7 @@ Signed-off-by: florent pirou 2 files changed, 4 insertions(+) diff --git a/6.17.0/drivers/media/v4l2-core/Makefile b/6.17.0/drivers/media/v4l2-core/Makefile -index 2177b9d..82c235f 100644 +index 2177b9d..fc41f58 100644 --- a/6.17.0/drivers/media/v4l2-core/Makefile +++ b/6.17.0/drivers/media/v4l2-core/Makefile @@ -2,6 +2,9 @@ @@ -33,5 +33,5 @@ index c369235..1997370 100644 MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); +MODULE_VERSION(DRIVER_VERSION_SUFFIX); -- -2.43.0 +2.25.1 diff --git a/patches/0009-dkms-disable-tuner-build-in-v4l2-core-6.17.patch b/patches/0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch similarity index 68% rename from patches/0009-dkms-disable-tuner-build-in-v4l2-core-6.17.patch rename to patches/0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch index 9259b5d8..4661a62b 100644 --- a/patches/0009-dkms-disable-tuner-build-in-v4l2-core-6.17.patch +++ b/patches/0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch @@ -1,6 +1,7 @@ -From: Khai Wen, Ng -Date: Wed, 12 Feb 2026 11:08:00 +0800 -Subject: [PATCH] dkms: disable tuner build in v4l2-core 6.17 +From cc7236dc1214cb828be6bcfdb485c4ee2cab7caf Mon Sep 17 00:00:00 2001 +From: "Khai Wen, Ng" +Date: Thu, 12 Feb 2026 11:08:00 +0800 +Subject: [PATCH 05/13] dkms: disable tuner build in v4l2-core 6.17 Disable CONFIG_VIDEO_TUNER build as tuner dependencies (mt20xx.h, etc.) are not available in the DKMS build environment. @@ -11,10 +12,10 @@ Signed-off-by: Khai Wen, Ng 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/6.17.0/drivers/media/v4l2-core/Makefile b/6.17.0/drivers/media/v4l2-core/Makefile -index 2177b9d..de5ac11 100644 +index fc41f58..623b065 100644 --- a/6.17.0/drivers/media/v4l2-core/Makefile +++ b/6.17.0/drivers/media/v4l2-core/Makefile -@@ -33,5 +33,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o +@@ -36,5 +36,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o @@ -22,5 +23,5 @@ index 2177b9d..de5ac11 100644 +# obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o -- -2.43.0 +2.25.1 diff --git a/patches/0005-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patches/0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch similarity index 83% rename from patches/0005-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch rename to patches/0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch index 9d7099f4..346b188d 100644 --- a/patches/0005-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch +++ b/patches/0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -1,7 +1,7 @@ -From 33750bc14545f84d7c26168b956e960b6427b005 Mon Sep 17 00:00:00 2001 +From 7d6515b8b045280828adc87d3fd7be86f79425b5 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Mon, 17 Nov 2025 07:45:23 -0700 -Subject: [PATCH 5/8] drivers: media: set v4l2_subdev_enable_streams_api=true +Subject: [PATCH 06/13] drivers: media: set v4l2_subdev_enable_streams_api=true for WA 6.17 Signed-off-by: hepengpx @@ -24,5 +24,5 @@ index 4fd25fe..60a0556 100644 /* -- -2.43.0 +2.25.1 diff --git a/patches/0007-dkms-v4l2-core-makefile-adaptation-6.18.patch b/patches/0007-dkms-v4l2-core-makefile-adaptation-6.18.patch new file mode 100644 index 00000000..1283f6b9 --- /dev/null +++ b/patches/0007-dkms-v4l2-core-makefile-adaptation-6.18.patch @@ -0,0 +1,37 @@ +From 06e85f57caf2906718d0b6becfd1756de501494b Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Fri, 16 Jan 2026 03:20:05 -0700 +Subject: [PATCH 07/13] dkms : v4l2-core makefile adaptation 6.18 + +Signed-off-by: Florent Pirou +--- + 6.18.0/drivers/media/v4l2-core/Makefile | 3 +++ + 6.18.0/drivers/media/v4l2-core/v4l2-dev.c | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/6.18.0/drivers/media/v4l2-core/Makefile b/6.18.0/drivers/media/v4l2-core/Makefile +index 2177b9d..82c235f 100644 +--- a/6.18.0/drivers/media/v4l2-core/Makefile ++++ b/6.18.0/drivers/media/v4l2-core/Makefile +@@ -2,6 +2,9 @@ + # + # Makefile for the V4L2 core + # ++# ipu7-dkms appendix ++CC := ${CC} -I ${M}/../../../include-overrides -I ${M}/../tuners -I ${M}/../dvb-core -I ${M}/../dvb-frontends ++ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" + + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends + ccflags-y += -I$(srctree)/drivers/media/tuners +diff --git a/6.18.0/drivers/media/v4l2-core/v4l2-dev.c b/6.18.0/drivers/media/v4l2-core/v4l2-dev.c +index 10a126e..96cc91d 100644 +--- a/6.18.0/drivers/media/v4l2-core/v4l2-dev.c ++++ b/6.18.0/drivers/media/v4l2-core/v4l2-dev.c +@@ -1256,3 +1256,4 @@ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab , Bill Dirks, + MODULE_DESCRIPTION("Video4Linux2 core driver"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +-- +2.25.1 + diff --git a/patches/0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch b/patches/0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch new file mode 100644 index 00000000..f5a3d6e5 --- /dev/null +++ b/patches/0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch @@ -0,0 +1,24 @@ +From 1f78d510db230acc7b26b4dab2a485a514f1f2ee Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:14:28 -0700 +Subject: [PATCH 08/13] dkms: disable tuner build in v4l2-core 6.18 + +Signed-off-by: florent pirou +--- + 6.18.0/drivers/media/v4l2-core/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/6.18.0/drivers/media/v4l2-core/Makefile b/6.18.0/drivers/media/v4l2-core/Makefile +index 82c235f..e5880ff 100644 +--- a/6.18.0/drivers/media/v4l2-core/Makefile ++++ b/6.18.0/drivers/media/v4l2-core/Makefile +@@ -36,5 +36,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o + obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o + obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o + +-obj-$(CONFIG_VIDEO_TUNER) += tuner.o ++# obj-$(CONFIG_VIDEO_TUNER) += tuner.o + obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o +-- +2.25.1 + diff --git a/patches/0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch b/patches/0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch new file mode 100644 index 00000000..85959b94 --- /dev/null +++ b/patches/0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch @@ -0,0 +1,95 @@ +From 00504987e020729a8a87467487289ce204cb53ba Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Fri, 16 Jan 2026 10:10:16 -0700 +Subject: [PATCH 09/13] Revert "media: v4l2-subdev: Make struct + v4l2_subdev_stream_config private" + +This reverts commit 5195b777552d2e2fa735c6cad75797efa132bd60. + +Signed-off-by: Florent Pirou +--- + 6.18.0/drivers/media/v4l2-core/v4l2-subdev.c | 24 ------------------- + 6.18.0/include/media/v4l2-subdev.h | 25 +++++++++++++++++++- + 2 files changed, 24 insertions(+), 25 deletions(-) + +diff --git a/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c b/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c +index 25e66bf..4f64ac1 100644 +--- a/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c +@@ -26,30 +26,6 @@ + #include + #include + +-/** +- * struct v4l2_subdev_stream_config - Used for storing stream configuration. +- * +- * @pad: pad number +- * @stream: stream number +- * @enabled: has the stream been enabled with v4l2_subdev_enable_streams() +- * @fmt: &struct v4l2_mbus_framefmt +- * @crop: &struct v4l2_rect to be used for crop +- * @compose: &struct v4l2_rect to be used for compose +- * @interval: frame interval +- * +- * This structure stores configuration for a stream. +- */ +-struct v4l2_subdev_stream_config { +- u32 pad; +- u32 stream; +- bool enabled; +- +- struct v4l2_mbus_framefmt fmt; +- struct v4l2_rect crop; +- struct v4l2_rect compose; +- struct v4l2_fract interval; +-}; +- + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + /* + * The Streams API is an experimental feature. To use the Streams API, set +diff --git a/6.18.0/include/media/v4l2-subdev.h b/6.18.0/include/media/v4l2-subdev.h +index e0bb58c..ad37aaf 100644 +--- a/6.18.0/include/media/v4l2-subdev.h ++++ b/6.18.0/include/media/v4l2-subdev.h +@@ -36,7 +36,6 @@ struct v4l2_event_subscription; + struct v4l2_fh; + struct v4l2_subdev; + struct v4l2_subdev_fh; +-struct v4l2_subdev_stream_config; + struct tuner_setup; + struct v4l2_mbus_frame_desc; + struct led_classdev; +@@ -684,6 +683,30 @@ struct v4l2_subdev_pad_config { + struct v4l2_fract interval; + }; + ++/** ++ * struct v4l2_subdev_stream_config - Used for storing stream configuration. ++ * ++ * @pad: pad number ++ * @stream: stream number ++ * @enabled: has the stream been enabled with v4l2_subdev_enable_streams() ++ * @fmt: &struct v4l2_mbus_framefmt ++ * @crop: &struct v4l2_rect to be used for crop ++ * @compose: &struct v4l2_rect to be used for compose ++ * @interval: frame interval ++ * ++ * This structure stores configuration for a stream. ++ */ ++struct v4l2_subdev_stream_config { ++ u32 pad; ++ u32 stream; ++ bool enabled; ++ ++ struct v4l2_mbus_framefmt fmt; ++ struct v4l2_rect crop; ++ struct v4l2_rect compose; ++ struct v4l2_fract interval; ++}; ++ + /** + * struct v4l2_subdev_stream_configs - A collection of stream configs. + * +-- +2.25.1 + diff --git a/patches/0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patches/0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch new file mode 100644 index 00000000..fbdb142b --- /dev/null +++ b/patches/0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -0,0 +1,27 @@ +From ce0bdb0a496906c9c4b595bfc6f95173c19c1305 Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Fri, 16 Jan 2026 03:22:26 -0700 +Subject: [PATCH 10/13] drivers: media: set v4l2_subdev_enable_streams_api=true + for WA 6.18 + +Signed-off-by: Florent Pirou +--- + 6.18.0/drivers/media/v4l2-core/v4l2-subdev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c b/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c +index 4f64ac1..b2b7125 100644 +--- a/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/6.18.0/drivers/media/v4l2-core/v4l2-subdev.c +@@ -32,7 +32,7 @@ + * 'v4l2_subdev_enable_streams_api' to 1 below. + */ + +-static bool v4l2_subdev_enable_streams_api; ++static bool v4l2_subdev_enable_streams_api = true; + #endif + + /* +-- +2.25.1 + diff --git a/patches/0011-dkms-v4l2-core-makefile-adaptation-7.0.patch b/patches/0011-dkms-v4l2-core-makefile-adaptation-7.0.patch new file mode 100644 index 00000000..8e02640c --- /dev/null +++ b/patches/0011-dkms-v4l2-core-makefile-adaptation-7.0.patch @@ -0,0 +1,37 @@ +From 2bda71610890498595695874cf79714aac8cabdc Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:09:24 -0700 +Subject: [PATCH 11/13] dkms : v4l2-core makefile adaptation 7.0 + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/v4l2-core/Makefile | 3 +++ + 7.0.0/drivers/media/v4l2-core/v4l2-dev.c | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/7.0.0/drivers/media/v4l2-core/Makefile b/7.0.0/drivers/media/v4l2-core/Makefile +index 329f0ea..e7bcbf4 100644 +--- a/7.0.0/drivers/media/v4l2-core/Makefile ++++ b/7.0.0/drivers/media/v4l2-core/Makefile +@@ -2,6 +2,9 @@ + # + # Makefile for the V4L2 core + # ++# ipu7-dkms appendix ++CC := ${CC} -I ${M}/../../../include-overrides -I ${M}/../tuners -I ${M}/../dvb-core -I ${M}/../dvb-frontends ++ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" + + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends + ccflags-y += -I$(srctree)/drivers/media/tuners +diff --git a/7.0.0/drivers/media/v4l2-core/v4l2-dev.c b/7.0.0/drivers/media/v4l2-core/v4l2-dev.c +index 6ce623a..6a7394c 100644 +--- a/7.0.0/drivers/media/v4l2-core/v4l2-dev.c ++++ b/7.0.0/drivers/media/v4l2-core/v4l2-dev.c +@@ -1256,3 +1256,4 @@ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab , Bill Dirks, + MODULE_DESCRIPTION("Video4Linux2 core driver"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +-- +2.25.1 + diff --git a/patches/0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch b/patches/0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch new file mode 100644 index 00000000..9e0ea4ee --- /dev/null +++ b/patches/0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch @@ -0,0 +1,24 @@ +From 36f284f942076f6cfce62fe5653e71dfbce60502 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:14:40 -0700 +Subject: [PATCH 12/13] dkms: disable tuner build in v4l2-core 7.0 + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/v4l2-core/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/7.0.0/drivers/media/v4l2-core/Makefile b/7.0.0/drivers/media/v4l2-core/Makefile +index e7bcbf4..6ae8206 100644 +--- a/7.0.0/drivers/media/v4l2-core/Makefile ++++ b/7.0.0/drivers/media/v4l2-core/Makefile +@@ -37,5 +37,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o + obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o + obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o + +-obj-$(CONFIG_VIDEO_TUNER) += tuner.o ++# obj-$(CONFIG_VIDEO_TUNER) += tuner.o + obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o +-- +2.25.1 + diff --git a/patches/0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patches/0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch new file mode 100644 index 00000000..08e45218 --- /dev/null +++ b/patches/0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -0,0 +1,27 @@ +From 17c5e0eb30598f4c821c1d645fc9cdf63aec4ac3 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:10:52 -0700 +Subject: [PATCH 13/13] drivers: media: set v4l2_subdev_enable_streams_api=true + for WA 7.0 + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/v4l2-core/v4l2-subdev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/7.0.0/drivers/media/v4l2-core/v4l2-subdev.c b/7.0.0/drivers/media/v4l2-core/v4l2-subdev.c +index 32e6f60..9cf0075 100644 +--- a/7.0.0/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/7.0.0/drivers/media/v4l2-core/v4l2-subdev.c +@@ -56,7 +56,7 @@ struct v4l2_subdev_stream_config { + * 'v4l2_subdev_enable_streams_api' to 1 below. + */ + +-static bool v4l2_subdev_enable_streams_api; ++static bool v4l2_subdev_enable_streams_api = true; + #endif + + /* +-- +2.25.1 + diff --git a/patches/0040-ipu-bridge-6.12-makefile.patch b/patches/0040-ipu-bridge-6.12-makefile.patch deleted file mode 100644 index 62bca464..00000000 --- a/patches/0040-ipu-bridge-6.12-makefile.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: Yew, Chang Ching -Date: Mon, 9 Mar 2026.17:30:00 +0800 -Subject: [PATCH] ipu-bridge: Add scaling direct MIPI HIDS for v6.12 - -Signed-off-by: Yew, Chang Ching ---- - 6.12.0/drivers/media/pci/intel/Kconfig | 5 - - 6.12.0/drivers/media/pci/intel/Makefile | 5 +- - 3 files changed, 187 insertions(+), 38 deletions(-) -diff --git b/6.12.0/drivers/media/pci/intel/Kconfig a/6.12.0/drivers/media/pci/intel/Kconfig -index d9fcddc..3fa2b72 100644 ---- b/6.12.0/drivers/media/pci/intel/Kconfig -+++ a/6.12.0/drivers/media/pci/intel/Kconfig -@@ -1,9 +1,4 @@ - # SPDX-License-Identifier: GPL-2.0-only -- --source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu6/Kconfig" --source "drivers/media/pci/intel/ivsc/Kconfig" -- - config IPU_BRIDGE - tristate "Intel IPU Bridge" - depends on ACPI || COMPILE_TEST -diff --git b/6.12.0/drivers/media/pci/intel/Makefile a/6.12.0/drivers/media/pci/intel/Makefile -index 3a2cc65..16dc2d2 100644 ---- b/6.12.0/drivers/media/pci/intel/Makefile -+++ a/6.12.0/drivers/media/pci/intel/Makefile -@@ -2,7 +2,4 @@ - # - # Makefile for the IPU drivers - # --obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o --obj-y += ipu3/ --obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ -+obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o -\ No newline at end of file diff --git a/patches/0040-ipu-bridge-6.17-makefile.patch b/patches/0040-ipu-bridge-6.17-makefile.patch deleted file mode 100644 index 5d152b0d..00000000 --- a/patches/0040-ipu-bridge-6.17-makefile.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Yew, Chang Ching -Date: Tue, 3 Mar 2026 17:30:00 +0800 -Subject: [PATCH] ipu-bridge: Add scaling direct MIPI HIDS - -Signed-off-by: Yew, Chang Ching ---- - 6.17.0/drivers/media/pci/intel/Kconfig | 5 - - 6.17.0/drivers/media/pci/intel/Makefile | 5 +- - 3 files changed, 173 insertions(+), 10 deletions(-) - -diff --git b/6.17.0/drivers/media/pci/intel/Kconfig a/6.17.0/drivers/media/pci/intel/Kconfig -index d9fcddc..3fa2b72 100644 ---- b/6.17.0/drivers/media/pci/intel/Kconfig -+++ a/6.17.0/drivers/media/pci/intel/Kconfig -@@ -1,9 +1,4 @@ - # SPDX-License-Identifier: GPL-2.0-only -- --source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu6/Kconfig" --source "drivers/media/pci/intel/ivsc/Kconfig" -- - config IPU_BRIDGE - tristate "Intel IPU Bridge" - depends on ACPI || COMPILE_TEST -diff --git b/6.17.0/drivers/media/pci/intel/Makefile a/6.17.0/drivers/media/pci/intel/Makefile -index 3a2cc65..16dc2d2 100644 ---- b/6.17.0/drivers/media/pci/intel/Makefile -+++ a/6.17.0/drivers/media/pci/intel/Makefile -@@ -2,7 +2,4 @@ - # - # Makefile for the IPU drivers - # --obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o --obj-y += ipu3/ --obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ -+obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o -\ No newline at end of file diff --git a/patches/0042-ipu-bridge-6.12-add-sensor.patch b/patches/0042-ipu-bridge-6.12-add-sensor.patch deleted file mode 100644 index ef4e1b68..00000000 --- a/patches/0042-ipu-bridge-6.12-add-sensor.patch +++ /dev/null @@ -1,19 +0,0 @@ - 6.12.0/drivers/media/pci/intel/ipu-bridge.c | 6 ++++++ - 1 file changed, 6 insertions(+) -diff --git a/6.12.0/drivers/media/pci/intel/ipu-bridge.c b/6.12.0/drivers/media/pci/intel/ipu-bridge.c -index 4e4f86a..e680967 100644 ---- a/6.12.0/drivers/media/pci/intel/ipu-bridge.c -+++ b/6.12.0/drivers/media/pci/intel/ipu-bridge.c -@@ -91,6 +91,12 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { - IPU_SENSOR_CONFIG("INTC10C5", 0), - /* Lontium lt6911uxc */ - IPU_SENSOR_CONFIG("INTC10B1", 0), -+ /* D3 Embedded ISX031 */ -+ IPU_SENSOR_CONFIG("INTC113C", 1, 300000000), -+ /* LI AR0830 */ -+ IPU_SENSOR_CONFIG("LIAR0830", 1, 600000000), -+ /* Innodisk AR0822 */ -+ IPU_SENSOR_CONFIG("EV8MOOM1", 1, 600000000), - }; - - static const struct ipu_sensor_config ipu_supported_sensors_dummy[] = { diff --git a/patches/0050-isys-video-let-v4l2-set-default-colorspace.patch b/patches/0050-isys-video-let-v4l2-set-default-colorspace.patch new file mode 100644 index 00000000..04b532a5 --- /dev/null +++ b/patches/0050-isys-video-let-v4l2-set-default-colorspace.patch @@ -0,0 +1,25 @@ +From 4b109df879f8b2a99b0afe7de5a9d55f11e7b813 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Sat, 14 Feb 2026 14:04:59 -0700 +Subject: [PATCH 50/57] isys-video: let v4l2 set default colorspace + +Signed-off-by: florent pirou +--- + ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +index 927d92e..7967db1 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +@@ -288,7 +288,6 @@ static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av, + &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); + + f->fmt.pix.field = V4L2_FIELD_NONE; +- f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; +-- +2.25.1 + diff --git a/patches/0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch b/patches/0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch new file mode 100644 index 00000000..17574e9e --- /dev/null +++ b/patches/0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch @@ -0,0 +1,73 @@ +From d08c02ae48a7e101a083c6c2a88ce4e33cda75cf Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Mon, 12 Jan 2026 03:45:03 -0700 +Subject: [PATCH 51/57] ipu6-dkms: add isys makefile adaptation 6.12 + +*ipu6-isys: compile ISYS RESET and ACPI PDATA support + +Signed-off-by: Florent Pirou +--- + ipu6-drivers/drivers/media/pci/intel/ipu6/Makefile | 13 +++++++++++++ + .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 1 + + ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c | 1 + + .../drivers/media/pci/intel/ipu6/psys/ipu-psys.c | 1 + + 4 files changed, 16 insertions(+) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/Makefile b/ipu6-drivers/drivers/media/pci/intel/ipu6/Makefile +index 67c13c9..5ac7944 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/Makefile ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/Makefile +@@ -1,5 +1,16 @@ + # SPDX-License-Identifier: GPL-2.0-only + ++KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | sed 's/[^0-9.]*\([0-9]\.[0-9]*\)\..*/\1.0/') ++ ++CC := ${CC} -I ${M}/../../../../../../include -I ${M}/../../../../../../$(KERNEL_VERSION)/include-overrides -DCONFIG_DEBUG_FS -DCONFIG_INTEL_IPU_ACPI -DCONFIG_INTEL_IPU6_ACPI -DCONFIG_VIDEO_INTEL_IPU6_ISYS_RESET -DCONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ ++KBUILD_EXTRA_SYMBOLS += ${M}/../../../../../../Module.symvers ++ ++ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" ++ ++export EXTERNAL_BUILD = 1 ++export CONFIG_VIDEO_INTEL_IPU6 = m ++ + intel-ipu6-y := ipu6.o \ + ipu6-bus.o \ + ipu6-dma.o \ +@@ -22,4 +33,6 @@ intel-ipu6-isys-y := ipu6-isys.o \ + ipu6-isys-dwc-phy.o + + obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o ++ ++subdir-ccflags-y += -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" + obj-$(CONFIG_VIDEO_INTEL_IPU6) += psys/ +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +index 415ff06..eb66918 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +@@ -1774,3 +1774,4 @@ MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Intel IPU6 input system driver"); + MODULE_IMPORT_NS(INTEL_IPU6); + MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c +index 738232e..67894a3 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c +@@ -963,3 +963,4 @@ MODULE_AUTHOR("Yunliang Ding "); + MODULE_AUTHOR("Hongju Wang "); + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Intel IPU6 PCI driver"); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/psys/ipu-psys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/psys/ipu-psys.c +index 6423238..66c1c28 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/psys/ipu-psys.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/psys/ipu-psys.c +@@ -2749,3 +2749,4 @@ MODULE_IMPORT_NS(INTEL_IPU6); + MODULE_IMPORT_NS("DMA_BUF"); + MODULE_IMPORT_NS("INTEL_IPU6"); + #endif ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +-- +2.25.1 + diff --git a/patches/0053-ipu6-isys-disable-src_pad-validation-for-dkms.patch b/patches/0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch similarity index 76% rename from patches/0053-ipu6-isys-disable-src_pad-validation-for-dkms.patch rename to patches/0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch index 21fd7677..1ee7d013 100644 --- a/patches/0053-ipu6-isys-disable-src_pad-validation-for-dkms.patch +++ b/patches/0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch @@ -1,15 +1,18 @@ -Subject: [PATCH] ipu6: isys: disable src_pad validation for dkms +From f5f8e9ca3695645166c5eebe434cd5162079194d Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 08:26:40 -0700 +Subject: [PATCH 52/57] ipu6: isys: disable src_pad validation for dkms src_pad field is added by VTG to support UXC use case in BKC kernel. Hence, this patch is only applicable to BKC kernel header file. -Signed-off-by: Khai Wen, Ng +Signed-off-by: florent pirou --- - .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 6 +++--- + ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c -index 415ff067e..f4bc8ca12 100644 +index eb66918..241bdeb 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -175,7 +175,7 @@ isys_complete_ext_device_registration(struct ipu6_isys *isys, @@ -40,4 +43,5 @@ index 415ff067e..f4bc8ca12 100644 if (ret) return ret; -- -2.43.0 +2.25.1 + diff --git a/patches/0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch b/patches/0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch new file mode 100644 index 00000000..719c5019 --- /dev/null +++ b/patches/0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch @@ -0,0 +1,80 @@ +From 88644a7b7a16a3dbb87cd41918bc566c6929ff1f Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Mon, 19 Jan 2026 09:02:23 -0700 +Subject: [PATCH 53/57] Revert "ipu6-isys: lt6911uxc 2 pads linked to ipu 2 + ports for split mode" + +This reverts commit b1c889b6240723d849da32f615ed8945d08d4e21. + +Change Description: +single camera in bios setting, pprval for dummy port. +create software node and link connection in ipu-brige. +match the connection and port num in v4l2-async. +finally create the media links. + +Signed-off-by: Chen Meng J +Signed-off-by: Florent Pirou +--- + .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +index 241bdeb..d0db7ea 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +@@ -158,7 +158,9 @@ static int isys_isr_one(struct ipu6_bus_device *adev); + static int + isys_complete_ext_device_registration(struct ipu6_isys *isys, + struct v4l2_subdev *sd, ++#if 0 + s16 src_pad, ++#endif + struct ipu6_isys_csi2_config *csi2) + { + struct device *dev = &isys->adev->auxdev.dev; +@@ -175,6 +177,7 @@ isys_complete_ext_device_registration(struct ipu6_isys *isys, + ret = -ENOENT; + goto unregister_subdev; + } ++ + #if 0 + if (src_pad >= 0) + i = (unsigned int)src_pad; +@@ -187,6 +190,7 @@ isys_complete_ext_device_registration(struct ipu6_isys *isys, + goto unregister_subdev; + } + #endif ++ + ret = media_create_pad_link(&sd->entity, i, + &isys->csi2[csi2->port].asd.sd.entity, + 0, MEDIA_LNK_FL_ENABLED | +@@ -273,8 +277,12 @@ static int isys_register_ext_subdev(struct ipu6_isys *isys, + if (!sd_info->csi2) + return 0; + ++#if 0 + /* src_pad is useless for non MIPI split case. Set it to '-1'.*/ + return isys_complete_ext_device_registration(isys, sd, -1, sd_info->csi2); ++#else ++ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); ++#endif + + skip_put_adapter: + i2c_put_adapter(adapter); +@@ -1008,9 +1016,13 @@ static int isys_notifier_bound(struct v4l2_async_notifier *notifier, + + dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n", + sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); ++#if 0 + ret = isys_complete_ext_device_registration(isys, sd, + -1, + &s_asd->csi2); ++#else ++ ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); ++#endif + if (ret) + return ret; + +-- +2.25.1 + diff --git a/patches/0052-module-version-conditional-symbol-namespace.patch b/patches/0054-module-Add-version-conditional-for-symbol-namespace-.patch similarity index 74% rename from patches/0052-module-version-conditional-symbol-namespace.patch rename to patches/0054-module-Add-version-conditional-for-symbol-namespace-.patch index 9f75c98d..36201dbf 100644 --- a/patches/0052-module-version-conditional-symbol-namespace.patch +++ b/patches/0054-module-Add-version-conditional-for-symbol-namespace-.patch @@ -1,46 +1,34 @@ -From e82bcd12246eed133513993db7422afb9cce6a7d Mon Sep 17 00:00:00 2001 -From: Peter Zijlstra -Date: Mon, 19 Jan 2026 11:41:13 -0700 -Subject: [PATCH 52/52] module: Add version conditional for symbol namespace +From 26c29a1e7f35cd8c735b68a6b48e96a0b856fd4a Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:31:44 -0700 +Subject: [PATCH 54/57] module: Add version conditional for symbol namespace syntax Handle the namespace syntax change between kernel 6.12 and 6.17. In 6.17+, EXPORT_SYMBOL_NS_GPL requires quoted namespace strings. +Signed-off-by: florent pirou --- - .../media/pci/intel/ipu6/ipu6-buttress.c | 70 ++++++++++++++++--- - .../drivers/media/pci/intel/ipu6/ipu6-cpd.c | 12 +++- - .../drivers/media/pci/intel/ipu6/ipu6-dma.c | 54 ++++++++++++--- - .../media/pci/intel/ipu6/ipu6-fw-com.c | 55 ++++++++++++--- - .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 6 +- - .../drivers/media/pci/intel/ipu6/ipu6-mmu.c | 12 +++- - .../drivers/media/pci/intel/ipu6/ipu6.c | 8 ++- + .../media/pci/intel/ipu6/ipu6-buttress.c | 28 ++++++++++++++ + .../drivers/media/pci/intel/ipu6/ipu6-cpd.c | 8 ++++ + .../drivers/media/pci/intel/ipu6/ipu6-dma.c | 36 ++++++++++++++++++ + .../media/pci/intel/ipu6/ipu6-fw-com.c | 37 +++++++++++++++++++ + .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 5 +++ + .../drivers/media/pci/intel/ipu6/ipu6-mmu.c | 8 ++++ + .../drivers/media/pci/intel/ipu6/ipu6.c | 8 ++++ .../drivers/media/pci/intel/ipu6/ipu6.h | 1 + - 8 files changed, 180 insertions(+), 38 deletions(-) + 8 files changed, 131 insertions(+) -diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h -index a1b2e3c..b2c3d4e 100644 ---- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h -+++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - - #include "ipu6-buttress.h" - diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-buttress.c -index fc18cc6..dc5dbe5 100644 +index fc18cc6..2b89dd9 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -508,7 +508,11 @@ bool ipu6_buttress_auth_done(struct ipu6_device *isp) return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, "INTEL_IPU6"); +#endif @@ -51,9 +39,8 @@ index fc18cc6..dc5dbe5 100644 return ret; } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, "INTEL_IPU6"); +#endif @@ -64,9 +51,8 @@ index fc18cc6..dc5dbe5 100644 dma_unmap_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0); sg_free_table(sgt); } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, "INTEL_IPU6"); +#endif @@ -77,9 +63,8 @@ index fc18cc6..dc5dbe5 100644 return -ETIMEDOUT; } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, "INTEL_IPU6"); +#endif @@ -90,9 +75,8 @@ index fc18cc6..dc5dbe5 100644 WARN_ON_ONCE(1); return -1; } --EXPORT_SYMBOL_NS_GPL(ipu6_get_i2c_bus_id, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_get_i2c_bus_id, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_get_i2c_bus_id, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_get_i2c_bus_id, "INTEL_IPU6"); +#endif @@ -103,9 +87,8 @@ index fc18cc6..dc5dbe5 100644 } local_irq_restore(flags); } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, "INTEL_IPU6"); +#endif @@ -116,9 +99,8 @@ index fc18cc6..dc5dbe5 100644 */ return div_u64(ns, isp->buttress.ref_clk); } --EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, "INTEL_IPU6"); +#endif @@ -126,16 +108,15 @@ index fc18cc6..dc5dbe5 100644 void ipu6_buttress_restore(struct ipu6_device *isp) { diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-cpd.c -index 21c1c12..fd81dc2 100644 +index 21c1c12..ef5ea74 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-cpd.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-cpd.c @@ -216,14 +216,22 @@ int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src) return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, "INTEL_IPU6"); +#endif @@ -145,9 +126,8 @@ index 21c1c12..fd81dc2 100644 ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir, adev->pkg_dir_dma_addr, 0); } --EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, "INTEL_IPU6"); +#endif @@ -155,16 +135,15 @@ index 21c1c12..fd81dc2 100644 static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd, unsigned long cpd_size, diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-dma.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-dma.c -index b71f66b..aa3e1f7 100644 +index b71f66b..8c8575b 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-dma.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-dma.c @@ -130,7 +130,11 @@ void ipu6_dma_sync_single(struct ipu6_bus_device *sys, dma_addr_t dma_handle, vaddr = info->vaddr + offset; clflush_cache_range(vaddr, size); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_single, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_single, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_single, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_single, "INTEL_IPU6"); +#endif @@ -175,9 +154,8 @@ index b71f66b..aa3e1f7 100644 for_each_sg(sglist, sg, nents, i) clflush_cache_range(page_to_virt(sg_page(sg)), sg->length); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sg, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sg, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sg, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sg, "INTEL_IPU6"); +#endif @@ -186,9 +164,8 @@ index b71f66b..aa3e1f7 100644 { ipu6_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sgtable, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sgtable, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sgtable, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_sync_sgtable, "INTEL_IPU6"); +#endif @@ -199,9 +176,8 @@ index b71f66b..aa3e1f7 100644 return NULL; } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_alloc, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_alloc, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_alloc, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_alloc, "INTEL_IPU6"); +#endif @@ -212,9 +188,8 @@ index b71f66b..aa3e1f7 100644 kfree(info); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_free, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_free, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_free, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_free, "INTEL_IPU6"); +#endif @@ -225,9 +200,8 @@ index b71f66b..aa3e1f7 100644 mmu->tlb_invalidate(mmu); __free_iova(&mmu->dmap->iovad, iova); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, "INTEL_IPU6"); +#endif @@ -238,9 +212,8 @@ index b71f66b..aa3e1f7 100644 return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sg, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sg, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sg, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sg, "INTEL_IPU6"); +#endif @@ -251,9 +224,8 @@ index b71f66b..aa3e1f7 100644 return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sgtable, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sgtable, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sgtable, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_map_sgtable, "INTEL_IPU6"); +#endif @@ -263,9 +235,8 @@ index b71f66b..aa3e1f7 100644 { ipu6_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); } --EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sgtable, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sgtable, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sgtable, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sgtable, "INTEL_IPU6"); +#endif @@ -273,7 +244,7 @@ index b71f66b..aa3e1f7 100644 /* * Create scatter-list for the already allocated DMA buffer diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-com.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-com.c -index 45738f3..7a9fcd9 100644 +index 45738f3..947b281 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-com.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-com.c @@ -10,6 +10,7 @@ @@ -288,9 +259,8 @@ index 45738f3..7a9fcd9 100644 return ctx; } --EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, "INTEL_IPU6"); +#endif @@ -301,9 +271,8 @@ index 45738f3..7a9fcd9 100644 return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, "INTEL_IPU6"); +#endif @@ -314,9 +283,8 @@ index 45738f3..7a9fcd9 100644 return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, "INTEL_IPU6"); +#endif @@ -327,9 +295,8 @@ index 45738f3..7a9fcd9 100644 kfree(ctx); return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, "INTEL_IPU6"); +#endif @@ -340,9 +307,8 @@ index 45738f3..7a9fcd9 100644 return state == SYSCOM_STATE_READY; } --EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, "INTEL_IPU6"); +#endif @@ -353,9 +319,8 @@ index 45738f3..7a9fcd9 100644 return (void *)(q->host_address + index * q->token_size); } --EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, "INTEL_IPU6"); +#endif @@ -366,9 +331,8 @@ index 45738f3..7a9fcd9 100644 writel(wr, q_dmem + FW_COM_WR_REG); } --EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, "INTEL_IPU6"); +#endif @@ -379,9 +343,8 @@ index 45738f3..7a9fcd9 100644 return (void *)(q->host_address + rd * q->token_size); } --EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, "INTEL_IPU6"); +#endif @@ -392,18 +355,16 @@ index 45738f3..7a9fcd9 100644 writel(rd, q_dmem + FW_COM_RD_REG); } --EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, "INTEL_IPU6"); +#endif diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c -index 412afd0..79780d3 100644 +index d0db7ea..526c916 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c -@@ -1773,6 +1773,11 @@ MODULE_AUTHOR("Bingbu Cao "); - MODULE_AUTHOR("Yunliang Ding "); +@@ -1784,6 +1784,11 @@ MODULE_AUTHOR("Yunliang Ding "); MODULE_AUTHOR("Hongju Wang "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel IPU6 input system driver"); @@ -414,17 +375,17 @@ index 412afd0..79780d3 100644 +MODULE_IMPORT_NS("INTEL_IPU6"); +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +#endif + MODULE_VERSION(DRIVER_VERSION_SUFFIX); diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-mmu.c -index 57298ac..dc4b4e0 100644 +index 57298ac..ea73dd5 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-mmu.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-mmu.c @@ -518,7 +518,11 @@ int ipu6_mmu_hw_init(struct ipu6_mmu *mmu) return 0; } --EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, "INTEL_IPU6"); +#endif @@ -435,9 +396,8 @@ index 57298ac..dc4b4e0 100644 mmu->ready = false; spin_unlock_irqrestore(&mmu->ready_lock, flags); } --EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, "INTEL_IPU6"); +#endif @@ -445,16 +405,15 @@ index 57298ac..dc4b4e0 100644 static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp) { diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c -index 67894a3..6341775 100644 +index 67894a3..42646bc 100644 --- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c +++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.c @@ -302,7 +302,11 @@ void ipu6_configure_spc(struct ipu6_device *isp, ipu6_pkg_dir_configure_spc(isp, hw_variant, pkg_dir_idx, base, pkg_dir, pkg_dir_dma_addr); } --EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); + EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); +#else +EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, "INTEL_IPU6"); +#endif @@ -465,14 +424,26 @@ index 67894a3..6341775 100644 module_pci_driver(ipu6_pci_driver); --MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) -+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); + MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); +#else +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +#endif MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Tianshu Qiu "); MODULE_AUTHOR("Bingbu Cao "); +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h +index baad7f8..b76ab9d 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6.h +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + #include "ipu6-buttress.h" + -- -2.43.0 +2.25.1 + diff --git a/patches/0055-ipu6-isys-allow-virtual-channels-on-16-csi2-input-vi.patch b/patches/0055-ipu6-isys-allow-virtual-channels-on-16-csi2-input-vi.patch new file mode 100644 index 00000000..718e3ed6 --- /dev/null +++ b/patches/0055-ipu6-isys-allow-virtual-channels-on-16-csi2-input-vi.patch @@ -0,0 +1,27 @@ +From 437c44be3c27089cb0dcca81d31a97c1011165ec Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Fri, 23 Jan 2026 16:55:59 -0700 +Subject: [PATCH 55/57] ipu6-isys : allow virtual-channels on 16 csi2 input + video capture + +Signed-off-by: florent pirou +--- + ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +index ea609dd..3b4328b 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +@@ -21,7 +21,7 @@ struct ipu6_isys_stream; + #define INVALID_VC_ID -1 + #define NR_OF_CSI2_SINK_PADS 1 + #define CSI2_PAD_SINK 0 +-#define NR_OF_CSI2_SRC_PADS 8 ++#define NR_OF_CSI2_SRC_PADS 16 + #define CSI2_PAD_SRC 1 + #define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS) + +-- +2.25.1 + diff --git a/patches/0056-ipu6-isys-add-d4xx-pixel-format-support.patch b/patches/0056-ipu6-isys-add-d4xx-pixel-format-support.patch new file mode 100644 index 00000000..860003fc --- /dev/null +++ b/patches/0056-ipu6-isys-add-d4xx-pixel-format-support.patch @@ -0,0 +1,144 @@ +From 0159600f2d533dba3a3b3b024cabd2973df91514 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:50:59 -0700 +Subject: [PATCH 56/57] ipu6-isys : add d4xx pixel format support + +Signed-off-by: florent pirou +--- + .../drivers/media/pci/intel/ipu6/ipu6-fw-isys.h | 1 + + .../drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 4 ++++ + .../drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c | 6 ++++++ + .../drivers/media/pci/intel/ipu6/ipu6-isys-video.c | 11 +++++++++++ + ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.h | 4 ++-- + 5 files changed, 24 insertions(+), 2 deletions(-) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +index 545180c..0ec08ee 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +@@ -209,6 +209,7 @@ enum ipu6_fw_isys_frame_format_type { + IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */ + IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */ + IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */ ++ IPU6_FW_ISYS_FRAME_FORMAT_VYUY, /* 16 bit YUV 422, VYUY interleaved */ + N_IPU6_FW_ISYS_FRAME_FORMAT + }; + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +index 5f2f836..27a5368 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +@@ -26,10 +26,13 @@ + #include "ipu6-platform-isys-csi2-reg.h" + + static const u32 csi2_supported_codes[] = { ++ MEDIA_BUS_FMT_Y8_1X8, ++ MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_VYUY8_1X16, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, +@@ -47,6 +50,7 @@ static const u32 csi2_supported_codes[] = { + MEDIA_BUS_FMT_META_12, + MEDIA_BUS_FMT_META_16, + MEDIA_BUS_FMT_META_24, ++ MEDIA_BUS_FMT_FIXED, + 0 + }; + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c +index 0a06de5..76c7e43 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c +@@ -26,6 +26,8 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_META_16: ++ case MEDIA_BUS_FMT_Y10_1X10: ++ case MEDIA_BUS_FMT_VYUY8_1X16: + return 16; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: +@@ -44,6 +46,7 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_META_8: ++ case MEDIA_BUS_FMT_Y8_1X8: + return 8; + default: + WARN_ON(1); +@@ -60,6 +63,8 @@ unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) + return MIPI_CSI2_DT_RGB888; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: ++ case MEDIA_BUS_FMT_Y10_1X10: ++ case MEDIA_BUS_FMT_VYUY8_1X16: + return MIPI_CSI2_DT_YUV422_8B; + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: +@@ -80,6 +85,7 @@ unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: ++ case MEDIA_BUS_FMT_Y8_1X8: + return MIPI_CSI2_DT_RAW8; + default: + /* return unavailable MIPI data type - 0x3f */ +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +index 7967db1..1ac771a 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +@@ -40,6 +40,14 @@ + #include "ipu6-platform-regs.h" + + const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { ++ { V4L2_PIX_FMT_GREY, 8, 8, MEDIA_BUS_FMT_Y8_1X8, ++ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 }, ++ { V4L2_PIX_FMT_Y10, 16, 10, MEDIA_BUS_FMT_Y10_1X10, ++ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, ++ { V4L2_PIX_FMT_Y8I, 16, 16, MEDIA_BUS_FMT_VYUY8_1X16, ++ IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, ++ { V4L2_PIX_FMT_Z16, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, ++ IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, + { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, + { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, +@@ -86,6 +94,8 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { + IPU6_FW_ISYS_FRAME_FORMAT_YUYV}, + { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, + IPU6_FW_ISYS_FRAME_FORMAT_RGB565 }, ++ { V4L2_PIX_FMT_Y12I, 32, 24, MEDIA_BUS_FMT_RGB888_1X24, ++ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 }, + { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, + IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 }, + { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, +@@ -96,6 +106,7 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { + IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true }, + { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, + IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true }, ++ { V4L2_META_FMT_D4XX, 8, 8, MEDIA_BUS_FMT_FIXED, 0 }, + }; + + static int video_open(struct file *file) +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.h +index 4c7ba6e..6a425c8 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.h +@@ -43,8 +43,8 @@ struct ipu6_bus_device; + #define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5 + #define IPU6_ISYS_NUM_RECV_QUEUE 1 + +-#define IPU6_ISYS_MIN_WIDTH 2U +-#define IPU6_ISYS_MIN_HEIGHT 2U ++#define IPU6_ISYS_MIN_WIDTH 1U ++#define IPU6_ISYS_MIN_HEIGHT 1U + #define IPU6_ISYS_MAX_WIDTH 4672U + #define IPU6_ISYS_MAX_HEIGHT 3416U + +-- +2.25.1 + diff --git a/patches/0057-ipu6-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch b/patches/0057-ipu6-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch new file mode 100644 index 00000000..cede6f1b --- /dev/null +++ b/patches/0057-ipu6-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch @@ -0,0 +1,1206 @@ +From 4a2c1f24ff44495435f5a858d5ba09bccce26a0e Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Fri, 23 Jan 2026 18:42:59 -0700 +Subject: [PATCH 57/58] ipu6-isys: add video VIDIOC_S_EXT_CTRLS callbacks by + streamid + +* isys-video: add video VIDIOC_S_EXT_CTRLS callbacks by streamid +* isys-csi: add graph walk subdev to get frame desc by streamid +* isys-video: add V4L2_CID_IPU definitions + +Signed-off-by: florent pirou +--- + .../media/pci/intel/ipu6/ipu6-isys-csi2.c | 314 ++++++++- + .../media/pci/intel/ipu6/ipu6-isys-csi2.h | 8 +- + .../pci/intel/ipu6/ipu6-isys-video-ext.h | 52 ++ + .../media/pci/intel/ipu6/ipu6-isys-video.c | 622 +++++++++++++++++- + .../media/pci/intel/ipu6/ipu6-isys-video.h | 4 + + 5 files changed, 992 insertions(+), 8 deletions(-) + create mode 100644 ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video-ext.h + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +index 27a5368..f4d024e 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +@@ -84,24 +84,36 @@ static const struct ipu6_csi2_error dphy_rx_errors[] = { + s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2) + { + struct media_pad *src_pad; ++ struct v4l2_subdev_state *s_state; + struct v4l2_subdev *ext_sd; + struct device *dev; ++ int ret = -EPIPE; + + if (!csi2) + return -EINVAL; + ++ s_state = v4l2_subdev_get_unlocked_active_state(&csi2->asd.sd); ++ if (!s_state) ++ return ret; ++ + dev = &csi2->isys->adev->auxdev.dev; +- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); ++ src_pad = media_pad_remote_pad_first(&csi2->asd.sd.entity.pads[CSI2_PAD_SINK]); + if (IS_ERR(src_pad)) { +- dev_err(dev, "can't get source pad of %s (%ld)\n", +- csi2->asd.sd.name, PTR_ERR(src_pad)); +- return PTR_ERR(src_pad); ++ dev_err(dev, ++ "can't get source pad of %s\n", ++ csi2->asd.sd.name); ++ return -EINVAL; + } + + ext_sd = media_entity_to_v4l2_subdev(src_pad->entity); + if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name)) + return -ENODEV; + ++ dev_dbg(dev, ++ "%s:%u -> %s:%u\n", ++ csi2->asd.sd.name, CSI2_PAD_SINK, ++ ext_sd->name, src_pad->index); ++ + return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0); + } + +@@ -349,6 +361,146 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++static int ipu6_isys_csi2_link_validate(struct media_link *link) ++{ ++ ++ struct v4l2_subdev *sd = ++ media_entity_to_v4l2_subdev(link->sink->entity); ++ struct device *dev = sd->entity.graph_obj.mdev->dev; ++ struct ipu6_isys_subdev *asd; ++ struct ipu6_isys_csi2 *csi2; ++ struct v4l2_subdev_state *s_state; ++ struct v4l2_mbus_framefmt *s_fmt; ++ struct v4l2_subdev *s_sd; ++ struct media_pad *s_pad; ++ u32 s_stream, code; ++ int ret = -EPIPE; ++ ++ if (!link->source->entity) ++ return ret; ++ ++ asd = to_ipu6_isys_subdev(sd); ++ csi2 = to_ipu6_isys_csi2(asd); ++ dev = &asd->isys->adev->auxdev.dev; ++ ++ s_sd = media_entity_to_v4l2_subdev(link->source->entity); ++ s_state = v4l2_subdev_get_unlocked_active_state(s_sd); ++ if (!s_state) ++ return ret; ++ ++ dev_dbg(dev, "%s: validating link \"%s\":%u -> \"%s\"\n",__func__, ++ link->source->entity->name, link->source->index, ++ link->sink->entity->name); ++ ++ return 0; ++} ++ ++static int ipu6_isys_csi2_streams_by_sources(struct ipu6_isys_csi2 *csi2, ++ u32 pad, u32 stream, bool on) ++{ ++ struct media_entity *entity = &csi2->asd.sd.entity; ++ struct media_device *mdev = entity->graph_obj.mdev; ++ struct device *dev = &csi2->isys->adev->auxdev.dev; ++ struct v4l2_subdev *sd = NULL; ++ struct v4l2_subdev_state *v4l2_state; ++ struct media_graph graph; ++ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; ++ struct v4l2_mbus_frame_desc desc; ++ unsigned int i; ++ int ret; ++ ++ if (!mdev) ++ return -EPIPE; ++ ++ mutex_lock(&mdev->graph_mutex); ++ if (media_graph_walk_init(&graph, mdev)) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s: walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_VID_MUX) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->get_frame_desc) ++ continue; ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ ret = sd->ops->pad->get_frame_desc(sd, pad, &desc); ++ if (ret) ++ continue; ++ ++ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) ++ continue; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (stream == desc.entry[i].stream) { ++ desc_entry = &desc.entry[i]; ++ break; ++ } ++ } ++ ++ if (!desc_entry) ++ continue; ++ ++ if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) ++ continue; ++ ++ ++ dev_dbg(dev, "%s: found %s subdev stream:%u vc/dt:%u/0x%x\n", __func__, ++ sd->name, ++ desc_entry->stream, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->disable_streams || !sd->ops->pad->enable_streams) ++ break; ++ ++ v4l2_state = v4l2_subdev_get_unlocked_active_state(sd); ++ if (!v4l2_state) ++ break; ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (on) ++ ret = sd->ops->pad->enable_streams(sd, v4l2_state, pad, BIT_ULL(stream)); ++ else ++ ret = sd->ops->pad->disable_streams(sd, v4l2_state, pad, BIT_ULL(stream)); ++ if (ret) ++ break; ++ ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++ return 0; ++ } ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++ ++error_graph_walk_source: ++ return -EINVAL; ++} ++#endif ++ + /* + * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set + * of streams. +@@ -388,6 +540,14 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd, + if (ret) + return ret; + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ if (!ipu6_isys_csi2_streams_by_sources(csi2, sink_pad, sink_stream, true)) { ++ csi2->stream_count++; ++ return 0; ++ } ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "remote stream on fallback\n"); ++#endif + r_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(r_pad->entity); + +@@ -425,6 +585,15 @@ static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd, + if (ret) + return ret; + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ if (!ipu6_isys_csi2_streams_by_sources(csi2, sink_pad, sink_stream, false)) { ++ if (--csi2->stream_count) ++ return 0; ++ goto ret; ++ } ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "remote stream off fallback\n"); ++#endif + r_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(r_pad->entity); + +@@ -432,7 +601,7 @@ static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd, + + if (--csi2->stream_count) + return 0; +- ++ret: + dev_dbg(&csi2->isys->adev->auxdev.dev, + "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); + +@@ -548,7 +717,11 @@ static const struct v4l2_subdev_ops csi2_sd_ops = { + }; + + static const struct media_entity_operations csi2_entity_ops = { ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ .link_validate = ipu6_isys_csi2_link_validate, ++#else + .link_validate = v4l2_subdev_link_validate, ++#endif + .has_pad_interdep = v4l2_subdev_has_pad_interdep, + }; + +@@ -698,3 +871,134 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, + #endif + return 0; + } ++ ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++int ipu6_isys_csi2_walk_remote_desc(u32 source_stream, ++ struct ipu6_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc_entry *entry, ++ int *nr_queues) ++{ ++ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; ++ struct device *dev = &csi2->isys->adev->auxdev.dev; ++ struct v4l2_mbus_frame_desc desc; ++ struct v4l2_subdev *source; ++ struct media_pad *pad; ++ struct media_graph graph; ++ struct media_entity *entity; ++ struct media_device *mdev; ++ struct v4l2_subdev *sd; ++ unsigned int pad_id; ++ u32 stream_id = source_stream; ++ unsigned int i; ++ int ret; ++ ++#ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET ++ csi2->is_multiple = false; ++#endif ++ ++ source = media_entity_to_v4l2_subdev(source_entity); ++ if (!source) ++ return -EPIPE; ++ ++ pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]); ++ if (!pad) ++ return -EPIPE; ++ ++ dev_dbg(dev, "%s: source pad/stream:%u/%u\n", __func__,pad->index, source_stream); ++ pad_id = pad->index; ++ entity = source_entity; ++ mdev = entity->graph_obj.mdev; ++ if (!mdev) { ++ ret = -EPIPE; ++ goto error_graph_walk_source; ++ } ++ ++ mutex_lock(&mdev->graph_mutex); ++ ret = media_graph_walk_init(&graph, mdev); ++ if (ret) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s: walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_CAM_SENSOR) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->get_frame_desc) ++ continue; ++ ++ dev_dbg(dev, "%s(): %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ pad_id, ++ source_stream); ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ ret = sd->ops->pad->get_frame_desc(sd, pad_id, &desc); ++ if (ret) ++ continue; ++ ++ ret = -EINVAL; ++ ++ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) ++ continue; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (source_stream == desc.entry[i].stream) { ++ desc_entry = &desc.entry[i]; ++ break; ++ } ++ } ++ ++ if (!desc_entry) ++ continue; ++ ++ if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) ++ continue; ++ ++ *entry = *desc_entry; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) ++ (*nr_queues)++; ++ } ++ ++ dev_dbg(dev, "%s: found frame desc stream:%u vc/dt:%u/0x%x\n", __func__, ++ desc_entry->stream, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ ret = 0; ++ break; ++ } ++ ++#ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET ++ csi2->is_multiple = true; ++ dev_dbg(dev, "set csi2->is_multiple is true.\n"); ++#endif ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++error_graph_walk_source: ++ return ret; ++} ++#endif +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +index 3b4328b..f5ef4d4 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +@@ -81,5 +81,11 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, + struct media_entity *source_entity, + struct v4l2_mbus_frame_desc_entry *entry, + int *nr_queues); +- ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++int ipu6_isys_csi2_walk_remote_desc(u32 source_stream, ++ struct ipu6_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc_entry *entry, ++ int *nr_queues); ++#endif + #endif /* IPU6_ISYS_CSI2_H */ +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video-ext.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video-ext.h +new file mode 100644 +index 0000000..66fee7f +--- /dev/null ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video-ext.h +@@ -0,0 +1,52 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2013 - 2025 Intel Corporation ++ */ ++ ++#define V4L2_CID_IPU_BASE (V4L2_CID_USER_BASE + 0x1080) ++ ++#define V4L2_CID_IPU_STORE_CSI2_HEADER (V4L2_CID_IPU_BASE + 2) ++#define V4L2_CID_IPU_ISYS_COMPRESSION (V4L2_CID_IPU_BASE + 3) ++ ++#define V4L2_CID_IPU_QUERY_SUB_STREAM (V4L2_CID_IPU_BASE + 4) ++#define V4L2_CID_IPU_SET_SUB_STREAM (V4L2_CID_IPU_BASE + 5) ++ ++#define V4L2_CID_IPU_ENUMERATE_LINK (V4L2_CID_IPU_BASE + 6) ++ ++#define VIDIOC_IPU_GET_DRIVER_VERSION \ ++ _IOWR('v', BASE_VIDIOC_PRIVATE + 3, uint32_t) ++ ++enum ipu6_isys_enum_link_state { ++ IPU6_ISYS_LINK_STATE_DISABLED = 0, ++ IPU6_ISYS_LINK_STATE_ENABLED = 1, ++ IPU6_ISYS_LINK_STATE_DONE = 2, ++ IPU6_ISYS_LINK_STATE_MD = 3, ++ IPU6_ISYS_LINK_STATE_MAX, ++}; ++ ++static struct media_pad *other_pad(struct media_pad *pad); ++bool has_src_pad_stream_active(struct v4l2_subdev *sd, u32 stream, u32 pad); ++bool get_src_pad_by_src_stream(struct v4l2_subdev *sd, u32 stream, u32 *s_pad); ++ ++/* needed for callback */ ++extern int ipu6_isys_inherit_ctrls(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_set_fmt_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_enum_frameintervals_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_enum_fmt_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_enum_framesizes_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_set_parm_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu6_isys_get_parm_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++ ++extern int media_pipeline_enumerate_by_stream_cb( ++ struct ipu6_isys_video *av, ++ int (*cb_fn)(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, ++ void *data), ++ void *data); +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +index 1ac771a..53750b8 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +@@ -37,6 +37,9 @@ + #include "ipu6-isys-csi2.h" + #include "ipu6-isys-queue.h" + #include "ipu6-isys-video.h" ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++#include "ipu6-isys-video-ext.h" ++#endif + #include "ipu6-platform-regs.h" + + const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { +@@ -122,7 +125,11 @@ static int video_open(struct file *file) + return -EIO; + } + mutex_unlock(&isys->mutex); +- ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ /* ipu_isys inherit remote sd ctrls */ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_ENABLED && media_pad_remote_pad_first(&av->pad)) ++ media_pipeline_enumerate_by_stream_cb(av, ipu6_isys_inherit_ctrls, NULL); ++#endif + return v4l2_fh_open(file); + } + +@@ -188,7 +195,50 @@ static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) + { + unsigned int i, num_found; ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ struct ipu6_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_mbus_code_enum mce; ++ int ret=-EINVAL; + ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) { ++ ++ mce.index = f->index; ++ mce.pad = 0; ++ ++ /* poll remote sensors */ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu6_isys_enum_fmt_subdev, &mce); ++ if (!ret) { ++ ret=-EINVAL; ++ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { ++ if ((ipu6_isys_pfmts[i].is_meta && ++ f->type != V4L2_BUF_TYPE_META_CAPTURE) || ++ (!ipu6_isys_pfmts[i].is_meta && ++ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) ++ continue; ++ ++ if (f->mbus_code && f->mbus_code != mce.code) ++ continue; ++ ++ if (mce.code != ipu6_isys_pfmts[i].code) ++ continue; ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x, sd_code::0x%x\n", ++ __func__, ++ ipu6_isys_pfmts[i].code, ++ ipu6_isys_pfmts[i].pixelformat); ++ ++ f->flags = 0; ++ f->pixelformat = ipu6_isys_pfmts[i].pixelformat; ++ ++ return 0; ++ } ++ } ++ return ret; ++ } ++#endif + for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { + if ((ipu6_isys_pfmts[i].is_meta && + f->type != V4L2_BUF_TYPE_META_CAPTURE) || +@@ -217,6 +267,48 @@ static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) + { + unsigned int i; ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ struct ipu6_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_size_enum fse; ++ int ret = -EINVAL; ++ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) { ++ ++ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { ++ if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat) ++ continue; ++ ++ fse.code = ipu6_isys_pfmts[i].code; ++ fse.index = fsize->index; ++ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu6_isys_enum_framesizes_subdev, &fse); ++ if (ret) ++ continue; ++ ++ if (!fse.max_width && !fse.max_height) { ++ fsize->discrete.width = 0; ++ fsize->discrete.height = 0; ++ return -EINVAL; ++ } ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x, sd_code::0x%x, WxH:%ux%u\n", ++ __func__, ++ ipu6_isys_pfmts[i].code, ++ ipu6_isys_pfmts[i].pixelformat, ++ fse.max_width, ++ fse.max_height); ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = fse.max_width; ++ fsize->discrete.height = fse.max_height; ++ return 0; ++ } ++ return -EINVAL; ++ } ++#endif + + if (fsize->index > 0) + return -EINVAL; +@@ -239,6 +331,169 @@ static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh, + return -EINVAL; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++/* extended v4l2_ctrl_ops */ ++static int ipu6_isys_video_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ipu6_isys_video *av = ctrl->priv; ++ struct ipu6_isys *isys = av->isys; ++ mutex_lock(&isys->mutex); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_IPU_ENUMERATE_LINK: ++ av->enum_link_state = ctrl->val; ++ break; ++ } ++ ++ mutex_unlock(&isys->mutex); ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops ipu6_isys_video_ctrl_ops = { ++ .s_ctrl = ipu6_isys_video_s_ctrl, ++}; ++ ++static const struct v4l2_ctrl_config ipu6_isys_video_enum_link = { ++ .ops = &ipu6_isys_video_ctrl_ops, ++ .id = V4L2_CID_IPU_ENUMERATE_LINK, ++ .name = "Enumerate graph link", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0, ++ .max = IPU6_ISYS_LINK_STATE_MAX, ++ .step = 1, ++ .def = 0, ++}; ++ ++/* extended v4l2_ioctl_ops */ ++static struct media_pad *other_pad(struct media_pad *pad) ++{ ++ struct media_link *link; ++ ++ list_for_each_entry(link, &pad->entity->links, list) { ++ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) ++ != MEDIA_LNK_FL_DATA_LINK) ++ continue; ++ ++ return link->source == pad ? link->sink : link->source; ++ } ++ ++ WARN_ON(1); ++ return NULL; ++} ++ ++static int ipu6_isys_get_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct ipu6_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval fi; ++ int ret = -ENOLINK; ++ ++ a->parm.capture.timeperframe.numerator = 1; ++ a->parm.capture.timeperframe.denominator = 30; ++ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DISABLED) ++ return 0; ++ ++ if (media_pad_remote_pad_first(&av->pad)) { ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu6_isys_get_parm_subdev, &fi); ++ ++ if (!ret) { ++ a->parm.capture.timeperframe.numerator = fi.interval.numerator; ++ a->parm.capture.timeperframe.denominator = fi.interval.denominator; ++ } ++ } ++ return ret; ++} ++ ++static int ipu6_isys_set_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct ipu6_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval fi; ++ int ret = -ENOLINK; ++ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DISABLED) ++ return 0; ++ ++ fi.interval.numerator = a->parm.capture.timeperframe.numerator; ++ fi.interval.denominator = a->parm.capture.timeperframe.denominator; ++ ++ if (media_pad_remote_pad_first(&av->pad)) ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu6_isys_set_parm_subdev, &fi); ++ ++ return ret; ++} ++ ++static int ipu6_isys_enum_frameintervals(struct file *file, void *fh, ++ struct v4l2_frmivalenum *intervals) ++{ ++ struct ipu6_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval_enum fie; ++ unsigned int i, num_found; ++ int ret=-EINVAL; ++ ++ /* poll remote sensors */ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) { ++ ++ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { ++ if (intervals->pixel_format != ipu6_isys_pfmts[i].pixelformat) ++ continue; ++ ++ fie.code = ipu6_isys_pfmts[i].code; ++ fie.index = intervals->index; ++ fie.width = intervals->width; ++ fie.height = intervals->height; ++ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu6_isys_enum_frameintervals_subdev, &fie); ++ if (ret) ++ continue; ++ ++ if (!fie.interval.numerator && ++ !fie.interval.denominator) { ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = 1; ++ intervals->discrete.denominator = 30; ++ return -EINVAL; ++ } ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x,sd_code::0x%x,WxH:%ux%u,fps:%u/%u\n", ++ __func__, ++ ipu6_isys_pfmts[i].code, ++ ipu6_isys_pfmts[i].pixelformat, ++ fie.width, fie.height, ++ fie.interval.numerator,fie.interval.denominator); ++ ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = fie.interval.numerator; ++ intervals->discrete.denominator = fie.interval.denominator; ++ return 0; ++ } ++ return -EINVAL; ++ } ++ ++ if (intervals->index > 0) ++ return -EINVAL; ++ ++ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { ++ if (intervals->pixel_format != ipu6_isys_pfmts[i].pixelformat) ++ continue; ++ ++ /* Code found */ ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = 1; ++ intervals->discrete.denominator = 30; ++ ++ return 0; ++ } ++ return -EINVAL; ++} ++#endif ++ + static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) + { +@@ -314,6 +569,30 @@ static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, + + __ipu6_isys_vidioc_try_fmt_vid_cap(av, f); + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) ++ { ++ int ret = 0; ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = 0, ++ }; ++ struct v4l2_mbus_framefmt format = { ++ .width = f->fmt.pix.width, ++ .height = f->fmt.pix.height, ++ .code = ipu6_isys_get_isys_format(f->fmt.pix.pixelformat, 0)->code, ++ .field = f->fmt.pix.field, ++ .colorspace = f->fmt.pix.colorspace, ++ .ycbcr_enc = f->fmt.pix.ycbcr_enc, ++ .quantization = f->fmt.pix.quantization, ++ .xfer_func = f->fmt.pix.xfer_func, ++ }; ++ fmt.format = format; ++ ret = media_pipeline_enumerate_by_stream_cb(av, ipu6_isys_set_fmt_subdev, &fmt); ++ if (ret) ++ return -EINVAL; ++ } ++#endif + return 0; + } + +@@ -343,7 +622,50 @@ static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, + { + struct ipu6_isys_video *av = video_drvdata(file); + ++#ifndef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS + ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f); ++#else ++ struct media_pad *source_pad = media_pad_remote_pad_first(&av->pad); ++ struct media_pad *remote_pad = source_pad; ++ struct v4l2_subdev *sd = NULL; ++ ++ if (vb2_is_busy(&av->aq.vbq)) ++ return -EBUSY; ++ ++ __ipu6_isys_vidioc_try_fmt_vid_cap(av, f); ++ ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) { ++ ++ int ret = 0; ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = 0, ++ }; ++ struct v4l2_mbus_framefmt format = { ++ .width = f->fmt.pix.width, ++ .height = f->fmt.pix.height, ++ .code = ipu6_isys_get_isys_format(f->fmt.pix.pixelformat, 0)->code, ++ .field = f->fmt.pix.field, ++ .colorspace = f->fmt.pix.colorspace, ++ .ycbcr_enc = f->fmt.pix.ycbcr_enc, ++ .quantization = f->fmt.pix.quantization, ++ .xfer_func = f->fmt.pix.xfer_func, ++ }; ++ fmt.format = format; ++ ret = media_pipeline_enumerate_by_stream_cb(av, ipu6_isys_set_fmt_subdev, &fmt); ++ if (ret) ++ return -EINVAL; ++ ++ /* TODO: set format for CSI-2 ++ do { ++ // Non-subdev nodes can be safely ignored here. ++ if (!is_media_entity_v4l2_subdev(remote_pad->entity)) ++ continue; ++ // Set only for IPU CSI entities ++ } while ((remote_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]))); ++ */ ++ } ++#endif + av->pix_fmt = f->fmt.pix; + + return 0; +@@ -1151,6 +1473,266 @@ out_media_entity_stop_streaming_firmware: + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++int ipu6_isys_set_fmt_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_format *fmt = ++ (struct v4l2_subdev_format *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->set_fmt) ++ ret = sd->ops->pad->set_fmt(sd, NULL, fmt); ++ ++ return ret; ++} ++ ++int ipu6_isys_get_parm_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval *fi = ++ (struct v4l2_subdev_frame_interval *)data; ++ struct v4l2_subdev_state *state = v4l2_subdev_get_unlocked_active_state(sd); ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->get_frame_interval) ++ ret = sd->ops->pad->get_frame_interval(sd, state, fi); ++ ++ return ret; ++} ++ ++int ipu6_isys_set_parm_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval *fi = ++ (struct v4l2_subdev_frame_interval *)data; ++ struct v4l2_subdev_state *state = v4l2_subdev_get_unlocked_active_state(sd); ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->set_frame_interval) ++ ret = sd->ops->pad->set_frame_interval(sd, state, fi); ++ ++ return ret; ++} ++ ++int ipu6_isys_enum_fmt_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_mbus_code_enum *mce = ++ (struct v4l2_subdev_mbus_code_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_mbus_code) ++ ret = sd->ops->pad->enum_mbus_code(sd, NULL, mce); ++ ++ return ret; ++} ++ ++int ipu6_isys_enum_framesizes_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_size_enum *fse = ++ (struct v4l2_subdev_frame_size_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_size) ++ ret = sd->ops->pad->enum_frame_size(sd, NULL, fse); ++ ++ return ret; ++} ++ ++int ipu6_isys_enum_frameintervals_subdev(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval_enum *fie = ++ (struct v4l2_subdev_frame_interval_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_interval) ++ ret = sd->ops->pad->enum_frame_interval(sd, NULL, fie); ++ ++ return ret; ++} ++ ++int ipu6_isys_inherit_ctrls(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ ret = v4l2_ctrl_add_handler(&av->ctrl_handler, ++ sd->ctrl_handler, NULL, true); ++ return ret; ++} ++ ++bool has_src_pad_stream_active(struct v4l2_subdev *sd, u32 stream, u32 pad) ++{ ++ struct v4l2_subdev_state *state; ++ struct v4l2_subdev_route *routes; ++ bool has_active_source_stream = false; ++ unsigned int i; ++ ++ state = v4l2_subdev_lock_and_get_active_state(sd); ++ if (!state) ++ return has_active_source_stream; ++ ++ dev_dbg(sd->dev, "%s(): check %s pad/stream: %u/%u has V4L2_SUBDEV_ROUTE_FL_ACTIVE flag\n", ++ __func__, ++ sd->name, ++ pad, ++ stream); ++ routes = state->routing.routes; ++ for (i = 0; i < state->routing.num_routes; i++) { ++ dev_dbg(sd->dev, "%s(): %s pad/stream: %u/%u flags=0x%x\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ routes[i].source_stream, ++ routes[i].flags); ++ if (routes[i].source_pad == pad && ++ routes[i].source_stream == stream && ++ routes[i].flags == V4L2_SUBDEV_ROUTE_FL_ACTIVE) { ++ dev_dbg(sd->dev, "%s(): %s pad/stream: %u/%u has V4L2_SUBDEV_ROUTE_FL_ACTIVE flag\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ routes[i].source_stream); ++ has_active_source_stream = true; ++ break; ++ } ++ } ++ ++ v4l2_subdev_unlock_state(state); ++ ++ return has_active_source_stream; ++} ++ ++bool get_src_pad_by_src_stream(struct v4l2_subdev *sd, u32 stream, u32 *s_pad) ++{ ++ struct v4l2_subdev_state *state; ++ struct v4l2_subdev_route *routes; ++ bool has_source_pad = false; ++ unsigned int i; ++ ++ if (!s_pad) ++ return has_source_pad; ++ ++ state = v4l2_subdev_lock_and_get_active_state(sd); ++ if (!state) ++ return has_source_pad; ++ ++ routes = state->routing.routes; ++ for (i = 0; i < state->routing.num_routes; i++) { ++ if (routes[i].source_stream == stream) { ++ dev_dbg(sd->dev, "%s(): Found %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ stream); ++ *s_pad = routes[i].source_pad; ++ has_source_pad = true; ++ break; ++ } ++ } ++ ++ v4l2_subdev_unlock_state(state); ++ return has_source_pad; ++} ++ ++int media_pipeline_enumerate_by_stream_cb( ++ struct ipu6_isys_video *av, ++ int (*cb_fn)(struct ipu6_isys_video *av, ++ struct v4l2_subdev *sd, ++ void *data), ++ void *data) ++{ ++ int i; ++ struct media_graph graph; ++ struct media_entity *entity = &av->vdev.entity; ++ struct media_device *mdev = entity->graph_obj.mdev; ++ struct device *dev = entity->graph_obj.mdev->dev; ++ struct media_pad *remote_pad = media_pad_remote_pad_unique(&av->pad); ++ struct ipu6_isys_stream *stream = av->stream; ++ struct ipu6_isys_subdev *asd; ++ struct v4l2_subdev *sd; ++ unsigned int pad_id; ++ u32 stream_id; ++ int ret = -ENOLINK; ++ ++ if (!remote_pad) { ++ dev_err(dev, ++ "%s:%d no remote pad found\n", ++ __func__, __LINE__); ++ goto error_graph_walk_source; ++ } ++ ++ pad_id = remote_pad->index; ++ sd = media_entity_to_v4l2_subdev(remote_pad->entity); ++ if(!sd) { ++ dev_err(dev, ++ "%s():%d no stream found on remote pad %u\n", ++ __func__, __LINE__, pad_id); ++ goto error_graph_walk_source; ++ } ++ stream_id = ipu6_isys_get_src_stream_by_src_pad(sd, pad_id); ++ dev_dbg(dev, ++ "%s(): Check %s (vc=%u) inheritence from %s pad/stream %u/%u\n", ++ __func__, av->vdev.name, av->vc, ++ sd->name, pad_id, stream_id); ++ ++ mutex_lock(&mdev->graph_mutex); ++ ret = media_graph_walk_init(&graph, mdev); ++ if (ret) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s(): walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_CAM_SENSOR) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with src pad on same stream id */ ++ if(!get_src_pad_by_src_stream(sd, stream_id, &pad_id)) ++ continue; ++ ++ /* access only subdevices on source stream active route */ ++ if(!has_src_pad_stream_active(sd, stream_id, pad_id)) ++ continue; ++ ++ dev_dbg(dev, "%s(): %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ pad_id, ++ stream_id); ++ ++ /* call function once */ ++ ret = cb_fn(av, sd, data); ++ break; ++ } ++ av->enum_link_state = IPU6_ISYS_LINK_STATE_DONE; ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++error_graph_walk_source: ++ return ret; ++} ++#endif ++ + static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = { + .vidioc_querycap = ipu6_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt, +@@ -1171,6 +1753,11 @@ static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = { + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ .vidioc_g_parm = ipu6_isys_get_parm, ++ .vidioc_s_parm = ipu6_isys_set_parm, ++ .vidioc_enum_frameintervals = ipu6_isys_enum_frameintervals, ++#endif + }; + + static const struct media_entity_operations entity_ops = { +@@ -1312,6 +1899,14 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av, + av->source_stream = route->sink_stream; + v4l2_subdev_unlock_state(state); + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ if (av->enum_link_state == IPU6_ISYS_LINK_STATE_DONE) ++ ret = ipu6_isys_csi2_walk_remote_desc(route->sink_stream, ++ to_ipu6_isys_csi2(asd), ++ *source_entity, &entry, ++ nr_queues); ++ else ++#endif + ret = ipu6_isys_csi2_get_remote_desc(av->source_stream, + to_ipu6_isys_csi2(asd), + *source_entity, &entry, +@@ -1416,9 +2011,29 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av) + if (ret) + goto out_media_entity_cleanup; + ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ /* create v4l2 ctrl for capture video node */ ++ ret = v4l2_ctrl_handler_init(&av->ctrl_handler, 0); ++ if (ret) { ++ dev_err(&av->isys->adev->auxdev.dev, ++ "failed to init v4l2 ctrl handler for %s\n", av->vdev.name); ++ goto out_ctrl_handler_cleanup; ++ } ++ v4l2_ctrl_new_custom(&av->ctrl_handler, &ipu6_isys_video_enum_link, av); ++ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "ext-ctrls: %s: set video_enum_link\n", av->vdev.name); ++ ++ av->vdev.ctrl_handler = &av->ctrl_handler; ++ return ret; ++ ++out_ctrl_handler_cleanup: ++ v4l2_ctrl_handler_free(&av->ctrl_handler); ++#else + return ret; ++#endif + +-out_media_entity_cleanup: ++ out_media_entity_cleanup: + vb2_video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + +@@ -1433,6 +2048,9 @@ out_free_watermark: + + void ipu6_isys_video_cleanup(struct ipu6_isys_video *av) + { ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ v4l2_ctrl_handler_free(&av->ctrl_handler); ++#endif + vb2_video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + mutex_destroy(&av->mutex); +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +index 5fa1643..7999e79 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +@@ -109,6 +109,10 @@ struct ipu6_isys_video { + unsigned int skipframe; + unsigned int start_streaming; + #endif ++#ifdef CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS ++ struct v4l2_ctrl_handler ctrl_handler; ++ unsigned int enum_link_state; /* state for link enumeration by vc */ ++#endif + }; + + #define ipu6_isys_queue_to_video(__aq) \ +-- +2.43.0 + diff --git a/patches/0058-ipu6-isys-video-force-START-then-CAPTURE-distinct-st.patch b/patches/0058-ipu6-isys-video-force-START-then-CAPTURE-distinct-st.patch new file mode 100644 index 00000000..d2d77443 --- /dev/null +++ b/patches/0058-ipu6-isys-video-force-START-then-CAPTURE-distinct-st.patch @@ -0,0 +1,294 @@ +From f0a44d52e53cebb8dc50feda5d148c6e354a3029 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Mon, 23 Mar 2026 04:43:04 -0700 +Subject: [PATCH 58/58] ipu6-isys: video: force START then CAPTURE distinct + start_stream_firmware commands + +Change introduced at [start_stream_firmware](https://github.com/intel/ipu6-drivers/commit/f310264660de132ed7dd7634cca8c03714a774ad#diff-5d8bb31c30c9393a0d7b5d59f0389db65f8189d9270aea922df3564c0614cb25R1907) +hands-up sending multiple concurrent/overlapping stream_capture commands +to the IPU6 firmware at several point in time. +.e.g. STREAM_CAPTURE_ACK to STREAM_CAPTURE_DONE overlaps +in the STREAM_START_AND_CAPTURE_ACK to STREAM_START_AND_CAPTURE_DONE interval. + +Reverting the start_stream_firmware to a 2-phase approach +limit this stream_capture accumulation to max two commands +sent the IPU6 firmware at any given point of time. + +* isys-video : remove START_AND_CAPTURE to avoid multi streams start race-conditions +* isys-video: add stream stop and close state message transition +* isys-video : add timeout on initial STREAM_CAPTURE + +Signed-off-by: florent pirou +--- + .../media/pci/intel/ipu6/ipu6-fw-isys.h | 1 + + .../media/pci/intel/ipu6/ipu6-isys-queue.c | 7 +- + .../media/pci/intel/ipu6/ipu6-isys-video.c | 97 +++++++++++-------- + .../media/pci/intel/ipu6/ipu6-isys-video.h | 1 + + .../drivers/media/pci/intel/ipu6/ipu6-isys.c | 2 + + 5 files changed, 61 insertions(+), 47 deletions(-) + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +index 0ec08ee..e08b239 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +@@ -65,6 +65,7 @@ struct ipu6_isys; + */ + #define IPU6_ISYS_OPEN_RETRY 2000 + #define IPU6_ISYS_CLOSE_RETRY 2000 ++#define IPU6_ISYS_CAPTURE_TIMEOUT_JIFFIES msecs_to_jiffies(2) + #define IPU6_FW_CALL_TIMEOUT_JIFFIES msecs_to_jiffies(2000) + #ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET + #define IPU6_LIB_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +index 67cf253..60346a6 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +@@ -1023,13 +1023,8 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, + ivb = vb2_buffer_to_ipu6_isys_video_buffer(vvb); + addr = ivb->dma_addr; + +- if (info->pin.addr != addr) { +- if (first) +- dev_err(dev, "Unexpected buffer address %pad\n", +- &addr); +- first = false; ++ if (info->pin.addr != addr) + continue; +- } + + if (info->error_info.error == + IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +index 53750b8..c4e39a6 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +@@ -886,7 +886,7 @@ bool is_support_vc(struct ipu6_isys_video *av) + } + + #endif +-static int start_stream_firmware(struct ipu6_isys_video *av, ++int start_stream_firmware(struct ipu6_isys_video *av, + struct ipu6_isys_buffer_list *bl) + { + struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg; +@@ -966,23 +966,11 @@ static int start_stream_firmware(struct ipu6_isys_video *av, + } + + reinit_completion(&stream->stream_start_completion); +-#ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET +- if(bl && is_support_vc(av)) { +-#else +- if (bl) { +-#endif +- send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; +- ipu6_fw_isys_dump_frame_buff_set(dev, buf, +- stream_cfg->nof_output_pins); +- ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, +- buf, msg->dma_addr, +- sizeof(*buf), send_type); +- } else { +- send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; +- ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, +- send_type); +- } ++ dev_dbg(dev, "start stream: start\n"); + ++ send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; ++ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, ++ send_type); + if (ret < 0) { + dev_err(dev, "can't start streaming (%d)\n", ret); + goto out_stream_close; +@@ -1002,7 +990,11 @@ static int start_stream_firmware(struct ipu6_isys_video *av, + } + #ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET + if (bl && !is_support_vc(av)) { +- dev_dbg(dev, "start stream: capture\n"); ++#else ++ if (bl) { ++#endif ++ reinit_completion(&stream->stream_initialcapture_completion); ++ dev_dbg(dev, "start stream: initial capture\n"); + send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE; + ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream_cfg->nof_output_pins); + ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, +@@ -1014,14 +1006,21 @@ static int start_stream_firmware(struct ipu6_isys_video *av, + dev_err(dev, "can't queue buffers (%d)\n", ret); + goto out_stream_close; + } ++ ++ tout = wait_for_completion_timeout(&stream->stream_initialcapture_completion, ++ IPU6_ISYS_CAPTURE_TIMEOUT_JIFFIES); ++ if (!tout) { ++ dev_warn(dev, "start initial capture time out\n"); ++ } else if (stream->error) { ++ dev_warn(dev, "start initial capture error: %d\n", stream->error); ++ } else { ++ dev_dbg(dev, "start initial capture : complete\n"); ++ } ++ ret = 0; + } + else + dev_dbg(dev, "start stream: complete\n"); + +-#else +- dev_dbg(dev, "start stream: complete\n"); +-#endif +- + return 0; + + out_stream_close: +@@ -1050,7 +1049,7 @@ out_put_stream_opened: + return ret; + } + +-static void stop_streaming_firmware(struct ipu6_isys_video *av) ++int stop_streaming_firmware(struct ipu6_isys_video *av) + { + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu6_isys_stream *stream = av->stream; +@@ -1063,7 +1062,7 @@ static void stop_streaming_firmware(struct ipu6_isys_video *av) + + if (ret < 0) { + dev_err(dev, "can't stop stream (%d)\n", ret); +- return; ++ return -EIO; + } + + tout = wait_for_completion_timeout(&stream->stream_stop_completion, +@@ -1072,15 +1071,23 @@ static void stop_streaming_firmware(struct ipu6_isys_video *av) + #else + IPU6_FW_CALL_TIMEOUT_JIFFIES); + #endif +- if (!tout) +- dev_warn(dev, "stream stop time out\n"); +- else if (stream->error) +- dev_warn(dev, "stream stop error: %d\n", stream->error); +- else +- dev_dbg(dev, "stop stream: complete\n"); ++ if (!tout) { ++ dev_err(dev, "stream stop time out for entity %s\n", ++ av->vdev.entity.name); ++ ret = -ETIMEDOUT; ++ } else if (stream->error) { ++ dev_err(dev, "stream stop failed for entity %s with error %d\n", ++ av->vdev.entity.name, stream->error); ++ ret = -EIO; ++ } else { ++ dev_dbg(dev, "stop stream complete for entity %s\n", ++ av->vdev.entity.name); ++ ret = 0; ++ } ++ return ret; + } + +-static void close_streaming_firmware(struct ipu6_isys_video *av) ++int close_streaming_firmware(struct ipu6_isys_video *av) + { + struct ipu6_isys_stream *stream = av->stream; + struct device *dev = &av->isys->adev->auxdev.dev; +@@ -1092,7 +1099,7 @@ static void close_streaming_firmware(struct ipu6_isys_video *av) + IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); + if (ret < 0) { + dev_err(dev, "can't close stream (%d)\n", ret); +- return; ++ return -EIO; + } + + tout = wait_for_completion_timeout(&stream->stream_close_completion, +@@ -1101,12 +1108,19 @@ static void close_streaming_firmware(struct ipu6_isys_video *av) + #else + IPU6_FW_CALL_TIMEOUT_JIFFIES); + #endif +- if (!tout) +- dev_warn(dev, "stream close time out\n"); +- else if (stream->error) +- dev_warn(dev, "stream close error: %d\n", stream->error); +- else +- dev_dbg(dev, "close stream: complete\n"); ++ if (!tout) { ++ dev_err(dev, "stream close time out for entity %s\n", ++ av->vdev.entity.name); ++ ret = -ETIMEDOUT; ++ } else if (stream->error) { ++ dev_err(dev, "stream close failed for entity %s with error %d\n", ++ av->vdev.entity.name, stream->error); ++ ret = -EIO; ++ } else { ++ dev_dbg(dev, "close stream complete for entity %s\n", ++ av->vdev.entity.name); ++ ret = 0; ++ } + + #ifdef CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET + stream->last_sequence = atomic_read(&stream->sequence); +@@ -1115,6 +1129,7 @@ static void close_streaming_firmware(struct ipu6_isys_video *av) + + #endif + put_stream_opened(av); ++ return ret; + } + + int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, +@@ -1428,7 +1443,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, + r_pad = media_pad_remote_pad_first(&av->pad); + r_stream = get_remote_pad_stream(r_pad); + if (!state) { +- stop_streaming_firmware(av); ++ ret = stop_streaming_firmware(av); + + /* stop sub-device which connects with video */ + dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx for %s\n", +@@ -1442,7 +1457,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, + return ret; + } + +- close_streaming_firmware(av); ++ ret = close_streaming_firmware(av); + } else { + ret = start_stream_firmware(av, bl); + if (ret) { +@@ -1468,7 +1483,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, + return 0; + + out_media_entity_stop_streaming_firmware: +- stop_streaming_firmware(av); ++ ret = stop_streaming_firmware(av); + + return ret; + } +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +index 7999e79..01009a6 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +@@ -68,6 +68,7 @@ struct ipu6_isys_stream { + struct completion stream_open_completion; + struct completion stream_close_completion; + struct completion stream_start_completion; ++ struct completion stream_initialcapture_completion; + struct completion stream_stop_completion; + struct ipu6_isys *isys; + +diff --git a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +index 526c916..3460cf1 100644 +--- a/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c ++++ b/ipu6-drivers/drivers/media/pci/intel/ipu6/ipu6-isys.c +@@ -370,6 +370,7 @@ static void isys_stream_init(struct ipu6_isys *isys) + mutex_init(&isys->streams[i].mutex); + init_completion(&isys->streams[i].stream_open_completion); + init_completion(&isys->streams[i].stream_close_completion); ++ init_completion(&isys->streams[i].stream_initialcapture_completion); + init_completion(&isys->streams[i].stream_start_completion); + init_completion(&isys->streams[i].stream_stop_completion); + INIT_LIST_HEAD(&isys->streams[i].queues); +@@ -1711,6 +1712,7 @@ static int isys_isr_one(struct ipu6_bus_device *adev) + + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: ++ complete(&stream->stream_initialcapture_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: +-- +2.25.1 + diff --git a/patches/0100-isys-video-let-v4l2-set-default-colorspace.patch b/patches/0100-isys-video-let-v4l2-set-default-colorspace.patch new file mode 100644 index 00000000..f44d0523 --- /dev/null +++ b/patches/0100-isys-video-let-v4l2-set-default-colorspace.patch @@ -0,0 +1,25 @@ +From aa9461f0cdcc69bb4441df0539905b9815fe93a6 Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Mon, 19 Jan 2026 10:38:25 -0700 +Subject: [PATCH 100/105] isys-video: let v4l2 set default colorspace + +Signed-off-by: Florent Pirou +--- + ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +index f69e7a6..fdd59bc 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +@@ -257,7 +257,6 @@ static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, + &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); + + f->fmt.pix.field = V4L2_FIELD_NONE; +- f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; +-- +2.43.0 + diff --git a/patches/0101-ipu7-isys-make-modules-suffix-versioning-same-as-dkm.patch b/patches/0101-ipu7-isys-make-modules-suffix-versioning-same-as-dkm.patch new file mode 100644 index 00000000..e185f19d --- /dev/null +++ b/patches/0101-ipu7-isys-make-modules-suffix-versioning-same-as-dkm.patch @@ -0,0 +1,73 @@ +From b12a8f1ec6a84d5e3232144703c5d797399894d9 Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Sat, 17 Jan 2026 02:10:39 -0700 +Subject: [PATCH 101/105] ipu7-isys: make modules suffix versioning same as + dkms + +Signed-off-by: Florent Pirou +--- + ipu7-drivers/drivers/media/pci/intel/ipu7/Makefile | 12 ++++++++++++ + .../drivers/media/pci/intel/ipu7/ipu7-isys.c | 1 + + ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7.c | 1 + + .../drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 1 + + 4 files changed, 15 insertions(+) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/Makefile b/ipu7-drivers/drivers/media/pci/intel/ipu7/Makefile +index 5041701..a862e2c 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/Makefile ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/Makefile +@@ -1,5 +1,15 @@ + # SPDX-License-Identifier: GPL-2.0 + # Copyright (c) 2017 - 2025 Intel Corporation. ++KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | sed 's/[^0-9.]*\([0-9]\.[0-9]*\)\..*/\1.0/') ++ ++CC := ${CC} -I ${M}/include -I ${M}/$(KERNEL_VERSION)/include-overrides -DCONFIG_DEBUG_FS -DCONFIG_INTEL_IPU_ACPI -DCONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -DCONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ ++KBUILD_EXTRA_SYMBOLS += ${M}/Module.symvers ++ ++ccflags-y += -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" ++ ++export EXTERNAL_BUILD = 1 ++export CONFIG_VIDEO_INTEL_IPU7=m + + is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) + ifeq ($(is_kernel_lt_6_10), 1) +@@ -32,6 +42,8 @@ intel-ipu7-isys-objs += ipu7-isys-tpg.o + endif + obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o + ++subdir-ccflags-y += -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" ++ + obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ + + ccflags-y += -I$(src)/ +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.c +index ea46c8c..ee6470a 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.c +@@ -1501,3 +1501,4 @@ MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); + MODULE_IMPORT_NS(INTEL_IPU7); + MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); + #endif ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7.c +index a9f03ed..f48b591 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7.c +@@ -2877,3 +2877,4 @@ MODULE_AUTHOR("Qingwu Zhang "); + MODULE_AUTHOR("Intel"); + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Intel ipu7 pci driver"); ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c +index 838f0b2..b5ff06a 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c +@@ -1554,3 +1554,4 @@ MODULE_IMPORT_NS("DMA_BUF"); + MODULE_IMPORT_NS(INTEL_IPU7); + MODULE_IMPORT_NS(DMA_BUF); + #endif ++MODULE_VERSION(DRIVER_VERSION_SUFFIX); +-- +2.43.0 + diff --git a/patches/0102-ipu7-isys-allow-virtual-channels-on-16-csi2-input-vi.patch b/patches/0102-ipu7-isys-allow-virtual-channels-on-16-csi2-input-vi.patch new file mode 100644 index 00000000..63e4df25 --- /dev/null +++ b/patches/0102-ipu7-isys-allow-virtual-channels-on-16-csi2-input-vi.patch @@ -0,0 +1,29 @@ +From 251af50e4701db5fcebce9478650ec18f22838e9 Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Sat, 17 Jan 2026 02:05:34 -0700 +Subject: [PATCH 102/105] ipu7-isys : allow virtual-channels on 16 csi2 input + video capture + +* ipu7-isys : extend per csi2 video captures v4l2 to 16 sensors input streams + +Signed-off-by: Florent Pirou +--- + ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h +index 6c23b80..dfca0d2 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h +@@ -20,7 +20,7 @@ struct ipu7_isys_stream; + #define INVALID_VC_ID -1 + #define IPU7_NR_OF_CSI2_SINK_PADS 1U + #define IPU7_CSI2_PAD_SINK 0U +-#define IPU7_NR_OF_CSI2_SRC_PADS 8U ++#define IPU7_NR_OF_CSI2_SRC_PADS 16U + #define IPU7_CSI2_PAD_SRC 1U + #define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ + IPU7_NR_OF_CSI2_SRC_PADS) +-- +2.43.0 + diff --git a/patches/0103-ipu7-isys-add-d4xx-pixel-format-support.patch b/patches/0103-ipu7-isys-add-d4xx-pixel-format-support.patch new file mode 100644 index 00000000..3b4b5768 --- /dev/null +++ b/patches/0103-ipu7-isys-add-d4xx-pixel-format-support.patch @@ -0,0 +1,139 @@ +From ef52957c76686f674a76783e53af6f092e9ebea6 Mon Sep 17 00:00:00 2001 +From: Florent Pirou +Date: Sat, 17 Jan 2026 02:08:28 -0700 +Subject: [PATCH 103/105] ipu7-isys : add d4xx pixel format support + +Signed-off-by: Florent Pirou +--- + .../media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h | 1 + + .../drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 3 +++ + .../drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c | 5 ++++- + .../drivers/media/pci/intel/ipu7/ipu7-isys-video.c | 13 +++++++++++++ + .../drivers/media/pci/intel/ipu7/ipu7-isys.h | 4 ++-- + 5 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h +index dc63449..9abf8d1 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h +@@ -125,6 +125,7 @@ enum ipu7_insys_frame_format_type { + IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, + IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, + IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, ++ IPU_INSYS_FRAME_FORMAT_VYUY = 34, + N_IPU_INSYS_FRAME_FORMAT + }; + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c +index 6ba21c3..75be4a7 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c +@@ -28,11 +28,13 @@ + #include "ipu7-isys-csi-phy.h" + + static const u32 csi2_supported_codes[] = { ++ MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_VYUY8_1X16, + MEDIA_BUS_FMT_YUYV10_1X20, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, +@@ -46,6 +48,7 @@ static const u32 csi2_supported_codes[] = { + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_FIXED, + 0, + }; + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c +index 98b6ef6..45be983 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c +@@ -19,6 +19,7 @@ + #include "ipu7-isys.h" + #include "ipu7-isys-subdev.h" + ++ + unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) + { + switch (code) { +@@ -30,13 +31,14 @@ unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) + return MIPI_CSI2_DT_YUV422_10B; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: ++ case MEDIA_BUS_FMT_Y10_1X10: ++ case MEDIA_BUS_FMT_VYUY8_1X16: + return MIPI_CSI2_DT_YUV422_8B; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return MIPI_CSI2_DT_RAW12; +- case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: +@@ -46,6 +48,7 @@ unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: ++ case MEDIA_BUS_FMT_Y8_1X8: + return MIPI_CSI2_DT_RAW8; + default: + WARN_ON(1); +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +index fdd59bc..0d35224 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +@@ -41,6 +41,16 @@ + #include "ipu7-platform-regs.h" + + const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { ++ { V4L2_PIX_FMT_GREY, 8, 8, MEDIA_BUS_FMT_Y8_1X8, ++ IPU_INSYS_FRAME_FORMAT_RAW8 }, ++ { V4L2_PIX_FMT_Y10, 16, 10, MEDIA_BUS_FMT_Y10_1X10, ++ IPU_INSYS_FRAME_FORMAT_RAW16 }, ++ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, ++ IPU_INSYS_FRAME_FORMAT_UYVY}, ++ { V4L2_PIX_FMT_Y8I, 16, 16, MEDIA_BUS_FMT_VYUY8_1X16, ++ IPU_INSYS_FRAME_FORMAT_UYVY}, ++ { V4L2_PIX_FMT_Z16, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, ++ IPU_INSYS_FRAME_FORMAT_UYVY}, + {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, +@@ -87,8 +97,11 @@ const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { + IPU_INSYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, + IPU_INSYS_FRAME_FORMAT_RGB565}, ++ {V4L2_PIX_FMT_Y12I, 32, 24, MEDIA_BUS_FMT_RGB888_1X24, ++ IPU_INSYS_FRAME_FORMAT_RGBA888}, + {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, + IPU_INSYS_FRAME_FORMAT_RGBA888}, ++ {V4L2_META_FMT_D4XX, 8, 8, MEDIA_BUS_FMT_FIXED, 0}, + }; + + static int video_open(struct file *file) +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.h +index 2e45258..19eea94 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys.h +@@ -43,8 +43,8 @@ struct dentry; + #define IPU_ISYS_SIZE_SEND_QUEUE 40U + #define IPU_ISYS_NUM_RECV_QUEUE 1U + +-#define IPU_ISYS_MIN_WIDTH 2U +-#define IPU_ISYS_MIN_HEIGHT 2U ++#define IPU_ISYS_MIN_WIDTH 1U ++#define IPU_ISYS_MIN_HEIGHT 1U + #define IPU_ISYS_MAX_WIDTH 8160U + #define IPU_ISYS_MAX_HEIGHT 8190U + +-- +2.43.0 + diff --git a/patches/0104-ipu7-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch b/patches/0104-ipu7-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch new file mode 100644 index 00000000..f08796b6 --- /dev/null +++ b/patches/0104-ipu7-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch @@ -0,0 +1,1621 @@ +From a40415416e69bd707421278898d0b176f9f60f82 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 10:31:00 -0700 +Subject: [PATCH 104/105] ipu7-isys: add video VIDIOC_S_EXT_CTRLS callbacks by + streamid + +* isys-video: add video VIDIOC_S_EXT_CTRLS callbacks by streamid +* isys-csi: add graph walk subdev to get frame desc by streamid +* isys-csi: add graph walk subdev to enable/disable streams by streamid +* isys-video: add V4L2_CID_IPU definitions + +Signed-off-by: florent pirou +--- + .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 85 ++- + .../media/pci/intel/ipu7/ipu7-isys-csi-phy.h | 4 + + .../media/pci/intel/ipu7/ipu7-isys-csi2.c | 481 +++++++++++++- + .../media/pci/intel/ipu7/ipu7-isys-csi2.h | 11 + + .../pci/intel/ipu7/ipu7-isys-video-ext.h | 61 ++ + .../media/pci/intel/ipu7/ipu7-isys-video.c | 624 +++++++++++++++++- + .../media/pci/intel/ipu7/ipu7-isys-video.h | 4 + + 7 files changed, 1256 insertions(+), 14 deletions(-) + create mode 100644 ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video-ext.h + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c +index 97cd47d..cc6f57f 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c +@@ -22,6 +22,9 @@ + #include "ipu7-isys-csi2-regs.h" + #include "ipu7-platform-regs.h" + #include "ipu7-isys-csi-phy.h" ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++#include "ipu7-isys-video-ext.h" ++#endif + + #define PORT_A 0U + #define PORT_B 1U +@@ -276,6 +279,10 @@ static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) + + dwc_csi_write(isys, id, VC_EXTENSION, 0x0); + n = find_first_zero_bit(data_ids, N_DATA_IDS); ++ ++ dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with n/data_ids:%u/%p\n", ++ id, n, data_ids); ++ + if (n == N_DATA_IDS) + return -ENOSPC; + +@@ -296,7 +303,17 @@ static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) + return 0; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++/* ++ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set ++ * of streams. ++ */ ++#define CSI2_SUBDEV_MAX_STREAM_ID 63 ++static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id, ++ struct v4l2_subdev_state *state, u64 streams_mask) ++#else + static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) ++#endif + { + struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; + struct device *dev = &csi2->isys->adev->auxdev.dev; +@@ -308,9 +325,52 @@ static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) + + pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); + if (IS_ERR(pad)) { ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ ret = ipu7_isys_csi2_walk_unique_remote_desc(state, csi2, ++ &csi2->asd.sd.entity, ++ &desc); ++ if (!ret) { ++ ++ ret = -EINVAL; ++ for (i = 0; i < desc.num_entries; i++) { ++ desc_entry = &desc.entry[i]; ++ ++ dev_dbg(dev, "%s: stream:%u, mbus:%x, vc/dt:%u/%x\n", ++ __func__, ++ desc_entry->stream, ++ desc_entry->pixelcode, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { ++ ++ ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ if (ret) { ++ dev_warn(dev, "failed to set frame descriptor: %d\n", ret); ++ continue; ++ } ++ } ++ } ++ } ++ ++ if (!ret) ++ return ret; ++ ++fallback: ++ pad = media_pad_remote_pad_first(&csi2->asd.sd.entity.pads[IPU7_CSI2_PAD_SINK]); ++ dev_warn(dev, "fallback on %s first remote pad\n", ++ csi2->asd.sd.name); ++ if (IS_ERR(pad)) { ++ dev_warn(dev, "can't get first remote pad of %s (%ld)\n", ++ csi2->asd.sd.name, PTR_ERR(pad)); ++ return PTR_ERR(pad); ++ } ++#else + dev_warn(dev, "can't get remote source pad of %s (%ld)\n", + csi2->asd.sd.name, PTR_ERR(pad)); + return PTR_ERR(pad); ++#endif + } + + ext_sd = media_entity_to_v4l2_subdev(pad->entity); +@@ -329,11 +389,21 @@ static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) + + for (i = 0; i < desc.num_entries; i++) { + desc_entry = &desc.entry[i]; ++ ++ dev_dbg(dev, "%s: stream:%u, mbus:%x, vc/dt:%u/%x\n", ++ __func__, ++ desc_entry->stream, ++ desc_entry->pixelcode, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ + if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { + ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, + desc_entry->bus.csi2.dt); +- if (ret) ++ if (ret) { ++ dev_warn(dev, "failed to set frame descriptor: %d\n", ret); + return ret; ++ } + } + } + +@@ -957,7 +1027,12 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + return 0; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2, ++ struct v4l2_subdev_state *state, u64 streams_mask) ++#else + int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) ++#endif + { + struct ipu7_isys *isys = csi2->isys; + u32 lanes = csi2->nlanes; +@@ -989,7 +1064,11 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) + } + + ipu7_isys_csi_ctrl_cfg(csi2); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ ipu7_isys_csi_ctrl_dids_config(csi2, id, state, streams_mask); ++#else + ipu7_isys_csi_ctrl_dids_config(csi2, id); ++#endif + + ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); + if (ret < 0) +@@ -1038,4 +1117,8 @@ void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) + if (!is_ipu7(isys->adev->isp->hw_ver) && + csi2->nlanes > 2U && csi2->port == PORT_A) + ipu7_isys_csi_phy_reset(isys, PORT_B); ++ ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ bitmap_clear(data_ids, 0, N_DATA_IDS); ++#endif + } +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h +index dfdcb61..7e36837 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h +@@ -11,6 +11,10 @@ struct ipu7_isys; + #define PHY_MODE_DPHY 0U + #define PHY_MODE_CPHY 1U + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2, struct v4l2_subdev_state *state, u64 streams_mask); ++#else + int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); ++#endif + void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); + #endif +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c +index 75be4a7..aa3e6d2 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c +@@ -26,6 +26,9 @@ + #include "ipu7-isys-csi2.h" + #include "ipu7-isys-csi2-regs.h" + #include "ipu7-isys-csi-phy.h" ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++#include "ipu7-isys-video-ext.h" ++#endif + + static const u32 csi2_supported_codes[] = { + MEDIA_BUS_FMT_Y8_1X8, +@@ -56,39 +59,65 @@ static const u32 csi2_supported_codes[] = { + s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) + { + struct media_pad *src_pad; ++ struct v4l2_subdev_state *s_state; ++ struct v4l2_subdev *s_sd; ++ int ret = -EPIPE; + +- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); +- if (IS_ERR(src_pad)) { ++ s_state = v4l2_subdev_get_unlocked_active_state(&csi2->asd.sd); ++ if (!s_state) ++ return ret; ++ ++ src_pad = media_pad_remote_pad_first(&csi2->asd.sd.entity.pads[IPU7_CSI2_PAD_SINK]); ++ if (!src_pad) { + dev_err(&csi2->isys->adev->auxdev.dev, +- "can't get source pad of %s (%ld)\n", +- csi2->asd.sd.name, PTR_ERR(src_pad)); +- return PTR_ERR(src_pad); ++ "can't get source pad of %s\n", ++ csi2->asd.sd.name); ++ return -EINVAL; + } + ++ s_sd = media_entity_to_v4l2_subdev(src_pad->entity); ++ ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "%s:%u -> %s:%u\n", ++ csi2->asd.sd.name, IPU7_CSI2_PAD_SINK, ++ s_sd->name, src_pad->index); ++ + return v4l2_get_link_freq(src_pad, 0, 0); + } + #else + s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) + { + struct media_pad *src_pad; ++ struct v4l2_subdev_state *s_state; + struct v4l2_subdev *ext_sd; + struct device *dev; ++ int ret = -EPIPE; + + if (!csi2) + return -EINVAL; + ++ s_state = v4l2_subdev_get_unlocked_active_state(&csi2->asd.sd); ++ if (!s_state) ++ return ret; ++ + dev = &csi2->isys->adev->auxdev.dev; +- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); ++ src_pad = media_pad_remote_pad_first(&csi2->asd.sd.entity.pads[IPU7_CSI2_PAD_SINK]); + if (IS_ERR(src_pad)) { +- dev_err(dev, "can't get source pad of %s (%ld)\n", +- csi2->asd.sd.name, PTR_ERR(src_pad)); +- return PTR_ERR(src_pad); ++ dev_err(&csi2->isys->adev->auxdev.dev, ++ "can't get source pad of %s\n", ++ csi2->asd.sd.name); ++ return -EINVAL; + } + + ext_sd = media_entity_to_v4l2_subdev(src_pad->entity); + if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name)) + return -ENODEV; + ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "%s:%u -> %s:%u\n", ++ csi2->asd.sd.name, IPU7_CSI2_PAD_SINK, ++ ext_sd->name, src_pad->index); ++ + return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0); + } + #endif +@@ -182,7 +211,12 @@ static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) + csi2_irq_disable(csi2); + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2, ++ struct v4l2_subdev_state *state, u64 streams_mask) ++#else + static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) ++#endif + { + struct ipu7_isys *isys = csi2->isys; + struct device *dev = &isys->adev->auxdev.dev; +@@ -208,7 +242,11 @@ static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) + writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); + writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ ret = ipu7_isys_csi_phy_powerup(csi2, state, streams_mask); ++#else + ret = ipu7_isys_csi_phy_powerup(csi2); ++#endif + if (ret) { + dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); + return ret; +@@ -309,6 +347,146 @@ static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++static int ipu7_isys_csi2_link_validate(struct media_link *link) ++{ ++ ++ struct v4l2_subdev *sd = ++ media_entity_to_v4l2_subdev(link->sink->entity); ++ struct device *dev = sd->entity.graph_obj.mdev->dev; ++ struct ipu7_isys_subdev *asd; ++ struct ipu7_isys_csi2 *csi2; ++ struct v4l2_subdev_state *s_state; ++ struct v4l2_mbus_framefmt *s_fmt; ++ struct v4l2_subdev *s_sd; ++ struct media_pad *s_pad; ++ u32 s_stream, code; ++ int ret = -EPIPE; ++ ++ if (!link->source->entity) ++ return ret; ++ ++ asd = to_ipu7_isys_subdev(sd); ++ csi2 = to_ipu7_isys_csi2(asd); ++ dev = &asd->isys->adev->auxdev.dev; ++ ++ s_sd = media_entity_to_v4l2_subdev(link->source->entity); ++ s_state = v4l2_subdev_get_unlocked_active_state(s_sd); ++ if (!s_state) ++ return ret; ++ ++ dev_dbg(dev, "%s: validating link \"%s\":%u -> \"%s\"\n",__func__, ++ link->source->entity->name, link->source->index, ++ link->sink->entity->name); ++ ++ return 0; ++} ++ ++int ipu7_isys_csi2_streams_by_sources(struct ipu7_isys_csi2 *csi2, ++ u32 pad, u32 stream, bool on) ++{ ++ struct media_entity *entity = &csi2->asd.sd.entity; ++ struct media_device *mdev = entity->graph_obj.mdev; ++ struct device *dev = &csi2->isys->adev->auxdev.dev; ++ struct v4l2_subdev *sd = NULL; ++ struct v4l2_subdev_state *v4l2_state; ++ struct media_graph graph; ++ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; ++ struct v4l2_mbus_frame_desc desc; ++ unsigned int i; ++ int ret; ++ ++ if (!mdev) ++ return -EPIPE; ++ ++ mutex_lock(&mdev->graph_mutex); ++ if (media_graph_walk_init(&graph, mdev)) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s: walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_VID_MUX) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->get_frame_desc) ++ continue; ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ ret = sd->ops->pad->get_frame_desc(sd, pad, &desc); ++ if (ret) ++ continue; ++ ++ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) ++ continue; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (stream == desc.entry[i].stream) { ++ desc_entry = &desc.entry[i]; ++ break; ++ } ++ } ++ ++ if (!desc_entry) ++ continue; ++ ++ if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) ++ continue; ++ ++ ++ dev_dbg(dev, "%s: found %s subdev stream:%u vc/dt:%u/0x%x\n", __func__, ++ sd->name, ++ desc_entry->stream, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->disable_streams || !sd->ops->pad->enable_streams) ++ break; ++ ++ v4l2_state = v4l2_subdev_get_unlocked_active_state(sd); ++ if (!v4l2_state) ++ break; ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (on) ++ ret = sd->ops->pad->enable_streams(sd, v4l2_state, pad, BIT_ULL(stream)); ++ else ++ ret = sd->ops->pad->disable_streams(sd, v4l2_state, pad, BIT_ULL(stream)); ++ if (ret) ++ break; ++ ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++ return 0; ++ } ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++ ++error_graph_walk_source: ++ return -EINVAL; ++} ++#endif ++ + /* + * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set + * of streams. +@@ -330,7 +508,11 @@ static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, + dev_dbg(&csi2->isys->adev->auxdev.dev, + "stream on CSI2-%u with %u lanes\n", csi2->port, + csi2->nlanes); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ ret = ipu7_isys_csi2_enable_stream(csi2, state, streams_mask); ++#else + ret = ipu7_isys_csi2_enable_stream(csi2); ++#endif + if (ret) + return ret; + } +@@ -345,6 +527,14 @@ static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, + if (ret) + return ret; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ if (!ipu7_isys_csi2_streams_by_sources(csi2, sink_pad, sink_stream, true)) { ++ csi2->stream_count++; ++ return 0; ++ } ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "remote stream on fallback\n"); ++#endif + rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(rp->entity); + +@@ -382,6 +572,15 @@ static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, + if (ret) + return ret; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ if (!ipu7_isys_csi2_streams_by_sources(csi2, sink_pad, sink_stream, false)) { ++ if (--csi2->stream_count) ++ return 0; ++ goto ret; ++ } ++ dev_dbg(&csi2->isys->adev->auxdev.dev, ++ "remote stream off fallback\n"); ++#endif + rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(rp->entity); + +@@ -389,7 +588,7 @@ static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, + + if (--csi2->stream_count) + return 0; +- ++ret: + dev_dbg(&csi2->isys->adev->auxdev.dev, + "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); + +@@ -415,7 +614,11 @@ static const struct v4l2_subdev_ops csi2_sd_ops = { + }; + + static const struct media_entity_operations csi2_entity_ops = { ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ .link_validate = ipu7_isys_csi2_link_validate, ++#else + .link_validate = v4l2_subdev_link_validate, ++#endif + .has_pad_interdep = v4l2_subdev_has_pad_interdep, + }; + +@@ -571,3 +774,261 @@ int ipu7_isys_csi2_get_remote_desc(u32 source_stream, + + return 0; + } ++ ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++int ipu7_isys_csi2_walk_remote_desc_by_stream(u32 source_stream, ++ struct ipu7_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc_entry *entry, ++ int *nr_queues) ++{ ++ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; ++ struct device *dev = &csi2->isys->adev->auxdev.dev; ++ struct v4l2_mbus_frame_desc desc; ++ struct v4l2_subdev *source; ++ struct media_pad *pad; ++ struct media_graph graph; ++ struct media_entity *entity; ++ struct media_device *mdev; ++ struct v4l2_subdev *sd; ++ unsigned int pad_id; ++ u32 stream_id = source_stream; ++ unsigned int i; ++ int ret; ++ ++ source = media_entity_to_v4l2_subdev(source_entity); ++ if (!source) ++ return -EPIPE; ++ ++ pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); ++ if (!pad) ++ return -EPIPE; ++ ++ dev_dbg(dev, "%s: source pad/stream:%u/%u\n", __func__,pad->index, source_stream); ++ pad_id = pad->index; ++ entity = source_entity; ++ mdev = entity->graph_obj.mdev; ++ if (!mdev) { ++ ret = -EPIPE; ++ goto error_graph_walk_source; ++ } ++ ++ mutex_lock(&mdev->graph_mutex); ++ ret = media_graph_walk_init(&graph, mdev); ++ if (ret) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s: walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_VID_MUX) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->get_frame_desc) ++ continue; ++ ++ dev_dbg(dev, "%s(): %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ pad_id, ++ source_stream); ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ ret = sd->ops->pad->get_frame_desc(sd, pad_id, &desc); ++ if (ret) ++ continue; ++ ++ ret = -EINVAL; ++ ++ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) ++ continue; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (source_stream == desc.entry[i].stream) { ++ desc_entry = &desc.entry[i]; ++ break; ++ } ++ } ++ ++ if (!desc_entry) ++ continue; ++ ++ if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) ++ continue; ++ ++ *entry = *desc_entry; ++ ++ for (i = 0; i < desc.num_entries; i++) { ++ if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) ++ (*nr_queues)++; ++ } ++ ++ dev_dbg(dev, "%s: found frame desc stream:%u vc/dt:%u/0x%x\n", __func__, ++ desc_entry->stream, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ ret = 0; ++ break; ++ } ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++error_graph_walk_source: ++ return ret; ++} ++ ++int ipu7_isys_csi2_walk_unique_remote_desc(struct v4l2_subdev_state *state, ++ struct ipu7_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc *desc) ++{ ++ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; ++ struct device *dev = &csi2->isys->adev->auxdev.dev; ++ struct v4l2_mbus_frame_desc _desc; ++ struct v4l2_subdev *source; ++ struct media_pad *pad; ++ struct media_graph graph; ++ struct media_entity *entity; ++ struct media_device *mdev; ++ struct v4l2_subdev *sd; ++ struct v4l2_subdev_route *routes; ++ unsigned int pad_id; ++ unsigned int i, j; ++ int ret, skip = 0; ++ ++ if (!desc) ++ return -EPIPE; ++ ++ source = media_entity_to_v4l2_subdev(source_entity); ++ if (!source) ++ return -EPIPE; ++ ++ pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); ++ if (!pad) ++ return -EPIPE; ++ ++ pad_id = pad->index; ++ entity = source_entity; ++ mdev = entity->graph_obj.mdev; ++ if (!mdev) { ++ ret = -EPIPE; ++ goto error_graph_walk_source; ++ } ++ ++ mutex_lock(&mdev->graph_mutex); ++ ret = media_graph_walk_init(&graph, mdev); ++ if (ret) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s: walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_VID_MUX) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with get_frame_desc callback */ ++ if (!sd->ops || !sd->ops->pad || !sd->ops->pad->get_frame_desc) ++ continue; ++ ++ /* v4l2_subdev_call doesn't call, access directly */ ++ ret = sd->ops->pad->get_frame_desc(sd, pad_id, &_desc); ++ if (ret) ++ continue; ++ ++ if (_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) ++ continue; ++ ++ routes = state->routing.routes; ++ for (i = 0; i < _desc.num_entries; i++) { ++ ++ desc_entry = &_desc.entry[i]; ++ ++ skip = 0; ++ for (j = 0; j < state->routing.num_routes; j++) { ++ ++ /* ignore inactive routes */ ++ if (routes[j].source_stream == desc_entry->stream && ++ routes[j].flags != V4L2_SUBDEV_ROUTE_FL_ACTIVE) { ++ skip = 1; ++ break; ++ } ++ } ++ if (skip) ++ continue; ++ ++ for (j = 0; j < desc->num_entries; j++) { ++ ++ /* ignore duplicates frame desc */ ++ if (desc_entry->pixelcode == desc->entry[j].pixelcode && ++ desc_entry->bus.csi2.vc == desc->entry[j].bus.csi2.vc && ++ desc_entry->bus.csi2.dt == desc->entry[j].bus.csi2.dt) { ++ skip = 1; ++ break; ++ } ++ } ++ if (skip) ++ continue; ++ ++ /* don't exceed v4l2 api */ ++ if (desc->num_entries >= V4L2_FRAME_DESC_ENTRY_MAX) ++ continue; ++ ++ dev_dbg(dev, "%s: found frame desc stream:%u vc/dt:%u/0x%x\n", __func__, ++ desc_entry->stream, ++ desc_entry->bus.csi2.vc, ++ desc_entry->bus.csi2.dt); ++ ++ desc->entry[desc->num_entries] = *desc_entry; ++ desc->num_entries++; ++ } ++ } ++ ++ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; ++ ++ if (desc->num_entries) ++ ret = 0; ++ else ++ ret = -EINVAL; ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++error_graph_walk_source: ++ return ret; ++} ++#endif +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h +index dfca0d2..7397de2 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h +@@ -61,4 +61,15 @@ int ipu7_isys_csi2_get_remote_desc(u32 source_stream, + struct media_entity *source_entity, + struct v4l2_mbus_frame_desc_entry *entry, + int *nr_queues); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++int ipu7_isys_csi2_walk_remote_desc_by_stream(u32 source_stream, ++ struct ipu7_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc_entry *entry, ++ int *nr_queues); ++int ipu7_isys_csi2_walk_unique_remote_desc(struct v4l2_subdev_state *state, ++ struct ipu7_isys_csi2 *csi2, ++ struct media_entity *source_entity, ++ struct v4l2_mbus_frame_desc *desc); ++#endif + #endif /* IPU7_ISYS_CSI2_H */ +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video-ext.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video-ext.h +new file mode 100644 +index 0000000..7487242 +--- /dev/null ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video-ext.h +@@ -0,0 +1,61 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2013 - 2025 Intel Corporation ++ */ ++ ++#ifndef IPU7_ISYS_VIDEO_EXT_H ++#define IPU7_ISYS_VIDEO_EXT_H ++ ++#define V4L2_CID_IPU_BASE (V4L2_CID_USER_BASE + 0x1080) ++ ++#define V4L2_CID_IPU_STORE_CSI2_HEADER (V4L2_CID_IPU_BASE + 2) ++#define V4L2_CID_IPU_ISYS_COMPRESSION (V4L2_CID_IPU_BASE + 3) ++ ++#define V4L2_CID_IPU_QUERY_SUB_STREAM (V4L2_CID_IPU_BASE + 4) ++#define V4L2_CID_IPU_SET_SUB_STREAM (V4L2_CID_IPU_BASE + 5) ++ ++#define V4L2_CID_IPU_ENUMERATE_LINK (V4L2_CID_IPU_BASE + 6) ++ ++#define VIDIOC_IPU_GET_DRIVER_VERSION \ ++ _IOWR('v', BASE_VIDIOC_PRIVATE + 3, uint32_t) ++ ++enum ipu7_isys_enum_link_state { ++ IPU7_ISYS_LINK_STATE_DISABLED = 0, ++ IPU7_ISYS_LINK_STATE_ENABLED = 1, ++ IPU7_ISYS_LINK_STATE_DONE = 2, ++ IPU7_ISYS_LINK_STATE_MD = 3, ++ IPU7_ISYS_LINK_STATE_MAX, ++}; ++ ++static struct media_pad *other_pad(struct media_pad *pad); ++bool has_src_pad_stream_active(struct v4l2_subdev *sd, u32 stream, u32 pad); ++bool get_src_pad_by_src_stream(struct v4l2_subdev *sd, u32 stream, u32 *s_pad); ++ ++/* needed for callback */ ++extern int ipu7_isys_inherit_ctrls(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_set_fmt_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_enum_frameintervals_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_enum_fmt_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_enum_framesizes_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_set_parm_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++extern int ipu7_isys_get_parm_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data); ++ ++extern int media_pipeline_enumerate_by_stream_cb( ++ struct ipu7_isys_video *av, ++ int (*cb_fn)(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, ++ void *data), ++ void *data); ++ ++extern int ipu7_isys_csi2_streams_by_sources(struct ipu7_isys_csi2 *csi2, ++ u32 pad, u32 stream, bool on); ++ ++ ++#endif /* IPU7_ISYS_VIDEO_EXT_H */ +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +index 0d35224..e60e8c8 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +@@ -38,6 +38,9 @@ + #include "ipu7-fw-isys.h" + #include "ipu7-isys.h" + #include "ipu7-isys-video.h" ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++#include "ipu7-isys-video-ext.h" ++#endif + #include "ipu7-platform-regs.h" + + const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { +@@ -106,8 +109,8 @@ const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { + + static int video_open(struct file *file) + { +-#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET + struct ipu7_isys_video *av = video_drvdata(file); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET + struct ipu7_isys *isys = av->isys; + struct ipu7_bus_device *adev = isys->adev; + +@@ -119,6 +122,11 @@ static int video_open(struct file *file) + } + mutex_unlock(&isys->reset_mutex); + ++#endif ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ /* ipu_isys inherit remote sd ctrls */ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_ENABLED && media_pad_remote_pad_first(&av->pad)) ++ media_pipeline_enumerate_by_stream_cb(av, ipu7_isys_inherit_ctrls, NULL); + #endif + return v4l2_fh_open(file); + } +@@ -143,6 +151,7 @@ static int video_release(struct file *file) + } + + #endif ++ + const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) + { + unsigned int i; +@@ -172,7 +181,47 @@ static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) + { + unsigned int i, num_found; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_mbus_code_enum mce; ++ int ret=-EINVAL; ++ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) { + ++ mce.index = f->index; ++ mce.pad = 0; ++ ++ /* poll remote sensors */ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu7_isys_enum_fmt_subdev, &mce); ++ if (!ret) { ++ ret=-EINVAL; ++ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { ++ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ continue; ++ ++ if (f->mbus_code && f->mbus_code != mce.code) ++ continue; ++ ++ if (mce.code != ipu7_isys_pfmts[i].code) ++ continue; ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x, sd_code::0x%x\n", ++ __func__, ++ ipu7_isys_pfmts[i].code, ++ ipu7_isys_pfmts[i].pixelformat); ++ ++ f->flags = 0; ++ f->pixelformat = ipu7_isys_pfmts[i].pixelformat; ++ ++ return 0; ++ } ++ } ++ return ret; ++ } ++#endif + for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + continue; +@@ -198,6 +247,48 @@ static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) + { + unsigned int i; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_size_enum fse; ++ int ret = -EINVAL; ++ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) { ++ ++ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { ++ if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) ++ continue; ++ ++ fse.code = ipu7_isys_pfmts[i].code; ++ fse.index = fsize->index; ++ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu7_isys_enum_framesizes_subdev, &fse); ++ if (ret) ++ continue; ++ ++ if (!fse.max_width && !fse.max_height) { ++ fsize->discrete.width = 0; ++ fsize->discrete.height = 0; ++ return -EINVAL; ++ } ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x, sd_code::0x%x, WxH:%ux%u\n", ++ __func__, ++ ipu7_isys_pfmts[i].code, ++ ipu7_isys_pfmts[i].pixelformat, ++ fse.max_width, ++ fse.max_height); ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = fse.max_width; ++ fsize->discrete.height = fse.max_height; ++ return 0; ++ } ++ return -EINVAL; ++ } ++#endif + + if (fsize->index > 0) + return -EINVAL; +@@ -220,6 +311,169 @@ static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, + return -EINVAL; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++/* extended v4l2_ctrl_ops */ ++static int ipu7_isys_video_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ipu7_isys_video *av = ctrl->priv; ++ struct ipu7_isys *isys = av->isys; ++ mutex_lock(&isys->mutex); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_IPU_ENUMERATE_LINK: ++ av->enum_link_state = ctrl->val; ++ break; ++ } ++ ++ mutex_unlock(&isys->mutex); ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops ipu7_isys_video_ctrl_ops = { ++ .s_ctrl = ipu7_isys_video_s_ctrl, ++}; ++ ++static const struct v4l2_ctrl_config ipu7_isys_video_enum_link = { ++ .ops = &ipu7_isys_video_ctrl_ops, ++ .id = V4L2_CID_IPU_ENUMERATE_LINK, ++ .name = "Enumerate graph link", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0, ++ .max = IPU7_ISYS_LINK_STATE_MAX, ++ .step = 1, ++ .def = 0, ++}; ++ ++/* extended v4l2_ioctl_ops */ ++static struct media_pad *other_pad(struct media_pad *pad) ++{ ++ struct media_link *link; ++ ++ list_for_each_entry(link, &pad->entity->links, list) { ++ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) ++ != MEDIA_LNK_FL_DATA_LINK) ++ continue; ++ ++ return link->source == pad ? link->sink : link->source; ++ } ++ ++ WARN_ON(1); ++ return NULL; ++} ++ ++static int ipu7_isys_get_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval fi; ++ int ret = -ENOLINK; ++ ++ a->parm.capture.timeperframe.numerator = 1; ++ a->parm.capture.timeperframe.denominator = 30; ++ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DISABLED) ++ return 0; ++ ++ if (media_pad_remote_pad_first(&av->pad)) { ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu7_isys_get_parm_subdev, &fi); ++ ++ if (!ret) { ++ a->parm.capture.timeperframe.numerator = fi.interval.numerator; ++ a->parm.capture.timeperframe.denominator = fi.interval.denominator; ++ } ++ } ++ return ret; ++} ++ ++static int ipu7_isys_set_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval fi; ++ int ret = -ENOLINK; ++ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DISABLED) ++ return 0; ++ ++ fi.interval.numerator = a->parm.capture.timeperframe.numerator; ++ fi.interval.denominator = a->parm.capture.timeperframe.denominator; ++ ++ if (media_pad_remote_pad_first(&av->pad)) ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu7_isys_set_parm_subdev, &fi); ++ ++ return ret; ++} ++ ++static int ipu7_isys_enum_frameintervals(struct file *file, void *fh, ++ struct v4l2_frmivalenum *intervals) ++{ ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct v4l2_subdev_frame_interval_enum fie; ++ unsigned int i, num_found; ++ int ret=-EINVAL; ++ ++ /* poll remote sensors */ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) { ++ ++ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { ++ if (intervals->pixel_format != ipu7_isys_pfmts[i].pixelformat) ++ continue; ++ ++ fie.code = ipu7_isys_pfmts[i].code; ++ fie.index = intervals->index; ++ fie.width = intervals->width; ++ fie.height = intervals->height; ++ ++ ret = media_pipeline_enumerate_by_stream_cb(av, ++ ipu7_isys_enum_frameintervals_subdev, &fie); ++ if (ret) ++ continue; ++ ++ if (!fie.interval.numerator && ++ !fie.interval.denominator) { ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = 1; ++ intervals->discrete.denominator = 30; ++ return -EINVAL; ++ } ++ ++ /* Code found */ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "%s: sd_mbus:0x%x,sd_code::0x%x,WxH:%ux%u,fps:%u/%u\n", ++ __func__, ++ ipu7_isys_pfmts[i].code, ++ ipu7_isys_pfmts[i].pixelformat, ++ fie.width, fie.height, ++ fie.interval.numerator,fie.interval.denominator); ++ ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = fie.interval.numerator; ++ intervals->discrete.denominator = fie.interval.denominator; ++ return 0; ++ } ++ return -EINVAL; ++ } ++ ++ if (intervals->index > 0) ++ return -EINVAL; ++ ++ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { ++ if (intervals->pixel_format != ipu7_isys_pfmts[i].pixelformat) ++ continue; ++ ++ /* Code found */ ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete.numerator = 1; ++ intervals->discrete.denominator = 30; ++ ++ return 0; ++ } ++ return -EINVAL; ++} ++#endif ++ + static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) + { +@@ -285,6 +539,30 @@ static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, + + __ipu_isys_vidioc_try_fmt_vid_cap(av, f); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) ++ { ++ int ret = 0; ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = 0, ++ }; ++ struct v4l2_mbus_framefmt format = { ++ .width = f->fmt.pix.width, ++ .height = f->fmt.pix.height, ++ .code = ipu7_isys_get_isys_format(f->fmt.pix.pixelformat)->code, ++ .field = f->fmt.pix.field, ++ .colorspace = f->fmt.pix.colorspace, ++ .ycbcr_enc = f->fmt.pix.ycbcr_enc, ++ .quantization = f->fmt.pix.quantization, ++ .xfer_func = f->fmt.pix.xfer_func, ++ }; ++ fmt.format = format; ++ ret = media_pipeline_enumerate_by_stream_cb(av, ipu7_isys_set_fmt_subdev, &fmt); ++ if (ret) ++ return -EINVAL; ++ } ++#endif + return 0; + } + +@@ -293,9 +571,51 @@ static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, + { + struct ipu7_isys_video *av = video_drvdata(file); + ++#ifndef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS + ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); +- av->pix_fmt = f->fmt.pix; ++#else ++ struct media_pad *source_pad = media_pad_remote_pad_first(&av->pad); ++ struct media_pad *remote_pad = source_pad; ++ struct v4l2_subdev *sd = NULL; + ++ if (vb2_is_busy(&av->aq.vbq)) ++ return -EBUSY; ++ ++ __ipu_isys_vidioc_try_fmt_vid_cap(av, f); ++ ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) { ++ ++ int ret = 0; ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = 0, ++ }; ++ struct v4l2_mbus_framefmt format = { ++ .width = f->fmt.pix.width, ++ .height = f->fmt.pix.height, ++ .code = ipu7_isys_get_isys_format(f->fmt.pix.pixelformat)->code, ++ .field = f->fmt.pix.field, ++ .colorspace = f->fmt.pix.colorspace, ++ .ycbcr_enc = f->fmt.pix.ycbcr_enc, ++ .quantization = f->fmt.pix.quantization, ++ .xfer_func = f->fmt.pix.xfer_func, ++ }; ++ fmt.format = format; ++ ret = media_pipeline_enumerate_by_stream_cb(av, ipu7_isys_set_fmt_subdev, &fmt); ++ if (ret) ++ return -EINVAL; ++ ++ /* TODO: set format for CSI-2 ++ do { ++ // Non-subdev nodes can be safely ignored here. ++ if (!is_media_entity_v4l2_subdev(remote_pad->entity)) ++ continue; ++ // Set only for IPU CSI entities ++ } while ((remote_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]))); ++ */ ++ } ++#endif ++ av->pix_fmt = f->fmt.pix; + return 0; + } + +@@ -377,7 +697,9 @@ static int link_validate(struct media_link *link) + } + + v4l2_subdev_unlock_state(s_state); +- ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ //av->enum_link_state = IPU7_ISYS_LINK_STATE_DONE; ++#endif + return 0; + unlock: + v4l2_subdev_unlock_state(s_state); +@@ -958,6 +1280,266 @@ out_media_entity_stop_streaming_firmware: + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++int ipu7_isys_set_fmt_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_format *fmt = ++ (struct v4l2_subdev_format *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->set_fmt) ++ ret = sd->ops->pad->set_fmt(sd, NULL, fmt); ++ ++ return ret; ++} ++ ++int ipu7_isys_get_parm_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval *fi = ++ (struct v4l2_subdev_frame_interval *)data; ++ struct v4l2_subdev_state *state = v4l2_subdev_get_unlocked_active_state(sd); ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->get_frame_interval) ++ ret = sd->ops->pad->get_frame_interval(sd, state, fi); ++ ++ return ret; ++} ++ ++int ipu7_isys_set_parm_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval *fi = ++ (struct v4l2_subdev_frame_interval *)data; ++ struct v4l2_subdev_state *state = v4l2_subdev_get_unlocked_active_state(sd); ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->set_frame_interval) ++ ret = sd->ops->pad->set_frame_interval(sd, state, fi); ++ ++ return ret; ++} ++ ++int ipu7_isys_enum_fmt_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_mbus_code_enum *mce = ++ (struct v4l2_subdev_mbus_code_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_mbus_code) ++ ret = sd->ops->pad->enum_mbus_code(sd, NULL, mce); ++ ++ return ret; ++} ++ ++int ipu7_isys_enum_framesizes_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_size_enum *fse = ++ (struct v4l2_subdev_frame_size_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_size) ++ ret = sd->ops->pad->enum_frame_size(sd, NULL, fse); ++ ++ return ret; ++} ++ ++int ipu7_isys_enum_frameintervals_subdev(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ struct v4l2_subdev_frame_interval_enum *fie = ++ (struct v4l2_subdev_frame_interval_enum *)data; ++ /* v4l2_subdev_call doesn't call, access directly */ ++ if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_interval) ++ ret = sd->ops->pad->enum_frame_interval(sd, NULL, fie); ++ ++ return ret; ++} ++ ++int ipu7_isys_inherit_ctrls(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, void *data) ++{ ++ int ret = 0; ++ ret = v4l2_ctrl_add_handler(&av->ctrl_handler, ++ sd->ctrl_handler, NULL, true); ++ return ret; ++} ++ ++bool has_src_pad_stream_active(struct v4l2_subdev *sd, u32 stream, u32 pad) ++{ ++ struct v4l2_subdev_state *state; ++ struct v4l2_subdev_route *routes; ++ bool has_active_source_stream = false; ++ unsigned int i; ++ ++ state = v4l2_subdev_lock_and_get_active_state(sd); ++ if (!state) ++ return has_active_source_stream; ++ ++ dev_dbg(sd->dev, "%s(): check %s pad/stream: %u/%u has V4L2_SUBDEV_ROUTE_FL_ACTIVE flag\n", ++ __func__, ++ sd->name, ++ pad, ++ stream); ++ routes = state->routing.routes; ++ for (i = 0; i < state->routing.num_routes; i++) { ++ dev_dbg(sd->dev, "%s(): %s pad/stream: %u/%u flags=0x%x\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ routes[i].source_stream, ++ routes[i].flags); ++ if (routes[i].source_pad == pad && ++ routes[i].source_stream == stream && ++ routes[i].flags == V4L2_SUBDEV_ROUTE_FL_ACTIVE) { ++ dev_dbg(sd->dev, "%s(): %s pad/stream: %u/%u has V4L2_SUBDEV_ROUTE_FL_ACTIVE flag\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ routes[i].source_stream); ++ has_active_source_stream = true; ++ break; ++ } ++ } ++ ++ v4l2_subdev_unlock_state(state); ++ ++ return has_active_source_stream; ++} ++ ++bool get_src_pad_by_src_stream(struct v4l2_subdev *sd, u32 stream, u32 *s_pad) ++{ ++ struct v4l2_subdev_state *state; ++ struct v4l2_subdev_route *routes; ++ bool has_source_pad = false; ++ unsigned int i; ++ ++ if (!s_pad) ++ return has_source_pad; ++ ++ state = v4l2_subdev_lock_and_get_active_state(sd); ++ if (!state) ++ return has_source_pad; ++ ++ routes = state->routing.routes; ++ for (i = 0; i < state->routing.num_routes; i++) { ++ if (routes[i].source_stream == stream) { ++ dev_dbg(sd->dev, "%s(): Found %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ routes[i].source_pad, ++ stream); ++ *s_pad = routes[i].source_pad; ++ has_source_pad = true; ++ break; ++ } ++ } ++ ++ v4l2_subdev_unlock_state(state); ++ return has_source_pad; ++} ++ ++int media_pipeline_enumerate_by_stream_cb( ++ struct ipu7_isys_video *av, ++ int (*cb_fn)(struct ipu7_isys_video *av, ++ struct v4l2_subdev *sd, ++ void *data), ++ void *data) ++{ ++ int i; ++ struct media_graph graph; ++ struct media_entity *entity = &av->vdev.entity; ++ struct media_device *mdev = entity->graph_obj.mdev; ++ struct device *dev = entity->graph_obj.mdev->dev; ++ struct media_pad *remote_pad = media_pad_remote_pad_unique(&av->pad); ++ struct ipu7_isys_stream *stream = av->stream; ++ struct ipu7_isys_subdev *asd; ++ struct v4l2_subdev *sd; ++ unsigned int pad_id; ++ u32 stream_id; ++ int ret = -ENOLINK; ++ ++ if (!remote_pad) { ++ dev_err(dev, ++ "%s:%d no remote pad found\n", ++ __func__, __LINE__); ++ goto error_graph_walk_source; ++ } ++ ++ pad_id = remote_pad->index; ++ sd = media_entity_to_v4l2_subdev(remote_pad->entity); ++ if(!sd) { ++ dev_err(dev, ++ "%s():%d no stream found on remote pad %u\n", ++ __func__, __LINE__, pad_id); ++ goto error_graph_walk_source; ++ } ++ stream_id = ipu7_isys_get_src_stream_by_src_pad(sd, pad_id); ++ dev_dbg(dev, ++ "%s(): Check %s (vc=%u) inheritence from %s pad/stream %u/%u\n", ++ __func__, av->vdev.name, av->vc, ++ sd->name, pad_id, stream_id); ++ ++ mutex_lock(&mdev->graph_mutex); ++ ret = media_graph_walk_init(&graph, mdev); ++ if (ret) ++ goto error_graph_walk_start_enum; ++ ++ media_graph_walk_start(&graph, entity); ++ while ((entity = media_graph_walk_next(&graph))) { ++ dev_dbg(dev, ++ "%s(): walk entity name: %s, type:%x, func:%x\n", ++ __func__, ++ entity->name, ++ entity->obj_type, ++ entity->function); ++ ++ sd = (entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV && \ ++ entity->function == MEDIA_ENT_F_CAM_SENSOR) \ ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (!sd) ++ continue; ++ if (!strlen(sd->name)) ++ continue; ++ ++ /* pre-filter sub-devices */ ++ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) ++ continue; ++ ++ /* access only subdevices with src pad on same stream id */ ++ if(!get_src_pad_by_src_stream(sd, stream_id, &pad_id)) ++ continue; ++ ++ /* access only subdevices on source stream active route */ ++ if(!has_src_pad_stream_active(sd, stream_id, pad_id)) ++ continue; ++ ++ dev_dbg(dev, "%s(): %s pad/stream: %u/%u\n", ++ __func__, ++ sd->name, ++ pad_id, ++ stream_id); ++ ++ /* call function once */ ++ ret = cb_fn(av, sd, data); ++ break; ++ } ++ av->enum_link_state = IPU7_ISYS_LINK_STATE_DONE; ++ ++error_graph_walk_start_enum: ++ media_graph_walk_cleanup(&graph); ++ mutex_unlock(&mdev->graph_mutex); ++error_graph_walk_source: ++ return ret; ++} ++#endif ++ + static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { + .vidioc_querycap = ipu7_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, +@@ -974,6 +1556,11 @@ static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ .vidioc_g_parm = ipu7_isys_get_parm, ++ .vidioc_s_parm = ipu7_isys_set_parm, ++ .vidioc_enum_frameintervals = ipu7_isys_enum_frameintervals, ++#endif + }; + + static const struct media_entity_operations entity_ops = { +@@ -1121,6 +1708,14 @@ int ipu7_isys_setup_video(struct ipu7_isys_video *av, + + v4l2_subdev_unlock_state(state); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ if (av->enum_link_state == IPU7_ISYS_LINK_STATE_DONE) ++ ret = ipu7_isys_csi2_walk_remote_desc_by_stream(route->sink_stream, ++ to_ipu7_isys_csi2(asd), ++ *source_entity, &entry, ++ nr_queues); ++ else ++#endif + ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, + to_ipu7_isys_csi2(asd), + *source_entity, &entry, +@@ -1218,7 +1813,27 @@ int ipu7_isys_video_init(struct ipu7_isys_video *av) + if (ret) + goto out_media_entity_cleanup; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ /* create v4l2 ctrl for capture video node */ ++ ret = v4l2_ctrl_handler_init(&av->ctrl_handler, 0); ++ if (ret) { ++ dev_err(&av->isys->adev->auxdev.dev, ++ "failed to init v4l2 ctrl handler for %s\n", av->vdev.name); ++ goto out_ctrl_handler_cleanup; ++ } ++ v4l2_ctrl_new_custom(&av->ctrl_handler, &ipu7_isys_video_enum_link, av); ++ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "ext-ctrls: %s: set video_enum_link\n", av->vdev.name); ++ ++ av->vdev.ctrl_handler = &av->ctrl_handler; ++ return ret; ++ ++out_ctrl_handler_cleanup: ++ v4l2_ctrl_handler_free(&av->ctrl_handler); ++#else + return ret; ++#endif + + out_media_entity_cleanup: + vb2_video_unregister_device(&av->vdev); +@@ -1235,6 +1850,9 @@ out_mutex_destroy: + + void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) + { ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ v4l2_ctrl_handler_free(&av->ctrl_handler); ++#endif + vb2_video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + mutex_destroy(&av->mutex); +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.h b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.h +index e6d1da2..c36be8d 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.h ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-video.h +@@ -97,6 +97,10 @@ struct ipu7_isys_video { + unsigned int skipframe; + unsigned int start_streaming; + #endif ++#ifdef CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS ++ struct v4l2_ctrl_handler ctrl_handler; ++ unsigned int enum_link_state; /* state for link enumeration by vc */ ++#endif + }; + + #define ipu7_isys_queue_to_video(__aq) \ +-- +2.43.0 + diff --git a/patches/0105-isys-queue-ignore-HW_ERR_BAD_FRAME_DIM-led-expected-.patch b/patches/0105-isys-queue-ignore-HW_ERR_BAD_FRAME_DIM-led-expected-.patch new file mode 100644 index 00000000..101b6dd7 --- /dev/null +++ b/patches/0105-isys-queue-ignore-HW_ERR_BAD_FRAME_DIM-led-expected-.patch @@ -0,0 +1,28 @@ +From fe3d41df4632fac80ad860e4d8f4a7f23a850475 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Sun, 26 Apr 2026 09:52:35 -0700 +Subject: [PATCH 105/105] isys-queue : ignore HW_ERR_BAD_FRAME_DIM led expected + buffers noise" + +--- + ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c +index ddf7ae3..2c12a2c 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c +@@ -1138,10 +1138,6 @@ void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, + addr = ivb->dma_addr; + + if (info->pin.addr != addr) { +- if (first) +- dev_err(dev, "Unexpected buffer address %pad\n", +- &addr); +- + first = false; + continue; + } +-- +2.43.0 + diff --git a/patches/0039-ipu-bridge-port-6.12.0-to-bkc.patch b/patches/0200-ipu-bridge-port-6.12.0-to-bkc.patch similarity index 53% rename from patches/0039-ipu-bridge-port-6.12.0-to-bkc.patch rename to patches/0200-ipu-bridge-port-6.12.0-to-bkc.patch index 51c8b2da..c82f69f4 100644 --- a/patches/0039-ipu-bridge-port-6.12.0-to-bkc.patch +++ b/patches/0200-ipu-bridge-port-6.12.0-to-bkc.patch @@ -1,9 +1,17 @@ +From 6cd562af29bfa35419c6791c5cc22ef939b730d3 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:56:24 -0700 +Subject: [PATCH 200/212] ipu-bridge : port 6.12.0 to bkc + +Signed-off-by: florent pirou +--- 6.12.0/drivers/media/pci/intel/ipu-bridge.c | 2 ++ 1 file changed, 2 insertions(+) -diff --git b/6.12.0/drivers/media/pci/intel/ipu-bridge.c a/6.12.0/drivers/media/pci/intel/ipu-bridge.c + +diff --git a/6.12.0/drivers/media/pci/intel/ipu-bridge.c b/6.12.0/drivers/media/pci/intel/ipu-bridge.c index a0e9a71..4e921c7 100644 ---- b/6.12.0/drivers/media/pci/intel/ipu-bridge.c -+++ a/6.12.0/drivers/media/pci/intel/ipu-bridge.c +--- a/6.12.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/6.12.0/drivers/media/pci/intel/ipu-bridge.c @@ -59,6 +59,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), /* Omnivision OV2740 */ @@ -13,3 +21,6 @@ index a0e9a71..4e921c7 100644 /* Omnivision OV8865 */ IPU_SENSOR_CONFIG("INT347A", 1, 360000000), /* Omnivision OV7251 */ +-- +2.25.1 + diff --git a/patches/0201-ipu-bridge-6.12-makefile.patch b/patches/0201-ipu-bridge-6.12-makefile.patch new file mode 100644 index 00000000..e56c0ab2 --- /dev/null +++ b/patches/0201-ipu-bridge-6.12-makefile.patch @@ -0,0 +1,42 @@ +From dbeb7ea2b32db8ff79047bf581a2485b61e163b3 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:57:25 -0700 +Subject: [PATCH 201/212] ipu-bridge : 6.12 makefile + +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/pci/intel/Kconfig | 5 ----- + 6.12.0/drivers/media/pci/intel/Makefile | 5 +---- + 2 files changed, 1 insertion(+), 9 deletions(-) + +diff --git a/6.12.0/drivers/media/pci/intel/Kconfig b/6.12.0/drivers/media/pci/intel/Kconfig +index d9fcddc..3fa2b72 100644 +--- a/6.12.0/drivers/media/pci/intel/Kconfig ++++ b/6.12.0/drivers/media/pci/intel/Kconfig +@@ -1,9 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only +- +-source "drivers/media/pci/intel/ipu3/Kconfig" +-source "drivers/media/pci/intel/ipu6/Kconfig" +-source "drivers/media/pci/intel/ivsc/Kconfig" +- + config IPU_BRIDGE + tristate "Intel IPU Bridge" + depends on ACPI || COMPILE_TEST +diff --git a/6.12.0/drivers/media/pci/intel/Makefile b/6.12.0/drivers/media/pci/intel/Makefile +index 3a2cc65..16dc2d2 100644 +--- a/6.12.0/drivers/media/pci/intel/Makefile ++++ b/6.12.0/drivers/media/pci/intel/Makefile +@@ -2,7 +2,4 @@ + # + # Makefile for the IPU drivers + # +-obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +-obj-y += ipu3/ +-obj-y += ivsc/ +-obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ ++obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +\ No newline at end of file +-- +2.25.1 + diff --git a/patches/0041-ipu-bridge-6.12-bkc-backport.patch b/patches/0202-ipu-bridge-6.12-bkc-backport.patch similarity index 85% rename from patches/0041-ipu-bridge-6.12-bkc-backport.patch rename to patches/0202-ipu-bridge-6.12-bkc-backport.patch index cae8d352..34669231 100644 --- a/patches/0041-ipu-bridge-6.12-bkc-backport.patch +++ b/patches/0202-ipu-bridge-6.12-bkc-backport.patch @@ -1,10 +1,18 @@ - 6.12.0/drivers/media/pci/intel/ipu-bridge.c | 102 ++++++++++++++++++++++++---- - 1 file changed, 89 insertions(+), 13 deletions(-) +From 1aa28e272a8161282a486be8c280a8f804f1a70f Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:58:38 -0700 +Subject: [PATCH 202/212] ipu-bridge : 6.12 bkc backport + +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/pci/intel/ipu-bridge.c | 100 +++++++++++++++++--- + 1 file changed, 87 insertions(+), 13 deletions(-) + diff --git a/6.12.0/drivers/media/pci/intel/ipu-bridge.c b/6.12.0/drivers/media/pci/intel/ipu-bridge.c -index a0e9a71..4e4f86a 100644 +index 4e921c7..4e4f86a 100644 --- a/6.12.0/drivers/media/pci/intel/ipu-bridge.c +++ b/6.12.0/drivers/media/pci/intel/ipu-bridge.c -@@ -83,6 +85,17 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { +@@ -85,6 +85,17 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), @@ -22,7 +30,7 @@ index a0e9a71..4e4f86a 100644 }; static const struct ipu_property_names prop_names = { -@@ -313,6 +326,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) +@@ -315,6 +326,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) } sensor->link = ssdb.link; @@ -30,7 +38,7 @@ index a0e9a71..4e4f86a 100644 sensor->lanes = ssdb.lanes; sensor->mclkspeed = ssdb.mclkspeed; sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); -@@ -472,18 +486,49 @@ static void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor) +@@ -474,18 +486,49 @@ static void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor) } } @@ -85,7 +93,7 @@ index a0e9a71..4e4f86a 100644 nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( sensor->node_names.endpoint, &nodes[SWNODE_SENSOR_PORT], -@@ -664,7 +709,7 @@ static void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge) +@@ -666,7 +709,7 @@ static void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge) } static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, @@ -94,7 +102,7 @@ index a0e9a71..4e4f86a 100644 { struct fwnode_handle *fwnode, *primary; struct ipu_sensor *sensor; -@@ -691,31 +736,53 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, +@@ -693,31 +736,53 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, if (ret) goto err_put_adev; @@ -154,7 +162,7 @@ index a0e9a71..4e4f86a 100644 ret = ipu_bridge_instantiate_ivsc(sensor); if (ret) -@@ -748,7 +815,16 @@ static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) +@@ -750,7 +815,16 @@ static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) const struct ipu_sensor_config *cfg = &ipu_supported_sensors[i]; @@ -172,3 +180,6 @@ index a0e9a71..4e4f86a 100644 if (ret) goto err_unregister_sensors; } +-- +2.25.1 + diff --git a/patches/0203-ipu-bridge-6.12-add-sensors.patch b/patches/0203-ipu-bridge-6.12-add-sensors.patch new file mode 100644 index 00000000..20b8f81a --- /dev/null +++ b/patches/0203-ipu-bridge-6.12-add-sensors.patch @@ -0,0 +1,30 @@ +From 905a2805e458f1c3b3deef016fb43bcd7e80d2a0 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 05:59:32 -0700 +Subject: [PATCH 203/212] ipu-bridge : 6.12 add sensors + +Signed-off-by: florent pirou +--- + 6.12.0/drivers/media/pci/intel/ipu-bridge.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/6.12.0/drivers/media/pci/intel/ipu-bridge.c b/6.12.0/drivers/media/pci/intel/ipu-bridge.c +index 4e4f86a..66983ff 100644 +--- a/6.12.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/6.12.0/drivers/media/pci/intel/ipu-bridge.c +@@ -47,6 +47,12 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { ++ /* D3 Embedded ISX031 */ ++ IPU_SENSOR_CONFIG("INTC113C", 1, 300000000), ++ /* LI AR0830 */ ++ IPU_SENSOR_CONFIG("LIAR0830", 1, 600000000), ++ /* Innodisk AR0822 */ ++ IPU_SENSOR_CONFIG("EV8MOOM1", 1, 600000000), + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +-- +2.25.1 + diff --git a/patches/0204-ipu-bridge-6.17-makefile.patch b/patches/0204-ipu-bridge-6.17-makefile.patch new file mode 100644 index 00000000..1aea6b37 --- /dev/null +++ b/patches/0204-ipu-bridge-6.17-makefile.patch @@ -0,0 +1,42 @@ +From 9522d4dcc2932d53e2022b5c6c321fd1236512ea Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 06:08:59 -0700 +Subject: [PATCH 204/212] ipu-bridge : 6.17 makefile + +Signed-off-by: florent pirou +--- + 6.17.0/drivers/media/pci/intel/Kconfig | 5 ----- + 6.17.0/drivers/media/pci/intel/Makefile | 5 +---- + 2 files changed, 1 insertion(+), 9 deletions(-) + +diff --git a/6.17.0/drivers/media/pci/intel/Kconfig b/6.17.0/drivers/media/pci/intel/Kconfig +index d9fcddc..3fa2b72 100644 +--- a/6.17.0/drivers/media/pci/intel/Kconfig ++++ b/6.17.0/drivers/media/pci/intel/Kconfig +@@ -1,9 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only +- +-source "drivers/media/pci/intel/ipu3/Kconfig" +-source "drivers/media/pci/intel/ipu6/Kconfig" +-source "drivers/media/pci/intel/ivsc/Kconfig" +- + config IPU_BRIDGE + tristate "Intel IPU Bridge" + depends on ACPI || COMPILE_TEST +diff --git a/6.17.0/drivers/media/pci/intel/Makefile b/6.17.0/drivers/media/pci/intel/Makefile +index 3a2cc65..16dc2d2 100644 +--- a/6.17.0/drivers/media/pci/intel/Makefile ++++ b/6.17.0/drivers/media/pci/intel/Makefile +@@ -2,7 +2,4 @@ + # + # Makefile for the IPU drivers + # +-obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +-obj-y += ipu3/ +-obj-y += ivsc/ +-obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ ++obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +\ No newline at end of file +-- +2.25.1 + diff --git a/patches/0041-ipu-bridge-6.17-bkc-backport.patch b/patches/0205-ipu-bridge-6.17-bkc-backport.patch similarity index 86% rename from patches/0041-ipu-bridge-6.17-bkc-backport.patch rename to patches/0205-ipu-bridge-6.17-bkc-backport.patch index 928c14e8..13a1e748 100644 --- a/patches/0041-ipu-bridge-6.17-bkc-backport.patch +++ b/patches/0205-ipu-bridge-6.17-bkc-backport.patch @@ -1,3 +1,13 @@ +From ff6bbad886d1d48d96162382bfed79074b5d57e3 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:52:11 -0700 +Subject: [PATCH 205/212] ipu-bridge : 6.17 bkc backport + +Signed-off-by: florent pirou +--- + 6.17.0/drivers/media/pci/intel/ipu-bridge.c | 25 ++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + diff --git a/6.17.0/drivers/media/pci/intel/ipu-bridge.c b/6.17.0/drivers/media/pci/intel/ipu-bridge.c index 4e57935..3c947d1 100644 --- a/6.17.0/drivers/media/pci/intel/ipu-bridge.c @@ -71,3 +81,6 @@ index 4e57935..3c947d1 100644 } static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) +-- +2.25.1 + diff --git a/patches/0042-ipu-bridge-6.17-add-sensor.patch b/patches/0206-ipu-bridge-6.17-add-sensors.patch similarity index 50% rename from patches/0042-ipu-bridge-6.17-add-sensor.patch rename to patches/0206-ipu-bridge-6.17-add-sensors.patch index 7e7b45ba..7e5cdc54 100644 --- a/patches/0042-ipu-bridge-6.17-add-sensor.patch +++ b/patches/0206-ipu-bridge-6.17-add-sensors.patch @@ -1,19 +1,30 @@ +From b41a2e9f0aff4dfa923000d49dc05a778c37b535 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:55:49 -0700 +Subject: [PATCH 206/212] ipu-bridge : 6.17 add sensors + +Signed-off-by: florent pirou +--- 6.17.0/drivers/media/pci/intel/ipu-bridge.c | 6 ++++++ 1 file changed, 6 insertions(+) + diff --git a/6.17.0/drivers/media/pci/intel/ipu-bridge.c b/6.17.0/drivers/media/pci/intel/ipu-bridge.c -index 3c947d1..36e8f10 100644 +index 3c947d1..e9c8422 100644 --- a/6.17.0/drivers/media/pci/intel/ipu-bridge.c +++ b/6.17.0/drivers/media/pci/intel/ipu-bridge.c -@@ -97,6 +97,12 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { - IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), - /* Lontium lt6911gxd */ - IPU_SENSOR_CONFIG("INTC1124", 0), +@@ -51,6 +51,12 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* D3 Embedded ISX031 */ + IPU_SENSOR_CONFIG("INTC113C", 1, 300000000), + /* LI AR0830 */ + IPU_SENSOR_CONFIG("LIAR0830", 1, 600000000), + /* Innodisk AR0822 */ + IPU_SENSOR_CONFIG("EV8MOOM1", 1, 600000000), - }; - - static const struct ipu_property_names prop_names = { + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +-- +2.25.1 + diff --git a/patches/0207-ipu-bridge-6.18-makefile.patch b/patches/0207-ipu-bridge-6.18-makefile.patch new file mode 100644 index 00000000..8d5a70ae --- /dev/null +++ b/patches/0207-ipu-bridge-6.18-makefile.patch @@ -0,0 +1,42 @@ +From b45b649b285b1faaf6adc25e596862010c0ac6ad Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 06:09:09 -0700 +Subject: [PATCH 207/212] ipu-bridge : 6.18 makefile + +Signed-off-by: florent pirou +--- + 6.18.0/drivers/media/pci/intel/Kconfig | 5 ----- + 6.18.0/drivers/media/pci/intel/Makefile | 5 +---- + 2 files changed, 1 insertion(+), 9 deletions(-) + +diff --git a/6.18.0/drivers/media/pci/intel/Kconfig b/6.18.0/drivers/media/pci/intel/Kconfig +index d9fcddc..3fa2b72 100644 +--- a/6.18.0/drivers/media/pci/intel/Kconfig ++++ b/6.18.0/drivers/media/pci/intel/Kconfig +@@ -1,9 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only +- +-source "drivers/media/pci/intel/ipu3/Kconfig" +-source "drivers/media/pci/intel/ipu6/Kconfig" +-source "drivers/media/pci/intel/ivsc/Kconfig" +- + config IPU_BRIDGE + tristate "Intel IPU Bridge" + depends on ACPI || COMPILE_TEST +diff --git a/6.18.0/drivers/media/pci/intel/Makefile b/6.18.0/drivers/media/pci/intel/Makefile +index 3a2cc65..16dc2d2 100644 +--- a/6.18.0/drivers/media/pci/intel/Makefile ++++ b/6.18.0/drivers/media/pci/intel/Makefile +@@ -2,7 +2,4 @@ + # + # Makefile for the IPU drivers + # +-obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +-obj-y += ipu3/ +-obj-y += ivsc/ +-obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ ++obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +\ No newline at end of file +-- +2.25.1 + diff --git a/patches/0208-ipu-bridge-6.18-bkc-backport.patch b/patches/0208-ipu-bridge-6.18-bkc-backport.patch new file mode 100644 index 00000000..bc95c5e0 --- /dev/null +++ b/patches/0208-ipu-bridge-6.18-bkc-backport.patch @@ -0,0 +1,86 @@ +From 228aac68727091672dbb7fdbae1bb89b59ffe9fb Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:52:42 -0700 +Subject: [PATCH 208/212] ipu-bridge : 6.18 bkc backport + +Signed-off-by: florent pirou +--- + 6.18.0/drivers/media/pci/intel/ipu-bridge.c | 25 ++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/6.18.0/drivers/media/pci/intel/ipu-bridge.c b/6.18.0/drivers/media/pci/intel/ipu-bridge.c +index 4e57935..1c60df1 100644 +--- a/6.18.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/6.18.0/drivers/media/pci/intel/ipu-bridge.c +@@ -35,6 +35,9 @@ + */ + #define IVSC_DEV_NAME "intel_vsc" + ++#define PHY_MODE_DPHY 0 ++#define PHY_MODE_CPHY 1 ++ + /* + * Extend this array with ACPI Hardware IDs of devices known to be working + * plus the number of link-frequencies expected by their drivers, along with +@@ -92,6 +95,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), + /* Toshiba T4KA3 */ + IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), ++ /* Lontium lt6911gxd */ ++ IPU_SENSOR_CONFIG("INTC1124", 0), + }; + + static const struct ipu_property_names prop_names = { +@@ -312,6 +317,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) + + sensor->link = ssdb.link; + sensor->lanes = ssdb.lanes; ++ sensor->phyconfig = ssdb.phyconfig; + sensor->mclkspeed = ssdb.mclkspeed; + sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); + sensor->orientation = ipu_bridge_parse_orientation(adev); +@@ -330,6 +336,7 @@ static void ipu_bridge_create_fwnode_properties( + { + struct ipu_property_names *names = &sensor->prop_names; + struct software_node *nodes = sensor->swnodes; ++ u8 bus_type; + + sensor->prop_names = prop_names; + +@@ -387,9 +394,16 @@ static void ipu_bridge_create_fwnode_properties( + PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); + } + ++ if (sensor->phyconfig == PHY_MODE_DPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; ++ else if (sensor->phyconfig == PHY_MODE_CPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; ++ else ++ bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; ++ + sensor->ep_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.bus_type, +- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); ++ bus_type); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, sensor->lanes); +@@ -409,6 +423,15 @@ static void ipu_bridge_create_fwnode_properties( + sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->remote_ref); ++ ++ /* ++ * TODO: Remove the bus_type property for IPU ++ * 1. keep fwnode property list no change. ++ * 2. IPU driver needs to get bus_type from remote sensor ep. ++ */ ++ sensor->ipu_properties[2] = PROPERTY_ENTRY_U32( ++ sensor->prop_names.bus_type, ++ bus_type); + } + + static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) +-- +2.25.1 + diff --git a/patches/0209-ipu-bridge-6.18-add-sensors.patch b/patches/0209-ipu-bridge-6.18-add-sensors.patch new file mode 100644 index 00000000..bed1034f --- /dev/null +++ b/patches/0209-ipu-bridge-6.18-add-sensors.patch @@ -0,0 +1,30 @@ +From 17039446833ec3e35d220c66c1d0a2cc0bbb7638 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:56:02 -0700 +Subject: [PATCH 209/212] ipu-bridge : 6.18 add sensors + +Signed-off-by: florent pirou +--- + 6.18.0/drivers/media/pci/intel/ipu-bridge.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/6.18.0/drivers/media/pci/intel/ipu-bridge.c b/6.18.0/drivers/media/pci/intel/ipu-bridge.c +index 1c60df1..88bec73 100644 +--- a/6.18.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/6.18.0/drivers/media/pci/intel/ipu-bridge.c +@@ -51,6 +51,12 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { ++ /* D3 Embedded ISX031 */ ++ IPU_SENSOR_CONFIG("INTC113C", 1, 300000000), ++ /* LI AR0830 */ ++ IPU_SENSOR_CONFIG("LIAR0830", 1, 600000000), ++ /* Innodisk AR0822 */ ++ IPU_SENSOR_CONFIG("EV8MOOM1", 1, 600000000), + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +-- +2.25.1 + diff --git a/patches/0210-ipu-bridge-7.0-makefile.patch b/patches/0210-ipu-bridge-7.0-makefile.patch new file mode 100644 index 00000000..eb4c4fcc --- /dev/null +++ b/patches/0210-ipu-bridge-7.0-makefile.patch @@ -0,0 +1,42 @@ +From 0559982cdecf828adc47e3d6a350e230d0cf6c50 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 06:09:20 -0700 +Subject: [PATCH 210/212] ipu-bridge : 7.0 makefile + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/pci/intel/Kconfig | 5 ----- + 7.0.0/drivers/media/pci/intel/Makefile | 5 +---- + 2 files changed, 1 insertion(+), 9 deletions(-) + +diff --git a/7.0.0/drivers/media/pci/intel/Kconfig b/7.0.0/drivers/media/pci/intel/Kconfig +index 3f14ca1..99e9869 100644 +--- a/7.0.0/drivers/media/pci/intel/Kconfig ++++ b/7.0.0/drivers/media/pci/intel/Kconfig +@@ -1,9 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only +- +-source "drivers/media/pci/intel/ipu3/Kconfig" +-source "drivers/media/pci/intel/ipu6/Kconfig" +-source "drivers/media/pci/intel/ivsc/Kconfig" +- + config IPU_BRIDGE + tristate "Intel IPU Bridge" + depends on ACPI +diff --git a/7.0.0/drivers/media/pci/intel/Makefile b/7.0.0/drivers/media/pci/intel/Makefile +index 3a2cc65..16dc2d2 100644 +--- a/7.0.0/drivers/media/pci/intel/Makefile ++++ b/7.0.0/drivers/media/pci/intel/Makefile +@@ -2,7 +2,4 @@ + # + # Makefile for the IPU drivers + # +-obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +-obj-y += ipu3/ +-obj-y += ivsc/ +-obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ ++obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o +\ No newline at end of file +-- +2.25.1 + diff --git a/patches/0211-ipu-bridge-7.0-bkc-backport.patch b/patches/0211-ipu-bridge-7.0-bkc-backport.patch new file mode 100644 index 00000000..3d372cb6 --- /dev/null +++ b/patches/0211-ipu-bridge-7.0-bkc-backport.patch @@ -0,0 +1,86 @@ +From 0c6d1785c2b5fedf76cf39ec13ef556751594cfb Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:52:55 -0700 +Subject: [PATCH 211/212] ipu-bridge : 7.0 bkc backport + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/pci/intel/ipu-bridge.c | 25 +++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/7.0.0/drivers/media/pci/intel/ipu-bridge.c b/7.0.0/drivers/media/pci/intel/ipu-bridge.c +index 32cc95a..7d0614c 100644 +--- a/7.0.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/7.0.0/drivers/media/pci/intel/ipu-bridge.c +@@ -36,6 +36,9 @@ + */ + #define IVSC_DEV_NAME "intel_vsc" + ++#define PHY_MODE_DPHY 0 ++#define PHY_MODE_CPHY 1 ++ + /* + * Extend this array with ACPI Hardware IDs of devices known to be working + * plus the number of link-frequencies expected by their drivers, along with +@@ -97,6 +100,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), + /* Toshiba T4KA3 */ + IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), ++ /* Lontium lt6911gxd */ ++ IPU_SENSOR_CONFIG("INTC1124", 0), + }; + + /* +@@ -345,6 +350,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) + + sensor->link = ssdb.link; + sensor->lanes = ssdb.lanes; ++ sensor->phyconfig = ssdb.phyconfig; + sensor->mclkspeed = ssdb.mclkspeed; + sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); + sensor->orientation = ipu_bridge_parse_orientation(adev); +@@ -363,6 +369,7 @@ static void ipu_bridge_create_fwnode_properties( + { + struct ipu_property_names *names = &sensor->prop_names; + struct software_node *nodes = sensor->swnodes; ++ u8 bus_type; + + sensor->prop_names = prop_names; + +@@ -420,9 +427,16 @@ static void ipu_bridge_create_fwnode_properties( + PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); + } + ++ if (sensor->phyconfig == PHY_MODE_DPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; ++ else if (sensor->phyconfig == PHY_MODE_CPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; ++ else ++ bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; ++ + sensor->ep_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.bus_type, +- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); ++ bus_type); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, sensor->lanes); +@@ -442,6 +456,15 @@ static void ipu_bridge_create_fwnode_properties( + sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->remote_ref); ++ ++ /* ++ * TODO: Remove the bus_type property for IPU ++ * 1. keep fwnode property list no change. ++ * 2. IPU driver needs to get bus_type from remote sensor ep. ++ */ ++ sensor->ipu_properties[2] = PROPERTY_ENTRY_U32( ++ sensor->prop_names.bus_type, ++ bus_type); + } + + static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) +-- +2.25.1 + diff --git a/patches/0212-ipu-bridge-7.0-add-sensors.patch b/patches/0212-ipu-bridge-7.0-add-sensors.patch new file mode 100644 index 00000000..e4cc078a --- /dev/null +++ b/patches/0212-ipu-bridge-7.0-add-sensors.patch @@ -0,0 +1,30 @@ +From ae386d240250c8700afad129f9cf78dbccf970a0 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 28 Apr 2026 07:56:15 -0700 +Subject: [PATCH 212/212] ipu-bridge : 7.0 add sensors + +Signed-off-by: florent pirou +--- + 7.0.0/drivers/media/pci/intel/ipu-bridge.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/7.0.0/drivers/media/pci/intel/ipu-bridge.c b/7.0.0/drivers/media/pci/intel/ipu-bridge.c +index 7d0614c..21c379f 100644 +--- a/7.0.0/drivers/media/pci/intel/ipu-bridge.c ++++ b/7.0.0/drivers/media/pci/intel/ipu-bridge.c +@@ -52,6 +52,12 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { ++ /* D3 Embedded ISX031 */ ++ IPU_SENSOR_CONFIG("INTC113C", 1, 300000000), ++ /* LI AR0830 */ ++ IPU_SENSOR_CONFIG("LIAR0830", 1, 600000000), ++ /* Innodisk AR0822 */ ++ IPU_SENSOR_CONFIG("EV8MOOM1", 1, 600000000), + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +-- +2.25.1 + From 36514328b50330e1db5f32372453b846ae6068e1 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 7 May 2026 06:33:21 -0700 Subject: [PATCH 02/72] ipu7-isys: display cphy/dphy 5/4-bits vc-extension Signed-off-by: florent pirou --- ...able-cphy-dphy-5-4-bits-vc-extension.patch | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 patches/0106-ipu7-isys-enable-cphy-dphy-5-4-bits-vc-extension.patch diff --git a/patches/0106-ipu7-isys-enable-cphy-dphy-5-4-bits-vc-extension.patch b/patches/0106-ipu7-isys-enable-cphy-dphy-5-4-bits-vc-extension.patch new file mode 100644 index 00000000..228a13e8 --- /dev/null +++ b/patches/0106-ipu7-isys-enable-cphy-dphy-5-4-bits-vc-extension.patch @@ -0,0 +1,39 @@ +From 12091ca1671bba71ea11b12d353c861f6a95dc06 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Wed, 6 May 2026 08:53:02 -0700 +Subject: [PATCH 106/106] ipu7-isys: enable cphy/dphy 5/4-bits vc-extension + +VC_EXTENSION register activates extra bits for virtual channel. +The Virtual Channel Extension (VCX) field is common to both options, +but is a 2-bit field for D-PHY and a 3-bit field for C-PHY. + +Signed-off-by: florent pirou +--- + .../drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c +index cc6f57f..4badb6a 100644 +--- a/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c ++++ b/ipu7-drivers/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c +@@ -274,10 +274,15 @@ static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) + u8 lo, hi; + int ret; + +- dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", ++#ifdef CONFIG_INTEL_IPU_VC_EXT ++ dev_info(&isys->adev->auxdev.dev, "config CSI-%u with vc_ext:%u [vc_msb:%u vc_lsb:%u] dt:0x%02x\n", ++ id, vc, vc & GENMASK(4, 2), vc & GENMASK(1, 0), dt); ++#else ++ dev_info(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", + id, vc, dt); +- ++#endif + dwc_csi_write(isys, id, VC_EXTENSION, 0x0); ++ + n = find_first_zero_bit(data_ids, N_DATA_IDS); + + dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with n/data_ids:%u/%p\n", +-- +2.43.0 + From b99d65e6b386b98c6c4f6a8b4099abe46eaad676 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 12 Feb 2026 01:59:58 -0700 Subject: [PATCH 03/72] makefile : ignore lt6911* make from dpkg build Signed-off-by: florent pirou --- Makefile | 6 ------ drivers/media/i2c/Makefile | 3 --- 2 files changed, 9 deletions(-) diff --git a/Makefile b/Makefile index 8a3de41c..7b2c6fde 100644 --- a/Makefile +++ b/Makefile @@ -27,9 +27,6 @@ export CONFIG_VIDEO_AR0830=m export CONFIG_VIDEO_AR0234=m export CONFIG_VIDEO_ISX031=m export CONFIG_VIDEO_MAX9X=m -export CONFIG_VIDEO_LT6911UXE=m -export CONFIG_VIDEO_LT6911UXC=m -export CONFIG_VIDEO_LT6911GXD=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y export CONFIG_INTEL_IPU_ACPI=m @@ -43,9 +40,6 @@ subdir-ccflags-$(CONFIG_VIDEO_AR0820) += -DCONFIG_VIDEO_AR0820 subdir-ccflags-$(CONFIG_VIDEO_AR0234) += -DCONFIG_VIDEO_AR0234 -DCONFIG_V4L2_CCI_I2C subdir-ccflags-$(CONFIG_IPU_BRIDGE) += -DCONFIG_IPU_BRIDGE subdir-ccflags-$(CONFIG_INTEL_IPU_ACPI) += -DCONFIG_INTEL_IPU_ACPI -subdir-ccflags-$(CONFIG_VIDEO_LT6911UXE) += -DCONFIG_VIDEO_LT6911UXE -subdir-ccflags-$(CONFIG_VIDEO_LT6911UXC) += -DCONFIG_VIDEO_LT6911UXC -subdir-ccflags-$(CONFIG_VIDEO_LT6911GXD) += -DCONFIG_VIDEO_LT6911GXD subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6) += -DCONFIG_VIDEO_INTEL_IPU6 subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU6_ISYS_RESET # Override LINUXINCLUDE to put our include path first diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 42d33362..94833cbb 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -12,9 +12,6 @@ obj-$(CONFIG_VIDEO_AR0234) += ar0234.o obj-$(CONFIG_VIDEO_AR0820) += ar0820.o obj-$(CONFIG_VIDEO_AR0830) += ar0830.o obj-$(CONFIG_VIDEO_ISX031) += isx031.o -obj-$(CONFIG_VIDEO_LT6911UXC) += lt6911uxc.o -obj-$(CONFIG_VIDEO_LT6911UXE) += lt6911uxe.o -obj-$(CONFIG_VIDEO_LT6911GXD) += lt6911gxd.o subdir-ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" obj-$(CONFIG_VIDEO_MAX9X) += max9x/ From 938ad4e691eeb3d1f7021bb0500d573a97e42cbc Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 21 Apr 2026 22:06:22 -0700 Subject: [PATCH 04/72] Makefile: compile ipu7 fw logger support Signed-off-by: florent pirou --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7b2c6fde..34a2b4c4 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ export CONFIG_VIDEO_INTEL_IPU7=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y -subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU7 +subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU7 -DENABLE_FW_OFFLINE_LOGGER subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU6 # Build IPU7 drivers from submodule From 96bfa5a822721c82a9f87337928fb0c184978106 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 5 May 2026 11:36:25 -0700 Subject: [PATCH 05/72] makefile: add compile EXT_CTRLS Signed-off-by: florent pirou --- Makefile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 34a2b4c4..1be24779 100644 --- a/Makefile +++ b/Makefile @@ -51,9 +51,17 @@ ccflags-y := -I$(src)/include export CONFIG_VIDEO_INTEL_IPU7=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y +export CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET=y +export CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS=y +export CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS=y +export CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS=y -subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU7 -DENABLE_FW_OFFLINE_LOGGER -subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU6 +subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU7 -DENABLE_FW_OFFLINE_LOGGER -DCONFIG_DEBUG_FS +subdir-ccflags-y += -DCONFIG_VIDEO_INTEL_IPU6 -DCONFIG_DEBUG_FS +subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU6_ISYS_RESET +subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU7_ISYS_RESET +subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS) += -DCONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS +subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS) += -DCONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS # Build IPU7 drivers from submodule obj-m += ipu7-drivers/drivers/media/pci/intel/ipu7/ From 910bffc92746a6fbb3b7c7a8993d1e4a12d7c31b Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 18 Feb 2026 16:31:42 +0100 Subject: [PATCH 06/72] max9x : add csi link phy lanesmap debug messages Signed-off-by: florent pirou --- drivers/media/i2c/max9x/serdes.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c index 0bd01019..f093f9c6 100644 --- a/drivers/media/i2c/max9x/serdes.c +++ b/drivers/media/i2c/max9x/serdes.c @@ -384,6 +384,12 @@ static void *parse_serdes_pdata(struct device *dev) do { struct max9x_csi_link_pdata *csi_link = &des_pdata->csi_links[0]; + dev_dbg(dev, "%s: DESERIALIZER CSI link %u enable %s x%u-lanes map", + __func__, + csi_port, + serdes_pdata->bus_type == V4L2_MBUS_CSI2_CPHY ? "CPHY" : "DPHY", + serdes_pdata->deser_nlanes); + csi_link->link_id = csi_port; csi_link->bus_type = serdes_pdata->bus_type; csi_link->num_lanes = serdes_pdata->deser_nlanes; @@ -2419,6 +2425,15 @@ static int max9x_parse_csi_link_pdata(struct max9x_common *common, csi_link->config.initial_deskew_width = csi_link_pdata->initial_deskew_width; csi_link->config.auto_start = csi_link_pdata->auto_start; + dev_dbg(common->dev, "%s: CSI link %u (%s x%u-lanes) to des_pdata[0..3]=%u, %u, %u, %u", __func__, + csi_link_id, + csi_link->config.bus_type == V4L2_MBUS_CSI2_CPHY ? "CPHY" : "DPHY", + csi_link_pdata->num_lanes, + (&common->csi_link[0])->config.num_lanes, + (&common->csi_link[1])->config.num_lanes, + (&common->csi_link[2])->config.num_lanes, + (&common->csi_link[3])->config.num_lanes); + return 0; } From 59d20df245ee63300ad930a1636a2577e8339316 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 25 Jun 2025 00:34:00 -0700 Subject: [PATCH 07/72] ipu-acpi : add d4xx pdata aggregrated-link detection * serdes, sensor: use new naming convention : "-" Signed-off-by: florent pirou --- drivers/media/platform/intel/Makefile | 8 + drivers/media/platform/intel/ipu-acpi-pdata.c | 137 ++++++++++++++++-- drivers/media/platform/intel/ipu-acpi.c | 29 ++++ include/media/i2c/d4xx-max929x_pdata.h | 19 +++ include/media/ipu-acpi-pdata.h | 12 +- include/media/serdes-pdata.h | 3 + 6 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 include/media/i2c/d4xx-max929x_pdata.h diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile index 041fcd80..7a37e0b4 100644 --- a/drivers/media/platform/intel/Makefile +++ b/drivers/media/platform/intel/Makefile @@ -5,3 +5,11 @@ obj-$(CONFIG_INTEL_IPU_ACPI) += ipu-acpi.o \ ipu-acpi-pdata.o \ ipu-acpi-common.o + +ifeq ($(CONFIG_VIDEO_D4XX), m) + ccflags-y += -DCONFIG_VIDEO_D4XX +endif + +ifeq ($(CONFIG_VIDEO_MAX9X), m) + ccflags-y += -DCONFIG_VIDEO_MAX9X +endif diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c index f19d38b6..41bf45e8 100644 --- a/drivers/media/platform/intel/ipu-acpi-pdata.c +++ b/drivers/media/platform/intel/ipu-acpi-pdata.c @@ -20,6 +20,7 @@ #define SENSOR_2X_I2C 5 #define SUFFIX_BASE 97 #define MSG_LEN 128 +#define SUFFIX_BASE_OFFSET 5 // SERDES aggregated-link offset static struct ipu_isys_subdev_pdata *ptr_built_in_pdata; @@ -122,6 +123,9 @@ static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) pr_debug("\t\tphy_i2c_addr \t\t= 0x%x", sdinfo->phy_i2c_addr); pr_debug("\t\tser_alias \t\t= 0x%x", sdinfo->ser_alias); pr_debug("\t\tser_phys_addr \t\t= 0x%x", sdinfo->ser_phys_addr); +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + pr_debug("\t\taggregated_link \t= %u", sdinfo->aggregated_link); +#endif pr_debug("\t\tsuffix \t\t\t= %s", sdinfo->suffix); pr_debug("\t\tboard_info.type \t= %s", sdinfo->board_info.type); pr_debug("\t\tboard_info.addr \t= 0x%x", sdinfo->board_info.addr); @@ -608,13 +612,15 @@ static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, const char *hid_name, unsigned int lanes, unsigned int addr, + unsigned int port, unsigned int subdev_num) { int i; + int serdes_suffix; + unsigned int suffix_offset; struct serdes_module_pdata *module_pdata[PORT_NR]; struct serdes_subdev_info *serdes_sdinfo; size_t subdev_size = subdev_num * sizeof(*serdes_sdinfo); - unsigned int port = (*pdata)->suffix - SUFFIX_BASE; serdes_sdinfo = kzalloc(subdev_size, GFP_KERNEL); if (!serdes_sdinfo) @@ -640,8 +646,86 @@ static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; - snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + if (!strcmp(sensor_name, D457_NAME)) { + pr_debug("IPU ACPI: %s current i2c_bdf %s serdes %d-%d, prior %s", + sensor_name, + serdes_info.i2c_adapter_bdf, + serdes_info.deser_num, + subdev_num, + serdes_info.i2c_adapter_bdf_2); + pr_debug("IPU ACPI: %s current i2c_addr 0x%x serdes %d-%d, prior 0x%x", + sensor_name, + serdes_info.des_map_addr, + serdes_info.deser_num, + subdev_num, + serdes_info.des_map_addr_2); + pr_debug("IPU ACPI: %s current i2c_addr 0x%x sensor %d-%d, prior 0x%x", + sensor_name, + serdes_info.sensor_map_addr, + serdes_info.deser_num, + subdev_num, + serdes_info.sensor_map_addr_2); + /* define namespacing offset (suffix `e` -> `a-2`, suffix `e` w/ `degree`=90 -> `b-2` ), for : + * - pprunit > 1, set # of deserializer aggregated-link. + * - pprunit = 1, prior sensor_map_addr sensor_map_addr pdata already defined same deserializer, e.g same i2c-bus and i2c-addr, + * maps degree pdata > 90|180|270 to GMSL input offset + * - pprunit = 1, prior d4xx sensor_map_addr pdata already defined same deserializer, e.g same i2c-bus and i2c-addr, + * no degree pdata assumes max9296 and increment by 1 + */ + if (( subdev_num == 1 ) && (i == 0) && ((*pdata)->des_port == 0) && + ( serdes_info.sensor_map_addr == serdes_info.sensor_map_addr_2 ) && + ( !strcmp(serdes_info.i2c_adapter_bdf, serdes_info.i2c_adapter_bdf_2))) { + serdes_sdinfo[i].aggregated_link = 1; + (*pdata)->des_port = 1; + serdes_suffix = (*pdata)->suffix + SUFFIX_BASE_OFFSET + 1 + (*pdata)->des_port; + pr_info("IPU ACPI: Increment %s %c namespacing on aggregated-link sensors %d", + sensor_name, + serdes_suffix, + serdes_info.deser_num); + } else if (( subdev_num == 1 ) && (i == 0) && + ( serdes_info.sensor_map_addr == serdes_info.sensor_map_addr_2 ) && + ( !strcmp(serdes_info.i2c_adapter_bdf, serdes_info.i2c_adapter_bdf_2))) { + serdes_sdinfo[i].aggregated_link = 1; + serdes_suffix = (*pdata)->suffix + SUFFIX_BASE_OFFSET + 1 + (*pdata)->des_port; + pr_info("IPU ACPI: Use degree=%u pdata in %s %c namespacing on aggregated-link sensors %d", + (*pdata)->des_port, + sensor_name, + serdes_suffix, + serdes_info.deser_num); + } else if ((*pdata)->des_port > 0) { + /* - degree == 90|180|270, offset deserializer source link */ + pr_info("IPU ACPI: Use degree=%u pdata in %s %c namespacing", + (*pdata)->des_port, + sensor_name, + serdes_suffix); + serdes_sdinfo[i].aggregated_link = 0; + serdes_suffix = (*pdata)->suffix + SUFFIX_BASE_OFFSET + (*pdata)->des_port; + } else { + serdes_sdinfo[i].aggregated_link = 0; + serdes_suffix = (*pdata)->suffix; + } + snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", + SUFFIX_BASE + i + (*pdata)->des_port , port); + } else { + if (i >= 1) { + serdes_sdinfo[i].aggregated_link = i; + serdes_suffix = (*pdata)->suffix + i; + pr_info("IPU ACPI: Add %s %cnamespacing on aggregated-link sensors %d", + sensor_name, + serdes_suffix, + serdes_info.deser_num); + } else { + serdes_sdinfo[i].aggregated_link = 0; + serdes_suffix = (*pdata)->suffix; + } + snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", + SUFFIX_BASE + i, port); + } +#else + snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", SUFFIX_BASE + i, port); +#endif serdes_sdinfo[i].ser_phys_addr = serdes_info.ser_phys_addr; memcpy(serdes_sdinfo[i].ser_gpio, serdes_info.ser_gpio, sizeof(struct gpiod_lookup) * MAX_SER_GPIO_NUM); @@ -654,7 +738,7 @@ static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, return 0; } -static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, +int set_pdata(struct ipu_isys_subdev_info **sensor_sd, struct device *dev, const char *sensor_name, const char *hid_name, @@ -667,7 +751,7 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, bool is_dummy, enum connection_type connect, int link_freq, - int des_port) + unsigned int degree) { if (connect == TYPE_DIRECT) { struct sensor_platform_data *pdata; @@ -696,6 +780,7 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, (*sensor_sd)->i2c.board_info.platform_data = pdata; } else if (connect == TYPE_SERDES) { struct serdes_platform_data *pdata; + unsigned int des_port = (degree == 14) ? 270 : degree; pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -704,6 +789,13 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, pr_debug("IPU ACPI: %s - Serdes connection", __func__); /* use ascii */ if (port >= 0) { +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + if ((!strcmp(sensor_name, D457_NAME)) && + (degree == 90 || + degree == 180 || + degree == 14)) // 14 is equal to 270 (e.g. 255 byte overflow + 15) + des_port = (degree == 14) ? 3 :(degree / 90); +#endif pdata->suffix = port + SUFFIX_BASE; pr_info("IPU ACPI: create %s on mipi port %d", sensor_name, port); @@ -716,7 +808,7 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, pdata->ser_nlanes = lanes; pdata->des_port = des_port; strscpy(pdata->ser_name, (*sensor_sd)->i2c.board_info.type, I2C_NAME_SIZE); - set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, subdev_num); + set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, port, subdev_num); (*sensor_sd)->i2c.board_info.platform_data = pdata; pdata->deser_board_info = &(*sensor_sd)->i2c.board_info; @@ -725,7 +817,7 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, return 0; } -static void set_serdes_info(struct device *dev, const char *sensor_name, +void set_serdes_info(struct device *dev, const char *sensor_name, const char *serdes_name, struct sensor_bios_data *cam_data, int sensor_physical_addr, @@ -750,6 +842,24 @@ static void set_serdes_info(struct device *dev, const char *sensor_name, /* sensor mapped addr */ serdes_info.sensor_map_addr = cam_data->i2c[i++].addr; +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + /* serializer mapped addr */ + if ( serdes_info.deser_num > 0 ) + serdes_info.ser_map_addr_2 = serdes_info.ser_map_addr; + serdes_info.ser_map_addr = cam_data->i2c[cam_data->i2c_num - 2].addr; + + /* deserializer mapped addr */ + if ( serdes_info.deser_num > 0 ) + serdes_info.des_map_addr_2 = serdes_info.des_map_addr; + serdes_info.des_map_addr = cam_data->i2c[cam_data->i2c_num - 3].addr; + + /* serdes mapped i2c-adapter */ + if ( serdes_info.deser_num > 0 ) + strscpy(&serdes_info.i2c_adapter_bdf_2[0], serdes_info.i2c_adapter_bdf, + sizeof(serdes_info.i2c_adapter_bdf_2)); + strscpy(&serdes_info.i2c_adapter_bdf[0], cam_data->i2c[cam_data->i2c_num - 1].bdf, + sizeof(serdes_info.i2c_adapter_bdf)); +#endif serdes_info.gpio_powerup_seq = 0; serdes_info.phy_i2c_addr = sensor_physical_addr; @@ -766,7 +876,7 @@ static void set_serdes_info(struct device *dev, const char *sensor_name, serdes_info.sensor_dt = sensor_dt; } -static int populate_sensor_pdata(struct device *dev, +int populate_sensor_pdata(struct device *dev, struct ipu_isys_subdev_info **sensor_sd, struct sensor_bios_data *cam_data, struct control_logic_data *ctl_data, @@ -834,16 +944,18 @@ static int populate_sensor_pdata(struct device *dev, } /* Use last I2C device */ - ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, cam_data->link, - cam_data->lanes, cam_data->i2c[cam_data->i2c_num - 1].addr, - cam_data->pprunit, cam_data->pprval, false, connect, link_freq, cam_data->degree); + ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, + cam_data->link, + cam_data->lanes, + cam_data->i2c[cam_data->i2c_num - 1].addr, + cam_data->pprunit, + cam_data->pprval, + false, connect, link_freq, cam_data->degree); if (ret) return ret; update_pdata(dev, *sensor_sd, connect); - /* Lontium specific */ - return 0; } @@ -922,3 +1034,4 @@ EXPORT_SYMBOL(get_sensor_pdata); MODULE_AUTHOR("Khai Wen, Ng "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("IPU ACPI support"); +MODULE_VERSION(DRIVER_VERSION_SUFFIX); diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c index a32b378f..d29fff2c 100644 --- a/drivers/media/platform/intel/ipu-acpi.c +++ b/drivers/media/platform/intel/ipu-acpi.c @@ -53,6 +53,11 @@ #include #endif +#if IS_ENABLED(CONFIG_VIDEO_D4XX) +#include +#include +#endif + static LIST_HEAD(devices); static struct ipu_camera_module_data *add_device_to_list( @@ -244,6 +249,27 @@ static const struct ipu_acpi_devices supported_devices[] = { },// AR0234 HID #endif #endif +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + .hid_name = "INTC10CD", + .real_driver = D457_NAME, + .get_platform_data = get_sensor_pdata, + .priv_data = NULL, + .priv_size = 0, + .connect = TYPE_SERDES, + .serdes_name = D457_NAME, + .sensor_physical_addr = D457_I2C_ADDRESS, + .link_freq = 1600, + .ser_physical_addr = 0x40, + .ser_gpio = { + { + .chip_hwnum = 0, + .con_id = "reset", + .flags = GPIO_ACTIVE_LOW, + }, + }, + .sensor_dt = MIPI_CSI2_TYPE_YUV422_8, + },// D457 HID +#endif }; static int get_table_index(const char *acpi_name) @@ -277,6 +303,9 @@ static const struct acpi_device_id ipu_acpi_match[] = { #endif #if IS_ENABLED(CONFIG_VIDEO_AR0234) { "INTC0234", 0 }, +#endif +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + { "INTC10CD", 0 }, // D457 HID #endif {}, }; diff --git a/include/media/i2c/d4xx-max929x_pdata.h b/include/media/i2c/d4xx-max929x_pdata.h new file mode 100644 index 00000000..89a86641 --- /dev/null +++ b/include/media/i2c/d4xx-max929x_pdata.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2024 Intel Corporation */ + +#ifndef MAX929X_H +#define MAX929X_H + +#define MAX929X_NAME "max929x" +#define MAX9295_I2C_ADDRESS 0x40 + +/* set this flag if this module needs serializer initialization */ +#define MAX9295_FL_INIT_SER BIT(0) +/* set this flag if this module has extra powerup sequence */ +#define MAX9295_FL_POWERUP BIT(1) +/* set this flag if this module needs reset signal */ +#define MAX9295_FL_RESET BIT(2) +/* set this flag if it need to init serial clk only */ +#define MAX9295_FL_INIT_SER_CLK BIT(4) + +#endif diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h index 53ce05a4..cc19ba63 100644 --- a/include/media/ipu-acpi-pdata.h +++ b/include/media/ipu-acpi-pdata.h @@ -106,6 +106,16 @@ struct serdes_local { /* sensor data type */ unsigned int sensor_dt; + +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + /* last deserializer mapped addr */ + unsigned short des_map_addr; + unsigned short des_map_addr_2; + + /* last mapped i2c_adapter */ + char i2c_adapter_bdf[32]; + char i2c_adapter_bdf_2[32]; +#endif }; -#endif \ No newline at end of file +#endif diff --git a/include/media/serdes-pdata.h b/include/media/serdes-pdata.h index 4f6d5fb0..ca321b00 100644 --- a/include/media/serdes-pdata.h +++ b/include/media/serdes-pdata.h @@ -16,6 +16,9 @@ struct serdes_subdev_info { unsigned short ser_phys_addr; unsigned int sensor_dt; struct gpiod_lookup ser_gpio[MAX_SER_GPIO_NUM]; +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + unsigned short aggregated_link; +#endif }; struct serdes_platform_data { From 3104b5b42330a915aae6c51474096cd8ec51037a Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 25 Mar 2026 19:12:24 -0700 Subject: [PATCH 08/72] ipu-acpi: fallback for camera pdata without rotation param ``` [ 45.223993] IPU ACPI: d4xx current i2c_bdf 0000:00:15.1 serdes 0-1, prior 0000:00:15.1 [ 45.228600] IPU ACPI: d4xx current i2c_addr 0x14 serdes 0-1, prior 0x12 [ 45.228602] IPU ACPI: d4xx current i2c_addr 0x48 sensor 0-1, prior 0x48 [ 45.228603] IPU ACPI: Increment d4xx namespacing on aggregated-link sensors 0 ``` * ipu-acpi: fallback for camera pdata with wrong d4xx pprval and pprnum entries Signed-off-by: florent pirou --- drivers/media/platform/intel/ipu-acpi-pdata.c | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c index 41bf45e8..ab366e4b 100644 --- a/drivers/media/platform/intel/ipu-acpi-pdata.c +++ b/drivers/media/platform/intel/ipu-acpi-pdata.c @@ -803,6 +803,17 @@ int set_pdata(struct ipu_isys_subdev_info **sensor_sd, pr_err("IPU ACPI: Invalid MIPI Port : %d", port); pdata->link_freq_mbps = link_freq; +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + if ((!strcmp(sensor_name, D457_NAME)) && + (lanes != 2 || + deser_lanes == 0 || + subdev_num != 1)) { + //fallback from PDATA misconfiguration corner-cases that leads d4xx segfault + deser_lanes = 2; + lanes = 2; + subdev_num = 1; + } +#endif pdata->bus_type = (*sensor_sd)->csi2->bus_type; pdata->deser_nlanes = deser_lanes; pdata->ser_nlanes = lanes; @@ -835,28 +846,31 @@ void set_serdes_info(struct device *dev, const char *sensor_name, serdes_info.i2c_num = cam_data->i2c_num; i = 1; + +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + /* prior serializer mapped addr */ + serdes_info.ser_map_addr_2 = serdes_info.ser_map_addr; +#endif /* serializer mapped addr */ serdes_info.ser_map_addr = cam_data->i2c[i++].addr; /* serializer physical addr */ serdes_info.ser_phys_addr = ser_physical_addr; + +#if IS_ENABLED(CONFIG_VIDEO_D4XX) + /* prior serializer mapped addr */ + serdes_info.sensor_map_addr_2 = serdes_info.sensor_map_addr; +#endif /* sensor mapped addr */ serdes_info.sensor_map_addr = cam_data->i2c[i++].addr; #if IS_ENABLED(CONFIG_VIDEO_D4XX) - /* serializer mapped addr */ - if ( serdes_info.deser_num > 0 ) - serdes_info.ser_map_addr_2 = serdes_info.ser_map_addr; - serdes_info.ser_map_addr = cam_data->i2c[cam_data->i2c_num - 2].addr; - /* deserializer mapped addr */ - if ( serdes_info.deser_num > 0 ) - serdes_info.des_map_addr_2 = serdes_info.des_map_addr; + serdes_info.des_map_addr_2 = serdes_info.des_map_addr; serdes_info.des_map_addr = cam_data->i2c[cam_data->i2c_num - 3].addr; /* serdes mapped i2c-adapter */ - if ( serdes_info.deser_num > 0 ) - strscpy(&serdes_info.i2c_adapter_bdf_2[0], serdes_info.i2c_adapter_bdf, - sizeof(serdes_info.i2c_adapter_bdf_2)); + strscpy(&serdes_info.i2c_adapter_bdf_2[0], serdes_info.i2c_adapter_bdf, + sizeof(serdes_info.i2c_adapter_bdf_2)); strscpy(&serdes_info.i2c_adapter_bdf[0], cam_data->i2c[cam_data->i2c_num - 1].bdf, sizeof(serdes_info.i2c_adapter_bdf)); #endif From d4d4c6d9649654eaf693cf033a18fb873a3624fd Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 5 May 2026 04:27:06 -0700 Subject: [PATCH 09/72] ipu-acpi: fix d4xx PDATA missing headers and typos Signed-off-by: florent pirou --- drivers/media/platform/intel/ipu-acpi.c | 1 + include/media/ipu-acpi-pdata.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c index d29fff2c..f5251df3 100644 --- a/drivers/media/platform/intel/ipu-acpi.c +++ b/drivers/media/platform/intel/ipu-acpi.c @@ -250,6 +250,7 @@ static const struct ipu_acpi_devices supported_devices[] = { #endif #endif #if IS_ENABLED(CONFIG_VIDEO_D4XX) + { .hid_name = "INTC10CD", .real_driver = D457_NAME, .get_platform_data = get_sensor_pdata, diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h index cc19ba63..368d038a 100644 --- a/include/media/ipu-acpi-pdata.h +++ b/include/media/ipu-acpi-pdata.h @@ -8,6 +8,10 @@ #include #include +#if IS_ENABLED(CONFIG_VIDEO_D4XX) +#include +#endif + #define CL_EMPTY 0 #define CL_DISCRETE 1 #define CL_LT 5 From 369da4e2d6eddd22555e3871cd778178c71d93af Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 26 Aug 2025 13:25:23 +0000 Subject: [PATCH 10/72] d4xx-compat: initial d457 gmsl2 1.0.1.21 driver port * maintain intel-ipu6-dkms (20250703+iotgipu6-0eci1) backward compat * d4xx-compat: maitain backward compatible d4xx Signed-off-by: florent pirou --- Makefile | 2 + drivers/media/i2c/Makefile | 3 + drivers/media/i2c/d4xx-compat/Makefile | 9 + drivers/media/i2c/d4xx-compat/d4xx.c | 5526 ++++++++++++++++++++++++ include/media/i2c/d4xx_pdata.h | 32 + 5 files changed, 5572 insertions(+) create mode 100644 drivers/media/i2c/d4xx-compat/Makefile create mode 100644 drivers/media/i2c/d4xx-compat/d4xx.c create mode 100644 include/media/i2c/d4xx_pdata.h diff --git a/Makefile b/Makefile index 1be24779..25e60983 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,14 @@ export CONFIG_VIDEO_MAX9X=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y export CONFIG_INTEL_IPU_ACPI=m +export CONFIG_VIDEO_D4XX_COMPAT=m # Define config macros for conditional compilation in ipu-acpi.c # IS_ENABLED() checks for CONFIG_XXX or CONFIG_XXX_MODULE subdir-ccflags-$(CONFIG_VIDEO_MAX9X) += -DCONFIG_VIDEO_MAX9X subdir-ccflags-$(CONFIG_VIDEO_ISX031) += -DCONFIG_VIDEO_ISX031 subdir-ccflags-$(CONFIG_VIDEO_AR0233) += -DCONFIG_VIDEO_AR0233 +subdir-ccflags-$(CONFIG_VIDEO_D4XX_COMPAT) += -DCONFIG_VIDEO_D4XX subdir-ccflags-$(CONFIG_VIDEO_AR0820) += -DCONFIG_VIDEO_AR0820 subdir-ccflags-$(CONFIG_VIDEO_AR0234) += -DCONFIG_VIDEO_AR0234 -DCONFIG_V4L2_CCI_I2C subdir-ccflags-$(CONFIG_IPU_BRIDGE) += -DCONFIG_IPU_BRIDGE diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 94833cbb..8a038a22 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -14,4 +14,7 @@ obj-$(CONFIG_VIDEO_AR0830) += ar0830.o obj-$(CONFIG_VIDEO_ISX031) += isx031.o subdir-ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" + obj-$(CONFIG_VIDEO_MAX9X) += max9x/ + +obj-$(CONFIG_VIDEO_D4XX_COMPAT) += d4xx-compat/ diff --git a/drivers/media/i2c/d4xx-compat/Makefile b/drivers/media/i2c/d4xx-compat/Makefile new file mode 100644 index 00000000..3554abfb --- /dev/null +++ b/drivers/media/i2c/d4xx-compat/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2025 Intel Corporation. + +KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | cut -d- -f1 | sed -r 's/([0-9]+\.[0-9]+).*/\1.0/g') + +ccflags-y += -I$(src)/../../../$(KERNEL_VERSION)/include-overrides +ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES -DCONFIG_SYSFS -DPLATFORM_AXIOMTEK + +obj-$(CONFIG_VIDEO_D4XX_COMPAT) += d4xx.o diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c new file mode 100644 index 00000000..f61cd88e --- /dev/null +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -0,0 +1,5526 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ds5.c - Intel(R) RealSense(TM) D4XX camera driver + * + * Copyright (c) 2017-2024, INTEL CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +//#define DS5_DRIVER_NAME "DS5 RealSense camera driver" +#define DS5_DRIVER_NAME "d4xx" +#define DS5_DRIVER_NAME_AWG "d4xx-awg" +#define DS5_DRIVER_NAME_ASR "d4xx-asr" +#define DS5_DRIVER_NAME_CLASS "d4xx-class" +#define DS5_DRIVER_NAME_DFU "d4xx-dfu" + +#define DS5_MIPI_SUPPORT_LINES 0x0300 +#define DS5_MIPI_SUPPORT_PHY 0x0304 +#define DS5_MIPI_DATARATE_MIN 0x0308 +#define DS5_MIPI_DATARATE_MAX 0x030A +#define DS5_FW_VERSION 0x030C +#define DS5_FW_BUILD 0x030E +#define DS5_DEVICE_TYPE 0x0310 +#define DS5_DEVICE_TYPE_D45X 6 +#define DS5_DEVICE_TYPE_D43X 5 +#define DS5_DEVICE_TYPE_D46X 4 + +#define DS5_MIPI_LANE_NUMS 0x0400 +#define DS5_MIPI_LANE_DATARATE 0x0402 +#define DS5_MIPI_CONF_STATUS 0x0500 + +#define DS5_START_STOP_STREAM 0x1000 +#define DS5_DEPTH_STREAM_STATUS 0x1004 +#define DS5_RGB_STREAM_STATUS 0x1008 +#define DS5_IMU_STREAM_STATUS 0x100C +#define DS5_IR_STREAM_STATUS 0x1014 + +#define DS5_STREAM_DEPTH 0x0 +#define DS5_STREAM_RGB 0x1 +#define DS5_STREAM_IMU 0x2 +#define DS5_STREAM_IR 0x4 +#define DS5_STREAM_STOP 0x100 +#define DS5_STREAM_START 0x200 +#define DS5_STREAM_IDLE 0x1 +#define DS5_STREAM_STREAMING 0x2 + +#define DS5_DEPTH_STREAM_DT 0x4000 +#define DS5_DEPTH_STREAM_MD 0x4002 +#define DS5_DEPTH_RES_WIDTH 0x4004 +#define DS5_DEPTH_RES_HEIGHT 0x4008 +#define DS5_DEPTH_FPS 0x400C +#define DS5_DEPTH_OVERRIDE 0x401C + +#define DS5_RGB_STREAM_DT 0x4020 +#define DS5_RGB_STREAM_MD 0x4022 +#define DS5_RGB_RES_WIDTH 0x4024 +#define DS5_RGB_RES_HEIGHT 0x4028 +#define DS5_RGB_FPS 0x402C + +#define DS5_IMU_STREAM_DT 0x4040 +#define DS5_IMU_STREAM_MD 0x4042 +#define DS5_IMU_RES_WIDTH 0x4044 +#define DS5_IMU_RES_HEIGHT 0x4048 +#define DS5_IMU_FPS 0x404C + +#define DS5_IR_STREAM_DT 0x4080 +#define DS5_IR_STREAM_MD 0x4082 +#define DS5_IR_RES_WIDTH 0x4084 +#define DS5_IR_RES_HEIGHT 0x4088 +#define DS5_IR_FPS 0x408C +#define DS5_IR_OVERRIDE 0x409C + +#define DS5_DEPTH_CONTROL_BASE 0x4100 +#define DS5_RGB_CONTROL_BASE 0x4200 +#define DS5_MANUAL_EXPOSURE_LSB 0x0000 +#define DS5_MANUAL_EXPOSURE_MSB 0x0002 +#define DS5_MANUAL_GAIN 0x0004 +#define DS5_LASER_POWER 0x0008 +#define DS5_AUTO_EXPOSURE_MODE 0x000C +#define DS5_EXPOSURE_ROI_TOP 0x0010 +#define DS5_EXPOSURE_ROI_LEFT 0x0014 +#define DS5_EXPOSURE_ROI_BOTTOM 0x0018 +#define DS5_EXPOSURE_ROI_RIGHT 0x001C +#define DS5_MANUAL_LASER_POWER 0x0024 +#define DS5_PWM_FREQUENCY 0x0028 + +#define DS5_DEPTH_CONFIG_STATUS 0x4800 +#define DS5_RGB_CONFIG_STATUS 0x4802 +#define DS5_IMU_CONFIG_STATUS 0x4804 +#define DS5_IR_CONFIG_STATUS 0x4808 + +#define DS5_STATUS_STREAMING 0x1 +#define DS5_STATUS_INVALID_DT 0x2 +#define DS5_STATUS_INVALID_RES 0x4 +#define DS5_STATUS_INVALID_FPS 0x8 + +#define MIPI_LANE_RATE 1000 + +#define MAX_DEPTH_EXP 200000 +#define MAX_RGB_EXP 10000 +#define DEF_DEPTH_EXP 33000 +#define DEF_RGB_EXP 1660 + +/* Currently both depth and IR use VC 0 */ +#define DS5_DEPTH_VCHAN_N 0 +#define DS5_MOTION_T_VCHAN_N 0 +//#define DS5_DEBUG_VCHAN_N 1 +//#define DS5_MOTION_T_VCHAN_N 2 + +#define MAX9295_REG0 0x0000 +#define MAX9295_I2C_4 0x0044 +#define MAX9295_I2C_5 0x0045 + +#define MAX9296_CTRL0 0x0010 +#define RESET_LINK (0x1 << 6) +#define RESET_ONESHOT (0x1 << 5) +#define AUTO_LINK (0x1 << 4) +#define DUAL_LINK (0x0) +#define LINK_A (0x1) +#define LINK_B (0x2) +#define SPLITTER (0x3) +#define MAX9296_NUM (4) + +#define MAX9295_I2C_ADDR_DEF 0x40 +#define D457_I2C_ADDR 0x10 + +enum ds5_mux_pad { + DS5_MUX_PAD_EXTERNAL, + DS5_MUX_PAD_DEPTH_A, + DS5_MUX_PAD_RGB_A, + DS5_MUX_PAD_MOTION_T_A, + DS5_MUX_PAD_IMU_A, + DS5_MUX_PAD_DEPTH_B, + DS5_MUX_PAD_RGB_B, + DS5_MUX_PAD_MOTION_T_B, + DS5_MUX_PAD_IMU_B, + DS5_MUX_PAD_COUNT, +}; + +enum ds5_sid { + DS5_SID_MUX_A = -1, + DS5_SID_DEPTH_A, + DS5_SID_RGB_A, + DS5_SID_MOTION_T_A, + DS5_SID_IMU_A, + DS5_SID_DEPTH_B, + DS5_SID_RGB_B, +}; + +#define DS5_N_CONTROLS 8 + +#define CSI2_MAX_VIRTUAL_CHANNELS 4 + +#define DFU_WAIT_RET_LEN 6 + +#define DS5_START_POLL_TIME 10 +#define DS5_START_MAX_TIME 1000 +#define DS5_START_MAX_COUNT (DS5_START_MAX_TIME / DS5_START_POLL_TIME) + +#define MAX_D457_COUNT 5 +#define D457_MAX_SUBSTREAM 10 + +/* DFU definition section */ +#define DFU_MAGIC_NUMBER "/0x01/0x02/0x03/0x04" +#define DFU_BLOCK_SIZE 1024 +#define ds5_read_with_check(state, addr, val) {\ + if (ds5_read(state, addr, val)) \ + return -EINVAL; } +#define ds5_raw_read_with_check(state, addr, buf, size) {\ + if (ds5_raw_read(state, addr, buf, size)) \ + return -EINVAL; } +#define ds5_write_with_check(state, addr, val) {\ + if (ds5_write(state, addr, val)) \ + return -EINVAL; } +#define ds5_raw_write_with_check(state, addr, buf, size) {\ + if (ds5_raw_write(state, addr, buf, size)) \ + return -EINVAL; } +#define max9296_write_8_with_check(state, addr, buf) {\ + if (max9296_write_8(state, addr, buf)) \ + return -EINVAL; \ + } +#define max9295_write_8_with_check(state, addr, buf) {\ + if (max9295_write_8(state, addr, buf)) \ + return -EINVAL; \ + } +#define D4XX_LINK_FREQ_360MHZ 360000000ULL +#define D4XX_LINK_FREQ_300MHZ 300000000ULL +#define D4XX_LINK_FREQ_288MHZ 288000000ULL +#define D4XX_LINK_FREQ_240MHZ 240000000ULL +#define D4XX_LINK_FREQ_225MHZ 22500000ULL + +enum dfu_fw_state { + appIDLE = 0x0000, + appDETACH = 0x0001, + dfuIDLE = 0x0002, + dfuDNLOAD_SYNC = 0x0003, + dfuDNBUSY = 0x0004, + dfuDNLOAD_IDLE = 0x0005, + dfuMANIFEST_SYNC = 0x0006, + dfuMANIFEST = 0x0007, + dfuMANIFEST_WAIT_RESET = 0x0008, + dfuUPLOAD_IDLE = 0x0009, + dfuERROR = 0x000a +}; + +enum dfu_state { + DS5_DFU_IDLE = 0, + DS5_DFU_RECOVERY, + DS5_DFU_OPEN, + DS5_DFU_IN_PROGRESS, + DS5_DFU_DONE, + DS5_DFU_ERROR +} dfu_state_t; + +struct hwm_cmd { + u16 header; + u16 magic_word; + u32 opcode; + u32 param1; + u32 param2; + u32 param3; + u32 param4; + unsigned char Data[]; +}; + +static const struct hwm_cmd cmd_switch_to_dfu = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x1e, + .param1 = 0x01, +}; + +enum table_id { + COEF_CALIBRATION_ID = 0x19, + DEPTH_CALIBRATION_ID = 0x1f, + RGB_CALIBRATION_ID = 0x20, + IMU_CALIBRATION_ID = 0x22 +} table_id_t; + +static const struct hwm_cmd get_calib_data = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x15, + .param1 = 0x00, //table_id +}; + +static const struct hwm_cmd set_calib_data = { + .header = 0x0114, + .magic_word = 0xCDAB, + .opcode = 0x62, + .param1 = 0x00, //table_id + .param2 = 0x02, //region +}; + +static const struct hwm_cmd gvd = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x10, +}; + +static const struct hwm_cmd set_ae_roi = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x44, +}; + +static const struct hwm_cmd get_ae_roi = { + .header = 0x014, + .magic_word = 0xCDAB, + .opcode = 0x45, +}; + +static const struct hwm_cmd set_ae_setpoint = { + .header = 0x18, + .magic_word = 0xCDAB, + .opcode = 0x2B, + .param1 = 0xa, // AE control +}; + +static const struct hwm_cmd get_ae_setpoint = { + .header = 0x014, + .magic_word = 0xCDAB, + .opcode = 0x2C, + .param1 = 0xa, // AE control + .param2 = 0, // get current +}; + +static const struct hwm_cmd erb = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x17, +}; + +static const struct hwm_cmd ewb = { + .header = 0x14, + .magic_word = 0xCDAB, + .opcode = 0x18, +}; + +static const s64 link_freq_menu_items[] = { + D4XX_LINK_FREQ_360MHZ, + D4XX_LINK_FREQ_300MHZ, + D4XX_LINK_FREQ_288MHZ, + D4XX_LINK_FREQ_240MHZ, + D4XX_LINK_FREQ_225MHZ, +}; + +struct __fw_status { + uint32_t spare1; + uint32_t FW_lastVersion; + uint32_t FW_highestVersion; + uint16_t FW_DownloadStatus; + uint16_t DFU_isLocked; + uint16_t DFU_version; + uint8_t ivcamSerialNum[8]; + uint8_t spare2[42]; +}; + +/*************************/ + +struct ds5_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl_handler handler_depth; + struct v4l2_ctrl_handler handler_depth_b; + struct v4l2_ctrl_handler handler_rgb; + struct v4l2_ctrl_handler handler_rgb_b; + struct v4l2_ctrl_handler handler_y8; + struct v4l2_ctrl_handler handler_imu; + struct { + struct v4l2_ctrl *log; + struct v4l2_ctrl *fw_version; + struct v4l2_ctrl *gvd; + struct v4l2_ctrl *get_depth_calib; + struct v4l2_ctrl *set_depth_calib; + struct v4l2_ctrl *get_coeff_calib; + struct v4l2_ctrl *set_coeff_calib; + struct v4l2_ctrl *ae_roi_get; + struct v4l2_ctrl *ae_roi_set; + struct v4l2_ctrl *ae_setpoint_get; + struct v4l2_ctrl *ae_setpoint_set; + struct v4l2_ctrl *erb; + struct v4l2_ctrl *ewb; + struct v4l2_ctrl *hwmc; + struct v4l2_ctrl *laser_power; + struct v4l2_ctrl *manual_laser_power; + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + /* in DS5 manual gain only works with manual exposure */ + struct v4l2_ctrl *gain; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *query_sub_stream; + struct v4l2_ctrl *set_sub_stream; + }; +}; + +struct ds5_resolution { + u16 width; + u16 height; + u8 n_framerates; + const u16 *framerates; +}; + +struct ds5_format { + unsigned int n_resolutions; + const struct ds5_resolution *resolutions; + u32 mbus_code; + u8 data_type; +}; + +struct ds5_sensor { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + u16 mux_pad; + struct { + const struct ds5_format *format; + const struct ds5_resolution *resolution; + u16 framerate; + } config; + bool streaming; + /*struct ds5_vchan *vchan;*/ + const struct ds5_format *formats; + unsigned int n_formats; +}; + +struct ds5_des { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt format; + u16 mux_pad; +}; + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#include +#define ds5_mux_subdev camera_common_data +#else +struct ds5_mux_subdev { + struct v4l2_subdev subdev; +}; +#endif + +struct ds5_variant { + const struct ds5_format *formats; + unsigned int n_formats; +}; + +struct ds5_dfu_dev { + struct cdev ds5_cdev; + struct class *ds5_class; + int device_open_count; + enum dfu_state dfu_state_flag; + unsigned char *dfu_msg; + u16 msg_write_once; + unsigned char init_v4l_f; +}; + +enum { + DS5_DS5U, + DS5_ASR, + DS5_AWG, +}; + +#define NR_OF_DS5_PADS 7 +#define NR_OF_DS5_STREAMS (NR_OF_DS5_PADS - 1) + +struct v4l2_mbus_framefmt ds5_ffmts[NR_OF_DS5_PADS]; + +struct ds5 { + struct { + struct ds5_sensor sensor; + } depth; + struct { + struct ds5_sensor sensor; + } depth_b; + struct { + struct ds5_sensor sensor; + } motion_t; + struct { + struct ds5_sensor sensor; + } rgb; + struct { + struct ds5_sensor sensor; + } rgb_b; + struct { + struct ds5_sensor sensor; + } imu; + struct { + struct ds5_des des; + } max9296; + struct { + struct ds5_mux_subdev sd; + struct media_pad pads[DS5_MUX_PAD_COUNT]; + struct ds5_sensor *last_set; + } mux; + struct ds5_ctrls ctrls; + struct ds5_dfu_dev dfu_dev; + bool power; + struct i2c_client *client; + /*struct ds5_vchan virtual_channels[CSI2_MAX_VIRTUAL_CHANNELS];*/ + /* All below pointers are used for writing, cannot be const */ + struct mutex lock; + struct regmap *regmap; + struct regmap *regmap_max9296; + struct regmap *regmap_max9295; + struct regulator *vcc; + const struct ds5_variant *variant; + int is_depth; + int is_depth_b; + int is_y8; + int is_rgb; + int is_rgb_b; + int is_imu; + u16 fw_version; + u16 fw_build; +}; + +struct ds5_counters { + unsigned int n_res; + unsigned int n_fmt; + unsigned int n_ctrl; +}; + +#define ds5_from_depth_sd(sd) container_of(sd, struct ds5, depth.sd) +#define ds5_from_motion_t_sd(sd) container_of(sd, struct ds5, motion_t.sd) +#define ds5_from_rgb_sd(sd) container_of(sd, struct ds5, rgb.sd) + +static inline void msleep_range(unsigned int delay_base) +{ + usleep_range(delay_base * 1000, delay_base * 1000 + 500); +} + +static int max9296_write_8(struct ds5 *state, u16 reg, u8 val) +{ + int ret; + + ret = regmap_raw_write(state->regmap_max9296, reg, &val, 1); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c write failed %d, 0x%04x = 0x%x\n", + __func__, ret, reg, val); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_dbg(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", + __func__, reg, val); + + dev_dbg(&state->client->dev, "%s(): (%d), 0x%02x 0x%04x = 0x%02x\n", + __func__, ret, state->client->addr, reg, val); + + return ret; +} + +static int max9296_read_8(struct ds5 *state, u16 reg, u8 *val) +{ + int ret; + + ret = regmap_raw_read(state->regmap_max9296, reg, val, 1); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c read failed %d, 0x%04x\n", + __func__, ret, reg); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_info(&state->client->dev, "%s(): i2c read 0x%04x = 0x%x\n", + __func__, reg, *val); + + dev_dbg(&state->client->dev, "%s(): (%d), 0x%02x 0x%04x = 0x%02x\n", + __func__, ret, state->client->addr, reg, *val); + + return ret; +} +static int max9295_write_8(struct ds5 *state, u16 reg, u8 val) +{ + int ret; + + ret = regmap_raw_write(state->regmap_max9295, reg, &val, 1); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c write failed %d, 0x%04x = 0x%x\n", + __func__, ret, reg, val); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_info(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", + __func__, reg, val); + + dev_dbg(&state->client->dev, "%s(): (%d), 0x%02x 0x%04x = 0x%02x\n", + __func__, ret, state->client->addr, reg, val); + + return ret; +} + +static int max9295_read_8(struct ds5 *state, u16 reg, u8 *val) +{ + int ret; + + ret = regmap_raw_read(state->regmap_max9295, reg, val, 1); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c read failed %d, 0x%04x\n", + __func__, ret, reg); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_info(&state->client->dev, "%s(): i2c read 0x%04x = 0x%x\n", + __func__, reg, *val); + + dev_dbg(&state->client->dev, "%s(): (%d), 0x%02x 0x%04x = 0x%02x\n", + __func__, ret, state->client->addr, reg, *val); + + return ret; +} + +static int ds5_write(struct ds5 *state, u16 reg, u16 val) +{ + int ret; + u8 value[2]; + + value[1] = val >> 8; + value[0] = val & 0x00FF; + + dev_dbg(&state->client->dev, + "%s(): writing to register: 0x%02x, (0x%04x) value1: 0x%x, value2:0x%x\n", + __func__, state->client->addr, reg, value[1], value[0]); + + ret = regmap_raw_write(state->regmap, reg, value, sizeof(value)); + if (ret < 0) + dev_err(&state->client->dev, + "%s(): i2c write failed %d,0x%02x ,0x%04x = 0x%x\n", + __func__, ret, state->client->addr, reg, val); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_dbg(&state->client->dev, "%s():0x%02x i2c write 0x%04x: 0x%x\n", + __func__, state->client->addr, reg, val); + + return ret; +} + +static int ds5_raw_write(struct ds5 *state, u16 reg, + const void *val, size_t val_len) +{ + int ret = regmap_raw_write(state->regmap, reg, val, val_len); + if (ret < 0) + dev_err(&state->client->dev, + "%s(): i2c raw write failed %d, %04x size(%d) bytes\n", + __func__, ret, reg, (int)val_len); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_dbg(&state->client->dev, + "%s(): i2c raw write 0x%04x: %d bytes\n", + __func__, reg, (int)val_len); + + return ret; +} + +static int ds5_read(struct ds5 *state, u16 reg, u16 *val) +{ + int ret = regmap_raw_read(state->regmap, reg, val, 2); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c read failed %d, 0x%04x\n", + __func__, ret, reg); + else { + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_dbg(&state->client->dev, "%s(): i2c read 0x%04x: 0x%x\n", + __func__, reg, *val); + } + + return ret; +} + +static int ds5_raw_read(struct ds5 *state, u16 reg, void *val, size_t val_len) +{ + int ret = regmap_raw_read(state->regmap, reg, val, val_len); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c read failed %d, 0x%04x\n", + __func__, ret, reg); + + return ret; +} + +static int pad_to_substream[DS5_MUX_PAD_COUNT]; + +static s64 d4xx_query_sub_stream[MAX_D457_COUNT][D457_MAX_SUBSTREAM] = {0}; + +static void set_sub_stream_fmt(s64 *query_sub_stream, int index, u32 code) +{ + query_sub_stream[index] &= 0xFFFFFFFFFFFF0000; + query_sub_stream[index] |= code; +} + +static void set_sub_stream_h(s64 *query_sub_stream, int index, u32 height) +{ + s64 val = height; + + val &= 0xFFFF; + query_sub_stream[index] &= 0xFFFFFFFF0000FFFF; + query_sub_stream[index] |= val << 16; +} + +static void set_sub_stream_w(s64 *query_sub_stream, int index, u32 width) +{ + s64 val = width; + + val &= 0xFFFF; + query_sub_stream[index] &= 0xFFFF0000FFFFFFFF; + query_sub_stream[index] |= val << 32; +} + +static void set_sub_stream_dt(s64 *query_sub_stream, int index, u32 dt) +{ + s64 val = dt; + + val &= 0xFF; + query_sub_stream[index] &= 0xFF00FFFFFFFFFFFF; + query_sub_stream[index] |= val << 48; +} + +static void set_sub_stream_vc_id(s64 *query_sub_stream, int index, u32 vc_id) +{ + s64 val = vc_id; + + val &= 0xFF; + query_sub_stream[index] &= 0x00FFFFFFFFFFFFFF; + query_sub_stream[index] |= val << 56; +} + +static int get_sub_stream_vc_id(const s64 *query_sub_stream, int index) +{ + s64 val = 0; + val = query_sub_stream[index] >> 56; + val &= 0xFF; + return (int)val; +} + +static u8 d4xx_set_sub_stream[] = { + 0, 0, 0, 0, 0, 0 +}; + +/* Pad ops */ + +static const u16 ds5_default_framerate = 30; + +// ********************** +// FIXME: D16 width must be doubled, because an 8-bit format is used. Check how +// the Tegra driver propagates resolutions and formats. +// ********************** + +//TODO: keep 6, till 5 is supported by FW +static const u16 ds5_framerates[] = {5, 30}; + +#define DS5_FRAMERATE_DEFAULT_IDX 1 + +static const u16 ds5_framerate_30 = 30; + +static const u16 ds5_framerate_15_30[] = {15, 30}; + +static const u16 ds5_framerate_25 = 25; + +static const u16 ds5_depth_framerate_to_30[] = {5, 15, 30}; +static const u16 ds5_framerate_to_30[] = {5, 10, 15, 30}; +static const u16 ds5_framerate_to_60[] = {5, 15, 30, 60}; +static const u16 ds5_framerate_to_90[] = {5, 15, 30, 60, 90}; +static const u16 ds5_framerate_100[] = {100}; +static const u16 ds5_framerate_90[] = {90}; +static const u16 ds5_imu_framerates[] = {50, 100, 200, 400}; + +static const struct ds5_resolution d43x_depth_sizes[] = { + { + .width = 1280, + .height = 720, + .framerates = ds5_depth_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_depth_framerate_to_30), + }, { + .width = 848, + .height = 480, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 848, + .height = 100, + .framerates = ds5_framerate_100, + .n_framerates = ARRAY_SIZE(ds5_framerate_100), + }, { + .width = 640, + .height = 480, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 640, + .height = 360, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 480, + .height = 270, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 424, + .height = 240, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 256, + .height = 144, + .framerates = ds5_framerate_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_90), + }, +}; + +static const struct ds5_resolution d46x_depth_sizes[] = { + { + .width = 1280, + .height = 960, + .framerates = ds5_framerates, + .n_framerates = ARRAY_SIZE(ds5_framerates), + }, { + .width = 640, + .height = 480, + .framerates = ds5_framerates, + .n_framerates = ARRAY_SIZE(ds5_framerates), + }, +}; + +static const struct ds5_resolution y8_sizes[] = { + { + .width = 1280, + .height = 720, + .framerates = ds5_depth_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_depth_framerate_to_30), + }, { + .width = 848, + .height = 480, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 640, + .height = 480, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 640, + .height = 360, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 480, + .height = 270, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 424, + .height = 240, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + } +}; + +static const struct ds5_resolution ds5_rlt_rgb_sizes[] = { + { + .width = 1280, + .height = 800, + .framerates = ds5_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_30), + }, { + .width = 1280, + .height = 720, + .framerates = ds5_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_30), + }, { + .width = 848, + .height = 480, + .framerates = ds5_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_60), + }, { + .width = 640, + .height = 480, + .framerates = ds5_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_60), + }, { + .width = 640, + .height = 360, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 480, + .height = 270, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 424, + .height = 240, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, +}; + +static const struct ds5_resolution ds5_onsemi_rgb_sizes[] = { + { + .width = 640, + .height = 480, + .framerates = ds5_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_90), + }, { + .width = 960, + .height = 720, + .framerates = ds5_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_framerate_to_60), + }, { + .width = 1280, + .height = 720, + .framerates = ds5_framerates, + .n_framerates = ARRAY_SIZE(ds5_framerates), + }, { + .width = 1920, + .height = 1080, + .framerates = ds5_framerates, + .n_framerates = ARRAY_SIZE(ds5_framerates), + }, { + .width = 2048, + .height = 1536, + .framerates = ds5_framerates, + .n_framerates = ARRAY_SIZE(ds5_framerates), + }, +}; + +static const struct ds5_resolution ds5_size_w10 = { + .width = 1920, + .height = 1080, + .framerates = &ds5_framerate_30, + .n_framerates = 1, +}; + +static const struct ds5_resolution d43x_calibration_sizes[] = { + { + .width = 1280, + .height = 800, + .framerates = ds5_framerate_15_30, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_30), + }, +}; + +static const struct ds5_resolution d46x_calibration_sizes[] = { + { + .width = 1600, + .height = 1300, + .framerates = ds5_framerate_15_30, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_30), + }, +}; + +static const struct ds5_resolution ds5_size_imu[] = { + { + .width = 32, + .height = 1, + .framerates = ds5_imu_framerates, + .n_framerates = ARRAY_SIZE(ds5_imu_framerates), + }, +}; + +static const struct ds5_format ds5_depth_formats_d43x[] = { + { + // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. + .data_type = 0x1e, /* UYVY */ + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), + .resolutions = d43x_depth_sizes, + }, { + .data_type = 0x2a, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), + .resolutions = d43x_depth_sizes, + }, { + .data_type = 0x24, /* 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), + .resolutions = d43x_calibration_sizes, + }, +}; + +static const struct ds5_format ds5_depth_formats_d46x[] = { + { + // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. + .data_type = 0x1e, /* Z16 */ + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), + .resolutions = d46x_depth_sizes, + }, { + /* First format: default */ + .data_type = 0x2a, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), + .resolutions = d46x_depth_sizes, + }, { + .data_type = 0x24, /* 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), + .resolutions = d43x_calibration_sizes, + }, +}; + +#define DS5_DEPTH_N_FORMATS 1 + +static const struct ds5_format ds5_y_formats_ds5u[] = { + { + /* First format: default */ + .data_type = 0x2a, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(y8_sizes), + .resolutions = y8_sizes, + }, { + .data_type = 0x1e, /* Y8I */ + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .n_resolutions = ARRAY_SIZE(y8_sizes), + .resolutions = y8_sizes, + }, { + .data_type = 0x24, /* 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), + .resolutions = d43x_calibration_sizes, + }, +}; + +static const struct ds5_format ds5_rlt_rgb_format = { + .data_type = 0x1e, /* UYVY */ + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .n_resolutions = ARRAY_SIZE(ds5_rlt_rgb_sizes), + .resolutions = ds5_rlt_rgb_sizes, +}; +#define DS5_RLT_RGB_N_FORMATS 1 + +static const struct ds5_format ds5_onsemi_rgb_format = { + .data_type = 0x1e, /* UYVY */ + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .n_resolutions = ARRAY_SIZE(ds5_onsemi_rgb_sizes), + .resolutions = ds5_onsemi_rgb_sizes, +}; +#define DS5_ONSEMI_RGB_N_FORMATS 1 + +static const struct ds5_variant ds5_variants[] = { + [DS5_DS5U] = { + .formats = ds5_y_formats_ds5u, + .n_formats = ARRAY_SIZE(ds5_y_formats_ds5u), + }, +}; + +static const struct ds5_format ds5_imu_formats[] = { + { + /* First format: default */ + .data_type = 0x2a, /* IMU DT */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(ds5_size_imu), + .resolutions = ds5_size_imu, + }, +}; + +static const struct v4l2_mbus_framefmt ds5_mbus_framefmt_template = { + .width = 0, + .height = 0, + .code = MEDIA_BUS_FMT_FIXED, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_DEFAULT, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + +/* Get readable sensor name */ +static const char *ds5_get_sensor_name(struct ds5 *state) +{ + static const char *sensor_name[] = {"unknown", "RGB", "DEPTH", "Y8", "IMU"}; + int sensor_id = state->is_rgb | state->is_rgb_b; + sensor_id += (state->is_depth | state->is_depth_b) * 2; + sensor_id += state->is_y8 * 3 + state->is_imu * 4; + + if (sensor_id >= (sizeof(sensor_name)/sizeof(*sensor_name))) + sensor_id = 0; + + return sensor_name[sensor_id]; +} + +static void ds5_set_state_last_set(struct ds5 *state) +{ + dev_info(&state->client->dev, "%s(): %s\n", + __func__, ds5_get_sensor_name(state)); + + if (state->is_depth) + state->mux.last_set = &state->depth.sensor; + else if (state->is_rgb) + state->mux.last_set = &state->rgb.sensor; + else if (state->is_rgb_b) + state->mux.last_set = &state->rgb_b.sensor; + else if (state->is_depth_b) + state->mux.last_set = &state->depth_b.sensor; + else if (state->is_y8) + state->mux.last_set = &state->motion_t.sensor; + else + state->mux.last_set = &state->imu.sensor; +} + +/* This is needed for .get_fmt() + * and if streaming is started without .set_fmt() + */ +static void ds5_sensor_format_init(struct ds5_sensor *sensor) +{ + const struct ds5_format *fmt; + struct v4l2_mbus_framefmt *ffmt; + unsigned int i; + + if (sensor->config.format) + return; + + dev_info(sensor->sd.dev, "%s(): on pad %u\n", __func__, sensor->mux_pad); + + ffmt = &sensor->format; + *ffmt = ds5_mbus_framefmt_template; + /* Use the first format */ + fmt = sensor->formats; + ffmt->code = fmt->mbus_code; + /* and the first resolution */ + ffmt->width = fmt->resolutions->width; + ffmt->height = fmt->resolutions->height; + + sensor->config.format = fmt; + sensor->config.resolution = fmt->resolutions; + /* Set default framerate to 30, or to 1st one if not supported */ + for (i = 0; i < fmt->resolutions->n_framerates; i++) { + if (fmt->resolutions->framerates[i] == ds5_framerate_30 /* fps */) { + sensor->config.framerate = ds5_framerate_30; + return; + } + } + sensor->config.framerate = fmt->resolutions->framerates[0]; +} + +/* No locking needed for enumeration methods */ +static int ds5_sensor_enum_mbus_code(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_mbus_code_enum *mce) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + //struct ds5_vchan *vchan = sensor->vchan; + dev_info(sensor->sd.dev, "%s(): sensor %s pad: %d index: %d\n", + __func__, sensor->sd.name, mce->pad, mce->index); + if (mce->pad) + return -EINVAL; + + if (mce->index >= sensor->n_formats) + return -EINVAL; + + mce->code = sensor->formats[mce->index].mbus_code; + + return 0; +} + +static int ds5_sensor_enum_frame_size(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + struct ds5 *state = v4l2_get_subdevdata(sd); + const struct ds5_format *fmt; + unsigned int i; + + dev_info(sensor->sd.dev, "%s(): sensor %s is %s\n", + __func__, sensor->sd.name, ds5_get_sensor_name(state)); + + for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) + if (fse->code == fmt->mbus_code) + break; + + if (i == sensor->n_formats) + return -EINVAL; + + if (fse->index >= fmt->n_resolutions) + return -EINVAL; + + fse->min_width = fse->max_width = fmt->resolutions[fse->index].width; + fse->min_height = fse->max_height = fmt->resolutions[fse->index].height; + + return 0; +} + +static int ds5_sensor_enum_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + const struct ds5_format *fmt; + const struct ds5_resolution *res; + unsigned int i; + + for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) + if (fie->code == fmt->mbus_code) + break; + + if (i == sensor->n_formats) + return -EINVAL; + + for (i = 0, res = fmt->resolutions; i < fmt->n_resolutions; i++, res++) + if (res->width == fie->width && res->height == fie->height) + break; + + if (i == fmt->n_resolutions) + return -EINVAL; + + if (fie->index >= res->n_framerates) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = res->framerates[fie->index]; + + return 0; +} + +static int ds5_sensor_get_fmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + struct ds5 *state = v4l2_get_subdevdata(sd); + + if (fmt->pad) + return -EINVAL; + + mutex_lock(&state->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) + fmt->format = *v4l2_subdev_get_try_format(sd, v4l2_state, fmt->pad); +#else + fmt->format = *v4l2_subdev_state_get_format(v4l2_state, fmt->pad); +#endif + else + fmt->format = sensor->format; + + mutex_unlock(&state->lock); + + dev_info(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", + __func__, fmt->pad, fmt->format.code, + fmt->format.width, fmt->format.height); + + return 0; +} + +/* Called with lock held */ +static const struct ds5_format *ds5_sensor_find_format( + struct ds5_sensor *sensor, + struct v4l2_mbus_framefmt *ffmt, + const struct ds5_resolution **best) +{ + const struct ds5_resolution *res; + const struct ds5_format *fmt; + unsigned long best_delta = ~0; + unsigned int i; + + for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) { + if (fmt->mbus_code == ffmt->code) + break; + } + dev_info(sensor->sd.dev, "%s(): mbus_code = %x, code = %x \n", + __func__, fmt->mbus_code, ffmt->code); + + if (i == sensor->n_formats) { + /* Not found, use default */ + dev_dbg(sensor->sd.dev, "%s:%d Not found, use default\n", + __func__, __LINE__); + fmt = sensor->formats; + } + for (i = 0, res = fmt->resolutions; i < fmt->n_resolutions; i++, res++) { + unsigned long delta = abs(ffmt->width * ffmt->height - + res->width * res->height); + if (delta < best_delta) { + best_delta = delta; + *best = res; + } + } + + ffmt->code = fmt->mbus_code; + ffmt->width = (*best)->width; + ffmt->height = (*best)->height; + + ffmt->field = V4L2_FIELD_NONE; + /* Should we use V4L2_COLORSPACE_RAW for Y12I? */ + ffmt->colorspace = V4L2_COLORSPACE_SRGB; + + return fmt; +} + +#define MIPI_CSI2_TYPE_NULL 0x10 +#define MIPI_CSI2_TYPE_BLANKING 0x11 +#define MIPI_CSI2_TYPE_EMBEDDED8 0x12 +#define MIPI_CSI2_TYPE_YUV422_8 0x1e +#define MIPI_CSI2_TYPE_YUV422_10 0x1f +#define MIPI_CSI2_TYPE_RGB565 0x22 +#define MIPI_CSI2_TYPE_RGB888 0x24 +#define MIPI_CSI2_TYPE_RAW6 0x28 +#define MIPI_CSI2_TYPE_RAW7 0x29 +#define MIPI_CSI2_TYPE_RAW8 0x2a +#define MIPI_CSI2_TYPE_RAW10 0x2b +#define MIPI_CSI2_TYPE_RAW12 0x2c +#define MIPI_CSI2_TYPE_RAW14 0x2d +/* 1-8 */ +#define MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i) - 1) + +static unsigned int mbus_code_to_mipi(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_RGB565_1X16: + return MIPI_CSI2_TYPE_RGB565; + case MEDIA_BUS_FMT_RGB888_1X24: + return MIPI_CSI2_TYPE_RGB888; + case MEDIA_BUS_FMT_YUYV10_1X20: + return MIPI_CSI2_TYPE_YUV422_10; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + return MIPI_CSI2_TYPE_YUV422_8; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return MIPI_CSI2_TYPE_RAW12; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return MIPI_CSI2_TYPE_RAW10; + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return MIPI_CSI2_TYPE_RAW8; + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return MIPI_CSI2_TYPE_USER_DEF(1); + default: + WARN_ON(1); + return -EINVAL; + } +} + +static s64 *match_d4xx_sub_stream_qmenu(const s64 *qmenu_int) +{ + int i = 0; + s64 *matched_qmenu_int = NULL; + + for (i = 0; i < MAX_D457_COUNT; i++) { + if (d4xx_query_sub_stream[i] == qmenu_int) { + matched_qmenu_int = d4xx_query_sub_stream[i]; + break; + } + } + + return matched_qmenu_int; +} + +static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mf;// = &fmt->format; + int substream = -1; + s64 *query_sub_stream = NULL; + + dev_dbg(sensor->sd.dev, "%s(): state %p, " + "sensor %p, fmt %p, fmt->format %p\n", + __func__, state, sensor, fmt, &fmt->format); + + mf = &fmt->format; + + if (fmt->pad) + return -EINVAL; + + mutex_lock(&state->lock); + + sensor->config.format = ds5_sensor_find_format(sensor, mf, + &sensor->config.resolution); + //r = DS5_FRAMERATE_DEFAULT_IDX < sensor->config.resolution->n_framerates ? + // DS5_FRAMERATE_DEFAULT_IDX : 0; + /* FIXME: check if a framerate has been set */ + //sensor->config.framerate = sensor->config.resolution->framerates[r]; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + if (cfg && fmt->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(&sensor->sd, cfg, fmt->pad) = *mf; +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) + if (v4l2_state && fmt->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(&sensor->sd, v4l2_state, fmt->pad) = *mf; +#else + if (v4l2_state && fmt->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_state_get_format(v4l2_state, fmt->pad) = *mf; +#endif + + else +// FIXME: use this format in .s_stream() + sensor->format = *mf; + + state->mux.last_set = sensor; + + mutex_unlock(&state->lock); + + substream = pad_to_substream[sensor->mux_pad]; + + if (substream != -1) { + query_sub_stream = match_d4xx_sub_stream_qmenu(state->ctrls.query_sub_stream->qmenu_int); + if (query_sub_stream) { + set_sub_stream_fmt(query_sub_stream, substream, mf->code); + set_sub_stream_h(query_sub_stream, substream, mf->height); + set_sub_stream_w(query_sub_stream, substream, mf->width); + set_sub_stream_dt(query_sub_stream, substream, mbus_code_to_mipi(mf->code)); + } else { + dev_err(sensor->sd.dev, + "failed to find sub stream fmt info: %s\n", + sensor->sd.name); + } + } + + dev_info(sensor->sd.dev, "%s(): fmt->pad: %d, sensor->mux_pad: %d, code: 0x%x, %ux%u substream:%d\n", __func__, + fmt->pad, sensor->mux_pad, fmt->format.code, + fmt->format.width, fmt->format.height, substream); + + return 0; +} + +static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + struct ds5 *state = v4l2_get_subdevdata(sd); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + return __ds5_sensor_set_fmt(state, sensor, cfg, fmt); +#else + return __ds5_sensor_set_fmt(state, sensor, v4l2_state, fmt); +#endif +} + +static int ds5_configure(struct ds5 *state) +{ + struct ds5_sensor *sensor; + u16 fmt, md_fmt, vc_id; + u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; + int ret; + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + + if (state->is_depth) { + sensor = &state->depth.sensor; + dt_addr = DS5_DEPTH_STREAM_DT; + md_addr = DS5_DEPTH_STREAM_MD; + override_addr = DS5_DEPTH_OVERRIDE; + fps_addr = DS5_DEPTH_FPS; + width_addr = DS5_DEPTH_RES_WIDTH; + height_addr = DS5_DEPTH_RES_HEIGHT; + md_fmt = 0x12; + vc_id = 0; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_rgb) { + sensor = &state->rgb.sensor; + dt_addr = DS5_RGB_STREAM_DT; + md_addr = DS5_RGB_STREAM_MD; + override_addr = 0; + fps_addr = DS5_RGB_FPS; + width_addr = DS5_RGB_RES_WIDTH; + height_addr = DS5_RGB_RES_HEIGHT; + md_fmt = 0x12; + vc_id = 1; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_y8) { + sensor = &state->motion_t.sensor; + dt_addr = DS5_IR_STREAM_DT; + md_addr = DS5_IR_STREAM_MD; + override_addr = DS5_IR_OVERRIDE; + fps_addr = DS5_IR_FPS; + width_addr = DS5_IR_RES_WIDTH; + height_addr = DS5_IR_RES_HEIGHT; + md_fmt = 0x12; + vc_id = 2; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_imu) { + sensor = &state->imu.sensor; + dt_addr = DS5_IMU_STREAM_DT; + md_addr = DS5_IMU_STREAM_MD; + override_addr = 0; + fps_addr = DS5_IMU_FPS; + width_addr = DS5_IMU_RES_WIDTH; + height_addr = DS5_IMU_RES_HEIGHT; + vc_id = 3; + md_fmt = 0x0; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_depth_b) { + sensor = &state->depth_b.sensor; + + dt_addr = DS5_DEPTH_STREAM_DT; + md_addr = DS5_DEPTH_STREAM_MD; + override_addr = DS5_DEPTH_OVERRIDE; + fps_addr = DS5_DEPTH_FPS; + width_addr = DS5_DEPTH_RES_WIDTH; + height_addr = DS5_DEPTH_RES_HEIGHT; + md_fmt = 0x12; + vc_id = 2; + state->client->addr = dpdata->subdev_info[1].board_info.addr; + } else if (state->is_rgb_b) { + sensor = &state->rgb_b.sensor; + dt_addr = DS5_RGB_STREAM_DT; + md_addr = DS5_RGB_STREAM_MD; + override_addr = 0; + fps_addr = DS5_RGB_FPS; + width_addr = DS5_RGB_RES_WIDTH; + height_addr = DS5_RGB_RES_HEIGHT; + md_fmt = 0x12; + vc_id = 3; + state->client->addr = dpdata->subdev_info[1].board_info.addr; + } else { + return -EINVAL; + } + + fmt = sensor->streaming ? sensor->config.format->data_type : 0; + + /* + * Set depth stream Z16 data type as 0x31 + * Set IR stream Y8I data type as 0x32 + */ + if ((state->is_depth || state->is_depth_b) && fmt != 0) + ret = ds5_write(state, dt_addr, 0x31); + else if (state->is_y8 && fmt != 0 && + sensor->config.format->data_type == 0x1E) + ret = ds5_write(state, dt_addr, 0x32); + else + ret = ds5_write(state, dt_addr, fmt); + if (ret < 0) + return ret; + + if (state->is_depth_b || state->is_rgb_b) + ret = ds5_write(state, md_addr, ((vc_id % 2) << 8) | md_fmt); + else + ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); + + if (ret < 0) + return ret; + + if (!sensor->streaming) + return ret; + + if (override_addr != 0) { + ret = ds5_write(state, override_addr, fmt); + if (ret < 0) + return ret; + } + + ret = ds5_write(state, fps_addr, sensor->config.framerate); + if (ret < 0) + return ret; + + ret = ds5_write(state, width_addr, sensor->config.resolution->width); + if (ret < 0) + return ret; + + ret = ds5_write(state, height_addr, sensor->config.resolution->height); + if (ret < 0) + return ret; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ds5_depth_pad_ops = { + .enum_mbus_code = ds5_sensor_enum_mbus_code, + .enum_frame_size = ds5_sensor_enum_frame_size, + .enum_frame_interval = ds5_sensor_enum_frame_interval, + .get_fmt = ds5_sensor_get_fmt, + .set_fmt = ds5_sensor_set_fmt, +}; + +static int ds5_sensor_g_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + struct v4l2_subdev_frame_interval *fi) +#else + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +#endif +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + + if (NULL == sd || NULL == fi) + return -EINVAL; + + fi->interval.numerator = 1; + fi->interval.denominator = sensor->config.framerate; + + dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, + fi->interval.denominator); + + return 0; +} +static u16 __ds5_probe_framerate(const struct ds5_resolution *res, u16 target); + +static int ds5_sensor_s_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + struct v4l2_subdev_frame_interval *fi) +#else + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +#endif +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + u16 framerate = 1; + + if (NULL == sd || NULL == fi || fi->interval.numerator == 0) + return -EINVAL; + + framerate = fi->interval.denominator / fi->interval.numerator; + framerate = __ds5_probe_framerate(sensor->config.resolution, framerate); + sensor->config.framerate = framerate; + fi->interval.numerator = 1; + fi->interval.denominator = framerate; + + dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); + + return 0; +} + +static int ds5_sensor_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + + dev_info(sensor->sd.dev, "%s(): sensor: name=%s state=%d\n", __func__, sensor->sd.name, on); + + sensor->streaming = on; + + return 0; +} + +static const struct v4l2_subdev_video_ops ds5_sensor_video_ops = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + .g_frame_interval = ds5_sensor_g_frame_interval, + .s_frame_interval = ds5_sensor_s_frame_interval, +#endif + .s_stream = ds5_sensor_s_stream, +}; + +static const struct v4l2_subdev_ops ds5_depth_subdev_ops = { + .pad = &ds5_depth_pad_ops, + .video = &ds5_sensor_video_ops, +}; + +/* Motion detection */ + +/* FIXME: identical to ds5_depth_pad_ops, use one for both */ +static const struct v4l2_subdev_pad_ops ds5_motion_t_pad_ops = { + .enum_mbus_code = ds5_sensor_enum_mbus_code, + .enum_frame_size = ds5_sensor_enum_frame_size, + .enum_frame_interval = ds5_sensor_enum_frame_interval, + .get_fmt = ds5_sensor_get_fmt, + .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, +#endif +}; + +static const struct v4l2_subdev_ops ds5_motion_t_subdev_ops = { + .pad = &ds5_motion_t_pad_ops, + .video = &ds5_sensor_video_ops, +}; + +/* FIXME: identical to ds5_depth_pad_ops, use one for both? */ +static const struct v4l2_subdev_pad_ops ds5_rgb_pad_ops = { + .enum_mbus_code = ds5_sensor_enum_mbus_code, + .enum_frame_size = ds5_sensor_enum_frame_size, + .enum_frame_interval = ds5_sensor_enum_frame_interval, + .get_fmt = ds5_sensor_get_fmt, + .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, +#endif +}; + +static const struct v4l2_subdev_ops ds5_rgb_subdev_ops = { + .pad = &ds5_rgb_pad_ops, + .video = &ds5_sensor_video_ops, +}; + +static const struct v4l2_subdev_pad_ops ds5_imu_pad_ops = { + .enum_mbus_code = ds5_sensor_enum_mbus_code, + .enum_frame_size = ds5_sensor_enum_frame_size, + .enum_frame_interval = ds5_sensor_enum_frame_interval, + .get_fmt = ds5_sensor_get_fmt, + .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, +#endif +}; + +static const struct v4l2_subdev_ops ds5_imu_subdev_ops = { + .pad = &ds5_imu_pad_ops, + .video = &ds5_sensor_video_ops, +}; + +static int ds5_hw_set_auto_exposure(struct ds5 *state, u32 base, s32 val) +{ + if (val != V4L2_EXPOSURE_APERTURE_PRIORITY && + val != V4L2_EXPOSURE_MANUAL) + return -EINVAL; + + /* + * In firmware color auto exposure setting follow the uvc_menu_info + * exposure_auto_controls numbers, in drivers/media/usb/uvc/uvc_ctrl.c. + */ + if ((state->is_rgb || state->is_rgb_b) && val == V4L2_EXPOSURE_APERTURE_PRIORITY) + val = 8; + + /* + * In firmware depth auto exposure on: 1, off: 0. + */ + if (!(state->is_rgb & state->is_rgb_b)) { + if (val == V4L2_EXPOSURE_APERTURE_PRIORITY) + val = 1; + else if (val == V4L2_EXPOSURE_MANUAL) + val = 0; + } + + return ds5_write(state, base | DS5_AUTO_EXPOSURE_MODE, (u16)val); +} + +/* + * Manual exposure in us + * Depth/Y8: between 100 and 200000 (200ms) + * Color: between 100 and 1000000 (1s) + */ +static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) +{ + int ret = -1; + + if (val < 1) + val = 1; + if ((state->is_depth || state->is_depth_b || state->is_y8) && val > MAX_DEPTH_EXP) + val = MAX_DEPTH_EXP; + if ((state->is_rgb || state->is_rgb_b) && val > MAX_RGB_EXP) + val = MAX_RGB_EXP; + + /* + * Color and depth uses different unit: + * Color: 1 is 100 us + * Depth: 1 is 1 us + */ + + ret = ds5_write(state, base | DS5_MANUAL_EXPOSURE_MSB, (u16)(val >> 16)); + if (!ret) + ret = ds5_write(state, base | DS5_MANUAL_EXPOSURE_LSB, + (u16)(val & 0xffff)); + + return ret; +} + +#define DS5_MAX_LOG_WAIT 200 +#define DS5_MAX_LOG_SLEEP 10 +#define DS5_MAX_LOG_POLL (DS5_MAX_LOG_WAIT / DS5_MAX_LOG_SLEEP) + +// TODO: why to use DS5_DEPTH_Y_STREAMS_DT? +#define DS5_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | DS5_DEPTH_STREAM_DT) + +#define DS5_CAMERA_CID_LOG (DS5_CAMERA_CID_BASE+0) +#define DS5_CAMERA_CID_LASER_POWER (DS5_CAMERA_CID_BASE+1) +#define DS5_CAMERA_CID_MANUAL_LASER_POWER (DS5_CAMERA_CID_BASE+2) +#define DS5_CAMERA_DEPTH_CALIBRATION_TABLE_GET (DS5_CAMERA_CID_BASE+3) +#define DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET (DS5_CAMERA_CID_BASE+4) +#define DS5_CAMERA_COEFF_CALIBRATION_TABLE_GET (DS5_CAMERA_CID_BASE+5) +#define DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET (DS5_CAMERA_CID_BASE+6) +#define DS5_CAMERA_CID_FW_VERSION (DS5_CAMERA_CID_BASE+7) +#define DS5_CAMERA_CID_GVD (DS5_CAMERA_CID_BASE+8) +#define DS5_CAMERA_CID_AE_ROI_GET (DS5_CAMERA_CID_BASE+9) +#define DS5_CAMERA_CID_AE_ROI_SET (DS5_CAMERA_CID_BASE+10) +#define DS5_CAMERA_CID_AE_SETPOINT_GET (DS5_CAMERA_CID_BASE+11) +#define DS5_CAMERA_CID_AE_SETPOINT_SET (DS5_CAMERA_CID_BASE+12) +#define DS5_CAMERA_CID_ERB (DS5_CAMERA_CID_BASE+13) +#define DS5_CAMERA_CID_EWB (DS5_CAMERA_CID_BASE+14) +#define DS5_CAMERA_CID_HWMC (DS5_CAMERA_CID_BASE+15) + +#define DS5_CAMERA_CID_PWM (DS5_CAMERA_CID_BASE+22) + +/* the HWMC will remain for legacy tools compatibility, + * HWMC_RW used for UVC compatibility + */ +#define DS5_CAMERA_CID_HWMC_RW (DS5_CAMERA_CID_BASE+32) + +static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, + bool isRead, u16 *dataLen) +{ + int ret = 0; + u16 status = 2; + int retries = 100; + int errorCode; + int iter = retries; + + dev_dbg(&state->client->dev, + "%s(): HWMC header: 0x%x, magic: 0x%x, opcode: 0x%x, " + "param1: %d, param2: %d, param3: %d, param4: %d\n", + __func__, cmd->header, cmd->magic_word, cmd->opcode, + cmd->param1, cmd->param2, cmd->param3, cmd->param4); + + ds5_raw_write_with_check(state, 0x4900, cmd, cmdLen); + + ds5_write_with_check(state, 0x490C, 0x01); /* execute cmd */ + do { + if (iter != retries) + msleep_range(10); + ret = ds5_read(state, 0x4904, &status); + } while (iter-- && status != 0); + + if (ret || status != 0) { + ds5_raw_read(state, 0x4900, &errorCode, 4); + dev_err(&state->client->dev, + "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", + __func__, ret, status, errorCode); + ret = -EAGAIN; + } + + if (isRead) { + if (*dataLen == 0) { + ret = regmap_raw_read(state->regmap, 0x4908, dataLen, sizeof(u16)); + if (ret) + return -EAGAIN; + } + + dev_dbg(&state->client->dev, "%s(): HWMC read len: %d\n", + __func__, *dataLen); + // First 4 bytes of cmd->Data after read will include opcode + ds5_raw_read_with_check(state, 0x4900, cmd->Data, *dataLen); + + /*This is neede for libreealsense, to align there code with UVC*/ + cmd->Data[1000] = (unsigned char)((*dataLen) & 0x00FF); + cmd->Data[1001] = (unsigned char)(((*dataLen) & 0xFF00) >> 8); + } + + return 0; +} + +#define DS5_HWMC_DATA 0x4900 +#define DS5_HWMC_STATUS 0x4904 +#define DS5_HWMC_RESP_LEN 0x4908 +#define DS5_HWMC_EXEC 0x490C + +#define DS5_HWMC_STATUS_OK 0 +#define DS5_HWMC_STATUS_ERR 1 +#define DS5_HWMC_STATUS_WIP 2 +#define DS5_HWMC_BUFFER_SIZE 1024 + +static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) +{ + int ret = 0; + u16 status = DS5_HWMC_STATUS_WIP; + int retries = 100; + int errorCode; + u16 tmp_len = 0; + const int SIZE_OF_HW_MONITOR_HEADER = 4; + + memset(data, 0, DS5_HWMC_BUFFER_SIZE); + + do { + if (retries != 100) + msleep_range(1); + ret = ds5_read(state, DS5_HWMC_STATUS, &status); + } while (!ret && retries-- && status != DS5_HWMC_STATUS_OK); + + if (ret || status != DS5_HWMC_STATUS_OK) { + if (status == DS5_HWMC_STATUS_ERR) { + ds5_raw_read(state, DS5_HWMC_DATA, &errorCode, sizeof(errorCode)); + dev_err(&state->client->dev, + "%s(): HWMC failed, ret: %d, error code: %d\n", + __func__, ret, errorCode); + } else { + dev_err(&state->client->dev, + "%s(): HWMC failed because of timeout, ret: %d\n", + __func__, ret); + } + return -EAGAIN; + } + + ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, + &tmp_len, sizeof(tmp_len)); + if (ret) + return -EAGAIN; + + if (tmp_len > DS5_HWMC_BUFFER_SIZE) + return -ENOBUFS; + + dev_dbg(&state->client->dev, + "%s(): HWMC read len: %d, lrs_len: %d\n", + __func__, tmp_len, tmp_len - SIZE_OF_HW_MONITOR_HEADER); + + ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, tmp_len); + + /* This is needed for librealsense, to align there code with UVC, + * last word is length - 4 bytes header length + */ + tmp_len -= SIZE_OF_HW_MONITOR_HEADER; + data[DS5_HWMC_BUFFER_SIZE - 4] = (unsigned char)(tmp_len & 0x00FF); + data[DS5_HWMC_BUFFER_SIZE - 3] = (unsigned char)((tmp_len & 0xFF00) >> 8); + data[DS5_HWMC_BUFFER_SIZE - 2] = 0; + data[DS5_HWMC_BUFFER_SIZE - 1] = 0; + + return 0; +} + +static int ds5_set_calibration_data(struct ds5 *state, + struct hwm_cmd *cmd, u16 length) +{ + int ret = -1; + int retries = 10; + u16 status = 2; + + ds5_raw_write_with_check(state, 0x4900, cmd, length); + + ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + do { + if (retries != 10) + msleep_range(200); + ret = ds5_read(state, 0x4904, &status); + } while (retries-- && status != 0); + + if (ret || status != 0) { + dev_err(&state->client->dev, + "%s(): Failed to set calibration table %d," + "ret: %d, fw error: %x\n", + __func__, cmd->param1, ret, status); + } + + return -EINVAL; +} + +static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on); + +static int ds5_s_state(struct ds5 *state, int vc) +{ + int ret = 0; + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + + dev_dbg(&state->client->dev, "%s(): set state for vc: %d\n", __func__, vc); + + switch (vc) { + case 0: + state->is_depth = 1; + state->is_depth_b = 0; + state->is_rgb = 0; + state->is_rgb_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case 1: + state->is_depth = 0; + state->is_depth_b = 0; + state->is_rgb = 1; + state->is_rgb_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case 2: + state->is_depth = 0; + state->is_rgb = 0; + if (dpdata->subdev_num == 1) { + state->is_y8 = 1; //default case: vc2:state is IR + state->is_imu = 0; + } else if (dpdata->subdev_num == 2) { + state->is_depth_b = 1; //A+A case: vc2:state is depth_b + state->is_rgb_b = 0; + } + break; + case 3: + state->is_depth = 0; + state->is_rgb = 0; + if (dpdata->subdev_num == 1) { + state->is_y8 = 0; + state->is_imu = 0; //default case: vc3:state is IMU + } else if (dpdata->subdev_num == 2) { + state->is_depth_b = 0; + state->is_rgb_b = 1; //A+A case: vc3:state is rgb_b + } + break; + default: + dev_warn(&state->client->dev, "%s(): unknown vc: %d\n", __func__, vc); + ret = -EINVAL; + break; + } + ds5_set_state_last_set(state); + return ret; +} + +static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ds5 *state = container_of(ctrl->handler, struct ds5, + ctrls.handler); + struct v4l2_subdev *sd = &state->mux.sd.subdev; + struct ds5_sensor *sensor = (struct ds5_sensor *)ctrl->priv; + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + int ret = -EINVAL; + u16 base = DS5_DEPTH_CONTROL_BASE; + u32 val; + u16 on; + u16 vc_id; + + if (sensor) { + switch (sensor->mux_pad) { + case DS5_MUX_PAD_DEPTH_B: + state->client->addr = dpdata->subdev_info[1].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth_b); + state->is_rgb_b = 0; + state->is_depth_b = 1; + state->is_rgb = 0; + state->is_depth = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB_B: + state->client->addr = dpdata->subdev_info[1].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb_b); + state->is_rgb_b = 1; + state->is_depth_b = 0; + state->is_rgb = 0; + state->is_depth = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_DEPTH_A: + state->client->addr = dpdata->subdev_info[0].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); + state->is_rgb = 0; + state->is_depth = 1; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB_A: + state->client->addr = dpdata->subdev_info[0].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); + state->is_rgb = 1; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_MOTION_T_A: + state->client->addr = dpdata->subdev_info[0].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 1; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IMU_A: + state->client->addr = dpdata->subdev_info[0].board_info.addr; + state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + default: + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + + } + } + + if (state->is_rgb || state->is_rgb_b) + base = DS5_RGB_CONTROL_BASE; + + v4l2_dbg(3, 1, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); + dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s, value: %d\n", + __func__, ds5_get_sensor_name(state), ctrl->name, ctrl->val); + + mutex_lock(&state->lock); + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ds5_write(state, base | DS5_MANUAL_GAIN, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = ds5_hw_set_auto_exposure(state, base, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_ABSOLUTE: + ret = ds5_hw_set_exposure(state, base, ctrl->val); + break; + case DS5_CAMERA_CID_LASER_POWER: + if (!(state->is_rgb & state->is_rgb_b)) + ret = ds5_write(state, base | DS5_LASER_POWER, + ctrl->val); + break; + case DS5_CAMERA_CID_MANUAL_LASER_POWER: + if (!(state->is_rgb & state->is_rgb_b)) + ret = ds5_write(state, base | DS5_MANUAL_LASER_POWER, + ctrl->val); + break; + case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET: { + struct hwm_cmd *calib_cmd; + + dev_dbg(&state->client->dev, + "%s(): DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET \n", + __func__); + dev_dbg(&state->client->dev, + "%s(): table id: 0x%x\n", + __func__, *((u8 *)ctrl->p_new.p + 2)); + if (ctrl->p_new.p && DEPTH_CALIBRATION_ID == + *((u8 *)ctrl->p_new.p + 2)) { + calib_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 256, GFP_KERNEL); + if (!calib_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(calib_cmd, &set_calib_data, sizeof(set_calib_data)); + calib_cmd->header = 276; + calib_cmd->param1 = DEPTH_CALIBRATION_ID; + memcpy (calib_cmd->Data, (u8 *)ctrl->p_new.p, 256); + ret = ds5_set_calibration_data(state, calib_cmd, + sizeof(struct hwm_cmd) + 256); + devm_kfree(&state->client->dev, calib_cmd); + } + break; + } + case DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET: { + struct hwm_cmd *calib_cmd; + + dev_dbg(&state->client->dev, + "%s(): DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET \n", + __func__); + dev_dbg(&state->client->dev, + "%s(): table id %d\n", + __func__, *((u8 *)ctrl->p_new.p + 2)); + if (ctrl->p_new.p && COEF_CALIBRATION_ID == + *((u8 *)ctrl->p_new.p + 2)) { + calib_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 512, GFP_KERNEL); + if (!calib_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy (calib_cmd, &set_calib_data, sizeof (set_calib_data)); + calib_cmd->header = 532; + calib_cmd->param1 = COEF_CALIBRATION_ID; + memcpy(calib_cmd->Data, (u8 *)ctrl->p_new.p, 512); + ret = ds5_set_calibration_data(state, calib_cmd, + sizeof(struct hwm_cmd) + 512); + devm_kfree(&state->client->dev, calib_cmd); + } + } + break; + case DS5_CAMERA_CID_AE_ROI_SET: { + struct hwm_cmd ae_roi_cmd; + + memcpy(&ae_roi_cmd, &set_ae_roi, sizeof(ae_roi_cmd)); + ae_roi_cmd.param1 = *((u16 *)ctrl->p_new.p_u16); + ae_roi_cmd.param2 = *((u16 *)ctrl->p_new.p_u16 + 1); + ae_roi_cmd.param3 = *((u16 *)ctrl->p_new.p_u16 + 2); + ae_roi_cmd.param4 = *((u16 *)ctrl->p_new.p_u16 + 3); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), + &ae_roi_cmd, false, NULL); + break; + } + case DS5_CAMERA_CID_AE_SETPOINT_SET: { + struct hwm_cmd *ae_setpoint_cmd; + + if (ctrl->p_new.p_s32) { + dev_dbg(&state->client->dev, "%s():0x%x \n", + __func__, *(ctrl->p_new.p_s32)); + ae_setpoint_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 4, GFP_KERNEL); + if (!ae_setpoint_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(ae_setpoint_cmd, &set_ae_setpoint, sizeof (set_ae_setpoint)); + memcpy(ae_setpoint_cmd->Data, (u8 *)ctrl->p_new.p_s32, 4); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd) + 4, + ae_setpoint_cmd, false, NULL); + devm_kfree(&state->client->dev, ae_setpoint_cmd); + } + break; + } + case DS5_CAMERA_CID_ERB: + if (ctrl->p_new.p_u8) { + u16 offset = 0; + u16 size = 0; + struct hwm_cmd *erb_cmd; + + offset = *(ctrl->p_new.p_u8) << 8; + offset |= *(ctrl->p_new.p_u8 + 1); + size = *(ctrl->p_new.p_u8 + 2) << 8; + size |= *(ctrl->p_new.p_u8 + 3); + + dev_dbg(&state->client->dev, "%s(): offset %x, size: %x\n", + __func__, offset, size); + + erb_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + size, GFP_KERNEL); + if (!erb_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(erb_cmd, &erb, sizeof(struct hwm_cmd)); + erb_cmd->param1 = offset; + erb_cmd->param2 = size; + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), + erb_cmd, true, &size); + + if (ret) { + dev_err(&state->client->dev, + "%s(): ERB cmd failed, ret: %d," + "requested size: %d, actual size: %d\n", + __func__, ret, erb_cmd->param2, size); + devm_kfree(&state->client->dev, erb_cmd); + return -EAGAIN; + } + + // Actual size returned from FW + *(ctrl->p_new.p_u8 + 2) = (size & 0xFF00) >> 8; + *(ctrl->p_new.p_u8 + 3) = (size & 0x00FF); + + memcpy(ctrl->p_new.p_u8 + 4, erb_cmd->Data + 4, size - 4); + dev_dbg(&state->client->dev, "%s(): 0x%x 0x%x 0x%x 0x%x \n", + __func__, + *(ctrl->p_new.p_u8), + *(ctrl->p_new.p_u8+1), + *(ctrl->p_new.p_u8+2), + *(ctrl->p_new.p_u8+3)); + devm_kfree(&state->client->dev, erb_cmd); + } + break; + case DS5_CAMERA_CID_EWB: + if (ctrl->p_new.p_u8) { + u16 offset = 0; + u16 size = 0; + struct hwm_cmd *ewb_cmd; + + offset = *((u8 *)ctrl->p_new.p_u8) << 8; + offset |= *((u8 *)ctrl->p_new.p_u8 + 1); + size = *((u8 *)ctrl->p_new.p_u8 + 2) << 8; + size |= *((u8 *)ctrl->p_new.p_u8 + 3); + + dev_dbg(&state->client->dev, "%s():0x%x 0x%x 0x%x 0x%x\n", + __func__, + *((u8 *)ctrl->p_new.p_u8), + *((u8 *)ctrl->p_new.p_u8 + 1), + *((u8 *)ctrl->p_new.p_u8 + 2), + *((u8 *)ctrl->p_new.p_u8 + 3)); + + ewb_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + size, + GFP_KERNEL); + if (!ewb_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(ewb_cmd, &ewb, sizeof(ewb)); + ewb_cmd->header = 0x14 + size; + ewb_cmd->param1 = offset; // start index + ewb_cmd->param2 = size; // size + memcpy(ewb_cmd->Data, (u8 *)ctrl->p_new.p_u8 + 4, size); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd) + size, + ewb_cmd, false, NULL); + + if (ret) { + dev_err(&state->client->dev, + "%s(): EWB cmd failed, ret: %d," + "requested size: %d, actual size: %d\n", + __func__, ret, ewb_cmd->param2, size); + devm_kfree(&state->client->dev, ewb_cmd); + return -EAGAIN; + } + + devm_kfree(&state->client->dev, ewb_cmd); + } + break; + case DS5_CAMERA_CID_HWMC: + if (ctrl->p_new.p_u8) { + u16 dataLen = 0; + u16 size = 0; + + size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8; + size |= *((u8 *)ctrl->p_new.p_u8 + 0); + dev_dbg(&state->client->dev, + "%s(): HWMC size %d\n", + __func__, size); + ret = ds5_send_hwmc(state, size + 4, + (struct hwm_cmd *)ctrl->p_new.p_u8, true, &dataLen); + } + break; + case DS5_CAMERA_CID_HWMC_RW: + if (ctrl->p_new.p_u8) { + u16 size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8; + + size |= *((u8 *)ctrl->p_new.p_u8 + 0); + ret = ds5_send_hwmc(state, size + 4, + (struct hwm_cmd *)ctrl->p_new.p_u8, false, NULL); + } + break; + case DS5_CAMERA_CID_PWM: + if (state->is_depth || state->is_depth_b) + ret = ds5_write(state, base | DS5_PWM_FREQUENCY, ctrl->val); + break; + case V4L2_CID_IPU_SET_SUB_STREAM: + val = (*ctrl->p_new.p_s64 & 0xFFFF); + dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x\n", val); + vc_id = (val >> 8) & 0x00FF; + on = val & 0x00FF; + if (vc_id < DS5_MUX_PAD_COUNT) + ret = ds5_s_state(state, vc_id); + if (on == 0xff) + break; + if (vc_id > NR_OF_DS5_STREAMS - 1) + dev_err(&state->client->dev, "invalid vc %d\n", vc_id); + else + d4xx_set_sub_stream[vc_id] = on; + + if (on == 0) + ret = ds5_s_state(state, vc_id); + + ret = ds5_mux_s_stream(sd, on); + break; + } + + mutex_unlock(&state->lock); + + return ret; +} + +static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, + unsigned char *table, unsigned int length) +{ + struct hwm_cmd *cmd; + int ret = -1; + int retries = 3; + u16 status = 2; + u16 table_length; + + cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + length + 4, GFP_KERNEL); + if (!cmd) { + dev_err(&state->client->dev, "%s(): Can't allocate memory\n", __func__); + return -ENOMEM; + } + + memcpy(cmd, &get_calib_data, sizeof(get_calib_data)); + cmd->param1 = id; + ds5_raw_write_with_check(state, 0x4900, cmd, sizeof(struct hwm_cmd)); + ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + do { + if (retries != 3) + msleep_range(10); + ret = ds5_read(state, 0x4904, &status); + } while (ret && retries-- && status != 0); + + if (ret || status != 0) { + dev_err(&state->client->dev, + "%s(): Failed to get calibration table %d, fw error: %x\n", + __func__, id, status); + devm_kfree(&state->client->dev, cmd); + return status; + } + + // get table length from fw + ret = regmap_raw_read(state->regmap, 0x4908, + &table_length, sizeof(table_length)); + + // read table + ds5_raw_read_with_check(state, 0x4900, cmd->Data, table_length); + + // first 4 bytes are opcode HWM, not part of calibration table + memcpy(table, cmd->Data + 4, length); + devm_kfree(&state->client->dev, cmd); + return 0; +} + +static int ds5_gvd(struct ds5 *state, unsigned char *data) +{ + struct hwm_cmd cmd; + int ret = -1; + u16 length = 0; + u16 status = 2; + u8 retries = 3; + + memcpy(&cmd, &gvd, sizeof(gvd)); + ds5_raw_write_with_check(state, 0x4900, &cmd, sizeof(cmd)); + ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + do { + if (retries != 3) + msleep_range(10); + + ret = ds5_read(state, 0x4904, &status); + } while (ret && retries-- && status != 0); + + if (ret || status != 0) { + dev_err(&state->client->dev, + "%s(): Failed to read GVD, HWM cmd status: %x\n", + __func__, status); + return status; + } + + ret = regmap_raw_read(state->regmap, 0x4908, &length, sizeof(length)); + ds5_raw_read_with_check(state, 0x4900, data, length); + + return ret; +} + +static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ds5 *state = container_of(ctrl->handler, struct ds5, + ctrls.handler); + u16 log_prepare[] = {0x0014, 0xcdab, 0x000f, 0x0000, 0x0400, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + u16 execute_cmd = 0x0001; + unsigned int i; + u32 data; + int ret = 0; + struct ds5_sensor *sensor = (struct ds5_sensor *)ctrl->priv; + u16 base = (state->is_rgb) ? DS5_RGB_CONTROL_BASE : DS5_DEPTH_CONTROL_BASE; + u16 reg; + + if (sensor) { + switch (sensor->mux_pad) { + case DS5_MUX_PAD_DEPTH_B: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth_b); + state->is_rgb = 0; + state->is_rgb_b = 0; + state->is_depth = 0; + state->is_depth_b = 1; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB_B: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb_b); + state->is_rgb_b = 1; + state->is_depth = 0; + state->is_rgb = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_DEPTH_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); + state->is_rgb = 0; + state->is_depth = 1; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); + state->is_rgb = 1; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_MOTION_T_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 1; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IMU_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + default: + state->is_rgb = 0; + state->is_depth = 0; + state->is_rgb_b = 0; + state->is_depth_b = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + + } + } + base = (state->is_rgb || state->is_rgb_b) ? DS5_RGB_CONTROL_BASE : DS5_DEPTH_CONTROL_BASE; + + dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s \n", + __func__, ds5_get_sensor_name(state), ctrl->name); + + switch (ctrl->id) { + + case V4L2_CID_ANALOGUE_GAIN: + if (state->is_imu) + return -EINVAL; + ret = ds5_read(state, base | DS5_MANUAL_GAIN, ctrl->p_new.p_u16); + break; + + case V4L2_CID_EXPOSURE_AUTO: + if (state->is_imu) + return -EINVAL; + ds5_read(state, base | DS5_AUTO_EXPOSURE_MODE, ®); + *ctrl->p_new.p_u16 = reg; + /* see ds5_hw_set_auto_exposure */ + if (!(state->is_rgb & state->is_rgb_b)) { + if (reg == 1) + *ctrl->p_new.p_u16 = V4L2_EXPOSURE_APERTURE_PRIORITY; + else if (reg == 0) + *ctrl->p_new.p_u16 = V4L2_EXPOSURE_MANUAL; + } + + if ((state->is_rgb || state->is_rgb_b) && reg == 8) + *ctrl->p_new.p_u16 = V4L2_EXPOSURE_APERTURE_PRIORITY; + + break; + + case V4L2_CID_EXPOSURE_ABSOLUTE: + if (state->is_imu) + return -EINVAL; + /* see ds5_hw_set_exposure */ + ds5_read(state, base | DS5_MANUAL_EXPOSURE_MSB, ®); + data = ((u32)reg << 16) & 0xffff0000; + ds5_read(state, base | DS5_MANUAL_EXPOSURE_LSB, ®); + data |= reg; + *ctrl->p_new.p_u32 = data; + break; + + case DS5_CAMERA_CID_LASER_POWER: + if (!(state->is_rgb & state->is_rgb_b)) + ds5_read(state, base | DS5_LASER_POWER, ctrl->p_new.p_u16); + break; + + case DS5_CAMERA_CID_MANUAL_LASER_POWER: + if (!(state->is_rgb & state->is_rgb_b)) + ds5_read(state, base | DS5_MANUAL_LASER_POWER, ctrl->p_new.p_u16); + break; + + case DS5_CAMERA_CID_LOG: + // TODO: wrap HWMonitor command + // 1. prepare and send command + // 2. send command + // 3. execute command + // 4. wait for ccompletion + ret = regmap_raw_write(state->regmap, 0x4900, + log_prepare, sizeof(log_prepare)); + if (ret < 0) + return ret; + + ret = regmap_raw_write(state->regmap, 0x490C, + &execute_cmd, sizeof(execute_cmd)); + if (ret < 0) + return ret; + + for (i = 0; i < DS5_MAX_LOG_POLL; i++) { + ret = regmap_raw_read(state->regmap, 0x4904, + &data, sizeof(data)); + dev_dbg(&state->client->dev, "%s(): log ready 0x%x\n", + __func__, data); + if (ret < 0) + return ret; + if (!data) + break; + msleep_range(5); + } + +// if (i == DS5_MAX_LOG_POLL) +// return -ETIMEDOUT; + + ret = regmap_raw_read(state->regmap, 0x4908, + &data, sizeof(data)); + dev_dbg(&state->client->dev, "%s(): log size 0x%x\n", + __func__, data); + if (ret < 0) + return ret; + if (!data) + return 0; + if (data > 1024) + return -ENOBUFS; + ret = regmap_raw_read(state->regmap, 0x4900, + ctrl->p_new.p_u8, data); + break; + case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_GET: + ret = ds5_get_calibration_data(state, DEPTH_CALIBRATION_ID, + ctrl->p_new.p_u8, 256); + break; + case DS5_CAMERA_COEFF_CALIBRATION_TABLE_GET: + ret = ds5_get_calibration_data(state, COEF_CALIBRATION_ID, + ctrl->p_new.p_u8, 512); + break; + case DS5_CAMERA_CID_FW_VERSION: + ret = ds5_read(state, DS5_FW_VERSION, &state->fw_version); + ret = ds5_read(state, DS5_FW_BUILD, &state->fw_build); + *ctrl->p_new.p_u32 = state->fw_version << 16; + *ctrl->p_new.p_u32 |= state->fw_build; + break; + case DS5_CAMERA_CID_GVD: + ret = ds5_gvd(state, ctrl->p_new.p_u8); + break; + case DS5_CAMERA_CID_AE_ROI_GET: { + u16 len = 0; + struct hwm_cmd *ae_roi_cmd; + ae_roi_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 12, GFP_KERNEL); + if (!ae_roi_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(ae_roi_cmd, &get_ae_roi, sizeof(struct hwm_cmd)); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), + ae_roi_cmd, true, &len); + memcpy(ctrl->p_new.p_u16, ae_roi_cmd->Data + 4, 8); + devm_kfree(&state->client->dev, ae_roi_cmd); + } + break; + case DS5_CAMERA_CID_AE_SETPOINT_GET: { + u16 len = 0; + struct hwm_cmd *ae_setpoint_cmd; + ae_setpoint_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 8, GFP_KERNEL); + if (!ae_setpoint_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(ae_setpoint_cmd, &get_ae_setpoint, sizeof(struct hwm_cmd)); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), + ae_setpoint_cmd, true, &len); + memcpy(ctrl->p_new.p_s32, ae_setpoint_cmd->Data + 4, 4); + dev_dbg(&state->client->dev, "%s(): 0x%x \n", + __func__, + *(ctrl->p_new.p_s32)); + devm_kfree(&state->client->dev, ae_setpoint_cmd); + } + break; + case DS5_CAMERA_CID_HWMC_RW: + ds5_get_hwmc(state, ctrl->p_new.p_u8); + break; + case DS5_CAMERA_CID_PWM: + if (state->is_depth || state->is_depth_b) + ds5_read(state, base | DS5_PWM_FREQUENCY, ctrl->p_new.p_u16); + break; + case V4L2_CID_IPU_QUERY_SUB_STREAM: { + if (sensor) { + int vc_id = get_sub_stream_vc_id(state->ctrls.query_sub_stream->qmenu_int, + pad_to_substream[sensor->mux_pad]); + + dev_dbg(sensor->sd.dev, + "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d vc:[%d]\n", + __func__, sensor->mux_pad, vc_id); + *ctrl->p_new.p_s32 = pad_to_substream[sensor->mux_pad]; + state->mux.last_set = sensor; + } else { + /* we are in DS5 MUX case */ + *ctrl->p_new.p_s32 = -1; + } + } + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops ds5_ctrl_ops = { + .s_ctrl = ds5_s_ctrl, + .g_volatile_ctrl = ds5_g_volatile_ctrl, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_log = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_LOG, + .name = "Logger", + .type = V4L2_CTRL_TYPE_U8, + .dims = {1024}, + .elem_size = sizeof(u8), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_laser_power = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_LASER_POWER, + .name = "Laser power on/off", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_manual_laser_power = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_MANUAL_LASER_POWER, + .name = "Manual laser power", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 360, + .step = 30, + .def = 150, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_fw_version = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_FW_VERSION, + .name = "fw version", + .type = V4L2_CTRL_TYPE_U32, + .dims = {1}, + .elem_size = sizeof(u32), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_gvd = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_GVD, + .name = "GVD", + .type = V4L2_CTRL_TYPE_U8, + .dims = {239}, + .elem_size = sizeof(u8), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_get_depth_calib = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_DEPTH_CALIBRATION_TABLE_GET, + .name = "get depth calib", + .type = V4L2_CTRL_TYPE_U8, + .dims = {256}, + .elem_size = sizeof(u8), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_set_depth_calib = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET, + .name = "set depth calib", + .type = V4L2_CTRL_TYPE_U8, + .dims = {256}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_get_coeff_calib = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_COEFF_CALIBRATION_TABLE_GET, + .name = "get coeff calib", + .type = V4L2_CTRL_TYPE_U8, + .dims = {512}, + .elem_size = sizeof(u8), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_set_coeff_calib = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET, + .name = "set coeff calib", + .type = V4L2_CTRL_TYPE_U8, + .dims = {512}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_ae_roi_get = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_AE_ROI_GET, + .name = "ae roi get", + .type = V4L2_CTRL_TYPE_U8, + .dims = {8}, + .elem_size = sizeof(u16), + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_ae_roi_set = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_AE_ROI_SET, + .name = "ae roi set", + .type = V4L2_CTRL_TYPE_U8, + .dims = {8}, + .elem_size = sizeof(u16), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_ae_setpoint_get = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_AE_SETPOINT_GET, + .name = "ae setpoint get", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_ae_setpoint_set = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_AE_SETPOINT_SET, + .name = "ae setpoint set", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 4095, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_erb = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_ERB, + .name = "ERB eeprom read", + .type = V4L2_CTRL_TYPE_U8, + .dims = {1020}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_ewb = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_EWB, + .name = "EWB eeprom write", + .type = V4L2_CTRL_TYPE_U8, + .dims = {1020}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_hwmc = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_HWMC, + .name = "HWMC", + .type = V4L2_CTRL_TYPE_U8, + .dims = {DS5_HWMC_BUFFER_SIZE + 4}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_hwmc_rw = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_HWMC_RW, + .name = "HWMC_RW", + .type = V4L2_CTRL_TYPE_U8, + .dims = {DS5_HWMC_BUFFER_SIZE}, + .elem_size = sizeof(u8), + .min = 0, + .max = 0xFFFFFFFF, + .def = 240, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, +}; + +static const struct v4l2_ctrl_config ds5_ctrl_pwm = { + .ops = &ds5_ctrl_ops, + .id = DS5_CAMERA_CID_PWM, + .name = "PWM Frequency Selector", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, +}; + +static const struct v4l2_ctrl_config d4xx_controls_link_freq = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(link_freq_menu_items) - 1, + .min = 0, + .step = 0, + .def = 1, + .qmenu_int = link_freq_menu_items, +}; + +static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_a = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + .name = "query virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(d4xx_query_sub_stream[0]) - 1, + .min = 0, + .def = 0, + .menu_skip_mask = 0, + .qmenu_int = d4xx_query_sub_stream[0], +}; + +static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_b = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + .name = "query virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(d4xx_query_sub_stream[1]) - 1, + .min = 0, + .def = 0, + .menu_skip_mask = 0, + .qmenu_int = d4xx_query_sub_stream[1], +}; + +static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_c = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + .name = "query virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(d4xx_query_sub_stream[2]) - 1, + .min = 0, + .def = 0, + .menu_skip_mask = 0, + .qmenu_int = d4xx_query_sub_stream[2], +}; + +static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_d = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + .name = "query virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(d4xx_query_sub_stream[3]) - 1, + .min = 0, + .def = 0, + .menu_skip_mask = 0, + .qmenu_int = d4xx_query_sub_stream[3], +}; + +static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_e = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + .name = "query virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(d4xx_query_sub_stream[4]) - 1, + .min = 0, + .def = 0, + .menu_skip_mask = 0, + .qmenu_int = d4xx_query_sub_stream[4], +}; + +static const struct v4l2_ctrl_config d4xx_controls_s_sub_stream = { + .ops = &ds5_ctrl_ops, + .id = V4L2_CID_IPU_SET_SUB_STREAM, + .name = "set virtual channel", + .type = V4L2_CTRL_TYPE_INTEGER64, + .max = 0xFFFF, + .min = 0, + .def = 0, + .step = 1, +}; + +static int ds5_mux_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ds5 *state = v4l2_get_subdevdata(sd); + + dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + if (state->dfu_dev.dfu_state_flag) + return -EBUSY; + state->dfu_dev.device_open_count++; + + return 0; +}; + +static int ds5_mux_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ds5 *state = v4l2_get_subdevdata(sd); + + dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + state->dfu_dev.device_open_count--; + return 0; +}; + +static const struct v4l2_subdev_internal_ops ds5_sensor_internal_ops = { + .open = ds5_mux_open, + .close = ds5_mux_close, +}; + +static int ds5_ctrl_init(struct ds5 *state, int sid) +{ + const struct v4l2_ctrl_ops *ops = &ds5_ctrl_ops; + struct ds5_ctrls *ctrls = &state->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_subdev *sd = &state->mux.sd.subdev; + int ret = -1; + char d4xx_name; + struct ds5_sensor *sensor = NULL; + + switch (sid) { + case DS5_SID_DEPTH_A: + hdl = &ctrls->handler_depth; + sensor = &state->depth.sensor; + break; + case DS5_SID_RGB_A: + hdl = &ctrls->handler_rgb; + sensor = &state->rgb.sensor; + break; + case DS5_SID_MOTION_T_A: + hdl = &ctrls->handler_y8; + sensor = &state->motion_t.sensor; + break; + case DS5_SID_IMU_A: + hdl = &ctrls->handler_imu; + sensor = &state->imu.sensor; + break; + case DS5_SID_DEPTH_B: + hdl = &ctrls->handler_depth_b; + sensor = &state->depth_b.sensor; + break; + case DS5_SID_RGB_B: + hdl = &ctrls->handler_rgb_b; + sensor = &state->rgb_b.sensor; + break; + default: + hdl = &ctrls->handler; + sensor = NULL; + break; + } + + dev_info(NULL, "%s(), line %d sid: %d\n", __func__, __LINE__, sid); + ret = v4l2_ctrl_handler_init(hdl, DS5_N_CONTROLS); + if (ret < 0) { + v4l2_err(sd, "cannot init ctrl handler (%d)\n", ret); + return ret; + } + + if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + ctrls->laser_power = v4l2_ctrl_new_custom(hdl, + &ds5_ctrl_laser_power, + sensor); + ctrls->manual_laser_power = v4l2_ctrl_new_custom(hdl, + &ds5_ctrl_manual_laser_power, + sensor); + } + + /* Total gain */ + if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_ANALOGUE_GAIN, + 16, 248, 1, 16); + } else if (sid == DS5_SID_RGB_A || sid == DS5_SID_RGB_B) { + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_ANALOGUE_GAIN, + 0, 128, 1, 64); + } + + if ((ctrls->gain) && (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B)) { + ctrls->gain->priv = sensor; + ctrls->gain->flags = + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + } + if (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B) { + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_APERTURE_PRIORITY, + ~((1 << V4L2_EXPOSURE_MANUAL) | + (1 << V4L2_EXPOSURE_APERTURE_PRIORITY)), + V4L2_EXPOSURE_APERTURE_PRIORITY); + + if (ctrls->auto_exp) { + ctrls->auto_exp->flags |= + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + ctrls->auto_exp->priv = sensor; + } + } + /* Exposure time: V4L2_CID_EXPOSURE_ABSOLUTE default unit: 100 us. */ + if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + 1, MAX_DEPTH_EXP, 1, DEF_DEPTH_EXP); + } else if (sid == DS5_SID_RGB_A || sid == DS5_SID_RGB_B) { + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + 1, MAX_RGB_EXP, 1, DEF_RGB_EXP); + } + + if ((ctrls->exposure) && (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B)) { + ctrls->exposure->priv = sensor; + ctrls->exposure->flags |= + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + /* override default int type to u32 to match SKU & UVC */ + ctrls->exposure->type = V4L2_CTRL_TYPE_U32; + } + + ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); + dev_info(sd->dev, "%s(): %p\n", __func__, ctrls->link_freq); + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + d4xx_name = (sd->name)[strlen(sd->name)-1]; + dev_info(sd->dev, "%s(): d4xx_name is %c\n", __func__, d4xx_name); + switch (d4xx_name) { + case 'a': + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_a, sensor); + break; + + case 'b': + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_b, sensor); + break; + + case 'c': + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_c, sensor); + break; + + case 'd': + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_d, sensor); + break; + + case 'e': + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_e, sensor); + break; + + default: + dev_err(state->rgb.sensor.sd.dev, + "%s():!! can not get d4xx_controls_q_sub_stream: sd->name is %s\n", + __func__, sd->name); + break; + } + ctrls->query_sub_stream->flags |= + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + + ctrls->set_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_s_sub_stream, sensor); + dev_info(NULL, "%s(), line %d\n", __func__, __LINE__); + if (hdl->error) { + v4l2_err(sd, "error creating controls (%d)\n", hdl->error); + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + // ret = v4l2_ctrl_handler_setup(hdl); + // if (ret < 0) { + // dev_err(&state->client->dev, + // "failed to set default values for controls\n"); + // v4l2_ctrl_handler_free(hdl); + // return ret; + // } + + // Add these after v4l2_ctrl_handler_setup so they won't be set up + if (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B) { + ctrls->log = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_log, sensor); + ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); + ctrls->gvd = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_gvd, sensor); + ctrls->get_depth_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_depth_calib, sensor); + ctrls->set_depth_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_depth_calib, sensor); + ctrls->get_coeff_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_coeff_calib, sensor); + ctrls->set_coeff_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_coeff_calib, sensor); + ctrls->ae_roi_get = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_get, sensor); + ctrls->ae_roi_set = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_set, sensor); + ctrls->ae_setpoint_get = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_get, sensor); + ctrls->ae_setpoint_set = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_set, sensor); + ctrls->erb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_erb, sensor); + ctrls->ewb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ewb, sensor); + ctrls->hwmc = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc, sensor); + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor); + } + // DEPTH custom + if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_DEPTH_B) + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_pwm, sensor); + // IMU custom + if (sid == DS5_SID_IMU_A) + ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); + + switch (sid) { + case DS5_SID_DEPTH_A: + state->depth.sensor.sd.ctrl_handler = hdl; + dev_info(state->depth.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->depth.sensor.mux_pad); + break; + case DS5_SID_RGB_A: + state->rgb.sensor.sd.ctrl_handler = hdl; + dev_info(state->rgb.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->rgb.sensor.mux_pad); + break; + case DS5_SID_MOTION_T_A: + state->motion_t.sensor.sd.ctrl_handler = hdl; + dev_info(state->motion_t.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->motion_t.sensor.mux_pad); + break; + case DS5_SID_IMU_A: + state->imu.sensor.sd.ctrl_handler = hdl; + dev_info(state->imu.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->imu.sensor.mux_pad); + break; + case DS5_SID_DEPTH_B: + state->depth_b.sensor.sd.ctrl_handler = hdl; + dev_info(state->depth_b.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->depth_b.sensor.mux_pad); + break; + case DS5_SID_RGB_B: + state->rgb_b.sensor.sd.ctrl_handler = hdl; + dev_info(state->rgb_b.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->rgb_b.sensor.mux_pad); + break; + default: + state->mux.sd.subdev.ctrl_handler = hdl; + dev_info(state->mux.sd.subdev.dev, + "%s():%d set ctrl_handler for MUX\n", __func__, __LINE__); + break; + } + + return 0; +} + +static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, + struct ds5_sensor *sensor, const struct v4l2_subdev_ops *ops, + const char *name) +{ + struct v4l2_subdev *sd = &sensor->sd; + struct media_entity *entity = &sensor->sd.entity; + struct media_pad *pad = &sensor->pad; + dev_t *dev_num = &state->client->dev.devt; + struct d4xx_pdata *dpdata = c->dev.platform_data; + + v4l2_i2c_subdev_init(sd, c, ops); + sd->owner = NULL; + sd->internal_ops = &ds5_sensor_internal_ops; + sd->grp_id = *dev_num; + v4l2_set_subdevdata(sd, state); + /* + * TODO: suffix for 2 D457 connected to 1 Deser + */ + dev_dbg(&state->client->dev, "%s() sensor->mux_pad = %d\n", __func__, sensor->mux_pad); + + if (sensor->mux_pad >= DS5_MUX_PAD_DEPTH_A && sensor->mux_pad <= DS5_MUX_PAD_IMU_A) + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[0].suffix); + else if (sensor->mux_pad == DS5_MUX_PAD_RGB_B || sensor->mux_pad == DS5_MUX_PAD_DEPTH_B) + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[1].suffix); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pad->flags = MEDIA_PAD_FL_SOURCE; + entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; + entity->function = MEDIA_ENT_F_CAM_SENSOR; + return media_entity_pads_init(entity, 1, pad); +} + +static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) +{ + struct v4l2_subdev *sd = &sensor->sd; + struct media_entity *entity = &sensor->sd.entity; + int ret = -1; + + dev_dbg(sd->dev, "%s(): sd->name = %s sensor->mux_pad = %d\n", __func__, sd->name, sensor->mux_pad); + // FIXME: is async needed? + ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); + if (ret < 0) { + dev_err(sd->dev, "%s(): %d: %d\n", __func__, __LINE__, ret); + return ret; + } + + ret = media_create_pad_link(entity, 0, + &state->mux.sd.subdev.entity, sensor->mux_pad, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + dev_err(sd->dev, "%s(): %d: %d\n", __func__, __LINE__, ret); + goto e_sd; + } + + dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); + + return 0; + +e_sd: + v4l2_device_unregister_subdev(sd); + + return ret; +} + +static void ds5_sensor_remove(struct ds5_sensor *sensor) +{ + v4l2_device_unregister_subdev(&sensor->sd); + + media_entity_cleanup(&sensor->sd.entity); +} + +static int ds5_depth_init(struct i2c_client *c, struct ds5 *state) +{ + /* Which mux pad we're connecting to */ + state->depth.sensor.mux_pad = DS5_MUX_PAD_DEPTH_A; + return ds5_sensor_init(c, state, &state->depth.sensor, + &ds5_depth_subdev_ops, "depth"); +} + +static int ds5_depth_b_init(struct i2c_client *c, struct ds5 *state) +{ + /* Which mux pad we're connecting to */ + state->depth_b.sensor.mux_pad = DS5_MUX_PAD_DEPTH_B; + return ds5_sensor_init(c, state, &state->depth_b.sensor, + &ds5_depth_subdev_ops, "depth"); +} + +static int ds5_motion_t_init(struct i2c_client *c, struct ds5 *state) +{ + state->motion_t.sensor.mux_pad = DS5_MUX_PAD_MOTION_T_A; + return ds5_sensor_init(c, state, &state->motion_t.sensor, + &ds5_motion_t_subdev_ops, "motion detection"); +} + +static int ds5_rgb_init(struct i2c_client *c, struct ds5 *state) +{ + state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB_A; + return ds5_sensor_init(c, state, &state->rgb.sensor, + &ds5_rgb_subdev_ops, "rgb"); +} + +static int ds5_rgb_b_init(struct i2c_client *c, struct ds5 *state) +{ + state->rgb_b.sensor.mux_pad = DS5_MUX_PAD_RGB_B; + return ds5_sensor_init(c, state, &state->rgb_b.sensor, + &ds5_rgb_subdev_ops, "rgb"); +} + +static int ds5_imu_init(struct i2c_client *c, struct ds5 *state) +{ + state->imu.sensor.mux_pad = DS5_MUX_PAD_IMU_A; + return ds5_sensor_init(c, state, &state->imu.sensor, + &ds5_imu_subdev_ops, "imu"); +} + +/* No locking needed */ +static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_mbus_code_enum *mce) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct v4l2_subdev_mbus_code_enum tmp = *mce; + struct v4l2_subdev *remote_sd; + int ret = -1; + + dev_info(&state->client->dev, "%s(): %s \n", __func__, sd->name); + switch (mce->pad) { + case DS5_MUX_PAD_DEPTH_B: + remote_sd = &state->depth_b.sensor.sd; + break; + case DS5_MUX_PAD_RGB_B: + remote_sd = &state->rgb_b.sensor.sd; + break; + case DS5_MUX_PAD_MOTION_T_A: + remote_sd = &state->motion_t.sensor.sd; + break; + case DS5_MUX_PAD_DEPTH_A: + remote_sd = &state->depth.sensor.sd; + break; + case DS5_MUX_PAD_RGB_A: + remote_sd = &state->rgb.sensor.sd; + break; + case DS5_MUX_PAD_IMU_A: + remote_sd = &state->imu.sensor.sd; + break; + case DS5_MUX_PAD_EXTERNAL: + if (mce->index >= state->motion_t.sensor.n_formats + + state->depth.sensor.n_formats) + return -EINVAL; + + /* + * First list Left node / Motion Tracker formats, then depth. + * This should also help because D16 doesn't have a direct + * analog in MIPI CSI-2. + */ + if (mce->index < state->motion_t.sensor.n_formats) { + remote_sd = &state->motion_t.sensor.sd; + } else { + tmp.index = mce->index - state->motion_t.sensor.n_formats; + remote_sd = &state->depth.sensor.sd; + } + + break; + default: + return -EINVAL; + } + + tmp.pad = 0; + if (state->is_rgb) + remote_sd = &state->rgb.sensor.sd; + if (state->is_depth) + remote_sd = &state->depth.sensor.sd; + if (state->is_rgb_b) + remote_sd = &state->rgb_b.sensor.sd; + if (state->is_depth_b) + remote_sd = &state->depth_b.sensor.sd; + if (state->is_y8) + remote_sd = &state->motion_t.sensor.sd; + if (state->is_imu) + remote_sd = &state->imu.sensor.sd; + + /* Locks internally */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + ret = ds5_sensor_enum_mbus_code(remote_sd, cfg, &tmp); + struct v4l2_subdev_pad_config *cfg, +#else + ret = ds5_sensor_enum_mbus_code(remote_sd, v4l2_state, &tmp); +#endif + if (!ret) + mce->code = tmp.code; + + return ret; +} + +/* No locking needed */ +static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct v4l2_subdev_frame_size_enum tmp = *fse; + struct v4l2_subdev *remote_sd; + u32 pad = fse->pad; + int ret = -1; + + tmp.pad = 0; + + if (state->is_depth) + pad = DS5_MUX_PAD_DEPTH_A; + if (state->is_y8) + pad = DS5_MUX_PAD_MOTION_T_A; + if (state->is_rgb) + pad = DS5_MUX_PAD_RGB_A; + if (state->is_imu) + pad = DS5_MUX_PAD_IMU_A; + if (state->is_depth_b) + pad = DS5_MUX_PAD_DEPTH_B; + if (state->is_rgb_b) + pad = DS5_MUX_PAD_RGB_B; + + switch (pad) { + case DS5_MUX_PAD_MOTION_T_A: + remote_sd = &state->motion_t.sensor.sd; + break; + case DS5_MUX_PAD_DEPTH_A: + remote_sd = &state->depth.sensor.sd; + break; + case DS5_MUX_PAD_RGB_A: + remote_sd = &state->rgb.sensor.sd; + break; + case DS5_MUX_PAD_DEPTH_B: + remote_sd = &state->depth_b.sensor.sd; + break; + case DS5_MUX_PAD_RGB_B: + remote_sd = &state->rgb_b.sensor.sd; + break; + case DS5_MUX_PAD_IMU_A: + remote_sd = &state->imu.sensor.sd; + break; + case DS5_MUX_PAD_EXTERNAL: + /* + * Assume, that different sensors don't support the same formats + * Try the Depth sensor first, then the Motion Tracker + */ + remote_sd = &state->depth.sensor.sd; + ret = ds5_sensor_enum_frame_size(remote_sd, NULL, &tmp); + if (!ret) { + *fse = tmp; + fse->pad = pad; + return 0; + } + + remote_sd = &state->motion_t.sensor.sd; + break; + default: + return -EINVAL; + } + + /* Locks internally */ + ret = ds5_sensor_enum_frame_size(remote_sd, NULL, &tmp); + if (!ret) { + *fse = tmp; + fse->pad = pad; + } + + return ret; +} + +/* No locking needed */ +static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct v4l2_subdev_frame_interval_enum tmp = *fie; + struct v4l2_subdev *remote_sd; + u32 pad = fie->pad; + int ret = -1; + + tmp.pad = 0; + + dev_err(state->depth.sensor.sd.dev, + "%s(): pad %d code %x width %d height %d\n", + __func__, pad, tmp.code, tmp.width, tmp.height); + + if (state->is_depth) + pad = DS5_MUX_PAD_DEPTH_A; + if (state->is_y8) + pad = DS5_MUX_PAD_MOTION_T_A; + if (state->is_rgb) + pad = DS5_MUX_PAD_RGB_A; + if (state->is_imu) + pad = DS5_MUX_PAD_IMU_A; + if (state->is_depth_b) + pad = DS5_MUX_PAD_DEPTH_B; + if (state->is_rgb_b) + pad = DS5_MUX_PAD_RGB_B; + + switch (pad) { + case DS5_MUX_PAD_DEPTH_B: + remote_sd = &state->depth_b.sensor.sd; + break; + case DS5_MUX_PAD_RGB_B: + remote_sd = &state->rgb_b.sensor.sd; + break; + case DS5_MUX_PAD_MOTION_T_A: + remote_sd = &state->motion_t.sensor.sd; + break; + case DS5_MUX_PAD_DEPTH_A: + remote_sd = &state->depth.sensor.sd; + break; + case DS5_MUX_PAD_RGB_A: + remote_sd = &state->rgb.sensor.sd; + break; + case DS5_MUX_PAD_IMU_A: + remote_sd = &state->imu.sensor.sd; + break; + case DS5_MUX_PAD_EXTERNAL: + /* Similar to ds5_mux_enum_frame_size() above */ + if (state->is_rgb) + remote_sd = &state->rgb.sensor.sd; + else if (state->is_rgb_b) + remote_sd = &state->rgb_b.sensor.sd; + else + remote_sd = &state->motion_t.sensor.sd; + ret = ds5_sensor_enum_frame_interval(remote_sd, NULL, &tmp); + if (!ret) { + *fie = tmp; + fie->pad = pad; + return 0; + } + + remote_sd = &state->motion_t.sensor.sd; + break; + default: + return -EINVAL; + } + + /* Locks internally */ + ret = ds5_sensor_enum_frame_interval(remote_sd, NULL, &tmp); + if (!ret) { + *fie = tmp; + fie->pad = pad; + } + + return ret; +} + +/* No locking needed */ +static int ds5_mux_set_fmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct v4l2_mbus_framefmt *ffmt; + struct ds5_sensor *sensor = state->mux.last_set; + u32 pad = sensor->mux_pad; + s64 *query_sub_stream = NULL; + int ret = 0; + int substream = -1; + if (pad != DS5_MUX_PAD_EXTERNAL) + ds5_s_state(state, pad - 1); + sensor = state->mux.last_set; + switch (pad) { + case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH_B: + case DS5_MUX_PAD_MOTION_T_A: + case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB_B: + case DS5_MUX_PAD_IMU_A: + //ffmt = &ds5_ffmts[pad]; + ffmt = &sensor->format;//ds5_ffmts[pad]; + break; + case DS5_MUX_PAD_EXTERNAL: + ffmt = &ds5_ffmts[pad]; + break; + default: + return -EINVAL; + } + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = fmt->format.code; + } + fmt->format = *ffmt; + + // substream = pad_to_substream[fmt->pad]; + substream = pad_to_substream[pad]; + + if (substream != -1) { + query_sub_stream = match_d4xx_sub_stream_qmenu(state->ctrls.query_sub_stream->qmenu_int); + if (query_sub_stream) { + set_sub_stream_fmt(query_sub_stream, substream, ffmt->code); + set_sub_stream_h(query_sub_stream, substream, ffmt->height); + set_sub_stream_w(query_sub_stream, substream, ffmt->width); + set_sub_stream_dt(query_sub_stream, substream, mbus_code_to_mipi(ffmt->code)); + } else { + dev_err(sd->dev, + "failed to find sub stream fmt info: %s\n", + sensor->sd.name); + } + } + + dev_info(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad: %d, \ + code: 0x%x: %ux%u substream:%d for sensor: %s\n", + __func__, + fmt->pad, pad, fmt->format.code, + fmt->format.width, fmt->format.height, + substream, sensor->sd.name); + + return ret; +} + +/* No locking needed */ +static int ds5_mux_get_fmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct v4l2_subdev_pad_config *cfg, +#else + struct v4l2_subdev_state *v4l2_state, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + // u32 pad = fmt->pad; + int ret = 0; + struct ds5_sensor *sensor = state->mux.last_set; + u32 pad = sensor->mux_pad; + if (pad != DS5_MUX_PAD_EXTERNAL) + ds5_s_state(state, pad - 1); + sensor = state->mux.last_set; + dev_info(sd->dev, "%s(): %u %s %p\n", __func__, pad, ds5_get_sensor_name(state), state->mux.last_set); + + switch (pad) { + case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH_B: + case DS5_MUX_PAD_MOTION_T_A: + case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB_B: + case DS5_MUX_PAD_IMU_A: + //fmt->format = ds5_ffmts[pad]; + fmt->format = sensor->format;//ds5_ffmts[pad]; + break; + case DS5_MUX_PAD_EXTERNAL: + fmt->format = ds5_ffmts[pad]; + break; + default: + return -EINVAL; + } + + dev_dbg(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad:%u size:%d-%d, code:0x%x field:%d, color:%d\n", + __func__, fmt->pad, pad, + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.field, fmt->format.colorspace); + return ret; +} + +/* Video ops */ +static int ds5_mux_g_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + struct v4l2_subdev_frame_interval *fi) +#else + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +#endif +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct ds5_sensor *sensor = NULL; + + if (NULL == sd || NULL == fi) + return -EINVAL; + + sensor = state->mux.last_set; + + fi->interval.numerator = 1; + fi->interval.denominator = sensor->config.framerate; + + dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, + fi->interval.denominator); + + return 0; +} + +static u16 __ds5_probe_framerate(const struct ds5_resolution *res, u16 target) +{ + int i; + u16 framerate; + + for (i = 0; i < res->n_framerates; i++) { + framerate = res->framerates[i]; + if (target <= framerate) + return framerate; + } + + return res->framerates[res->n_framerates - 1]; +} + +static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + struct v4l2_subdev_frame_interval *fi) +#else + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +#endif +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct ds5_sensor *sensor = NULL; + u16 framerate = 1; + + if (NULL == sd || NULL == fi || fi->interval.numerator == 0) + return -EINVAL; + + sensor = state->mux.last_set; + + framerate = fi->interval.denominator / fi->interval.numerator; + framerate = __ds5_probe_framerate(sensor->config.resolution, framerate); + sensor->config.framerate = framerate; + fi->interval.numerator = 1; + fi->interval.denominator = framerate; + + dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); + + return 0; +} + +static int d4xx_reset_oneshot(struct ds5 *state) +{ + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + struct i2c_board_info *deser = dpdata->deser_board_info; + + int s_addr = state->client->addr; + int n_addr = deser->addr; + int ret = 0; + u8 val = 0; + + if (n_addr) { + state->client->addr = n_addr; + dev_warn(&state->client->dev, + "One-shot reset n_addr = 0x%x s_addr=0x%x\n", n_addr, s_addr); + /* One-shot reset enable auto-link */ + /* 0x23 a+a case*/ + ret = max9296_read_8(state, MAX9296_CTRL0, &val); + dev_dbg(&state->client->dev, "read current mode: %d\n", val); + ret = max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | val); + state->client->addr = s_addr; + /* delay to settle link */ + msleep(100); + } + + return ret; +} + +static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + u16 streaming, status; + int ret = 0; + unsigned int i = 0; + int restore_val = 0; + u16 config_status_base, stream_status_base, stream_id, vc_id; + struct ds5_sensor *sensor; + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + + // spare duplicate calls + if (state->mux.last_set->streaming == on) + return 0; + if (state->is_depth) { + sensor = &state->depth.sensor; + config_status_base = DS5_DEPTH_CONFIG_STATUS; + stream_status_base = DS5_DEPTH_STREAM_STATUS; + stream_id = DS5_STREAM_DEPTH; + vc_id = 0; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_rgb) { + sensor = &state->rgb.sensor; + config_status_base = DS5_RGB_CONFIG_STATUS; + stream_status_base = DS5_RGB_STREAM_STATUS; + stream_id = DS5_STREAM_RGB; + vc_id = 1; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_depth_b) { + sensor = &state->depth_b.sensor; + config_status_base = DS5_DEPTH_CONFIG_STATUS; + stream_status_base = DS5_DEPTH_STREAM_STATUS; + stream_id = DS5_STREAM_DEPTH; + vc_id = 2; + state->client->addr = dpdata->subdev_info[1].board_info.addr; + } else if (state->is_rgb_b) { + sensor = &state->rgb_b.sensor; + config_status_base = DS5_RGB_CONFIG_STATUS; + stream_status_base = DS5_RGB_STREAM_STATUS; + stream_id = DS5_STREAM_RGB; + vc_id = 3; + state->client->addr = dpdata->subdev_info[1].board_info.addr; + } else if (state->is_y8) { + sensor = &state->motion_t.sensor; + config_status_base = DS5_IR_CONFIG_STATUS; + stream_status_base = DS5_IR_STREAM_STATUS; + stream_id = DS5_STREAM_IR; + vc_id = 2; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else if (state->is_imu) { + sensor = &state->imu.sensor; + config_status_base = DS5_IMU_CONFIG_STATUS; + stream_status_base = DS5_IMU_STREAM_STATUS; + stream_id = DS5_STREAM_IMU; + vc_id = 3; + state->client->addr = dpdata->subdev_info[0].board_info.addr; + } else { + return -EINVAL; + } + + dev_warn(&state->client->dev, "s_stream for stream %s, on = %d\n", + state->mux.last_set->sd.name, on); + + restore_val = state->mux.last_set->streaming; + state->mux.last_set->streaming = on; + sensor->streaming = on; + + if (on) { + + ret = ds5_configure(state); + if (ret) + goto restore_s_state; + + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_START | stream_id); + if (ret < 0) + goto restore_s_state; + + // check streaming status from FW + for (i = 0; i < DS5_START_MAX_COUNT; i++) { + ds5_read(state, stream_status_base, &streaming); + ds5_read(state, config_status_base, &status); + if ((status & DS5_STATUS_STREAMING) && + streaming == DS5_STREAM_STREAMING) + break; + + msleep_range(DS5_START_POLL_TIME); + } + + if (i == DS5_START_MAX_COUNT) { + dev_err(&state->client->dev, + "start streaming failed, exit on timeout\n"); + /* notify fw */ + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_STOP | stream_id); + ret = -EAGAIN; + goto restore_s_state; + } else { + dev_dbg(&state->client->dev, "started after %dms\n", + i * DS5_START_POLL_TIME); + } + } else { // off + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_STOP | stream_id); + if (ret < 0) + goto restore_s_state; + + if (!state->depth.sensor.streaming + && !state->depth_b.sensor.streaming + && !state->rgb.sensor.streaming + && !state->rgb_b.sensor.streaming + && !state->motion_t.sensor.streaming + && !state->imu.sensor.streaming) + d4xx_reset_oneshot(state); + } + + ds5_read(state, config_status_base, &status); + ds5_read(state, stream_status_base, &streaming); + dev_info(&state->client->dev, + "%s %s, stream_status 0x%x:%x, config_status 0x%x:%x\n", + ds5_get_sensor_name(state), + (on)?"START":"STOP", + stream_status_base, streaming, + config_status_base, status); + + return ret; + +restore_s_state: + + ds5_read(state, config_status_base, &status); + dev_err(&state->client->dev, + "%s stream toggle failed! %x status 0x%04x\n", + ds5_get_sensor_name(state), restore_val, status); + + state->mux.last_set->streaming = restore_val; + sensor->streaming = restore_val; + + return ret; +} + +//static int ds5_set_power(struct ds5 *state, int on) +//{ +// int ret = 0; +// +// mutex_lock(&state->lock); +// +// if (state->power != !on) { +// mutex_unlock(&state->lock); +// return 0; +// } +// +//// gpio_set_value_cansleep(state->pwdn_gpio, on); +// +// dev_info(&state->client->dev, "%s(): power %d\n", __func__, on); +// +// usleep_range(100, 200); +// +// if (on) { +// state->power = true; +// } else { +// state->power = false; +// } +// +// mutex_unlock(&state->lock); +// +// /* TODO: Restore controls when powering on */ +// //if (on) +// // ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); +// +// return ret; +//} + +/* Core ops */ +/*static int ds5_mux_set_power(struct v4l2_subdev *sd, int on) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + + return ds5_set_power(state, on); +}*/ + +#define DS5_N_STREAMS 4 +#define DS5_PAD_SOURCE 0 + +static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) +{ + unsigned int i; + + desc->num_entries = V4L2_FRAME_DESC_ENTRY_MAX; + + for (i = 0; i < desc->num_entries; i++) { + desc->entry[i].flags = 0; + desc->entry[i].pixelcode = MEDIA_BUS_FMT_FIXED; + desc->entry[i].length = 0; + if (i == desc->num_entries - 1) { + desc->entry[i].pixelcode = 0x12; + desc->entry[i].length = 68; + } + } + return 0; +} + +static const struct v4l2_subdev_pad_ops ds5_mux_pad_ops = { + .enum_mbus_code = ds5_mux_enum_mbus_code, + .enum_frame_size = ds5_mux_enum_frame_size, + .enum_frame_interval = ds5_mux_enum_frame_interval, + .get_fmt = ds5_mux_get_fmt, + .set_fmt = ds5_mux_set_fmt, + .get_frame_desc = ds5_mux_get_frame_desc, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) + .get_frame_interval = ds5_mux_g_frame_interval, + .set_frame_interval = ds5_mux_s_frame_interval, +#endif +}; + +static const struct v4l2_subdev_core_ops ds5_mux_core_ops = { + //.s_power = ds5_mux_set_power, + .log_status = v4l2_ctrl_subdev_log_status, +}; + +static const struct v4l2_subdev_video_ops ds5_mux_video_ops = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) + .g_frame_interval = ds5_mux_g_frame_interval, + .s_frame_interval = ds5_mux_s_frame_interval, +#endif + .s_stream = ds5_mux_s_stream, +}; + +static const struct v4l2_subdev_ops ds5_mux_subdev_ops = { + .core = &ds5_mux_core_ops, + .pad = &ds5_mux_pad_ops, + .video = &ds5_mux_video_ops, +}; + +#if 0 /* function not used */ +static int ds5_des_register(struct ds5 *state, struct ds5_des *des) +{ + struct v4l2_subdev *sd = &des->sd; + int ret; + + // FIXME: is async needed? + ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); + if (ret < 0) { + return ret; + } + + //ret = media_create_pad_link(entity, 1, &state->mux.sd.subdev.entity, des->mux_pad, + // MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + //if (ret < 0) { + // goto e_sd; + // } + + dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, des->mux_pad); + + return 0; + +e_sd: + v4l2_device_unregister_subdev(sd); + + return ret; +} +#endif + +static int ds5_mux_registered(struct v4l2_subdev *sd) +{ + struct ds5 *state = v4l2_get_subdevdata(sd); + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + + int ret = ds5_sensor_register(state, &state->depth.sensor); + if (ret < 0) + return ret; + + ret = ds5_sensor_register(state, &state->rgb.sensor); + if (ret < 0) + goto e_rgb; + + if (dpdata->subdev_num == 1) { + ret = ds5_sensor_register(state, &state->motion_t.sensor); + if (ret < 0) + goto e_depth; + + ret = ds5_sensor_register(state, &state->imu.sensor); + if (ret < 0) + goto e_imu; + } else if (dpdata->subdev_num == 2) { + ret = ds5_sensor_register(state, &state->depth_b.sensor); + if (ret < 0) + dev_err(&state->client->dev, + "error: ds5_sensor_register state->depth_b.sensor\n"); + + ret = ds5_sensor_register(state, &state->rgb_b.sensor); + if (ret < 0) + dev_err(&state->client->dev, + "error: ds5_sensor_register state->rgb_b.sensor\n"); + } + return 0; + +e_imu: + v4l2_device_unregister_subdev(&state->rgb.sensor.sd); + +e_rgb: + v4l2_device_unregister_subdev(&state->motion_t.sensor.sd); + +e_depth: + v4l2_device_unregister_subdev(&state->depth.sensor.sd); + + return ret; +} + +static void ds5_mux_unregistered(struct v4l2_subdev *sd) +{ + struct ds5 *state = v4l2_get_subdevdata(sd); + struct d4xx_pdata *dpdata = state->client->dev.platform_data; + + ds5_sensor_remove(&state->rgb.sensor); + ds5_sensor_remove(&state->depth.sensor); + if (dpdata->subdev_num == 1) { + ds5_sensor_remove(&state->motion_t.sensor); + ds5_sensor_remove(&state->imu.sensor); + } else if (dpdata->subdev_num == 2) { + ds5_sensor_remove(&state->depth_b.sensor); + ds5_sensor_remove(&state->rgb_b.sensor); + } +} + +static const struct v4l2_subdev_internal_ops ds5_mux_internal_ops = { + .open = ds5_mux_open, + .close = ds5_mux_close, + .registered = ds5_mux_registered, + .unregistered = ds5_mux_unregistered, +}; + +static int ds5_mux_register(struct i2c_client *c, struct ds5 *state) +{ + return v4l2_async_register_subdev(&state->mux.sd.subdev); +} + +static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) +{ + struct v4l2_subdev *sd = &state->mux.sd.subdev; + dev_dbg(&state->client->dev, "ds5_hw_init :sd->name = %s\n", sd->name); + u16 mipi_status, n_lanes, phy, drate_min, drate_max; + int ret = ds5_read(state, DS5_MIPI_SUPPORT_LINES, &n_lanes); + if (!ret) + ret = ds5_read(state, DS5_MIPI_SUPPORT_PHY, &phy); + + if (!ret) + ret = ds5_read(state, DS5_MIPI_DATARATE_MIN, &drate_min); + + if (!ret) + ret = ds5_read(state, DS5_MIPI_DATARATE_MAX, &drate_max); + + n_lanes = 2; + + ret = ds5_write(state, DS5_MIPI_LANE_NUMS, n_lanes - 1); + if (!ret) + ret = ds5_write(state, DS5_MIPI_LANE_DATARATE, MIPI_LANE_RATE); + + ret = ds5_read(state, DS5_MIPI_CONF_STATUS, &mipi_status); + + return ret; +} + +#if 0 /* not used function */ +static int ds5_des_init(struct i2c_client *c, struct ds5 *state) +{ + struct v4l2_subdev *sd = &state->max9296.des.sd; + struct media_entity *entity = &state->max9296.des.sd.entity; + struct media_pad *pads = state->max9296.des.pads; + dev_t *dev_num = &state->client->dev.devt; + + state->max9296.des.mux_pad = DS5_MUX_PAD_EXTERNAL; + + dev_info(sd->dev, "%s(): %p %s %p %p", __func__, c, c->name, state, state->client); + + v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); + sd->owner = THIS_MODULE; + sd->internal_ops = &ds5_sensor_internal_ops; + sd->grp_id = *dev_num; + v4l2_set_subdevdata(sd, state); + snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", + "max9296", i2c_adapter_id(c->adapter), 0x48); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[0].flags = MEDIA_PAD_FL_SOURCE; + pads[1].flags = MEDIA_PAD_FL_SINK; + entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; + entity->function = MEDIA_ENT_F_CAM_SENSOR; + return media_entity_pads_init(entity, 2, pads); +} +#endif +static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) +{ + struct v4l2_subdev *sd = &state->mux.sd.subdev; + struct media_entity *entity = &state->mux.sd.subdev.entity; + struct media_pad *pads = state->mux.pads, *pad; + unsigned int i; + int ret; + + struct d4xx_pdata *dpdata = c->dev.platform_data; + v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); + // Set owner to NULL so we can unload the driver module + sd->owner = NULL; + sd->internal_ops = &ds5_mux_internal_ops; + v4l2_set_subdevdata(sd, state); + snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", dpdata->suffix); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; + entity->function = MEDIA_ENT_F_CAM_SENSOR; + + pads[0].flags = MEDIA_PAD_FL_SOURCE; + for (i = 1, pad = pads + 1; i < ARRAY_SIZE(state->mux.pads); i++, pad++) + pad->flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(entity, ARRAY_SIZE(state->mux.pads), pads); + if (ret < 0) + return ret; + + /*set for mux*/ + ret = ds5_ctrl_init(state, DS5_SID_MUX_A); + if (ret < 0) + return ret; + /*set for depth*/ + ret = ds5_ctrl_init(state, DS5_SID_DEPTH_A); + if (ret < 0) + return ret; + /*set for rgb*/ + ret = ds5_ctrl_init(state, DS5_SID_RGB_A); + if (ret < 0) + return ret; + /*set for y8*/ + ret = ds5_ctrl_init(state, DS5_SID_MOTION_T_A); + if (ret < 0) + return ret; + /*set for imu*/ + ret = ds5_ctrl_init(state, DS5_SID_IMU_A); + if (ret < 0) + return ret; + if (dpdata->subdev_num == 2) { + /*set for depth_b*/ + ret = ds5_ctrl_init(state, DS5_SID_DEPTH_B); + if (ret < 0) + return ret; + /*set for rgb_b*/ + ret = ds5_ctrl_init(state, DS5_SID_RGB_B); + if (ret < 0) + return ret; + } + + ds5_set_state_last_set(state); + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + state->mux.sd.dev = &c->dev; + ret = camera_common_initialize(&state->mux.sd, "d4xx"); + if (ret) { + dev_err(&c->dev, "Failed to initialize d4xx.\n"); + goto e_ctrl; + } +#endif + + return 0; + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +e_ctrl: + v4l2_ctrl_handler_free(sd->ctrl_handler); +#endif + media_entity_cleanup(entity); + + return ret; +} + +#define USE_Y + +static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) +{ + struct ds5_sensor *sensor; + u16 cfg0 = 0, cfg0_md = 0, cfg1 = 0, cfg1_md = 0; + u16 dw = 0, dh = 0, yw = 0, yh = 0, dev_type = 0; + int ret; + + ret = ds5_read(state, DS5_DEPTH_STREAM_DT, &cfg0); + if (!ret) + ret = ds5_read(state, DS5_DEPTH_STREAM_MD, &cfg0_md); + if (!ret) + ret = ds5_read(state, DS5_DEPTH_RES_WIDTH, &dw); + if (!ret) + ret = ds5_read(state, DS5_DEPTH_RES_HEIGHT, &dh); + if (!ret) + ret = ds5_read(state, DS5_IR_STREAM_DT, &cfg1); + if (!ret) + ret = ds5_read(state, DS5_IR_STREAM_MD, &cfg1_md); + if (!ret) + ret = ds5_read(state, DS5_IR_RES_WIDTH, &yw); + if (!ret) + ret = ds5_read(state, DS5_IR_RES_HEIGHT, &yh); + if (!ret) + ret = ds5_read(state, DS5_DEVICE_TYPE, &dev_type); + if (ret < 0) + return ret; + + dev_info(&client->dev, "%s(): cfg0 %x %ux%u cfg0_md %x %ux%u\n", __func__, + cfg0, dw, dh, cfg0_md, yw, yh); + + dev_info(&client->dev, "%s(): cfg1 %x %ux%u cfg1_md %x %ux%u\n", __func__, + cfg1, dw, dh, cfg1_md, yw, yh); + + sensor = &state->depth.sensor; + switch (dev_type) { + case DS5_DEVICE_TYPE_D43X: + case DS5_DEVICE_TYPE_D45X: + sensor->formats = ds5_depth_formats_d43x; + break; + case DS5_DEVICE_TYPE_D46X: + sensor->formats = ds5_depth_formats_d46x; + break; + default: + sensor->formats = ds5_depth_formats_d46x; + } + sensor->n_formats = 1; + sensor->mux_pad = DS5_MUX_PAD_DEPTH_A; + + sensor = &state->depth_b.sensor; + switch (dev_type) { + case DS5_DEVICE_TYPE_D43X: + case DS5_DEVICE_TYPE_D45X: + sensor->formats = ds5_depth_formats_d43x; + break; + case DS5_DEVICE_TYPE_D46X: + sensor->formats = ds5_depth_formats_d46x; + break; + default: + sensor->formats = ds5_depth_formats_d46x; + } + sensor->n_formats = 1; + sensor->mux_pad = DS5_MUX_PAD_DEPTH_B; + + sensor = &state->motion_t.sensor; + sensor->formats = state->variant->formats; + sensor->n_formats = state->variant->n_formats; + sensor->mux_pad = DS5_MUX_PAD_MOTION_T_A; + switch (dev_type) { + // case DS5_DEVICE_TYPE_D45X: + case DS5_DEVICE_TYPE_D43X: { + unsigned int *calib_resolutions_size = + (unsigned int *)&(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].n_resolutions); + const struct ds5_resolution **calib_resolutions = (const struct ds5_resolution **) + &(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].resolutions); + *calib_resolutions_size = ARRAY_SIZE(d43x_calibration_sizes), + *calib_resolutions = d43x_calibration_sizes; + break; + } + case DS5_DEVICE_TYPE_D46X: { + dev_info(&client->dev, "%s(): DS5_DEVICE_TYPE_D46X for calib\n", __func__); + /*unsigned int *calib_resolutions_size = + * (unsigned int *)&(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].n_resolutions); + *const struct ds5_resolution **calib_resolutions = (const struct ds5_resolution**) + * &(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].resolutions); + **calib_resolutions_size = ARRAY_SIZE(d46x_calibration_sizes), + **calib_resolutions = d46x_calibration_sizes; + */ + break; + } + } + + sensor = &state->rgb.sensor; + switch (dev_type) { + case DS5_DEVICE_TYPE_D43X: + case DS5_DEVICE_TYPE_D46X: + sensor->formats = &ds5_onsemi_rgb_format; + sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; + break; + case DS5_DEVICE_TYPE_D45X: + sensor->formats = &ds5_rlt_rgb_format; + sensor->n_formats = DS5_RLT_RGB_N_FORMATS; + break; + default: + sensor->formats = &ds5_onsemi_rgb_format; + sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; + } + sensor->mux_pad = DS5_MUX_PAD_RGB_A; + + sensor = &state->rgb_b.sensor; + switch (dev_type) { + case DS5_DEVICE_TYPE_D43X: + case DS5_DEVICE_TYPE_D46X: + sensor->formats = &ds5_onsemi_rgb_format; + sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; + break; + case DS5_DEVICE_TYPE_D45X: + sensor->formats = &ds5_rlt_rgb_format; + sensor->n_formats = DS5_RLT_RGB_N_FORMATS; + break; + default: + sensor->formats = &ds5_onsemi_rgb_format; + sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; + } + sensor->mux_pad = DS5_MUX_PAD_RGB_B; + + /*sensor->formats = &ds5_onsemi_rgb_format; + *sensor->n_formats = DS5_RGB_N_FORMATS; + *sensor->mux_pad = DS5_MUX_PAD_RGB; + */ + + sensor = &state->imu.sensor; + sensor->formats = ds5_imu_formats; + sensor->n_formats = 1; + sensor->mux_pad = DS5_MUX_PAD_IMU_A; + + /* Development: set a configuration during probing */ + if ((cfg0 & 0xff00) == 0x1800) { + /* MIPI CSI-2 YUV420 isn't supported by V4L, reconfigure to Y8 */ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + /* Use template to fill in .field, .colorspace etc. */ + .format = ds5_mbus_framefmt_template, + }; + +//#undef USE_Y + /* Override .width, .height, .code */ + fmt.format.width = yw; + fmt.format.height = yh; + fmt.format.code = MEDIA_BUS_FMT_UYVY8_2X8; + + //state->mux.sd.mode_prop_idx = 0; + state->motion_t.sensor.streaming = true; + state->depth.sensor.streaming = true; + ret = __ds5_sensor_set_fmt(state, &state->motion_t.sensor, NULL, &fmt); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ds5_parse_cam(struct i2c_client *client, struct ds5 *state) +{ + int ret; + + ret = ds5_fixed_configuration(client, state); + if (ret < 0) + return ret; + + ds5_sensor_format_init(&state->depth.sensor); + ds5_sensor_format_init(&state->motion_t.sensor); + ds5_sensor_format_init(&state->rgb.sensor); + ds5_sensor_format_init(&state->imu.sensor); + if ((&(state->depth_b) != NULL) && (&(state->rgb_b) != NULL)) { + ds5_sensor_format_init(&state->depth_b.sensor); + ds5_sensor_format_init(&state->rgb_b.sensor); + } + + return 0; +} + +static void ds5_mux_remove(struct ds5 *state) +{ +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + camera_common_cleanup(&state->mux.sd); +#endif + v4l2_async_unregister_subdev(&state->mux.sd.subdev); + v4l2_ctrl_handler_free(state->mux.sd.subdev.ctrl_handler); + media_entity_cleanup(&state->mux.sd.subdev.entity); +} + +static const struct regmap_config ds5_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static int ds5_dfu_wait_for_status(struct ds5 *state) +{ + int i, ret = 0; + u16 status; + + for (i = 0; i < DS5_START_MAX_COUNT; i++) { + ds5_read(state, 0x5000, &status); + if (status == 0x0001 || status == 0x0002) { + dev_err(&state->client->dev, + "%s(): dfu failed status(0x%4x)\n", + __func__, status); + ret = -EREMOTEIO; + break; + } + if (!status) + break; + msleep_range(DS5_START_POLL_TIME); + } + + return ret; +}; + +static int ds5_dfu_switch_to_dfu(struct ds5 *state) +{ + int ret; + int i = DS5_START_MAX_COUNT; + u16 status; + + ds5_raw_write_with_check(state, 0x4900, + &cmd_switch_to_dfu, sizeof(cmd_switch_to_dfu)); + ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + /*Wait for DFU fw to boot*/ + do { + msleep_range(DS5_START_POLL_TIME*10); + ret = ds5_read(state, 0x5000, &status); + } while (ret && i--); + return ret; +}; + +static int ds5_dfu_wait_for_get_dfu_status(struct ds5 *state, + enum dfu_fw_state exp_state) +{ + int ret = 0; + u16 status, dfu_state_len = 0x0000; + unsigned char dfu_asw_buf[DFU_WAIT_RET_LEN]; + unsigned int dfu_wr_wait_msec = 0; + + do { + ds5_write_with_check(state, 0x5008, 0x0003); // Get Write state + do { + ds5_read_with_check(state, 0x5000, &status); + if (status == 0x0001) { + dev_err(&state->client->dev, + "%s(): Write status error I2C_STATUS_ERROR(1)\n", + __func__); + return -EINVAL; + } else + if (status == 0x0002 && dfu_wr_wait_msec) + msleep_range(dfu_wr_wait_msec); + + } while (status); + + ds5_read_with_check(state, 0x5004, &dfu_state_len); + if (dfu_state_len != DFU_WAIT_RET_LEN) { + dev_err(&state->client->dev, + "%s(): Wrong answer len (%d)\n", __func__, dfu_state_len); + return -EINVAL; + } + ds5_raw_read_with_check(state, 0x4e00, &dfu_asw_buf, DFU_WAIT_RET_LEN); + if (dfu_asw_buf[0]) { + dev_err(&state->client->dev, + "%s(): Wrong dfu_status (%d)\n", __func__, dfu_asw_buf[0]); + return -EINVAL; + } + dfu_wr_wait_msec = (((unsigned int)dfu_asw_buf[3]) << 16) + | (((unsigned int)dfu_asw_buf[2]) << 8) + | dfu_asw_buf[1]; + } while (dfu_asw_buf[4] == dfuDNBUSY && exp_state == dfuDNLOAD_IDLE); + + if (dfu_asw_buf[4] != exp_state) { + dev_notice(&state->client->dev, + "%s(): Wrong dfu_state (%d) while expected(%d)\n", + __func__, dfu_asw_buf[4], exp_state); + ret = -EINVAL; + } + return ret; +}; + +static int ds5_dfu_get_dev_info(struct ds5 *state, struct __fw_status *buf) +{ + int ret = 0; + u16 len = 0; + + ret = ds5_write(state, 0x5008, 0x0002); //Upload DFU cmd + if (!ret) + ret = ds5_dfu_wait_for_status(state); + if (!ret) + ds5_read_with_check(state, 0x5004, &len); + /*Sanity check*/ + if (len == sizeof(struct __fw_status)) { + ds5_raw_read_with_check(state, 0x4e00, buf, len); + } else { + dev_err(&state->client->dev, + "%s(): Wrong state size (%d)\n", + __func__, len); + ret = -EINVAL; + } + return ret; +}; + +static int ds5_dfu_detach(struct ds5 *state) +{ + int ret; + struct __fw_status buf = {0}; + + ds5_write_with_check(state, 0x500c, 0x00); + ret = ds5_dfu_wait_for_get_dfu_status(state, dfuIDLE); + if (!ret) + ret = ds5_dfu_get_dev_info(state, &buf); + dev_notice(&state->client->dev, "%s():DFU ver (0x%x) received\n", + __func__, buf.DFU_version); + dev_notice(&state->client->dev, "%s():FW last version (0x%x) received\n", + __func__, buf.FW_lastVersion); + dev_notice(&state->client->dev, "%s():FW status (%s)\n", + __func__, buf.DFU_isLocked ? "locked" : "unlocked"); + return ret; +}; + +/* When a process reads from our device, this gets called. */ +static ssize_t ds5_dfu_device_read(struct file *flip, + char __user *buffer, size_t len, loff_t *offset) +{ + struct ds5 *state = flip->private_data; + u16 fw_ver, fw_build; + char msg[32]; + int ret = 0; + + if (mutex_lock_interruptible(&state->lock)) + return -ERESTARTSYS; + ret |= ds5_read(state, DS5_FW_VERSION, &fw_ver); + ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); + if (ret < 0) + goto e_dfu_read_failed; + snprintf(msg, sizeof(msg), "DFU info: \tver: %d.%d.%d.%d\n", + (fw_ver >> 8) & 0xff, fw_ver & 0xff, + (fw_build >> 8) & 0xff, fw_build & 0xff); + + if (copy_to_user(buffer, msg, strlen(msg))) + ret = -EFAULT; + else { + state->dfu_dev.msg_write_once = ~state->dfu_dev.msg_write_once; + ret = strlen(msg) & state->dfu_dev.msg_write_once; + } + +e_dfu_read_failed: + mutex_unlock(&state->lock); + return ret; +}; + +static ssize_t ds5_dfu_device_write(struct file *flip, + const char __user *buffer, size_t len, loff_t *offset) +{ + struct ds5 *state = flip->private_data; + int ret = 0; + + if (mutex_lock_interruptible(&state->lock)) + return -ERESTARTSYS; + switch (state->dfu_dev.dfu_state_flag) { + + case DS5_DFU_OPEN: + ret = ds5_dfu_switch_to_dfu(state); + if (ret < 0) { + dev_err(&state->client->dev, "%s(): Switch to dfu failed (%d)\n", + __func__, ret); + goto dfu_write_error; + } + /* fallthrough - procceed to recovery */ + __attribute__((__fallthrough__)); + case DS5_DFU_RECOVERY: + ret = ds5_dfu_detach(state); + if (ret < 0) { + dev_err(&state->client->dev, "%s(): Detach failed (%d)\n", + __func__, ret); + goto dfu_write_error; + } + state->dfu_dev.dfu_state_flag = DS5_DFU_IN_PROGRESS; + state->dfu_dev.init_v4l_f = 1; + /* fallthrough - procceed to download */ + __attribute__((__fallthrough__)); + case DS5_DFU_IN_PROGRESS: { + unsigned int dfu_full_blocks = len / DFU_BLOCK_SIZE; + unsigned int dfu_part_blocks = len % DFU_BLOCK_SIZE; + + while (dfu_full_blocks--) { + if (copy_from_user(state->dfu_dev.dfu_msg, buffer, DFU_BLOCK_SIZE)) { + ret = -EFAULT; + goto dfu_write_error; + } + ret = ds5_raw_write(state, 0x4a00, + state->dfu_dev.dfu_msg, DFU_BLOCK_SIZE); + if (ret < 0) + goto dfu_write_error; + ret = ds5_dfu_wait_for_get_dfu_status(state, dfuDNLOAD_IDLE); + if (ret < 0) + goto dfu_write_error; + buffer += DFU_BLOCK_SIZE; + } + if (copy_from_user(state->dfu_dev.dfu_msg, buffer, dfu_part_blocks)) { + ret = -EFAULT; + goto dfu_write_error; + } + if (dfu_part_blocks) { + ret = ds5_raw_write(state, 0x4a00, + state->dfu_dev.dfu_msg, dfu_part_blocks); + if (!ret) + ret = ds5_dfu_wait_for_get_dfu_status(state, dfuDNLOAD_IDLE); + if (!ret) + ret = ds5_write(state, 0x4a04, 0x00); /*Download complete */ + if (!ret) + ret = ds5_dfu_wait_for_get_dfu_status(state, dfuMANIFEST); + if (ret < 0) + goto dfu_write_error; + state->dfu_dev.dfu_state_flag = DS5_DFU_DONE; + } + dev_notice(&state->client->dev, "%s(): DFU block (%d) bytes written\n", + __func__, (int)len); + break; + } + default: + dev_err(&state->client->dev, "%s(): Wrong state (%d)\n", + __func__, state->dfu_dev.dfu_state_flag); + ret = -EINVAL; + goto dfu_write_error; + + }; + mutex_unlock(&state->lock); + return len; + +dfu_write_error: + state->dfu_dev.dfu_state_flag = DS5_DFU_ERROR; + // Reset DFU device to IDLE states + ret = ds5_write(state, 0x5010, 0x0); + if (!ret) + state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; + mutex_unlock(&state->lock); + return ret; +}; + +static int ds5_dfu_device_open(struct inode *inode, struct file *file) +{ + struct ds5 *state = container_of(inode->i_cdev, struct ds5, + dfu_dev.ds5_cdev); + + if (state->dfu_dev.device_open_count) + return -EBUSY; + state->dfu_dev.device_open_count++; + if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) + state->dfu_dev.dfu_state_flag = DS5_DFU_OPEN; + state->dfu_dev.dfu_msg = devm_kzalloc(&state->client->dev, + DFU_BLOCK_SIZE, GFP_KERNEL); + if (!state->dfu_dev.dfu_msg) + return -ENOMEM; + + file->private_data = state; + return 0; +}; + +static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) +{ + int ret; + struct d4xx_pdata *dpdata = c->dev.platform_data; + + ret = ds5_parse_cam(c, state); + if (ret < 0) + return ret; + + ret = ds5_depth_init(c, state); + if (ret < 0) + return ret; + + ret = ds5_motion_t_init(c, state); + if (ret < 0) + goto e_depth; + + ret = ds5_rgb_init(c, state); + if (ret < 0) + goto e_motion_t; + + if (dpdata->subdev_num == 2) { + ret = ds5_depth_b_init(c, state); + if (ret < 0) + return ret; + + ret = ds5_rgb_b_init(c, state); + if (ret < 0) + goto e_depth_b; + } + + ret = ds5_imu_init(c, state); + if (ret < 0) + goto e_rgb; + + ret = ds5_mux_init(c, state); + if (ret < 0) + goto e_imu; + + ret = ds5_hw_init(c, state); + if (ret < 0) + goto e_mux; + + ret = ds5_mux_register(c, state); + if (ret < 0) + goto e_mux; + + return 0; +e_mux: + ds5_mux_remove(state); +e_imu: + media_entity_cleanup(&state->imu.sensor.sd.entity); +e_rgb: + media_entity_cleanup(&state->rgb.sensor.sd.entity); +e_motion_t: + media_entity_cleanup(&state->motion_t.sensor.sd.entity); +e_depth_b: + media_entity_cleanup(&state->depth_b.sensor.sd.entity); +e_depth: + media_entity_cleanup(&state->depth.sensor.sd.entity); + + return ret; +} + +static int ds5_dfu_device_release(struct inode *inode, struct file *file) +{ + struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); + + state->dfu_dev.device_open_count--; + if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) + state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; + if (state->dfu_dev.dfu_state_flag == DS5_DFU_DONE + && state->dfu_dev.init_v4l_f) + ds5_v4l_init(state->client, state); + state->dfu_dev.init_v4l_f = 0; + if (state->dfu_dev.dfu_msg) + devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg); + state->dfu_dev.dfu_msg = NULL; + + return 0; +}; + +static const struct file_operations ds5_device_file_ops = { + .owner = THIS_MODULE, + .read = &ds5_dfu_device_read, + .write = &ds5_dfu_device_write, + .open = &ds5_dfu_device_open, + .release = &ds5_dfu_device_release +}; + +struct class *g_ds5_class; +atomic_t primary_chardev = ATOMIC_INIT(0); + +static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) +{ + struct cdev *ds5_cdev = &state->dfu_dev.ds5_cdev; + struct class **ds5_class = &state->dfu_dev.ds5_class; + struct d4xx_pdata *pdata = c->dev.platform_data; + + struct device *chr_dev; + char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 8]; + dev_t *dev_num = &c->dev.devt; + int ret; + + dev_dbg(&c->dev, "%s()\n", __func__); + /* Request the kernel for N_MINOR devices */ + ret = alloc_chrdev_region(dev_num, 0, 1, DS5_DRIVER_NAME_DFU); + if (ret < 0) + return ret; + + if (!atomic_cmpxchg(&primary_chardev, 0, MAJOR(*dev_num))) { + dev_dbg(&c->dev, "%s(): : <%d, %d>\n", + __func__, MAJOR(*dev_num), MINOR(*dev_num)); + /* Create a class : appears at /sys/class */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + *ds5_class = class_create(THIS_MODULE, DS5_DRIVER_NAME_CLASS); +#else + *ds5_class = class_create(DS5_DRIVER_NAME_CLASS); +#endif + if (IS_ERR(*ds5_class)) { + dev_err(&c->dev, "Could not create class device\n"); + unregister_chrdev_region(0, 1); + ret = PTR_ERR(*ds5_class); + return ret; + } + g_ds5_class = *ds5_class; + } else + *ds5_class = g_ds5_class; + /* Associate the cdev with a set of file_operations */ + cdev_init(ds5_cdev, &ds5_device_file_ops); + /* Build up the current device number. To be used further */ + *dev_num = MKDEV(MAJOR(*dev_num), MINOR(*dev_num)); + /* Create a device node for this device. */ + snprintf(dev_name, sizeof(dev_name), "%s-%c", + DS5_DRIVER_NAME_DFU, pdata->suffix); + chr_dev = device_create(*ds5_class, NULL, *dev_num, NULL, dev_name); + if (IS_ERR(chr_dev)) { + ret = PTR_ERR(chr_dev); + dev_err(&c->dev, "Could not create device\n"); + class_destroy(*ds5_class); + unregister_chrdev_region(0, 1); + return ret; + } + cdev_add(ds5_cdev, *dev_num, 1); + return 0; +}; + +static int ds5_chrdev_remove(struct ds5 *state) +{ + struct class **ds5_class = &state->dfu_dev.ds5_class; + dev_t *dev_num = &state->client->dev.devt; + if (!ds5_class) { + return 0; + } + dev_dbg(&state->client->dev, "%s()\n", __func__); + unregister_chrdev_region(*dev_num, 1); + device_destroy(*ds5_class, *dev_num); + if (atomic_cmpxchg(&primary_chardev, MAJOR(*dev_num), 0) == MAJOR(*dev_num)) + class_destroy(*ds5_class); + return 0; +} + +static void ds5_substream_init(void) +{ + int i, j; + unsigned int mipi_csi2_type; + s64 *sub_stream = NULL; + /* + * 0, vc 0, depth + * 1, vc 0, meta data + * 2, vc 1, RGB + * 3, vc 1, meta data + * 4, vc 2, IR + * 5, vc 3, IMU + * 6, vc 2, depth_b + * 7, vc 3,rgb_b + */ + for (j = 0; j < MAX_D457_COUNT; j++) { + sub_stream = d4xx_query_sub_stream[j]; + mipi_csi2_type = mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_fmt(sub_stream, 0, MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h(sub_stream, 0, 480); + set_sub_stream_w(sub_stream, 0, 640); + set_sub_stream_dt(sub_stream, 0, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 0, 0); + + set_sub_stream_fmt(sub_stream, 1, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h(sub_stream, 1, 1); + set_sub_stream_w(sub_stream, 1, 68); + set_sub_stream_dt(sub_stream, 1, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(sub_stream, 1, 0); + + /*RGB*/ + set_sub_stream_fmt(sub_stream, 2, MEDIA_BUS_FMT_YUYV8_1X16); + set_sub_stream_h(sub_stream, 2, 640); + set_sub_stream_w(sub_stream, 2, 480); + set_sub_stream_dt(sub_stream, 2, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 2, 1); + + set_sub_stream_fmt(sub_stream, 3, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h(sub_stream, 3, 1); + set_sub_stream_w(sub_stream, 3, 68); + set_sub_stream_dt(sub_stream, 3, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(sub_stream, 3, 1); + /*IR*/ + set_sub_stream_fmt(sub_stream, 4, MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h(sub_stream, 4, 640); + set_sub_stream_w(sub_stream, 4, 480); + set_sub_stream_dt(sub_stream, 4, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 4, 2); + + set_sub_stream_fmt(sub_stream, 5, MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h(sub_stream, 5, 640); + set_sub_stream_w(sub_stream, 5, 480); + set_sub_stream_dt(sub_stream, 5, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 5, 3); + + /*add DEPTH_b*/ + set_sub_stream_fmt(sub_stream, 6, MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h(sub_stream, 6, 480); + set_sub_stream_w(sub_stream, 6, 640); + set_sub_stream_dt(sub_stream, 6, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 6, 2); + + set_sub_stream_fmt(sub_stream, 7, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h(sub_stream, 7, 1); + set_sub_stream_w(sub_stream, 7, 68); + set_sub_stream_dt(sub_stream, 7, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(sub_stream, 7, 2); + + /*add RGB_b*/ + set_sub_stream_fmt(sub_stream, 8, MEDIA_BUS_FMT_YUYV8_1X16); + set_sub_stream_h(sub_stream, 8, 640); + set_sub_stream_w(sub_stream, 8, 480); + set_sub_stream_dt(sub_stream, 8, mipi_csi2_type); + set_sub_stream_vc_id(sub_stream, 8, 3); + + set_sub_stream_fmt(sub_stream, 9, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h(sub_stream, 9, 1); + set_sub_stream_w(sub_stream, 9, 68); + set_sub_stream_dt(sub_stream, 9, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(sub_stream, 9, 3); + } + + for (i = 0; i < DS5_MUX_PAD_COUNT; i++) + pad_to_substream[i] = -1; + + pad_to_substream[DS5_MUX_PAD_DEPTH_A] = 0; + pad_to_substream[DS5_MUX_PAD_RGB_A] = 2; + pad_to_substream[DS5_MUX_PAD_MOTION_T_A] = 4; + pad_to_substream[DS5_MUX_PAD_IMU_A] = 5; + pad_to_substream[DS5_MUX_PAD_DEPTH_B] = 6; + pad_to_substream[DS5_MUX_PAD_RGB_B] = 8;//7 metadata +} + +/* SYSFS attributes */ +#ifdef CONFIG_SYSFS +static ssize_t ds5_fw_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *c = to_i2c_client(dev); + struct ds5 *state = container_of(i2c_get_clientdata(c), + struct ds5, mux.sd.subdev); + + ds5_read(state, DS5_FW_VERSION, &state->fw_version); + ds5_read(state, DS5_FW_BUILD, &state->fw_build); + + return snprintf(buf, PAGE_SIZE, "D4XX Sensor: %s, Version: %d.%d.%d.%d\n", + ds5_get_sensor_name(state), + (state->fw_version >> 8) & 0xff, state->fw_version & 0xff, + (state->fw_build >> 8) & 0xff, state->fw_build & 0xff); +} + +static DEVICE_ATTR_RO(ds5_fw_ver); + +/* Derive 'device_attribute' structure for a read register's attribute */ +struct dev_ds5_reg_attribute { + struct device_attribute attr; + u16 reg; // register + u8 valid; // validity of above data +}; + +/** Read DS5 register. + * ds5_read_reg_show will actually read register from ds5 while + * ds5_read_reg_store will store register to read + * Example: + * echo -n "0xc03c" >ds5_read_reg + * Read register result: + * cat ds5_read_reg + * Expected: + * reg:0xc93c, result:0x11 + */ +static ssize_t ds5_read_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u16 rbuf; + int n; + struct i2c_client *c = to_i2c_client(dev); + struct ds5 *state = container_of(i2c_get_clientdata(c), + struct ds5, mux.sd.subdev); + struct dev_ds5_reg_attribute *ds5_rw_attr = container_of(attr, + struct dev_ds5_reg_attribute, attr); + if (ds5_rw_attr->valid != 1) + return -EINVAL; + ds5_read(state, ds5_rw_attr->reg, &rbuf); + + n = snprintf(buf, PAGE_SIZE, "register:0x%4x, value:0x%02x\n", + ds5_rw_attr->reg, rbuf); + + return n; +} + +/** Read DS5 register - Store reg to attr struct pointer + * ds5_read_reg_show will actually read register from ds5 while + * ds5_read_reg_store will store module, offset and length + */ +static ssize_t ds5_read_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dev_ds5_reg_attribute *ds5_rw_attr = container_of(attr, + struct dev_ds5_reg_attribute, attr); + int rc = -1; + u32 reg; + + ds5_rw_attr->valid = 0; + /* Decode input */ + rc = sscanf(buf, "0x%04x", ®); + if (rc != 1) + return -EINVAL; + ds5_rw_attr->reg = reg; + ds5_rw_attr->valid = 1; + return count; +} + +#define DS5_RW_REG_ATTR(_name) \ + struct dev_ds5_reg_attribute dev_attr_##_name = { \ + __ATTR(_name, S_IRUGO | S_IWUSR, \ + ds5_read_reg_show, ds5_read_reg_store), \ + 0, 0 } + +static DS5_RW_REG_ATTR(ds5_read_reg); + +static ssize_t ds5_write_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *c = to_i2c_client(dev); + struct ds5 *state = container_of(i2c_get_clientdata(c), + struct ds5, mux.sd.subdev); + + int rc = -1; + u32 reg, w_val = 0; + u16 val = -1; + /* Decode input */ + rc = sscanf(buf, "0x%04x 0x%04x", ®, &w_val); + if (rc != 2) + return -EINVAL; + val = w_val & 0xffff; + mutex_lock(&state->lock); + ds5_write(state, reg, val); + mutex_unlock(&state->lock); + return count; +} + +static DEVICE_ATTR_WO(ds5_write_reg); + +static struct attribute *ds5_attributes[] = { + &dev_attr_ds5_fw_ver.attr, + &dev_attr_ds5_read_reg.attr.attr, + &dev_attr_ds5_write_reg.attr, + NULL +}; + +static const struct attribute_group ds5_attr_group = { + .attrs = ds5_attributes, +}; +#endif + +#define NR_DESER 4 + +static const struct regmap_config ds5_regmap_max9296 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static const struct regmap_config ds5_regmap_max9295 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) +{ + int curr_max9296 = c->addr; + int max9296_addrs[MAX9296_NUM] = {0x48, 0x4a, 0x68, 0x6c}; + int i; + u8 val; + int ret; + struct d4xx_pdata *dpdata = c->dev.platform_data; + unsigned short ser_alias; + unsigned short sensor_alias; + + unsigned short ser_alias_b; + unsigned short sensor_alias_b; + + /* TODO: 2x D457 subdev connect to MAX9296 */ + if (dpdata->subdev_num == 1) { + curr_max9296 = c->addr; + sensor_alias = dpdata->subdev_info[0].board_info.addr; + ser_alias = dpdata->subdev_info[0].ser_alias; + } else if (dpdata->subdev_num == 2) { + curr_max9296 = c->addr; + sensor_alias = dpdata->subdev_info[0].board_info.addr; + ser_alias = dpdata->subdev_info[0].ser_alias; + sensor_alias_b = dpdata->subdev_info[1].board_info.addr; + ser_alias_b = dpdata->subdev_info[1].ser_alias; + } else { + dev_err(&c->dev, "no subdev found!\n"); + return -EINVAL; + } + + dev_dbg(&c->dev, "subdev_num = %u, max9296 = 0x%02x\n", dpdata->subdev_num, curr_max9296); + dev_dbg(&c->dev, "sensor_alias = 0x%02x, ser_alias = 0x%02x\n", sensor_alias, ser_alias); + dev_dbg(&c->dev, "sensor_alias_b = 0x%02x, ser_alias_b = 0x%02x\n", sensor_alias_b, ser_alias_b); + + for (i = 0; i < MAX9296_NUM; i++) { + if (curr_max9296 < max9296_addrs[i]) { + c->addr = max9296_addrs[i]; + max9296_write_8(state, MAX9296_CTRL0, RESET_LINK); + } + } + + for (i = 0; i < MAX9296_NUM; i++) { + if (curr_max9296 == max9296_addrs[i]) { + if (dpdata->subdev_num == 1) { + c->addr = curr_max9296; + max9296_write_8(state, MAX9296_CTRL0, AUTO_LINK | LINK_A); + msleep_range(1000); + + c->addr = MAX9295_I2C_ADDR_DEF; + ret = max9295_read_8(state, MAX9295_REG0, &val); + if (ret < 0) { + dev_err(&c->dev, "no max9295 found for max9296 0x%02x\n", curr_max9296); + continue; + } + + ret = max9295_read_8(state, MAX9295_REG0, &val); + max9295_write_8(state, MAX9295_REG0, ser_alias << 1); + msleep_range(1000); // need this? + c->addr = ser_alias; + max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); + max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); + break; + } + + if (dpdata->subdev_num == 2) { + //link a + c->addr = curr_max9296; + max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | LINK_A); + msleep_range(1000); + + c->addr = MAX9295_I2C_ADDR_DEF; + + max9295_write_8(state, MAX9295_REG0, ser_alias_b << 1); + + c->addr = ser_alias_b; + ret = max9295_read_8(state, MAX9295_REG0, &val); + max9295_write_8(state, MAX9295_I2C_4, sensor_alias_b << 1); + max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); + + // Packets are transmitted over GMSLA in splitter mode. + max9295_write_8(state, 0x7B, 0x31); + max9295_write_8(state, 0x83, 0x31); + max9295_write_8(state, 0x8B, 0x31); + max9295_write_8(state, 0x93, 0x31); + max9295_write_8(state, 0xA3, 0x31); + max9295_write_8(state, 0xAB, 0x31); + + c->addr = ser_alias_b; + ret = max9295_read_8(state, MAX9295_REG0, &val); + + //link b + c->addr = curr_max9296; + max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | LINK_B); + msleep_range(1000); + + c->addr = MAX9295_I2C_ADDR_DEF; + ret = max9295_read_8(state, MAX9295_REG0, &val); + max9295_write_8(state, MAX9295_REG0, ser_alias << 1); + msleep_range(1000); // need this? + c->addr = ser_alias; + max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); + max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); + + c->addr = max9296_addrs[i]; + + max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | SPLITTER); + msleep_range(1000); + + break; + } + continue; + } + } + + c->addr = sensor_alias; + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) +static int ds5_probe(struct i2c_client *c) +#else +static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) +#endif +{ + struct ds5 *state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); + u16 rec_state; + int ret, retry, err = 0; + const char *str; + + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + + dev_warn(&c->dev, "Driver addr 0x%x\n", c->addr); + + state->client = c; + dev_warn(&c->dev, "Probing new driver for D45x\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) + state->variant = ds5_variants; +#else + dev_warn(&c->dev, "Driver data NAEL %d\n", (int)id->driver_data); + state->variant = ds5_variants + id->driver_data; +#endif + + state->vcc = devm_regulator_get(&c->dev, "vcc"); + if (IS_ERR(state->vcc)) { + ret = PTR_ERR(state->vcc); + dev_warn(&c->dev, "failed %d to get vcc regulator\n", ret); + return ret; + } + + if (state->vcc) { + ret = regulator_enable(state->vcc); + if (ret < 0) { + dev_warn(&c->dev, "failed %d to enable the vcc regulator\n", ret); + return ret; + } + } + state->regmap = devm_regmap_init_i2c(c, &ds5_regmap_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + dev_err(&c->dev, "regmap init failed: %d\n", ret); + goto e_regulator; + } + + state->regmap_max9296 = devm_regmap_init_i2c(c, &ds5_regmap_max9296); + if (IS_ERR(state->regmap_max9296)) { + ret = PTR_ERR(state->regmap_max9296); + dev_err(&c->dev, "regmap max9296 init failed: %d\n", ret); + return ret; + } + + state->regmap_max9295 = devm_regmap_init_i2c(c, &ds5_regmap_max9295); + if (IS_ERR(state->regmap_max9295)) { + ret = PTR_ERR(state->regmap_max9295); + dev_err(&c->dev, "regmap max9295 init failed: %d\n", ret); + return ret; + } + + ret = ds5_i2c_addr_setting(c, state); + if (ret) { + dev_err(&c->dev, "failed apply i2c addr setting\n"); + return ret; + } + + // Verify communication + retry = 10; + do { + ret = ds5_read(state, 0x5020, &rec_state); + } while (retry-- && ret < 0); + if (ret < 0) { + dev_err(&c->dev, + "%s(): cannot communicate with D4XX: %d on addr: 0x%x\n", + __func__, ret, c->addr); + goto e_regulator; + } + + state->is_depth = 0; + state->is_y8 = 0; + state->is_rgb = 0; + state->is_depth_b = 0; + state->is_rgb_b = 0; + state->is_imu = 0; +#ifdef CONFIG_OF + ret = of_property_read_string(c->dev.of_node, "cam-type", &str); + if (!ret && !strncmp(str, "Depth", strlen("Depth"))) { + state->is_depth = 1; + } + if (!ret && !strncmp(str, "Y8", strlen("Y8"))) { + state->is_y8 = 1; + } + if (!ret && !strncmp(str, "RGB", strlen("RGB"))) { + state->is_rgb = 1; + } + if (!ret && !strncmp(str, "IMU", strlen("IMU"))) { + state->is_imu = 1; + } +#else + state->is_depth = 1; +#endif + /* create DFU chardev once */ + if (state->is_depth) { + ret = ds5_chrdev_init(c, state); + if (ret < 0) + goto e_regulator; + } + + ret = ds5_read(state, 0x5020, &rec_state); + if (ret < 0) { + dev_err(&c->dev, "%s(): cannot communicate with D4XX: %d\n", + __func__, ret); + goto e_chardev; + } + + if (rec_state == 0x201) { + dev_info(&c->dev, "%s(): D4XX recovery state\n", __func__); + state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY; + return 0; + } + + ds5_read_with_check(state, DS5_FW_VERSION, &state->fw_version); + ds5_read_with_check(state, DS5_FW_BUILD, &state->fw_build); + + dev_info(&c->dev, "D4XX Sensor: %s, firmware build: %d.%d.%d.%d\n", + ds5_get_sensor_name(state), + (state->fw_version >> 8) & 0xff, state->fw_version & 0xff, + (state->fw_build >> 8) & 0xff, state->fw_build & 0xff); + + ret = ds5_v4l_init(c, state); + if (ret < 0) + goto e_chardev; + /* Override I2C drvdata */ + /* i2c_set_clientdata(c, state); */ + +/* regulators? clocks? + * devm_regulator_bulk_get(&c->dev, DS5_N_SUPPLIES, state->supplies); + * state->clock = devm_clk_get(&c->dev, DS5_CLK_NAME); + * if (IS_ERR(state->clock)) { + * ret = -EPROBE_DEFER; + * goto err; + * } + */ +#ifdef CONFIG_SYSFS + /* Custom sysfs attributes */ + /* create the sysfs file group */ + err = sysfs_create_group(&state->client->dev.kobj, &ds5_attr_group); +#endif + ds5_substream_init(); + return 0; + +e_chardev: + if (state->dfu_dev.ds5_class) + ds5_chrdev_remove(state); +e_regulator: + if (state->vcc) + regulator_disable(state->vcc); + return ret; +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int ds5_remove(struct i2c_client *c) +#else +static void ds5_remove(struct i2c_client *c) +#endif +{ + struct ds5 *state = container_of(i2c_get_clientdata(c), struct ds5, mux.sd.subdev); + + dev_info(&c->dev, "D4XX remove %s\n", + ds5_get_sensor_name(state)); + if (state->vcc) + regulator_disable(state->vcc); +// gpio_free(state->pwdn_gpio); + if (state->is_depth) + ds5_chrdev_remove(state); + + if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) { +#ifdef CONFIG_SYSFS + sysfs_remove_group(&c->dev.kobj, &ds5_attr_group); +#endif + ds5_mux_remove(state); + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +static const struct i2c_device_id ds5_id[] = { + { DS5_DRIVER_NAME, DS5_DS5U }, + { DS5_DRIVER_NAME_ASR, DS5_ASR }, + { DS5_DRIVER_NAME_AWG, DS5_AWG }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ds5_id); + +static struct i2c_driver ds5_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DS5_DRIVER_NAME + }, + .probe = ds5_probe, + .remove = ds5_remove, + .id_table = ds5_id, +}; + +module_i2c_driver(ds5_i2c_driver); + +MODULE_DESCRIPTION("Intel RealSense D4XX Camera Driver"); +MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ + Nael Masalha ,\n\ + Alexander Gantman ,\n\ + Emil Jahshan ,\n\ + Xin Zhang ,\n\ + Qingwu Zhang ,\n\ + Evgeni Raikhel ,\n\ + Shikun Ding "); +MODULE_AUTHOR("Dmitry Perchanov "); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0.2.13"); diff --git a/include/media/i2c/d4xx_pdata.h b/include/media/i2c/d4xx_pdata.h new file mode 100644 index 00000000..73a54185 --- /dev/null +++ b/include/media/i2c/d4xx_pdata.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2022 Intel Corporation */ + +#ifndef D457_H +#define D457_H + +#define D457_NAME "d4xx" +#define MAX9296_NAME "MAX9296" + +#define D457_I2C_ADDRESS 0x10 + +struct d4xx_subdev_info { + struct i2c_board_info board_info; + int i2c_adapter_id; + unsigned short rx_port; + unsigned short phy_i2c_addr; + unsigned short ser_alias; + char suffix[5]; /* suffix for subdevs */ +}; + +struct d4xx_pdata { + unsigned int subdev_num; + struct d4xx_subdev_info *subdev_info; + unsigned int reset_gpio; + int FPD_gpio; + const char suffix; + unsigned int link_freq_mbps; + unsigned int deser_nlanes; + unsigned int ser_nlanes; + struct i2c_board_info *deser_board_info; +}; +#endif From 9850679f57f6bff5250e6e5f349acc8d459764f1 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Mon, 13 May 2024 06:06:46 -0700 Subject: [PATCH 11/72] d4xx-compat: Update d4xx.c driver to match RealSense latest development RealSense PRQ d4xx version is 1.0.1.20. - support for sensor vc. - support for sensor state. - sensor own v4l2 controls. - max9295/6 serdes drivers and control for multiple pipe. - aggregation support Signed-off-by: Dmitry Perchanov Signed-off-by: florent pirou --- Makefile | 2 + drivers/media/i2c/Makefile | 4 + drivers/media/i2c/d4xx-compat/Makefile | 2 + drivers/media/i2c/d4xx-compat/d4xx.c | 3054 +++++++++++++---------- drivers/media/i2c/d4xx-compat/max9295.c | 773 ++++++ drivers/media/i2c/d4xx-compat/max9296.c | 1184 +++++++++ include/media/gmsl-link.h | 107 + include/media/max9295.h | 111 + include/media/max9296.h | 177 ++ 9 files changed, 4057 insertions(+), 1357 deletions(-) create mode 100644 drivers/media/i2c/d4xx-compat/max9295.c create mode 100644 drivers/media/i2c/d4xx-compat/max9296.c create mode 100644 include/media/gmsl-link.h create mode 100644 include/media/max9295.h create mode 100644 include/media/max9296.h diff --git a/Makefile b/Makefile index 25e60983..944f3ef1 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ export CONFIG_VIDEO_MAX9X=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y export CONFIG_INTEL_IPU_ACPI=m +export CONFIG_I2C_IOEXPANDER_SER_MAX9295 = m +export CONFIG_I2C_IOEXPANDER_DESER_MAX9296 = m export CONFIG_VIDEO_D4XX_COMPAT=m # Define config macros for conditional compilation in ipu-acpi.c diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8a038a22..e096a2b8 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -13,6 +13,10 @@ obj-$(CONFIG_VIDEO_AR0820) += ar0820.o obj-$(CONFIG_VIDEO_AR0830) += ar0830.o obj-$(CONFIG_VIDEO_ISX031) += isx031.o +ifeq ($(CONFIG_VIDEO_D4XX), m) + ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES +endif + subdir-ccflags-y := -DDRIVER_VERSION_SUFFIX=\"${DRIVER_VERSION_SUFFIX}\" obj-$(CONFIG_VIDEO_MAX9X) += max9x/ diff --git a/drivers/media/i2c/d4xx-compat/Makefile b/drivers/media/i2c/d4xx-compat/Makefile index 3554abfb..4a2b5835 100644 --- a/drivers/media/i2c/d4xx-compat/Makefile +++ b/drivers/media/i2c/d4xx-compat/Makefile @@ -6,4 +6,6 @@ KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | cut -d- -f1 | sed -r 's/([0-9] ccflags-y += -I$(src)/../../../$(KERNEL_VERSION)/include-overrides ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES -DCONFIG_SYSFS -DPLATFORM_AXIOMTEK +obj-$(CONFIG_I2C_IOEXPANDER_SER_MAX9295) += max9295.o +obj-$(CONFIG_I2C_IOEXPANDER_DESER_MAX9296) += max9296.o obj-$(CONFIG_VIDEO_D4XX_COMPAT) += d4xx.o diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index f61cd88e..2088efe2 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -30,15 +30,26 @@ #include #include #include - +#ifdef CONFIG_VIDEO_INTEL_IPU6 #include - +#include +#endif #include #include #include #include #include -#include + +#ifdef CONFIG_VIDEO_D4XX_SERDES +#include +#include +#else +#include +#define GMSL_CSI_DT_YUV422_8 0x1E +#define GMSL_CSI_DT_RGB_888 0x24 +#define GMSL_CSI_DT_RAW_8 0x2A +#define GMSL_CSI_DT_EMBED 0x12 +#endif //#define DS5_DRIVER_NAME "DS5 RealSense camera driver" #define DS5_DRIVER_NAME "d4xx" @@ -133,12 +144,7 @@ #define MAX_RGB_EXP 10000 #define DEF_DEPTH_EXP 33000 #define DEF_RGB_EXP 1660 - -/* Currently both depth and IR use VC 0 */ -#define DS5_DEPTH_VCHAN_N 0 -#define DS5_MOTION_T_VCHAN_N 0 -//#define DS5_DEBUG_VCHAN_N 1 -//#define DS5_MOTION_T_VCHAN_N 2 +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define MAX9295_REG0 0x0000 #define MAX9295_I2C_4 0x0044 @@ -156,30 +162,16 @@ #define MAX9295_I2C_ADDR_DEF 0x40 #define D457_I2C_ADDR 0x10 - +#endif enum ds5_mux_pad { DS5_MUX_PAD_EXTERNAL, - DS5_MUX_PAD_DEPTH_A, - DS5_MUX_PAD_RGB_A, - DS5_MUX_PAD_MOTION_T_A, - DS5_MUX_PAD_IMU_A, - DS5_MUX_PAD_DEPTH_B, - DS5_MUX_PAD_RGB_B, - DS5_MUX_PAD_MOTION_T_B, - DS5_MUX_PAD_IMU_B, + DS5_MUX_PAD_DEPTH, + DS5_MUX_PAD_RGB, + DS5_MUX_PAD_IR, + DS5_MUX_PAD_IMU, DS5_MUX_PAD_COUNT, }; -enum ds5_sid { - DS5_SID_MUX_A = -1, - DS5_SID_DEPTH_A, - DS5_SID_RGB_A, - DS5_SID_MOTION_T_A, - DS5_SID_IMU_A, - DS5_SID_DEPTH_B, - DS5_SID_RGB_B, -}; - #define DS5_N_CONTROLS 8 #define CSI2_MAX_VIRTUAL_CHANNELS 4 @@ -190,12 +182,14 @@ enum ds5_sid { #define DS5_START_MAX_TIME 1000 #define DS5_START_MAX_COUNT (DS5_START_MAX_TIME / DS5_START_POLL_TIME) -#define MAX_D457_COUNT 5 -#define D457_MAX_SUBSTREAM 10 - /* DFU definition section */ #define DFU_MAGIC_NUMBER "/0x01/0x02/0x03/0x04" #define DFU_BLOCK_SIZE 1024 +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#define DFU_I2C_STANDARD_MODE 100000 +#define DFU_I2C_FAST_MODE 400000 +#define DFU_I2C_BUS_CLK_RATE DFU_I2C_FAST_MODE +#endif #define ds5_read_with_check(state, addr, val) {\ if (ds5_read(state, addr, val)) \ return -EINVAL; } @@ -208,6 +202,7 @@ enum ds5_sid { #define ds5_raw_write_with_check(state, addr, buf, size) {\ if (ds5_raw_write(state, addr, buf, size)) \ return -EINVAL; } +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define max9296_write_8_with_check(state, addr, buf) {\ if (max9296_write_8(state, addr, buf)) \ return -EINVAL; \ @@ -221,7 +216,7 @@ enum ds5_sid { #define D4XX_LINK_FREQ_288MHZ 288000000ULL #define D4XX_LINK_FREQ_240MHZ 240000000ULL #define D4XX_LINK_FREQ_225MHZ 22500000ULL - +#endif enum dfu_fw_state { appIDLE = 0x0000, appDETACH = 0x0001, @@ -253,7 +248,7 @@ struct hwm_cmd { u32 param2; u32 param3; u32 param4; - unsigned char Data[]; + unsigned char Data[0]; }; static const struct hwm_cmd cmd_switch_to_dfu = { @@ -329,7 +324,7 @@ static const struct hwm_cmd ewb = { .magic_word = 0xCDAB, .opcode = 0x18, }; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_360MHZ, D4XX_LINK_FREQ_300MHZ, @@ -337,7 +332,7 @@ static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_240MHZ, D4XX_LINK_FREQ_225MHZ, }; - +#endif struct __fw_status { uint32_t spare1; uint32_t FW_lastVersion; @@ -354,9 +349,7 @@ struct __fw_status { struct ds5_ctrls { struct v4l2_ctrl_handler handler; struct v4l2_ctrl_handler handler_depth; - struct v4l2_ctrl_handler handler_depth_b; struct v4l2_ctrl_handler handler_rgb; - struct v4l2_ctrl_handler handler_rgb_b; struct v4l2_ctrl_handler handler_y8; struct v4l2_ctrl_handler handler_imu; struct { @@ -414,13 +407,7 @@ struct ds5_sensor { /*struct ds5_vchan *vchan;*/ const struct ds5_format *formats; unsigned int n_formats; -}; - -struct ds5_des { - struct v4l2_subdev sd; - struct media_pad pads[2]; - struct v4l2_mbus_framefmt format; - u16 mux_pad; + int pipe_id; }; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM @@ -445,6 +432,7 @@ struct ds5_dfu_dev { unsigned char *dfu_msg; u16 msg_write_once; unsigned char init_v4l_f; + u32 bus_clk_rate; }; enum { @@ -453,33 +441,17 @@ enum { DS5_AWG, }; +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define NR_OF_DS5_PADS 7 -#define NR_OF_DS5_STREAMS (NR_OF_DS5_PADS - 1) - +#define NR_OF_DS5_STREAMS 4 struct v4l2_mbus_framefmt ds5_ffmts[NR_OF_DS5_PADS]; +#endif struct ds5 { - struct { - struct ds5_sensor sensor; - } depth; - struct { - struct ds5_sensor sensor; - } depth_b; - struct { - struct ds5_sensor sensor; - } motion_t; - struct { - struct ds5_sensor sensor; - } rgb; - struct { - struct ds5_sensor sensor; - } rgb_b; - struct { - struct ds5_sensor sensor; - } imu; - struct { - struct ds5_des des; - } max9296; + struct { struct ds5_sensor sensor; } depth; + struct { struct ds5_sensor sensor; } ir; + struct { struct ds5_sensor sensor; } rgb; + struct { struct ds5_sensor sensor; } imu; struct { struct ds5_mux_subdev sd; struct media_pad pads[DS5_MUX_PAD_COUNT]; @@ -493,18 +465,29 @@ struct ds5 { /* All below pointers are used for writing, cannot be const */ struct mutex lock; struct regmap *regmap; +#ifndef CONFIG_VIDEO_D4XX_SERDES struct regmap *regmap_max9296; struct regmap *regmap_max9295; +#endif struct regulator *vcc; const struct ds5_variant *variant; - int is_depth; - int is_depth_b; - int is_y8; - int is_rgb; - int is_rgb_b; - int is_imu; + int is_depth, is_y8, is_rgb, is_imu; + int aggregated; u16 fw_version; u16 fw_build; +#ifdef CONFIG_VIDEO_D4XX_SERDES + struct gmsl_link_ctx g_ctx; + struct device *ser_dev; + struct device *dser_dev; + struct i2c_client *ser_i2c; + struct i2c_client *dser_i2c; +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6 +#define NR_OF_CSI2_BE_SOC_STREAMS 16 +#define NR_OF_DS5_SUB_STREAMS 6 /*d+d.md,c+c.md,ir,imu*/ + int pad_to_vc[DS5_MUX_PAD_COUNT]; + int pad_to_substream[NR_OF_CSI2_BE_SOC_STREAMS]; +#endif }; struct ds5_counters { @@ -514,14 +497,14 @@ struct ds5_counters { }; #define ds5_from_depth_sd(sd) container_of(sd, struct ds5, depth.sd) -#define ds5_from_motion_t_sd(sd) container_of(sd, struct ds5, motion_t.sd) +#define ds5_from_ir_sd(sd) container_of(sd, struct ds5, ir.sd) #define ds5_from_rgb_sd(sd) container_of(sd, struct ds5, rgb.sd) - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static inline void msleep_range(unsigned int delay_base) { usleep_range(delay_base * 1000, delay_base * 1000 + 500); } - +#ifndef CONFIG_VIDEO_D4XX_SERDES static int max9296_write_8(struct ds5 *state, u16 reg, u8 val) { int ret; @@ -597,6 +580,25 @@ static int max9295_read_8(struct ds5 *state, u16 reg, u8 *val) return ret; } +#else +static int ds5_write_8(struct ds5 *state, u16 reg, u8 val) +{ + int ret; + + ret = regmap_raw_write(state->regmap, reg, &val, 1); + if (ret < 0) + dev_err(&state->client->dev, "%s(): i2c write failed %d, 0x%04x = 0x%x\n", + __func__, ret, reg, val); + else + if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) + dev_dbg(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", + __func__, reg, val); + + return ret; +} +#endif +#endif + static int ds5_write(struct ds5 *state, u16 reg, u16 val) { int ret; @@ -606,18 +608,18 @@ static int ds5_write(struct ds5 *state, u16 reg, u16 val) value[0] = val & 0x00FF; dev_dbg(&state->client->dev, - "%s(): writing to register: 0x%02x, (0x%04x) value1: 0x%x, value2:0x%x\n", - __func__, state->client->addr, reg, value[1], value[0]); + "%s(): writing to register: 0x%04x, value1: 0x%x, value2:0x%x\n", + __func__, reg, value[1], value[0]); ret = regmap_raw_write(state->regmap, reg, value, sizeof(value)); if (ret < 0) dev_err(&state->client->dev, - "%s(): i2c write failed %d,0x%02x ,0x%04x = 0x%x\n", - __func__, ret, state->client->addr, reg, val); + "%s(): i2c write failed %d, 0x%04x = 0x%x\n", + __func__, ret, reg, val); else if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) - dev_dbg(&state->client->dev, "%s():0x%02x i2c write 0x%04x: 0x%x\n", - __func__, state->client->addr, reg, val); + dev_dbg(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", + __func__, reg, val); return ret; } @@ -664,63 +666,60 @@ static int ds5_raw_read(struct ds5 *state, u16 reg, void *val, size_t val_len) return ret; } -static int pad_to_substream[DS5_MUX_PAD_COUNT]; - -static s64 d4xx_query_sub_stream[MAX_D457_COUNT][D457_MAX_SUBSTREAM] = {0}; - -static void set_sub_stream_fmt(s64 *query_sub_stream, int index, u32 code) +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static s64 d4xx_query_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; +static u8 d4xx_set_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; +static void set_sub_stream_fmt(int index, u32 code) { - query_sub_stream[index] &= 0xFFFFFFFFFFFF0000; - query_sub_stream[index] |= code; + d4xx_query_sub_stream[index] &= 0xFFFFFFFFFFFF0000; + d4xx_query_sub_stream[index] |= code; } -static void set_sub_stream_h(s64 *query_sub_stream, int index, u32 height) +static void set_sub_stream_h(int index, u32 height) { s64 val = height; val &= 0xFFFF; - query_sub_stream[index] &= 0xFFFFFFFF0000FFFF; - query_sub_stream[index] |= val << 16; + d4xx_query_sub_stream[index] &= 0xFFFFFFFF0000FFFF; + d4xx_query_sub_stream[index] |= val << 16; } -static void set_sub_stream_w(s64 *query_sub_stream, int index, u32 width) +static void set_sub_stream_w(int index, u32 width) { s64 val = width; val &= 0xFFFF; - query_sub_stream[index] &= 0xFFFF0000FFFFFFFF; - query_sub_stream[index] |= val << 32; + d4xx_query_sub_stream[index] &= 0xFFFF0000FFFFFFFF; + d4xx_query_sub_stream[index] |= val << 32; } -static void set_sub_stream_dt(s64 *query_sub_stream, int index, u32 dt) +static void set_sub_stream_dt(int index, u32 dt) { s64 val = dt; val &= 0xFF; - query_sub_stream[index] &= 0xFF00FFFFFFFFFFFF; - query_sub_stream[index] |= val << 48; + d4xx_query_sub_stream[index] &= 0xFF00FFFFFFFFFFFF; + d4xx_query_sub_stream[index] |= val << 48; } -static void set_sub_stream_vc_id(s64 *query_sub_stream, int index, u32 vc_id) +static void set_sub_stream_vc_id(int index, u32 vc_id) { s64 val = vc_id; val &= 0xFF; - query_sub_stream[index] &= 0x00FFFFFFFFFFFFFF; - query_sub_stream[index] |= val << 56; + d4xx_query_sub_stream[index] &= 0x00FFFFFFFFFFFFFF; + d4xx_query_sub_stream[index] |= val << 56; } -static int get_sub_stream_vc_id(const s64 *query_sub_stream, int index) +static int get_sub_stream_vc_id(int index) { s64 val = 0; - val = query_sub_stream[index] >> 56; + + val = d4xx_query_sub_stream[index] >> 56; val &= 0xFF; return (int)val; } - -static u8 d4xx_set_sub_stream[] = { - 0, 0, 0, 0, 0, 0 -}; +#endif /* Pad ops */ @@ -944,20 +943,30 @@ static const struct ds5_resolution ds5_size_imu[] = { }, }; +// 32 bit IMU introduced with IMU sensitivity attribute Firmware +static const struct ds5_resolution ds5_size_imu_extended[] = { + { + .width = 38, + .height = 1, + .framerates = ds5_imu_framerates, + .n_framerates = ARRAY_SIZE(ds5_imu_framerates), + }, +}; + static const struct ds5_format ds5_depth_formats_d43x[] = { { // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* Z16 */ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), .resolutions = d43x_depth_sizes, }, { - .data_type = 0x2a, /* Y8 */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), .resolutions = d43x_depth_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), .resolutions = d43x_calibration_sizes, @@ -967,21 +976,21 @@ static const struct ds5_format ds5_depth_formats_d43x[] = { static const struct ds5_format ds5_depth_formats_d46x[] = { { // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. - .data_type = 0x1e, /* Z16 */ + .data_type = GMSL_CSI_DT_YUV422_8, /* Z16 */ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), .resolutions = d46x_depth_sizes, }, { /* First format: default */ - .data_type = 0x2a, /* Y8 */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), .resolutions = d46x_depth_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ - .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), - .resolutions = d43x_calibration_sizes, + .n_resolutions = ARRAY_SIZE(d46x_calibration_sizes), + .resolutions = d46x_calibration_sizes, }, }; @@ -990,17 +999,17 @@ static const struct ds5_format ds5_depth_formats_d46x[] = { static const struct ds5_format ds5_y_formats_ds5u[] = { { /* First format: default */ - .data_type = 0x2a, /* Y8 */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(y8_sizes), .resolutions = y8_sizes, }, { - .data_type = 0x1e, /* Y8I */ - .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .data_type = GMSL_CSI_DT_YUV422_8, /* Y8I */ + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, .n_resolutions = ARRAY_SIZE(y8_sizes), .resolutions = y8_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), .resolutions = d43x_calibration_sizes, @@ -1008,7 +1017,7 @@ static const struct ds5_format ds5_y_formats_ds5u[] = { }; static const struct ds5_format ds5_rlt_rgb_format = { - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .n_resolutions = ARRAY_SIZE(ds5_rlt_rgb_sizes), .resolutions = ds5_rlt_rgb_sizes, @@ -1016,7 +1025,7 @@ static const struct ds5_format ds5_rlt_rgb_format = { #define DS5_RLT_RGB_N_FORMATS 1 static const struct ds5_format ds5_onsemi_rgb_format = { - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .n_resolutions = ARRAY_SIZE(ds5_onsemi_rgb_sizes), .resolutions = ds5_onsemi_rgb_sizes, @@ -1033,13 +1042,23 @@ static const struct ds5_variant ds5_variants[] = { static const struct ds5_format ds5_imu_formats[] = { { /* First format: default */ - .data_type = 0x2a, /* IMU DT */ + .data_type = GMSL_CSI_DT_RAW_8, /* IMU DT */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(ds5_size_imu), .resolutions = ds5_size_imu, }, }; +static const struct ds5_format ds5_imu_formats_extended[] = { + { + /* First format: default */ + .data_type = GMSL_CSI_DT_RAW_8, /* IMU DT */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(ds5_size_imu_extended), + .resolutions = ds5_size_imu_extended, + }, +}; + static const struct v4l2_mbus_framefmt ds5_mbus_framefmt_template = { .width = 0, .height = 0, @@ -1055,10 +1074,8 @@ static const struct v4l2_mbus_framefmt ds5_mbus_framefmt_template = { static const char *ds5_get_sensor_name(struct ds5 *state) { static const char *sensor_name[] = {"unknown", "RGB", "DEPTH", "Y8", "IMU"}; - int sensor_id = state->is_rgb | state->is_rgb_b; - sensor_id += (state->is_depth | state->is_depth_b) * 2; - sensor_id += state->is_y8 * 3 + state->is_imu * 4; - + int sensor_id = state->is_rgb * 1 + state->is_depth * 2 + \ + state->is_y8 * 3 + state->is_imu * 4; if (sensor_id >= (sizeof(sensor_name)/sizeof(*sensor_name))) sensor_id = 0; @@ -1067,19 +1084,15 @@ static const char *ds5_get_sensor_name(struct ds5 *state) static void ds5_set_state_last_set(struct ds5 *state) { - dev_info(&state->client->dev, "%s(): %s\n", + dev_dbg(&state->client->dev, "%s(): %s\n", __func__, ds5_get_sensor_name(state)); if (state->is_depth) state->mux.last_set = &state->depth.sensor; else if (state->is_rgb) state->mux.last_set = &state->rgb.sensor; - else if (state->is_rgb_b) - state->mux.last_set = &state->rgb_b.sensor; - else if (state->is_depth_b) - state->mux.last_set = &state->depth_b.sensor; else if (state->is_y8) - state->mux.last_set = &state->motion_t.sensor; + state->mux.last_set = &state->ir.sensor; else state->mux.last_set = &state->imu.sensor; } @@ -1096,7 +1109,7 @@ static void ds5_sensor_format_init(struct ds5_sensor *sensor) if (sensor->config.format) return; - dev_info(sensor->sd.dev, "%s(): on pad %u\n", __func__, sensor->mux_pad); + dev_dbg(sensor->sd.dev, "%s(): on pad %u\n", __func__, sensor->mux_pad); ffmt = &sensor->format; *ffmt = ds5_mbus_framefmt_template; @@ -1129,8 +1142,8 @@ static int ds5_sensor_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *mce) { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); - //struct ds5_vchan *vchan = sensor->vchan; - dev_info(sensor->sd.dev, "%s(): sensor %s pad: %d index: %d\n", + + dev_dbg(sensor->sd.dev, "%s(): sensor %s pad: %d index: %d\n", __func__, sensor->sd.name, mce->pad, mce->index); if (mce->pad) return -EINVAL; @@ -1156,7 +1169,7 @@ static int ds5_sensor_enum_frame_size(struct v4l2_subdev *sd, const struct ds5_format *fmt; unsigned int i; - dev_info(sensor->sd.dev, "%s(): sensor %s is %s\n", + dev_dbg(sensor->sd.dev, "%s(): sensor %s is %s\n", __func__, sensor->sd.name, ds5_get_sensor_name(state)); for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) @@ -1240,7 +1253,7 @@ static int ds5_sensor_get_fmt(struct v4l2_subdev *sd, mutex_unlock(&state->lock); - dev_info(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", + dev_dbg(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", __func__, fmt->pad, fmt->format.code, fmt->format.width, fmt->format.height); @@ -1262,7 +1275,7 @@ static const struct ds5_format *ds5_sensor_find_format( if (fmt->mbus_code == ffmt->code) break; } - dev_info(sensor->sd.dev, "%s(): mbus_code = %x, code = %x \n", + dev_dbg(sensor->sd.dev, "%s(): mbus_code = %x, code = %x \n", __func__, fmt->mbus_code, ffmt->code); if (i == sensor->n_formats) { @@ -1306,7 +1319,7 @@ static const struct ds5_format *ds5_sensor_find_format( #define MIPI_CSI2_TYPE_RAW14 0x2d /* 1-8 */ #define MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i) - 1) - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static unsigned int mbus_code_to_mipi(u32 code) { switch (code) { @@ -1347,22 +1360,69 @@ static unsigned int mbus_code_to_mipi(u32 code) return -EINVAL; } } +#endif -static s64 *match_d4xx_sub_stream_qmenu(const s64 *qmenu_int) +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static int ds5_s_state_pad(struct ds5 *state, int pad) { - int i = 0; - s64 *matched_qmenu_int = NULL; + int ret = 0; + + dev_dbg(&state->client->dev, "%s(): set state for pad: %d\n", __func__, pad); + + switch (pad) { + case DS5_MUX_PAD_DEPTH: + state->is_depth = 1; + state->is_rgb = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB: + state->is_depth = 0; + state->is_rgb = 1; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IR: + state->is_depth = 0; + state->is_rgb = 0; + state->is_y8 = 1; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IMU: + state->is_depth = 0; + state->is_rgb = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + default: + dev_warn(&state->client->dev, "%s(): unknown pad: %d\n", __func__, pad); + ret = -EINVAL; + break; + } + ds5_set_state_last_set(state); + return ret; +} - for (i = 0; i < MAX_D457_COUNT; i++) { - if (d4xx_query_sub_stream[i] == qmenu_int) { - matched_qmenu_int = d4xx_query_sub_stream[i]; +static int ds5_s_state(struct ds5 *state, int vc) +{ + int ret = 0; + int i = 0; + int pad = 0; + for (i = 0; i < ARRAY_SIZE(state->pad_to_vc); i++) { + if (state->pad_to_vc[i] == vc) { + pad = i; break; } } - return matched_qmenu_int; + dev_info(&state->client->dev, "%s(): set state for vc: %d on pad: %d\n", __func__, vc, pad); + + ret = ds5_s_state_pad(state, pad); + return ret; } +#endif + static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) struct v4l2_subdev_pad_config *cfg, @@ -1372,12 +1432,14 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf;// = &fmt->format; +#ifdef CONFIG_VIDEO_INTEL_IPU6 int substream = -1; - s64 *query_sub_stream = NULL; +#endif + //unsigned r; dev_dbg(sensor->sd.dev, "%s(): state %p, " "sensor %p, fmt %p, fmt->format %p\n", - __func__, state, sensor, fmt, &fmt->format); + __func__, state, sensor, fmt, &fmt->format); mf = &fmt->format; @@ -1411,27 +1473,20 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, state->mux.last_set = sensor; mutex_unlock(&state->lock); - - substream = pad_to_substream[sensor->mux_pad]; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + substream = state->pad_to_substream[sensor->mux_pad]; if (substream != -1) { - query_sub_stream = match_d4xx_sub_stream_qmenu(state->ctrls.query_sub_stream->qmenu_int); - if (query_sub_stream) { - set_sub_stream_fmt(query_sub_stream, substream, mf->code); - set_sub_stream_h(query_sub_stream, substream, mf->height); - set_sub_stream_w(query_sub_stream, substream, mf->width); - set_sub_stream_dt(query_sub_stream, substream, mbus_code_to_mipi(mf->code)); - } else { - dev_err(sensor->sd.dev, - "failed to find sub stream fmt info: %s\n", - sensor->sd.name); - } + set_sub_stream_fmt(substream, mf->code); + set_sub_stream_h(substream, mf->height); + set_sub_stream_w(substream, mf->width); + set_sub_stream_dt(substream, mbus_code_to_mipi(mf->code)); } - dev_info(sensor->sd.dev, "%s(): fmt->pad: %d, sensor->mux_pad: %d, code: 0x%x, %ux%u substream:%d\n", __func__, + dev_dbg(sensor->sd.dev, "%s(): fmt->pad: %d, sensor->mux_pad: %d, code: 0x%x, %ux%u substream:%d\n", __func__, fmt->pad, sensor->mux_pad, fmt->format.code, fmt->format.width, fmt->format.height, substream); - +#endif return 0; } @@ -1445,7 +1500,10 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); struct ds5 *state = v4l2_get_subdevdata(sd); - +#ifdef CONFIG_VIDEO_INTEL_IPU6 + /* set state by vc */ + ds5_s_state_pad(state, sensor->mux_pad); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) return __ds5_sensor_set_fmt(state, sensor, cfg, fmt); #else @@ -1453,13 +1511,38 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, #endif } +#ifdef CONFIG_VIDEO_D4XX_SERDES +static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, + int pipe_id, u32 vc_id) +{ + int ret = 0; + dev_dbg(&state->client->dev, + "set pipe %d, data_type1: 0x%x, \ + data_type2: 0x%x, vc_id: %u\n", + pipe_id, data_type1, data_type2, vc_id); + ret |= max9295_set_pipe(state->ser_dev, pipe_id, + data_type1, data_type2, vc_id); + ret |= max9296_set_pipe(state->dser_dev, pipe_id, + data_type1, data_type2, vc_id); + if (ret) + dev_warn(&state->client->dev, + "failed to set pipe %d, data_type1: 0x%x, \ + data_type2: 0x%x, vc_id: %u\n", + pipe_id, data_type1, data_type2, vc_id); + + return ret; +} +#endif + static int ds5_configure(struct ds5 *state) { struct ds5_sensor *sensor; u16 fmt, md_fmt, vc_id; +#ifdef CONFIG_VIDEO_D4XX_SERDES + u16 data_type1, data_type2; +#endif u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; int ret; - struct d4xx_pdata *dpdata = state->client->dev.platform_data; if (state->is_depth) { sensor = &state->depth.sensor; @@ -1469,9 +1552,8 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_DEPTH_FPS; width_addr = DS5_DEPTH_RES_WIDTH; height_addr = DS5_DEPTH_RES_HEIGHT; - md_fmt = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 0; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else if (state->is_rgb) { sensor = &state->rgb.sensor; dt_addr = DS5_RGB_STREAM_DT; @@ -1480,20 +1562,18 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_RGB_FPS; width_addr = DS5_RGB_RES_WIDTH; height_addr = DS5_RGB_RES_HEIGHT; - md_fmt = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 1; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else if (state->is_y8) { - sensor = &state->motion_t.sensor; + sensor = &state->ir.sensor; dt_addr = DS5_IR_STREAM_DT; md_addr = DS5_IR_STREAM_MD; override_addr = DS5_IR_OVERRIDE; fps_addr = DS5_IR_FPS; width_addr = DS5_IR_RES_WIDTH; height_addr = DS5_IR_RES_HEIGHT; - md_fmt = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 2; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else if (state->is_imu) { sensor = &state->imu.sensor; dt_addr = DS5_IMU_STREAM_DT; @@ -1502,57 +1582,44 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_IMU_FPS; width_addr = DS5_IMU_RES_WIDTH; height_addr = DS5_IMU_RES_HEIGHT; - vc_id = 3; md_fmt = 0x0; - state->client->addr = dpdata->subdev_info[0].board_info.addr; - } else if (state->is_depth_b) { - sensor = &state->depth_b.sensor; - - dt_addr = DS5_DEPTH_STREAM_DT; - md_addr = DS5_DEPTH_STREAM_MD; - override_addr = DS5_DEPTH_OVERRIDE; - fps_addr = DS5_DEPTH_FPS; - width_addr = DS5_DEPTH_RES_WIDTH; - height_addr = DS5_DEPTH_RES_HEIGHT; - md_fmt = 0x12; - vc_id = 2; - state->client->addr = dpdata->subdev_info[1].board_info.addr; - } else if (state->is_rgb_b) { - sensor = &state->rgb_b.sensor; - dt_addr = DS5_RGB_STREAM_DT; - md_addr = DS5_RGB_STREAM_MD; - override_addr = 0; - fps_addr = DS5_RGB_FPS; - width_addr = DS5_RGB_RES_WIDTH; - height_addr = DS5_RGB_RES_HEIGHT; - md_fmt = 0x12; vc_id = 3; - state->client->addr = dpdata->subdev_info[1].board_info.addr; } else { return -EINVAL; } +#ifdef CONFIG_VIDEO_D4XX_SERDES + data_type1 = sensor->config.format->data_type; + data_type2 = state->is_y8 ? 0x00 : md_fmt; + + vc_id = state->g_ctx.dst_vc; + + ret = ds5_setup_pipeline(state, data_type1, data_type2, sensor->pipe_id, + vc_id); + // reset data path when switching to Y12I + if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) + max9296_reset_oneshot(state->dser_dev); + if (ret < 0) + return ret; +#endif + fmt = sensor->streaming ? sensor->config.format->data_type : 0; /* * Set depth stream Z16 data type as 0x31 * Set IR stream Y8I data type as 0x32 */ - if ((state->is_depth || state->is_depth_b) && fmt != 0) + if (state->is_depth && fmt != 0) ret = ds5_write(state, dt_addr, 0x31); else if (state->is_y8 && fmt != 0 && - sensor->config.format->data_type == 0x1E) + sensor->config.format->data_type == GMSL_CSI_DT_YUV422_8) ret = ds5_write(state, dt_addr, 0x32); else ret = ds5_write(state, dt_addr, fmt); if (ret < 0) return ret; - if (state->is_depth_b || state->is_rgb_b) - ret = ds5_write(state, md_addr, ((vc_id % 2) << 8) | md_fmt); - else - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - + ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); if (ret < 0) return ret; @@ -1604,7 +1671,7 @@ static int ds5_sensor_g_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = sensor->config.framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, fi->interval.denominator); return 0; @@ -1631,7 +1698,7 @@ static int ds5_sensor_s_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); return 0; } @@ -1640,7 +1707,8 @@ static int ds5_sensor_s_stream(struct v4l2_subdev *sd, int on) { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); - dev_info(sensor->sd.dev, "%s(): sensor: name=%s state=%d\n", __func__, sensor->sd.name, on); + dev_dbg(sensor->sd.dev, "%s(): sensor: name=%s state=%d\n", + __func__, sensor->sd.name, on); sensor->streaming = on; @@ -1660,10 +1728,10 @@ static const struct v4l2_subdev_ops ds5_depth_subdev_ops = { .video = &ds5_sensor_video_ops, }; -/* Motion detection */ +/* InfraRed stream Y8/Y16 */ /* FIXME: identical to ds5_depth_pad_ops, use one for both */ -static const struct v4l2_subdev_pad_ops ds5_motion_t_pad_ops = { +static const struct v4l2_subdev_pad_ops ds5_ir_pad_ops = { .enum_mbus_code = ds5_sensor_enum_mbus_code, .enum_frame_size = ds5_sensor_enum_frame_size, .enum_frame_interval = ds5_sensor_enum_frame_interval, @@ -1675,8 +1743,8 @@ static const struct v4l2_subdev_pad_ops ds5_motion_t_pad_ops = { #endif }; -static const struct v4l2_subdev_ops ds5_motion_t_subdev_ops = { - .pad = &ds5_motion_t_pad_ops, +static const struct v4l2_subdev_ops ds5_ir_subdev_ops = { + .pad = &ds5_ir_pad_ops, .video = &ds5_sensor_video_ops, }; @@ -1725,13 +1793,13 @@ static int ds5_hw_set_auto_exposure(struct ds5 *state, u32 base, s32 val) * In firmware color auto exposure setting follow the uvc_menu_info * exposure_auto_controls numbers, in drivers/media/usb/uvc/uvc_ctrl.c. */ - if ((state->is_rgb || state->is_rgb_b) && val == V4L2_EXPOSURE_APERTURE_PRIORITY) + if (state->is_rgb && val == V4L2_EXPOSURE_APERTURE_PRIORITY) val = 8; /* * In firmware depth auto exposure on: 1, off: 0. */ - if (!(state->is_rgb & state->is_rgb_b)) { + if (!state->is_rgb) { if (val == V4L2_EXPOSURE_APERTURE_PRIORITY) val = 1; else if (val == V4L2_EXPOSURE_MANUAL) @@ -1752,9 +1820,9 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) if (val < 1) val = 1; - if ((state->is_depth || state->is_depth_b || state->is_y8) && val > MAX_DEPTH_EXP) + if ((state->is_depth || state->is_y8) && val > MAX_DEPTH_EXP) val = MAX_DEPTH_EXP; - if ((state->is_rgb || state->is_rgb_b) && val > MAX_RGB_EXP) + if (state->is_rgb && val > MAX_RGB_EXP) val = MAX_RGB_EXP; /* @@ -1802,58 +1870,6 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) */ #define DS5_CAMERA_CID_HWMC_RW (DS5_CAMERA_CID_BASE+32) -static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, - bool isRead, u16 *dataLen) -{ - int ret = 0; - u16 status = 2; - int retries = 100; - int errorCode; - int iter = retries; - - dev_dbg(&state->client->dev, - "%s(): HWMC header: 0x%x, magic: 0x%x, opcode: 0x%x, " - "param1: %d, param2: %d, param3: %d, param4: %d\n", - __func__, cmd->header, cmd->magic_word, cmd->opcode, - cmd->param1, cmd->param2, cmd->param3, cmd->param4); - - ds5_raw_write_with_check(state, 0x4900, cmd, cmdLen); - - ds5_write_with_check(state, 0x490C, 0x01); /* execute cmd */ - do { - if (iter != retries) - msleep_range(10); - ret = ds5_read(state, 0x4904, &status); - } while (iter-- && status != 0); - - if (ret || status != 0) { - ds5_raw_read(state, 0x4900, &errorCode, 4); - dev_err(&state->client->dev, - "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", - __func__, ret, status, errorCode); - ret = -EAGAIN; - } - - if (isRead) { - if (*dataLen == 0) { - ret = regmap_raw_read(state->regmap, 0x4908, dataLen, sizeof(u16)); - if (ret) - return -EAGAIN; - } - - dev_dbg(&state->client->dev, "%s(): HWMC read len: %d\n", - __func__, *dataLen); - // First 4 bytes of cmd->Data after read will include opcode - ds5_raw_read_with_check(state, 0x4900, cmd->Data, *dataLen); - - /*This is neede for libreealsense, to align there code with UVC*/ - cmd->Data[1000] = (unsigned char)((*dataLen) & 0x00FF); - cmd->Data[1001] = (unsigned char)(((*dataLen) & 0xFF00) >> 8); - } - - return 0; -} - #define DS5_HWMC_DATA 0x4900 #define DS5_HWMC_STATUS 0x4904 #define DS5_HWMC_RESP_LEN 0x4908 @@ -1864,59 +1880,103 @@ static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, #define DS5_HWMC_STATUS_WIP 2 #define DS5_HWMC_BUFFER_SIZE 1024 -static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) +enum DS5_HWMC_ERR { + DS5_HWMC_ERR_SUCCESS = 0, + DS5_HWMC_ERR_CMD = -1, + DS5_HWMC_ERR_PARAM = -6, + DS5_HWMC_ERR_NODATA = -21, +}; + +static int ds5_get_hwmc_status(struct ds5 *state) { int ret = 0; u16 status = DS5_HWMC_STATUS_WIP; int retries = 100; int errorCode; - u16 tmp_len = 0; - const int SIZE_OF_HW_MONITOR_HEADER = 4; - - memset(data, 0, DS5_HWMC_BUFFER_SIZE); - do { if (retries != 100) msleep_range(1); ret = ds5_read(state, DS5_HWMC_STATUS, &status); - } while (!ret && retries-- && status != DS5_HWMC_STATUS_OK); - + } while (!ret && retries-- && status == DS5_HWMC_STATUS_WIP); + dev_dbg(&state->client->dev, + "%s(): ret: 0x%x, status: 0x%x\n", + __func__, ret, status); if (ret || status != DS5_HWMC_STATUS_OK) { if (status == DS5_HWMC_STATUS_ERR) { ds5_raw_read(state, DS5_HWMC_DATA, &errorCode, sizeof(errorCode)); - dev_err(&state->client->dev, - "%s(): HWMC failed, ret: %d, error code: %d\n", - __func__, ret, errorCode); - } else { - dev_err(&state->client->dev, - "%s(): HWMC failed because of timeout, ret: %d\n", - __func__, ret); + switch(errorCode) { + case (DS5_HWMC_ERR_CMD): + case (DS5_HWMC_ERR_PARAM): + ret = -EBADMSG; + break; + case (DS5_HWMC_ERR_NODATA): + ret = -ENODATA; + break; + + default: + dev_dbg(&state->client->dev, + "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", + __func__, ret, status, errorCode); + ret = -EBADMSG; + break; + } } - return -EAGAIN; + } + if (!ret && (status != DS5_HWMC_STATUS_OK)) + ret = -EBUSY; + + return ret; +} + +static int ds5_get_hwmc(struct ds5 *state, unsigned char *data, + u16 cmdDataLen, u16 *dataLen) +{ + int ret = 0; + u16 tmp_len = 0; + + if (!data) + return -ENOBUFS; + + memset(data, 0, cmdDataLen); + ret = ds5_get_hwmc_status(state); + if (ret) { + dev_dbg(&state->client->dev, + "%s(): HWMC status not clear, ret: %d\n", + __func__, ret); + return ret; } ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &tmp_len, sizeof(tmp_len)); if (ret) - return -EAGAIN; + return -EBADMSG; - if (tmp_len > DS5_HWMC_BUFFER_SIZE) + if (tmp_len > cmdDataLen) return -ENOBUFS; dev_dbg(&state->client->dev, "%s(): HWMC read len: %d, lrs_len: %d\n", - __func__, tmp_len, tmp_len - SIZE_OF_HW_MONITOR_HEADER); + __func__, tmp_len, tmp_len - 4); ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, tmp_len); + if (dataLen) + *dataLen = tmp_len; + return ret; +} - /* This is needed for librealsense, to align there code with UVC, - * last word is length - 4 bytes header length - */ - tmp_len -= SIZE_OF_HW_MONITOR_HEADER; - data[DS5_HWMC_BUFFER_SIZE - 4] = (unsigned char)(tmp_len & 0x00FF); - data[DS5_HWMC_BUFFER_SIZE - 3] = (unsigned char)((tmp_len & 0xFF00) >> 8); - data[DS5_HWMC_BUFFER_SIZE - 2] = 0; - data[DS5_HWMC_BUFFER_SIZE - 1] = 0; +static int ds5_send_hwmc(struct ds5 *state, + u16 cmdLen, + struct hwm_cmd *cmd) +{ + dev_dbg(&state->client->dev, + "%s(): HWMC header: 0x%x, magic: 0x%x, opcode: 0x%x, " + "cmdLen: %d, param1: %d, param2: %d, param3: %d, param4: %d\n", + __func__, cmd->header, cmd->magic_word, cmd->opcode, + cmdLen, cmd->param1, cmd->param2, cmd->param3, cmd->param4); + + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, cmdLen); + + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ return 0; } @@ -1928,13 +1988,13 @@ static int ds5_set_calibration_data(struct ds5 *state, int retries = 10; u16 status = 2; - ds5_raw_write_with_check(state, 0x4900, cmd, length); + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, length); - ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ do { if (retries != 10) msleep_range(200); - ret = ds5_read(state, 0x4904, &status); + ret = ds5_read(state, DS5_HWMC_STATUS, &status); } while (retries-- && status != 0); if (ret || status != 0) { @@ -1944,146 +2004,53 @@ static int ds5_set_calibration_data(struct ds5 *state, __func__, cmd->param1, ret, status); } - return -EINVAL; + return ret; } static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on); -static int ds5_s_state(struct ds5 *state, int vc) +static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) { - int ret = 0; - struct d4xx_pdata *dpdata = state->client->dev.platform_data; - - dev_dbg(&state->client->dev, "%s(): set state for vc: %d\n", __func__, vc); - - switch (vc) { - case 0: - state->is_depth = 1; - state->is_depth_b = 0; - state->is_rgb = 0; - state->is_rgb_b = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case 1: - state->is_depth = 0; - state->is_depth_b = 0; - state->is_rgb = 1; - state->is_rgb_b = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case 2: - state->is_depth = 0; - state->is_rgb = 0; - if (dpdata->subdev_num == 1) { - state->is_y8 = 1; //default case: vc2:state is IR - state->is_imu = 0; - } else if (dpdata->subdev_num == 2) { - state->is_depth_b = 1; //A+A case: vc2:state is depth_b - state->is_rgb_b = 0; - } - break; - case 3: - state->is_depth = 0; - state->is_rgb = 0; - if (dpdata->subdev_num == 1) { - state->is_y8 = 0; - state->is_imu = 0; //default case: vc3:state is IMU - } else if (dpdata->subdev_num == 2) { - state->is_depth_b = 0; - state->is_rgb_b = 1; //A+A case: vc3:state is rgb_b - } - break; - default: - dev_warn(&state->client->dev, "%s(): unknown vc: %d\n", __func__, vc); - ret = -EINVAL; - break; - } - ds5_set_state_last_set(state); - return ret; -} - -static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ds5 *state = container_of(ctrl->handler, struct ds5, - ctrls.handler); - struct v4l2_subdev *sd = &state->mux.sd.subdev; - struct ds5_sensor *sensor = (struct ds5_sensor *)ctrl->priv; - struct d4xx_pdata *dpdata = state->client->dev.platform_data; - int ret = -EINVAL; - u16 base = DS5_DEPTH_CONTROL_BASE; - u32 val; - u16 on; - u16 vc_id; + struct ds5 *state = container_of(ctrl->handler, struct ds5, + ctrls.handler); + struct v4l2_subdev *sd = &state->mux.sd.subdev; + struct ds5_sensor *sensor = (struct ds5_sensor *)ctrl->priv; + int ret = -EINVAL; + u16 base = DS5_DEPTH_CONTROL_BASE; if (sensor) { switch (sensor->mux_pad) { - case DS5_MUX_PAD_DEPTH_B: - state->client->addr = dpdata->subdev_info[1].board_info.addr; - state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth_b); - state->is_rgb_b = 0; - state->is_depth_b = 1; - state->is_rgb = 0; - state->is_depth = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case DS5_MUX_PAD_RGB_B: - state->client->addr = dpdata->subdev_info[1].board_info.addr; - state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb_b); - state->is_rgb_b = 1; - state->is_depth_b = 0; - state->is_rgb = 0; - state->is_depth = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case DS5_MUX_PAD_DEPTH_A: - state->client->addr = dpdata->subdev_info[0].board_info.addr; + case DS5_MUX_PAD_DEPTH: state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); state->is_rgb = 0; state->is_depth = 1; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_RGB_A: - state->client->addr = dpdata->subdev_info[0].board_info.addr; + case DS5_MUX_PAD_RGB: state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); state->is_rgb = 1; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_MOTION_T_A: - state->client->addr = dpdata->subdev_info[0].board_info.addr; + case DS5_MUX_PAD_IR: state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 1; state->is_imu = 0; break; - case DS5_MUX_PAD_IMU_A: - state->client->addr = dpdata->subdev_info[0].board_info.addr; + case DS5_MUX_PAD_IMU: state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 1; break; default: state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 1; break; @@ -2091,9 +2058,12 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) } } - if (state->is_rgb || state->is_rgb_b) + if (state->is_rgb) base = DS5_RGB_CONTROL_BASE; - +#ifndef CONFIG_VIDEO_INTEL_IPU6 + else if (state->is_imu) + return -EINVAL; +#endif v4l2_dbg(3, 1, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s, value: %d\n", __func__, ds5_get_sensor_name(state), ctrl->name, ctrl->val); @@ -2113,58 +2083,26 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) ret = ds5_hw_set_exposure(state, base, ctrl->val); break; case DS5_CAMERA_CID_LASER_POWER: - if (!(state->is_rgb & state->is_rgb_b)) + if (!state->is_rgb) ret = ds5_write(state, base | DS5_LASER_POWER, ctrl->val); break; case DS5_CAMERA_CID_MANUAL_LASER_POWER: - if (!(state->is_rgb & state->is_rgb_b)) + if (!state->is_rgb) ret = ds5_write(state, base | DS5_MANUAL_LASER_POWER, ctrl->val); break; - case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET: { - struct hwm_cmd *calib_cmd; - + case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET: dev_dbg(&state->client->dev, - "%s(): DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET \n", - __func__); - dev_dbg(&state->client->dev, - "%s(): table id: 0x%x\n", - __func__, *((u8 *)ctrl->p_new.p + 2)); - if (ctrl->p_new.p && DEPTH_CALIBRATION_ID == - *((u8 *)ctrl->p_new.p + 2)) { - calib_cmd = devm_kzalloc(&state->client->dev, - sizeof(struct hwm_cmd) + 256, GFP_KERNEL); - if (!calib_cmd) { - dev_err(&state->client->dev, - "%s(): Can't allocate memory for 0x%x\n", - __func__, ctrl->id); - ret = -ENOMEM; - break; - } - memcpy(calib_cmd, &set_calib_data, sizeof(set_calib_data)); - calib_cmd->header = 276; - calib_cmd->param1 = DEPTH_CALIBRATION_ID; - memcpy (calib_cmd->Data, (u8 *)ctrl->p_new.p, 256); - ret = ds5_set_calibration_data(state, calib_cmd, - sizeof(struct hwm_cmd) + 256); - devm_kfree(&state->client->dev, calib_cmd); - } - break; - } - case DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET: { + "%s(): DS5_CAMERA_DEPTH_CALIBRATION_TABLE_SET \n", __func__); + if (ctrl->p_new.p) { struct hwm_cmd *calib_cmd; - dev_dbg(&state->client->dev, - "%s(): DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET \n", - __func__); - dev_dbg(&state->client->dev, - "%s(): table id %d\n", + "%s(): table id: 0x%x\n", __func__, *((u8 *)ctrl->p_new.p + 2)); - if (ctrl->p_new.p && COEF_CALIBRATION_ID == - *((u8 *)ctrl->p_new.p + 2)) { + if (DEPTH_CALIBRATION_ID == *((u8 *)ctrl->p_new.p + 2)) { calib_cmd = devm_kzalloc(&state->client->dev, - sizeof(struct hwm_cmd) + 512, GFP_KERNEL); + sizeof(struct hwm_cmd) + 256, GFP_KERNEL); if (!calib_cmd) { dev_err(&state->client->dev, "%s(): Can't allocate memory for 0x%x\n", @@ -2172,7 +2110,36 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) ret = -ENOMEM; break; } - memcpy (calib_cmd, &set_calib_data, sizeof (set_calib_data)); + memcpy(calib_cmd, &set_calib_data, sizeof(set_calib_data)); + calib_cmd->header = 276; + calib_cmd->param1 = DEPTH_CALIBRATION_ID; + memcpy(calib_cmd->Data, (u8 *)ctrl->p_new.p, 256); + ret = ds5_set_calibration_data(state, calib_cmd, + sizeof(struct hwm_cmd) + 256); + devm_kfree(&state->client->dev, calib_cmd); + } + } + break; + case DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET: + dev_dbg(&state->client->dev, + "%s(): DS5_CAMERA_COEFF_CALIBRATION_TABLE_SET \n", + __func__); + if (ctrl->p_new.p) { + struct hwm_cmd *calib_cmd; + dev_dbg(&state->client->dev, + "%s(): table id %d\n", + __func__, *((u8 *)ctrl->p_new.p + 2)); + if (COEF_CALIBRATION_ID == *((u8 *)ctrl->p_new.p + 2)) { + calib_cmd = devm_kzalloc(&state->client->dev, + sizeof(struct hwm_cmd) + 512, GFP_KERNEL); + if (!calib_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(calib_cmd, &set_calib_data, sizeof (set_calib_data)); calib_cmd->header = 532; calib_cmd->param1 = COEF_CALIBRATION_ID; memcpy(calib_cmd->Data, (u8 *)ctrl->p_new.p, 512); @@ -2182,22 +2149,23 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) } } break; - case DS5_CAMERA_CID_AE_ROI_SET: { - struct hwm_cmd ae_roi_cmd; - - memcpy(&ae_roi_cmd, &set_ae_roi, sizeof(ae_roi_cmd)); - ae_roi_cmd.param1 = *((u16 *)ctrl->p_new.p_u16); - ae_roi_cmd.param2 = *((u16 *)ctrl->p_new.p_u16 + 1); - ae_roi_cmd.param3 = *((u16 *)ctrl->p_new.p_u16 + 2); - ae_roi_cmd.param4 = *((u16 *)ctrl->p_new.p_u16 + 3); - ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), - &ae_roi_cmd, false, NULL); - break; + case DS5_CAMERA_CID_AE_ROI_SET: + if (ctrl->p_new.p_u16) { + struct hwm_cmd ae_roi_cmd; + memcpy(&ae_roi_cmd, &set_ae_roi, sizeof(ae_roi_cmd)); + ae_roi_cmd.param1 = *((u16 *)ctrl->p_new.p_u16); + ae_roi_cmd.param2 = *((u16 *)ctrl->p_new.p_u16 + 1); + ae_roi_cmd.param3 = *((u16 *)ctrl->p_new.p_u16 + 2); + ae_roi_cmd.param4 = *((u16 *)ctrl->p_new.p_u16 + 3); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), + &ae_roi_cmd); + if (!ret) + ret = ds5_get_hwmc_status(state); } - case DS5_CAMERA_CID_AE_SETPOINT_SET: { - struct hwm_cmd *ae_setpoint_cmd; - + break; + case DS5_CAMERA_CID_AE_SETPOINT_SET: if (ctrl->p_new.p_s32) { + struct hwm_cmd *ae_setpoint_cmd; dev_dbg(&state->client->dev, "%s():0x%x \n", __func__, *(ctrl->p_new.p_s32)); ae_setpoint_cmd = devm_kzalloc(&state->client->dev, @@ -2212,15 +2180,17 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) memcpy(ae_setpoint_cmd, &set_ae_setpoint, sizeof (set_ae_setpoint)); memcpy(ae_setpoint_cmd->Data, (u8 *)ctrl->p_new.p_s32, 4); ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd) + 4, - ae_setpoint_cmd, false, NULL); + ae_setpoint_cmd); + if (!ret) + ret = ds5_get_hwmc_status(state); devm_kfree(&state->client->dev, ae_setpoint_cmd); } break; - } case DS5_CAMERA_CID_ERB: if (ctrl->p_new.p_u8) { u16 offset = 0; u16 size = 0; + u16 len = 0; struct hwm_cmd *erb_cmd; offset = *(ctrl->p_new.p_u8) << 8; @@ -2230,9 +2200,8 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&state->client->dev, "%s(): offset %x, size: %x\n", __func__, offset, size); - - erb_cmd = devm_kzalloc(&state->client->dev, - sizeof(struct hwm_cmd) + size, GFP_KERNEL); + len = sizeof(struct hwm_cmd) + size; + erb_cmd = devm_kzalloc(&state->client->dev, len, GFP_KERNEL); if (!erb_cmd) { dev_err(&state->client->dev, "%s(): Can't allocate memory for 0x%x\n", @@ -2243,9 +2212,9 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) memcpy(erb_cmd, &erb, sizeof(struct hwm_cmd)); erb_cmd->param1 = offset; erb_cmd->param2 = size; - ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), - erb_cmd, true, &size); - + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), erb_cmd); + if (!ret) + ret = ds5_get_hwmc(state, erb_cmd->Data, len, &size); if (ret) { dev_err(&state->client->dev, "%s(): ERB cmd failed, ret: %d," @@ -2302,9 +2271,9 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) ewb_cmd->param1 = offset; // start index ewb_cmd->param2 = size; // size memcpy(ewb_cmd->Data, (u8 *)ctrl->p_new.p_u8 + 4, size); - ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd) + size, - ewb_cmd, false, NULL); - + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd) + size, ewb_cmd); + if (!ret) + ret = ds5_get_hwmc_status(state); if (ret) { dev_err(&state->client->dev, "%s(): EWB cmd failed, ret: %d," @@ -2319,50 +2288,58 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) break; case DS5_CAMERA_CID_HWMC: if (ctrl->p_new.p_u8) { - u16 dataLen = 0; u16 size = 0; - + struct hwm_cmd *cmd = (struct hwm_cmd *)ctrl->p_new.p_u8; size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8; size |= *((u8 *)ctrl->p_new.p_u8 + 0); - dev_dbg(&state->client->dev, - "%s(): HWMC size %d\n", - __func__, size); - ret = ds5_send_hwmc(state, size + 4, - (struct hwm_cmd *)ctrl->p_new.p_u8, true, &dataLen); + ret = ds5_send_hwmc(state, size + 4, cmd); + ret = ds5_get_hwmc(state, cmd->Data, ctrl->dims[0], &size); + if (ctrl->dims[0] < DS5_HWMC_BUFFER_SIZE) { + ret = -ENODATA; + break; + } + /*This is needed for legacy hwmc */ + size += 4; // SIZE_OF_HW_MONITOR_HEADER + cmd->Data[1000] = (unsigned char)((size) & 0x00FF); + cmd->Data[1001] = (unsigned char)(((size) & 0xFF00) >> 8); } break; case DS5_CAMERA_CID_HWMC_RW: if (ctrl->p_new.p_u8) { u16 size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8; - size |= *((u8 *)ctrl->p_new.p_u8 + 0); ret = ds5_send_hwmc(state, size + 4, - (struct hwm_cmd *)ctrl->p_new.p_u8, false, NULL); + (struct hwm_cmd *)ctrl->p_new.p_u8); } break; case DS5_CAMERA_CID_PWM: - if (state->is_depth || state->is_depth_b) + if (state->is_depth) ret = ds5_write(state, base | DS5_PWM_FREQUENCY, ctrl->val); break; +#ifdef CONFIG_VIDEO_INTEL_IPU6 case V4L2_CID_IPU_SET_SUB_STREAM: - val = (*ctrl->p_new.p_s64 & 0xFFFF); - dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x\n", val); - vc_id = (val >> 8) & 0x00FF; - on = val & 0x00FF; + { + u32 val = (*ctrl->p_new.p_s64 & 0xFFFF); + u16 on = val & 0x00FF; + u16 vc_id = (val >> 8) & 0x00FF; + int substream = -1; if (vc_id < DS5_MUX_PAD_COUNT) ret = ds5_s_state(state, vc_id); + substream = state->pad_to_substream[state->mux.last_set->mux_pad]; + dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x vc_id:%d, substream:%d, on:%d\n", val, vc_id, substream, on); if (on == 0xff) break; if (vc_id > NR_OF_DS5_STREAMS - 1) dev_err(&state->client->dev, "invalid vc %d\n", vc_id); else - d4xx_set_sub_stream[vc_id] = on; - - if (on == 0) - ret = ds5_s_state(state, vc_id); - + d4xx_set_sub_stream[substream] = on; + ret = 0; +#ifndef CONFIG_VIDEO_D4XX_SERDES ret = ds5_mux_s_stream(sd, on); +#endif + } break; +#endif } mutex_unlock(&state->lock); @@ -2464,72 +2441,44 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (sensor) { switch (sensor->mux_pad) { - case DS5_MUX_PAD_DEPTH_B: - state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth_b); - state->is_rgb = 0; - state->is_rgb_b = 0; - state->is_depth = 0; - state->is_depth_b = 1; - state->is_y8 = 0; - state->is_imu = 0; - break; - case DS5_MUX_PAD_RGB_B: - state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb_b); - state->is_rgb_b = 1; - state->is_depth = 0; - state->is_rgb = 0; - state->is_depth_b = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); state->is_rgb = 0; state->is_depth = 1; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); state->is_rgb = 1; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_MOTION_T_A: + case DS5_MUX_PAD_IR: state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 1; state->is_imu = 0; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 1; break; default: state->is_rgb = 0; state->is_depth = 0; - state->is_rgb_b = 0; - state->is_depth_b = 0; state->is_y8 = 0; state->is_imu = 1; break; } } - base = (state->is_rgb || state->is_rgb_b) ? DS5_RGB_CONTROL_BASE : DS5_DEPTH_CONTROL_BASE; + base = (state->is_rgb) ? DS5_RGB_CONTROL_BASE : DS5_DEPTH_CONTROL_BASE; dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s \n", __func__, ds5_get_sensor_name(state), ctrl->name); @@ -2548,14 +2497,14 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ds5_read(state, base | DS5_AUTO_EXPOSURE_MODE, ®); *ctrl->p_new.p_u16 = reg; /* see ds5_hw_set_auto_exposure */ - if (!(state->is_rgb & state->is_rgb_b)) { + if (!state->is_rgb) { if (reg == 1) *ctrl->p_new.p_u16 = V4L2_EXPOSURE_APERTURE_PRIORITY; else if (reg == 0) *ctrl->p_new.p_u16 = V4L2_EXPOSURE_MANUAL; } - if ((state->is_rgb || state->is_rgb_b) && reg == 8) + if (state->is_rgb && reg == 8) *ctrl->p_new.p_u16 = V4L2_EXPOSURE_APERTURE_PRIORITY; break; @@ -2572,12 +2521,12 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) break; case DS5_CAMERA_CID_LASER_POWER: - if (!(state->is_rgb & state->is_rgb_b)) + if (!state->is_rgb) ds5_read(state, base | DS5_LASER_POWER, ctrl->p_new.p_u16); break; case DS5_CAMERA_CID_MANUAL_LASER_POWER: - if (!(state->is_rgb & state->is_rgb_b)) + if (!state->is_rgb) ds5_read(state, base | DS5_MANUAL_LASER_POWER, ctrl->p_new.p_u16); break; @@ -2612,10 +2561,8 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) // if (i == DS5_MAX_LOG_POLL) // return -ETIMEDOUT; - ret = regmap_raw_read(state->regmap, 0x4908, - &data, sizeof(data)); - dev_dbg(&state->client->dev, "%s(): log size 0x%x\n", - __func__, data); + ret = regmap_raw_read(state->regmap, 0x4908, &data, sizeof(data)); + dev_dbg(&state->client->dev, "%s(): log size 0x%x\n", __func__, data); if (ret < 0) return ret; if (!data) @@ -2642,30 +2589,37 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) case DS5_CAMERA_CID_GVD: ret = ds5_gvd(state, ctrl->p_new.p_u8); break; - case DS5_CAMERA_CID_AE_ROI_GET: { - u16 len = 0; - struct hwm_cmd *ae_roi_cmd; - ae_roi_cmd = devm_kzalloc(&state->client->dev, - sizeof(struct hwm_cmd) + 12, GFP_KERNEL); - if (!ae_roi_cmd) { - dev_err(&state->client->dev, - "%s(): Can't allocate memory for 0x%x\n", - __func__, ctrl->id); - ret = -ENOMEM; - break; - } - memcpy(ae_roi_cmd, &get_ae_roi, sizeof(struct hwm_cmd)); - ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), - ae_roi_cmd, true, &len); - memcpy(ctrl->p_new.p_u16, ae_roi_cmd->Data + 4, 8); - devm_kfree(&state->client->dev, ae_roi_cmd); + case DS5_CAMERA_CID_AE_ROI_GET: + if (ctrl->p_new.p_u16) { + u16 len = sizeof(struct hwm_cmd) + 12; + u16 dataLen = 0; + struct hwm_cmd *ae_roi_cmd; + ae_roi_cmd = devm_kzalloc(&state->client->dev, len, GFP_KERNEL); + if (!ae_roi_cmd) { + dev_err(&state->client->dev, + "%s(): Can't allocate memory for 0x%x\n", + __func__, ctrl->id); + ret = -ENOMEM; + break; + } + memcpy(ae_roi_cmd, &get_ae_roi, sizeof(struct hwm_cmd)); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), ae_roi_cmd); + if (ret) { + devm_kfree(&state->client->dev, ae_roi_cmd); + return ret; + } + ret = ds5_get_hwmc(state, ae_roi_cmd->Data, len, &dataLen); + if (!ret && dataLen <= ctrl->dims[0]) + memcpy(ctrl->p_new.p_u16, ae_roi_cmd->Data + 4, 8); + devm_kfree(&state->client->dev, ae_roi_cmd); } break; - case DS5_CAMERA_CID_AE_SETPOINT_GET: { - u16 len = 0; + case DS5_CAMERA_CID_AE_SETPOINT_GET: + if (ctrl->p_new.p_s32) { + u16 len = sizeof(struct hwm_cmd) + 8; + u16 dataLen = 0; struct hwm_cmd *ae_setpoint_cmd; - ae_setpoint_cmd = devm_kzalloc(&state->client->dev, - sizeof(struct hwm_cmd) + 8, GFP_KERNEL); + ae_setpoint_cmd = devm_kzalloc(&state->client->dev, len, GFP_KERNEL); if (!ae_setpoint_cmd) { dev_err(&state->client->dev, "%s(): Can't allocate memory for 0x%x\n", @@ -2674,40 +2628,59 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) break; } memcpy(ae_setpoint_cmd, &get_ae_setpoint, sizeof(struct hwm_cmd)); - ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), - ae_setpoint_cmd, true, &len); + ret = ds5_send_hwmc(state, sizeof(struct hwm_cmd), ae_setpoint_cmd); + if (ret) { + devm_kfree(&state->client->dev, ae_setpoint_cmd); + return ret; + } + ret = ds5_get_hwmc(state, ae_setpoint_cmd->Data, len, &dataLen); memcpy(ctrl->p_new.p_s32, ae_setpoint_cmd->Data + 4, 4); - dev_dbg(&state->client->dev, "%s(): 0x%x \n", - __func__, - *(ctrl->p_new.p_s32)); + dev_dbg(&state->client->dev, "%s(): len: %d, 0x%x \n", + __func__, dataLen, *(ctrl->p_new.p_s32)); devm_kfree(&state->client->dev, ae_setpoint_cmd); } break; - case DS5_CAMERA_CID_HWMC_RW: - ds5_get_hwmc(state, ctrl->p_new.p_u8); + case DS5_CAMERA_CID_HWMC_RW: + if (ctrl->p_new.p_u8) { + unsigned char *data = (unsigned char *)ctrl->p_new.p_u8; + u16 dataLen = 0; + u16 bufLen = ctrl->dims[0]; + ret = ds5_get_hwmc(state, data, bufLen, &dataLen); + if (ret) + break; + /* This is needed for librealsense, to align there code with UVC, + * last word is length - 4 bytes header length */ + dataLen -= 4; + data[bufLen - 4] = (unsigned char)(dataLen & 0x00FF); + data[bufLen - 3] = (unsigned char)((dataLen & 0xFF00) >> 8); + data[bufLen - 2] = 0; + data[bufLen - 1] = 0; + } break; case DS5_CAMERA_CID_PWM: - if (state->is_depth || state->is_depth_b) + if (state->is_depth) ds5_read(state, base | DS5_PWM_FREQUENCY, ctrl->p_new.p_u16); break; +#ifdef CONFIG_VIDEO_INTEL_IPU6 case V4L2_CID_IPU_QUERY_SUB_STREAM: { if (sensor) { - int vc_id = get_sub_stream_vc_id(state->ctrls.query_sub_stream->qmenu_int, - pad_to_substream[sensor->mux_pad]); + int substream = state->pad_to_substream[sensor->mux_pad]; + int vc_id = get_sub_stream_vc_id(substream); dev_dbg(sensor->sd.dev, - "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d vc:[%d]\n", - __func__, sensor->mux_pad, vc_id); - *ctrl->p_new.p_s32 = pad_to_substream[sensor->mux_pad]; + "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d" + "vc:[%d] %d\n", + __func__, sensor->mux_pad, vc_id, substream); + *ctrl->p_new.p_s32 = substream; state->mux.last_set = sensor; - } else { + } else { /* we are in DS5 MUX case */ *ctrl->p_new.p_s32 = -1; - } } + } break; +#endif } - return ret; } @@ -2932,7 +2905,7 @@ static const struct v4l2_ctrl_config ds5_ctrl_pwm = { .def = 1, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, }; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_LINK_FREQ, @@ -2945,64 +2918,16 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .qmenu_int = link_freq_menu_items, }; -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_a = { - .ops = &ds5_ctrl_ops, - .id = V4L2_CID_IPU_QUERY_SUB_STREAM, - .name = "query virtual channel", - .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream[0]) - 1, - .min = 0, - .def = 0, - .menu_skip_mask = 0, - .qmenu_int = d4xx_query_sub_stream[0], -}; - -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_b = { +static struct v4l2_ctrl_config d4xx_controls_q_sub_stream = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_IPU_QUERY_SUB_STREAM, .name = "query virtual channel", .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream[1]) - 1, + .max = NR_OF_DS5_SUB_STREAMS - 1, .min = 0, .def = 0, .menu_skip_mask = 0, - .qmenu_int = d4xx_query_sub_stream[1], -}; - -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_c = { - .ops = &ds5_ctrl_ops, - .id = V4L2_CID_IPU_QUERY_SUB_STREAM, - .name = "query virtual channel", - .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream[2]) - 1, - .min = 0, - .def = 0, - .menu_skip_mask = 0, - .qmenu_int = d4xx_query_sub_stream[2], -}; - -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_d = { - .ops = &ds5_ctrl_ops, - .id = V4L2_CID_IPU_QUERY_SUB_STREAM, - .name = "query virtual channel", - .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream[3]) - 1, - .min = 0, - .def = 0, - .menu_skip_mask = 0, - .qmenu_int = d4xx_query_sub_stream[3], -}; - -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream_e = { - .ops = &ds5_ctrl_ops, - .id = V4L2_CID_IPU_QUERY_SUB_STREAM, - .name = "query virtual channel", - .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream[4]) - 1, - .min = 0, - .def = 0, - .menu_skip_mask = 0, - .qmenu_int = d4xx_query_sub_stream[4], + .qmenu_int = d4xx_query_sub_stream, }; static const struct v4l2_ctrl_config d4xx_controls_s_sub_stream = { @@ -3015,12 +2940,12 @@ static const struct v4l2_ctrl_config d4xx_controls_s_sub_stream = { .def = 0, .step = 1, }; - +#endif static int ds5_mux_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ds5 *state = v4l2_get_subdevdata(sd); - dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); if (state->dfu_dev.dfu_state_flag) return -EBUSY; state->dfu_dev.device_open_count++; @@ -3032,7 +2957,7 @@ static int ds5_mux_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ds5 *state = v4l2_get_subdevdata(sd); - dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); state->dfu_dev.device_open_count--; return 0; }; @@ -3042,6 +2967,523 @@ static const struct v4l2_subdev_internal_ops ds5_sensor_internal_ops = { .close = ds5_mux_close, }; +#ifdef CONFIG_VIDEO_D4XX_SERDES + +/* + * FIXME + * temporary solution before changing GMSL data structure or merging all 4 D457 + * sensors into one i2c device. Only first sensor node per max9295 sets up the + * link. + * + * max 24 number from this link: + * https://docs.nvidia.com/jetson/archives/r35.1/DeveloperGuide/text/ + * SD/CameraDevelopment/JetsonVirtualChannelWithGmslCameraFramework.html + * #jetson-agx-xavier-series + */ +#define MAX_DEV_NUM 24 +static struct ds5 *serdes_inited[MAX_DEV_NUM]; +#ifdef CONFIG_OF +static int ds5_board_setup(struct ds5 *state) +{ + struct device *dev = &state->client->dev; + struct device_node *node = dev->of_node; + struct device_node *ser_node; + struct i2c_client *ser_i2c = NULL; + struct device_node *dser_node; + struct i2c_client *dser_i2c = NULL; + struct device_node *gmsl; + int value = 0xFFFF; + const char *str_value; + int err; + int i; + + err = of_property_read_u32(node, "reg", &state->g_ctx.sdev_reg); + if (err < 0) { + dev_err(dev, "reg not found\n"); + goto error; + } + + err = of_property_read_u32(node, "def-addr", + &state->g_ctx.sdev_def); + if (err < 0) { + dev_err(dev, "def-addr not found\n"); + goto error; + } + + ser_node = of_parse_phandle(node, "maxim,gmsl-ser-device", 0); + if (ser_node == NULL) { + /* check compatibility with jetpack */ + ser_node = of_parse_phandle(node, "nvidia,gmsl-ser-device", 0); + if (ser_node == NULL) { + dev_err(dev, "missing %s handle\n", "[maxim|nvidia],gmsl-ser-device"); + goto error; + } + } + err = of_property_read_u32(ser_node, "reg", &state->g_ctx.ser_reg); + dev_dbg(dev, "serializer reg: 0x%x\n", state->g_ctx.ser_reg); + if (err < 0) { + dev_err(dev, "serializer reg not found\n"); + goto error; + } + + ser_i2c = of_find_i2c_device_by_node(ser_node); + of_node_put(ser_node); + + if (ser_i2c == NULL) { + err = -EPROBE_DEFER; + goto error; + } + if (ser_i2c->dev.driver == NULL) { + dev_err(dev, "missing serializer driver\n"); + goto error; + } + + state->ser_dev = &ser_i2c->dev; + + dser_node = of_parse_phandle(node, "maxim,gmsl-dser-device", 0); + if (dser_node == NULL) { + dser_node = of_parse_phandle(node, "nvidia,gmsl-dser-device", 0); + if (dser_node == NULL) { + dev_err(dev, "missing %s handle\n", "[maxim|nvidia],gmsl-dser-device"); + goto error; + } + } + + dser_i2c = of_find_i2c_device_by_node(dser_node); + of_node_put(dser_node); + + if (dser_i2c == NULL) { + err = -EPROBE_DEFER; + goto error; + } + if (dser_i2c->dev.driver == NULL) { + dev_err(dev, "missing deserializer driver\n"); + goto error; + } + + state->dser_dev = &dser_i2c->dev; + + /* populate g_ctx from DT */ + gmsl = of_get_child_by_name(node, "gmsl-link"); + if (gmsl == NULL) { + dev_err(dev, "missing gmsl-link device node\n"); + err = -EINVAL; + goto error; + } + + err = of_property_read_string(gmsl, "dst-csi-port", &str_value); + if (err < 0) { + dev_err(dev, "No dst-csi-port found\n"); + goto error; + } + state->g_ctx.dst_csi_port = + (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B; + + err = of_property_read_string(gmsl, "src-csi-port", &str_value); + if (err < 0) { + dev_err(dev, "No src-csi-port found\n"); + goto error; + } + state->g_ctx.src_csi_port = + (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B; + + err = of_property_read_string(gmsl, "csi-mode", &str_value); + if (err < 0) { + dev_err(dev, "No csi-mode found\n"); + goto error; + } + + if (!strcmp(str_value, "1x4")) { + state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; + } else if (!strcmp(str_value, "2x4")) { + state->g_ctx.csi_mode = GMSL_CSI_2X4_MODE; + } else if (!strcmp(str_value, "4x2")) { + state->g_ctx.csi_mode = GMSL_CSI_4X2_MODE; + } else if (!strcmp(str_value, "2x2")) { + state->g_ctx.csi_mode = GMSL_CSI_2X2_MODE; + } else { + dev_err(dev, "invalid csi mode\n"); + goto error; + } + + err = of_property_read_string(gmsl, "serdes-csi-link", &str_value); + if (err < 0) { + dev_err(dev, "No serdes-csi-link found\n"); + goto error; + } + state->g_ctx.serdes_csi_link = + (!strcmp(str_value, "a")) ? + GMSL_SERDES_CSI_LINK_A : GMSL_SERDES_CSI_LINK_B; + + err = of_property_read_u32(gmsl, "st-vc", &value); + if (err < 0) { + dev_err(dev, "No st-vc info\n"); + goto error; + } + state->g_ctx.st_vc = value; + + err = of_property_read_u32(gmsl, "vc-id", &value); + if (err < 0) { + dev_err(dev, "No vc-id info\n"); + goto error; + } + state->g_ctx.dst_vc = value; + + err = of_property_read_u32(gmsl, "num-lanes", &value); + if (err < 0) { + dev_err(dev, "No num-lanes info\n"); + goto error; + } + state->g_ctx.num_csi_lanes = value; + state->g_ctx.s_dev = dev; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (!serdes_inited[i]) { + serdes_inited[i] = state; + return 0; + } else if (serdes_inited[i]->ser_dev == state->ser_dev) { + return -ENOTSUPP; + } + } + err = -EINVAL; + dev_err(dev, "cannot handle more than %d D457 cameras\n", MAX_DEV_NUM); + +error: + return err; +} +#else +// ds5mux i2c ser des +// mux a - 2 0x42 0x48 +// mux b - 2 0x44 0x4a +// mux c - 4 0x42 0x48 +// mux d - 4 0x44 0x4a +// axiomtek +// mux a - 2 0x42 0x48 +// mux b - 2 0x44 0x4a +// mux c - 4 0x62 0x68 +// mux d - 4 0x64 0x6a + +static int ds5_board_setup(struct ds5 *state) +{ + struct device *dev = &state->client->dev; + struct d4xx_pdata *pdata = dev->platform_data; + struct i2c_adapter *adapter = state->client->adapter; + int bus = adapter->nr; + int err = 0; + int i; + char suffix = pdata->suffix; + static struct max9295_pdata max9295_pdata = { + .is_prim_ser = 1, // todo: configurable + .def_addr = 0x40, // todo: configurable + }; + static struct max9296_pdata max9296_pdata = { + .max_src = 2, + .csi_mode = GMSL_CSI_2X4_MODE, + }; + static struct i2c_board_info i2c_info_des = { + I2C_BOARD_INFO("max9296", 0x48), + .platform_data = &max9296_pdata, + }; + static struct i2c_board_info i2c_info_ser = { + I2C_BOARD_INFO("max9295", 0x42), + .platform_data = &max9295_pdata, + }; + + i2c_info_ser.addr = pdata->subdev_info[0].ser_alias; //0x42, 0x44, 0x62, 0x64 + state->ser_i2c = i2c_new_client_device(adapter, &i2c_info_ser); + + i2c_info_des.addr = pdata->subdev_info[0].board_info.addr; //0x48, 0x4a, 0x68, 0x6a + + /* look for already registered max9296, use same context if found */ + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { + dev_info(dev, "MAX9296 found device on %d@0x%x\n", + serdes_inited[i]->dser_i2c->adapter->nr, serdes_inited[i]->dser_i2c->addr); + if (bus == serdes_inited[i]->dser_i2c->adapter->nr + && serdes_inited[i]->dser_i2c->addr == i2c_info_des.addr) { + dev_info(dev, "MAX9296 AGGREGATION found device on 0x%x\n", i2c_info_des.addr); + state->dser_i2c = serdes_inited[i]->dser_i2c; + state->aggregated = 1; + } + } + } + if (state->aggregated) + suffix += 4; + dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", + suffix, + bus, pdata->subdev_info[0].board_info.addr, //48 + bus, pdata->subdev_info[0].ser_alias); //42 + + if (!state->dser_i2c) + state->dser_i2c = i2c_new_client_device(adapter, &i2c_info_des); + + if (state->ser_i2c == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing serializer client\n"); + goto error; + } + if (state->ser_i2c->dev.driver == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing serializer driver\n"); + goto error; + } + if (state->dser_i2c == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing deserializer client\n"); + goto error; + } + if (state->dser_i2c->dev.driver == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing deserializer driver\n"); + goto error; + } + + // reg + + state->g_ctx.sdev_reg = state->client->addr; + state->g_ctx.sdev_def = 0x10;// def-addr TODO: configurable + // Address reassignment for d4xx-a 0x10->0x12 + dev_info(dev, "Address reassignment for %s-%c 0x%x->0x%x\n", + pdata->subdev_info[0].board_info.type, suffix, + state->g_ctx.sdev_def, state->g_ctx.sdev_reg); + //0x42, 0x44, 0x62, 0x64 + state->g_ctx.ser_reg = pdata->subdev_info[0].ser_alias; + dev_info(dev, "serializer: i2c-%d@0x%x\n", + state->ser_i2c->adapter->nr, state->g_ctx.ser_reg); + + if (err < 0) { + dev_err(dev, "serializer reg not found\n"); + goto error; + } + + state->ser_dev = &state->ser_i2c->dev; + + dev_info(dev, "deserializer: i2c-%d@0x%x\n", + state->dser_i2c->adapter->nr, state->dser_i2c->addr); + + + state->dser_dev = &state->dser_i2c->dev; + + /* populate g_ctx from pdata */ + state->g_ctx.dst_csi_port = GMSL_CSI_PORT_A; + state->g_ctx.src_csi_port = GMSL_CSI_PORT_B; + state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; + if (state->aggregated) { // aggregation + dev_info(dev, "configure GMSL port B\n"); + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; + } else { + dev_info(dev, "configure GMSL port A\n"); + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; + } + state->g_ctx.st_vc = 0; + state->g_ctx.dst_vc = 0; + + state->g_ctx.num_csi_lanes = 2; + state->g_ctx.s_dev = dev; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (!serdes_inited[i]) { + serdes_inited[i] = state; + return 0; + } else if (serdes_inited[i]->ser_dev == state->ser_dev) { + return -ENOTSUPP; + } + } + err = -EINVAL; + dev_err(dev, "cannot handle more than %d D457 cameras\n", MAX_DEV_NUM); + +error: + return err; +} + +#endif +static const struct regmap_config ds5_regmap_max9296 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static const struct regmap_config ds5_regmap_max9295 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; +static struct mutex serdes_lock__; + +static int ds5_gmsl_serdes_setup(struct ds5 *state) +{ + int err = 0; + int des_err = 0; + struct device *dev; + + if (!state || !state->ser_dev || !state->dser_dev || !state->client) + return -EINVAL; + + dev = &state->client->dev; + + mutex_lock(&serdes_lock__); + + max9296_power_off(state->dser_dev); + /* For now no separate power on required for serializer device */ + max9296_power_on(state->dser_dev); + + dev_dbg(dev, "Setup SERDES addressing and control pipeline\n"); + /* setup serdes addressing and control pipeline */ + err = max9296_setup_link(state->dser_dev, &state->client->dev); + if (err) { + dev_err(dev, "gmsl deserializer link config failed\n"); + goto error; + } + msleep(100); + err = max9295_setup_control(state->ser_dev); + + /* proceed even if ser setup failed, to setup deser correctly */ + if (err) + dev_err(dev, "gmsl serializer setup failed\n"); + + des_err = max9296_setup_control(state->dser_dev, &state->client->dev); + if (des_err) { + dev_err(dev, "gmsl deserializer setup failed\n"); + /* overwrite err only if deser setup also failed */ + err = des_err; + } + +error: + mutex_unlock(&serdes_lock__); + return err; +} + +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; +module_param_array(sensor_vc, ushort, NULL, 0444); +MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" + "\t\tsensor_vc=0,1,2,3,2,3,0,1"); + +//#define PLATFORM_AXIOMTEK 1 +#ifdef PLATFORM_AXIOMTEK +static short serdes_bus[4] = {5, 5, 5, 5}; +#else +static short serdes_bus[4] = {2, 2, 4, 4}; +#endif +module_param_array(serdes_bus, ushort, NULL, 0444); +MODULE_PARM_DESC(serdes_bus, "max9295/6 deserializer i2c bus\n" + "\t\tserdes_bus=2,2,4,4"); + +// Deserializer addresses can be 0x40 0x48 0x4a +#ifdef PLATFORM_AXIOMTEK +static unsigned short des_addr[4] = {0x48, 0x4a, 0x68, 0x6c}; +#else +static unsigned short des_addr[4] = {0x48, 0x4a, 0x48, 0x4a}; +#endif +module_param_array(des_addr, ushort, NULL, 0444); +MODULE_PARM_DESC(des_addr, "max9296 deserializer i2c address\n" + "\t\tdes_addr=0x48,0x4a,0x48,0x4a"); + + +static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) +{ + int i = 0; + int c_addr_save = c->addr; + int c_bus = c->adapter->nr; + for (i = 0; i < 4; i++) { + if (c_bus == serdes_bus[i]) { + c->addr = des_addr[i]; + dev_info(&c->dev, "Set max9296@%d-0x%x Link reset\n", + c_bus, c->addr); + ds5_write_8(state, 0x1000, 0x40); // reset link + } + } + // restore original slave address + c->addr = c_addr_save; + + return 0; +} +#endif +static int ds5_serdes_setup(struct ds5 *state) +{ + int ret = 0; + struct i2c_client *c = state->client; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + int i = 0, c_bus = 0; + int c_bus_new = c->adapter->nr; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { + c_bus = serdes_inited[i]->dser_i2c->adapter->nr; + if (c_bus == c->adapter->nr) { + dev_info(&c->dev, "Already configured multiple camera for bus %d\n", c_bus); + c_bus_new = 0; + break; + } + } else { + break; + } + } + + if (c_bus_new) { + dev_info(&c->dev, "Apply multiple camera i2c addr setting for bus %d\n", c_bus_new); + ret = ds5_i2c_addr_setting(c, state); + if (ret) { + dev_err(&c->dev, "failed apply i2c addr setting\n"); + return ret; + } + } +#endif + ret = ds5_board_setup(state); + if (ret) { + if (ret == -ENOTSUPP) + return 0; + dev_err(&c->dev, "board setup failed\n"); + return ret; + } + + /* Pair sensor to serializer dev */ + ret = max9295_sdev_pair(state->ser_dev, &state->g_ctx); + if (ret) { + dev_err(&c->dev, "gmsl ser pairing failed\n"); + return ret; + } + + /* Register sensor to deserializer dev */ + ret = max9296_sdev_register(state->dser_dev, &state->g_ctx); + if (ret) { + dev_err(&c->dev, "gmsl deserializer register failed\n"); + return ret; + } + + ret = ds5_gmsl_serdes_setup(state); + if (ret) { + dev_err(&c->dev, "%s gmsl serdes setup failed\n", __func__); + return ret; + } + + ret = max9295_init_settings(state->ser_dev); + if (ret) { + dev_warn(&c->dev, "%s, failed to init max9295 settings\n", + __func__); + return ret; + } + + ret = max9296_init_settings(state->dser_dev); + if (ret) { + dev_warn(&c->dev, "%s, failed to init max9296 settings\n", + __func__); + return ret; + } + + return ret; +} +#endif +enum state_sid { + DEPTH_SID = 0, + RGB_SID, + IR_SID, + IMU_SID, + MUX_SID = -1 +}; + static int ds5_ctrl_init(struct ds5 *state, int sid) { const struct v4l2_ctrl_ops *ops = &ds5_ctrl_ops; @@ -3049,48 +3491,40 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) struct v4l2_ctrl_handler *hdl = &ctrls->handler; struct v4l2_subdev *sd = &state->mux.sd.subdev; int ret = -1; - char d4xx_name; struct ds5_sensor *sensor = NULL; switch (sid) { - case DS5_SID_DEPTH_A: + case DEPTH_SID: hdl = &ctrls->handler_depth; sensor = &state->depth.sensor; break; - case DS5_SID_RGB_A: + case RGB_SID: hdl = &ctrls->handler_rgb; sensor = &state->rgb.sensor; break; - case DS5_SID_MOTION_T_A: + case IR_SID: hdl = &ctrls->handler_y8; - sensor = &state->motion_t.sensor; + sensor = &state->ir.sensor; break; - case DS5_SID_IMU_A: + case IMU_SID: hdl = &ctrls->handler_imu; sensor = &state->imu.sensor; break; - case DS5_SID_DEPTH_B: - hdl = &ctrls->handler_depth_b; - sensor = &state->depth_b.sensor; - break; - case DS5_SID_RGB_B: - hdl = &ctrls->handler_rgb_b; - sensor = &state->rgb_b.sensor; - break; default: + /* control for MUX */ hdl = &ctrls->handler; sensor = NULL; break; } - dev_info(NULL, "%s(), line %d sid: %d\n", __func__, __LINE__, sid); + dev_dbg(NULL, "%s():%d sid: %d\n", __func__, __LINE__, sid); ret = v4l2_ctrl_handler_init(hdl, DS5_N_CONTROLS); if (ret < 0) { v4l2_err(sd, "cannot init ctrl handler (%d)\n", ret); return ret; } - if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->laser_power = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_laser_power, sensor); @@ -3100,22 +3534,22 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) } /* Total gain */ - if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 16, 248, 1, 16); - } else if (sid == DS5_SID_RGB_A || sid == DS5_SID_RGB_B) { + } else if (sid == RGB_SID) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 0, 128, 1, 64); } - if ((ctrls->gain) && (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B)) { + if ((ctrls->gain) && (sid >= DEPTH_SID && sid < IMU_SID)) { ctrls->gain->priv = sensor; ctrls->gain->flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; } - if (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B) { + if (sid >= DEPTH_SID && sid < IMU_SID) { ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_EXPOSURE_AUTO, @@ -3130,64 +3564,44 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) ctrls->auto_exp->priv = sensor; } } + /* Exposure time: V4L2_CID_EXPOSURE_ABSOLUTE default unit: 100 us. */ - if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_MOTION_T_A || sid == DS5_SID_DEPTH_B) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, MAX_DEPTH_EXP, 1, DEF_DEPTH_EXP); - } else if (sid == DS5_SID_RGB_A || sid == DS5_SID_RGB_B) { + } else if (sid == RGB_SID) { ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, MAX_RGB_EXP, 1, DEF_RGB_EXP); } - if ((ctrls->exposure) && (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B)) { + if ((ctrls->exposure) && (sid >= DEPTH_SID && sid < IMU_SID)) { ctrls->exposure->priv = sensor; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; /* override default int type to u32 to match SKU & UVC */ ctrls->exposure->type = V4L2_CTRL_TYPE_U32; } - +#ifdef CONFIG_VIDEO_INTEL_IPU6 ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); - dev_info(sd->dev, "%s(): %p\n", __func__, ctrls->link_freq); - if (ctrls->link_freq) - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - d4xx_name = (sd->name)[strlen(sd->name)-1]; - dev_info(sd->dev, "%s(): d4xx_name is %c\n", __func__, d4xx_name); - switch (d4xx_name) { - case 'a': - ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_a, sensor); - break; - - case 'b': - ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_b, sensor); - break; - case 'c': - ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_c, sensor); - break; - - case 'd': - ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_d, sensor); - break; - - case 'e': - ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream_e, sensor); - break; + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - default: - dev_err(state->rgb.sensor.sd.dev, - "%s():!! can not get d4xx_controls_q_sub_stream: sd->name is %s\n", - __func__, sd->name); - break; + if (state->aggregated) { + d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; + d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; + d4xx_controls_q_sub_stream.max = NR_OF_DS5_SUB_STREAMS * 2 - 1; } - ctrls->query_sub_stream->flags |= - V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream, sensor); + + if (ctrls->query_sub_stream) + ctrls->query_sub_stream->flags |= + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; ctrls->set_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_s_sub_stream, sensor); - dev_info(NULL, "%s(), line %d\n", __func__, __LINE__); +#endif if (hdl->error) { v4l2_err(sd, "error creating controls (%d)\n", hdl->error); ret = hdl->error; @@ -3195,16 +3609,8 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) return ret; } - // ret = v4l2_ctrl_handler_setup(hdl); - // if (ret < 0) { - // dev_err(&state->client->dev, - // "failed to set default values for controls\n"); - // v4l2_ctrl_handler_free(hdl); - // return ret; - // } - // Add these after v4l2_ctrl_handler_setup so they won't be set up - if (sid >= DS5_SID_DEPTH_A && sid <= DS5_SID_RGB_B) { + if (sid >= DEPTH_SID && sid < IMU_SID) { ctrls->log = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_log, sensor); ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); ctrls->gvd = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_gvd, sensor); @@ -3228,52 +3634,40 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor); } // DEPTH custom - if (sid == DS5_SID_DEPTH_A || sid == DS5_SID_DEPTH_B) + if (sid == DEPTH_SID) v4l2_ctrl_new_custom(hdl, &ds5_ctrl_pwm, sensor); // IMU custom - if (sid == DS5_SID_IMU_A) + if (sid == IMU_SID) ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); switch (sid) { - case DS5_SID_DEPTH_A: + case DEPTH_SID: state->depth.sensor.sd.ctrl_handler = hdl; - dev_info(state->depth.sensor.sd.dev, + dev_dbg(state->depth.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->depth.sensor.mux_pad); break; - case DS5_SID_RGB_A: + case RGB_SID: state->rgb.sensor.sd.ctrl_handler = hdl; - dev_info(state->rgb.sensor.sd.dev, + dev_dbg(state->rgb.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->rgb.sensor.mux_pad); break; - case DS5_SID_MOTION_T_A: - state->motion_t.sensor.sd.ctrl_handler = hdl; - dev_info(state->motion_t.sensor.sd.dev, + case IR_SID: + state->ir.sensor.sd.ctrl_handler = hdl; + dev_dbg(state->ir.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->motion_t.sensor.mux_pad); + __func__, __LINE__, state->ir.sensor.mux_pad); break; - case DS5_SID_IMU_A: + case IMU_SID: state->imu.sensor.sd.ctrl_handler = hdl; - dev_info(state->imu.sensor.sd.dev, + dev_dbg(state->imu.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->imu.sensor.mux_pad); break; - case DS5_SID_DEPTH_B: - state->depth_b.sensor.sd.ctrl_handler = hdl; - dev_info(state->depth_b.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->depth_b.sensor.mux_pad); - break; - case DS5_SID_RGB_B: - state->rgb_b.sensor.sd.ctrl_handler = hdl; - dev_info(state->rgb_b.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->rgb_b.sensor.mux_pad); - break; default: state->mux.sd.subdev.ctrl_handler = hdl; - dev_info(state->mux.sd.subdev.dev, + dev_dbg(state->mux.sd.subdev.dev, "%s():%d set ctrl_handler for MUX\n", __func__, __LINE__); break; } @@ -3289,22 +3683,28 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, struct media_entity *entity = &sensor->sd.entity; struct media_pad *pad = &sensor->pad; dev_t *dev_num = &state->client->dev.devt; +#ifndef CONFIG_OF struct d4xx_pdata *dpdata = c->dev.platform_data; - + char suffix = dpdata->suffix; +#endif v4l2_i2c_subdev_init(sd, c, ops); + // See tegracam_v4l2.c tegracam_v4l2subdev_register() + // Set owner to NULL so we can unload the driver module sd->owner = NULL; sd->internal_ops = &ds5_sensor_internal_ops; sd->grp_id = *dev_num; v4l2_set_subdevdata(sd, state); +#ifndef CONFIG_OF /* * TODO: suffix for 2 D457 connected to 1 Deser */ - dev_dbg(&state->client->dev, "%s() sensor->mux_pad = %d\n", __func__, sensor->mux_pad); - - if (sensor->mux_pad >= DS5_MUX_PAD_DEPTH_A && sensor->mux_pad <= DS5_MUX_PAD_IMU_A) - snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[0].suffix); - else if (sensor->mux_pad == DS5_MUX_PAD_RGB_B || sensor->mux_pad == DS5_MUX_PAD_DEPTH_B) - snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[1].suffix); + if (state->aggregated & 1) + suffix += 4; + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, suffix); +#else + snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", + name, i2c_adapter_id(c->adapter), c->addr); +#endif sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -3320,7 +3720,6 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) struct media_entity *entity = &sensor->sd.entity; int ret = -1; - dev_dbg(sd->dev, "%s(): sd->name = %s sensor->mux_pad = %d\n", __func__, sd->name, sensor->mux_pad); // FIXME: is async needed? ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); if (ret < 0) { @@ -3336,7 +3735,7 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) goto e_sd; } - dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); + dev_dbg(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); return 0; @@ -3356,43 +3755,28 @@ static void ds5_sensor_remove(struct ds5_sensor *sensor) static int ds5_depth_init(struct i2c_client *c, struct ds5 *state) { /* Which mux pad we're connecting to */ - state->depth.sensor.mux_pad = DS5_MUX_PAD_DEPTH_A; + state->depth.sensor.mux_pad = DS5_MUX_PAD_DEPTH; return ds5_sensor_init(c, state, &state->depth.sensor, &ds5_depth_subdev_ops, "depth"); } -static int ds5_depth_b_init(struct i2c_client *c, struct ds5 *state) -{ - /* Which mux pad we're connecting to */ - state->depth_b.sensor.mux_pad = DS5_MUX_PAD_DEPTH_B; - return ds5_sensor_init(c, state, &state->depth_b.sensor, - &ds5_depth_subdev_ops, "depth"); -} - -static int ds5_motion_t_init(struct i2c_client *c, struct ds5 *state) +static int ds5_ir_init(struct i2c_client *c, struct ds5 *state) { - state->motion_t.sensor.mux_pad = DS5_MUX_PAD_MOTION_T_A; - return ds5_sensor_init(c, state, &state->motion_t.sensor, - &ds5_motion_t_subdev_ops, "motion detection"); + state->ir.sensor.mux_pad = DS5_MUX_PAD_IR; + return ds5_sensor_init(c, state, &state->ir.sensor, + &ds5_ir_subdev_ops, "ir"); } static int ds5_rgb_init(struct i2c_client *c, struct ds5 *state) { - state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB_A; + state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB; return ds5_sensor_init(c, state, &state->rgb.sensor, &ds5_rgb_subdev_ops, "rgb"); } -static int ds5_rgb_b_init(struct i2c_client *c, struct ds5 *state) -{ - state->rgb_b.sensor.mux_pad = DS5_MUX_PAD_RGB_B; - return ds5_sensor_init(c, state, &state->rgb_b.sensor, - &ds5_rgb_subdev_ops, "rgb"); -} - static int ds5_imu_init(struct i2c_client *c, struct ds5 *state) { - state->imu.sensor.mux_pad = DS5_MUX_PAD_IMU_A; + state->imu.sensor.mux_pad = DS5_MUX_PAD_IMU; return ds5_sensor_init(c, state, &state->imu.sensor, &ds5_imu_subdev_ops, "imu"); } @@ -3411,28 +3795,22 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev *remote_sd; int ret = -1; - dev_info(&state->client->dev, "%s(): %s \n", __func__, sd->name); + dev_dbg(&state->client->dev, "%s(): %s \n", __func__, sd->name); switch (mce->pad) { - case DS5_MUX_PAD_DEPTH_B: - remote_sd = &state->depth_b.sensor.sd; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_RGB_B: - remote_sd = &state->rgb_b.sensor.sd; - break; - case DS5_MUX_PAD_MOTION_T_A: - remote_sd = &state->motion_t.sensor.sd; - break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: - if (mce->index >= state->motion_t.sensor.n_formats + + if (mce->index >= state->ir.sensor.n_formats + state->depth.sensor.n_formats) return -EINVAL; @@ -3441,10 +3819,10 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, * This should also help because D16 doesn't have a direct * analog in MIPI CSI-2. */ - if (mce->index < state->motion_t.sensor.n_formats) { - remote_sd = &state->motion_t.sensor.sd; + if (mce->index < state->ir.sensor.n_formats) { + remote_sd = &state->ir.sensor.sd; } else { - tmp.index = mce->index - state->motion_t.sensor.n_formats; + tmp.index = mce->index - state->ir.sensor.n_formats; remote_sd = &state->depth.sensor.sd; } @@ -3458,19 +3836,13 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, remote_sd = &state->rgb.sensor.sd; if (state->is_depth) remote_sd = &state->depth.sensor.sd; - if (state->is_rgb_b) - remote_sd = &state->rgb_b.sensor.sd; - if (state->is_depth_b) - remote_sd = &state->depth_b.sensor.sd; if (state->is_y8) - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; if (state->is_imu) remote_sd = &state->imu.sensor.sd; - /* Locks internally */ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) ret = ds5_sensor_enum_mbus_code(remote_sd, cfg, &tmp); - struct v4l2_subdev_pad_config *cfg, #else ret = ds5_sensor_enum_mbus_code(remote_sd, v4l2_state, &tmp); #endif @@ -3479,6 +3851,18 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, return ret; } +static int ds5_state_to_pad(struct ds5 *state) { + int pad = -1; + if (state->is_depth) + pad = DS5_MUX_PAD_DEPTH; + if (state->is_y8) + pad = DS5_MUX_PAD_IR; + if (state->is_rgb) + pad = DS5_MUX_PAD_RGB; + if (state->is_imu) + pad = DS5_MUX_PAD_IMU; + return pad; +} /* No locking needed */ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, @@ -3496,37 +3880,19 @@ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, int ret = -1; tmp.pad = 0; - - if (state->is_depth) - pad = DS5_MUX_PAD_DEPTH_A; - if (state->is_y8) - pad = DS5_MUX_PAD_MOTION_T_A; - if (state->is_rgb) - pad = DS5_MUX_PAD_RGB_A; - if (state->is_imu) - pad = DS5_MUX_PAD_IMU_A; - if (state->is_depth_b) - pad = DS5_MUX_PAD_DEPTH_B; - if (state->is_rgb_b) - pad = DS5_MUX_PAD_RGB_B; + pad = ds5_state_to_pad(state); switch (pad) { - case DS5_MUX_PAD_MOTION_T_A: - remote_sd = &state->motion_t.sensor.sd; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_DEPTH_B: - remote_sd = &state->depth_b.sensor.sd; - break; - case DS5_MUX_PAD_RGB_B: - remote_sd = &state->rgb_b.sensor.sd; - break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: @@ -3542,7 +3908,7 @@ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, return 0; } - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; break; default: return -EINVAL; @@ -3575,50 +3941,31 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, tmp.pad = 0; - dev_err(state->depth.sensor.sd.dev, + dev_dbg(state->depth.sensor.sd.dev, "%s(): pad %d code %x width %d height %d\n", __func__, pad, tmp.code, tmp.width, tmp.height); - if (state->is_depth) - pad = DS5_MUX_PAD_DEPTH_A; - if (state->is_y8) - pad = DS5_MUX_PAD_MOTION_T_A; - if (state->is_rgb) - pad = DS5_MUX_PAD_RGB_A; - if (state->is_imu) - pad = DS5_MUX_PAD_IMU_A; - if (state->is_depth_b) - pad = DS5_MUX_PAD_DEPTH_B; - if (state->is_rgb_b) - pad = DS5_MUX_PAD_RGB_B; + pad = ds5_state_to_pad(state); switch (pad) { - case DS5_MUX_PAD_DEPTH_B: - remote_sd = &state->depth_b.sensor.sd; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_RGB_B: - remote_sd = &state->rgb_b.sensor.sd; - break; - case DS5_MUX_PAD_MOTION_T_A: - remote_sd = &state->motion_t.sensor.sd; - break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: /* Similar to ds5_mux_enum_frame_size() above */ if (state->is_rgb) remote_sd = &state->rgb.sensor.sd; - else if (state->is_rgb_b) - remote_sd = &state->rgb_b.sensor.sd; else - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; ret = ds5_sensor_enum_frame_interval(remote_sd, NULL, &tmp); if (!ret) { *fie = tmp; @@ -3626,7 +3973,7 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, return 0; } - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; break; default: return -EINVAL; @@ -3655,19 +4002,25 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *ffmt; struct ds5_sensor *sensor = state->mux.last_set; u32 pad = sensor->mux_pad; - s64 *query_sub_stream = NULL; int ret = 0; +#ifdef CONFIG_VIDEO_INTEL_IPU6 int substream = -1; + dev_dbg(sd->dev, "%s:%d: fmt->pad:%d, sensor->mux_pad: %d, \ + for sensor: %s\n", + __func__, __LINE__, + fmt->pad, pad, + sensor->sd.name); + +#endif + if (pad != DS5_MUX_PAD_EXTERNAL) - ds5_s_state(state, pad - 1); + ds5_s_state_pad(state, pad); sensor = state->mux.last_set; switch (pad) { - case DS5_MUX_PAD_DEPTH_A: - case DS5_MUX_PAD_DEPTH_B: - case DS5_MUX_PAD_MOTION_T_A: - case DS5_MUX_PAD_RGB_A: - case DS5_MUX_PAD_RGB_B: - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_DEPTH: + case DS5_MUX_PAD_IR: + case DS5_MUX_PAD_RGB: + case DS5_MUX_PAD_IMU: //ffmt = &ds5_ffmts[pad]; ffmt = &sensor->format;//ds5_ffmts[pad]; break; @@ -3684,31 +4037,24 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, ffmt->code = fmt->format.code; } fmt->format = *ffmt; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 // substream = pad_to_substream[fmt->pad]; - substream = pad_to_substream[pad]; + substream = state->pad_to_substream[pad]; if (substream != -1) { - query_sub_stream = match_d4xx_sub_stream_qmenu(state->ctrls.query_sub_stream->qmenu_int); - if (query_sub_stream) { - set_sub_stream_fmt(query_sub_stream, substream, ffmt->code); - set_sub_stream_h(query_sub_stream, substream, ffmt->height); - set_sub_stream_w(query_sub_stream, substream, ffmt->width); - set_sub_stream_dt(query_sub_stream, substream, mbus_code_to_mipi(ffmt->code)); - } else { - dev_err(sd->dev, - "failed to find sub stream fmt info: %s\n", - sensor->sd.name); - } + set_sub_stream_fmt(substream, ffmt->code); + set_sub_stream_h(substream, ffmt->height); + set_sub_stream_w(substream, ffmt->width); + set_sub_stream_dt(substream, mbus_code_to_mipi(ffmt->code)); } - dev_info(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad: %d, \ + dev_dbg(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad: %d, \ code: 0x%x: %ux%u substream:%d for sensor: %s\n", __func__, fmt->pad, pad, fmt->format.code, fmt->format.width, fmt->format.height, substream, sensor->sd.name); - +#endif return ret; } @@ -3722,24 +4068,25 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - // u32 pad = fmt->pad; + u32 pad = fmt->pad; int ret = 0; struct ds5_sensor *sensor = state->mux.last_set; - u32 pad = sensor->mux_pad; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + pad = sensor->mux_pad; if (pad != DS5_MUX_PAD_EXTERNAL) - ds5_s_state(state, pad - 1); + ds5_s_state_pad(state, pad); +#else + pad = ds5_state_to_pad(state); +#endif sensor = state->mux.last_set; - dev_info(sd->dev, "%s(): %u %s %p\n", __func__, pad, ds5_get_sensor_name(state), state->mux.last_set); + dev_dbg(sd->dev, "%s(): %u %s %p\n", __func__, pad, ds5_get_sensor_name(state), state->mux.last_set); switch (pad) { - case DS5_MUX_PAD_DEPTH_A: - case DS5_MUX_PAD_DEPTH_B: - case DS5_MUX_PAD_MOTION_T_A: - case DS5_MUX_PAD_RGB_A: - case DS5_MUX_PAD_RGB_B: - case DS5_MUX_PAD_IMU_A: - //fmt->format = ds5_ffmts[pad]; - fmt->format = sensor->format;//ds5_ffmts[pad]; + case DS5_MUX_PAD_DEPTH: + case DS5_MUX_PAD_IR: + case DS5_MUX_PAD_RGB: + case DS5_MUX_PAD_IMU: + fmt->format = sensor->format; break; case DS5_MUX_PAD_EXTERNAL: fmt->format = ds5_ffmts[pad]; @@ -3775,7 +4122,7 @@ static int ds5_mux_g_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = sensor->config.framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, fi->interval.denominator); return 0; @@ -3818,11 +4165,13 @@ static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); return 0; } +#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifndef CONFIG_VIDEO_D4XX_SERDES static int d4xx_reset_oneshot(struct ds5 *state) { struct d4xx_pdata *dpdata = state->client->dev.platform_data; @@ -3831,17 +4180,12 @@ static int d4xx_reset_oneshot(struct ds5 *state) int s_addr = state->client->addr; int n_addr = deser->addr; int ret = 0; - u8 val = 0; if (n_addr) { state->client->addr = n_addr; - dev_warn(&state->client->dev, - "One-shot reset n_addr = 0x%x s_addr=0x%x\n", n_addr, s_addr); + dev_warn(&state->client->dev, "One-shot reset 0x%x enable auto-link\n", n_addr); /* One-shot reset enable auto-link */ - /* 0x23 a+a case*/ - ret = max9296_read_8(state, MAX9296_CTRL0, &val); - dev_dbg(&state->client->dev, "read current mode: %d\n", val); - ret = max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | val); + ret = max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | AUTO_LINK | LINK_A); state->client->addr = s_addr; /* delay to settle link */ msleep(100); @@ -3849,7 +4193,25 @@ static int d4xx_reset_oneshot(struct ds5 *state) return ret; } +#endif +static int ds5_state_to_vc(struct ds5 *state) { + int pad = 0; + if (state->is_depth) { + pad = DS5_MUX_PAD_DEPTH; + } + if (state->is_rgb) { + pad = DS5_MUX_PAD_RGB; + } + if (state->is_y8) { + pad = DS5_MUX_PAD_IR; + } + if (state->is_imu) { + pad = DS5_MUX_PAD_IMU; + } + return state->pad_to_vc[pad]; +} +#endif static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); @@ -3858,66 +4220,64 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) unsigned int i = 0; int restore_val = 0; u16 config_status_base, stream_status_base, stream_id, vc_id; - struct ds5_sensor *sensor; - struct d4xx_pdata *dpdata = state->client->dev.platform_data; + struct ds5_sensor *sensor = state->mux.last_set; // spare duplicate calls - if (state->mux.last_set->streaming == on) + if (sensor->streaming == on) return 0; if (state->is_depth) { - sensor = &state->depth.sensor; config_status_base = DS5_DEPTH_CONFIG_STATUS; stream_status_base = DS5_DEPTH_STREAM_STATUS; stream_id = DS5_STREAM_DEPTH; vc_id = 0; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else if (state->is_rgb) { - sensor = &state->rgb.sensor; config_status_base = DS5_RGB_CONFIG_STATUS; stream_status_base = DS5_RGB_STREAM_STATUS; stream_id = DS5_STREAM_RGB; vc_id = 1; - state->client->addr = dpdata->subdev_info[0].board_info.addr; - } else if (state->is_depth_b) { - sensor = &state->depth_b.sensor; - config_status_base = DS5_DEPTH_CONFIG_STATUS; - stream_status_base = DS5_DEPTH_STREAM_STATUS; - stream_id = DS5_STREAM_DEPTH; - vc_id = 2; - state->client->addr = dpdata->subdev_info[1].board_info.addr; - } else if (state->is_rgb_b) { - sensor = &state->rgb_b.sensor; - config_status_base = DS5_RGB_CONFIG_STATUS; - stream_status_base = DS5_RGB_STREAM_STATUS; - stream_id = DS5_STREAM_RGB; - vc_id = 3; - state->client->addr = dpdata->subdev_info[1].board_info.addr; } else if (state->is_y8) { - sensor = &state->motion_t.sensor; config_status_base = DS5_IR_CONFIG_STATUS; stream_status_base = DS5_IR_STREAM_STATUS; stream_id = DS5_STREAM_IR; vc_id = 2; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else if (state->is_imu) { - sensor = &state->imu.sensor; config_status_base = DS5_IMU_CONFIG_STATUS; stream_status_base = DS5_IMU_STREAM_STATUS; stream_id = DS5_STREAM_IMU; vc_id = 3; - state->client->addr = dpdata->subdev_info[0].board_info.addr; } else { return -EINVAL; } +#ifdef CONFIG_VIDEO_INTEL_IPU6 + vc_id = ds5_state_to_vc(state); +#endif +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#ifdef CONFIG_VIDEO_D4XX_SERDES + vc_id = state->g_ctx.dst_vc; +#endif +#endif + dev_dbg(&state->client->dev, "s_stream for stream %s, vc:%d, SENSOR=%s on = %d\n", + sensor->sd.name, vc_id, ds5_get_sensor_name(state), on); - dev_warn(&state->client->dev, "s_stream for stream %s, on = %d\n", - state->mux.last_set->sd.name, on); - - restore_val = state->mux.last_set->streaming; - state->mux.last_set->streaming = on; + restore_val = sensor->streaming; sensor->streaming = on; if (on) { +#ifdef CONFIG_VIDEO_D4XX_SERDES +#ifdef CONFIG_VIDEO_INTEL_IPU6 + // set manually, need to configure vc in pdata + state->g_ctx.dst_vc = vc_id; +#endif + sensor->pipe_id = + max9296_get_available_pipe_id(state->dser_dev, + (int)state->g_ctx.dst_vc); + if (sensor->pipe_id < 0) { + dev_err(&state->client->dev, + "No free pipe in max9296\n"); + ret = -(ENOSR); + goto restore_s_state; + } +#endif ret = ds5_configure(state); if (ret) @@ -3939,7 +4299,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) msleep_range(DS5_START_POLL_TIME); } - if (i == DS5_START_MAX_COUNT) { + if (DS5_START_MAX_COUNT == i) { dev_err(&state->client->dev, "start streaming failed, exit on timeout\n"); /* notify fw */ @@ -3957,82 +4317,67 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) if (ret < 0) goto restore_s_state; - if (!state->depth.sensor.streaming - && !state->depth_b.sensor.streaming - && !state->rgb.sensor.streaming - && !state->rgb_b.sensor.streaming - && !state->motion_t.sensor.streaming - && !state->imu.sensor.streaming) - d4xx_reset_oneshot(state); +#ifdef CONFIG_VIDEO_D4XX_SERDES + // reset data path when Y12I streaming is done + if (state->is_y8 && + state->ir.sensor.config.format->data_type == + GMSL_CSI_DT_RGB_888) { + max9296_reset_oneshot(state->dser_dev); + } +#ifndef CONFIG_TEGRA_CAMERA_PLATFORM + // reset for IPU6 + streaming = 0; + for (i = 0; i < ARRAY_SIZE(d4xx_set_sub_stream); i++) { + if (d4xx_set_sub_stream[i]) { + streaming = 1; + break; + } + } + if (!streaming) { + dev_warn(&state->client->dev, "max9296_reset_oneshot\n"); + max9296_reset_oneshot(state->dser_dev); + } +#endif + if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) + dev_warn(&state->client->dev, "release pipe failed\n"); + sensor->pipe_id = -1; +#else +#ifdef CONFIG_VIDEO_INTEL_IPU6 + d4xx_reset_oneshot(state); +#endif +#endif } ds5_read(state, config_status_base, &status); ds5_read(state, stream_status_base, &streaming); - dev_info(&state->client->dev, - "%s %s, stream_status 0x%x:%x, config_status 0x%x:%x\n", + dev_dbg(&state->client->dev, + "%s %s, stream_status 0x%x:%x, config_status 0x%x:%x ret=%d\n", ds5_get_sensor_name(state), (on)?"START":"STOP", stream_status_base, streaming, - config_status_base, status); + config_status_base, status, ret); return ret; restore_s_state: +#ifdef CONFIG_VIDEO_D4XX_SERDES + if (on && sensor->pipe_id >= 0) { + if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) + dev_warn(&state->client->dev, "release pipe failed\n"); + sensor->pipe_id = -1; + } +#endif ds5_read(state, config_status_base, &status); dev_err(&state->client->dev, "%s stream toggle failed! %x status 0x%04x\n", ds5_get_sensor_name(state), restore_val, status); - state->mux.last_set->streaming = restore_val; sensor->streaming = restore_val; return ret; } -//static int ds5_set_power(struct ds5 *state, int on) -//{ -// int ret = 0; -// -// mutex_lock(&state->lock); -// -// if (state->power != !on) { -// mutex_unlock(&state->lock); -// return 0; -// } -// -//// gpio_set_value_cansleep(state->pwdn_gpio, on); -// -// dev_info(&state->client->dev, "%s(): power %d\n", __func__, on); -// -// usleep_range(100, 200); -// -// if (on) { -// state->power = true; -// } else { -// state->power = false; -// } -// -// mutex_unlock(&state->lock); -// -// /* TODO: Restore controls when powering on */ -// //if (on) -// // ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); -// -// return ret; -//} - -/* Core ops */ -/*static int ds5_mux_set_power(struct v4l2_subdev *sd, int on) -{ - struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - - return ds5_set_power(state, on); -}*/ - -#define DS5_N_STREAMS 4 -#define DS5_PAD_SOURCE 0 - static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *desc) { @@ -4078,80 +4423,38 @@ static const struct v4l2_subdev_video_ops ds5_mux_video_ops = { .s_stream = ds5_mux_s_stream, }; -static const struct v4l2_subdev_ops ds5_mux_subdev_ops = { - .core = &ds5_mux_core_ops, - .pad = &ds5_mux_pad_ops, - .video = &ds5_mux_video_ops, -}; - -#if 0 /* function not used */ -static int ds5_des_register(struct ds5 *state, struct ds5_des *des) -{ - struct v4l2_subdev *sd = &des->sd; - int ret; - - // FIXME: is async needed? - ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); - if (ret < 0) { - return ret; - } - - //ret = media_create_pad_link(entity, 1, &state->mux.sd.subdev.entity, des->mux_pad, - // MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - //if (ret < 0) { - // goto e_sd; - // } - - dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, des->mux_pad); - - return 0; - -e_sd: - v4l2_device_unregister_subdev(sd); - - return ret; -} -#endif +static const struct v4l2_subdev_ops ds5_mux_subdev_ops = { + .core = &ds5_mux_core_ops, + .pad = &ds5_mux_pad_ops, + .video = &ds5_mux_video_ops, +}; static int ds5_mux_registered(struct v4l2_subdev *sd) { struct ds5 *state = v4l2_get_subdevdata(sd); - struct d4xx_pdata *dpdata = state->client->dev.platform_data; - int ret = ds5_sensor_register(state, &state->depth.sensor); if (ret < 0) return ret; + ret = ds5_sensor_register(state, &state->ir.sensor); + if (ret < 0) + goto e_depth; + ret = ds5_sensor_register(state, &state->rgb.sensor); if (ret < 0) goto e_rgb; - if (dpdata->subdev_num == 1) { - ret = ds5_sensor_register(state, &state->motion_t.sensor); - if (ret < 0) - goto e_depth; - - ret = ds5_sensor_register(state, &state->imu.sensor); - if (ret < 0) - goto e_imu; - } else if (dpdata->subdev_num == 2) { - ret = ds5_sensor_register(state, &state->depth_b.sensor); - if (ret < 0) - dev_err(&state->client->dev, - "error: ds5_sensor_register state->depth_b.sensor\n"); + ret = ds5_sensor_register(state, &state->imu.sensor); + if (ret < 0) + goto e_imu; - ret = ds5_sensor_register(state, &state->rgb_b.sensor); - if (ret < 0) - dev_err(&state->client->dev, - "error: ds5_sensor_register state->rgb_b.sensor\n"); - } return 0; e_imu: v4l2_device_unregister_subdev(&state->rgb.sensor.sd); e_rgb: - v4l2_device_unregister_subdev(&state->motion_t.sensor.sd); + v4l2_device_unregister_subdev(&state->ir.sensor.sd); e_depth: v4l2_device_unregister_subdev(&state->depth.sensor.sd); @@ -4162,17 +4465,10 @@ static int ds5_mux_registered(struct v4l2_subdev *sd) static void ds5_mux_unregistered(struct v4l2_subdev *sd) { struct ds5 *state = v4l2_get_subdevdata(sd); - struct d4xx_pdata *dpdata = state->client->dev.platform_data; - + ds5_sensor_remove(&state->imu.sensor); ds5_sensor_remove(&state->rgb.sensor); + ds5_sensor_remove(&state->ir.sensor); ds5_sensor_remove(&state->depth.sensor); - if (dpdata->subdev_num == 1) { - ds5_sensor_remove(&state->motion_t.sensor); - ds5_sensor_remove(&state->imu.sensor); - } else if (dpdata->subdev_num == 2) { - ds5_sensor_remove(&state->depth_b.sensor); - ds5_sensor_remove(&state->rgb_b.sensor); - } } static const struct v4l2_subdev_internal_ops ds5_mux_internal_ops = { @@ -4190,7 +4486,6 @@ static int ds5_mux_register(struct i2c_client *c, struct ds5 *state) static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) { struct v4l2_subdev *sd = &state->mux.sd.subdev; - dev_dbg(&state->client->dev, "ds5_hw_init :sd->name = %s\n", sd->name); u16 mipi_status, n_lanes, phy, drate_min, drate_max; int ret = ds5_read(state, DS5_MIPI_SUPPORT_LINES, &n_lanes); if (!ret) @@ -4202,46 +4497,31 @@ static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) if (!ret) ret = ds5_read(state, DS5_MIPI_DATARATE_MAX, &drate_max); + if (!ret) + dev_dbg(sd->dev, "%s(): %d: %u lanes, phy %x, data rate %u-%u\n", + __func__, __LINE__, n_lanes, phy, drate_min, drate_max); + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + n_lanes = state->mux.sd.numlanes; +#else n_lanes = 2; +#endif ret = ds5_write(state, DS5_MIPI_LANE_NUMS, n_lanes - 1); if (!ret) ret = ds5_write(state, DS5_MIPI_LANE_DATARATE, MIPI_LANE_RATE); - ret = ds5_read(state, DS5_MIPI_CONF_STATUS, &mipi_status); + if (!ret) + ret = ds5_read(state, DS5_MIPI_CONF_STATUS, &mipi_status); + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + dev_dbg(sd->dev, "%s(): %d phandle %x node %s status %x\n", __func__, __LINE__, + c->dev.of_node->phandle, c->dev.of_node->full_name, mipi_status); +#endif return ret; } -#if 0 /* not used function */ -static int ds5_des_init(struct i2c_client *c, struct ds5 *state) -{ - struct v4l2_subdev *sd = &state->max9296.des.sd; - struct media_entity *entity = &state->max9296.des.sd.entity; - struct media_pad *pads = state->max9296.des.pads; - dev_t *dev_num = &state->client->dev.devt; - - state->max9296.des.mux_pad = DS5_MUX_PAD_EXTERNAL; - - dev_info(sd->dev, "%s(): %p %s %p %p", __func__, c, c->name, state, state->client); - - v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); - sd->owner = THIS_MODULE; - sd->internal_ops = &ds5_sensor_internal_ops; - sd->grp_id = *dev_num; - v4l2_set_subdevdata(sd, state); - snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", - "max9296", i2c_adapter_id(c->adapter), 0x48); - - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - pads[0].flags = MEDIA_PAD_FL_SOURCE; - pads[1].flags = MEDIA_PAD_FL_SINK; - entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; - entity->function = MEDIA_ENT_F_CAM_SENSOR; - return media_entity_pads_init(entity, 2, pads); -} -#endif static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) { struct v4l2_subdev *sd = &state->mux.sd.subdev; @@ -4249,15 +4529,24 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) struct media_pad *pads = state->mux.pads, *pad; unsigned int i; int ret; - +#ifndef CONFIG_OF struct d4xx_pdata *dpdata = c->dev.platform_data; + char suffix = dpdata->suffix; +#endif v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); + // See tegracam_v4l2.c tegracam_v4l2subdev_register() // Set owner to NULL so we can unload the driver module sd->owner = NULL; sd->internal_ops = &ds5_mux_internal_ops; v4l2_set_subdevdata(sd, state); - snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", dpdata->suffix); - +#ifdef CONFIG_OF + snprintf(sd->name, sizeof(sd->name), "DS5 mux %d-%04x", + i2c_adapter_id(c->adapter), c->addr); +#else + if (state->aggregated) + suffix += 4; + snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); +#endif sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; @@ -4271,39 +4560,63 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) return ret; /*set for mux*/ - ret = ds5_ctrl_init(state, DS5_SID_MUX_A); + ret = ds5_ctrl_init(state, MUX_SID); if (ret < 0) - return ret; + goto e_entity; + /*set for depth*/ - ret = ds5_ctrl_init(state, DS5_SID_DEPTH_A); + ret = ds5_ctrl_init(state, DEPTH_SID); if (ret < 0) return ret; /*set for rgb*/ - ret = ds5_ctrl_init(state, DS5_SID_RGB_A); + ret = ds5_ctrl_init(state, RGB_SID); if (ret < 0) return ret; /*set for y8*/ - ret = ds5_ctrl_init(state, DS5_SID_MOTION_T_A); + ret = ds5_ctrl_init(state, IR_SID); if (ret < 0) return ret; /*set for imu*/ - ret = ds5_ctrl_init(state, DS5_SID_IMU_A); + ret = ds5_ctrl_init(state, IMU_SID); if (ret < 0) return ret; - if (dpdata->subdev_num == 2) { - /*set for depth_b*/ - ret = ds5_ctrl_init(state, DS5_SID_DEPTH_B); - if (ret < 0) - return ret; - /*set for rgb_b*/ - ret = ds5_ctrl_init(state, DS5_SID_RGB_B); - if (ret < 0) - return ret; - } ds5_set_state_last_set(state); #ifdef CONFIG_TEGRA_CAMERA_PLATFORM + if (state->is_depth) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0) + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_depth, NULL); +#else + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_depth, NULL, true); +#endif + state->mux.last_set = &state->depth.sensor; + } + else if (state->is_rgb) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0) + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_rgb, NULL); +#else + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_rgb, NULL, true); +#endif + state->mux.last_set = &state->rgb.sensor; + } + else if (state->is_y8) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0) + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_y8, NULL); +#else + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_y8, NULL, true); +#endif + state->mux.last_set = &state->ir.sensor; + } + else + state->mux.last_set = &state->imu.sensor; + state->mux.sd.dev = &c->dev; ret = camera_common_initialize(&state->mux.sd, "d4xx"); if (ret) { @@ -4318,6 +4631,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) e_ctrl: v4l2_ctrl_handler_free(sd->ctrl_handler); #endif +e_entity: media_entity_cleanup(entity); return ret; @@ -4352,10 +4666,10 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) if (ret < 0) return ret; - dev_info(&client->dev, "%s(): cfg0 %x %ux%u cfg0_md %x %ux%u\n", __func__, + dev_dbg(&client->dev, "%s(): cfg0 %x %ux%u cfg0_md %x %ux%u\n", __func__, cfg0, dw, dh, cfg0_md, yw, yh); - dev_info(&client->dev, "%s(): cfg1 %x %ux%u cfg1_md %x %ux%u\n", __func__, + dev_dbg(&client->dev, "%s(): cfg1 %x %ux%u cfg1_md %x %ux%u\n", __func__, cfg1, dw, dh, cfg1_md, yw, yh); sensor = &state->depth.sensor; @@ -4371,50 +4685,12 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = ds5_depth_formats_d46x; } sensor->n_formats = 1; - sensor->mux_pad = DS5_MUX_PAD_DEPTH_A; - - sensor = &state->depth_b.sensor; - switch (dev_type) { - case DS5_DEVICE_TYPE_D43X: - case DS5_DEVICE_TYPE_D45X: - sensor->formats = ds5_depth_formats_d43x; - break; - case DS5_DEVICE_TYPE_D46X: - sensor->formats = ds5_depth_formats_d46x; - break; - default: - sensor->formats = ds5_depth_formats_d46x; - } - sensor->n_formats = 1; - sensor->mux_pad = DS5_MUX_PAD_DEPTH_B; + sensor->mux_pad = DS5_MUX_PAD_DEPTH; - sensor = &state->motion_t.sensor; + sensor = &state->ir.sensor; sensor->formats = state->variant->formats; sensor->n_formats = state->variant->n_formats; - sensor->mux_pad = DS5_MUX_PAD_MOTION_T_A; - switch (dev_type) { - // case DS5_DEVICE_TYPE_D45X: - case DS5_DEVICE_TYPE_D43X: { - unsigned int *calib_resolutions_size = - (unsigned int *)&(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].n_resolutions); - const struct ds5_resolution **calib_resolutions = (const struct ds5_resolution **) - &(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].resolutions); - *calib_resolutions_size = ARRAY_SIZE(d43x_calibration_sizes), - *calib_resolutions = d43x_calibration_sizes; - break; - } - case DS5_DEVICE_TYPE_D46X: { - dev_info(&client->dev, "%s(): DS5_DEVICE_TYPE_D46X for calib\n", __func__); - /*unsigned int *calib_resolutions_size = - * (unsigned int *)&(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].n_resolutions); - *const struct ds5_resolution **calib_resolutions = (const struct ds5_resolution**) - * &(sensor->formats[ARRAY_SIZE(ds5_y_formats_ds5u)-1].resolutions); - **calib_resolutions_size = ARRAY_SIZE(d46x_calibration_sizes), - **calib_resolutions = d46x_calibration_sizes; - */ - break; - } - } + sensor->mux_pad = DS5_MUX_PAD_IR; sensor = &state->rgb.sensor; switch (dev_type) { @@ -4431,34 +4707,20 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = &ds5_onsemi_rgb_format; sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; } - sensor->mux_pad = DS5_MUX_PAD_RGB_A; - - sensor = &state->rgb_b.sensor; - switch (dev_type) { - case DS5_DEVICE_TYPE_D43X: - case DS5_DEVICE_TYPE_D46X: - sensor->formats = &ds5_onsemi_rgb_format; - sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; - break; - case DS5_DEVICE_TYPE_D45X: - sensor->formats = &ds5_rlt_rgb_format; - sensor->n_formats = DS5_RLT_RGB_N_FORMATS; - break; - default: - sensor->formats = &ds5_onsemi_rgb_format; - sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; - } - sensor->mux_pad = DS5_MUX_PAD_RGB_B; - - /*sensor->formats = &ds5_onsemi_rgb_format; - *sensor->n_formats = DS5_RGB_N_FORMATS; - *sensor->mux_pad = DS5_MUX_PAD_RGB; - */ + sensor->mux_pad = DS5_MUX_PAD_RGB; sensor = &state->imu.sensor; - sensor->formats = ds5_imu_formats; + + /* For fimware version starting from: 5.16, + IMU will have 32bit axis values. + 5.16.x.y = firmware version: 0x0510 */ + if (state->fw_version >= 0x510) + sensor->formats = ds5_imu_formats_extended; + else + sensor->formats = ds5_imu_formats; + sensor->n_formats = 1; - sensor->mux_pad = DS5_MUX_PAD_IMU_A; + sensor->mux_pad = DS5_MUX_PAD_IMU; /* Development: set a configuration during probing */ if ((cfg0 & 0xff00) == 0x1800) { @@ -4471,15 +4733,28 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) }; //#undef USE_Y +#ifdef USE_Y /* Override .width, .height, .code */ fmt.format.width = yw; fmt.format.height = yh; fmt.format.code = MEDIA_BUS_FMT_UYVY8_2X8; - - //state->mux.sd.mode_prop_idx = 0; - state->motion_t.sensor.streaming = true; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + state->mux.sd.mode_prop_idx = 0; +#endif + state->ir.sensor.streaming = true; state->depth.sensor.streaming = true; - ret = __ds5_sensor_set_fmt(state, &state->motion_t.sensor, NULL, &fmt); + ret = __ds5_sensor_set_fmt(state, &state->ir.sensor, NULL, &fmt); +#else + fmt.format.width = dw; + fmt.format.height = dh; + fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + state->mux.sd.mode_prop_idx = 1; +#endif + state->ir.sensor.streaming = false; + state->depth.sensor.streaming = true; + ret = __ds5_sensor_set_fmt(state, &state->depth.sensor, NULL, &fmt); +#endif if (ret < 0) return ret; } @@ -4496,13 +4771,9 @@ static int ds5_parse_cam(struct i2c_client *client, struct ds5 *state) return ret; ds5_sensor_format_init(&state->depth.sensor); - ds5_sensor_format_init(&state->motion_t.sensor); + ds5_sensor_format_init(&state->ir.sensor); ds5_sensor_format_init(&state->rgb.sensor); ds5_sensor_format_init(&state->imu.sensor); - if ((&(state->depth_b) != NULL) && (&(state->rgb_b) != NULL)) { - ds5_sensor_format_init(&state->depth_b.sensor); - ds5_sensor_format_init(&state->rgb_b.sensor); - } return 0; } @@ -4667,7 +4938,7 @@ static ssize_t ds5_dfu_device_read(struct file *flip, ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); if (ret < 0) goto e_dfu_read_failed; - snprintf(msg, sizeof(msg), "DFU info: \tver: %d.%d.%d.%d\n", + snprintf(msg, sizeof(msg) ,"DFU info: \tver: %d.%d.%d.%d\n", (fw_ver >> 8) & 0xff, fw_ver & 0xff, (fw_build >> 8) & 0xff, fw_build & 0xff); @@ -4688,6 +4959,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, { struct ds5 *state = flip->private_data; int ret = 0; + (void)offset; if (mutex_lock_interruptible(&state->lock)) return -ERESTARTSYS; @@ -4765,8 +5037,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, dfu_write_error: state->dfu_dev.dfu_state_flag = DS5_DFU_ERROR; // Reset DFU device to IDLE states - ret = ds5_write(state, 0x5010, 0x0); - if (!ret) + if (!ds5_write(state, 0x5010, 0x0)) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; mutex_unlock(&state->lock); return ret; @@ -4776,7 +5047,12 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) { struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); - +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( + state->client->adapter); +#endif +#endif if (state->dfu_dev.device_open_count) return -EBUSY; state->dfu_dev.device_open_count++; @@ -4788,13 +5064,31 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) return -ENOMEM; file->private_data = state; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + /* get i2c controller and set dfu bus clock rate */ + while (parent && i2c_parent_is_i2c_adapter(parent)) + parent = i2c_parent_is_i2c_adapter(state->client->adapter); + + if (!parent) + return 0; + + dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk = %d, set %d\n", + __func__, + i2c_adapter_id(parent), + i2c_get_adapter_bus_clk_rate(parent), + DFU_I2C_BUS_CLK_RATE); + + state->dfu_dev.bus_clk_rate = i2c_get_adapter_bus_clk_rate(parent); + i2c_set_adapter_bus_clk_rate(parent, DFU_I2C_BUS_CLK_RATE); +#endif +#endif return 0; }; static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) { int ret; - struct d4xx_pdata *dpdata = c->dev.platform_data; ret = ds5_parse_cam(c, state); if (ret < 0) @@ -4804,23 +5098,13 @@ static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) if (ret < 0) return ret; - ret = ds5_motion_t_init(c, state); + ret = ds5_ir_init(c, state); if (ret < 0) goto e_depth; ret = ds5_rgb_init(c, state); if (ret < 0) - goto e_motion_t; - - if (dpdata->subdev_num == 2) { - ret = ds5_depth_b_init(c, state); - if (ret < 0) - return ret; - - ret = ds5_rgb_b_init(c, state); - if (ret < 0) - goto e_depth_b; - } + goto e_ir; ret = ds5_imu_init(c, state); if (ret < 0) @@ -4845,20 +5129,23 @@ static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) media_entity_cleanup(&state->imu.sensor.sd.entity); e_rgb: media_entity_cleanup(&state->rgb.sensor.sd.entity); -e_motion_t: - media_entity_cleanup(&state->motion_t.sensor.sd.entity); -e_depth_b: - media_entity_cleanup(&state->depth_b.sensor.sd.entity); +e_ir: + media_entity_cleanup(&state->ir.sensor.sd.entity); e_depth: media_entity_cleanup(&state->depth.sensor.sd.entity); - return ret; } static int ds5_dfu_device_release(struct inode *inode, struct file *file) { struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); - +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( + state->client->adapter); +#endif +#endif + int ret = 0, retry = 10; state->dfu_dev.device_open_count--; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; @@ -4869,8 +5156,34 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) if (state->dfu_dev.dfu_msg) devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg); state->dfu_dev.dfu_msg = NULL; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) + /* get i2c controller and restore bus clock rate */ + while (parent && i2c_parent_is_i2c_adapter(parent)) + parent = i2c_parent_is_i2c_adapter(state->client->adapter); + if (!parent) + return 0; + dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk %d, restore to %d\n", + __func__, i2c_adapter_id(parent), + i2c_get_adapter_bus_clk_rate(parent), + state->dfu_dev.bus_clk_rate); - return 0; + i2c_set_adapter_bus_clk_rate(parent, state->dfu_dev.bus_clk_rate); +#endif +#endif + /* Verify communication */ + do { + ret = ds5_read(state, DS5_FW_VERSION, &state->fw_version); + if (ret) + msleep_range(10); + } while (retry-- && ret != 0 ); + if (ret) { + dev_warn(&state->client->dev, + "%s(): no communication with d4xx\n", __func__); + return ret; + } + ret = ds5_read(state, DS5_FW_BUILD, &state->fw_build); + return ret; }; static const struct file_operations ds5_device_file_ops = { @@ -4888,8 +5201,10 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) { struct cdev *ds5_cdev = &state->dfu_dev.ds5_cdev; struct class **ds5_class = &state->dfu_dev.ds5_class; +#ifndef CONFIG_OF struct d4xx_pdata *pdata = c->dev.platform_data; - + char suffix = pdata->suffix; +#endif struct device *chr_dev; char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 8]; dev_t *dev_num = &c->dev.devt; @@ -4901,7 +5216,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) if (ret < 0) return ret; - if (!atomic_cmpxchg(&primary_chardev, 0, MAJOR(*dev_num))) { + if (!atomic_read(&primary_chardev)) { dev_dbg(&c->dev, "%s(): : <%d, %d>\n", __func__, MAJOR(*dev_num), MINOR(*dev_num)); /* Create a class : appears at /sys/class */ @@ -4910,6 +5225,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) #else *ds5_class = class_create(DS5_DRIVER_NAME_CLASS); #endif + dev_warn(&state->client->dev, "%s() class_create\n", __func__); if (IS_ERR(*ds5_class)) { dev_err(&c->dev, "Could not create class device\n"); unregister_chrdev_region(0, 1); @@ -4924,8 +5240,15 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) /* Build up the current device number. To be used further */ *dev_num = MKDEV(MAJOR(*dev_num), MINOR(*dev_num)); /* Create a device node for this device. */ +#ifndef CONFIG_OF + if (state->aggregated) + suffix += 4; snprintf(dev_name, sizeof(dev_name), "%s-%c", - DS5_DRIVER_NAME_DFU, pdata->suffix); + DS5_DRIVER_NAME_DFU, suffix); +#else + snprintf (dev_name, sizeof(dev_name), "%s-%d-%04x", + DS5_DRIVER_NAME_DFU, i2c_adapter_id(c->adapter), c->addr); +#endif chr_dev = device_create(*ds5_class, NULL, *dev_num, NULL, dev_name); if (IS_ERR(chr_dev)) { ret = PTR_ERR(chr_dev); @@ -4935,6 +5258,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) return ret; } cdev_add(ds5_cdev, *dev_num, 1); + atomic_inc(&primary_chardev); return 0; }; @@ -4948,16 +5272,47 @@ static int ds5_chrdev_remove(struct ds5 *state) dev_dbg(&state->client->dev, "%s()\n", __func__); unregister_chrdev_region(*dev_num, 1); device_destroy(*ds5_class, *dev_num); - if (atomic_cmpxchg(&primary_chardev, MAJOR(*dev_num), 0) == MAJOR(*dev_num)) + if (atomic_dec_and_test(&primary_chardev)) { + dev_warn(&state->client->dev, "%s() class_destroy\n", __func__); class_destroy(*ds5_class); + } return 0; } -static void ds5_substream_init(void) +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static void ds5_substream_init(struct ds5 *state) { int i, j; unsigned int mipi_csi2_type; s64 *sub_stream = NULL; + state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; + if (!state->aggregated) { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + } else { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[4]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[5]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; + } + + for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) + state->pad_to_substream[i] = -1; + /* match for IPU6 CSI2 BE SOC video capture pads */ + if (!state->aggregated) { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + } + else { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 6; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 8; + state->pad_to_substream[DS5_MUX_PAD_IR] = 10; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; + } /* * 0, vc 0, depth * 1, vc 0, meta data @@ -4965,86 +5320,55 @@ static void ds5_substream_init(void) * 3, vc 1, meta data * 4, vc 2, IR * 5, vc 3, IMU - * 6, vc 2, depth_b - * 7, vc 3,rgb_b */ - for (j = 0; j < MAX_D457_COUNT; j++) { - sub_stream = d4xx_query_sub_stream[j]; - mipi_csi2_type = mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_fmt(sub_stream, 0, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(sub_stream, 0, 480); - set_sub_stream_w(sub_stream, 0, 640); - set_sub_stream_dt(sub_stream, 0, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 0, 0); - - set_sub_stream_fmt(sub_stream, 1, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(sub_stream, 1, 1); - set_sub_stream_w(sub_stream, 1, 68); - set_sub_stream_dt(sub_stream, 1, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(sub_stream, 1, 0); - - /*RGB*/ - set_sub_stream_fmt(sub_stream, 2, MEDIA_BUS_FMT_YUYV8_1X16); - set_sub_stream_h(sub_stream, 2, 640); - set_sub_stream_w(sub_stream, 2, 480); - set_sub_stream_dt(sub_stream, 2, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 2, 1); - - set_sub_stream_fmt(sub_stream, 3, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(sub_stream, 3, 1); - set_sub_stream_w(sub_stream, 3, 68); - set_sub_stream_dt(sub_stream, 3, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(sub_stream, 3, 1); - /*IR*/ - set_sub_stream_fmt(sub_stream, 4, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(sub_stream, 4, 640); - set_sub_stream_w(sub_stream, 4, 480); - set_sub_stream_dt(sub_stream, 4, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 4, 2); - - set_sub_stream_fmt(sub_stream, 5, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(sub_stream, 5, 640); - set_sub_stream_w(sub_stream, 5, 480); - set_sub_stream_dt(sub_stream, 5, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 5, 3); - - /*add DEPTH_b*/ - set_sub_stream_fmt(sub_stream, 6, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(sub_stream, 6, 480); - set_sub_stream_w(sub_stream, 6, 640); - set_sub_stream_dt(sub_stream, 6, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 6, 2); - - set_sub_stream_fmt(sub_stream, 7, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(sub_stream, 7, 1); - set_sub_stream_w(sub_stream, 7, 68); - set_sub_stream_dt(sub_stream, 7, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(sub_stream, 7, 2); - - /*add RGB_b*/ - set_sub_stream_fmt(sub_stream, 8, MEDIA_BUS_FMT_YUYV8_1X16); - set_sub_stream_h(sub_stream, 8, 640); - set_sub_stream_w(sub_stream, 8, 480); - set_sub_stream_dt(sub_stream, 8, mipi_csi2_type); - set_sub_stream_vc_id(sub_stream, 8, 3); - - set_sub_stream_fmt(sub_stream, 9, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(sub_stream, 9, 1); - set_sub_stream_w(sub_stream, 9, 68); - set_sub_stream_dt(sub_stream, 9, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(sub_stream, 9, 3); - } - - for (i = 0; i < DS5_MUX_PAD_COUNT; i++) - pad_to_substream[i] = -1; - - pad_to_substream[DS5_MUX_PAD_DEPTH_A] = 0; - pad_to_substream[DS5_MUX_PAD_RGB_A] = 2; - pad_to_substream[DS5_MUX_PAD_MOTION_T_A] = 4; - pad_to_substream[DS5_MUX_PAD_IMU_A] = 5; - pad_to_substream[DS5_MUX_PAD_DEPTH_B] = 6; - pad_to_substream[DS5_MUX_PAD_RGB_B] = 8;//7 metadata + /* aggreagated */ + /* + * 6, vc 2, depth + * 7, vc 2, meta data + * 8, vc 3, RGB + * 9, vc 3, meta data + * 10, vc 0, IR + * 11, vc 1, IMU + */ + /*DEPTH*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_DEPTH], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_DEPTH], 480); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_DEPTH], 640); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_DEPTH], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_DEPTH], state->pad_to_vc[DS5_MUX_PAD_DEPTH]); + /*DEPTH MD*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, 1); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, 68); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, state->pad_to_vc[DS5_MUX_PAD_DEPTH]); + + /*RGB*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_RGB], MEDIA_BUS_FMT_YUYV8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_RGB], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_RGB], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_RGB], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_RGB], state->pad_to_vc[DS5_MUX_PAD_RGB]); + /*RGB MD*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, 1); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, 68); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, state->pad_to_vc[DS5_MUX_PAD_RGB]); + /*IR*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_IR], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_IR], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_IR], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_IR], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_IR], state->pad_to_vc[DS5_MUX_PAD_IR]); + /*IMU*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_IMU], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_IMU], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_IMU], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_IMU], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_IMU], state->pad_to_vc[DS5_MUX_PAD_IMU]); } +#endif /* SYSFS attributes */ #ifdef CONFIG_SYSFS @@ -5114,7 +5438,6 @@ static ssize_t ds5_read_reg_store(struct device *dev, struct dev_ds5_reg_attribute, attr); int rc = -1; u32 reg; - ds5_rw_attr->valid = 0; /* Decode input */ rc = sscanf(buf, "0x%04x", ®); @@ -5168,8 +5491,10 @@ static const struct attribute_group ds5_attr_group = { }; #endif +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define NR_DESER 4 +#ifndef CONFIG_VIDEO_D4XX_SERDES static const struct regmap_config ds5_regmap_max9296 = { .reg_bits = 16, .val_bits = 8, @@ -5195,141 +5520,94 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) unsigned short ser_alias; unsigned short sensor_alias; - unsigned short ser_alias_b; - unsigned short sensor_alias_b; - /* TODO: 2x D457 subdev connect to MAX9296 */ - if (dpdata->subdev_num == 1) { - curr_max9296 = c->addr; - sensor_alias = dpdata->subdev_info[0].board_info.addr; - ser_alias = dpdata->subdev_info[0].ser_alias; - } else if (dpdata->subdev_num == 2) { + if (dpdata->subdev_num >= 1) { curr_max9296 = c->addr; sensor_alias = dpdata->subdev_info[0].board_info.addr; ser_alias = dpdata->subdev_info[0].ser_alias; - sensor_alias_b = dpdata->subdev_info[1].board_info.addr; - ser_alias_b = dpdata->subdev_info[1].ser_alias; } else { dev_err(&c->dev, "no subdev found!\n"); return -EINVAL; } - dev_dbg(&c->dev, "subdev_num = %u, max9296 = 0x%02x\n", dpdata->subdev_num, curr_max9296); - dev_dbg(&c->dev, "sensor_alias = 0x%02x, ser_alias = 0x%02x\n", sensor_alias, ser_alias); - dev_dbg(&c->dev, "sensor_alias_b = 0x%02x, ser_alias_b = 0x%02x\n", sensor_alias_b, ser_alias_b); + dev_dbg(&c->dev, "curr_max9296 0x%02x, sensor_alias 0x%02x, ser_alias 0x%02x\n", + curr_max9296, sensor_alias, ser_alias); - for (i = 0; i < MAX9296_NUM; i++) { - if (curr_max9296 < max9296_addrs[i]) { - c->addr = max9296_addrs[i]; - max9296_write_8(state, MAX9296_CTRL0, RESET_LINK); - } + /* + * don't reset link, + * check max9296 i2c addr + 1, + * max9295 i2c addr reassigned already. + */ + c->addr = curr_max9296 + 1; + ret = max9295_read_8(state, MAX9295_REG0, &val); + if (!ret) { + max9295_write_8(state, MAX9295_REG0, ser_alias << 1); + msleep_range(1000); /* need this? */ + c->addr = ser_alias; + max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); + max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); + c->addr = sensor_alias; + return 0; } + /* i2c addr reassignment for all max9295 */ for (i = 0; i < MAX9296_NUM; i++) { - if (curr_max9296 == max9296_addrs[i]) { - if (dpdata->subdev_num == 1) { - c->addr = curr_max9296; - max9296_write_8(state, MAX9296_CTRL0, AUTO_LINK | LINK_A); - msleep_range(1000); - - c->addr = MAX9295_I2C_ADDR_DEF; - ret = max9295_read_8(state, MAX9295_REG0, &val); - if (ret < 0) { - dev_err(&c->dev, "no max9295 found for max9296 0x%02x\n", curr_max9296); - continue; - } - - ret = max9295_read_8(state, MAX9295_REG0, &val); - max9295_write_8(state, MAX9295_REG0, ser_alias << 1); - msleep_range(1000); // need this? - c->addr = ser_alias; - max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); - max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); - break; - } - - if (dpdata->subdev_num == 2) { - //link a - c->addr = curr_max9296; - max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | LINK_A); - msleep_range(1000); - - c->addr = MAX9295_I2C_ADDR_DEF; - - max9295_write_8(state, MAX9295_REG0, ser_alias_b << 1); - - c->addr = ser_alias_b; - ret = max9295_read_8(state, MAX9295_REG0, &val); - max9295_write_8(state, MAX9295_I2C_4, sensor_alias_b << 1); - max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); - - // Packets are transmitted over GMSLA in splitter mode. - max9295_write_8(state, 0x7B, 0x31); - max9295_write_8(state, 0x83, 0x31); - max9295_write_8(state, 0x8B, 0x31); - max9295_write_8(state, 0x93, 0x31); - max9295_write_8(state, 0xA3, 0x31); - max9295_write_8(state, 0xAB, 0x31); - - c->addr = ser_alias_b; - ret = max9295_read_8(state, MAX9295_REG0, &val); - - //link b - c->addr = curr_max9296; - max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | LINK_B); - msleep_range(1000); - - c->addr = MAX9295_I2C_ADDR_DEF; - ret = max9295_read_8(state, MAX9295_REG0, &val); - max9295_write_8(state, MAX9295_REG0, ser_alias << 1); - msleep_range(1000); // need this? - c->addr = ser_alias; - max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); - max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); - - c->addr = max9296_addrs[i]; + c->addr = max9296_addrs[i]; + max9296_write_8(state, MAX9296_CTRL0, RESET_LINK); + } - max9296_write_8(state, MAX9296_CTRL0, RESET_ONESHOT | SPLITTER); - msleep_range(1000); + for (i = 0; i < MAX9296_NUM; i++) { + /* release reset */ + c->addr = max9296_addrs[i]; + max9296_write_8(state, MAX9296_CTRL0, AUTO_LINK | LINK_A); + msleep_range(1000); - break; + if (curr_max9296 == max9296_addrs[i]) { + c->addr = MAX9295_I2C_ADDR_DEF; + ret = max9295_read_8(state, MAX9295_REG0, &val); + if (ret < 0) { + dev_err(&c->dev, "no max9295 found for max9296 %x\n", curr_max9296); + continue; } + max9295_write_8(state, MAX9295_REG0, ser_alias << 1); + msleep_range(1000); // need this? + c->addr = ser_alias; + max9295_write_8(state, MAX9295_I2C_4, sensor_alias << 1); + max9295_write_8(state, MAX9295_I2C_5, D457_I2C_ADDR << 1); continue; } + + c->addr = MAX9295_I2C_ADDR_DEF; + ret = max9295_read_8(state, MAX9295_REG0, &val); + if (ret < 0) + continue; + max9295_write_8(state, MAX9295_REG0, (max9296_addrs[i] + 1) << 1); } c->addr = sensor_alias; return 0; } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -static int ds5_probe(struct i2c_client *c) -#else -static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) #endif +#endif //CONFIG_VIDEO_INTEL_IPU6 +static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) { struct ds5 *state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); u16 rec_state; int ret, retry, err = 0; +#ifdef CONFIG_OF const char *str; - +#endif if (!state) return -ENOMEM; mutex_init(&state->lock); - dev_warn(&c->dev, "Driver addr 0x%x\n", c->addr); - state->client = c; - dev_warn(&c->dev, "Probing new driver for D45x\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) - state->variant = ds5_variants; -#else - dev_warn(&c->dev, "Driver data NAEL %d\n", (int)id->driver_data); - state->variant = ds5_variants + id->driver_data; -#endif + dev_warn(&c->dev, "Probing driver for D45x\n"); + state->variant = ds5_variants + id->driver_data; +#ifdef CONFIG_OF state->vcc = devm_regulator_get(&c->dev, "vcc"); if (IS_ERR(state->vcc)) { ret = PTR_ERR(state->vcc); @@ -5344,6 +5622,7 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) return ret; } } +#endif state->regmap = devm_regmap_init_i2c(c, &ds5_regmap_config); if (IS_ERR(state->regmap)) { ret = PTR_ERR(state->regmap); @@ -5351,6 +5630,13 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) goto e_regulator; } +#ifdef CONFIG_VIDEO_D4XX_SERDES + ret = ds5_serdes_setup(state); + if (ret < 0) + goto e_regulator; +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifndef CONFIG_VIDEO_D4XX_SERDES state->regmap_max9296 = devm_regmap_init_i2c(c, &ds5_regmap_max9296); if (IS_ERR(state->regmap_max9296)) { ret = PTR_ERR(state->regmap_max9296); @@ -5370,11 +5656,12 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) dev_err(&c->dev, "failed apply i2c addr setting\n"); return ret; } - +#endif +#endif //CONFIG_VIDEO_INTEL_IPU6 // Verify communication - retry = 10; + retry = 5; do { - ret = ds5_read(state, 0x5020, &rec_state); + ret = ds5_read(state, 0x5020, &rec_state); } while (retry-- && ret < 0); if (ret < 0) { dev_err(&c->dev, @@ -5386,8 +5673,6 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) state->is_depth = 0; state->is_y8 = 0; state->is_rgb = 0; - state->is_depth_b = 0; - state->is_rgb_b = 0; state->is_imu = 0; #ifdef CONFIG_OF ret = of_property_read_string(c->dev.of_node, "cam-type", &str); @@ -5453,7 +5738,9 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) /* create the sysfs file group */ err = sysfs_create_group(&state->client->dev.kobj, &ds5_attr_group); #endif - ds5_substream_init(); +#ifdef CONFIG_VIDEO_INTEL_IPU6 + ds5_substream_init(state); +#endif return 0; e_chardev: @@ -5462,6 +5749,12 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) e_regulator: if (state->vcc) regulator_disable(state->vcc); +#ifdef CONFIG_VIDEO_D4XX_SERDES + if (state->ser_i2c) + i2c_unregister_device(state->ser_i2c); + if (state->dser_i2c && !state->aggregated) + i2c_unregister_device(state->dser_i2c); +#endif return ret; } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) @@ -5472,19 +5765,60 @@ static void ds5_remove(struct i2c_client *c) { struct ds5 *state = container_of(i2c_get_clientdata(c), struct ds5, mux.sd.subdev); +#ifdef CONFIG_VIDEO_D4XX_SERDES + int i, ret; + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i] == state) { + serdes_inited[i] = NULL; + mutex_lock(&serdes_lock__); + + ret = max9295_reset_control(state->ser_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9295 reset control\n"); + ret = max9296_reset_control(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9296 reset control\n"); + + ret = max9295_sdev_unpair(state->ser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, "failed to unpair sdev\n"); + ret = max9296_sdev_unregister(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed to sdev unregister sdev\n"); + max9296_power_off(state->dser_dev); + + mutex_unlock(&serdes_lock__); + break; + } + } + if (state->ser_i2c) + i2c_unregister_device(state->ser_i2c); + if (state->dser_i2c) + i2c_unregister_device(state->dser_i2c); +#endif +#ifndef CONFIG_TEGRA_CAMERA_PLATFORM + state->is_depth = 1; +#endif dev_info(&c->dev, "D4XX remove %s\n", ds5_get_sensor_name(state)); if (state->vcc) regulator_disable(state->vcc); // gpio_free(state->pwdn_gpio); - if (state->is_depth) - ds5_chrdev_remove(state); if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) { #ifdef CONFIG_SYSFS sysfs_remove_group(&c->dev.kobj, &ds5_attr_group); #endif ds5_mux_remove(state); + if (state->is_depth) { + ds5_chrdev_remove(state); + } } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) @@ -5500,14 +5834,20 @@ static const struct i2c_device_id ds5_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds5_id); +static const struct of_device_id d4xx_of_match[] = { + { .compatible = "intel,d4xx", }, + { }, +}; +MODULE_DEVICE_TABLE(of, d4xx_of_match); + static struct i2c_driver ds5_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = DS5_DRIVER_NAME }, - .probe = ds5_probe, - .remove = ds5_remove, - .id_table = ds5_id, + .probe = ds5_probe, + .remove = ds5_remove, + .id_table = ds5_id, }; module_i2c_driver(ds5_i2c_driver); @@ -5523,4 +5863,4 @@ MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Shikun Ding "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("1.0.2.13"); +MODULE_VERSION("1.0.2.20"); diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c new file mode 100644 index 00000000..d4bea270 --- /dev/null +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -0,0 +1,773 @@ +/* + * max9295.c - max9295 GMSL Serializer driver + * + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// #include +#include +#include +#include +#include +#include +#include +#include + +/* register specifics */ +#define MAX9295_MIPI_RX0_ADDR 0x330 +#define MAX9295_MIPI_RX1_ADDR 0x331 +#define MAX9295_MIPI_RX2_ADDR 0x332 +#define MAX9295_MIPI_RX3_ADDR 0x333 + +#define MAX9295_PIPE_X_DT_ADDR 0x314 +#define MAX9295_PIPE_Y_DT_ADDR 0x316 +#define MAX9295_PIPE_Z_DT_ADDR 0x318 +#define MAX9295_PIPE_U_DT_ADDR 0x31A + +#define MAX9295_CTRL0_ADDR 0x10 +#define MAX9295_SRC_CTRL_ADDR 0x2BF +#define MAX9295_SRC_PWDN_ADDR 0x02BE +#define MAX9295_SRC_OUT_RCLK_ADDR 0x3F1 +#define MAX9295_START_PIPE_ADDR 0x311 +#define MAX9295_PIPE_EN_ADDR 0x2 +#define MAX9295_CSI_PORT_SEL_ADDR 0x308 + +#define MAX9295_I2C4_ADDR 0x44 +#define MAX9295_I2C5_ADDR 0x45 + +#define MAX9295_DEV_ADDR 0x00 + +#define MAX9295_STREAM_PIPE_UNUSED 0x22 +#define MAX9295_CSI_MODE_1X4 0x00 +#define MAX9295_CSI_MODE_2X2 0x03 +#define MAX9295_CSI_MODE_2X4 0x06 + +#define MAX9295_CSI_PORT_B(num_lanes) (((num_lanes) << 4) & 0xF0) +#define MAX9295_CSI_PORT_A(num_lanes) ((num_lanes) & 0x0F) + +#define MAX9295_CSI_1X4_MODE_LANE_MAP1 0xE0 +#define MAX9295_CSI_1X4_MODE_LANE_MAP2 0x04 + +#define MAX9295_CSI_2X4_MODE_LANE_MAP1 0xEE +#define MAX9295_CSI_2X4_MODE_LANE_MAP2 0xE4 + +#define MAX9295_CSI_2X2_MODE_LANE_MAP1 MAX9295_CSI_2X4_MODE_LANE_MAP1 +#define MAX9295_CSI_2X2_MODE_LANE_MAP2 MAX9295_CSI_2X4_MODE_LANE_MAP2 + +#define MAX9295_ST_ID_0 0x0 +#define MAX9295_ST_ID_1 0x1 +#define MAX9295_ST_ID_2 0x2 +#define MAX9295_ST_ID_3 0x3 + +#define MAX9295_PIPE_X_START_B 0x80 +#define MAX9295_PIPE_Y_START_B 0x40 +#define MAX9295_PIPE_Z_START_B 0x20 +#define MAX9295_PIPE_U_START_B 0x10 + +#define MAX9295_PIPE_X_START_A 0x1 +#define MAX9295_PIPE_Y_START_A 0x2 +#define MAX9295_PIPE_Z_START_A 0x4 +#define MAX9295_PIPE_U_START_A 0x8 + +#define MAX9295_START_PORT_A 0x10 +#define MAX9295_START_PORT_B 0x20 + +#define MAX9295_CSI_LN2 0x1 +#define MAX9295_CSI_LN4 0x3 + +#define MAX9295_EN_LINE_INFO 0x40 + +#define MAX9295_VID_TX_EN_X 0x10 +#define MAX9295_VID_TX_EN_Y 0x20 +#define MAX9295_VID_TX_EN_Z 0x40 +#define MAX9295_VID_TX_EN_U 0x80 + +#define MAX9295_VID_INIT 0x3 +#define MAX9295_SRC_RCLK 0x89 + +#define MAX9295_RESET_ALL 0x80 +#define MAX9295_RESET_SRC 0x60 +#define MAX9295_PWDN_GPIO 0x90 + +#define MAX9295_MAX_PIPES 0x4 + +struct max9295_client_ctx { + struct gmsl_link_ctx *g_ctx; + bool st_done; +}; + +struct max9295 { + struct i2c_client *i2c_client; + struct regmap *regmap; + struct max9295_client_ctx g_client; + struct mutex lock; + /* primary serializer properties */ + __u32 def_addr; + __u32 pst2_ref; +}; + +static struct max9295 *prim_priv__; + +struct map_ctx { + u8 dt; + u16 addr; + u8 val; + u8 st_id; +}; + +static int max9295_write_reg(struct device *dev, u16 addr, u8 val) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err; + + err = regmap_write(priv->regmap, addr, val); + if (err) + dev_err(dev, "%s:i2c write failed, 0x%x = %x\n", + __func__, addr, val); + + /* delay before next i2c command as required for SERDES link */ + usleep_range(100, 110); + + return err; +} + +int max9295_setup_streaming(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + u32 csi_mode; + u32 lane_map1; + u32 lane_map2; + u32 port; + u32 rx1_lanes; + u32 st_pipe; + u32 pipe_en; + u32 port_sel = 0; + struct gmsl_link_ctx *g_ctx; + u32 i; + u32 j; + u32 st_en; + + struct map_ctx map_pipe_dtype[] = { + {GMSL_CSI_DT_RAW_12, MAX9295_PIPE_Z_DT_ADDR, 0x2C, + MAX9295_ST_ID_2}, + {GMSL_CSI_DT_UED_U1, MAX9295_PIPE_X_DT_ADDR, 0x30, + MAX9295_ST_ID_0}, + {GMSL_CSI_DT_EMBED, MAX9295_PIPE_Y_DT_ADDR, 0x12, + MAX9295_ST_ID_1}, + }; + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sdev client found\n", __func__); + err = -EINVAL; + goto error; + } + + if (priv->g_client.st_done) { + dev_dbg(dev, "%s: stream setup is already done\n", __func__); + goto error; + } + + g_ctx = priv->g_client.g_ctx; + + switch (g_ctx->csi_mode) { + case GMSL_CSI_1X4_MODE: + csi_mode = MAX9295_CSI_MODE_1X4; + lane_map1 = MAX9295_CSI_1X4_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_1X4_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN4; + break; + case GMSL_CSI_2X2_MODE: + csi_mode = MAX9295_CSI_MODE_2X2; + lane_map1 = MAX9295_CSI_2X2_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_2X2_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN2; + break; + case GMSL_CSI_2X4_MODE: + csi_mode = MAX9295_CSI_MODE_2X4; + lane_map1 = MAX9295_CSI_2X4_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_2X4_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN4; + break; + default: + dev_err(dev, "%s: invalid csi mode\n", __func__); + err = -EINVAL; + goto error; + } + + port = (g_ctx->src_csi_port == GMSL_CSI_PORT_B) ? + MAX9295_CSI_PORT_B(rx1_lanes) : + MAX9295_CSI_PORT_A(rx1_lanes); + + max9295_write_reg(dev, MAX9295_MIPI_RX0_ADDR, csi_mode); + max9295_write_reg(dev, MAX9295_MIPI_RX1_ADDR, port); + max9295_write_reg(dev, MAX9295_MIPI_RX2_ADDR, lane_map1); + max9295_write_reg(dev, MAX9295_MIPI_RX3_ADDR, lane_map2); + + for (i = 0; i < g_ctx->num_streams; i++) { + struct gmsl_stream *g_stream = &g_ctx->streams[i]; + + g_stream->st_id_sel = GMSL_ST_ID_UNUSED; + for (j = 0; j < ARRAY_SIZE(map_pipe_dtype); j++) { + if (map_pipe_dtype[j].dt == g_stream->st_data_type) { + /* + * TODO: + * 1) Remove link specific overrides, depends + * on #2. + * 2) Add support for vc id based stream sel + * overrides TX_SRC_SEL. would be useful in + * using same mappings in all ser devs. + */ + if (g_ctx->serdes_csi_link == + GMSL_SERDES_CSI_LINK_B) { + map_pipe_dtype[j].addr += 2; + map_pipe_dtype[j].st_id += 1; + } + + g_stream->st_id_sel = map_pipe_dtype[j].st_id; + st_en = (map_pipe_dtype[j].addr == + MAX9295_PIPE_X_DT_ADDR) ? + 0xC0 : 0x40; + + max9295_write_reg(dev, map_pipe_dtype[j].addr, + (st_en | map_pipe_dtype[j].val)); + } + } + } + + for (i = 0; i < g_ctx->num_streams; i++) + if (g_ctx->streams[i].st_id_sel != GMSL_ST_ID_UNUSED) + port_sel |= (1 << g_ctx->streams[i].st_id_sel); + + if (g_ctx->src_csi_port == GMSL_CSI_PORT_B) { + st_pipe = (MAX9295_PIPE_X_START_B | MAX9295_PIPE_Y_START_B | + MAX9295_PIPE_Z_START_B | MAX9295_PIPE_U_START_B); + port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_B); + } else { + st_pipe = MAX9295_PIPE_X_START_A | MAX9295_PIPE_Y_START_A | + MAX9295_PIPE_Z_START_A | MAX9295_PIPE_U_START_A; + port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_A); + } + + pipe_en = (MAX9295_VID_TX_EN_X | MAX9295_VID_TX_EN_Y | + MAX9295_VID_TX_EN_Z | MAX9295_VID_TX_EN_U | MAX9295_VID_INIT); + + max9295_write_reg(dev, MAX9295_START_PIPE_ADDR, st_pipe); + max9295_write_reg(dev, MAX9295_CSI_PORT_SEL_ADDR, port_sel); + max9295_write_reg(dev, MAX9295_PIPE_EN_ADDR, pipe_en); + + priv->g_client.st_done = true; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_setup_streaming); + +int max9295_setup_control(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + struct gmsl_link_ctx *g_ctx; + u32 offset1 = 0; + u32 offset2 = 0; + u32 i; + + u8 i2c_ovrd[] = { + 0x6B, 0x10, + 0x73, 0x11, + 0x7B, 0x30, + 0x83, 0x30, + 0x93, 0x30, + 0x9B, 0x30, + 0xA3, 0x30, + 0xAB, 0x30, + 0x8B, 0x30, + }; + + u8 addr_offset[] = { + 0x80, 0x00, 0x00, + 0x84, 0x00, 0x01, + 0x86, 0x00, 0x02, + 0x88, 0x00, 0x03, + 0x8A, 0x00, 0x04, + 0x8C, 0x00, 0x05, + 0xC0, 0x02, 0x06, + 0xC4, 0x02, 0x07, + 0xC6, 0x02, 0x08, + 0xC8, 0x02, 0x09, + 0xCA, 0x02, 0x0a, + 0xCC, 0x02, 0x0b, + }; + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sensor dev client found\n", __func__); + err = -EINVAL; + goto error; + } + + g_ctx = priv->g_client.g_ctx; + +#ifdef CONFIG_OF + if (prim_priv__) { + /* update address reassignment */ + max9295_write_reg(&prim_priv__->i2c_client->dev, + MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); + } +#else + if (priv->def_addr) { + /* update address reassignment */ + struct i2c_client *c = to_i2c_client(dev); + int addr = c->addr; + dev_info(dev, "%s: update address reassignment 0x%x->0x%x\n", __func__,priv->def_addr, g_ctx->ser_reg); + c->addr = priv->def_addr; + max9295_write_reg(dev, + MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); + c->addr = addr; + prim_priv__ = priv; + } else { + prim_priv__ = NULL; + } +#endif + if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A) + err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x21); + else + err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x22); + + /* check if serializer device exists */ + if (err) { + dev_err(dev, "%s: ERROR: ser device not found\n", __func__); + goto error; + } + + /* delay to settle link */ + msleep(100); + + for (i = 0; i < ARRAY_SIZE(addr_offset); i += 3) { + if ((g_ctx->ser_reg << 1) == addr_offset[i]) { + offset1 = addr_offset[i+1]; + offset2 = addr_offset[i+2]; + break; + } + } + + if (i == ARRAY_SIZE(addr_offset)) { + dev_err(dev, "%s: invalid ser slave address\n", __func__); + err = -EINVAL; + goto error; + } + + for (i = 0; i < ARRAY_SIZE(i2c_ovrd); i += 2) { + /* update address overrides */ + i2c_ovrd[i+1] += (i < 4) ? offset1 : offset2; + + /* i2c passthrough2 must be configured once for all devices */ + if ((i2c_ovrd[i] == 0x8B) && prim_priv__ && + prim_priv__->pst2_ref) + continue; + + max9295_write_reg(dev, i2c_ovrd[i], i2c_ovrd[i+1]); + } + + /* dev addr pass-through2 ref */ + if (prim_priv__) + prim_priv__->pst2_ref++; + + max9295_write_reg(dev, MAX9295_I2C4_ADDR, (g_ctx->sdev_reg << 1)); + max9295_write_reg(dev, MAX9295_I2C5_ADDR, (g_ctx->sdev_def << 1)); + msleep(100); + max9295_write_reg(dev, MAX9295_SRC_PWDN_ADDR, MAX9295_PWDN_GPIO); + max9295_write_reg(dev, MAX9295_SRC_CTRL_ADDR, MAX9295_RESET_SRC); + max9295_write_reg(dev, MAX9295_SRC_OUT_RCLK_ADDR, MAX9295_SRC_RCLK); + + g_ctx->serdev_found = true; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_setup_control); + +int max9295_reset_control(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sdev client found\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.st_done = false; +#ifndef CONFIG_OF + if (priv->def_addr) { + prim_priv__ = priv; + } else { + prim_priv__ = NULL; + } +#endif + if (prim_priv__) { + prim_priv__->pst2_ref--; + + max9295_write_reg(dev, MAX9295_DEV_ADDR, + (prim_priv__->def_addr << 1)); + if (prim_priv__->pst2_ref == 0) +#ifdef CONFIG_OF + max9295_write_reg(&prim_priv__->i2c_client->dev, + MAX9295_CTRL0_ADDR, MAX9295_RESET_ALL); +#else + if (priv->def_addr) { + /* update address reassingment */ + struct i2c_client *c = to_i2c_client(dev); + int addr = c->addr; + c->addr = priv->def_addr; + max9295_write_reg(dev, + MAX9295_CTRL0_ADDR, MAX9295_RESET_ALL); + c->addr = addr; + } +#endif + } + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_reset_control); + +int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx) +{ + struct max9295 *priv; + int err = 0; + + if (!dev || !g_ctx || !g_ctx->s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + mutex_lock(&priv->lock); + if (priv->g_client.g_ctx) { + dev_err(dev, "%s: device already paired\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.st_done = false; + + priv->g_client.g_ctx = g_ctx; + +error: + mutex_unlock(&priv->lock); + return 0; +} +EXPORT_SYMBOL(max9295_sdev_pair); + +int max9295_sdev_unpair(struct device *dev, struct device *s_dev) +{ + struct max9295 *priv = NULL; + int err = 0; + + if (!dev || !s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: device is not paired\n", __func__); + err = -ENOMEM; + goto error; + } + + if (priv->g_client.g_ctx->s_dev != s_dev) { + dev_err(dev, "%s: invalid device\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.g_ctx = NULL; + priv->g_client.st_done = false; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_sdev_unpair); + +static struct regmap_config max9295_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +struct reg_pair { + u16 addr; + u8 val; +}; + +static int max9295_set_registers(struct device *dev, struct reg_pair *map, + u32 count) +{ + int err = 0; + u32 j = 0; + + for (j = 0; j < count; j++) { + err = max9295_write_reg(dev, + map[j].addr, map[j].val); + if (err != 0) + break; + } + + return err; +} + +static int __max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) +{ + int err = 0; + u8 bpp = 0x30; + static u8 pipe_x_val = 0x0; + struct reg_pair map_multi_pipe_en[] = { + {0x0315, 0x80}, + }; + struct reg_pair map_bpp8dbl[] = { + {0x0312, 0x0F}, + }; + struct reg_pair map_pipe_control[] = { + /* addr, val */ + {MAX9295_PIPE_X_DT_ADDR, 0x5E}, // Pipe X pulls data_type1 + {0x0315, 0x52}, // Pipe X pulls data_type2 + {0x0309, 0x01}, // # Pipe X pulls vc_id + {0x030A, 0x00}, + {0x031C, 0x30}, // BPP in pipe X + {0x0102, 0x0E}, // LIM_HEART Pipe X: Disabled + }; + + if (data_type1 == GMSL_CSI_DT_RAW_8 || data_type1 == GMSL_CSI_DT_EMBED + || data_type2 == GMSL_CSI_DT_RAW_8 || data_type2 == GMSL_CSI_DT_EMBED) { + map_bpp8dbl[0].val |= (1 << pipe_id); + } else { + map_bpp8dbl[0].val &= ~(1 << pipe_id); + } + err |= max9295_set_registers(dev, map_bpp8dbl, ARRAY_SIZE(map_bpp8dbl)); + + if (data_type1 == GMSL_CSI_DT_RGB_888) + bpp = 0x18; + + map_pipe_control[0].addr += 0x2 * pipe_id; + map_pipe_control[1].addr += 0x2 * pipe_id; + map_pipe_control[2].addr += 0x2 * pipe_id; + map_pipe_control[3].addr += 0x2 * pipe_id; + map_pipe_control[4].addr += 0x1 * pipe_id; + map_pipe_control[5].addr += 0x8 * pipe_id; + + map_pipe_control[0].val = 0x40 | data_type1; + map_pipe_control[1].val = 0x40 | data_type2; + map_pipe_control[2].val = 1 << vc_id; + map_pipe_control[3].val = 0x00; + map_pipe_control[4].val = bpp; + map_pipe_control[5].val = 0x0E; + + if (pipe_id == 0) + pipe_x_val = map_pipe_control[1].val; + + err |= max9295_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + map_multi_pipe_en[0].val = 0x80 | pipe_x_val; + err |= max9295_set_registers(dev, map_multi_pipe_en, + ARRAY_SIZE(map_multi_pipe_en)); + + return err; +} + +int max9295_init_settings(struct device *dev) +{ + int err = 0; + int i; + struct max9295 *priv = dev_get_drvdata(dev); + + struct reg_pair map_pipe_opt[] = { + // Enable all pipes + {MAX9295_PIPE_EN_ADDR, 0xF3}, + // Write 0x33 for 4 lanes + {MAX9295_MIPI_RX1_ADDR, 0x11}, + // All pipes pull clock from port B + {MAX9295_CSI_PORT_SEL_ADDR, 0x6F}, + // All pipes pull data from port B + {MAX9295_START_PIPE_ADDR, 0xF0}, + }; + + mutex_lock(&priv->lock); + + // Init control + err |= max9295_set_registers(dev, map_pipe_opt, + ARRAY_SIZE(map_pipe_opt)); + + for (i = 0; i < MAX9295_MAX_PIPES; i++) + err |= __max9295_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9295_init_settings); + +int max9295_set_pipe(struct device *dev, int pipe_id, + u8 data_type1, u8 data_type2, u32 vc_id) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + + if (pipe_id > (MAX9295_MAX_PIPES - 1)) { + dev_info(dev, "%s, input pipe_id: %d exceed max9295 max pipes\n", + __func__, pipe_id); + return -EINVAL; + } + + dev_dbg(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", + __func__, pipe_id, data_type1, data_type2, vc_id); + + mutex_lock(&priv->lock); + + err = __max9295_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9295_set_pipe); + +static int max9295_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max9295 *priv; + struct max9295_pdata *pdata = client->dev.platform_data; + int err = 0; +#ifdef CONFIG_OF + struct device_node *node = client->dev.of_node; +#endif + + dev_info(&client->dev, "[MAX9295]: probing GMSL Serializer\n"); + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv->i2c_client = client; + priv->regmap = devm_regmap_init_i2c(priv->i2c_client, + &max9295_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } + + mutex_init(&priv->lock); +#ifdef CONFIG_OF + if (of_get_property(node, "is-prim-ser", NULL)) { + if (prim_priv__) { + dev_err(&client->dev, + "prim-ser already exists\n"); + return -EEXIST; + } + + err = of_property_read_u32(node, "reg", &priv->def_addr); + if (err < 0) { + dev_err(&client->dev, "reg not found\n"); + return -EINVAL; + } + + prim_priv__ = priv; + } +#else + if (pdata->is_prim_ser) { + prim_priv__ = priv; + priv->def_addr = pdata->def_addr; + } else { + priv->def_addr = 0; + } +#endif + dev_set_drvdata(&client->dev, priv); + + /* dev communication gets validated when GMSL link setup is done */ + dev_info(&client->dev, "%s: success\n", __func__); + + return err; +} + +static int max9295_remove(struct i2c_client *client) +{ + struct max9295 *priv; + + if (client != NULL) { + priv = dev_get_drvdata(&client->dev); + dev_info(&client->dev, "[MAX9295]: remove GMSL Serializer\n"); + mutex_destroy(&priv->lock); +#ifdef CONFIG_OF + i2c_unregister_device(client); + client = NULL; +#endif + } + + return 0; +} + +static const struct i2c_device_id max9295_id[] = { + { "max9295", 0 }, + { }, +}; + +static const struct of_device_id max9295_of_match[] = { + { .compatible = "maxim,max9295", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max9295_of_match); +MODULE_DEVICE_TABLE(i2c, max9295_id); + +static struct i2c_driver max9295_i2c_driver = { + .driver = { + .name = "max9295", + .owner = THIS_MODULE, + }, + .probe = max9295_probe, + .remove = max9295_remove, + .id_table = max9295_id, +}; + +static int __init max9295_init(void) +{ + return i2c_add_driver(&max9295_i2c_driver); +} + +static void __exit max9295_exit(void) +{ + i2c_del_driver(&max9295_i2c_driver); +} + +module_init(max9295_init); +module_exit(max9295_exit); + +MODULE_DESCRIPTION("GMSL Serializer driver max9295"); +MODULE_AUTHOR("Sudhir Vyas "); +MODULE_AUTHOR("Dmitry Perchanov "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/max9296.c new file mode 100644 index 00000000..8bd4ac04 --- /dev/null +++ b/drivers/media/i2c/d4xx-compat/max9296.c @@ -0,0 +1,1184 @@ +/* + * max9296.c - max9296 GMSL Deserializer driver + * + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include + +/* register specifics */ +#define MAX9296_DST_CSI_MODE_ADDR 0x330 +#define MAX9296_LANE_MAP1_ADDR 0x333 +#define MAX9296_LANE_MAP2_ADDR 0x334 + +#define MAX9296_LANE_CTRL0_ADDR 0x40A +#define MAX9296_LANE_CTRL1_ADDR 0x44A +#define MAX9296_LANE_CTRL2_ADDR 0x48A +#define MAX9296_LANE_CTRL3_ADDR 0x4CA + +#define MAX9296_TX11_PIPE_X_EN_ADDR 0x40B +#define MAX9296_TX45_PIPE_X_DST_CTRL_ADDR 0x42D + +#define MAX9296_PIPE_X_SRC_0_MAP_ADDR 0x40D +#define MAX9296_PIPE_X_DST_0_MAP_ADDR 0x40E +#define MAX9296_PIPE_X_SRC_1_MAP_ADDR 0x40F +#define MAX9296_PIPE_X_DST_1_MAP_ADDR 0x410 +#define MAX9296_PIPE_X_SRC_2_MAP_ADDR 0x411 +#define MAX9296_PIPE_X_DST_2_MAP_ADDR 0x412 +#define MAX9296_PIPE_X_SRC_3_MAP_ADDR 0x413 +#define MAX9296_PIPE_X_DST_3_MAP_ADDR 0x414 + +#define MAX9296_PIPE_X_ST_SEL_ADDR 0x50 + +#define MAX9296_PWDN_PHYS_ADDR 0x332 +#define MAX9296_PHY1_CLK_ADDR 0x320 +#define MAX9296_CTRL0_ADDR 0x10 + +/* data defines */ +#define MAX9296_CSI_MODE_4X2 0x1 +#define MAX9296_CSI_MODE_2X4 0x4 +#define MAX9296_LANE_MAP1_4X2 0x44 +#define MAX9296_LANE_MAP2_4X2 0x44 +#define MAX9296_LANE_MAP1_2X4 0x4E +#define MAX9296_LANE_MAP2_2X4 0xE4 + +#define MAX9296_LANE_CTRL_MAP(num_lanes) \ + (((num_lanes) << 6) & 0xF0) + +#define MAX9296_ALLPHYS_NOSTDBY 0xF0 +#define MAX9296_ST_ID_SEL_INVALID 0xF + +#define MAX9296_PHY1_CLK 0x2C + +#define MAX9296_RESET_ALL 0x80 + +/* Dual GMSL MAX9296A/B */ +#define MAX9296_MAX_SOURCES 2 + +#define MAX9296_MAX_PIPES 4 + +#define MAX9296_PIPE_X 0 +#define MAX9296_PIPE_Y 1 +#define MAX9296_PIPE_Z 2 +#define MAX9296_PIPE_U 3 +#define MAX9296_PIPE_INVALID 0xF + + +#define MAX9296_CSI_CTRL_0 0 +#define MAX9296_CSI_CTRL_1 1 +#define MAX9296_CSI_CTRL_2 2 +#define MAX9296_CSI_CTRL_3 3 + +#define MAX9296_INVAL_ST_ID 0xFF + +/* Use reset value as per spec, confirm with vendor */ +#define MAX9296_RESET_ST_ID 0x00 + +struct max9296_source_ctx { + struct gmsl_link_ctx *g_ctx; + bool st_enabled; +}; + +struct pipe_ctx { + u32 id; + u32 dt_type; + u32 dst_csi_ctrl; + u32 st_count; + u32 st_id_sel; +}; + +struct max9296 { + struct i2c_client *i2c_client; + struct regmap *regmap; + u32 num_src; + u32 max_src; + u32 num_src_found; + u32 src_link; + bool splitter_enabled; + struct max9296_source_ctx sources[MAX9296_MAX_SOURCES]; + struct mutex lock; + u32 sdev_ref; + bool lane_setup; + bool link_setup; + struct pipe_ctx pipe[MAX9296_MAX_PIPES]; + u8 csi_mode; + u8 lane_mp1; + u8 lane_mp2; + int reset_gpio; + int pw_ref; + struct regulator *vdd_cam_1v2; +}; + +static int max9296_write_reg(struct device *dev, + u16 addr, u8 val) +{ + struct max9296 *priv; + int err; + + priv = dev_get_drvdata(dev); + + err = regmap_write(priv->regmap, addr, val); + if (err) + dev_err(dev, + "%s:i2c write failed, 0x%x = %x\n", + __func__, addr, val); + + /* delay before next i2c command as required for SERDES link */ + usleep_range(100, 110); + + return err; +} + +static int max9296_get_sdev_idx(struct device *dev, + struct device *s_dev, unsigned int *idx) +{ + struct max9296 *priv = dev_get_drvdata(dev); + unsigned int i; + int err = 0; + + mutex_lock(&priv->lock); + for (i = 0; i < priv->max_src; i++) { + if (priv->sources[i].g_ctx->s_dev == s_dev) + break; + } + if (i == priv->max_src) { + dev_err(dev, "no sdev found\n"); + err = -EINVAL; + goto ret; + } + + if (idx) + *idx = i; + +ret: + mutex_unlock(&priv->lock); + return err; +} + +static void max9296_pipes_reset(struct max9296 *priv) +{ + /* + * This is default pipes combination. add more mappings + * for other combinations and requirements. + */ + struct pipe_ctx pipe_defaults[] = { + {MAX9296_PIPE_X, GMSL_CSI_DT_RAW_12, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_Y, GMSL_CSI_DT_RAW_12, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_Z, GMSL_CSI_DT_EMBED, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_U, GMSL_CSI_DT_EMBED, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID} + }; + + /* + * Add DT props for num-streams and stream sequence, and based on that + * set the appropriate pipes defaults. + * For now default it supports "2 RAW12 and 2 EMBED" 1:1 mappings. + */ + memcpy(priv->pipe, pipe_defaults, sizeof(pipe_defaults)); +} + +static void max9296_reset_ctx(struct max9296 *priv) +{ + unsigned int i; + + priv->link_setup = false; + priv->lane_setup = false; + priv->num_src_found = 0; + priv->src_link = 0; + priv->splitter_enabled = false; + max9296_pipes_reset(priv); + for (i = 0; i < priv->num_src; i++) + priv->sources[i].st_enabled = false; +} + +int max9296_power_on(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (priv->pw_ref == 0) { + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + usleep_range(30, 50); + + if (priv->vdd_cam_1v2) { + err = regulator_enable(priv->vdd_cam_1v2); + if (unlikely(err)) + goto ret; + } + + usleep_range(30, 50); + + /*exit reset mode: XCLR */ + if (priv->reset_gpio) { + gpio_set_value(priv->reset_gpio, 0); + usleep_range(30, 50); + gpio_set_value(priv->reset_gpio, 1); + usleep_range(30, 50); + } + + /* delay to settle reset */ + msleep(20); + } + + priv->pw_ref++; + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_power_on); + +void max9296_power_off(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + priv->pw_ref--; + + if (priv->pw_ref < 0) + priv->pw_ref = 0; + + if (priv->pw_ref == 0) { + /* enter reset mode: XCLR */ + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + if (priv->vdd_cam_1v2) + regulator_disable(priv->vdd_cam_1v2); + } + + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max9296_power_off); + +static int max9296_write_link(struct device *dev, u32 link) +{ + if (link == GMSL_SERDES_CSI_LINK_A) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x01); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x21); + } else if (link == GMSL_SERDES_CSI_LINK_B) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x02); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x22); + } else { + dev_err(dev, "%s: invalid gmsl link\n", __func__); + return -EINVAL; + } + + /* delay to settle link */ + msleep(100); + + return 0; +} + +int max9296_setup_link(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + + if (!priv->splitter_enabled) { + err = max9296_write_link(dev, + priv->sources[i].g_ctx->serdes_csi_link); + if (err) + goto ret; + + priv->link_setup = true; + } + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_setup_link); + +int max9296_setup_control(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + + if (!priv->link_setup) { + dev_err(dev, "%s: invalid state\n", __func__); + err = -EINVAL; + goto error; + } + + if (priv->sources[i].g_ctx->serdev_found) { + priv->num_src_found++; + priv->src_link = priv->sources[i].g_ctx->serdes_csi_link; + } + + /* Enable splitter mode */ + if ((priv->max_src > 1U) && + (priv->num_src_found > 0U) && + (priv->splitter_enabled == false)) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x03); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x23); + + priv->splitter_enabled = true; + + /* delay to settle link */ + msleep(100); + } + + max9296_write_reg(dev, + MAX9296_PWDN_PHYS_ADDR, MAX9296_ALLPHYS_NOSTDBY); + + priv->sdev_ref++; + + /* Reset splitter mode if all devices are not found */ + if ((priv->sdev_ref != priv->max_src) && + (priv->splitter_enabled == true) && + (priv->num_src_found > 0U) && + (priv->num_src_found < priv->max_src)) { + err = max9296_write_link(dev, priv->src_link); + if (err) + goto error; + + priv->splitter_enabled = false; + } + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_setup_control); + +int max9296_reset_control(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (!priv->sdev_ref) { + dev_info(dev, "%s: dev is already in reset state\n", __func__); + goto ret; + } + + priv->sdev_ref--; + if (priv->sdev_ref == 0) { + max9296_reset_ctx(priv); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, MAX9296_RESET_ALL); + + /* delay to settle reset */ + msleep(100); + } + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_reset_control); + +int max9296_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) +{ + struct max9296 *priv = NULL; + unsigned int i; + int err = 0; + + if (!dev || !g_ctx || !g_ctx->s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + if (priv->num_src > priv->max_src) { + dev_err(dev, + "%s: MAX9296 inputs size exhausted\n", __func__); + err = -ENOMEM; + goto error; + } + + /* Check csi mode compatibility */ + if (!((priv->csi_mode == MAX9296_CSI_MODE_2X4) ? + ((g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || + (g_ctx->csi_mode == GMSL_CSI_2X4_MODE)) : + ((g_ctx->csi_mode == GMSL_CSI_2X2_MODE) || + (g_ctx->csi_mode == GMSL_CSI_4X2_MODE)))) { + dev_err(dev, + "%s: csi mode not supported\n", __func__); + err = -EINVAL; + goto error; + } + + for (i = 0; i < priv->num_src; i++) { + if (g_ctx->serdes_csi_link == + priv->sources[i].g_ctx->serdes_csi_link) { + dev_err(dev, + "%s: serdes csi link is in use\n", __func__); + err = -EINVAL; + goto error; + } + /* + * All sdevs should have same num-csi-lanes regardless of + * dst csi port selected. + * Later if there is any usecase which requires each port + * to be configured with different num-csi-lanes, then this + * check should be performed per port. + */ + if (g_ctx->num_csi_lanes != + priv->sources[i].g_ctx->num_csi_lanes) { + dev_err(dev, + "%s: csi num lanes mismatch\n", __func__); + err = -EINVAL; + goto error; + } + } + + priv->sources[priv->num_src].g_ctx = g_ctx; + priv->sources[priv->num_src].st_enabled = false; + + priv->num_src++; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_sdev_register); + +int max9296_sdev_unregister(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = NULL; + int err = 0; + unsigned int i = 0; + + if (!dev || !s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + mutex_lock(&priv->lock); + + if (priv->num_src == 0) { + dev_err(dev, "%s: no source found\n", __func__); + err = -ENODATA; + goto error; + } + + for (i = 0; i < priv->num_src; i++) { + if (s_dev == priv->sources[i].g_ctx->s_dev) { + priv->sources[i].g_ctx = NULL; + break; + } + } + + if (i == priv->num_src) { + dev_err(dev, + "%s: requested device not found\n", __func__); + err = -EINVAL; + goto error; + } + priv->num_src--; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_sdev_unregister); + +static int max9296_get_available_pipe(struct device *dev, + u32 st_data_type, u32 dst_csi_port) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < MAX9296_MAX_PIPES; i++) { + /* + * TODO: Enable a pipe for multi stream configuration having + * similar stream data type. For now use st_count as a flag + * for 1 to 1 mapping in pipe and stream data type, same can + * be extended as count for many to 1 mapping. Would also need + * few more checks such as input stream id select, dst port etc. + */ + if ((priv->pipe[i].dt_type == st_data_type) && + ((dst_csi_port == GMSL_CSI_PORT_A) ? + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_0) || + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_1) : + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_2) || + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_3)) && + (!priv->pipe[i].st_count)) + break; + } + + if (i == MAX9296_MAX_PIPES) { + dev_err(dev, "%s: all pipes are busy\n", __func__); + return -ENOMEM; + } + + return i; +} + +struct reg_pair { + u16 addr; + u8 val; +}; + +static int max9296_setup_pipeline(struct device *dev, + struct gmsl_link_ctx *g_ctx) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_stream *g_stream; + struct reg_pair *map_list; + u32 arr_sz = 0; + int pipe_id = 0; + u32 i = 0; + u32 j = 0; + u32 vc_idx = 0; + + for (i = 0; i < g_ctx->num_streams; i++) { + /* Base data type mapping: pipeX/RAW12/CSICNTR1 */ + struct reg_pair map_pipe_raw12[] = { + /* addr, val */ + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x7}, + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x15}, + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x2C}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x2C}, + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + }; + + /* Base data type mapping: pipeX/EMBED/CSICNTR1 */ + struct reg_pair map_pipe_embed[] = { + /* addr, val */ + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x7}, + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x15}, + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + }; + + g_stream = &g_ctx->streams[i]; + g_stream->des_pipe = MAX9296_PIPE_INVALID; + + if (g_stream->st_data_type == GMSL_CSI_DT_RAW_12) { + map_list = map_pipe_raw12; + arr_sz = ARRAY_SIZE(map_pipe_raw12); + } else if (g_stream->st_data_type == GMSL_CSI_DT_EMBED) { + map_list = map_pipe_embed; + arr_sz = ARRAY_SIZE(map_pipe_embed); + } else if (g_stream->st_data_type == GMSL_CSI_DT_UED_U1) { + dev_dbg(dev, + "%s: No mapping for GMSL_CSI_DT_UED_U1\n", + __func__); + continue; + } else { + dev_err(dev, "%s: Invalid data type\n", __func__); + return -EINVAL; + } + + pipe_id = max9296_get_available_pipe(dev, + g_stream->st_data_type, g_ctx->dst_csi_port); + if (pipe_id < 0) + return pipe_id; + + for (j = 0, vc_idx = 3; j < arr_sz; j++, vc_idx += 2) { + /* update pipe configuration */ + map_list[j].addr += (0x40 * pipe_id); + /* update vc id configuration */ + if (vc_idx < arr_sz) + map_list[vc_idx].val |= + (g_ctx->dst_vc << 6); + + max9296_write_reg(dev, map_list[j].addr, + map_list[j].val); + } + + /* Set stream id select input */ + if (g_stream->st_id_sel == GMSL_ST_ID_UNUSED) { + dev_err(dev, "%s: Invalid stream st_id_sel\n", + __func__); + return -EINVAL; + } + + g_stream->des_pipe = MAX9296_PIPE_X_ST_SEL_ADDR + pipe_id; + + /* Update pipe internals */ + priv->pipe[pipe_id].st_count++; + priv->pipe[pipe_id].st_id_sel = g_stream->st_id_sel; + } + + return 0; +} + +int max9296_start_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + struct gmsl_stream *g_stream; + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + g_ctx = priv->sources[i].g_ctx; + + for (i = 0; i < g_ctx->num_streams; i++) { + g_stream = &g_ctx->streams[i]; + + if (g_stream->des_pipe != MAX9296_PIPE_INVALID) + max9296_write_reg(dev, g_stream->des_pipe, + g_stream->st_id_sel); + } + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_start_streaming); + +int max9296_stop_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + struct gmsl_stream *g_stream; + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + g_ctx = priv->sources[i].g_ctx; + + for (i = 0; i < g_ctx->num_streams; i++) { + g_stream = &g_ctx->streams[i]; + + if (g_stream->des_pipe != MAX9296_PIPE_INVALID) + max9296_write_reg(dev, g_stream->des_pipe, + MAX9296_RESET_ST_ID); + } + + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_stop_streaming); + +int max9296_setup_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + int err = 0; + unsigned int i = 0; + u16 lane_ctrl_addr; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + if (priv->sources[i].st_enabled) + goto ret; + + g_ctx = priv->sources[i].g_ctx; + + err = max9296_setup_pipeline(dev, g_ctx); + if (err) + goto ret; + + /* Derive CSI lane map register */ + switch(g_ctx->dst_csi_port) { + case GMSL_CSI_PORT_A: + case GMSL_CSI_PORT_D: + lane_ctrl_addr = MAX9296_LANE_CTRL1_ADDR; + break; + case GMSL_CSI_PORT_B: + case GMSL_CSI_PORT_E: + lane_ctrl_addr = MAX9296_LANE_CTRL2_ADDR; + break; + case GMSL_CSI_PORT_C: + lane_ctrl_addr = MAX9296_LANE_CTRL0_ADDR; + break; + case GMSL_CSI_PORT_F: + lane_ctrl_addr = MAX9296_LANE_CTRL3_ADDR; + break; + default: + dev_err(dev, "%s: invalid gmsl csi port!\n", __func__); + err = -EINVAL; + goto ret; + }; + + /* + * rewrite num_lanes to same dst port should not be an issue, + * as the device compatibility is already + * checked during sdev registration against the des properties. + */ + max9296_write_reg(dev, lane_ctrl_addr, + MAX9296_LANE_CTRL_MAP(g_ctx->num_csi_lanes-1)); + + if (!priv->lane_setup) { + max9296_write_reg(dev, + MAX9296_DST_CSI_MODE_ADDR, priv->csi_mode); + max9296_write_reg(dev, + MAX9296_LANE_MAP1_ADDR, priv->lane_mp1); + max9296_write_reg(dev, + MAX9296_LANE_MAP2_ADDR, priv->lane_mp2); + max9296_write_reg(dev, + MAX9296_PHY1_CLK_ADDR, MAX9296_PHY1_CLK); + + priv->lane_setup = true; + } + + priv->sources[i].st_enabled = true; + +ret: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_setup_streaming); + +static int max9296_set_registers(struct device *dev, struct reg_pair *map, + u32 count) +{ + int err = 0; + u32 j = 0; + + for (j = 0; j < count; j++) { + err = max9296_write_reg(dev, + map[j].addr, map[j].val); + if (err != 0) + break; + } + + return err; +} + +int max9296_get_available_pipe_id(struct device *dev, int vc_id) +{ + int i; + int pipe_id = -ENOMEM; + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + for (i = 0; i < MAX9296_MAX_PIPES; i++) { + if (i == vc_id && !priv->pipe[i].st_count) { + priv->pipe[i].st_count++; + pipe_id = i; + break; + } + } + mutex_unlock(&priv->lock); + + return pipe_id; +} +EXPORT_SYMBOL(max9296_get_available_pipe_id); + +int max9296_release_pipe(struct device *dev, int pipe_id) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + if (pipe_id < 0 || pipe_id >= MAX9296_MAX_PIPES) + return -EINVAL; + + mutex_lock(&priv->lock); + priv->pipe[pipe_id].st_count = 0; + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_release_pipe); + +void max9296_reset_oneshot(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + if (priv->splitter_enabled) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x03); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x23); + } else { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x31); + } + /* delay to settle link */ + msleep(100); + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max9296_reset_oneshot); + +static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) +{ + int err = 0; + int i = 0; + u8 en_mapping_num = 0x0F; + u8 all_mapping_phy = 0x55; + struct reg_pair map_pipe_opt[] = { + {0x1458, 0x28}, // PHY A Optimization + {0x1459, 0x68}, // PHY A Optimization + {0x1558, 0x28}, // PHY B Optimization + {0x1559, 0x68}, // PHY B Optimization + // 4 lanes on port A, write 0x50 for 2 lanes + {MAX9296_LANE_CTRL1_ADDR, 0x50}, + // 1500Mbps/lane on port A + {MAX9296_PHY1_CLK_ADDR, 0x2F}, + // Do not un-double 8bpp (Un-double 8bpp data) + //{0x031C, 0x00}, + // Do not un-double 8bpp + //{0x031F, 0x00}, + // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 + {0x0473, 0x10}, + }; + struct reg_pair map_pipe_control[] = { + // Enable 4 mappings for Pipe X + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x0F}, + // Map data_type1 on vc_id + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + // Map frame_start on vc_id + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX9296_PIPE_X_SRC_3_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_DST_3_MAP_ADDR, 0x12}, + // All mappings to PHY1 (master for port A) + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x55}, + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + {0x0100, 0x23}, // pipe X + }; + + for (i = 0; i < 10; i++) { + map_pipe_control[i].addr += 0x40 * pipe_id; + } + map_pipe_control[10].addr += 0x12 * pipe_id; + + if (data_type2 == 0x0) { + en_mapping_num = 0x07; + all_mapping_phy = 0x15; + } + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = (vc_id << 6) | data_type1; + map_pipe_control[2].val = (vc_id << 6) | data_type1; + map_pipe_control[3].val = (vc_id << 6) | 0x00; + map_pipe_control[4].val = (vc_id << 6) | 0x00; + map_pipe_control[5].val = (vc_id << 6) | 0x01; + map_pipe_control[6].val = (vc_id << 6) | 0x01; + map_pipe_control[7].val = (vc_id << 6) | data_type2; + map_pipe_control[8].val = (vc_id << 6) | data_type2; + map_pipe_control[9].val = all_mapping_phy; + map_pipe_control[10].val = 0x23; + + err |= max9296_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + err |= max9296_set_registers(dev, map_pipe_opt, + ARRAY_SIZE(map_pipe_opt)); + + return err; +} +int max9296_init_settings(struct device *dev) +{ + int err = 0; + int i; + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + for (i = 0; i < MAX9296_MAX_PIPES; i++) + err |= __max9296_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_init_settings); + +int max9296_set_pipe(struct device *dev, int pipe_id, + u8 data_type1, u8 data_type2, u32 vc_id) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + if (pipe_id > (MAX9296_MAX_PIPES - 1)) { + dev_info(dev, "%s, input pipe_id: %d exceed max9296 max pipes\n", + __func__, pipe_id); + return -EINVAL; + } + + dev_dbg(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", + __func__, pipe_id, data_type1, data_type2, vc_id); + + mutex_lock(&priv->lock); + + err = __max9296_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_set_pipe); + +static const struct of_device_id max9296_of_match[] = { + { .compatible = "maxim,max9296", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max9296_of_match); + +#ifdef CONFIG_OF +static int max9296_parse_dt(struct max9296 *priv, + struct i2c_client *client) +{ + struct device_node *node = client->dev.of_node; + int err = 0; + const char *str_value; + int value; + const struct of_device_id *match; + + if (!node) + return -EINVAL; + + match = of_match_device(max9296_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return -EFAULT; + } + + err = of_property_read_string(node, "csi-mode", &str_value); + if (err < 0) { + dev_err(&client->dev, "csi-mode property not found\n"); + return err; + } + + if (!strcmp(str_value, "2x4")) { + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + } else if (!strcmp(str_value, "4x2")) { + priv->csi_mode = MAX9296_CSI_MODE_4X2; + priv->lane_mp1 = MAX9296_LANE_MAP1_4X2; + priv->lane_mp2 = MAX9296_LANE_MAP2_4X2; + } else { + dev_err(&client->dev, "invalid csi mode\n"); + return -EINVAL; + } + + err = of_property_read_u32(node, "max-src", &value); + if (err < 0) { + dev_err(&client->dev, "No max-src info\n"); + return err; + } + priv->max_src = value; + + priv->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0); + if (priv->reset_gpio < 0) { + dev_err(&client->dev, "reset-gpios not found %d\n", err); + return err; + } + + /* digital 1.2v */ + if (of_get_property(node, "vdd_cam_1v2-supply", NULL)) { + priv->vdd_cam_1v2 = regulator_get(&client->dev, "vdd_cam_1v2"); + if (IS_ERR(priv->vdd_cam_1v2)) { + dev_err(&client->dev, + "vdd_cam_1v2 regulator get failed\n"); + err = PTR_ERR(priv->vdd_cam_1v2); + priv->vdd_cam_1v2 = NULL; + return err; + } + } else { + priv->vdd_cam_1v2 = NULL; + } + + return 0; +} +#else +static int max9296_parse_pdata(struct max9296 *priv, + struct i2c_client *client) +{ + struct max9296_pdata *pdata = client->dev.platform_data; + if (pdata) { + if (pdata->csi_mode == GMSL_CSI_2X4_MODE) { + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + } else if (pdata->csi_mode == GMSL_CSI_4X2_MODE) { + priv->csi_mode = MAX9296_CSI_MODE_4X2; + priv->lane_mp1 = MAX9296_LANE_MAP1_4X2; + priv->lane_mp2 = MAX9296_LANE_MAP2_4X2; + } else { + dev_err(&client->dev, "invalid csi mode\n"); + return -EINVAL; + } + priv->max_src = pdata->max_src; + } else { + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + priv->max_src = 1; + priv->reset_gpio = 0; + priv->vdd_cam_1v2 = NULL; + } + return 0; +} + +#endif +static struct regmap_config max9296_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static int max9296_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max9296 *priv; + int err = 0; + + dev_info(&client->dev, "[MAX9296]: probing GMSL Deserializer\n"); + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv->i2c_client = client; + priv->regmap = devm_regmap_init_i2c(priv->i2c_client, + &max9296_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } +#ifdef CONFIG_OF + err = max9296_parse_dt(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse dt\n"); + return -EFAULT; + } +#else + err = max9296_parse_pdata(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse pdata\n"); + return -EFAULT; + } +#endif + max9296_pipes_reset(priv); + + if (priv->max_src > MAX9296_MAX_SOURCES) { + dev_err(&client->dev, + "max sources more than currently supported\n"); + return -EINVAL; + } + + mutex_init(&priv->lock); + + dev_set_drvdata(&client->dev, priv); + + /* dev communication gets validated when GMSL link setup is done */ + dev_info(&client->dev, "%s: success\n", __func__); + + return err; +} + + +static int max9296_remove(struct i2c_client *client) +{ + struct max9296 *priv; + + if (client != NULL) { + priv = dev_get_drvdata(&client->dev); + dev_info(&client->dev, "[MAX9296]: remove GMSL Deserializer\n"); + mutex_destroy(&priv->lock); +#ifdef CONFIG_OF + i2c_unregister_device(client); + client = NULL; +#endif + } + + return 0; +} + +static const struct i2c_device_id max9296_id[] = { + { "max9296", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max9296_id); + +static struct i2c_driver max9296_i2c_driver = { + .driver = { + .name = "max9296", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max9296_of_match), + }, + .probe = max9296_probe, + .remove = max9296_remove, + .id_table = max9296_id, +}; + +static int __init max9296_init(void) +{ + return i2c_add_driver(&max9296_i2c_driver); +} + +static void __exit max9296_exit(void) +{ + i2c_del_driver(&max9296_i2c_driver); +} + +module_init(max9296_init); +module_exit(max9296_exit); + +MODULE_DESCRIPTION("Dual GMSL Deserializer driver max9296"); +MODULE_AUTHOR("Sudhir Vyas . + */ + +/** + * @file + * GMSL API: Gigabit Multimedia Serial Link protocol + * + * @b Description: Defines elements used to set up and use a GMSL link. + */ + +#ifndef __GMSL_LINK_H__ +/** + * \defgroup GMSL Gigabit Multimedia Serial Link (GMSL) + * + * Defines the interface used to control the MAX9295 serializer and + * MAX9296 deserializer modules. + * + * @ingroup serdes_group + * @{ + */ + +#define __GMSL_LINK_H__ + +#define GMSL_CSI_1X4_MODE 0x1 +#define GMSL_CSI_2X4_MODE 0x2 +#define GMSL_CSI_2X2_MODE 0x3 +#define GMSL_CSI_4X2_MODE 0x4 + +#define GMSL_CSI_PORT_A 0x0 +#define GMSL_CSI_PORT_B 0x1 +#define GMSL_CSI_PORT_C 0x2 +#define GMSL_CSI_PORT_D 0x3 +#define GMSL_CSI_PORT_E 0x4 +#define GMSL_CSI_PORT_F 0x5 + +#define GMSL_SERDES_CSI_LINK_A 0x1 +#define GMSL_SERDES_CSI_LINK_B 0x2 + +/* Didn't find kernel defintions, for now adding here */ +#define GMSL_CSI_DT_RAW_12 0x2C +#define GMSL_CSI_DT_UED_U1 0x30 +#define GMSL_CSI_DT_EMBED 0x12 +#define GMSL_CSI_DT_YUV422_8 0x1E +#define GMSL_CSI_DT_RGB_888 0x24 +#define GMSL_CSI_DT_RAW_8 0x2A + +#define GMSL_ST_ID_UNUSED 0xFF + +/** + * Maximum number of data streams (\ref gmsl_stream elements) in a GMSL link + * (\ref gmsl_link_ctx). + */ +#define GMSL_DEV_MAX_NUM_DATA_STREAMS 4 + +/** + * Holds information about a data stream in a GMSL link (\ref gmsl_link_ctx). + */ +struct gmsl_stream { + __u32 st_id_sel; + __u32 st_data_type; + __u32 des_pipe; +}; + +/** + * Holds the configuration of the GMSL links from a sensor to its serializer to + * its deserializer. + */ +struct gmsl_link_ctx { + __u32 st_vc; /**< Default sensor virtual channel. */ + __u32 dst_vc; /**< Destination virtual channel (user-defined). */ + __u32 src_csi_port; /**< Sensor to serializer CSI port connection. */ + __u32 dst_csi_port; /**< Deserializer to Jetson CSI port connection. */ + __u32 serdes_csi_link; /**< GMSL link between serializer and deserializer + devices. */ + __u32 num_streams; /**< Number of active streams to be mapped + from sensor. */ + __u32 num_csi_lanes; /**< Sensor's CSI lane configuration. */ + __u32 csi_mode; /**< Deserializer CSI mode. */ + __u32 ser_reg; /**< Serializer slave address. */ + __u32 sdev_reg; /**< Sensor proxy slave address. */ + __u32 sdev_def; /**< Sensor default slave address. */ + bool serdev_found; /**< Indicates whether the serializer device for + the specified sensor source was found. Set by + the serializer driver during setup; used by + the deserializer driver to choose certain + configuration settings during setup. */ + struct gmsl_stream streams[GMSL_DEV_MAX_NUM_DATA_STREAMS]; + /*< An array of information about the data streams in the link. */ + struct device *s_dev; /**< Sensor device handle. */ +}; + +/** @} */ + +#endif /* __GMSL_LINK_H__ */ diff --git a/include/media/max9295.h b/include/media/max9295.h new file mode 100644 index 00000000..8937d046 --- /dev/null +++ b/include/media/max9295.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * MAX9295 API: For Maxim Integrated MAX9295 serializer + * + * @b Description: Defines elements used to set up and use a + * Maxim Integrated MAX9295 serializer. + */ + +#ifndef __MAX9295_H__ +#define __MAX9295_H__ + +#include +#include + +struct max9295_pdata { + /* primary serializer properties */ + __u32 is_prim_ser; + __u32 def_addr; +}; + +/** + * \defgroup max9295 MAX9295 serializer driver + * + * Controls the MAX9295 serializer module. + * + * @ingroup serdes_group + * @{ + */ + +int max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id); + +/** + * @brief Powers on a serializer device and performs the I2C overrides + * for sensor and serializer devices. + * + * The I2C overrides include setting proxy I2C slave addresses for the devices. + * + * Before the client calls this function it must ensure that + * the deserializer device is in link_ex exclusive link mode + * by calling the deserializer driver's max9296_setup_link() function. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_setup_control(struct device *dev); + +/** + * Reverts I2C overrides and resets a serializer device. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_reset_control(struct device *dev); + +/** + * @brief Pairs a sensor device with a serializer device. + * + * To be called by sensor client driver. + * + * @param [in] dev The deserializer device handle. + * @param [in] g_ctx The @ref gmsl_link_ctx structure handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx); + +/** + * @brief Unpairs a sensor device from a serializer device. + * + * To be called by sensor client driver. + * + * @param [in] dev The serializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_sdev_unpair(struct device *dev, struct device *s_dev); + +/** + * Sets up the serializer device's internal pipeline for a specified + * sensor/serializer pair. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_setup_streaming(struct device *dev); + +int max9295_init_settings(struct device *dev); +/** @} */ + +#endif /* __MAX9295_H__ */ diff --git a/include/media/max9296.h b/include/media/max9296.h new file mode 100644 index 00000000..59b33cab --- /dev/null +++ b/include/media/max9296.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2018-2020, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * MAX9296 API: For Maxim Integrated MAX9296 deserializer + * + * @b Description: Defines elements used to set up and use a + * Maxim Integrated MAX9296 deserializer. + */ + +#ifndef __MAX9296_H__ +#define __MAX9296_H__ + +#include +#include +/** + * \defgroup max9296 MAX9296 deserializer driver + * + * Controls the MAX9296 deserializer module. + * + * @ingroup serdes_group + * @{ + */ +struct max9296_pdata { + bool splitter_enabled; + __u32 max_src; + __u32 csi_mode; +}; + +int max9296_get_available_pipe_id(struct device *dev, int vc_id); +int max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id); +int max9296_release_pipe(struct device *dev, int pipe_id); +void max9296_reset_oneshot(struct device *dev); + +/** + * Puts a deserializer device in single exclusive link mode, so link-specific + * I2C overrides can be performed for sensor and serializer devices. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_setup_link(struct device *dev, struct device *s_dev); + +/** + * @brief Sets up a deserializer link's control pipeline. + * + * Puts the deserializer in dual splitter mode. You must call this function + * during device boot, after max9296_setup_link(). + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_setup_control(struct device *dev, struct device *s_dev); + +/** + * @brief Resets a deserializer device's link control pipeline. + * + * The deserializer driver internally decrements the reference count and + * resets the deserializer device if all the source sensor devices are + * powered off, resetting all control and streaming configuration. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_reset_control(struct device *dev, struct device *s_dev); + +/** + * @brief Registers a source sensor device with a deserializer device. + * + * The deserializer driver internally checks all perquisites and compatibility + * factors. If it finds that the registration request is valid, + * it stores the source's @ref gmsl_link_ctx context handle in the source list + * maintained by the deserializer driver instance. + * + * @param [in] dev The deserializer device handle. + * @param [in] g_ctx A @c gmsl_link_ctx structure handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx); + +/** + * Unregisters a source sensor device from its deserializer device. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_sdev_unregister(struct device *dev, struct device *s_dev); + +/** + * Performs internal pipeline configuration for a link in context to set up + * streaming, and puts the deserializer link in ready-to-stream state. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 or success, or -1 otherwise. + */ +int max9296_setup_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Enables streaming. + * + * This function is to be called by the sensor client driver. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_start_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Disables streaming. + * + * This function is to be called by the sensor client driver. + * + * @note Both @c max9296_start_streaming and @c max9296_stop_streaming + * are mainly added to enable and disable sensor streaming on the fly + * while other sensors are active. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_stop_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Powers on the max9296 deserializer module. + * + * Asserts shared reset GPIO and powers on the regulator; + * maintains the reference count internally for source devices. + * + * @param [in] dev The deserializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_power_on(struct device *dev); + +/** + * @brief Powers off the max9296 deserializer module. + * + * Deasserts the shared reset GPIO and powers off the regulator based on + * the reference count. + * + * @param [in] dev The deserializer device handle. + */ +void max9296_power_off(struct device *dev); + +int max9296_init_settings(struct device *dev); +/** @} */ + +#endif /* __MAX9296_H__ */ From f81681c17da375afa2ea360ca5ae17bf0d1f1f30 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Thu, 21 Mar 2024 11:17:32 +0200 Subject: [PATCH 12/72] d4xx-compat: add kernel 6.5 support * d4xx: kernel6 #3 https://github.com/dmipx/ipu6-drivers/commit/15ddce4723625c26f1483fa2e523c2112ceaa63d * d4xx: kernel6 #2 https://github.com/dmipx/ipu6-drivers/commit/e113eca9460971353a7836b9041086642efe96fa * d4xx: kernel6 https://github.com/dmipx/ipu6-drivers/commit/772625227520ac8537edc8386ac4648084e88a3e Signed-off-by: Dmitry Perchanov --- drivers/media/i2c/d4xx-compat/d4xx.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 2088efe2..06ce1b1a 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -5220,7 +5220,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) dev_dbg(&c->dev, "%s(): : <%d, %d>\n", __func__, MAJOR(*dev_num), MINOR(*dev_num)); /* Create a class : appears at /sys/class */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) *ds5_class = class_create(THIS_MODULE, DS5_DRIVER_NAME_CLASS); #else *ds5_class = class_create(DS5_DRIVER_NAME_CLASS); @@ -5590,7 +5590,12 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) } #endif #endif //CONFIG_VIDEO_INTEL_IPU6 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) +#else +static int ds5_probe(struct i2c_client *c) +#endif { struct ds5 *state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); u16 rec_state; @@ -5605,8 +5610,11 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) state->client = c; dev_warn(&c->dev, "Probing driver for D45x\n"); - +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) state->variant = ds5_variants + id->driver_data; +#else + state->variant = ds5_variants; +#endif #ifdef CONFIG_OF state->vcc = devm_regulator_get(&c->dev, "vcc"); if (IS_ERR(state->vcc)) { From 5da15c71594718f5de45a4897ae3a77adc6d54db Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Thu, 21 Mar 2024 17:05:22 +0800 Subject: [PATCH 13/72] d4xx-compat: align to linux 6.6 v4l2 apis * Kernel 6.6.15 release for Ubuntu (#216) Signed-off-by: zouxiaoh Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 8 +++++++- drivers/media/i2c/d4xx-compat/max9295.c | 10 ++++++++++ drivers/media/i2c/d4xx-compat/max9296.c | 10 ++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 06ce1b1a..39d81f8b 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -5220,7 +5220,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) dev_dbg(&c->dev, "%s(): : <%d, %d>\n", __func__, MAJOR(*dev_num), MINOR(*dev_num)); /* Create a class : appears at /sys/class */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0) *ds5_class = class_create(THIS_MODULE, DS5_DRIVER_NAME_CLASS); #else *ds5_class = class_create(DS5_DRIVER_NAME_CLASS); @@ -5853,7 +5853,13 @@ static struct i2c_driver ds5_i2c_driver = { .owner = THIS_MODULE, .name = DS5_DRIVER_NAME }, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + .probe = ds5_probe, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ds5_probe, +#else .probe = ds5_probe, +#endif .remove = ds5_remove, .id_table = ds5_id, }; diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c index d4bea270..e0c06d1b 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -660,8 +660,12 @@ int max9295_set_pipe(struct device *dev, int pipe_id, } EXPORT_SYMBOL(max9295_set_pipe); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) static int max9295_probe(struct i2c_client *client, const struct i2c_device_id *id) +#else +static int max9295_probe(struct i2c_client *client) +#endif { struct max9295 *priv; struct max9295_pdata *pdata = client->dev.platform_data; @@ -749,7 +753,13 @@ static struct i2c_driver max9295_i2c_driver = { .name = "max9295", .owner = THIS_MODULE, }, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) .probe = max9295_probe, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = max9295_probe, +#else + .probe = max9295_probe, +#endif .remove = max9295_remove, .id_table = max9295_id, }; diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/max9296.c index 8bd4ac04..42807262 100644 --- a/drivers/media/i2c/d4xx-compat/max9296.c +++ b/drivers/media/i2c/d4xx-compat/max9296.c @@ -1082,8 +1082,12 @@ static struct regmap_config max9296_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) static int max9296_probe(struct i2c_client *client, const struct i2c_device_id *id) +#else +static int max9296_probe(struct i2c_client *client) +#endif { struct max9296 *priv; int err = 0; @@ -1161,7 +1165,13 @@ static struct i2c_driver max9296_i2c_driver = { .owner = THIS_MODULE, .of_match_table = of_match_ptr(max9296_of_match), }, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) .probe = max9296_probe, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = max9296_probe, +#else + .probe = max9296_probe, +#endif .remove = max9296_remove, .id_table = max9296_id, }; From d114309c25c1d9cb3120bae2b9d46ab2045cf80f Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 16 Jan 2024 06:32:20 -0700 Subject: [PATCH 14/72] d4xx-compat: force uapi isys headers include Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 39d81f8b..2d0ab292 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -31,7 +31,7 @@ #include #include #ifdef CONFIG_VIDEO_INTEL_IPU6 -#include +#include #include #endif #include From f96815eaa3fc5159304cfb0ab6c106fe585fe391 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 16 May 2024 06:03:35 -0700 Subject: [PATCH 15/72] d4xx-compat: turn .[gs]_frame_interval into pad operations ref-by: https://patches.linaro.org/project/linux-media/patch/20231213140450.21359-3-laurent.pinchart@ideasonboard.com/ The subdev .[gs]_frame_interval are video operations, but they operate on pads (and even on streams). Not only is this confusing, it causes practical issues for drivers as the operations don't receive a subdev state pointer, requiring manual state handling. To improve the situation, turn the operations into pad operations, and extend them to receive a state pointer like other pad operations. While at it, rename the operations to .[gs]et_frame_interval at the same time to match the naming scheme of other pad operations. This isn't strictly necessary, but given that all drivers using those operations need to be modified, handling the rename separately would generate more churn for very little gain (if at all). Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 73 ++++++++++++++++------------ 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 2d0ab292..aaa15e38 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1647,19 +1647,13 @@ static int ds5_configure(struct ds5 *state) return 0; } -static const struct v4l2_subdev_pad_ops ds5_depth_pad_ops = { - .enum_mbus_code = ds5_sensor_enum_mbus_code, - .enum_frame_size = ds5_sensor_enum_frame_size, - .enum_frame_interval = ds5_sensor_enum_frame_interval, - .get_fmt = ds5_sensor_get_fmt, - .set_fmt = ds5_sensor_set_fmt, -}; - static int ds5_sensor_g_frame_interval(struct v4l2_subdev *sd, -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +/* pad ops */ + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) #else - struct v4l2_subdev_state *sd_state, +/* Video ops */ struct v4l2_subdev_frame_interval *fi) #endif { @@ -1679,10 +1673,12 @@ static int ds5_sensor_g_frame_interval(struct v4l2_subdev *sd, static u16 __ds5_probe_framerate(const struct ds5_resolution *res, u16 target); static int ds5_sensor_s_frame_interval(struct v4l2_subdev *sd, -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +/* pad ops */ + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) #else - struct v4l2_subdev_state *sd_state, +/* Video ops */ struct v4l2_subdev_frame_interval *fi) #endif { @@ -1715,8 +1711,20 @@ static int ds5_sensor_s_stream(struct v4l2_subdev *sd, int on) return 0; } +static const struct v4l2_subdev_pad_ops ds5_depth_pad_ops = { + .enum_mbus_code = ds5_sensor_enum_mbus_code, + .enum_frame_size = ds5_sensor_enum_frame_size, + .enum_frame_interval = ds5_sensor_enum_frame_interval, + .get_fmt = ds5_sensor_get_fmt, + .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, +#endif +}; + static const struct v4l2_subdev_video_ops ds5_sensor_video_ops = { -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) .g_frame_interval = ds5_sensor_g_frame_interval, .s_frame_interval = ds5_sensor_s_frame_interval, #endif @@ -1737,9 +1745,9 @@ static const struct v4l2_subdev_pad_ops ds5_ir_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) - .get_frame_interval = ds5_sensor_g_frame_interval, - .set_frame_interval = ds5_sensor_s_frame_interval, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, #endif }; @@ -1755,9 +1763,9 @@ static const struct v4l2_subdev_pad_ops ds5_rgb_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) - .get_frame_interval = ds5_sensor_g_frame_interval, - .set_frame_interval = ds5_sensor_s_frame_interval, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, #endif }; @@ -1772,9 +1780,9 @@ static const struct v4l2_subdev_pad_ops ds5_imu_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) - .get_frame_interval = ds5_sensor_g_frame_interval, - .set_frame_interval = ds5_sensor_s_frame_interval, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + .get_frame_interval = ds5_sensor_g_frame_interval, + .set_frame_interval = ds5_sensor_s_frame_interval, #endif }; @@ -4102,12 +4110,13 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, return ret; } -/* Video ops */ static int ds5_mux_g_frame_interval(struct v4l2_subdev *sd, -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +/* pad ops */ + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) #else - struct v4l2_subdev_state *sd_state, +/* Video ops */ struct v4l2_subdev_frame_interval *fi) #endif { @@ -4143,10 +4152,12 @@ static u16 __ds5_probe_framerate(const struct ds5_resolution *res, u16 target) } static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +/* pad ops */ + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) #else - struct v4l2_subdev_state *sd_state, +/* Video ops */ struct v4l2_subdev_frame_interval *fi) #endif { @@ -4404,9 +4415,9 @@ static const struct v4l2_subdev_pad_ops ds5_mux_pad_ops = { .get_fmt = ds5_mux_get_fmt, .set_fmt = ds5_mux_set_fmt, .get_frame_desc = ds5_mux_get_frame_desc, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) - .get_frame_interval = ds5_mux_g_frame_interval, - .set_frame_interval = ds5_mux_s_frame_interval, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + .get_frame_interval = ds5_mux_g_frame_interval, + .set_frame_interval = ds5_mux_s_frame_interval, #endif }; @@ -4416,7 +4427,7 @@ static const struct v4l2_subdev_core_ops ds5_mux_core_ops = { }; static const struct v4l2_subdev_video_ops ds5_mux_video_ops = { -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) .g_frame_interval = ds5_mux_g_frame_interval, .s_frame_interval = ds5_mux_s_frame_interval, #endif From 985d654363afb6793fb56ae7050bdab8378ef135 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 30 May 2024 01:14:13 -0700 Subject: [PATCH 16/72] d4xx-compat: debug isys-video kernel oops on per-vc callbacks Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 61 +++++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index aaa15e38..12dfeb49 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -2677,7 +2677,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(sensor->sd.dev, "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d" - "vc:[%d] %d\n", + ", vc:[%d] %d\n", __func__, sensor->mux_pad, vc_id, substream); *ctrl->p_new.p_s32 = substream; state->mux.last_set = sensor; @@ -2685,6 +2685,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) /* we are in DS5 MUX case */ *ctrl->p_new.p_s32 = -1; } + } break; #endif @@ -3651,32 +3652,47 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) switch (sid) { case DEPTH_SID: state->depth.sensor.sd.ctrl_handler = hdl; - dev_dbg(state->depth.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->depth.sensor.mux_pad); + dev_info(state->depth.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d, %p, %s\n", + __func__, __LINE__, + state->depth.sensor.mux_pad, + state->depth.sensor.sd.ctrl_handler, + state->depth.sensor.sd.name); break; case RGB_SID: state->rgb.sensor.sd.ctrl_handler = hdl; - dev_dbg(state->rgb.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->rgb.sensor.mux_pad); + dev_info(state->rgb.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d, %p, %s\n", + __func__, __LINE__, + state->rgb.sensor.mux_pad, + state->rgb.sensor.sd.ctrl_handler, + state->rgb.sensor.sd.name); break; case IR_SID: state->ir.sensor.sd.ctrl_handler = hdl; - dev_dbg(state->ir.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->ir.sensor.mux_pad); + dev_info(state->ir.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d, %p, %s\n", + __func__, __LINE__, + state->ir.sensor.mux_pad, + state->ir.sensor.sd.ctrl_handler, + state->ir.sensor.sd.name); break; case IMU_SID: state->imu.sensor.sd.ctrl_handler = hdl; - dev_dbg(state->imu.sensor.sd.dev, - "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->imu.sensor.mux_pad); + dev_info(state->imu.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d, %p, %s\n", + __func__, __LINE__, + state->imu.sensor.mux_pad, + state->imu.sensor.sd.ctrl_handler, + state->imu.sensor.sd.name); break; default: state->mux.sd.subdev.ctrl_handler = hdl; - dev_dbg(state->mux.sd.subdev.dev, - "%s():%d set ctrl_handler for MUX\n", __func__, __LINE__); + dev_info(state->mux.sd.subdev.dev, + "%s():%d set ctrl_handler for MUX: %p, %s\n", + __func__, __LINE__, + state->mux.sd.subdev.ctrl_handler, + state->mux.sd.subdev.name); break; } @@ -3719,6 +3735,14 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, pad->flags = MEDIA_PAD_FL_SOURCE; entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; + + dev_info(&c->dev, + "%s():%d init media_entity %s, type:%x, func:%x\n", + __func__, __LINE__, + sd->name, + entity->obj_type, + entity->function); + return media_entity_pads_init(entity, 1, pad); } @@ -4562,6 +4586,13 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; + dev_info(&c->dev, + "%s():%d init media_entity %s, type:%x, func:%x\n", + __func__, __LINE__, + sd->name, + entity->obj_type, + entity->function); + pads[0].flags = MEDIA_PAD_FL_SOURCE; for (i = 1, pad = pads + 1; i < ARRAY_SIZE(state->mux.pads); i++, pad++) pad->flags = MEDIA_PAD_FL_SINK; From 9e0bfa6fae06aa3132eed272f201a588dc57482c Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Wed, 12 Jun 2024 12:39:18 +0200 Subject: [PATCH 17/72] d4xx-compat: add V4L2_CID_LINK_FREQ support MTL DPHY * enable `v4l2-ctl --set-ctrl v4l2_cid_link_freq=` runtime override * add D4XX_LINK_FREQ_750MHZ to MTL CSI D-PHY * add MTL CSI2 D-PHY debug messages Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 12dfeb49..d1ef3fbc 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -211,6 +211,7 @@ enum ds5_mux_pad { if (max9295_write_8(state, addr, buf)) \ return -EINVAL; \ } +#define D4XX_LINK_FREQ_750MHZ 750000000ULL #define D4XX_LINK_FREQ_360MHZ 360000000ULL #define D4XX_LINK_FREQ_300MHZ 300000000ULL #define D4XX_LINK_FREQ_288MHZ 288000000ULL @@ -326,6 +327,7 @@ static const struct hwm_cmd ewb = { }; #ifdef CONFIG_VIDEO_INTEL_IPU6 static const s64 link_freq_menu_items[] = { + D4XX_LINK_FREQ_750MHZ, D4XX_LINK_FREQ_360MHZ, D4XX_LINK_FREQ_300MHZ, D4XX_LINK_FREQ_288MHZ, @@ -2347,6 +2349,20 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) #endif } break; + case V4L2_CID_LINK_FREQ: { + if ( sensor && ctrl->p_new.p_u8) + { + if (*ctrl->p_new.p_u8 <= (ARRAY_SIZE(link_freq_menu_items) - 1)) { + struct v4l2_ctrl *link_freq = state->ctrls.link_freq; + dev_info(&state->client->dev, + "V4L2_CID_LINK_FREQ modify index to val=%d", + (unsigned int) *ctrl->p_new.p_u8); + ret = 0; + } + } + + } + break; #endif } @@ -2923,7 +2939,7 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .max = ARRAY_SIZE(link_freq_menu_items) - 1, .min = 0, .step = 0, - .def = 1, + .def = 2, // default D4XX_LINK_FREQ_300MHZ .qmenu_int = link_freq_menu_items, }; @@ -3594,10 +3610,13 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) } #ifdef CONFIG_VIDEO_INTEL_IPU6 ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); - + /* MTL and RPL/ADL IPU6 CSI-DPHY do NOT share + * the same default link_freq. + * V4L2_CID_LINK_FREQ must be R/W for udev ot set DPHY platform specific link_freq + * at systemd boottime. if (ctrls->link_freq) ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - + */ if (state->aggregated) { d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; From 59e6faa4f600be7e8b9600d61821ba760c9bda53 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Sat, 15 Jun 2024 02:29:27 +0800 Subject: [PATCH 18/72] d4xx-compat: ignore DS5_CAMERA_CID_HWMC_RW empty data ioctl Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index d1ef3fbc..7a1c06ab 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1,3 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 /* * ds5.c - Intel(R) RealSense(TM) D4XX camera driver @@ -2670,6 +2671,15 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) u16 dataLen = 0; u16 bufLen = ctrl->dims[0]; ret = ds5_get_hwmc(state, data, bufLen, &dataLen); + /* ignore empty data calls */ + if (!dataLen) { + dev_dbg(sensor->sd.dev, + "%s(): DS5_CAMERA_CID_HWMC_RW empty call " + " Len: %d (ret: %x)\n", + __func__, dataLen, ret); + ret = 0; + break; + } if (ret) break; /* This is needed for librealsense, to align there code with UVC, From 56212db8cbff21abe89d49ce23824cc14b3de633 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Tue, 18 Jun 2024 20:22:03 +0800 Subject: [PATCH 19/72] d4xx-compat: fix aggredated mode max9296 device removal * d4xx : kernel NULL pointer dereference [ 1950.081452] #PF: supervisor write access in kernel mode [ 1950.086655] #PF: error_code(0x0002) - not-present page [ 1950.216966] Call Trace: [ 1950.219401] [ 1950.221504] ? show_regs+0x6d/0x80 [ 1950.224902] ? __die+0x24/0x80 [ 1950.227952] ? page_fault_oops+0x99/0x1b0 [ 1950.231948] ? do_user_addr_fault+0x31d/0x6b0 [ 1950.236283] ? exc_page_fault+0x83/0x1b0 [ 1950.240190] ? asm_exc_page_fault+0x27/0x30 [ 1950.244358] ? mutex_lock+0x1d/0x50 [ 1950.247827] ? mutex_lock+0x12/0x50 [ 1950.251301] max9296_reset_control+0x26/0xf0 [max9296] [ 1950.256425] ds5_remove+0xba/0x2e0 [d4xx] [ 1950.260423] i2c_device_remove+0x25/0xc0 [ 1950.264338] device_remove+0x40/0x80 [ 1950.267899] device_release_driver_internal+0x20b/0x270 [ 1950.273096] driver_detach+0x4a/0xa0 [ 1950.276655] bus_remove_driver+0x83/0x110 [ 1950.280641] driver_unregister+0x2f/0x60 [ 1950.284542] i2c_del_driver+0x46/0x90 [ 1950.288191] ds5_i2c_driver_exit+0x10/0x18 [d4xx] Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 7a1c06ab..d5bc3097 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -5846,6 +5846,7 @@ static void ds5_remove(struct i2c_client *c) #ifdef CONFIG_VIDEO_D4XX_SERDES int i, ret; + int c_bus = c->adapter->nr; for (i = 0; i < MAX_DEV_NUM; i++) { if (serdes_inited[i] && serdes_inited[i] == state) { serdes_inited[i] = NULL; @@ -5855,22 +5856,31 @@ static void ds5_remove(struct i2c_client *c) if (ret) dev_warn(&c->dev, "failed in 9295 reset control\n"); - ret = max9296_reset_control(state->dser_dev, - state->g_ctx.s_dev); - if (ret) - dev_warn(&c->dev, - "failed in 9296 reset control\n"); - + if (state->dser_i2c) { + dev_info(&c->dev, "ignore 9296 reset control, already remove for bus %d\n", c_bus); + } else { + ret = max9296_reset_control(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9296 reset control\n"); + } ret = max9295_sdev_unpair(state->ser_dev, state->g_ctx.s_dev); if (ret) dev_warn(&c->dev, "failed to unpair sdev\n"); - ret = max9296_sdev_unregister(state->dser_dev, - state->g_ctx.s_dev); - if (ret) - dev_warn(&c->dev, - "failed to sdev unregister sdev\n"); - max9296_power_off(state->dser_dev); + + if (state->dser_i2c) { + dev_info(&c->dev, "ignore 9296 unregister sdev, already remove for bus %d\n", c_bus); + } else { + ret = max9296_sdev_unregister(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed to sdev unregister sdev\n"); + + max9296_power_off(state->dser_dev); + } mutex_unlock(&serdes_lock__); break; @@ -5878,7 +5888,7 @@ static void ds5_remove(struct i2c_client *c) } if (state->ser_i2c) i2c_unregister_device(state->ser_i2c); - if (state->dser_i2c) + if (state->dser_i2c && !state->aggregated) i2c_unregister_device(state->dser_i2c); #endif #ifndef CONFIG_TEGRA_CAMERA_PLATFORM From 34107126efc534c5099488c43db5cc73f5963c32 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Wed, 19 Jun 2024 21:45:06 +0800 Subject: [PATCH 20/72] d4xx-compat: add csi2 4 and 5 ports mapping, shift aggregated suffixes Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index d5bc3097..6a1b04db 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3243,7 +3243,7 @@ static int ds5_board_setup(struct ds5 *state) } } if (state->aggregated) - suffix += 4; + suffix += 6; dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", suffix, bus, pdata->subdev_info[0].board_info.addr, //48 @@ -3752,7 +3752,7 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, * TODO: suffix for 2 D457 connected to 1 Deser */ if (state->aggregated & 1) - suffix += 4; + suffix += 6; snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, suffix); #else snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", @@ -4608,7 +4608,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) i2c_adapter_id(c->adapter), c->addr); #else if (state->aggregated) - suffix += 4; + suffix += 6; snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); #endif sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -5313,7 +5313,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) /* Create a device node for this device. */ #ifndef CONFIG_OF if (state->aggregated) - suffix += 4; + suffix += 6; snprintf(dev_name, sizeof(dev_name), "%s-%c", DS5_DRIVER_NAME_DFU, suffix); #else From eef9a6d1b8c6f2daaa555f506864021ba4dc8691 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Wed, 26 Jun 2024 18:18:08 +0800 Subject: [PATCH 21/72] d4xx-compat: fix query sensor info failed on port e Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 6a1b04db..f796aaa3 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3631,6 +3631,10 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; d4xx_controls_q_sub_stream.max = NR_OF_DS5_SUB_STREAMS * 2 - 1; + } else { + d4xx_controls_q_sub_stream.def = 0; + d4xx_controls_q_sub_stream.min = 0; + d4xx_controls_q_sub_stream.max = NR_OF_DS5_SUB_STREAMS - 1; } ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream, sensor); @@ -3718,10 +3722,11 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) default: state->mux.sd.subdev.ctrl_handler = hdl; dev_info(state->mux.sd.subdev.dev, - "%s():%d set ctrl_handler for MUX: %p, %s\n", + "%s():%d set ctrl_handler for MUX: %p, %s, substream id %d \n", __func__, __LINE__, state->mux.sd.subdev.ctrl_handler, - state->mux.sd.subdev.name); + state->mux.sd.subdev.name, + d4xx_controls_q_sub_stream.def); break; } @@ -5384,6 +5389,11 @@ static void ds5_substream_init(struct ds5 *state) state->pad_to_substream[DS5_MUX_PAD_IR] = 10; state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; } + dev_info(&state->client->dev, "%s() IPU6 CSI2 BE SOC video capture init : \n", __func__); + for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) + if (state->pad_to_substream[i] >= 0) + dev_info(&state->client->dev, "pad[%d]->substream=%d\n", + i, state->pad_to_substream[i]); /* * 0, vc 0, depth * 1, vc 0, meta data From 88a58c08885e81250f7c3f07368af4a867f2a21c Mon Sep 17 00:00:00 2001 From: florent pirou Date: Mon, 15 Jul 2024 19:15:19 +0800 Subject: [PATCH 22/72] d4xx-compat: apply v4l2-ctl csi2 link_freq override to mux only Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 34 +++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index f796aaa3..0c091ccc 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -212,7 +212,12 @@ enum ds5_mux_pad { if (max9295_write_8(state, addr, buf)) \ return -EINVAL; \ } -#define D4XX_LINK_FREQ_750MHZ 750000000ULL +#define D4XX_LINK_FREQ_750MHZ 750000000ULL +#define D4XX_LINK_FREQ_720MHZ 720000000ULL +#define D4XX_LINK_FREQ_600MHZ 600000000ULL +#define D4XX_LINK_FREQ_576MHZ 576000000ULL +#define D4XX_LINK_FREQ_480MHZ 480000000ULL +#define D4XX_LINK_FREQ_450MHZ 450000000ULL #define D4XX_LINK_FREQ_360MHZ 360000000ULL #define D4XX_LINK_FREQ_300MHZ 300000000ULL #define D4XX_LINK_FREQ_288MHZ 288000000ULL @@ -329,6 +334,11 @@ static const struct hwm_cmd ewb = { #ifdef CONFIG_VIDEO_INTEL_IPU6 static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_750MHZ, + D4XX_LINK_FREQ_720MHZ, + D4XX_LINK_FREQ_600MHZ, + D4XX_LINK_FREQ_576MHZ, + D4XX_LINK_FREQ_480MHZ, + D4XX_LINK_FREQ_450MHZ, D4XX_LINK_FREQ_360MHZ, D4XX_LINK_FREQ_300MHZ, D4XX_LINK_FREQ_288MHZ, @@ -2351,13 +2361,21 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) } break; case V4L2_CID_LINK_FREQ: { - if ( sensor && ctrl->p_new.p_u8) + if ( !sensor && ctrl->p_new.p_u8) { + /* MTL and RPL/ADL IPU6 CSI-DPHY do NOT share + * the same default link_freq. + * V4L2_CID_LINK_FREQ DS5 mux must be R/W for udev ot set DPHY platform specific link_freq + * via systemd-udevd rules. + */ if (*ctrl->p_new.p_u8 <= (ARRAY_SIZE(link_freq_menu_items) - 1)) { struct v4l2_ctrl *link_freq = state->ctrls.link_freq; dev_info(&state->client->dev, - "V4L2_CID_LINK_FREQ modify index to val=%d", + "user-modified %s index val=%d to user-val=%d", + ctrl->name, + (unsigned int) link_freq->val, (unsigned int) *ctrl->p_new.p_u8); + link_freq->val = (s32) *ctrl->p_new.p_u8; ret = 0; } } @@ -2949,7 +2967,7 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .max = ARRAY_SIZE(link_freq_menu_items) - 1, .min = 0, .step = 0, - .def = 2, // default D4XX_LINK_FREQ_300MHZ + .def = 7, // default D4XX_LINK_FREQ_300MHZ .qmenu_int = link_freq_menu_items, }; @@ -3622,11 +3640,11 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); /* MTL and RPL/ADL IPU6 CSI-DPHY do NOT share * the same default link_freq. - * V4L2_CID_LINK_FREQ must be R/W for udev ot set DPHY platform specific link_freq - * at systemd boottime. - if (ctrls->link_freq) - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + * V4L2_CID_LINK_FREQ DS5 mux must be R/W for udev ot set DPHY platform specific link_freq + * via systemd-udevd rules. */ + if (sensor && ctrls->link_freq ) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (state->aggregated) { d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; From 14265f6d80be9936c71240a027834248add4c85c Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Mon, 30 Sep 2024 17:43:17 +0200 Subject: [PATCH 23/72] d4xx-compat: avoid G_FMT error on missing Y210 Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 0c091ccc..7cfbcdd8 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1340,8 +1340,10 @@ static unsigned int mbus_code_to_mipi(u32 code) return MIPI_CSI2_TYPE_RGB565; case MEDIA_BUS_FMT_RGB888_1X24: return MIPI_CSI2_TYPE_RGB888; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) case MEDIA_BUS_FMT_YUYV10_1X20: return MIPI_CSI2_TYPE_YUV422_10; +#endif case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_VYUY8_1X16: From bc7e8c3115c930803eed428f38886b5f43e68db7 Mon Sep 17 00:00:00 2001 From: Josh Watts Date: Fri, 11 Oct 2024 11:50:54 -0400 Subject: [PATCH 24/72] d4xx-compat: Enable hacks in max929x --- drivers/media/i2c/d4xx-compat/d4xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 7cfbcdd8..8f1f0364 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3230,10 +3230,12 @@ static int ds5_board_setup(struct ds5 *state) static struct max9295_pdata max9295_pdata = { .is_prim_ser = 1, // todo: configurable .def_addr = 0x40, // todo: configurable + .d4xx_hacks = 1, }; static struct max9296_pdata max9296_pdata = { .max_src = 2, .csi_mode = GMSL_CSI_2X4_MODE, + .d4xx_hacks = 1, }; static struct i2c_board_info i2c_info_des = { I2C_BOARD_INFO("max9296", 0x48), From 3587f9067a606cc5b6b1415a84b231e143c38abf Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Thu, 12 Dec 2024 07:57:55 +0000 Subject: [PATCH 25/72] d4xx-compat: crankup V4L2_CID_LINK_FREQ setpoints up 2500Mbps Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 8f1f0364..6920579d 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -212,6 +212,11 @@ enum ds5_mux_pad { if (max9295_write_8(state, addr, buf)) \ return -EINVAL; \ } +#define D4XX_LINK_FREQ_1250MHZ 1250000000ULL +#define D4XX_LINK_FREQ_1125MHZ 1125000000ULL +#define D4XX_LINK_FREQ_1000MHZ 1000000000ULL +#define D4XX_LINK_FREQ_900MHZ 900000000ULL +#define D4XX_LINK_FREQ_840MHZ 840000000ULL #define D4XX_LINK_FREQ_750MHZ 750000000ULL #define D4XX_LINK_FREQ_720MHZ 720000000ULL #define D4XX_LINK_FREQ_600MHZ 600000000ULL @@ -333,6 +338,11 @@ static const struct hwm_cmd ewb = { }; #ifdef CONFIG_VIDEO_INTEL_IPU6 static const s64 link_freq_menu_items[] = { + D4XX_LINK_FREQ_1250MHZ, + D4XX_LINK_FREQ_1125MHZ, + D4XX_LINK_FREQ_1000MHZ, + D4XX_LINK_FREQ_900MHZ, + D4XX_LINK_FREQ_840MHZ, D4XX_LINK_FREQ_750MHZ, D4XX_LINK_FREQ_720MHZ, D4XX_LINK_FREQ_600MHZ, @@ -2969,7 +2979,7 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .max = ARRAY_SIZE(link_freq_menu_items) - 1, .min = 0, .step = 0, - .def = 7, // default D4XX_LINK_FREQ_300MHZ + .def = 12, // default D4XX_LINK_FREQ_300MHZ .qmenu_int = link_freq_menu_items, }; From 9e5b5a395d5c47e0e21cfb1cbb78cf2eb2f0c0c4 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 18 Apr 2025 00:57:57 +0800 Subject: [PATCH 26/72] d4xx-compat: serdes setup fallback on invalid serializer source Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 167 ++++++++++++++++++++++----- 1 file changed, 141 insertions(+), 26 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 6920579d..050e1e1e 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1,4 +1,3 @@ - // SPDX-License-Identifier: GPL-2.0 /* * ds5.c - Intel(R) RealSense(TM) D4XX camera driver @@ -470,6 +469,14 @@ enum { struct v4l2_mbus_framefmt ds5_ffmts[NR_OF_DS5_PADS]; #endif +#ifdef CONFIG_VIDEO_D4XX_SERDES +struct serdes_state { + bool isolated; + int bus_nr; + int addr; +}; +#endif + struct ds5 { struct { struct ds5_sensor sensor; } depth; struct { struct ds5_sensor sensor; } ir; @@ -504,6 +511,7 @@ struct ds5 { struct device *dser_dev; struct i2c_client *ser_i2c; struct i2c_client *dser_i2c; + struct serdes_state dser_st; #endif #ifdef CONFIG_VIDEO_INTEL_IPU6 #define NR_OF_CSI2_BE_SOC_STREAMS 16 @@ -3237,6 +3245,7 @@ static int ds5_board_setup(struct ds5 *state) int err = 0; int i; char suffix = pdata->suffix; + static struct max9295_pdata max9295_pdata = { .is_prim_ser = 1, // todo: configurable .def_addr = 0x40, // todo: configurable @@ -3261,16 +3270,27 @@ static int ds5_board_setup(struct ds5 *state) i2c_info_des.addr = pdata->subdev_info[0].board_info.addr; //0x48, 0x4a, 0x68, 0x6a + state->dser_st.bus_nr = bus; + state->dser_st.addr = i2c_info_des.addr; + /* look for already registered max9296, use same context if found */ for (i = 0; i < MAX_DEV_NUM; i++) { - if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { - dev_info(dev, "MAX9296 found device on %d@0x%x\n", - serdes_inited[i]->dser_i2c->adapter->nr, serdes_inited[i]->dser_i2c->addr); - if (bus == serdes_inited[i]->dser_i2c->adapter->nr - && serdes_inited[i]->dser_i2c->addr == i2c_info_des.addr) { + if (serdes_inited[i]) { + if ( serdes_inited[i]->dser_st.isolated + && bus == serdes_inited[i]->dser_st.bus_nr + && serdes_inited[i]->dser_st.addr == i2c_info_des.addr ) { + + dev_info(dev, "Isolate unresponsive sensor/serializer AGGREGATED on MAX9296 device 0x%x\n", + i2c_info_des.addr); + state->aggregated = 1; + break; + } else if ( serdes_inited[i]->dser_i2c + && bus == serdes_inited[i]->dser_i2c->adapter->nr + && serdes_inited[i]->dser_i2c->addr == i2c_info_des.addr) { dev_info(dev, "MAX9296 AGGREGATION found device on 0x%x\n", i2c_info_des.addr); state->dser_i2c = serdes_inited[i]->dser_i2c; state->aggregated = 1; + break; } } } @@ -3383,6 +3403,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) { int err = 0; int des_err = 0; + int ser_err = 0; struct device *dev; if (!state || !state->ser_dev || !state->dser_dev || !state->client) @@ -3404,17 +3425,19 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) goto error; } msleep(100); - err = max9295_setup_control(state->ser_dev); - /* proceed even if ser setup failed, to setup deser correctly */ - if (err) - dev_err(dev, "gmsl serializer setup failed\n"); + ser_err = max9295_setup_control(state->ser_dev); + /* if ser setup failed, graceful deser setup fallback */ + if (ser_err) { + dev_warn(dev, "gmsl serializer invalid source\n"); + err= -ENOTSUPP; + } des_err = max9296_setup_control(state->dser_dev, &state->client->dev); if (des_err) { - dev_err(dev, "gmsl deserializer setup failed\n"); + dev_warn(dev, "gmsl deserializer setup failed\n"); /* overwrite err only if deser setup also failed */ - err = des_err; + err = ( err == -ENOTSUPP) ? err : des_err; } error: @@ -3459,7 +3482,7 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) c->addr = des_addr[i]; dev_info(&c->dev, "Set max9296@%d-0x%x Link reset\n", c_bus, c->addr); - ds5_write_8(state, 0x1000, 0x40); // reset link + ds5_write_8(state, 0x1000, 0x40); // sensor reset link } } // restore original slave address @@ -3473,15 +3496,26 @@ static int ds5_serdes_setup(struct ds5 *state) int ret = 0; struct i2c_client *c = state->client; #ifdef CONFIG_VIDEO_INTEL_IPU6 - int i = 0, c_bus = 0; + int i = 0, c_bus = -1; int c_bus_new = c->adapter->nr; for (i = 0; i < MAX_DEV_NUM; i++) { +#ifdef CONFIG_VIDEO_D4XX_SERDES + if (serdes_inited[i] && serdes_inited[i]->dser_st.isolated) { + c_bus = serdes_inited[i]->dser_st.bus_nr; + if (c_bus == c->adapter->nr) { + dev_info(&c->dev, "Already configured Isolated camera for bus %d\n", c_bus); + c_bus_new = -1; + break; + } + } else if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { +#else if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { +#endif c_bus = serdes_inited[i]->dser_i2c->adapter->nr; if (c_bus == c->adapter->nr) { - dev_info(&c->dev, "Already configured multiple camera for bus %d\n", c_bus); - c_bus_new = 0; + dev_info(&c->dev, "Already configured Addressable camera for bus %d\n", c_bus); + c_bus_new = -1; break; } } else { @@ -3489,7 +3523,7 @@ static int ds5_serdes_setup(struct ds5 *state) } } - if (c_bus_new) { + if (c_bus_new >= 0) { dev_info(&c->dev, "Apply multiple camera i2c addr setting for bus %d\n", c_bus_new); ret = ds5_i2c_addr_setting(c, state); if (ret) { @@ -3509,7 +3543,7 @@ static int ds5_serdes_setup(struct ds5 *state) /* Pair sensor to serializer dev */ ret = max9295_sdev_pair(state->ser_dev, &state->g_ctx); if (ret) { - dev_err(&c->dev, "gmsl ser pairing failed\n"); + dev_err(&c->dev, "gmsl serializer pairing failed\n"); return ret; } @@ -3522,7 +3556,16 @@ static int ds5_serdes_setup(struct ds5 *state) ret = ds5_gmsl_serdes_setup(state); if (ret) { - dev_err(&c->dev, "%s gmsl serdes setup failed\n", __func__); + if (ret == -ENOTSUPP) { + dev_warn(&c->dev, "graceful fallback, gmsl serdes setup\n"); + if (c_bus_new >= 0) + dev_info(&c->dev, "Unresponding serializer on Newly initialized bus %d\n", + c_bus_new); + else + dev_info(&c->dev, "Unresponding serializer on Already initialized bus %d\n", + state->dser_i2c->adapter->nr); + } else + dev_err(&c->dev, "%s gmsl serdes setup failed\n", __func__); return ret; } @@ -5722,6 +5765,10 @@ static int ds5_probe(struct i2c_client *c) mutex_init(&state->lock); state->client = c; + +#ifdef CONFIG_VIDEO_D4XX_SERDES + state->dser_st.isolated = false; +#endif dev_warn(&c->dev, "Probing driver for D45x\n"); #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) state->variant = ds5_variants + id->driver_data; @@ -5753,8 +5800,12 @@ static int ds5_probe(struct i2c_client *c) #ifdef CONFIG_VIDEO_D4XX_SERDES ret = ds5_serdes_setup(state); - if (ret < 0) + if (ret < 0) { + if (ret == -ENOTSUPP) + dev_warn(&c->dev, "max9295 communication failed : %d\n", ret); goto e_regulator; + + } #endif #ifdef CONFIG_VIDEO_INTEL_IPU6 #ifndef CONFIG_VIDEO_D4XX_SERDES @@ -5867,14 +5918,54 @@ static int ds5_probe(struct i2c_client *c) e_chardev: if (state->dfu_dev.ds5_class) ds5_chrdev_remove(state); + e_regulator: if (state->vcc) regulator_disable(state->vcc); #ifdef CONFIG_VIDEO_D4XX_SERDES - if (state->ser_i2c) + int i; + int c_bus = c->adapter->nr; + bool graceful_fallback = false; + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] + && serdes_inited[i] != state + && state->dser_i2c + && c_bus == serdes_inited[i]->dser_st.bus_nr + && state->dser_i2c->addr == serdes_inited[i]->dser_st.addr + && serdes_inited[i]->dser_st.isolated) { + + dev_info(&c->dev, "Cleanup unresponsive sensor/serializer Isolated on bus %d\n", + c_bus); + graceful_fallback = true; + } + } + + if (!state->g_ctx.serdev_found) + dev_warn(&c->dev, "graceful fallback due to unresponsive max9295, isolated SerDes %s single-link\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B"); + else + dev_warn(&c->dev, "graceful fallback due to unresponsive d4xx, isolated SerDes %s single-link\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B"); + + mutex_lock(&serdes_lock__); + if (state->ser_i2c) { + dev_info(&c->dev, "remove unresponding serializer i2c device 0x%x\n", + state->ser_i2c->addr); i2c_unregister_device(state->ser_i2c); - if (state->dser_i2c && !state->aggregated) + } + if (state->dser_i2c && !state->aggregated) { + dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + state->dser_i2c->addr); i2c_unregister_device(state->dser_i2c); + state->dser_st.isolated = true; + } else if (state->dser_i2c && graceful_fallback) { + dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + state->dser_i2c->addr); + i2c_unregister_device(state->dser_i2c); + } + mutex_unlock(&serdes_lock__); #endif return ret; } @@ -5889,7 +5980,17 @@ static void ds5_remove(struct i2c_client *c) #ifdef CONFIG_VIDEO_D4XX_SERDES int i, ret; int c_bus = c->adapter->nr; + bool graceful_fallback = false; for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && state->dser_i2c + && c_bus == serdes_inited[i]->dser_st.bus_nr + && state->dser_i2c->addr == serdes_inited[i]->dser_st.addr + && serdes_inited[i]->dser_st.isolated) { + + dev_info(&c->dev, "Cleanup unresponsive sensor/serializer Isolated on bus %d\n", + c_bus); + graceful_fallback = true; + } if (serdes_inited[i] && serdes_inited[i] == state) { serdes_inited[i] = NULL; mutex_lock(&serdes_lock__); @@ -5901,6 +6002,7 @@ static void ds5_remove(struct i2c_client *c) if (state->dser_i2c) { dev_info(&c->dev, "ignore 9296 reset control, already remove for bus %d\n", c_bus); } else { + dev_info(&c->dev, "trigger 9296 reset control on bus %d\n", c_bus); ret = max9296_reset_control(state->dser_dev, state->g_ctx.s_dev); if (ret) @@ -5915,6 +6017,7 @@ static void ds5_remove(struct i2c_client *c) if (state->dser_i2c) { dev_info(&c->dev, "ignore 9296 unregister sdev, already remove for bus %d\n", c_bus); } else { + dev_info(&c->dev, "unregister 9296 sdev on bus %d\n", c_bus); ret = max9296_sdev_unregister(state->dser_dev, state->g_ctx.s_dev); if (ret) @@ -5923,15 +6026,26 @@ static void ds5_remove(struct i2c_client *c) max9296_power_off(state->dser_dev); } - mutex_unlock(&serdes_lock__); break; } } - if (state->ser_i2c) + if (state->ser_i2c && !state->dser_st.isolated) { + dev_info(&c->dev, "remove unresponding serializer i2c device 0x%x\n", + state->ser_i2c->addr); i2c_unregister_device(state->ser_i2c); - if (state->dser_i2c && !state->aggregated) + } + if (state->dser_i2c && !state->aggregated && !state->dser_st.isolated) { i2c_unregister_device(state->dser_i2c); + dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + state->dser_i2c->addr); + } else if (state->dser_i2c && graceful_fallback) { + dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", + state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + state->dser_i2c->addr); + i2c_unregister_device(state->dser_i2c); + } #endif #ifndef CONFIG_TEGRA_CAMERA_PLATFORM state->is_depth = 1; @@ -5997,7 +6111,8 @@ MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Xin Zhang ,\n\ Qingwu Zhang ,\n\ Evgeni Raikhel ,\n\ - Shikun Ding "); + Shikun Ding ,\n\ + Florent Pirou "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); MODULE_VERSION("1.0.2.20"); From 8308aec1f69b57cfb50d671c2a859277f6376059 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 23 May 2025 10:13:04 +0800 Subject: [PATCH 27/72] d4xx-compat: add acpi pdata aggregated-link detection Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 14 +++++++++++++- include/media/i2c/d4xx_pdata.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 050e1e1e..e5870214 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3245,6 +3245,7 @@ static int ds5_board_setup(struct ds5 *state) int err = 0; int i; char suffix = pdata->suffix; + char serdes_suffix[5]; /* suffix string for subdevs */ static struct max9295_pdata max9295_pdata = { .is_prim_ser = 1, // todo: configurable @@ -3291,10 +3292,21 @@ static int ds5_board_setup(struct ds5 *state) state->dser_i2c = serdes_inited[i]->dser_i2c; state->aggregated = 1; break; + } else if ( pdata->subdev_info[0].aggregated_link ) { + dev_info(dev, "PDATA sensor/serializer AGGREGATED on MAX9296 device 0x%x GMSL port %s\n", + i2c_info_des.addr, + pdata->subdev_info[0].suffix); + state->aggregated = 1; } } } - if (state->aggregated) + if ( pdata->subdev_info[0].aggregated_link ) { + snprintf(serdes_suffix, sizeof(serdes_suffix), "%s", pdata->subdev_info[0].suffix); + dev_info(dev, "PDATA GMSL port suffix %s\n", + i2c_info_des.addr, + serdes_suffix); + suffix = (const char) serdes_suffix[0]; /* d4xx suffix only uses subdevs first char */ + } else if (state->aggregated) suffix += 6; dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", suffix, diff --git a/include/media/i2c/d4xx_pdata.h b/include/media/i2c/d4xx_pdata.h index 73a54185..7f6037d0 100644 --- a/include/media/i2c/d4xx_pdata.h +++ b/include/media/i2c/d4xx_pdata.h @@ -16,6 +16,7 @@ struct d4xx_subdev_info { unsigned short phy_i2c_addr; unsigned short ser_alias; char suffix[5]; /* suffix for subdevs */ + int aggregated_link; }; struct d4xx_pdata { From b2917e0c8d0a8d7024005e9c951ffc7d591eae07 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Thu, 21 Mar 2024 10:48:12 +0200 Subject: [PATCH 28/72] d4xx-max929x: add kernel 6.5 support * max929x: backported kernel6 #3 https://github.com/dmipx/ipu6-drivers/commit/cd6786ff212285b1e4004360bddd995a9a30b280 * max929x: backported kernel6 #2 https://github.com/dmipx/ipu6-drivers/commit/6883c69e6630332c652ea152cdc8426bfc6b1231 * max929x: backported kernel6 https://github.com/dmipx/ipu6-drivers/commit/f5caff791a9600ea1d836efe81e211a3e8a105cd Signed-off-by: Dmitry Perchanov --- drivers/media/i2c/d4xx-compat/max9295.c | 7 +++++++ drivers/media/i2c/d4xx-compat/max9296.c | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c index e0c06d1b..4e203af0 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -23,6 +23,7 @@ #include #include #include +#include #include /* register specifics */ @@ -719,7 +720,11 @@ static int max9295_probe(struct i2c_client *client) return err; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) static int max9295_remove(struct i2c_client *client) +#else +static void max9295_remove(struct i2c_client *client) +#endif { struct max9295 *priv; @@ -733,7 +738,9 @@ static int max9295_remove(struct i2c_client *client) #endif } +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) return 0; +#endif } static const struct i2c_device_id max9295_id[] = { diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/max9296.c index 42807262..45641eb6 100644 --- a/drivers/media/i2c/d4xx-compat/max9296.c +++ b/drivers/media/i2c/d4xx-compat/max9296.c @@ -24,6 +24,7 @@ #include #include #include +#include // #include #include #include @@ -1134,8 +1135,11 @@ static int max9296_probe(struct i2c_client *client) return err; } - +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) static int max9296_remove(struct i2c_client *client) +#else +static void max9296_remove(struct i2c_client *client) +#endif { struct max9296 *priv; @@ -1148,8 +1152,9 @@ static int max9296_remove(struct i2c_client *client) client = NULL; #endif } - +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) return 0; +#endif } static const struct i2c_device_id max9296_id[] = { From ecc6b221deb9c234fc852865b74fd81817e9715a Mon Sep 17 00:00:00 2001 From: Josh Watts Date: Fri, 11 Oct 2024 11:45:51 -0400 Subject: [PATCH 29/72] d4xx-max9295: Isolate D4XX changes Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/max9295.c | 152 ++++++++++++++++++------ include/media/max9295.h | 1 + 2 files changed, 114 insertions(+), 39 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c index 4e203af0..9d6a9d99 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -117,6 +117,7 @@ struct max9295 { /* primary serializer properties */ __u32 def_addr; __u32 pst2_ref; + __u32 d4xx_hacks; }; static struct max9295 *prim_priv__; @@ -161,15 +162,6 @@ int max9295_setup_streaming(struct device *dev) u32 j; u32 st_en; - struct map_ctx map_pipe_dtype[] = { - {GMSL_CSI_DT_RAW_12, MAX9295_PIPE_Z_DT_ADDR, 0x2C, - MAX9295_ST_ID_2}, - {GMSL_CSI_DT_UED_U1, MAX9295_PIPE_X_DT_ADDR, 0x30, - MAX9295_ST_ID_0}, - {GMSL_CSI_DT_EMBED, MAX9295_PIPE_Y_DT_ADDR, 0x12, - MAX9295_ST_ID_1}, - }; - mutex_lock(&priv->lock); if (!priv->g_client.g_ctx) { @@ -219,35 +211,47 @@ int max9295_setup_streaming(struct device *dev) max9295_write_reg(dev, MAX9295_MIPI_RX2_ADDR, lane_map1); max9295_write_reg(dev, MAX9295_MIPI_RX3_ADDR, lane_map2); - for (i = 0; i < g_ctx->num_streams; i++) { - struct gmsl_stream *g_stream = &g_ctx->streams[i]; - - g_stream->st_id_sel = GMSL_ST_ID_UNUSED; - for (j = 0; j < ARRAY_SIZE(map_pipe_dtype); j++) { - if (map_pipe_dtype[j].dt == g_stream->st_data_type) { - /* - * TODO: - * 1) Remove link specific overrides, depends - * on #2. - * 2) Add support for vc id based stream sel - * overrides TX_SRC_SEL. would be useful in - * using same mappings in all ser devs. - */ - if (g_ctx->serdes_csi_link == - GMSL_SERDES_CSI_LINK_B) { - map_pipe_dtype[j].addr += 2; - map_pipe_dtype[j].st_id += 1; + if (priv->d4xx_hacks) { + struct map_ctx map_pipe_dtype[] = { + {GMSL_CSI_DT_RAW_12, MAX9295_PIPE_Z_DT_ADDR, 0x2C, + MAX9295_ST_ID_2}, + {GMSL_CSI_DT_UED_U1, MAX9295_PIPE_X_DT_ADDR, 0x30, + MAX9295_ST_ID_0}, + {GMSL_CSI_DT_EMBED, MAX9295_PIPE_Y_DT_ADDR, 0x12, + MAX9295_ST_ID_1}, + }; + + for (i = 0; i < g_ctx->num_streams; i++) { + struct gmsl_stream *g_stream = &g_ctx->streams[i]; + + g_stream->st_id_sel = GMSL_ST_ID_UNUSED; + for (j = 0; j < ARRAY_SIZE(map_pipe_dtype); j++) { + if (map_pipe_dtype[j].dt == g_stream->st_data_type) { + /* + * TODO: + * 1) Remove link specific overrides, depends + * on #2. + * 2) Add support for vc id based stream sel + * overrides TX_SRC_SEL. would be useful in + * using same mappings in all ser devs. + */ + if (g_ctx->serdes_csi_link == + GMSL_SERDES_CSI_LINK_B) { + map_pipe_dtype[j].addr += 2; + map_pipe_dtype[j].st_id += 1; + } + + g_stream->st_id_sel = map_pipe_dtype[j].st_id; + st_en = (map_pipe_dtype[j].addr == + MAX9295_PIPE_X_DT_ADDR) ? + 0xC0 : 0x40; + + max9295_write_reg(dev, map_pipe_dtype[j].addr, + (st_en | map_pipe_dtype[j].val)); } - - g_stream->st_id_sel = map_pipe_dtype[j].st_id; - st_en = (map_pipe_dtype[j].addr == - MAX9295_PIPE_X_DT_ADDR) ? - 0xC0 : 0x40; - - max9295_write_reg(dev, map_pipe_dtype[j].addr, - (st_en | map_pipe_dtype[j].val)); } } + dev_dbg(dev, "%s: d4xx specfic setup done\n", __func__); } for (i = 0; i < g_ctx->num_streams; i++) @@ -545,6 +549,57 @@ static int max9295_set_registers(struct device *dev, struct reg_pair *map, static int __max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, u8 data_type2, u32 vc_id) +{ + int err = 0; + u8 bpp = 0x30; + static u8 pipe_x_val = 0x0; + struct reg_pair map_multi_pipe_en[] = { + {0x0315, 0x80}, + }; + struct reg_pair map_bpp8dbl[] = { + {0x0312, 0x0F}, + }; + struct reg_pair map_pipe_control[] = { + /* addr, val */ + {MAX9295_PIPE_X_DT_ADDR, 0x5E}, // Pipe X pulls data_type1 + {0x0315, 0x52}, // Pipe X pulls data_type2 + {0x0309, 0x01}, // # Pipe X pulls vc_id + {0x030A, 0x00}, + }; + + if (data_type1 == GMSL_CSI_DT_RAW_8 || data_type1 == GMSL_CSI_DT_EMBED + || data_type2 == GMSL_CSI_DT_RAW_8 || data_type2 == GMSL_CSI_DT_EMBED) { + map_bpp8dbl[0].val |= (1 << pipe_id); + } else { + map_bpp8dbl[0].val &= ~(1 << pipe_id); + } + err |= max9295_set_registers(dev, map_bpp8dbl, ARRAY_SIZE(map_bpp8dbl)); + + map_pipe_control[0].addr += 0x2 * pipe_id; + map_pipe_control[1].addr += 0x2 * pipe_id; + map_pipe_control[2].addr += 0x2 * pipe_id; + map_pipe_control[3].addr += 0x2 * pipe_id; + + map_pipe_control[0].val = 0x40 | data_type1; + map_pipe_control[1].val = 0x40 | data_type2; + map_pipe_control[2].val = 1 << vc_id; + map_pipe_control[3].val = 0x00; + + if (pipe_id == 0) + pipe_x_val = map_pipe_control[1].val; + + err |= max9295_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + map_multi_pipe_en[0].val = 0x80 | pipe_x_val; + err |= max9295_set_registers(dev, map_multi_pipe_en, + ARRAY_SIZE(map_multi_pipe_en)); + + return err; +} + +static int __max9295_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) { int err = 0; u8 bpp = 0x30; @@ -565,6 +620,8 @@ static int __max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, {0x0102, 0x0E}, // LIM_HEART Pipe X: Disabled }; + dev_dbg(dev, "%s: d4xx specfic pipe set\n", __func__); + if (data_type1 == GMSL_CSI_DT_RAW_8 || data_type1 == GMSL_CSI_DT_EMBED || data_type2 == GMSL_CSI_DT_RAW_8 || data_type2 == GMSL_CSI_DT_EMBED) { map_bpp8dbl[0].val |= (1 << pipe_id); @@ -626,9 +683,15 @@ int max9295_init_settings(struct device *dev) err |= max9295_set_registers(dev, map_pipe_opt, ARRAY_SIZE(map_pipe_opt)); - for (i = 0; i < MAX9295_MAX_PIPES; i++) - err |= __max9295_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, - GMSL_CSI_DT_EMBED, i); + if (priv->d4xx_hacks) { + for (i = 0; i < MAX9295_MAX_PIPES; i++) + err |= __max9295_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + } else { + for (i = 0; i < MAX9295_MAX_PIPES; i++) + err |= __max9295_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + } mutex_unlock(&priv->lock); @@ -653,7 +716,12 @@ int max9295_set_pipe(struct device *dev, int pipe_id, mutex_lock(&priv->lock); - err = __max9295_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + + if (priv->d4xx_hacks) { + err = __max9295_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id); + } else { + err = __max9295_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + } mutex_unlock(&priv->lock); @@ -712,6 +780,12 @@ static int max9295_probe(struct i2c_client *client) priv->def_addr = 0; } #endif + if (pdata->d4xx_hacks) { + dev_info(&client->dev, "%s: set for d4xx\n", __func__); + priv->d4xx_hacks = pdata->d4xx_hacks; + } else { + priv->d4xx_hacks = 0; + } dev_set_drvdata(&client->dev, priv); /* dev communication gets validated when GMSL link setup is done */ diff --git a/include/media/max9295.h b/include/media/max9295.h index 8937d046..ff91d3cf 100644 --- a/include/media/max9295.h +++ b/include/media/max9295.h @@ -32,6 +32,7 @@ struct max9295_pdata { /* primary serializer properties */ __u32 is_prim_ser; __u32 def_addr; + __u32 d4xx_hacks; }; /** From c05bc5c85170bcaf32468795698ae9cf6e9b9d11 Mon Sep 17 00:00:00 2001 From: Josh Watts Date: Fri, 11 Oct 2024 11:47:39 -0400 Subject: [PATCH 30/72] d4xx-max9296: Isolate D4XX changes Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/max9296.c | 108 ++++++++++++++++++++---- include/media/max9296.h | 1 + 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/max9296.c index 45641eb6..4996ff17 100644 --- a/drivers/media/i2c/d4xx-compat/max9296.c +++ b/drivers/media/i2c/d4xx-compat/max9296.c @@ -130,6 +130,7 @@ struct max9296 { int reset_gpio; int pw_ref; struct regulator *vdd_cam_1v2; + __u32 d4xx_hacks; }; static int max9296_write_reg(struct device *dev, @@ -438,16 +439,19 @@ int max9296_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) goto error; } - /* Check csi mode compatibility */ - if (!((priv->csi_mode == MAX9296_CSI_MODE_2X4) ? - ((g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || - (g_ctx->csi_mode == GMSL_CSI_2X4_MODE)) : - ((g_ctx->csi_mode == GMSL_CSI_2X2_MODE) || - (g_ctx->csi_mode == GMSL_CSI_4X2_MODE)))) { - dev_err(dev, - "%s: csi mode not supported\n", __func__); - err = -EINVAL; - goto error; + if (priv->d4xx_hacks) { + /* Check csi mode compatibility */ + if (!((priv->csi_mode == MAX9296_CSI_MODE_2X4) ? + ((g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || + (g_ctx->csi_mode == GMSL_CSI_2X4_MODE)) : + ((g_ctx->csi_mode == GMSL_CSI_2X2_MODE) || + (g_ctx->csi_mode == GMSL_CSI_4X2_MODE)))) { + dev_err(dev, + "%s: csi mode not supported\n", __func__); + err = -EINVAL; + goto error; + } + dev_dbg(dev, "%s: d4xx specfic csi-mode set\n", __func__); } for (i = 0; i < priv->num_src; i++) { @@ -857,13 +861,15 @@ void max9296_reset_oneshot(struct device *dev) } EXPORT_SYMBOL(max9296_reset_oneshot); -static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, +static int __max9296_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_type1, u8 data_type2, u32 vc_id) { + struct max9296 *priv = dev_get_drvdata(dev); int err = 0; int i = 0; u8 en_mapping_num = 0x0F; u8 all_mapping_phy = 0x55; + struct reg_pair map_pipe_opt[] = { {0x1458, 0x28}, // PHY A Optimization {0x1459, 0x68}, // PHY A Optimization @@ -880,6 +886,7 @@ static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 {0x0473, 0x10}, }; + struct reg_pair map_pipe_control[] = { // Enable 4 mappings for Pipe X {MAX9296_TX11_PIPE_X_EN_ADDR, 0x0F}, @@ -901,6 +908,8 @@ static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, {0x0100, 0x23}, // pipe X }; + dev_dbg(dev, "%s: d4xx specfic pipe set\n", __func__); + for (i = 0; i < 10; i++) { map_pipe_control[i].addr += 0x40 * pipe_id; } @@ -927,7 +936,59 @@ static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, err |= max9296_set_registers(dev, map_pipe_opt, ARRAY_SIZE(map_pipe_opt)); + return err; +} +static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + int i = 0; + u8 en_mapping_num = 0x0F; + u8 all_mapping_phy = 0x55; + + struct reg_pair map_pipe_control[] = { + // Enable 4 mappings for Pipe X + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x0F}, + // Map data_type1 on vc_id + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + // Map frame_start on vc_id + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX9296_PIPE_X_SRC_3_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_DST_3_MAP_ADDR, 0x12}, + // All mappings to PHY1 (master for port A) + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x55}, + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + }; + + for (i = 0; i < ARRAY_SIZE(map_pipe_control); i++) { + map_pipe_control[i].addr += 0x40 * pipe_id; + } + + if (data_type2 == 0x0) { + en_mapping_num = 0x07; + all_mapping_phy = 0x15; + } + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = (vc_id << 6) | data_type1; + map_pipe_control[2].val = (vc_id << 6) | data_type1; + map_pipe_control[3].val = (vc_id << 6) | 0x00; + map_pipe_control[4].val = (vc_id << 6) | 0x00; + map_pipe_control[5].val = (vc_id << 6) | 0x01; + map_pipe_control[6].val = (vc_id << 6) | 0x01; + map_pipe_control[7].val = (vc_id << 6) | data_type2; + map_pipe_control[8].val = (vc_id << 6) | data_type2; + map_pipe_control[9].val = all_mapping_phy; + + err |= max9296_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); return err; } int max9296_init_settings(struct device *dev) @@ -938,9 +999,15 @@ int max9296_init_settings(struct device *dev) mutex_lock(&priv->lock); - for (i = 0; i < MAX9296_MAX_PIPES; i++) - err |= __max9296_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, - GMSL_CSI_DT_EMBED, i); + if (priv->d4xx_hacks) { + for (i = 0; i < MAX9296_MAX_PIPES; i++) + err |= __max9296_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + } else { + for (i = 0; i < MAX9296_MAX_PIPES; i++) + err |= __max9296_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + } mutex_unlock(&priv->lock); @@ -965,7 +1032,11 @@ int max9296_set_pipe(struct device *dev, int pipe_id, mutex_lock(&priv->lock); - err = __max9296_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + if (priv->d4xx_hacks) { + err = __max9296_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id); + } else { + err = __max9296_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + } mutex_unlock(&priv->lock); @@ -1065,6 +1136,12 @@ static int max9296_parse_pdata(struct max9296 *priv, return -EINVAL; } priv->max_src = pdata->max_src; + if (pdata->d4xx_hacks) { + dev_info(&client->dev, "%s: set for d4xx\n", __func__); + priv->d4xx_hacks = pdata->d4xx_hacks; + } else { + priv->d4xx_hacks = 0; + } } else { priv->csi_mode = MAX9296_CSI_MODE_2X4; priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; @@ -1072,6 +1149,7 @@ static int max9296_parse_pdata(struct max9296 *priv, priv->max_src = 1; priv->reset_gpio = 0; priv->vdd_cam_1v2 = NULL; + priv->d4xx_hacks = 0; } return 0; } diff --git a/include/media/max9296.h b/include/media/max9296.h index 59b33cab..27c9cd1f 100644 --- a/include/media/max9296.h +++ b/include/media/max9296.h @@ -39,6 +39,7 @@ struct max9296_pdata { bool splitter_enabled; __u32 max_src; __u32 csi_mode; + __u32 d4xx_hacks; }; int max9296_get_available_pipe_id(struct device *dev, int vc_id); From 8928be130723549f163e10d5ac40bf10dd2d82bb Mon Sep 17 00:00:00 2001 From: Josh Watts Date: Fri, 11 Oct 2024 11:48:32 -0400 Subject: [PATCH 31/72] d4xx-max9295: Expose MFP IO --- drivers/media/i2c/d4xx-compat/max9295.c | 22 ++++++++++++++++++++++ include/media/max9295.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c index 9d6a9d99..102a2655 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -699,6 +699,28 @@ int max9295_init_settings(struct device *dev) } EXPORT_SYMBOL(max9295_init_settings); +int max9295_set_mfp(struct device *dev, int pin, int val) +{ + struct max9295 *priv = dev_get_drvdata(dev); + struct regmap *map = priv->regmap; + int err; + + if (pin > 10) + return -EINVAL; + + err = regmap_update_bits(map, 0x2BE + (pin * 3), + (1<<4 | 1<<2 | 1<<0), + ((val ? 1<<4 : 0) | 0<<2 | 0<<0) ); + + err |= regmap_update_bits(map, 0x2BF + (pin * 3), + 3<<6|1<<5, + 1<<6|1<<5); + + return err; + +} +EXPORT_SYMBOL(max9295_set_mfp); + int max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, u8 data_type2, u32 vc_id) { diff --git a/include/media/max9295.h b/include/media/max9295.h index ff91d3cf..71c2106a 100644 --- a/include/media/max9295.h +++ b/include/media/max9295.h @@ -109,4 +109,6 @@ int max9295_setup_streaming(struct device *dev); int max9295_init_settings(struct device *dev); /** @} */ +int max9295_set_mfp(struct device *dev, int pin, int val); + #endif /* __MAX9295_H__ */ From 504641ba7ec229dc978dbf5f8437498e0aec0f93 Mon Sep 17 00:00:00 2001 From: Josh Watts Date: Fri, 11 Oct 2024 11:52:09 -0400 Subject: [PATCH 32/72] d4xx-max9295: Force port_sel Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/max9295.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/max9295.c index 102a2655..a42f1ac9 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/max9295.c @@ -252,6 +252,9 @@ int max9295_setup_streaming(struct device *dev) } } dev_dbg(dev, "%s: d4xx specfic setup done\n", __func__); + } else { + //HACK: JNW 2024-10-10 Until we actually setup num_streams for proper mapping + port_sel |= 0x61; // Just pipe X & port B } for (i = 0; i < g_ctx->num_streams; i++) From bbe50c152a37c504a2487a98c8b765f6d043580a Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Tue, 11 Feb 2025 11:53:36 -0700 Subject: [PATCH 33/72] d4xx-max9296: setup control fallback for serializer invalid source * deserializer link setup graceful fallback when no csi-link aggregation is possible (e.g. serializer not responding) ``` [ 75.854983] d4xx 2-0012: configure GMSL port B [ 76.092278] max9295 2-0064: max9295_setup_control: update address reassignment 0x40->0x64 [ 76.100995] max9295 2-0064: max9295_write_reg:i2c write failed, 0x0 = c8 [ 76.108082] max9295 2-0064: max9295_write_reg:i2c write failed, 0x10 = 22 [ 76.115152] max9295 2-0064: max9295_setup_control: ERROR: ser device not found [ 76.122556] d4xx 2-0012: gmsl serializer invalid source [ 76.127955] max9296 2-0048: max9296_setup_control: set SerDes GMSL A & B dual-link) [ 76.244858] max9296 2-0048: max9296_setup_control: checking serializer sources (count/ref=1/2 -> src[1]=GMSL B) [ 76.255190] max9296 2-0048: max9296_setup_control: restore SerDes GMSL A single-link [ 76.372275] d4xx 2-0012: gmsl serdes setup gracefully fallback [ 76.378294] d4xx 2-0012: max9295 communication failed : -524 [ 76.384156] max9295 2-0064: [MAX9295]: remove GMSL Serializer [ 76.390186] d4xx: probe of 2-0012 failed with error -524 [ 76.395939] intel-ipu6-isys intel-ipu6-isys0: can't create new i2c subdev ``` Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/max9296.c | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/max9296.c index 4996ff17..0405a5ee 100644 --- a/drivers/media/i2c/d4xx-compat/max9296.c +++ b/drivers/media/i2c/d4xx-compat/max9296.c @@ -2,6 +2,7 @@ * max9296.c - max9296 GMSL Deserializer driver * * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024-2025, INTEL CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -358,6 +359,8 @@ int max9296_setup_control(struct device *dev, struct device *s_dev) if ((priv->max_src > 1U) && (priv->num_src_found > 0U) && (priv->splitter_enabled == false)) { + dev_info(dev, "%s: set SerDes GMSL A & B dual-link)\n", + __func__); max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x03); max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x23); @@ -372,18 +375,45 @@ int max9296_setup_control(struct device *dev, struct device *s_dev) priv->sdev_ref++; + dev_info(dev, "%s: checking serializer sources (count/ref=%d/%d -> src[%d]=%s)\n", + __func__, + priv->num_src_found, + priv->sdev_ref, i, + priv->sources[i].g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A? "GMSL A": "GMSL B"); + /* Reset splitter mode if all devices are not found */ if ((priv->sdev_ref != priv->max_src) && (priv->splitter_enabled == true) && (priv->num_src_found > 0U) && (priv->num_src_found < priv->max_src)) { + dev_info(dev, "%s: restore SerDes %s single-link\n", + __func__, + priv->src_link == GMSL_SERDES_CSI_LINK_A? "GMSL A": "GMSL B"); err = max9296_write_link(dev, priv->src_link); - if (err) + if (err) { + dev_info(dev, "%s: fail to write source link\n", + __func__); goto error; + } priv->splitter_enabled = false; + + } else if (priv->num_src_found < priv->sdev_ref) { + + dev_info(dev, "%s: restore SerDes %s single-link\n", + __func__, + priv->src_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B"); + + err = max9296_write_link(dev, priv->src_link); + if (err) { + dev_info(dev, "%s: fail to write source link\n", + __func__); + goto error; + } + priv->splitter_enabled = false; } + error: mutex_unlock(&priv->lock); return err; From f532bea6183350e05188456bee6e2caa33eb8859 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Fri, 29 Aug 2025 06:48:40 -0700 Subject: [PATCH 34/72] d4xx-compat: add realsense2 driver specific namespace Signed-off-by: Florent Pirou --- Makefile | 4 ++-- drivers/media/i2c/d4xx-compat/Makefile | 4 ++-- .../media/i2c/d4xx-compat/{max9295.c => d4xx-max9295.c} | 8 ++++---- .../media/i2c/d4xx-compat/{max9296.c => d4xx-max9296.c} | 6 +++--- drivers/media/i2c/d4xx-compat/d4xx.c | 8 ++++---- include/media/{max9295.h => i2c/d4xx-max9295.h} | 0 include/media/{max9296.h => i2c/d4xx-max9296.h} | 0 7 files changed, 15 insertions(+), 15 deletions(-) rename drivers/media/i2c/d4xx-compat/{max9295.c => d4xx-max9295.c} (99%) rename drivers/media/i2c/d4xx-compat/{max9296.c => d4xx-max9296.c} (99%) rename include/media/{max9295.h => i2c/d4xx-max9295.h} (100%) rename include/media/{max9296.h => i2c/d4xx-max9296.h} (100%) diff --git a/Makefile b/Makefile index 944f3ef1..bab93cd4 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,8 @@ export CONFIG_VIDEO_MAX9X=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y export CONFIG_INTEL_IPU_ACPI=m -export CONFIG_I2C_IOEXPANDER_SER_MAX9295 = m -export CONFIG_I2C_IOEXPANDER_DESER_MAX9296 = m +export CONFIG_VIDEO_D4XX_MAX9295 = m +export CONFIG_VIDEO_D4XX_MAX9296 = m export CONFIG_VIDEO_D4XX_COMPAT=m # Define config macros for conditional compilation in ipu-acpi.c diff --git a/drivers/media/i2c/d4xx-compat/Makefile b/drivers/media/i2c/d4xx-compat/Makefile index 4a2b5835..8875a1a2 100644 --- a/drivers/media/i2c/d4xx-compat/Makefile +++ b/drivers/media/i2c/d4xx-compat/Makefile @@ -6,6 +6,6 @@ KERNEL_VERSION := $(shell echo $(KERNELRELEASE) | cut -d- -f1 | sed -r 's/([0-9] ccflags-y += -I$(src)/../../../$(KERNEL_VERSION)/include-overrides ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES -DCONFIG_SYSFS -DPLATFORM_AXIOMTEK -obj-$(CONFIG_I2C_IOEXPANDER_SER_MAX9295) += max9295.o -obj-$(CONFIG_I2C_IOEXPANDER_DESER_MAX9296) += max9296.o +obj-$(CONFIG_VIDEO_D4XX_MAX9295) += d4xx-max9295.o +obj-$(CONFIG_VIDEO_D4XX_MAX9296) += d4xx-max9296.o obj-$(CONFIG_VIDEO_D4XX_COMPAT) += d4xx.o diff --git a/drivers/media/i2c/d4xx-compat/max9295.c b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c similarity index 99% rename from drivers/media/i2c/d4xx-compat/max9295.c rename to drivers/media/i2c/d4xx-compat/d4xx-max9295.c index a42f1ac9..c1bfc210 100644 --- a/drivers/media/i2c/d4xx-compat/max9295.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include /* register specifics */ #define MAX9295_MIPI_RX0_ADDR 0x330 @@ -843,12 +843,12 @@ static void max9295_remove(struct i2c_client *client) } static const struct i2c_device_id max9295_id[] = { - { "max9295", 0 }, + { "d4xx-max9295", 0 }, { }, }; static const struct of_device_id max9295_of_match[] = { - { .compatible = "maxim,max9295", }, + { .compatible = "maxim,d4xx-max9295", }, { }, }; MODULE_DEVICE_TABLE(of, max9295_of_match); @@ -856,7 +856,7 @@ MODULE_DEVICE_TABLE(i2c, max9295_id); static struct i2c_driver max9295_i2c_driver = { .driver = { - .name = "max9295", + .name = "d4xx-max9295", .owner = THIS_MODULE, }, #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) diff --git a/drivers/media/i2c/d4xx-compat/max9296.c b/drivers/media/i2c/d4xx-compat/d4xx-max9296.c similarity index 99% rename from drivers/media/i2c/d4xx-compat/max9296.c rename to drivers/media/i2c/d4xx-compat/d4xx-max9296.c index 0405a5ee..a550c642 100644 --- a/drivers/media/i2c/d4xx-compat/max9296.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9296.c @@ -28,7 +28,7 @@ #include // #include #include -#include +#include /* register specifics */ #define MAX9296_DST_CSI_MODE_ADDR 0x330 @@ -1266,7 +1266,7 @@ static void max9296_remove(struct i2c_client *client) } static const struct i2c_device_id max9296_id[] = { - { "max9296", 0 }, + { "d4xx-max9296", 0 }, { }, }; @@ -1274,7 +1274,7 @@ MODULE_DEVICE_TABLE(i2c, max9296_id); static struct i2c_driver max9296_i2c_driver = { .driver = { - .name = "max9296", + .name = "d4xx-max9296", .owner = THIS_MODULE, .of_match_table = of_match_ptr(max9296_of_match), }, diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index e5870214..a7af5e45 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -41,8 +41,8 @@ #include #ifdef CONFIG_VIDEO_D4XX_SERDES -#include -#include +#include +#include #else #include #define GMSL_CSI_DT_YUV422_8 0x1E @@ -3258,11 +3258,11 @@ static int ds5_board_setup(struct ds5 *state) .d4xx_hacks = 1, }; static struct i2c_board_info i2c_info_des = { - I2C_BOARD_INFO("max9296", 0x48), + I2C_BOARD_INFO("d4xx-max9296", 0x48), .platform_data = &max9296_pdata, }; static struct i2c_board_info i2c_info_ser = { - I2C_BOARD_INFO("max9295", 0x42), + I2C_BOARD_INFO("d4xx-max9295", 0x42), .platform_data = &max9295_pdata, }; diff --git a/include/media/max9295.h b/include/media/i2c/d4xx-max9295.h similarity index 100% rename from include/media/max9295.h rename to include/media/i2c/d4xx-max9295.h diff --git a/include/media/max9296.h b/include/media/i2c/d4xx-max9296.h similarity index 100% rename from include/media/max9296.h rename to include/media/i2c/d4xx-max9296.h From f475fb9eaa6a4bbc9e85b9f63d426e0e235d9215 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 12 Sep 2025 09:47:59 -0700 Subject: [PATCH 35/72] d4xx-compat: add v4l set_routing support, disable set query substream legacy api * d4xx: compile ipu7 and ipu6 isys-video * d4xx: add sensor subdev stream set/get fmt ops * d4xx: add mux subdev stream set/get fmt ops * d4xx: add mux enable and disable streams v4l2 ops * d4xx: make modules suffix versioning same as dkms Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx-max9295.c | 1 + drivers/media/i2c/d4xx-compat/d4xx-max9296.c | 1 + drivers/media/i2c/d4xx-compat/d4xx.c | 868 ++++++++++++++++++- 3 files changed, 828 insertions(+), 42 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c index c1bfc210..fb58f984 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c @@ -887,3 +887,4 @@ MODULE_DESCRIPTION("GMSL Serializer driver max9295"); MODULE_AUTHOR("Sudhir Vyas "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION_SUFFIX); diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max9296.c b/drivers/media/i2c/d4xx-compat/d4xx-max9296.c index a550c642..ed05e147 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max9296.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9296.c @@ -1305,3 +1305,4 @@ module_exit(max9296_exit); MODULE_DESCRIPTION("Dual GMSL Deserializer driver max9296"); MODULE_AUTHOR("Sudhir Vyas #include #include -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #include #include #endif @@ -50,6 +50,9 @@ #define GMSL_CSI_DT_RAW_8 0x2A #define GMSL_CSI_DT_EMBED 0x12 #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) +#include +#endif //#define DS5_DRIVER_NAME "DS5 RealSense camera driver" #define DS5_DRIVER_NAME "d4xx" @@ -144,7 +147,7 @@ #define MAX_RGB_EXP 10000 #define DEF_DEPTH_EXP 33000 #define DEF_RGB_EXP 1660 -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #define MAX9295_REG0 0x0000 #define MAX9295_I2C_4 0x0044 @@ -202,7 +205,7 @@ enum ds5_mux_pad { #define ds5_raw_write_with_check(state, addr, buf, size) {\ if (ds5_raw_write(state, addr, buf, size)) \ return -EINVAL; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #define max9296_write_8_with_check(state, addr, buf) {\ if (max9296_write_8(state, addr, buf)) \ return -EINVAL; \ @@ -335,7 +338,7 @@ static const struct hwm_cmd ewb = { .magic_word = 0xCDAB, .opcode = 0x18, }; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_1250MHZ, D4XX_LINK_FREQ_1125MHZ, @@ -430,6 +433,7 @@ struct ds5_sensor { const struct ds5_format *formats; unsigned int n_formats; int pipe_id; + int initialized; }; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM @@ -463,7 +467,7 @@ enum { DS5_AWG, }; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #define NR_OF_DS5_PADS 7 #define NR_OF_DS5_STREAMS 4 struct v4l2_mbus_framefmt ds5_ffmts[NR_OF_DS5_PADS]; @@ -503,6 +507,11 @@ struct ds5 { const struct ds5_variant *variant; int is_depth, is_y8, is_rgb, is_imu; int aggregated; +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) + int routing_initialized; +#endif +#endif u16 fw_version; u16 fw_build; #ifdef CONFIG_VIDEO_D4XX_SERDES @@ -513,11 +522,15 @@ struct ds5 { struct i2c_client *dser_i2c; struct serdes_state dser_st; #endif -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #define NR_OF_CSI2_BE_SOC_STREAMS 16 #define NR_OF_DS5_SUB_STREAMS 6 /*d+d.md,c+c.md,ir,imu*/ int pad_to_vc[DS5_MUX_PAD_COUNT]; +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT int pad_to_substream[NR_OF_CSI2_BE_SOC_STREAMS]; +#else + struct mutex mutex; #endif }; @@ -530,7 +543,7 @@ struct ds5_counters { #define ds5_from_depth_sd(sd) container_of(sd, struct ds5, depth.sd) #define ds5_from_ir_sd(sd) container_of(sd, struct ds5, ir.sd) #define ds5_from_rgb_sd(sd) container_of(sd, struct ds5, rgb.sd) -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static inline void msleep_range(unsigned int delay_base) { usleep_range(delay_base * 1000, delay_base * 1000 + 500); @@ -697,7 +710,7 @@ static int ds5_raw_read(struct ds5 *state, u16 reg, void *val, size_t val_len) return ret; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT static s64 d4xx_query_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; static u8 d4xx_set_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; static void set_sub_stream_fmt(int index, u32 code) @@ -1282,12 +1295,38 @@ static int ds5_sensor_get_fmt(struct v4l2_subdev *sd, else fmt->format = sensor->format; +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + mutex_unlock(&state->lock); dev_dbg(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", __func__, fmt->pad, fmt->format.code, fmt->format.width, fmt->format.height); +#else + int ret = 0; + if (!v4l2_state) + goto sensor_g_fmt_state_end; + + struct v4l2_mbus_framefmt *mf; + mf = v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); + if (!mf) { + dev_warn(sd->dev, "Failed to get %s format for sensor pad/stream %u/%u \n", + sd->name, fmt->pad, fmt->stream); + ret = -EINVAL; + goto sensor_g_fmt_state_end; + } + sensor->format = *mf; + + sensor_g_fmt_state_end: + mutex_unlock(&state->lock); + dev_dbg(sensor->sd.dev, + "%s(): fmt->pad/stream: %u/%u@0x%x/%ux%u\n", + __func__, + fmt->pad, fmt->stream,fmt->format.code, fmt->format.width, fmt->format.height); + return ret; +#endif + return 0; } @@ -1350,7 +1389,7 @@ static const struct ds5_format *ds5_sensor_find_format( #define MIPI_CSI2_TYPE_RAW14 0x2d /* 1-8 */ #define MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i) - 1) -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static unsigned int mbus_code_to_mipi(u32 code) { switch (code) { @@ -1395,7 +1434,7 @@ static unsigned int mbus_code_to_mipi(u32 code) } #endif -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static int ds5_s_state_pad(struct ds5 *state, int pad) { int ret = 0; @@ -1465,7 +1504,7 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf;// = &fmt->format; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT int substream = -1; #endif //unsigned r; @@ -1473,7 +1512,6 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, dev_dbg(sensor->sd.dev, "%s(): state %p, " "sensor %p, fmt %p, fmt->format %p\n", __func__, state, sensor, fmt, &fmt->format); - mf = &fmt->format; if (fmt->pad) @@ -1505,8 +1543,8 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, state->mux.last_set = sensor; +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT mutex_unlock(&state->lock); -#ifdef CONFIG_VIDEO_INTEL_IPU6 substream = state->pad_to_substream[sensor->mux_pad]; if (substream != -1) { @@ -1519,8 +1557,29 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, dev_dbg(sensor->sd.dev, "%s(): fmt->pad: %d, sensor->mux_pad: %d, code: 0x%x, %ux%u substream:%d\n", __func__, fmt->pad, sensor->mux_pad, fmt->format.code, fmt->format.width, fmt->format.height, substream); -#endif return 0; +#else + int ret = 0; + if (!v4l2_state) + goto sensor_s_fmt_state_end; + + mf = v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); + if (!mf) { + dev_warn(sensor->sd.dev, "Failed to get %s format for sensor pad %u/%u \n", + sensor->sd.name, fmt->pad, fmt->stream); + ret = -EINVAL; + goto sensor_s_fmt_state_end; + } + *mf = sensor->format; + + sensor_s_fmt_state_end: + mutex_unlock(&state->lock); + dev_dbg(sensor->sd.dev, + "%s(): fmt->pad/stream: %u/%u@0x%x/%ux%u\n", + __func__, + fmt->pad, fmt->stream,fmt->format.code, fmt->format.width, fmt->format.height); + return ret; +#endif } static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, @@ -1533,9 +1592,13 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); struct ds5 *state = v4l2_get_subdevdata(sd); -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) /* set state by vc */ ds5_s_state_pad(state, sensor->mux_pad); + + dev_dbg(sd->dev, "%s(): %s (state %p)\n", + __func__, sd->name, state); + #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) return __ds5_sensor_set_fmt(state, sensor, cfg, fmt); @@ -1741,15 +1804,94 @@ static int ds5_sensor_s_stream(struct v4l2_subdev *sd, int on) sensor->streaming = on; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT return 0; +#else + return v4l2_subdev_s_stream_helper(sd, on); +#endif +#endif + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +static int ds5_sensor_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + struct ds5 *state = v4l2_get_subdevdata(sd); + unsigned int i; + int ret = 0; + + /* set state by vc */ + ds5_s_state_pad(state, sensor->mux_pad); + + struct v4l2_subdev_route ds5_sensor_routes[] = { + { + .sink_stream = state->pad_to_vc[sensor->mux_pad], + .source_stream = state->pad_to_vc[sensor->mux_pad], + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting ds5_sensor_routing = { + .num_routes = ARRAY_SIZE(ds5_sensor_routes), + .routes = ds5_sensor_routes, + }; + + /* Needed by v4l2_subdev_s_stream_helper(), even with 1 stream only */ + ret = v4l2_subdev_set_routing(sd, v4l2_state, &ds5_sensor_routing); + if (ret) { + dev_warn(sensor->sd.dev, "%s(): failed to set default route on %s\n", + __func__, sensor->sd.name); + return ret; + } + dev_dbg(sensor->sd.dev, "%s(): set %s stream-id=%u\n", + __func__, sensor->sd.name, state->pad_to_vc[sensor->mux_pad]); + + return 0; +} + +static int ds5_sensor_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + u32 pad, u64 streams_mask) +{ + + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + + dev_dbg(sensor->sd.dev, "%s(): enable: name=%s pad=%u streams_mask=%llu\n", + __func__, sensor->sd.name, pad, streams_mask); + + return 0; + +} +static int ds5_sensor_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + u32 pad, u64 streams_mask) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + + dev_dbg(sensor->sd.dev, "%s(): disable: name=%s pad=%u streams_mask=%llu\n", + __func__, sensor->sd.name, pad, streams_mask); + + return 0; +} +#endif +#endif + static const struct v4l2_subdev_pad_ops ds5_depth_pad_ops = { .enum_mbus_code = ds5_sensor_enum_mbus_code, .enum_frame_size = ds5_sensor_enum_frame_size, .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .enable_streams = ds5_sensor_enable_streams, + .disable_streams = ds5_sensor_disable_streams, +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) .get_frame_interval = ds5_sensor_g_frame_interval, .set_frame_interval = ds5_sensor_s_frame_interval, @@ -1778,6 +1920,12 @@ static const struct v4l2_subdev_pad_ops ds5_ir_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .enable_streams = ds5_sensor_enable_streams, + .disable_streams = ds5_sensor_disable_streams, +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) .get_frame_interval = ds5_sensor_g_frame_interval, .set_frame_interval = ds5_sensor_s_frame_interval, @@ -1796,6 +1944,12 @@ static const struct v4l2_subdev_pad_ops ds5_rgb_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .enable_streams = ds5_sensor_enable_streams, + .disable_streams = ds5_sensor_disable_streams, +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) .get_frame_interval = ds5_sensor_g_frame_interval, .set_frame_interval = ds5_sensor_s_frame_interval, @@ -1813,6 +1967,12 @@ static const struct v4l2_subdev_pad_ops ds5_imu_pad_ops = { .enum_frame_interval = ds5_sensor_enum_frame_interval, .get_fmt = ds5_sensor_get_fmt, .set_fmt = ds5_sensor_set_fmt, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .enable_streams = ds5_sensor_enable_streams, + .disable_streams = ds5_sensor_disable_streams, +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) .get_frame_interval = ds5_sensor_g_frame_interval, .set_frame_interval = ds5_sensor_s_frame_interval, @@ -2101,7 +2261,7 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) if (state->is_rgb) base = DS5_RGB_CONTROL_BASE; -#ifndef CONFIG_VIDEO_INTEL_IPU6 +#if !IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || !IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) else if (state->is_imu) return -EINVAL; #endif @@ -2357,7 +2517,7 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) if (state->is_depth) ret = ds5_write(state, base | DS5_PWM_FREQUENCY, ctrl->val); break; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT case V4L2_CID_IPU_SET_SUB_STREAM: { u32 val = (*ctrl->p_new.p_s64 & 0xFFFF); @@ -2380,6 +2540,8 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) #endif } break; +#endif +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) case V4L2_CID_LINK_FREQ: { if ( !sensor && ctrl->p_new.p_u8) { @@ -2733,7 +2895,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (state->is_depth) ds5_read(state, base | DS5_PWM_FREQUENCY, ctrl->p_new.p_u16); break; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT case V4L2_CID_IPU_QUERY_SUB_STREAM: { if (sensor) { int substream = state->pad_to_substream[sensor->mux_pad]; @@ -2978,7 +3140,7 @@ static const struct v4l2_ctrl_config ds5_ctrl_pwm = { .def = 1, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, }; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_LINK_FREQ, @@ -2990,7 +3152,9 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .def = 12, // default D4XX_LINK_FREQ_300MHZ .qmenu_int = link_freq_menu_items, }; +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT static struct v4l2_ctrl_config d4xx_controls_q_sub_stream = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_IPU_QUERY_SUB_STREAM, @@ -3038,6 +3202,11 @@ static int ds5_mux_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_internal_ops ds5_sensor_internal_ops = { .open = ds5_mux_open, .close = ds5_mux_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .init_state = ds5_sensor_init_state, +#endif +#endif }; #ifdef CONFIG_VIDEO_D4XX_SERDES @@ -3457,7 +3626,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) return err; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" @@ -3507,7 +3676,7 @@ static int ds5_serdes_setup(struct ds5 *state) { int ret = 0; struct i2c_client *c = state->client; -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) int i = 0, c_bus = -1; int c_bus_new = c->adapter->nr; @@ -3705,7 +3874,7 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) /* override default int type to u32 to match SKU & UVC */ ctrls->exposure->type = V4L2_CTRL_TYPE_U32; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); /* MTL and RPL/ADL IPU6 CSI-DPHY do NOT share * the same default link_freq. @@ -3714,6 +3883,8 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) */ if (sensor && ctrls->link_freq ) ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT if (state->aggregated) { d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; @@ -3808,12 +3979,14 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) break; default: state->mux.sd.subdev.ctrl_handler = hdl; +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT dev_info(state->mux.sd.subdev.dev, "%s():%d set ctrl_handler for MUX: %p, %s, substream id %d \n", __func__, __LINE__, state->mux.sd.subdev.ctrl_handler, state->mux.sd.subdev.name, d4xx_controls_q_sub_stream.def); +#endif break; } @@ -3851,8 +4024,11 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, name, i2c_adapter_id(c->adapter), c->addr); #endif +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - +#else + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; +#endif pad->flags = MEDIA_PAD_FL_SOURCE; entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; @@ -3873,6 +4049,14 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) struct media_entity *entity = &sensor->sd.entity; int ret = -1; +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(sd->dev, "failed to init v4l2 subdev stream: %d\n", ret); + return ret; + } +#endif + // FIXME: is async needed? ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); if (ret < 0) { @@ -3890,6 +4074,8 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) dev_dbg(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); + sensor->initialized = true; + return 0; e_sd: @@ -4142,6 +4328,115 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, return ret; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +static int _ds5_mux_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + struct v4l2_subdev_krouting *routing) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + int ret = 0; + unsigned int num_active_routes = 0; + unsigned int i; + + struct v4l2_mbus_framefmt *ffmt; + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + }; + + ffmt = &format; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + const struct media_pad *pads = sd->entity.pads; + + if (route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + num_active_routes++; + } + + if (num_active_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -E2BIG; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret < 0) { + dev_warn(sd->dev, "%s(): %s routing validation error : %d\n", + __func__, sd->name, ret); + return ret; + } + + ret = v4l2_subdev_set_routing(sd, v4l2_state, routing); + if (ret < 0) { + dev_warn(sd->dev, "%s(): %s set routing error : %d\n", + __func__, sd->name, ret); + return ret; + } + state->routing_initialized = 1; + struct v4l2_subdev_stream_configs *stream_configs= &v4l2_state->stream_configs; + + for (unsigned int i = 0; i < stream_configs->num_configs; i++) { + u32 sink_pad = stream_configs->configs[i].pad; + u32 sink_stream = stream_configs->configs[i].stream; + struct ds5_sensor *s = NULL; + + if (sink_pad != DS5_MUX_PAD_EXTERNAL) + state->pad_to_vc[sink_pad] = sink_stream; + + switch (sink_pad) { + case DS5_MUX_PAD_DEPTH: + s = &state->depth.sensor; + ffmt = &s->format; + break; + case DS5_MUX_PAD_RGB: + s = &state->rgb.sensor; + ffmt = &s->format; + break; + case DS5_MUX_PAD_IR: + s = &state->ir.sensor; + ffmt = &s->format; + break; + case DS5_MUX_PAD_IMU: + s = &state->imu.sensor; + ffmt = &s->format; + break; + case DS5_MUX_PAD_EXTERNAL: + break; + default: + dev_err(sd->dev, "Invalid sink pad %u\n", sink_pad); + return -EINVAL; + } + + stream_configs->configs[i].fmt = *ffmt; + + dev_dbg(sd->dev, "%s:%d: stream_configs->configs[%u] pad/stream=%u/%u format: code %x, res %ux%u\n", + __func__, __LINE__, i, sink_pad, sink_stream, + ffmt->code, ffmt->width, ffmt->height); + } + return 0; +} + +static int ds5_mux_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + dev_dbg(sd->dev,"%s:%d: v4l2_state=%p, sd_format_whence=%s\n", + __func__, __LINE__, v4l2_state, + which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY" : "V4L2_SUBDEV_FORMAT_ACTIVE" ); + + _ds5_mux_set_routing(sd, v4l2_state, routing); + + return 0; +} +#endif +#endif + /* No locking needed */ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) @@ -4156,19 +4451,21 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, struct ds5_sensor *sensor = state->mux.last_set; u32 pad = sensor->mux_pad; int ret = 0; -#ifdef CONFIG_VIDEO_INTEL_IPU6 + +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT int substream = -1; - dev_dbg(sd->dev, "%s:%d: fmt->pad:%d, sensor->mux_pad: %d, \ + + dev_dbg(sd->dev, "%s:%d: fmt->pad:%d, sensor->mux_pad: %d, \ for sensor: %s\n", __func__, __LINE__, fmt->pad, pad, sensor->sd.name); -#endif - if (pad != DS5_MUX_PAD_EXTERNAL) ds5_s_state_pad(state, pad); + sensor = state->mux.last_set; + switch (pad) { case DS5_MUX_PAD_DEPTH: case DS5_MUX_PAD_IR: @@ -4189,8 +4486,9 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, ffmt->height = fmt->format.height; ffmt->code = fmt->format.code; } + fmt->format = *ffmt; -#ifdef CONFIG_VIDEO_INTEL_IPU6 + // substream = pad_to_substream[fmt->pad]; substream = state->pad_to_substream[pad]; @@ -4207,6 +4505,133 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, fmt->pad, pad, fmt->format.code, fmt->format.width, fmt->format.height, substream, sensor->sd.name); +#else + struct media_pad *remote_pad; + struct v4l2_subdev *remote_sd; + u32 other_pad, other_stream; + unsigned int i; + + dev_dbg(sd->dev, "%s(): fmt->pad/stream:%u / %u (sensor->mux_pad: %u) \ + code: 0x%x: %ux%u for %s\n", + __func__, + fmt->pad, fmt->stream, pad, + fmt->format.code, fmt->format.width, fmt->format.height, + sd->name); + + /* No transcoding, sink and sensor formats must match.*/ + if (!(sd->entity.pads[fmt->pad].flags & MEDIA_PAD_FL_SOURCE)) { + + switch (fmt->pad) { + case DS5_MUX_PAD_DEPTH: + sensor = &state->depth.sensor; + break; + case DS5_MUX_PAD_IR: + sensor = &state->ir.sensor; + break; + case DS5_MUX_PAD_RGB: + sensor = &state->rgb.sensor; + break; + case DS5_MUX_PAD_IMU: + sensor = &state->imu.sensor; + break; + default: + sensor = state->mux.last_set; + ret = -EINVAL; + break; + } + ffmt = &sensor->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = fmt->format.code; + } + + fmt->format = *ffmt; + + dev_dbg(sd->dev, "%s(): %s sink pad/stream: %u/%u, \ + fmt code/size: 0x%x: %ux%u\n", + __func__, + sd->name, fmt->pad, fmt->stream, + ffmt->code, ffmt->width, ffmt->height); + + return ret; + } + + /* Store the format and propagate it to the source pad. */ + ffmt = v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); + if (!ffmt) { + dev_warn(sd->dev, "Failed to get %s format for mux pad %u \n", + sd->name, fmt->pad); + return -EINVAL; + } + *ffmt = fmt->format; + + dev_dbg(sd->dev, "%s(): %s source pad/stream: %u/%u, \ + fmt code/size: 0x%x: %ux%u\n", + __func__, + sd->name, fmt->pad, fmt->stream, + ffmt->code, ffmt->width, ffmt->height); + + /* propagate format to following sink pad */ + ret = v4l2_subdev_routing_find_opposite_end(&v4l2_state->routing, + fmt->pad, + fmt->stream, + &other_pad, + &other_stream); + if (ret) { + dev_warn(sd->dev, "ignored %s opposite stream %s end for mux pad %u\n", + sd->name, fmt->stream, fmt->pad); + return 0; + } + + ffmt = v4l2_subdev_state_get_format(v4l2_state, other_pad, other_stream); + if (!ffmt) { + dev_warn(sd->dev, "Failed to get %s format for mux pad %u \n", + sd->name, other_pad); + return -EINVAL; + } + *ffmt = fmt->format; + + dev_dbg(sd->dev, "%s(): %s sink pad/stream: %u/%u, \ + fmt code/size: 0x%x/%ux%u\n", + __func__, + sd->name, other_pad, other_stream, + ffmt->code, ffmt->width, ffmt->height); + + if (!(sd->entity.pads[other_pad].flags & MEDIA_PAD_FL_SINK)) + return 0; + + if (other_pad < 0 || other_pad >= sd->entity.num_pads) + return -EINVAL; + + remote_pad = media_pad_remote_pad_first(&state->mux.pads[other_pad]); + if (IS_ERR_OR_NULL(remote_pad)) { + dev_err(sd->dev, "Failed to find remote pad for %s %u", + sd->name, other_pad); + return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -ENODEV; + } + + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + if (!remote_sd) { + dev_err(sd->dev, "Failed to resolve entity %s to subdev", + remote_pad->entity->name); + return -ENODEV; + } + + //fmt->stream = 0; + dev_dbg(sd->dev, "%s(): %s remote pad/stream : %u/%u, \ + fmt code/size: 0x%x/%ux%u\n", + __func__, + remote_sd->name, remote_pad->index, fmt->stream, + ffmt->code, ffmt->width, ffmt->height); + + ret = v4l2_subdev_call(remote_sd, pad, set_fmt, v4l2_state, fmt); + if (ret) { + dev_warn(sd->dev, "ignored %s pad %u -> %s pad %u remote set_fmt (%d)\n", + sd->name, fmt->pad, remote_sd->name, remote_pad->index, ret); + return 0; + } #endif return ret; } @@ -4224,7 +4649,10 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, u32 pad = fmt->pad; int ret = 0; struct ds5_sensor *sensor = state->mux.last_set; -#ifdef CONFIG_VIDEO_INTEL_IPU6 + +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) pad = sensor->mux_pad; if (pad != DS5_MUX_PAD_EXTERNAL) ds5_s_state_pad(state, pad); @@ -4242,17 +4670,113 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, fmt->format = sensor->format; break; case DS5_MUX_PAD_EXTERNAL: - fmt->format = ds5_ffmts[pad]; + fmt->format = ds5_ffmts[pad]; break; default: return -EINVAL; } - dev_dbg(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad:%u size:%d-%d, code:0x%x field:%d, color:%d\n", __func__, fmt->pad, pad, fmt->format.width, fmt->format.height, fmt->format.code, fmt->format.field, fmt->format.colorspace); return ret; +#else + struct v4l2_mbus_framefmt *ffmt; + struct media_pad *remote_pad; + struct v4l2_subdev *remote_sd; + u32 other_pad, other_stream; + unsigned int i; + + dev_dbg(sd->dev, "%s(): Enter %s pad/stream %u/%u\n", + __func__, sd->name, + fmt->pad, fmt->stream); + + switch (fmt->pad) { + case DS5_MUX_PAD_DEPTH: + sensor = &state->depth.sensor; + break; + case DS5_MUX_PAD_IR: + sensor = &state->ir.sensor; + break; + case DS5_MUX_PAD_RGB: + sensor = &state->rgb.sensor; + break; + case DS5_MUX_PAD_IMU: + sensor = &state->imu.sensor; + break; + case DS5_MUX_PAD_EXTERNAL: + sensor = state->mux.last_set; + break; + default: + return -EINVAL; + } + //fallback get_fmt + fmt->format = sensor->format; + + dev_dbg(sd->dev, + "%s(): %s fallback fmt->pad:%d, fmt->stream:%u, size:%d-%d, code:0x%x field:%d, color:%d\n", + __func__, sd->name, fmt->pad, fmt->stream, + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.field, fmt->format.colorspace); + + mutex_lock(&state->mutex); + + if (IS_ERR_OR_NULL(fmt)) { + dev_err(sd->dev, "Invalid fmt %p", fmt); + ret = -EINVAL; + goto unlock; + } + + if (fmt->pad < 0 || fmt->pad >= sd->entity.num_pads) { + dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); + ret = -EINVAL; + goto unlock; + } + + // get_fmt callback on sink pad + if (!(sd->entity.pads[fmt->pad].flags & MEDIA_PAD_FL_SOURCE)) { + ffmt = v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); + if (!ffmt) { + dev_warn(sd->dev, "Failed to get %s format for mux pad %u \n", + sd->name, fmt->pad); + ret = -EINVAL; + goto unlock; + } + + fmt->format = *ffmt; + + dev_dbg(sd->dev, + "%s(): %s sink fmt->pad:%d, fmt->stream:%u, size:%d-%d, code:0x%x field:%d, color:%d\n", + __func__, sd->name, fmt->pad, fmt->stream, + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.field, fmt->format.colorspace); + goto unlock; + } + + // get_fmt routing propagate source to sink pad format by stream id + ffmt = v4l2_subdev_state_get_opposite_stream_format(v4l2_state, + fmt->pad, fmt->stream); + if (!ffmt) { + dev_warn(sd->dev, "Failed to get %s format for opposite mux pad %u \n", + sd->name, fmt->pad); + ret = -EINVAL; + goto unlock; + } + fmt->format = *ffmt; + dev_dbg(sd->dev, + "%s(): %s source fmt->pad:%d opposite fmt->stream:%u, size:%d-%d, code:0x%x field:%d, color:%d\n", + __func__, sd->name, fmt->pad, fmt->stream, + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.field, fmt->format.colorspace); + + unlock: + mutex_unlock(&state->mutex); + dev_dbg(sd->dev, "%s(): Exit %s pad/stream %u/%u\n", + __func__, sd->name, + fmt->pad, fmt->stream); + + return ret; +#endif } static int ds5_mux_g_frame_interval(struct v4l2_subdev *sd, @@ -4326,7 +4850,7 @@ static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, return 0; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #ifndef CONFIG_VIDEO_D4XX_SERDES static int d4xx_reset_oneshot(struct ds5 *state) { @@ -4404,7 +4928,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) } else { return -EINVAL; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) vc_id = ds5_state_to_vc(state); #endif #ifdef CONFIG_TEGRA_CAMERA_PLATFORM @@ -4420,7 +4944,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) if (on) { #ifdef CONFIG_VIDEO_D4XX_SERDES -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) // set manually, need to configure vc in pdata state->g_ctx.dst_vc = vc_id; #endif @@ -4483,12 +5007,14 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) #ifndef CONFIG_TEGRA_CAMERA_PLATFORM // reset for IPU6 streaming = 0; +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT for (i = 0; i < ARRAY_SIZE(d4xx_set_sub_stream); i++) { if (d4xx_set_sub_stream[i]) { streaming = 1; break; } } +#endif if (!streaming) { dev_warn(&state->client->dev, "max9296_reset_oneshot\n"); max9296_reset_oneshot(state->dser_dev); @@ -4498,7 +5024,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) dev_warn(&state->client->dev, "release pipe failed\n"); sensor->pipe_id = -1; #else -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) d4xx_reset_oneshot(state); #endif #endif @@ -4534,6 +5060,8 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) return ret; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *desc) { @@ -4553,6 +5081,151 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, return 0; } +#else +#define CSI2_SUBDEV_MAX_STREAM_ID 63 + +static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) +{ + unsigned int i; + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct ds5_sensor *sensor = NULL; + int ret = -EINVAL; + + if (NULL == sd) + return ret; + + if (pad < 0 || pad >= DS5_MUX_PAD_COUNT) + return -EINVAL; + + struct v4l2_subdev_state *v4l2_state = v4l2_subdev_lock_and_get_active_state(sd); + struct v4l2_subdev_route *route; + + dev_dbg(sd->dev, "%s(): name=%s pad=%u state=%p \n", + __func__, sd->name, pad, v4l2_state); + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + for_each_active_route(&v4l2_state->routing, route) { + if (route->source_pad != pad) + continue; + + if (route->sink_pad >= DS5_MUX_PAD_COUNT) { + ret = -EINVAL; + dev_err(sd->dev, "Found invalid route sink_pad!"); + goto out_unlock; + } + + // Get sensor based on sink pad from routing + switch (route->sink_pad) { + case DS5_MUX_PAD_DEPTH: + sensor = &state->depth.sensor; + break; + case DS5_MUX_PAD_IR: + sensor = &state->ir.sensor; + break; + case DS5_MUX_PAD_RGB: + sensor = &state->rgb.sensor; + break; + case DS5_MUX_PAD_IMU: + sensor = &state->imu.sensor; + break; + default: + dev_err(sd->dev, "Invalid sink pad %u for stream %u\n", + route->sink_pad, route->sink_stream); + ret = -EINVAL; + goto out_unlock; + } + + struct ds5_format *mf = sensor->config.format; + + dev_dbg(sd->dev, "sensor %s stream:%u pad:%u size:%dx%d code::0x%x vc:%u mbus:0x%x dt:0x%x\n", + sensor->sd.name, route->sink_stream, route->sink_pad, + sensor->format.width, sensor->format.height, sensor->format.code, + state->pad_to_vc[route->sink_pad], mf->mbus_code, mf->data_type); + + desc->entry[desc->num_entries].flags = 0; //V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + desc->entry[desc->num_entries].stream = route->sink_stream; + desc->entry[desc->num_entries].pixelcode = mf->mbus_code; + desc->entry[desc->num_entries].length = 0; + desc->entry[desc->num_entries].bus.csi2.vc = route->sink_stream; + desc->entry[desc->num_entries].bus.csi2.dt = mf->data_type; + desc->num_entries++; + } + ret = 0; + + out_unlock: + v4l2_subdev_unlock_state(v4l2_state); + return ret; +} + +static int ds5_mux_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + u32 pad, u64 streams_mask) +{ + + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + u32 sink_pad, sink_stream; + int ret, i; + + if (NULL == sd) + return -EINVAL; + + for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { + if (streams_mask & BIT_ULL(i)) + break; + } + + ret = v4l2_subdev_routing_find_opposite_end(&v4l2_state->routing, pad, i, + &sink_pad, &sink_stream); + if (ret) + return ret; + + dev_dbg(sd->dev, "%s(): pad/stream=%u/%u\n", + __func__, sink_pad, sink_stream); + + ds5_s_state_pad(state, sink_pad); + + ret = ds5_mux_s_stream(sd, 1); + + return ret; + +} +static int ds5_mux_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state, + u32 pad, u64 streams_mask) +{ + + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + u32 sink_pad, sink_stream; + int ret, i; + + if (NULL == sd) + return -EINVAL; + + + for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { + if (streams_mask & BIT_ULL(i)) + break; + } + + ret = v4l2_subdev_routing_find_opposite_end(&v4l2_state->routing, pad, i, + &sink_pad, &sink_stream); + if (ret) + return ret; + + dev_dbg(sd->dev, "%s(): pad/stream=%u/%u\n", + __func__, sink_pad, sink_stream); + + ds5_s_state_pad(state, sink_pad); + + ret = ds5_mux_s_stream(sd, 0); + + return 0; +} +#endif +#endif + static const struct v4l2_subdev_pad_ops ds5_mux_pad_ops = { .enum_mbus_code = ds5_mux_enum_mbus_code, .enum_frame_size = ds5_mux_enum_frame_size, @@ -4560,6 +5233,13 @@ static const struct v4l2_subdev_pad_ops ds5_mux_pad_ops = { .get_fmt = ds5_mux_get_fmt, .set_fmt = ds5_mux_set_fmt, .get_frame_desc = ds5_mux_get_frame_desc, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .set_routing = ds5_mux_set_routing, + .enable_streams = ds5_mux_enable_streams, + .disable_streams = ds5_mux_disable_streams, +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) .get_frame_interval = ds5_mux_g_frame_interval, .set_frame_interval = ds5_mux_s_frame_interval, @@ -4579,6 +5259,10 @@ static const struct v4l2_subdev_video_ops ds5_mux_video_ops = { .s_stream = ds5_mux_s_stream, }; +static const struct media_entity_operations ds5_mux_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + static const struct v4l2_subdev_ops ds5_mux_subdev_ops = { .core = &ds5_mux_core_ops, .pad = &ds5_mux_pad_ops, @@ -4627,16 +5311,111 @@ static void ds5_mux_unregistered(struct v4l2_subdev *sd) ds5_sensor_remove(&state->depth.sensor); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +static int ds5_mux_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *v4l2_state) +{ + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + int ret = 0; + + if (state->routing_initialized) + return 0; + state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; + if (!state->aggregated) { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + } else { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[4]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[5]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; + } + + struct v4l2_subdev_route ds5_mux_routes[] = { + //DEPTH + { + .sink_pad = DS5_MUX_PAD_DEPTH, + .sink_stream = state->pad_to_vc[DS5_MUX_PAD_DEPTH], + .source_pad = 0, + .source_stream = state->pad_to_vc[DS5_MUX_PAD_DEPTH], + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + //RGB + { + .sink_pad = DS5_MUX_PAD_RGB, + .sink_stream = state->pad_to_vc[DS5_MUX_PAD_RGB], + .source_pad = 0, + .source_stream = state->pad_to_vc[DS5_MUX_PAD_RGB], + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + //IR + { + .sink_pad = DS5_MUX_PAD_IR, + .sink_stream = state->pad_to_vc[DS5_MUX_PAD_IR], + .source_pad = 0, + .source_stream = state->pad_to_vc[DS5_MUX_PAD_IR], + .flags = 0, + }, + //IMU + { + .sink_pad = DS5_MUX_PAD_IMU, + .sink_stream = state->pad_to_vc[DS5_MUX_PAD_IMU], + .source_pad = 0, + .source_stream = state->pad_to_vc[DS5_MUX_PAD_IMU], + .flags = 0, + }, + }; + + struct v4l2_subdev_krouting ds5_mux_routing = { + .num_routes = ARRAY_SIZE(ds5_mux_routes), + .routes = ds5_mux_routes, + }; + + ret = _ds5_mux_set_routing(sd, v4l2_state, &ds5_mux_routing); + if (ret < 0) + dev_warn(sd->dev, "%s():%d: %s initial .num_routes=%u ignored (sd_state %p)\n", + __func__, __LINE__, sd->name, ds5_mux_routing.num_routes, v4l2_state); + else + dev_dbg(sd->dev, "%s():%d: %s initial .num_routes=%u applied (sd_state %p)\n", + __func__, __LINE__, sd->name, ds5_mux_routing.num_routes, v4l2_state); + return 0; +} +#endif +#endif + static const struct v4l2_subdev_internal_ops ds5_mux_internal_ops = { .open = ds5_mux_open, .close = ds5_mux_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .init_state = ds5_mux_init_state, +#endif +#endif .registered = ds5_mux_registered, .unregistered = ds5_mux_unregistered, }; static int ds5_mux_register(struct i2c_client *c, struct ds5 *state) { - return v4l2_async_register_subdev(&state->mux.sd.subdev); + struct v4l2_subdev *sd = &state->mux.sd.subdev; + int ret; + +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(sd->dev, "failed to init v4l2 subdev stream: %d\n", ret); + goto subdev_end; + } +#endif + ret = v4l2_async_register_subdev(sd); + if (ret) + dev_err(sd->dev, "v4l register failed: %d", ret); + +subdev_end: + return ret; } static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) @@ -4703,7 +5482,12 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) suffix += 6; snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); #endif +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#else + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + entity->ops = &ds5_mux_entity_ops; +#endif entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; @@ -5442,7 +6226,7 @@ static int ds5_chrdev_remove(struct ds5 *state) return 0; } -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT static void ds5_substream_init(struct ds5 *state) { int i, j; @@ -5659,7 +6443,7 @@ static const struct attribute_group ds5_attr_group = { }; #endif -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #define NR_DESER 4 #ifndef CONFIG_VIDEO_D4XX_SERDES @@ -5819,7 +6603,7 @@ static int ds5_probe(struct i2c_client *c) } #endif -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #ifndef CONFIG_VIDEO_D4XX_SERDES state->regmap_max9296 = devm_regmap_init_i2c(c, &ds5_regmap_max9296); if (IS_ERR(state->regmap_max9296)) { @@ -5922,7 +6706,7 @@ static int ds5_probe(struct i2c_client *c) /* create the sysfs file group */ err = sysfs_create_group(&state->client->dev.kobj, &ds5_attr_group); #endif -#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT ds5_substream_init(state); #endif return 0; @@ -6127,4 +6911,4 @@ MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Florent Pirou "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("1.0.2.20"); +MODULE_VERSION(DRIVER_VERSION_SUFFIX); From 2be198ee2d035ca183d2e62b3f6692acdba70b07 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 23 Dec 2025 07:17:02 -0700 Subject: [PATCH 36/72] d4xx-max9295: add gmsl2 link status register checker Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx-max9295.c | 262 ++++++++++++++++++- include/media/i2c/d4xx-max9295.h | 1 + 2 files changed, 250 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c index fb58f984..b16c63cc 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c @@ -50,6 +50,14 @@ #define MAX9295_DEV_ADDR 0x00 +#define MAX9295_LINK_STATUS_ADDR 0x13 +#define MAX9295_LINK_LOCK_BIT 0x08 + +#define MAX9295_PIPE_X_STATUS_ADDR 0x102 +#define MAX9295_PIPE_Y_STATUS_ADDR 0x10A +#define MAX9295_PIPE_Z_STATUS_ADDR 0x112 +#define MAX9295_PIPE_U_STATUS_ADDR 0x11A + #define MAX9295_STREAM_PIPE_UNUSED 0x22 #define MAX9295_CSI_MODE_1X4 0x00 #define MAX9295_CSI_MODE_2X2 0x03 @@ -104,6 +112,41 @@ #define MAX9295_MAX_PIPES 0x4 +/* Log all register writes */ +#define MAX9295_WRITE_REG(map, reg, val) \ +({ \ + int __ret; \ + struct regmap *__map = (map); \ + unsigned int __reg = (reg); \ + unsigned int __val = (val); \ + dev_dbg(regmap_get_device(__map), "REG_WRITE: [0x%04X] = 0x%02X", __reg, __val); \ + __ret = regmap_write(__map, __reg, __val); \ + if (__ret) \ + dev_err(regmap_get_device(__map), "REG_WRITE FAILED: [0x%04X] = 0x%02X, ret=%d", __reg, __val, __ret); \ + __ret; \ +}) + +/* Log register update_bits (read-modify-write) */ +#define MAX9295_UPDATE_BITS(map, reg, mask, val) \ +({ \ + int __ret; \ + struct regmap *__map = (map); \ + unsigned int __reg = (reg); \ + unsigned int __mask = (mask); \ + unsigned int __val = (val); \ + unsigned int __old_val = 0; \ + unsigned int __new_val = 0; \ + regmap_read(__map, __reg, &__old_val); \ + __new_val = (__old_val & ~__mask) | (__val & __mask); \ + dev_dbg(regmap_get_device(__map), "REG_UPDATE_BITS: [0x%04X] mask=0x%02X, old=0x%02X, new=0x%02X (val=0x%02X)", \ + __reg, __mask, __old_val, __new_val, __val); \ + __ret = regmap_update_bits(__map, __reg, __mask, __val); \ + if (__ret) \ + dev_err(regmap_get_device(__map), "REG_UPDATE_BITS FAILED: [0x%04X] mask=0x%02X val=0x%02X, ret=%d", \ + __reg, __mask, __val, __ret); \ + __ret; \ +}) + struct max9295_client_ctx { struct gmsl_link_ctx *g_ctx; bool st_done; @@ -129,12 +172,27 @@ struct map_ctx { u8 st_id; }; +static int max9295_read_reg(struct device *dev, + u16 addr, unsigned int *val) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err; + + err = regmap_read(priv->regmap, addr, val); + if (err) + dev_err(dev, + "%s:i2c read failed, 0x%x\n", + __func__, addr); + + return err; +} + static int max9295_write_reg(struct device *dev, u16 addr, u8 val) { struct max9295 *priv = dev_get_drvdata(dev); int err; - err = regmap_write(priv->regmap, addr, val); + err = MAX9295_WRITE_REG(priv->regmap, addr, val); if (err) dev_err(dev, "%s:i2c write failed, 0x%x = %x\n", __func__, addr, val); @@ -145,6 +203,62 @@ static int max9295_write_reg(struct device *dev, u16 addr, u8 val) return err; } +int max9295_check_status(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + /* Double-Check GMSL link status after stream start configuration */ + { + unsigned int link_status = 0; + max9295_read_reg(dev, MAX9295_LINK_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Link status: 0x%02x (LOCK=%d, bit0-5: %s%s%s%s)\n", + __func__, link_status, !!(link_status & MAX9295_LINK_LOCK_BIT), + (link_status & 0x02) ? "CMU_LOCKED " : "", + (link_status & 0x04) ? "ERROR " : "", + (link_status & MAX9295_LINK_LOCK_BIT) ? "LOCKED " : "", + (link_status & 0x30) == 0x01? "LINKA " : ""); + max9295_read_reg(dev, MAX9295_PIPE_X_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe X Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Y_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Y Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Z_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Z Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_U_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe U Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + } + + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9295_check_status); + int max9295_setup_streaming(struct device *dev) { struct max9295 *priv = dev_get_drvdata(dev); @@ -320,6 +434,7 @@ int max9295_setup_control(struct device *dev) 0xC8, 0x02, 0x09, 0xCA, 0x02, 0x0a, 0xCC, 0x02, 0x0b, + //0xD6, 0x00, 0x0c, /* 0x6b << 1 = 0xd6 */ }; mutex_lock(&priv->lock); @@ -344,9 +459,10 @@ int max9295_setup_control(struct device *dev) struct i2c_client *c = to_i2c_client(dev); int addr = c->addr; dev_info(dev, "%s: update address reassignment 0x%x->0x%x\n", __func__,priv->def_addr, g_ctx->ser_reg); + c->addr = priv->def_addr; max9295_write_reg(dev, - MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); + MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); c->addr = addr; prim_priv__ = priv; } else { @@ -365,7 +481,7 @@ int max9295_setup_control(struct device *dev) } /* delay to settle link */ - msleep(100); + msleep(150); for (i = 0; i < ARRAY_SIZE(addr_offset); i += 3) { if ((g_ctx->ser_reg << 1) == addr_offset[i]) { @@ -397,12 +513,43 @@ int max9295_setup_control(struct device *dev) if (prim_priv__) prim_priv__->pst2_ref++; - max9295_write_reg(dev, MAX9295_I2C4_ADDR, (g_ctx->sdev_reg << 1)); - max9295_write_reg(dev, MAX9295_I2C5_ADDR, (g_ctx->sdev_def << 1)); - msleep(100); - max9295_write_reg(dev, MAX9295_SRC_PWDN_ADDR, MAX9295_PWDN_GPIO); - max9295_write_reg(dev, MAX9295_SRC_CTRL_ADDR, MAX9295_RESET_SRC); - max9295_write_reg(dev, MAX9295_SRC_OUT_RCLK_ADDR, MAX9295_SRC_RCLK); + dev_dbg(dev, "%s: configuring I2C4=0x%02x (sdev_reg=0x%02x), I2C5=0x%02x (sdev_def=0x%02x)\n", + __func__, (g_ctx->sdev_reg << 1), g_ctx->sdev_reg, + (g_ctx->sdev_def << 1), g_ctx->sdev_def); + err = max9295_write_reg(dev, MAX9295_I2C4_ADDR, (g_ctx->sdev_reg << 1)); + if (err) + dev_err(dev, "%s: ERROR: failed to write I2C4_ADDR, err=%d\n", __func__, err); + err = max9295_write_reg(dev, MAX9295_I2C5_ADDR, (g_ctx->sdev_def << 1)); + if (err) + dev_err(dev, "%s: ERROR: failed to write I2C5_ADDR, err=%d\n", __func__, err); + msleep(150); + + dev_dbg(dev, "%s: configuring sensor power and reset\n", __func__); + err = max9295_write_reg(dev, MAX9295_SRC_PWDN_ADDR, MAX9295_PWDN_GPIO); + if (err) + dev_err(dev, "%s: ERROR: failed to write SRC_PWDN, err=%d\n", __func__, err); + err = max9295_write_reg(dev, MAX9295_SRC_CTRL_ADDR, MAX9295_RESET_SRC); + if (err) + dev_err(dev, "%s: ERROR: failed to write SRC_CTRL, err=%d\n", __func__, err); + err = max9295_write_reg(dev, MAX9295_SRC_OUT_RCLK_ADDR, MAX9295_SRC_RCLK); + if (err) + dev_err(dev, "%s: ERROR: failed to write SRC_OUT_RCLK, err=%d\n", __func__, err); + + /* Wait for sensor to power up and stabilize */ + msleep(200); + dev_dbg(dev, "%s: sensor power/reset configuration complete\n", __func__); + + /* Double-Check GMSL link status after stream start configuration */ + { + unsigned int link_status = 0; + max9295_read_reg(dev, MAX9295_LINK_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Link status: 0x%02x (LOCK=%d, bit0-5: %s%s%s%s)\n", + __func__, link_status, !!(link_status & MAX9295_LINK_LOCK_BIT), + (link_status & 0x02) ? "CMU_LOCKED " : "", + (link_status & 0x04) ? "ERROR " : "", + (link_status & MAX9295_LINK_LOCK_BIT) ? "LOCKED " : "", + (link_status & 0x30) == 0x01? "LINKA " : ""); + } g_ctx->serdev_found = true; @@ -696,6 +843,50 @@ int max9295_init_settings(struct device *dev) GMSL_CSI_DT_EMBED, i); } + /* Double-Check GMSL link status after stream start configuration */ + { + unsigned int link_status = 0; + max9295_read_reg(dev, MAX9295_LINK_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Link status: 0x%02x (LOCK=%d, bit0-5: %s%s%s%s)\n", + __func__, link_status, !!(link_status & MAX9295_LINK_LOCK_BIT), + (link_status & 0x02) ? "CMU_LOCKED " : "", + (link_status & 0x04) ? "ERROR " : "", + (link_status & MAX9295_LINK_LOCK_BIT) ? "LOCKED " : "", + (link_status & 0x30) == 0x01? "LINKA " : ""); + max9295_read_reg(dev, MAX9295_PIPE_X_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe X Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Y_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Y Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Z_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Z Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_U_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe U Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + } + mutex_unlock(&priv->lock); return err; @@ -711,11 +902,11 @@ int max9295_set_mfp(struct device *dev, int pin, int val) if (pin > 10) return -EINVAL; - err = regmap_update_bits(map, 0x2BE + (pin * 3), + err = MAX9295_UPDATE_BITS(map, 0x2BE + (pin * 3), (1<<4 | 1<<2 | 1<<0), ((val ? 1<<4 : 0) | 0<<2 | 0<<0) ); - err |= regmap_update_bits(map, 0x2BF + (pin * 3), + err |= MAX9295_UPDATE_BITS(map, 0x2BF + (pin * 3), 3<<6|1<<5, 1<<6|1<<5); @@ -748,6 +939,50 @@ int max9295_set_pipe(struct device *dev, int pipe_id, err = __max9295_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); } + + /* Double-Check GMSL link status after stream start configuration */ + { + unsigned int link_status = 0; + max9295_read_reg(dev, MAX9295_LINK_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Link status: 0x%02x (LOCK=%d, bit0-5: %s%s%s%s)\n", + __func__, link_status, !!(link_status & MAX9295_LINK_LOCK_BIT), + (link_status & 0x02) ? "CMU_LOCKED " : "", + (link_status & 0x04) ? "ERROR " : "", + (link_status & MAX9295_LINK_LOCK_BIT) ? "LOCKED " : "", + (link_status & 0x30) == 0x01? "LINKA " : ""); + max9295_read_reg(dev, MAX9295_PIPE_X_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe X Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Y_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Y Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_Z_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe Z Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + max9295_read_reg(dev, MAX9295_PIPE_U_STATUS_ADDR, &link_status); + dev_dbg(dev, "%s: Pipe U Link status: 0x%02x (bit3-7: %s%s%s%s%s)\n", + __func__, link_status, + (link_status & 0x04) ? "LIM_HEART " : "", + (link_status & 0x10) ? "FIFO_WARN " : "", + (link_status & 0x20) ? "OVERFLOW " : "", + (link_status & 0x40) ? "DRIFT_ERR " : "", + (link_status & 0x80) ? "PCLKDET " : ""); + } mutex_unlock(&priv->lock); return err; @@ -755,8 +990,7 @@ int max9295_set_pipe(struct device *dev, int pipe_id, EXPORT_SYMBOL(max9295_set_pipe); #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) -static int max9295_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max9295_probe(struct i2c_client *client, const struct i2c_device_id *id) #else static int max9295_probe(struct i2c_client *client) #endif @@ -887,4 +1121,6 @@ MODULE_DESCRIPTION("GMSL Serializer driver max9295"); MODULE_AUTHOR("Sudhir Vyas "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) MODULE_VERSION(DRIVER_VERSION_SUFFIX); +#endif diff --git a/include/media/i2c/d4xx-max9295.h b/include/media/i2c/d4xx-max9295.h index 71c2106a..2942ddbd 100644 --- a/include/media/i2c/d4xx-max9295.h +++ b/include/media/i2c/d4xx-max9295.h @@ -110,5 +110,6 @@ int max9295_init_settings(struct device *dev); /** @} */ int max9295_set_mfp(struct device *dev, int pin, int val); +int max9295_check_status(struct device *dev); #endif /* __MAX9295_H__ */ From 10e1038f277d0db1fc4db69741eb67202a0051c5 Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Fri, 3 Oct 2025 12:23:04 -0700 Subject: [PATCH 37/72] d4xx-compat : initial max9672x support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * d4xx : add max96724 Deserializer support * d4xx : add max96712/max96722 Deserializer support +------------------------------------------------------------------------+ | MAX967xx Deserializer | +------------------------------------------------------------------------+ | GMSL2 Input Side MIPI CSI-2 Output Side | | ___________________ ______________________ | | | | Link A ─┬─ Pipe X ──┐ | | ├─ Pipe Y | | | ├─ Pipe Z | VIDEO_PIPE_SEL Video Pipe Mapping MIPI PHY | | └─ Pipe U | (0x00F0/0x00F1) (0x090B-0x09CB) Output | | | | | Link B ─┬─ Pipe X ──┼─────────────────► Pipe 0 ──► VC0 ──┐ | | ├─ Pipe Y | | | | ├─ Pipe Z | Pipe 1 ──► VC1 ──┤ | | └─ Pipe U | | ──► CPHY0+1 ──► IPU7 CPHY0+PHY1 | | Pipe 2 ──► VC2 ──┤ x4-lanes | | Link C ─┬─ Pipe X ──┤ | (csi2-0) | | ├─ Pipe Y | Pipe 3 ──► VC3 ──┘ | | ├─ Pipe Z | | | └─ Pipe U | | | | | | Link D ─┬─ Pipe X ──┘ | | ├─ Pipe Y | | ├─ Pipe Z | | └─ Pipe U | +------------------------------------------------------------------------+ Signed-off-by: Florent Pirou d4xx-compat: maitain backward compatible d4xx Signed-off-by: florent pirou --- Makefile | 1 + drivers/media/i2c/d4xx-compat/Makefile | 1 + drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 2757 +++++++++++++++++ drivers/media/i2c/d4xx-compat/d4xx.c | 474 ++- include/media/gmsl-link.h | 6 + include/media/i2c/d4xx-max96724.h | 135 + include/media/i2c/d4xx_pdata.h | 17 +- 7 files changed, 3335 insertions(+), 56 deletions(-) create mode 100644 drivers/media/i2c/d4xx-compat/d4xx-max96724.c create mode 100644 include/media/i2c/d4xx-max96724.h diff --git a/Makefile b/Makefile index bab93cd4..a04f2f60 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ export CONFIG_INTEL_IPU_ACPI=m export CONFIG_VIDEO_D4XX_MAX9295 = m export CONFIG_VIDEO_D4XX_MAX9296 = m export CONFIG_VIDEO_D4XX_COMPAT=m +export CONFIG_VIDEO_D4XX_MAX96724 = m # Define config macros for conditional compilation in ipu-acpi.c # IS_ENABLED() checks for CONFIG_XXX or CONFIG_XXX_MODULE diff --git a/drivers/media/i2c/d4xx-compat/Makefile b/drivers/media/i2c/d4xx-compat/Makefile index 8875a1a2..ee5094db 100644 --- a/drivers/media/i2c/d4xx-compat/Makefile +++ b/drivers/media/i2c/d4xx-compat/Makefile @@ -8,4 +8,5 @@ ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES -DCONFIG_SYSFS -DPLATFORM_AXIOMTEK obj-$(CONFIG_VIDEO_D4XX_MAX9295) += d4xx-max9295.o obj-$(CONFIG_VIDEO_D4XX_MAX9296) += d4xx-max9296.o +obj-$(CONFIG_VIDEO_D4XX_MAX96724) += d4xx-max96724.o obj-$(CONFIG_VIDEO_D4XX_COMPAT) += d4xx.o diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c new file mode 100644 index 00000000..cdf415fb --- /dev/null +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -0,0 +1,2757 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2024, INTEL CORPORATION. All rights reserved. + * + * max96724.c - max96724 GMSL Deserializer driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include + + +/* MAX96724 register specifics */ +#define MAX96724_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) +#define MAX96724_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) +#define MAX96724_FIELD_PREP(_mask, _val) \ + ({ \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + + +#define MAX96724_REG3_ADDR 0x0003 /* I2C Remote Control Channel Enable */ +#define MAX96724_REG10_ADDR 0x0010 /* PHY A/B RX/TX rate control */ +#define MAX96724_REG11_ADDR 0x0011 /* PHY C/D RX/TX rate control */ +#define MAX96724_PWR1_ADDR 0x0013 /* RESET_ALL */ +#define MAX96724_REG14_ADDR 0x0014 +#define MAX96724_REG15_ADDR 0x0015 +#define MAX96724_REG16_ADDR 0x0016 +#define MAX96724_REG17_ADDR 0x0017 /* Internal regulator control */ +#define MAX96724_REG18_ADDR 0x0018 /* RESET_ONESHOT control */ +#define MAX96724_REG19_ADDR 0x0019 /* Internal regulator control */ + +#define MAX96724_REG_POC_CTRL1 MAX96724_REG14_ADDR +#define MAX96724_REG_POC_CTRL2 MAX96724_REG15_ADDR +#define MAX96724_REG_POC_CTRL3 MAX96724_REG16_ADDR + +#define MAX96724_REG17_CTRL_EN_FIELD BIT(2) +#define MAX96724_REG19_CTRL_EN_FIELD BIT(4) + +#define MAX96724_LINK_CTRL_ADDR 0x0006 +#define MAX96724_LINK_CTRL_GMSL_FIELD(link) BIT(link) << 4 +#define MAX96724_LINK_CTRL_EN_FIELD(link) BIT(link) + +#define MAX96724_RESET_CTRL_ADDR MAX96724_REG18_ADDR +#define MAX96724_RESET_CTRL_FIELD(link) BIT(link) +#define MAX96724_RESET_LINK_FIELD(link) BIT(link) << 4 + +#define MAX967XX_DEV_ID_ADDR 0x000D +#define MAX96712_DEV_REV_ID 0xA0 +#define MAX96724_DEV_REV_ID 0xA2 + +#define MAX967XX_DEV_REV_ADDR 0x004C +#define MAX967XX_DEV_REV_FIELD GENMASK(3, 0) + +/* Note that CSIs and pipes overlap: + * 0x901 through 0x90A and 0x933 through 0x934 are CSIs, repeated every 0x40 up to 4 times + * 0x90B through 0x932 are pipes, repeated every 0x40 up to 4 times + */ +#define MAX96724_MIPI_TX0 0x0900 +#define MAX96724_MIPI_TX(pipe) (MAX96724_MIPI_TX0 + ((pipe) * 0x40)) + +#define MAX96724_MIPI_TX_DESKEW_INIT(csi) (MAX96724_MIPI_TX(csi) + 0x03) +#define MAX96724_MIPI_TX_LANE_CNT(csi) (MAX96724_MIPI_TX(csi) + 0x0A) +#define MAX96724_MIPI_TX_ALT_MEM(csi) (MAX96724_MIPI_TX(csi) + 0x33) + +#define MAX96724_MAP_EN_L(pipe) (MAX96724_MIPI_TX(pipe) + 0x0B) +#define MAX96724_MAP_EN_H(pipe) (MAX96724_MIPI_TX(pipe) + 0x0C) +#define MAX96724_MAP_SRCDST_EN_FIELD(map) BIT(map) + +#define MAX96724_MAP_SRC_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2)) +#define MAX96724_MAP_DST_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) + +#define MAX96724_MAP_PHY_DEST(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x2D + ((map) / 4)) +#define MAX96724_MAP_DPHY_DEST_FIELD(map) GENMASK(1, 0) << MAX96724_FLD_OFS(map, 2, 4) + +#define MAX96724_MIPI_TX_EXT0 0x0800 +#define MAX96724_MIPI_TX_EXT(pipe) (MAX96724_MIPI_TX_EXT0 + ((pipe) * 0x10)) +#define MAX96724_MAP_SRCDST_H(pipe, map) (MAX96724_MIPI_TX_EXT(pipe) + (map)) + +/* Video Pipe MIPI Lanes deskew init */ +#define MAX96724_DESKEW_INIT0_ADDR MAX96724_MIPI_TX_DESKEW_INIT(0) +#define MAX96724_DESKEW_INIT1_ADDR MAX96724_MIPI_TX_DESKEW_INIT(1) +#define MAX96724_DESKEW_INIT2_ADDR MAX96724_MIPI_TX_DESKEW_INIT(2) +#define MAX96724_DESKEW_INIT3_ADDR MAX96724_MIPI_TX_DESKEW_INIT(3) +#define MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD BIT(7) +#define MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD GENMASK(2, 0) + +/* Video Pipe MIPI Lanes Controls */ +#define MAX96724_LANE_CTRL0_ADDR MAX96724_MIPI_TX_LANE_CNT(0) +#define MAX96724_LANE_CTRL1_ADDR MAX96724_MIPI_TX_LANE_CNT(1) +#define MAX96724_LANE_CTRL2_ADDR MAX96724_MIPI_TX_LANE_CNT(2) +#define MAX96724_LANE_CTRL3_ADDR MAX96724_MIPI_TX_LANE_CNT(3) +#define MAX96724_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) +#define MAX96724_MIPI_TX_CPHY_EN_FIELD BIT(5) +#define MAX96724_MIPI_TX_VCX_EN_FIELD BIT(4) +#define MAX96724_MIPI_TX_WAKEUP_CYC_FIELD GENMASK(2, 0) + +#define MAX96724_LANE_CTRL_MAP(num_lanes) \ + (((num_lanes) << 6) & MAX96724_MIPI_TX_LANE_CNT_FIELD) + +#define MAX96724_CSI_DPHY 0x0 +#define MAX96724_CSI_CPHY 0x1 +#define MAX96724_CSI_MODE_1X4 0x0 +#define MAX96724_CSI_MODE_4X2 0x1 +#define MAX96724_CSI_MODE_2X4 0x4 + +/* Video Pipe MIPI Mapping Enable */ +#define MAX96724_PIPE_X_MIPI_EN MAX96724_MAP_EN_L(0) +#define MAX96724_MAP_EN_FIELD GENMASK(7, 0) + +/* Video Pipe X Cross Mapping baseline */ +#define MAX96724_PIPE_X_EN_ADDR MAX96724_MAP_EN_L(0) +#define MAX96724_PIPE_X_SRC_0_MAP_ADDR MAX96724_MAP_SRC_L(0,0) +#define MAX96724_PIPE_X_DST_0_MAP_ADDR MAX96724_MAP_DST_L(0,0) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR MAX96724_MAP_SRCDST_H(0,0) +#define MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR MAX96724_MAP_PHY_DEST(0,0) +#define MAX96724_PIPE_X_SRC_1_MAP_ADDR MAX96724_MAP_SRC_L(0,1) +#define MAX96724_PIPE_X_DST_1_MAP_ADDR MAX96724_MAP_DST_L(0,1) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR MAX96724_MAP_SRCDST_H(0,1) +#define MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR MAX96724_MAP_PHY_DEST(0,1) +#define MAX96724_PIPE_X_SRC_2_MAP_ADDR MAX96724_MAP_SRC_L(0,2) +#define MAX96724_PIPE_X_DST_2_MAP_ADDR MAX96724_MAP_DST_L(0,2) +#define MAX96724_PIPE_X_PHY_DEST_2_MAP_ADDR MAX96724_MAP_PHY_DEST(0,2) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR MAX96724_MAP_SRCDST_H(0,2) +#define MAX96724_PIPE_X_SRC_3_MAP_ADDR MAX96724_MAP_SRC_L(0,3) +#define MAX96724_PIPE_X_DST_3_MAP_ADDR MAX96724_MAP_DST_L(0,3) +#define MAX96724_PIPE_X_PHY_DEST_3_MAP_ADDR MAX96724_MAP_PHY_DEST(0,3) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR MAX96724_MAP_SRCDST_H(0,3) +#define MAX96724_MAP_SRC_L_VC_FIELD GENMASK(7, 6) +#define MAX96724_MAP_SRC_L_DT_FIELD GENMASK(5, 0) +#define MAX96724_MAP_DST_L_VC_FIELD GENMASK(7, 6) +#define MAX96724_MAP_DST_L_DT_FIELD GENMASK(5, 0) + +/* Video Pipe X to alternative memory Mapping */ +#define MAX96724_PIPE_X_ALT_MEM_ADDR MAX96724_MIPI_TX_ALT_MEM(0) +#define MAX96724_MIPI_TX_ALT_MEM_FIELD GENMASK(4, 0) +#define MAX96724_MIPI_TX_ALT_MEM_8BPP BIT(1) +#define MAX96724_MIPI_TX_ALT_MEM_10BPP BIT(2) +#define MAX96724_MIPI_TX_ALT_MEM_12BPP BIT(0) +#define MAX96724_MIPI_TX_ALT_MEM_24BPP BIT(3) +#define MAX96724_MIPI_TX_ALT2_MEM_8BPP BIT(4) + +/* Video Pipe X to PHY Mapping */ +#define MAX96724_PIPE_X_SRCDST_0_MAP_ADDR MAX96724_MAP_SRCDST_H(0,0) +#define MAX96724_PIPE_X_SRCDST_1_MAP_ADDR MAX96724_MAP_SRCDST_H(0,1) +#define MAX96724_PIPE_X_SRCDST_2_MAP_ADDR MAX96724_MAP_SRCDST_H(0,2) +#define MAX96724_PIPE_X_SRCDST_3_MAP_ADDR MAX96724_MAP_SRCDST_H(0,3) +#define MAX96724_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) +#define MAX96724_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) + +#define MAX96724_VID_RX0_ADDR 0x0100 +#define MAX96724_VID_RX0(pipe) (MAX96724_VID_RX0_ADDR + (0x12 * (pipe))) +#define MAX96724_VID_RX6_ADDR 0x0106 +#define MAX96724_VID_RX6(pipe) (MAX96724_VID_RX6_ADDR + (0x12 * (pipe))) +#define MAX96724_VID_RX8_ADDR 0x0108 +#define MAX96724_VID_STATUS_ADDR(pipe) (MAX96724_VID_RX8_ADDR + (0x12 * (pipe))) +#define MAX96724_VID_LOCK_BIT BIT(6) + +#define MAX96724_PIPE_DE_STATUS_ADDR 0x11F0 +#define MAX96724_PIPE_HS_STATUS_ADDR 0x11F1 +#define MAX96724_PIPE_VS_STATUS_ADDR 0x11F2 + +#define MAX96724_PIPE_X_ST_SEL_ADDR 0x0050 + +/* Video Pipe Input Source Selection + * This tells MAX96724 which PHY (Link) feeds which Video Pipe. + * Without this, video data cannot flow from GMSL links to CSI-2 output. + * + * REG 0x00F0: Video Pipe 0/1 input selection + * Bit[7:6] = 01: Pipe 1 from GMSL B link + * Bit[5:4] = 10: Pipe 1 uses internal Pipe X + * Bit[3:2] = 00: Pipe 0 from GMSL A link + * Bit[1:0] = 10: Pipe 0 uses internal Pipe X + * Value: 0xF0 + * + * REG 0x00F1: Video Pipe 2/3 input selection + * Bit[7:6] = 11: Pipe 3 from GMSL D link + * Bit[5:4] = 10: Pipe 3 uses internal Pipe X + * Bit[3:2] = 10: Pipe 2 from GMSL C link + * Bit[1:0] = 10: Pipe 2 uses internal Pipe X + * Value: 0xEA + * + * REG 0x00F4: Enable all Video Pipes + * Bit[3:0] = 1111: Enable Pipe 3/2/1/0 + * Value: 0x0F + */ +#define MAX96724_VIDEO_PIPE_SEL_0_ADDR 0x00F0 /* Pipe 0/1 input: GMSL A/B */ +#define MAX96724_VIDEO_PIPE_SEL_1_ADDR 0x00F1 /* Pipe 2/3 input: GMSL C/D */ +#define MAX96724_VIDEO_PIPE_EN_ADDR 0x00F4 /* Enable Video Pipes */ +#define MAX96724_VIDEO_PIPE_SEL(pipe) (MAX96724_VIDEO_PIPE_SEL_0_ADDR + ((pipe) / 2)) +#define MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(link) GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2) +#define MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(link) GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0) +#define MAX96724_VIDEO_PIPE_EN(pipe) (MAX96724_VIDEO_PIPE_EN_ADDR) +#define MAX96724_VIDEO_PIPE_EN_FIELD(pipe) BIT(pipe) +#define MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD BIT(4) +#define MAX96724_VIDEO_PIPE_LEGACY_MODE 0U +#define MAX96724_VIDEO_PIPE_SRCMAP_MODE 1U + +/* MIPI CSI-2 PHY Configuration */ +#define MAX96724_PHY_RATE_CTRL_ADDR MAX96724_REG10_ADDR +#define MAX96724_PHY_RATE_CTRL(link) (MAX96724_PHY_RATE_CTRL_ADDR + ((link) / 2)) +#define MAX96724_PHY_RATE_CTRL_TX_FIELD(link) GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2) +#define MAX96724_PHY_RATE_CTRL_RX_FIELD(link) GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0) +#define MAX96724_PHY_RATE_CTRL_TX_6GBPS 0x10 +#define MAX96724_PHY_RATE_CTRL_TX_3GBPS 0x01 + +#define MAX96724_BACKTOP1_ADDR 0x0400 +#define MAX96724_DPLL_STATUS_ADDR MAX96724_BACKTOP1_ADDR +#define MAX96724_DPLL_STATUS_FIELD(phy) BIT(phy) << 4 + +#define MAX96724_BACKTOP22_ADDR 0x0415 +#define MAX96724_DPLL_FREQ(phy) (MAX96724_BACKTOP22_ADDR + ((phy) * 3)) +#define MAX96724_PHY0_CLK_ADDR MAX96724_DPLL_FREQ(0) +#define MAX96724_PHY1_CLK_ADDR MAX96724_DPLL_FREQ(1) +#define MAX96724_PHY2_CLK_ADDR MAX96724_DPLL_FREQ(3) +#define MAX96724_PHY3_CLK_ADDR MAX96724_DPLL_FREQ(3) +#define MAX96724_DPLL_FREQ_FIELD GENMASK(4, 0) + +#define MAX96724_BACKTOP12_ADDR 0x040B +#define MAX96724_CSI_OUT_EN_ADDR MAX96724_BACKTOP12_ADDR +#define MAX96724_CSI_OUT_EN_FIELD BIT(1) + +#define MAX96724_DPLL_RESET(phy) (0x1C00 + ((phy) * 0x100)) +#define MAX96724_DPLL_RESET_SOFT_RST_FIELD BIT(0) + +#define MAX96724_MIPI_PHY0 0x08A0 +#define MAX96724_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) +#define MAX96724_MIPI_PHY0_CLK_SEL_EN_FIELD(csi) BIT((csi) / 2) +#define MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD BIT(7) + +#define MAX96724_MIPI_PHY2 0x08A2 +#define MAX96724_MIPI_PHY_ENABLE MAX96724_MIPI_PHY2 +#define MAX96724_PWDN_PHYS_ADDR MAX96724_MIPI_PHY_ENABLE +#define MAX96724_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) + +#define MAX96724_MIPI_PHY3 0x08A3 +#define MAX96724_MIPI_PHY_LANE_MAP(csi) (MAX96724_MIPI_PHY3 + (csi) / 2) +#define MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ + (GENMASK(1, 0) << (MAX96724_FLD_OFS(csi, 4, 2) + MAX96724_FLD_OFS(lane, 2, 2))) + +#define MAX96724_MIPI_PHY13 0x08AD +#define MAX96724_CPHY_PREAMBLE_ADDR MAX96724_MIPI_PHY13 +#define MAX96724_CPHY_PREAMBLE_FIELD GENMASK(5, 0) + +#define MAX96724_MIPI_PHY14 0x08AE +#define MAX96724_CPHY_PREP_ADDR MAX96724_MIPI_PHY14 +#define MAX96724_CPHY_PREP_LENGTH_FIELD GENMASK(1, 0) +#define MAX96724_CPHY_POST_TIMING_FIELD GENMASK(6, 2) + +/* GPIO Configuration Registers */ + +#define MAX96724_GPIO_REG(n) (0x0300 + (3 * n)) +#define MAX96724_GPIO_RES_CFG BIT(7) +#define MAX96724_GPIO_TX_ENABLE BIT(1) +#define MAX96724_GPIO_OUTDRV_DISABLE BIT(0) +#define MAX96724_GPIO_PUSH_PULL BIT(5) +#define MAX96724_GPIO_A_REG(n) (MAX96724_GPIO_REG(n) + 1) +#define MAX96724_GPIO_B_REG(n) \ + ((n <= 2) ? (0x0337 + 3 * n) : (n <= 7) ? (0x341 + 3 * (n - 3)) \ + : (0x351 + 3 * (n - 8))) +#define MAX96724_GPIO_C_REG(n) \ + ((n == 0) ? (0x036D) : (n <= 5) ? (0x371 + 3 * (n - 1)) \ + : (0x381 + 3 * (n - 6))) +#define MAX96724_GPIO_D_REG(n) \ + ((n <= 3) ? (0x03A4 + 3 * n) : (n <= 8) ? (0x3B1 + 3 * (n - 4)) \ + : (0x3C1 + 3 * (n - 9))) + +/* According to MAX96724 spec: + * GPIO7_A (RES_CFG, TX_PRIO, TX_COMP_EN, GPIO_OUT, GPIO_IN, GPIO_RX_EN, GPIO_TX_EN, GPIO_OUT_DIS) + * GPIO7_B (PULL_UPDN_SEL[1:0], GPIO_TX_ID[4:0]) + * GPIO7_C (OVR_RES_CFG, GPIO_RX_ID[4:0]) + * These are GPIO configuration registers, NOT CSI mode or lane mapping registers. + */ +#define MAX96724_GPIO5_A MAX96724_GPIO_A_REG(5) +#define MAX96724_GPIO5_B MAX96724_GPIO_B_REG(5) +#define MAX96724_GPIO5_C MAX96724_GPIO_C_REG(5) +#define MAX96724_GPIO6_A MAX96724_GPIO_A_REG(6) + +#define MAX96724_GPIO7_C MAX96724_GPIO_C_REG(7) + +#define MAX96712_GPIO7_C 0x0318 +/* GPIO Configuration Registers */ +#define MAX96712_GPIO15_A 0x0330 /* GPIO15_A: RES_CFG, TX_PRIO, TX_COMP_EN, GPIO_OUT, GPIO_IN, GPIO_RX_EN, GPIO_TX_EN, GPIO_OUT_DIS */ +#define MAX96712_GPIO15_B 0x0331 /* GPIO15_B: PULL_UPDN_SEL[1:0], GPIO_TX_ID[4:0] */ +#define MAX96712_GPIO15_C 0x0332 /* GPIO15_C: OVR_RES_CFG, GPIO_RX_ID[4:0] */ +#define MAX96712_GPIO16_A 0x0333 /* GPIO16_A: RES_CFG, TX_PRIO, TX_COMP_EN, GPIO_OUT, GPIO_IN, GPIO_RX_EN, GPIO_TX_EN, GPIO_OUT_DIS */ + +#define MAX96724_CTRL1_ADDR MAX96724_REG18_ADDR + +/* data defines */ + +#define MAX96724_ALLPHYS_NOSTDBY 0xF0 +#define MAX96724_ST_ID_SEL_INVALID 0xF + +#define MAX96724_CPHY_PREAMBLE_DEFAULT 0x1f +#define MAX96724_CPHY_PREP_DEFAULT 0x5d + +// write 0x06 for CPHY 1500Mbps/lane on CSI2 controller-1 & PHY1 +#define MAX96724_CPHY_CLK_1500BPS 0x25 +// write 0x15 for DPHY 1500Mbps/lane on CSI2 controller-1 & PHY1 +#define MAX96724_DPHY_CLK_1500BPS 0x35 + +#define MAX96724_RESET_ALL 0x80 + +/* MAX96724 Quad GMSL Deserializer: + * - 4 GMSL input links (Link A/B/C/D from serializers) + * - 4 video pipelines (Pipe X/Y/Z/U) + * - 2 CSI-2 output controllers (2x4-lane or 4x2-lane modes) + */ +#define MAX96724_MAX_SOURCES 4 +#define MAX96724_NUM_CSI_LINKS 4 +#define MAX96724_MAX_PIPES 4 + +#define MAX96724_DEFAULT_SER_ADDR_A 0x28 +#define MAX96724_DEFAULT_SER_ADDR_B 0x2A +#define MAX96724_RESET_DELAY_MS 100 + +#define MAX96724_PIPE_X 0 +#define MAX96724_PIPE_Y 1 +#define MAX96724_PIPE_Z 2 +#define MAX96724_PIPE_U 3 +#define MAX96724_PIPE_INVALID 0xF + +/* CSI Controller IDs (not register addresses) */ +#define MAX96724_CSI_CTRL_ID_0 0 +#define MAX96724_CSI_CTRL_ID_1 1 +#define MAX96724_CSI_CTRL_ID_2 2 +#define MAX96724_CSI_CTRL_ID_3 3 + +#define MAX96724_INVAL_ST_ID 0xFF + +/* Use reset value as per spec, confirm with vendor */ +#define MAX96724_RESET_ST_ID 0x00 + +#define MAX96724_LINK_STATUS(link) ((link) == 0 ? 0x1A : 0x0A + ((link) - 1)) +#define MAX96724_LINK_LOCK_BIT BIT(3) + +#define MAX96724_GMSL_MODE_REG 0x06 +#define MAX96724_GMSL_MODE_PER_LINK_MASK 0x3 +#define MAX96724_GMSL_MODE_AUTO 0x0 +#define MAX96724_GMSL_MODE_GMSL2 0x1 +#define MAX96724_GMSL_MODE_GMSL1 0x2 +#define MAX96724_GMSL_MODE_RESERVED 0x3 + +#define MAX96724_VC_2_BITS 0U +#define MAX96724_VC_4_BITS 1U + +// See: Errata for MAX96724/MAX96724F/MAX96724R (Rev. 1) document +// https://www.analog.com/media/en/technical-documentation/data-sheets/max96724-f-r-rev1-b-0a-errata.pdf +static const struct reg_sequence max96724_errata_rev1[] = { + // Errata #5 - GMSL2 Link requires register writes for robust 6 Gbps operation + { 0x1449, 0x75, }, + { 0x1549, 0x75, }, + { 0x1649, 0x75, }, + { 0x1749, 0x75, }, +}; + +enum max96724_rev { + MAX96724_REV_F = 1, + MAX96724_REV_R, +}; + +static const struct reg_sequence max96712_phy_tuning_revABC[] = { + // PHY A + { 0x1458, 0x28, }, + { 0x1459, 0x68, }, + { 0x143E, 0xB3, }, + { 0x143F, 0x72, }, + // PHY B + { 0x1558, 0x28, }, + { 0x1559, 0x68, }, + { 0x153E, 0xB3, }, + { 0x153F, 0x72, }, + // PHY C + { 0x1658, 0x28, }, + { 0x1659, 0x68, }, + { 0x163E, 0xB3, }, + { 0x163F, 0x72, }, + // PHY D + { 0x1758, 0x28, }, + { 0x1759, 0x68, }, + { 0x173E, 0xB3, }, + { 0x173F, 0x72, }, +}; + +static const struct reg_sequence max96712_phy_tuning_revE[] = { + // Increase CMU regulator output voltage (bit 4) + { 0x06C2, 0x10, }, + // Set VgaHiGain_Init_6G (bit 1) and VgaHiGain_Init_3G (bit 0)* for PHY A/B/C/D + { 0x14D1, 0x03, }, + { 0x15D1, 0x03, }, + { 0x16D1, 0x03, }, + { 0x17D1, 0x03, }, +}; + +enum max96712_rev { + MAX96712_REV_A = 1, + MAX96712_REV_B, + MAX96712_REV_C, + MAX96712_REV_D, + MAX96712_REV_E, +}; + +/* Log all register writes */ +#define MAX96724_WRITE_REG(map, reg, val) \ +({ \ + int __ret; \ + struct regmap *__map = (map); \ + unsigned int __reg = (reg); \ + unsigned int __val = (val); \ + dev_dbg(regmap_get_device(__map), "REG_WRITE: [0x%04X] = 0x%02X", __reg, __val); \ + __ret = regmap_write(__map, __reg, __val); \ + if (__ret) \ + dev_err(regmap_get_device(__map), "REG_WRITE FAILED: [0x%04X] = 0x%02X, ret=%d", __reg, __val, __ret); \ + __ret; \ +}) + +/* Log register update_bits (read-modify-write) */ +#define MAX96724_UPDATE_BITS(map, reg, mask, val) \ +({ \ + int __ret; \ + struct regmap *__map = (map); \ + unsigned int __reg = (reg); \ + unsigned int __mask = (mask); \ + unsigned int __val = (val); \ + unsigned int __old_val = 0; \ + unsigned int __new_val = 0; \ + regmap_read(__map, __reg, &__old_val); \ + __new_val = (__old_val & ~__mask) | (__val & __mask); \ + dev_dbg(regmap_get_device(__map), "REG_UPDATE_BITS: [0x%04X] mask=0x%02X, old=0x%02X, new=0x%02X (val=0x%02X)", \ + __reg, __mask, __old_val, __new_val, __val); \ + __ret = regmap_update_bits(__map, __reg, __mask, __val); \ + if (__ret) \ + dev_err(regmap_get_device(__map), "REG_UPDATE_BITS FAILED: [0x%04X] mask=0x%02X val=0x%02X, ret=%d", \ + __reg, __mask, __val, __ret); \ + __ret; \ +}) + +struct max96724_source_ctx { + struct gmsl_link_ctx *g_ctx; + bool st_enabled; +}; + +struct pipe_ctx { + u32 id; + u32 dt_type; + u32 dst_csi_ctrl; + u32 st_count; + u32 st_id_sel; +}; + +struct max96724 { + struct i2c_client *i2c_client; + struct regmap *regmap; + u32 num_src; + u32 max_src; + u32 num_src_found; + u32 src_link; + bool splitter_enabled; + struct max96724_source_ctx sources[MAX96724_MAX_SOURCES]; + struct mutex lock; + u32 sdev_ref; + bool lane_setup; + bool link_setup; + struct pipe_ctx pipe[MAX96724_MAX_PIPES]; + u8 csi_mode; + int reset_gpio; + int pw_ref; + struct regulator *vdd_cam_1v2; + __u32 d4xx_hacks; + u32 dev_id; + u32 dst_link; + u8 csi_phy; + u8 dst_n_lanes; +}; + +static int max96724_read_reg(struct device *dev, + u16 addr, unsigned int *val) +{ + struct max96724 *priv; + int err; + + priv = dev_get_drvdata(dev); + + err = regmap_read(priv->regmap, addr, val); + if (err) + dev_err(dev, + "%s:i2c read failed, 0x%x\n", + __func__, addr); + + return err; +} + +static int max96724_write_reg(struct device *dev, + u16 addr, u8 val) +{ + struct max96724 *priv; + int err; + + priv = dev_get_drvdata(dev); + + err = MAX96724_WRITE_REG(priv->regmap, addr, val); + if (err) + dev_err(dev, + "%s:i2c write failed, 0x%x = %x\n", + __func__, addr, val); + + /* delay before next i2c command as required for SERDES link */ + usleep_range(100, 110); + + return err; +} + +static int max96724_get_sdev_idx(struct device *dev, + struct device *s_dev, unsigned int *idx) +{ + struct max96724 *priv = dev_get_drvdata(dev); + unsigned int i; + int err = 0; + + mutex_lock(&priv->lock); + for (i = 0; i < priv->max_src; i++) { + if (priv->sources[i].g_ctx->s_dev == s_dev) + break; + } + if (i == priv->max_src) { + dev_err(dev, "no sdev found\n"); + err = -EINVAL; + goto ret; + } + + if (idx) + *idx = i; + +ret: + mutex_unlock(&priv->lock); + return err; +} + +static void max96724_pipes_reset(struct max96724 *priv) +{ + /* + * This is default pipes combination. add more mappings + * for other combinations and requirements. + */ + struct pipe_ctx pipe_defaults[] = { + {MAX96724_PIPE_X, GMSL_CSI_DT_RAW_12, + MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + {MAX96724_PIPE_Y, GMSL_CSI_DT_RAW_12, + MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + {MAX96724_PIPE_Z, GMSL_CSI_DT_EMBED, + MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + {MAX96724_PIPE_U, GMSL_CSI_DT_EMBED, + MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID} + }; + + /* + * Add DT props for num-streams and stream sequence, and based on that + * set the appropriate pipes defaults. + * For now default it supports "2 RAW12 and 2 EMBED" 1:1 mappings. + */ + memcpy(priv->pipe, pipe_defaults, sizeof(pipe_defaults)); +} + +static void max96724_reset_ctx(struct max96724 *priv) +{ + unsigned int i; + + priv->link_setup = false; + priv->lane_setup = false; + priv->num_src_found = 0; + priv->src_link = 0; + priv->dst_link = 0; + priv->dst_n_lanes = 2; + priv->splitter_enabled = false; + max96724_pipes_reset(priv); + for (i = 0; i < priv->num_src; i++) + priv->sources[i].st_enabled = false; +} + +int max96724_power_on(struct device *dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (priv->pw_ref == 0) { + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + usleep_range(30, 50); + + if (priv->vdd_cam_1v2) { + err = regulator_enable(priv->vdd_cam_1v2); + if (unlikely(err)) + goto ret; + } + + usleep_range(30, 50); + + /*exit reset mode: XCLR */ + if (priv->reset_gpio) { + gpio_set_value(priv->reset_gpio, 0); + usleep_range(30, 50); + gpio_set_value(priv->reset_gpio, 1); + usleep_range(30, 50); + } + + /* delay to settle reset */ + msleep(20); + } + + priv->pw_ref++; + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max96724_power_on); + +void max96724_power_off(struct device *dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + priv->pw_ref--; + + if (priv->pw_ref < 0) + priv->pw_ref = 0; + + if (priv->pw_ref == 0) { + /* enter reset mode: XCLR */ + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + if (priv->vdd_cam_1v2) + regulator_disable(priv->vdd_cam_1v2); + } + + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max96724_power_off); + +static const char *max96724_get_link_name(u32 link) +{ + switch (link) { + case GMSL_SERDES_CSI_LINK_A: + return "GMSL A"; + case GMSL_SERDES_CSI_LINK_B: + return "GMSL B"; + case GMSL_SERDES_CSI_LINK_C: + return "GMSL C"; + case GMSL_SERDES_CSI_LINK_D: + return "GMSL D"; + default: + return "GMSL UNKNOWN"; + } +} + +static u8 max96724_link_to_port(u32 link) +{ + switch (link) { + case GMSL_SERDES_CSI_LINK_B: + return 1; + case GMSL_SERDES_CSI_LINK_C: + return 2; + case GMSL_SERDES_CSI_LINK_D: + return 3; + case GMSL_SERDES_CSI_LINK_A: + default: + return 0; + } +} + + +int max96724_check_status(struct device *dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int err = 0; + u8 src_port = max96724_link_to_port(priv->src_link); + + mutex_lock(&priv->lock); + + /* Re-Check GMSL link status after initial configuration */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); + dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + unsigned int pll_status = 0; + max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); + dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pll_status, + (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(3)) ? "CSIPLL3_LOCK " : ""); + unsigned int vid_status = 0; + max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); + dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + (vid_status & 0x40) ? "VID_LOCK " : "", + (vid_status & 0x20) ? "VID_PKT_DET " : "", + (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); + unsigned int pipe_de_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); + dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + (pipe_de_status & 0x01) ? "DE_DET_0 " : "", + (pipe_de_status & 0x02) ? "DE_DET_1 " : "", + (pipe_de_status & 0x04) ? "DE_DET_2 " : "", + (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + } + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max96724_check_status); + +int max96724_set_mfp(struct device *dev, int pin, int val) +{ + struct max96724 *priv = dev_get_drvdata(dev); + struct regmap *map = priv->regmap; + int err; + + if (pin > 10) + return -EINVAL; + + err = MAX96724_UPDATE_BITS(map, MAX96724_GPIO_REG(pin), + (1<<4 | 1<<2 | 1<<0), + ((val ? 1<<4 : 0) | 0<<2 | 0<<0) ); + + err |= MAX96724_UPDATE_BITS(map, MAX96724_GPIO_REG(pin), + 3<<6|1<<5, + 1<<6|1<<5); + + return err; + +} +EXPORT_SYMBOL(max96724_set_mfp); + +static int max967xx_phy_tuning(struct device *dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + struct regmap *map = priv->regmap; + int ret; + unsigned rev = 0; + + if (priv->dev_id == MAX96724_DEV_REV_ID) { + + ret = regmap_read(map, MAX967XX_DEV_REV_ADDR, &rev); + ret |= regmap_multi_reg_write(map, + max96724_errata_rev1, + ARRAY_SIZE(max96724_errata_rev1)); + + goto phy_tuning_out; + } + + ret = regmap_read(map, MAX967XX_DEV_REV_ADDR, &rev); + switch (FIELD_GET(MAX967XX_DEV_REV_FIELD, rev)) { + case MAX96712_REV_A: + case MAX96712_REV_B: + case MAX96712_REV_C: + ret |= regmap_multi_reg_write(map, + max96712_phy_tuning_revABC, + ARRAY_SIZE(max96712_phy_tuning_revABC)); + break; + case MAX96712_REV_D: + // No tuning necessary + break; + case MAX96712_REV_E: + ret |= regmap_multi_reg_write(map, + max96712_phy_tuning_revE, + ARRAY_SIZE(max96712_phy_tuning_revE)); + break; + default: + dev_warn(dev, "Unknown chip revision"); + break; + } + +phy_tuning_out: + dev_dbg(dev, "%s: applying %s (rev %u) CSI %u %s tunning\n", + __func__, + priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712", + rev, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY"); + return ret; +} + +static int max96724_write_link(struct device *dev, u32 link) +{ + int ret = -EINVAL; + int err = 0; + struct max96724 *priv = dev_get_drvdata(dev); + int link_id; + int port; + + port = max96724_link_to_port(link); + if (port < 0) { + dev_err(dev, "%s: invalid link 0x%x\n", __func__, link); + return ret; + } + + dev_dbg(dev, "%s: write %s link (id=0x%x, idx=%u)\n", + __func__, max96724_get_link_name(link), port); + + /* Configure 6Gbps GMSL2 mode BEFORE reset to match MAX9295 serializer + * Reference script: enable_6g sets 0x0010=0x22, 0x0011=0x22 + * These are CTRL0 and CTRL1 registers for GMSL2 6Gbps PHY rate + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(0), 0x22); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(2), 0x22); + if (err) { + dev_err(dev, "%s: Failed to enable %s 6Gbps link: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto write_link_out; + } + + + /* Disable CSI output initially (matches script step 2) + * This is done early in initialization, before any video configuration + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0U)); + if (err) { + dev_err(dev, "%s: Failed to csi output link: %d\n", + __func__, + err); + goto write_link_out; + } + + /* Enable GMSL2 input sources links state + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_LINK_CTRL_ADDR, + MAX96724_LINK_CTRL_EN_FIELD(0) + | MAX96724_LINK_CTRL_GMSL_FIELD(0) + | MAX96724_LINK_CTRL_EN_FIELD(1) + | MAX96724_LINK_CTRL_GMSL_FIELD(1) + | MAX96724_LINK_CTRL_EN_FIELD(2) + | MAX96724_LINK_CTRL_GMSL_FIELD(2) + | MAX96724_LINK_CTRL_EN_FIELD(3) + | MAX96724_LINK_CTRL_GMSL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(3), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(3), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable %s link control: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto write_link_out; + } + +#ifndef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE + /* Enable internal regulators (0x17 and 0x19) - before reset + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG17_ADDR, + MAX96724_REG17_CTRL_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_REG17_CTRL_EN_FIELD, 1U)); + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG19_ADDR, + MAX96724_REG19_CTRL_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_REG19_CTRL_EN_FIELD, 1U)); + + /* Enable PoC (Power over Coax) for cameras + */ + dev_dbg(dev, "Enabling PoC (Power over Coax)...\n"); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL1, 0x80); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL2, 0x80); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL3, 0x01); + + msleep(100); /* Wait for power to stabilize */ +#endif + + /* Reset GMSL2 links state + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_CTRL_FIELD(0) + | MAX96724_RESET_CTRL_FIELD(1) + | MAX96724_RESET_CTRL_FIELD(2) + | MAX96724_RESET_CTRL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 1U)); + if (err) { + dev_err(dev, "%s: Failed to reset %s link: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto write_link_out; + } + + /* delay to settle link */ + msleep(300); + + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_CTRL_FIELD(0) + | MAX96724_RESET_CTRL_FIELD(1) + | MAX96724_RESET_CTRL_FIELD(2) + | MAX96724_RESET_CTRL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 0U)); + if (err) { + dev_err(dev, "%s: Failed to reset %s link: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto write_link_out; + } + + /* Check GMSL link status - hardware auto-negotiation should have completed */ + { + int i; + unsigned int link_status = 0; + for (i = 0; i < MAX96724_MAX_SOURCES; i++) { + max96724_read_reg(dev, MAX96724_LINK_STATUS(i), &link_status); + dev_dbg(dev, "%s: GMSL %c 6Gbps link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, 'A' + i , link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + } + } + ret = 0; + +write_link_out: + return ret; +} + +int max96724_setup_link(struct device *dev, struct device *s_dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + dev_dbg(dev, "%s: ENTER\n", __func__); + + err = max96724_get_sdev_idx(dev, s_dev, &i); + if (err) { + dev_err(dev, "%s: Failed to get sdev_idx: %d\n", __func__, err); + return err; + } + + dev_info(dev, "%s: Source index=%d, link=%s\n", __func__, i, + max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link)); + + mutex_lock(&priv->lock); + + if (!priv->splitter_enabled) { + err = max96724_write_link(dev, + priv->sources[i].g_ctx->serdes_csi_link); + if (err) + goto ret; + + priv->link_setup = true; + } + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max96724_setup_link); + +int max96724_setup_control(struct device *dev, struct device *s_dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + u8 src_port; + + dev_dbg(dev, "%s: ENTER\n", __func__); + + err = max96724_get_sdev_idx(dev, s_dev, &i); + if (err) { + dev_err(dev, "%s: Failed to get sdev_idx: %d\n", __func__, err); + return err; + } + + mutex_lock(&priv->lock); + + if (!priv->link_setup) { + dev_err(dev, "%s: invalid state\n", __func__); + err = -EINVAL; + goto error; + } + + if (priv->sources[i].g_ctx->serdev_found) { + priv->num_src_found++; + priv->src_link = priv->sources[i].g_ctx->serdes_csi_link; + priv->dst_link = priv->sources[i].g_ctx->dst_csi_port; + src_port = max96724_link_to_port(priv->src_link); + priv->dst_n_lanes = priv->sources[i].g_ctx->num_csi_lanes; + + /* Check GMSL link status - hardware auto-negotiation should have completed */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); + dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + } + } + + /* Enable splitter mode */ + if ((priv->max_src > 1U) && + (priv->num_src_found > 0U) && + (priv->splitter_enabled == false)) { + + //Fetch and output chip name + unsigned int dev_id = 0; + err = max96724_read_reg(dev, MAX967XX_DEV_ID_ADDR, &dev_id); + if (err) { + dev_warn(dev, "%s(): Failed to read Serdes chip ID", __func__); + dev_id = MAX96724_DEV_REV_ID; // fallback max96724 + } + priv->dev_id = dev_id; + + dev_dbg(dev, "%s: set %s SerDes GMSL A & B & C & D quad-link)\n", + __func__, + priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712"); + /* + src_port = max96724_link_to_port(priv->src_link); + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 1U) | + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); + if (err) { + dev_err(dev, "%s: Failed to trigger %s link reset: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto error; + } + + // delay to settle link + msleep(100); + + // clear link reset + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 0U) | + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); + if (err) { + dev_err(dev, "%s: Failed to clear %s link reset: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto error; + } + */ + priv->splitter_enabled = true; + + dev_dbg(dev, "%s: reset %s link done\n", + __func__, + max96724_get_link_name(priv->src_link)); + } + + //max96724_write_reg(dev, + // MAX96724_PWDN_PHYS_ADDR, MAX96724_ALLPHYS_NOSTDBY); + + priv->sdev_ref++; + + dev_dbg(dev, "%s: checking serializer sources (count/ref=%d/%d -> src[%d]=%s)\n", + __func__, + priv->num_src_found, + priv->sdev_ref, i, + max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link)); + + /* Reset splitter mode if all devices are not found */ + if ((priv->sdev_ref == priv->max_src) && + (priv->splitter_enabled == true) && + (priv->num_src_found > 0U) && + (priv->num_src_found < priv->max_src)) { + dev_info(dev, "%s: restore SerDes %s single-link\n", + __func__, + max96724_get_link_name(priv->src_link)); + + /* Only restore link if it's valid (not UNKNOWN) */ + if (priv->src_link != GMSL_SERDES_CSI_LINK_UNKNOWN) { + err = max96724_write_link(dev, priv->src_link); + if (err) { + dev_info(dev, "%s: fail to write source link\n", + __func__); + goto error; + } + } else { + dev_info(dev, "%s: skip restore (src_link is UNKNOWN)\n", + __func__); + } + priv->splitter_enabled = false; + + } else if (priv->num_src_found < priv->sdev_ref) { + + dev_info(dev, "%s: restore SerDes %s single-link\n", + __func__, + max96724_get_link_name(priv->src_link)); + + /* Only restore link if it's valid (not UNKNOWN) */ + if (priv->src_link != GMSL_SERDES_CSI_LINK_UNKNOWN) { + err = max96724_write_link(dev, priv->src_link); + if (err) { + dev_info(dev, "%s: fail to write source link\n", + __func__); + goto error; + } + } else { + dev_info(dev, "%s: skip restore (src_link is UNKNOWN)\n", + __func__); + } + priv->splitter_enabled = false; + } + +error: + mutex_unlock(&priv->lock); + dev_dbg(dev, "%s: EXIT (err=%d, sdev_ref=%d, num_src_found=%d)\n", + __func__, err, priv->sdev_ref, priv->num_src_found); + return err; +} +EXPORT_SYMBOL(max96724_setup_control); + +int max96724_reset_control(struct device *dev, struct device *s_dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + if (!priv->sdev_ref) { + dev_info(dev, "%s: dev is already in reset state\n", __func__); + goto ret; + } + + priv->sdev_ref--; + if (priv->sdev_ref == 0) { + max96724_reset_ctx(priv); + max96724_write_reg(dev, MAX96724_PWR1_ADDR, MAX96724_RESET_ALL); + + /* delay to settle reset */ + msleep(100); + } + +ret: + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max96724_reset_control); + +int max96724_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) +{ + struct max96724 *priv = NULL; + unsigned int i; + int err = 0; + + if (!dev || !g_ctx || !g_ctx->s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + if (priv->num_src > priv->max_src) { + dev_err(dev, + "%s: MAX96724 inputs size exhausted\n", __func__); + err = -ENOMEM; + goto error; + } + + if (priv->d4xx_hacks) { + bool csi_mode_ok = false; + dev_dbg(dev, + "%s: Checking Deserializer g_ctx->csi_mode=%s\n", + __func__, + (g_ctx->csi_mode == GMSL_CSI_4X2_MODE) == GMSL_CSI_4X2_MODE ? "4X2" : "2X4"); + + switch (priv->csi_mode) { + case MAX96724_CSI_MODE_1X4: + csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_1X4_MODE); + break; + case MAX96724_CSI_MODE_2X4: + csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || + (g_ctx->csi_mode == GMSL_CSI_2X4_MODE); + break; + case MAX96724_CSI_MODE_4X2: + csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_4X2_MODE); + break; + default: + csi_mode_ok = false; + break; + } + + if (!csi_mode_ok) { + dev_err(dev, + "%s: csi mode not supported\n", __func__); + err = -EINVAL; + goto error; + } + dev_dbg(dev, "%s: d4xx specfic csi-mode set\n", __func__); + } + + for (i = 0; i < priv->num_src; i++) { + if (g_ctx->serdes_csi_link == + priv->sources[i].g_ctx->serdes_csi_link) { + dev_err(dev, + "%s: serdes csi link is in use\n", __func__); + err = -EINVAL; + goto error; + } + /* + * All sdevs should have same num-csi-lanes regardless of + * dst csi port selected. + * Later if there is any usecase which requires each port + * to be configured with different num-csi-lanes, then this + * check should be performed per port. + */ + if (g_ctx->num_csi_lanes != + priv->sources[i].g_ctx->num_csi_lanes) { + dev_err(dev, + "%s: csi num lanes mismatch\n", __func__); + err = -EINVAL; + goto error; + } + } + + priv->sources[priv->num_src].g_ctx = g_ctx; + priv->sources[priv->num_src].st_enabled = false; + + priv->num_src++; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max96724_sdev_register); + +int max96724_sdev_unregister(struct device *dev, struct device *s_dev) +{ + struct max96724 *priv = NULL; + int err = 0; + unsigned int i = 0; + + if (!dev || !s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + mutex_lock(&priv->lock); + + if (priv->num_src == 0) { + dev_err(dev, "%s: no source found\n", __func__); + err = -ENODATA; + goto error; + } + + for (i = 0; i < priv->num_src; i++) { + if (s_dev == priv->sources[i].g_ctx->s_dev) { + priv->sources[i].g_ctx = NULL; + break; + } + } + + if (i == priv->num_src) { + dev_err(dev, + "%s: requested device not found\n", __func__); + err = -EINVAL; + goto error; + } + priv->num_src--; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max96724_sdev_unregister); + +static int max96724_get_available_pipe(struct device *dev, + u32 st_data_type, u32 dst_csi_port) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + /* + * TODO: Enable a pipe for multi stream configuration having + * similar stream data type. For now use st_count as a flag + * for 1 to 1 mapping in pipe and stream data type, same can + * be extended as count for many to 1 mapping. Would also need + * few more checks such as input stream id select, dst port etc. + */ + if ((priv->pipe[i].dt_type == st_data_type) && + ((dst_csi_port == GMSL_CSI_PORT_A) ? + (priv->pipe[i].dst_csi_ctrl == + MAX96724_CSI_CTRL_ID_0) || + (priv->pipe[i].dst_csi_ctrl == + MAX96724_CSI_CTRL_ID_1) : + (priv->pipe[i].dst_csi_ctrl == + MAX96724_CSI_CTRL_ID_2) || + (priv->pipe[i].dst_csi_ctrl == + MAX96724_CSI_CTRL_ID_3)) && + (!priv->pipe[i].st_count)) + break; + } + + if (i == MAX96724_MAX_PIPES) { + dev_err(dev, "%s: all pipes are busy\n", __func__); + return -ENOMEM; + } + + return i; +} + +struct reg_pair { + u16 addr; + u8 val; +}; + +static int max96724_set_registers(struct device *dev, struct reg_pair *map, + u32 count) +{ + int err = 0; + u32 j = 0; + + for (j = 0; j < count; j++) { + err = max96724_write_reg(dev, + map[j].addr, map[j].val); + if (err != 0) + break; + } + + return err; +} + +int max96724_get_available_pipe_id(struct device *dev, int vc_id) +{ + int i; + int pipe_id = -ENOMEM; + struct max96724 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + if (i == vc_id && !priv->pipe[i].st_count) { + priv->pipe[i].st_count++; + pipe_id = i; + break; + } + } + mutex_unlock(&priv->lock); + + return pipe_id; +} +EXPORT_SYMBOL(max96724_get_available_pipe_id); + +int max96724_release_pipe(struct device *dev, int pipe_id) +{ + struct max96724 *priv = dev_get_drvdata(dev); + + if (pipe_id < 0 || pipe_id >= MAX96724_MAX_PIPES) + return -EINVAL; + + mutex_lock(&priv->lock); + priv->pipe[pipe_id].st_count = 0; + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max96724_release_pipe); + +void max96724_reset_oneshot(struct device *dev) +{ + struct max96724 *priv = dev_get_drvdata(dev); + int err = 0; + u8 src_port = max96724_link_to_port(priv->src_link); + + mutex_lock(&priv->lock); + + /* Re-Check GMSL link status after initial configuration */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); + dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + unsigned int pll_status = 0; + max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); + dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pll_status, + (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(3)) ? "CSIPLL3_LOCK " : ""); + unsigned int vid_status = 0; + max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); + dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + (vid_status & 0x40) ? "VID_LOCK " : "", + (vid_status & 0x20) ? "VID_PKT_DET " : "", + (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); + unsigned int pipe_de_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); + dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + (pipe_de_status & 0x01) ? "DE_DET_0 " : "", + (pipe_de_status & 0x02) ? "DE_DET_1 " : "", + (pipe_de_status & 0x04) ? "DE_DET_2 " : "", + (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + } + + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 1U) | + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); + if (err) { + dev_err(dev, "%s: Failed to trigger %s link reset: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto reset_link_out; + } + + /* delay to settle link */ + msleep(100); + + /* clear link and ctrl reset */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 0U) | + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); + if (err) + dev_err(dev, "%s: Failed to clear %s link reset: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + +reset_link_out: + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max96724_reset_oneshot); + +static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id, u32 csi_id ) +{ + int err = 0; + int i = 0; + u8 en_mapping_num = 0x0F; + u8 all_mapping_phy = 0x55; + u8 dis_lim_heart = 0x0A; + + u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; + u8 vc_id_2b_msb = (((u8) vc_id) >> 2) & 0x3; + + if (data_type2 == 0x0) + en_mapping_num = 0x07; + + switch (csi_id) { + case GMSL_CSI_PORT_A: + if (data_type2 == 0x0) + all_mapping_phy = 0x10; + else + all_mapping_phy = 0x00; + break; + case GMSL_CSI_PORT_B: + if (data_type2 == 0x0) + all_mapping_phy = 0x15; + else + all_mapping_phy = 0x55; + break; + case GMSL_CSI_PORT_C: + if (data_type2 == 0x0) + all_mapping_phy = 0x1A; + else + all_mapping_phy = 0xAA; + break; + case GMSL_CSI_PORT_D: + if (data_type2 == 0x0) + all_mapping_phy = 0x1F; + else + all_mapping_phy = 0xFF; + break; + default: + dev_warn(dev, "%s: invalid deserializer csi port, default CSI 1!\n", __func__); + if (data_type2 == 0x0) + all_mapping_phy = 0x15; + }; + + struct reg_pair map_pipe_opt[] = { + // Do not un-double 8bpp (Un-double 8bpp data) + //{0x031C, 0x00},o + // Do not un-double 8bpp + //{0x031F, 0x00}, + // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 + {MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10}, + }; + struct reg_pair map_pipe_control[] = { + // Enable 4 mappings for Pipe X + {MAX96724_PIPE_X_EN_ADDR, 0x0F}, + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, + // Map data_type1 on vc_id + {MAX96724_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, + {MAX96724_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + // Map frame_start on vc_id + {MAX96724_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX96724_PIPE_X_DST_1_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX96724_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX96724_PIPE_X_DST_2_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX96724_PIPE_X_SRC_3_MAP_ADDR, 0x12}, + {MAX96724_PIPE_X_DST_3_MAP_ADDR, 0x12}, + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + {MAX96724_VID_RX0_ADDR, 0x23}, // pipe X + // LIM_HEART : Disabled + {MAX96724_VID_RX6_ADDR, dis_lim_heart}, + // Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + {MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR, 0x0}, + }; + + for (i = 0; i < 10; i++) { + map_pipe_control[i].addr += 0x40 * pipe_id; + } + map_pipe_control[10].addr += 0x12 * pipe_id; + map_pipe_control[11].addr += 0x12 * pipe_id; + map_pipe_control[12].addr += 0x10 * pipe_id; + map_pipe_control[13].addr += 0x10 * pipe_id; + map_pipe_control[14].addr += 0x10 * pipe_id; + map_pipe_control[15].addr += 0x10 * pipe_id; + + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = all_mapping_phy; + map_pipe_control[2].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[3].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[4].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[5].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[6].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[7].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[8].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[10].val = 0x23; + map_pipe_control[11].val = dis_lim_heart; + map_pipe_control[12].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[13].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[14].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[15].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + + dev_dbg(dev, "%s: pipe %u set on vc_id=%u [reg: src/dst:0x%x, dstext:0x%x] \n", + __func__, pipe_id, vc_id, + (vc_id_2b_lsb << 6), + vc_id_2b_msb); + + err |= max96724_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + dev_dbg(dev, "%s: d4xx specfic pipe %u optimizations\n", + __func__, pipe_id, vc_id, + (vc_id_2b_lsb << 6), + vc_id_2b_msb); + + err |= max96724_set_registers(dev, map_pipe_opt, + ARRAY_SIZE(map_pipe_opt)); + + return err; +} + + +static int __max96724_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id, u32 csi_id ) +{ + int err = 0; + int i = 0; + u8 en_mapping_num = 0x0F; + u8 all_mapping_phy = 0x55; + u8 dis_lim_heart = 0x0A; + + if (data_type2 == 0x0) + en_mapping_num = 0x07; + + switch (csi_id) { + case GMSL_CSI_PORT_A: + if (data_type2 == 0x0) + all_mapping_phy = 0x10; + else + all_mapping_phy = 0x00; + break; + case GMSL_CSI_PORT_B: + if (data_type2 == 0x0) + all_mapping_phy = 0x15; + else + all_mapping_phy = 0x55; + break; + case GMSL_CSI_PORT_C: + if (data_type2 == 0x0) + all_mapping_phy = 0x1A; + else + all_mapping_phy = 0xAA; + break; + case GMSL_CSI_PORT_D: + if (data_type2 == 0x0) + all_mapping_phy = 0x1F; + else + all_mapping_phy = 0xFF; + break; + default: + dev_warn(dev, "%s: invalid deserializer csi port, default CSI 1!\n", __func__); + if (data_type2 == 0x0) + all_mapping_phy = 0x15; + }; + + u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; + u8 vc_id_2b_msb = (((u8) vc_id) >> 2) & 0x3; + + struct reg_pair map_pipe_control[] = { + // Enable 4 mappings for Pipe X + {MAX96724_PIPE_X_EN_ADDR, 0x0F}, + // Mappings to PHY1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, + // Map data_type1 on vc_id + {MAX96724_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, + {MAX96724_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + // Map frame_start on vc_id + {MAX96724_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX96724_PIPE_X_DST_1_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX96724_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX96724_PIPE_X_DST_2_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX96724_PIPE_X_SRC_3_MAP_ADDR, 0x12}, + {MAX96724_PIPE_X_DST_3_MAP_ADDR, 0x12}, + // SEQ_MISS_EN: Disabled / LINE_CRC_EN: Enabled / DIS_PKT_DET: Disabled / LCRC_ERR = Disabled + {MAX96724_VID_RX0_ADDR, 0x23}, + // LIM_HEART : Disabled + {MAX96724_VID_RX6_ADDR, dis_lim_heart}, + // Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + {MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR, 0x0}, + }; + + for (i = 0; i < 10; i++) { + map_pipe_control[i].addr += 0x40 * pipe_id; + } + map_pipe_control[10].addr += 0x12 * pipe_id; + map_pipe_control[11].addr += 0x12 * pipe_id; + map_pipe_control[12].addr += 0x10 * pipe_id; + map_pipe_control[13].addr += 0x10 * pipe_id; + map_pipe_control[14].addr += 0x10 * pipe_id; + map_pipe_control[15].addr += 0x10 * pipe_id; + + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = all_mapping_phy; + map_pipe_control[2].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[3].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[4].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[5].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[6].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[7].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[8].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[10].val = 0x23; + map_pipe_control[11].val = dis_lim_heart; + map_pipe_control[12].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[13].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[14].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + map_pipe_control[15].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; + + dev_dbg(dev, "%s: pipe %u set on vc_id=%u [src/dst_vc:0x%x, ext_vc:0x%x] \n", + __func__, pipe_id, + vc_id, + (vc_id_2b_lsb << 6), + vc_id_2b_msb); + + err |= max96724_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + return err; +} + +int max96724_init_settings(struct device *dev) +{ + int err = 0; + int i; + unsigned src_pipe = MAX96724_PIPE_X; + unsigned phy_lane_map[MAX96724_NUM_CSI_LINKS]; + unsigned rev; + + struct max96724 *priv = dev_get_drvdata(dev); + struct regmap *map = priv->regmap; + u8 src_port = max96724_link_to_port(priv->src_link); + + dev_dbg(dev, "%s: Clearing %s CSI %u %s mode %s (num_lanes:x%u)\n", + __func__, + max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->dst_n_lanes); + + mutex_lock(&priv->lock); + + // disable PHY CLK continuous mode + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0)); + if (err) { + dev_err(dev, "%s: Failed to disable %s continuous clock : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + // disable CSI out link during pipe re-configuration + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0)); + if (err) { + dev_err(dev, "%s: Failed to enable csi output link: %d\n", + __func__, + err); + goto init_settings_out; + } + + /* reconfigure all Video Pipe Input Sources + * This tells MAX96724 which PHY (Link) feeds which Video Pipe. + * Without this, video data cannot flow from GMSL links to CSI-2 output. + */ + +#ifdef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE + /* Map link A pipe X -> pipe 0 + * Map link B pipe X -> pipe 1 + * Map link C pipe X -> pipe 2 + * Map link D pipe X -> pipe 3 + * Enable pipes 0/1/2/3 + * Enable max96712 "legacy" mode + * Non "legacy" mode ignores pipe mapping, and selects all streams for pipe + * 0. The ipu doesn't know what to do with that and throws spurious data + * stream errors. + */ + err = MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_0_ADDR, 0x10); + err |= MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_1_ADDR, 0x32); + err |= MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD + | MAX96724_VIDEO_PIPE_EN_FIELD(0) + | MAX96724_VIDEO_PIPE_EN_FIELD(1) + | MAX96724_VIDEO_PIPE_EN_FIELD(2) + | MAX96724_VIDEO_PIPE_EN_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_VIDEO_PIPE_LEGACY_MODE) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(3), 1U)); +#else + /* Map link A pipe Z -> pipe 0 + * Map link B pipe Z -> pipe 1 + * Map link C pipe Z -> pipe 2 + * Map link D pipe Z -> pipe 3 + * Enable pipes 0/1/2/3 + */ + err = MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_0_ADDR, 0x50); + err |= MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_1_ADDR, 0xfa); + err |= MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(0) + | MAX96724_VIDEO_PIPE_EN_FIELD(1) + | MAX96724_VIDEO_PIPE_EN_FIELD(2) + | MAX96724_VIDEO_PIPE_EN_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(3), 1U)); +#endif + if (err) { + dev_err(dev, "%s: Failed to configure %s pipe to Video pipe X/Y/Z/U : %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto init_settings_out; + } + + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + dev_dbg(dev, "%s enable GMSL link through pipe X source to Video pipe %d", + max96724_get_link_name(priv->src_link), + i); + + if (priv->d4xx_hacks) { + err |= __max96724_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i, priv->dst_link); + } else { + err |= __max96724_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i, priv->dst_link); + } + } + if (err) { + dev_err(dev, "%s: Failed to configure %s pipe X to Video pipes : %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto init_settings_out; + } + +#ifndef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE + /* Write 0x1d00 register sequence + * This register is written twice with different values - appears to be + * some kind of command sequence or state machine control + */ + err = max96724_write_reg(dev, 0x1d00, 0xf4); + err |= max96724_write_reg(dev, 0x1d00, 0xf5); + if (err) { + dev_warn(dev, "%s: Failed to reset internal state machine : %d\n", + __func__, + err); + } +#endif + + msleep(300); /* Wait after pipe configuration */ + + /* Configure MIPI D-PHY/C-PHY + * These registers configure the MIPI physical layer for CSI-2 output + */ + dev_info(dev, "%s: Configuring CSI %u %s mode %s: num_lanes x%u (%s)\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->dst_n_lanes, + priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712"); + + err = max967xx_phy_tuning(dev); + if (err) { + dev_err(dev, "%s: Failed to apply %s tunning : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + err = MAX96724_UPDATE_BITS(map, MAX96724_DPLL_RESET(priv->dst_link), + MAX96724_DPLL_RESET_SOFT_RST_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to reset configure CSI %u %s DPLL : %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + /* Step 1: Configure MIPI PHY0 as 2-lane or 4-lanes (WITHOUT continuous clock first) + * Script: 0x08A0 = 0x02 (2-lane, no continuous clock) + * Script: 0x08A0 = 0x04 (4-lane, no continuous clock) + * Continuous clock will be added later in + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD + | MAX96724_MIPI_PHY0_MODE_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, priv->csi_mode)); + if (err) { + dev_err(dev, "%s: Failed to enable %s %s lanes : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + err); + goto init_settings_out; + } + + /* Step 2: Configure MIPI data rate + * Script: 0x08A3/0x08A4 = 0xE4 (0xE4 = 800Mbps per lane for 1x4 mode) + * Script: 0x08A3/0x08A4 = 0x44 (0x44 = 800Mbps per lane for 4x2 mode) + * + */ + err = MAX96724_WRITE_REG(map, MAX96724_MIPI_PHY_LANE_MAP(GMSL_CSI_PORT_A), + (priv->csi_mode == MAX96724_CSI_MODE_2X4) ? 0xE4 : 0x44 ); + err |= MAX96724_WRITE_REG(map, MAX96724_MIPI_PHY_LANE_MAP(GMSL_CSI_PORT_C), + (priv->csi_mode == MAX96724_CSI_MODE_2X4) ? 0xE4 : 0x44 ); + if (err) { + dev_err(dev, "%s: Failed to configure CSI %u %s lanes %s map: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + err); + goto init_settings_out; + } + + dev_dbg(dev, "%s: mapping %s %s mode x%u-lanes pinout : 0x8A3/0x8A4 applied\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->dst_n_lanes); + + + if (priv->csi_phy == MAX96724_CSI_CPHY) { + /* Step 3b: Configure CPHY preamble + */ + err = MAX96724_WRITE_REG(map, MAX96724_CPHY_PREAMBLE_ADDR, + MAX96724_FIELD_PREP(MAX96724_CPHY_PREAMBLE_FIELD, MAX96724_CPHY_PREAMBLE_DEFAULT)); + if (err) { + dev_err(dev, "%s: Failed to set CSI %u %s preamble : %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + err = MAX96724_WRITE_REG(map, MAX96724_CPHY_PREP_ADDR, MAX96724_CPHY_PREP_DEFAULT); + if (err) { + dev_err(dev, "%s: Failed to set CSI %u %s prep and post : %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + dev_dbg(dev, "%s: preparing %s preamble : 0x8AD/0x8AE applied\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY"); + } + + /* Step 3: Configure per Pipe CSI and PHY CLK cycle + */ + for (err=0, i = GMSL_CSI_PORT_A; i < MAX96724_MAX_SOURCES; i++) + err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_LANE_CNT(i), + MAX96724_MIPI_TX_CPHY_EN_FIELD + | MAX96724_MIPI_TX_WAKEUP_CYC_FIELD + | MAX96724_MIPI_TX_VCX_EN_FIELD + | MAX96724_MIPI_TX_LANE_CNT_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) + | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) + | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); + if (err) { + dev_err(dev, "%s: Failed to configure CSI %u %s %u lanes: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->dst_n_lanes, + err); + goto init_settings_out; + } + + /* Step 3b: Configure auto-deskew for all pipes + * Script: 0x0903/0x0943/0x0983/0x09C3 = auto deskew enable + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_A), + MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); + err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_B), + MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); + err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_C), + MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); + err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_D), + MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable deskew CSI %u %s: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + /* Step 4: Configure MIPI PHY lane control + * Script: 0x08A2 = 0xF0 (all lanes enabled) + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY_ENABLE, + MAX96724_MIPI_PHY_ENABLE_FIELD(0) + | MAX96724_MIPI_PHY_ENABLE_FIELD(1) + | MAX96724_MIPI_PHY_ENABLE_FIELD(2) + | MAX96724_MIPI_PHY_ENABLE_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(0), 1U) | + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(1), 1U) | + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(2), 1U) | + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(3), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable all %s output lanes: %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + /* Step 5: Configure lane mapping DPLL Frequency + * Script: DPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x35 + * Script: CPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x25 + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_A), + MAX96724_DPLL_FREQ_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS + : MAX96724_DPHY_CLK_1500BPS)); + + err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_B), + MAX96724_DPLL_FREQ_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS + : MAX96724_DPHY_CLK_1500BPS)); + err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_C), + MAX96724_DPLL_FREQ_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS + : MAX96724_DPHY_CLK_1500BPS)); + err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_D), + MAX96724_DPLL_FREQ_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS + : MAX96724_DPHY_CLK_1500BPS)); + if (err) { + dev_err(dev, "%s: Failed to set CSI %u %s at 1500bps: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto init_settings_out; + } + + + /* Reset all GMSL links after configuration (matches script line 508) + * Reference script does this AFTER all serializer config and BEFORE CSI enable: + * 1. Restore all links (0x0006 = 0xFF) + * 2. Reset all links (0x0018 = 0x0F) ← This step! + * 3. Enable CSI output (0x040B = 0x02) + * 4. Configure MIPI PHY continuous clock (0x08A0 = 0x84) + * + * This reset ensures all link configurations take effect properly + * and synchronizes the GMSL links before CSI streaming starts. + err = MAX96724_UPDATE_BITS(map, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(0) + | MAX96724_RESET_LINK_FIELD(1) + | MAX96724_RESET_LINK_FIELD(2) + | MAX96724_RESET_LINK_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 1U)); + if (err) { + dev_err(dev, "%s: Failed to reset %s link: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto init_settings_out; + } + + // delay to settle link + msleep(100); + + // clear GMSL2 links Reset + err = MAX96724_UPDATE_BITS(map, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(0) + | MAX96724_RESET_LINK_FIELD(1) + | MAX96724_RESET_LINK_FIELD(2) + | MAX96724_RESET_LINK_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 0U)); + if (err) { + dev_err(dev, "%s: Failed to reset %s link: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + goto init_settings_out; + } + */ + + /* CRITICAL: DO NOT enable CSI_OUT_EN (0x040b) here! + * + * Reference script shows CSI should be enabled LAST, after: + * 1. All MAX967xx configuration complete + * 2. GMSL link is locked + * 3. Sensor is streaming valid data + * + * IPU6 behavior is CORRECT: + * - Firmware waits for initial capture BEFORE calling s_stream + * - This ensures IPU is ready to receive data + * - CSI should only output when sensor has valid data ready + * + * We enable CSI in s_stream AFTER sensor stream-on succeeds. + */ + + /* Re-Check GMSL link status after initial configuration */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); + dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + unsigned int pll_status = 0; + max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); + dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pll_status, + (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(3)) ? "CSIPLL3_LOCK " : ""); + unsigned int vid_status = 0; + max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); + dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + (vid_status & 0x40) ? "VID_LOCK " : "", + (vid_status & 0x20) ? "VID_PKT_DET " : "", + (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); + unsigned int pipe_de_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); + dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + (pipe_de_status & 0x01) ? "DE_DET_0 " : "", + (pipe_de_status & 0x02) ? "DE_DET_1 " : "", + (pipe_de_status & 0x04) ? "DE_DET_2 " : "", + (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + } + +init_settings_out: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max96724_init_settings); + +int max96724_set_pipe(struct device *dev, int pipe_id, + u8 data_type1, u8 data_type2, u32 vc_id) +{ + struct max96724 *priv = dev_get_drvdata(dev); + unsigned src_pipe = MAX96724_PIPE_X; + unsigned rev; + struct regmap *map = priv->regmap; + int err = 0; + u8 src_port = max96724_link_to_port(priv->src_link); + + if (pipe_id > (MAX96724_MAX_PIPES - 1)) { + dev_info(dev, "%s, input pipe_id: %d exceed max96724 max pipes\n", + __func__, pipe_id); + return -EINVAL; + } + + dev_dbg(dev, "%s: disabling %s pipe %u (vc_id %u) CSI %u %s mode %s (num_lanes:x%u)\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, vc_id, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->dst_n_lanes); + + mutex_lock(&priv->lock); + + /* Step 1: Disable CSI output first (reverse order of enable) + * This stops data transmission to IPU before shutting down PHY + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0U)); + if (err) { + dev_err(dev, "%s: Failed to enable csi output link: %d\n", + __func__, + err); + goto set_pipe_out; + } + dev_dbg(dev, "%s: CSI output disabled\n", __func__); + + /* Step 2: Disable MIPI PHY continuous clock mode + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0U)); + if (err) { + dev_err(dev, "%s: Failed to disable %s continuous clock : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto set_pipe_out; + } + dev_dbg(dev, "%s: MIPI PHY continuous clock disabled\n", __func__); + + /* Small delay for clean shutdown */ + msleep(10); + + /* Enable PHY (clear standby bit) + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY_ENABLE, + MAX96724_MIPI_PHY_ENABLE_FIELD(priv->dst_link), + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(priv->dst_link), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable CSI %u %s : %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto set_pipe_out; + } + + dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, data_type1, data_type2, vc_id); + + err = MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); + if (err) { + dev_err(dev, "%s: Failed to disable %s pipe X to Video pipe %u : %d\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, + err); + goto set_pipe_out; + } + + if (priv->d4xx_hacks) { + err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); + } else { + err = __max96724_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); + } + + err = MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable %s pipe X to Video pipe %u : %d\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, + err); + goto set_pipe_out; + } + + msleep(300); /* Wait after pipe configuration */ + + dev_dbg(dev, "%s: Wake-up %s pipe %u (vc_id %u) CSI %u %s mode %s (num_lanes:x%u)\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, vc_id, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->dst_n_lanes); + + /* Enable MIPI TX controller and enable PHY CLK cycle */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), + MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to configure CSI %u %s wakeup cycles: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto set_pipe_out; + } + + /* CRITICAL: Wait for sensor video to stabilize before enabling CSI output! + * 1. Triggers all cameras via GPIO (MFP7/MFP8) + * 2. sleep 0.3 ← 300ms delay to let sensors stabilize! + * 3. i2ctransfer ... 0x04 0x0b 0x02 ← Enable CSI + * 4. i2ctransfer ... 0x08 0xa0 0x84 ← Enable continuous clock + * + * Without this delay, deserializer tries to lock video before sensor + * outputs stable video data, resulting in VIDEO_LOCK failure (0x0108=0x02) + */ + dev_info(dev, "%s: Waiting 300ms for sensor video to stabilize...\n", __func__); + msleep(300); + dev_info(dev, "%s: Sensor stabilization delay complete\n", __func__); + + // enable CSI out link after initialization complet + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable csi output link: %d\n", + __func__, + err); + goto set_pipe_out; + } + dev_dbg(dev, "%s: CSI output enabled\n", __func__); + + /* Small delay for CSI to stabilize */ + msleep(10); + + /* Turn on MIPI PHY continuous clock mode (matches script line 520) + * Reference script does this AFTER CSI enable (0x040B = 0x02) + * This enables continuous clock on MIPI CSI-2 interface + * 0x84 = 0b10000100 + * Bit[7] = 1: Enable continuous clock mode + */ + err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD + | MAX96724_MIPI_PHY0_MODE_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U) | + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, priv->csi_mode)); + if (err) { + dev_err(dev, "%s: Failed to enable %s continuous clock : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + goto set_pipe_out; + } + dev_dbg(dev, "%s: MIPI PHY continuous clock mode enabled\n", __func__); + + /* Double-Check GMSL link status after stream start configuration */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); + dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + unsigned int pll_status = 0; + max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); + dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pll_status, + (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", + (pll_status & MAX96724_DPLL_STATUS_FIELD(3)) ? "CSIPLL3_LOCK " : ""); + unsigned int vid_status = 0; + max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); + dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + (vid_status & 0x40) ? "VID_LOCK " : "", + (vid_status & 0x20) ? "VID_PKT_DET " : "", + (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); + unsigned int pipe_de_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); + dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + (pipe_de_status & 0x01) ? "DE_DET_0 " : "", + (pipe_de_status & 0x02) ? "DE_DET_1 " : "", + (pipe_de_status & 0x04) ? "DE_DET_2 " : "", + (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + } + + dev_info(dev, "%s: Successfully enabled %s pipe %u (vc_id %u) CSI %u %s lanes %s map\n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, vc_id, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2"); + + set_pipe_out: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max96724_set_pipe); + +static const struct of_device_id max96724_of_match[] = { + { .compatible = "maxim,d4xx-max96724", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max96724_of_match); + +#ifdef CONFIG_OF +static int max96724_parse_dt(struct max96724 *priv, + struct i2c_client *client) +{ + struct device_node *node = client->dev.of_node; + int err = 0; + const char *str_value; + int value; + const struct of_device_id *match; + + if (!node) + return -EINVAL; + + match = of_match_device(max96724_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return -EFAULT; + } + + err = of_property_read_string(node, "csi-mode", &str_value); + if (err < 0) { + dev_err(&client->dev, "csi-mode property not found\n"); + return err; + } + + if (!strcmp(str_value, "2x4")) { + priv->csi_mode = MAX96724_CSI_MODE_2X4; + } else if (!strcmp(str_value, "4x2")) { + priv->csi_mode = MAX96724_CSI_MODE_4X2; + } else { + dev_err(&client->dev, "invalid csi mode\n"); + return -EINVAL; + } + + err = of_property_read_u32(node, "max-src", &value); + if (err < 0) { + dev_err(&client->dev, "No max-src info\n"); + return err; + } + priv->max_src = value; + + priv->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0); + if (priv->reset_gpio < 0) { + dev_err(&client->dev, "reset-gpios not found %d\n", err); + return err; + } + + /* digital 1.2v */ + if (of_get_property(node, "vdd_cam_1v2-supply", NULL)) { + priv->vdd_cam_1v2 = regulator_get(&client->dev, "vdd_cam_1v2"); + if (IS_ERR(priv->vdd_cam_1v2)) { + dev_err(&client->dev, + "vdd_cam_1v2 regulator get failed\n"); + err = PTR_ERR(priv->vdd_cam_1v2); + priv->vdd_cam_1v2 = NULL; + return err; + } + } else { + priv->vdd_cam_1v2 = NULL; + } + + return 0; +} + +#else +static int max96724_parse_pdata(struct max96724 *priv, + struct i2c_client *client) +{ + struct max96724_pdata *pdata = client->dev.platform_data; + if (pdata) { + dev_dbg(&client->dev, "%s: Parsing Deserializer PDATA (mode=%s) \n", + __func__, + pdata->csi_mode == GMSL_CSI_4X2_MODE ? "4X2" : "2X4"); + if (pdata->csi_mode == GMSL_CSI_2X4_MODE || + pdata->csi_mode == GMSL_CSI_1X4_MODE) + priv->csi_mode = MAX96724_CSI_MODE_2X4; + else if (pdata->csi_mode == GMSL_CSI_4X2_MODE) + priv->csi_mode = MAX96724_CSI_MODE_4X2; + else { + dev_err(&client->dev, "invalid csi mode\n"); + return -EINVAL; + } + if (pdata->csi_phy == GMSL_CSI_CPHY) + priv->csi_phy = MAX96724_CSI_CPHY; + else if (pdata->csi_phy == GMSL_CSI_DPHY) + priv->csi_phy = MAX96724_CSI_DPHY; + else { + dev_err(&client->dev, "invalid csi phy\n"); + return -EINVAL; + } + + priv->max_src = pdata->max_src; + if (pdata->d4xx_hacks) { + priv->d4xx_hacks = pdata->d4xx_hacks; + } else { + priv->d4xx_hacks = 0; + } + } else { + priv->csi_mode = MAX96724_CSI_MODE_2X4; + priv->csi_phy = MAX96724_CSI_CPHY; + priv->max_src = 1; + priv->reset_gpio = 0; + priv->vdd_cam_1v2 = NULL; + priv->d4xx_hacks = 0; + } + + dev_info(&client->dev, "%s: %s Deserializer PDATA set CSI %s %s lane config (max_src=%u) \n", + __func__, + pdata->d4xx_hacks ? "d4xx" : "", + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->max_src); + + return 0; +} + +#endif + +static struct regmap_config max96724_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int max96724_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int max96724_probe(struct i2c_client *client) +#endif +{ + struct max96724 *priv; + int err = 0; + + dev_info(&client->dev, "[MAX96724]: probing GMSL Deserializer\n"); + + /* Try to wake up MAX96724 by attempting to write to various control registers */ + { + u8 wake_sequence[] = {MAX96724_CTRL1_ADDR, 0x00}; /* CTRL0 register, reset value */ + struct i2c_msg wake_msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = wake_sequence + }; + + dev_info(&client->dev, "Attempting to wake up MAX96724...\n"); + i2c_transfer(client->adapter, &wake_msg, 1); + msleep(50); /* Give device time to initialize */ + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv->i2c_client = client; + priv->regmap = devm_regmap_init_i2c(priv->i2c_client, + &max96724_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } + +#ifdef CONFIG_OF + err = max96724_parse_dt(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse dt\n"); + return -EFAULT; + } +#else + err = max96724_parse_pdata(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse pdata\n"); + return -EFAULT; + } +#endif + + max96724_pipes_reset(priv); + + dev_dbg(&client->dev, "[DEBUG] MAX96724_MAX_SOURCES defined as: %d, priv->max_src: %d\n", + MAX96724_MAX_SOURCES, priv->max_src); + + if (priv->max_src > MAX96724_MAX_SOURCES) { + dev_err(&client->dev, + "max sources more than currently supported\n"); + return -EINVAL; + } + + mutex_init(&priv->lock); + + dev_set_drvdata(&client->dev, priv); + + /* Verify device communication by reading chip ID registers */ + { + unsigned int reg_val; + int ret; + u8 buf[2]; + struct i2c_msg msgs[2]; + + dev_info(&client->dev, "Testing I2C communication at address 0x%02x\n", client->addr); + + /* First try raw I2C read of device ID register */ + buf[0] = 0x0D; /* Device ID register */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = buf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &buf[1]; + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + dev_info(&client->dev, "Raw I2C success! Device ID: 0x%02x\n", buf[1]); + } else { + dev_err(&client->dev, "Raw I2C failed: ret=%d\n", ret); + + /* Try alternative addresses */ + dev_info(&client->dev, "Trying alternative I2C addresses...\n"); + for (int alt_addr = 0x29; alt_addr <= 0x2C; alt_addr++) { + msgs[0].addr = alt_addr; + msgs[1].addr = alt_addr; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + dev_info(&client->dev, "Found device at address 0x%02x! Device ID: 0x%02x\n", + alt_addr, buf[1]); + break; + } + } + } + + /* Now try regmap-based access */ + err = max96724_read_reg(&client->dev, 0x0D, ®_val); + if (err) { + dev_err(&client->dev, "Regmap read device ID failed\n"); + } else { + dev_info(&client->dev, "Regmap success! Device ID: 0x%02x\n", reg_val); + } + + /* Read CTRL0 register to verify basic communication */ + err = max96724_read_reg(&client->dev, MAX96724_CTRL1_ADDR, ®_val); + if (err) { + dev_err(&client->dev, "Failed to read CTRL1 register (0x%x)\n", + MAX96724_CTRL1_ADDR); + } else { + dev_info(&client->dev, "MAX96724 CTRL1: 0x%02x\n", reg_val); + } + + /* Check initial link status on all ports */ + max96724_read_reg(&client->dev, 0x001A, ®_val); + dev_info(&client->dev, "Initial Link A status (0x001A): 0x%02x (LOCK=%d)\n", + reg_val, (reg_val >> 3) & 1); + max96724_read_reg(&client->dev, 0x000A, ®_val); + dev_info(&client->dev, "Initial Link B status (0x000A): 0x%02x (LOCK=%d)\n", + reg_val, (reg_val >> 3) & 1); + max96724_read_reg(&client->dev, 0x000B, ®_val); + dev_info(&client->dev, "Initial Link C status (0x000B): 0x%02x (LOCK=%d)\n", + reg_val, (reg_val >> 3) & 1); + max96724_read_reg(&client->dev, 0x000C, ®_val); + dev_info(&client->dev, "Initial Link D status (0x000C): 0x%02x (LOCK=%d)\n", + reg_val, (reg_val >> 3) & 1); + + /* Read REG 0x06 (GMSL configuration) - this tells us if GMSL1 or GMSL2 */ + max96724_read_reg(&client->dev, 0x06, ®_val); + dev_dbg(&client->dev, "REG 0x06 (GMSL config): 0x%02x (Bit0-3: LinkA-D mode)\n", reg_val); + + } + + /* dev communication gets validated when GMSL link setup is done */ + dev_info(&client->dev, "%s: success\n", __func__); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int max96724_remove(struct i2c_client *client) +#else +static void max96724_remove(struct i2c_client *client) +#endif +{ + struct max96724 *priv; + + if (client != NULL) { + priv = dev_get_drvdata(&client->dev); + dev_info(&client->dev, "[MAX96724]: remove GMSL Deserializer\n"); + mutex_destroy(&priv->lock); +#ifdef CONFIG_OF + i2c_unregister_device(client); + client = NULL; +#endif + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +static const struct i2c_device_id max96724_id[] = { + { "d4xx-max96724", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max96724_id); + +static struct i2c_driver max96724_i2c_driver = { + .driver = { + .name = "d4xx-max96724", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max96724_of_match), + }, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + .probe = max96724_probe, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = max96724_probe, +#else + .probe = max96724_probe, +#endif + .remove = max96724_remove, + .id_table = max96724_id, +}; + +module_i2c_driver(max96724_i2c_driver); + +MODULE_DESCRIPTION("Quad GMSL Deserializer driver max96724"); +MODULE_AUTHOR("Florent Pirou "); +MODULE_LICENSE("GPL v2"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) +MODULE_VERSION(DRIVER_VERSION_SUFFIX); +#endif diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index af8acb31..76fb0e77 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -42,7 +42,11 @@ #ifdef CONFIG_VIDEO_D4XX_SERDES #include +#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#include +#else #include +#endif #else #include #define GMSL_CSI_DT_YUV422_8 0x1E @@ -154,6 +158,7 @@ #define MAX9295_I2C_5 0x0045 #define MAX9296_CTRL0 0x0010 +#define MAX96724_CTRL0 MAX9296_CTRL0 #define RESET_LINK (0x1 << 6) #define RESET_ONESHOT (0x1 << 5) #define AUTO_LINK (0x1 << 4) @@ -162,6 +167,7 @@ #define LINK_B (0x2) #define SPLITTER (0x3) #define MAX9296_NUM (4) +#define MAX96724_NUM MAX9296_NUM #define MAX9295_I2C_ADDR_DEF 0x40 #define D457_I2C_ADDR 0x10 @@ -206,6 +212,10 @@ enum ds5_mux_pad { if (ds5_raw_write(state, addr, buf, size)) \ return -EINVAL; } #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) +#define max96724_write_8_with_check(state, addr, buf) {\ + if (max96724_write_8(state, addr, buf)) \ + return -EINVAL; \ + } #define max9296_write_8_with_check(state, addr, buf) {\ if (max9296_write_8(state, addr, buf)) \ return -EINVAL; \ @@ -434,6 +444,11 @@ struct ds5_sensor { unsigned int n_formats; int pipe_id; int initialized; +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) + int routing_initialized; +#endif +#endif }; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM @@ -1618,8 +1633,13 @@ static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, pipe_id, data_type1, data_type2, vc_id); ret |= max9295_set_pipe(state->ser_dev, pipe_id, data_type1, data_type2, vc_id); +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + ret |= max96724_set_pipe(state->dser_dev, pipe_id, + data_type1, data_type2, vc_id); +#else ret |= max9296_set_pipe(state->dser_dev, pipe_id, data_type1, data_type2, vc_id); +#endif if (ret) dev_warn(&state->client->dev, "failed to set pipe %d, data_type1: 0x%x, \ @@ -1694,7 +1714,11 @@ static int ds5_configure(struct ds5 *state) vc_id); // reset data path when switching to Y12I if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + max96724_reset_oneshot(state->dser_dev); +#else max9296_reset_oneshot(state->dser_dev); +#endif if (ret < 0) return ret; #endif @@ -1823,10 +1847,45 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, struct ds5 *state = v4l2_get_subdevdata(sd); unsigned int i; int ret = 0; + struct v4l2_subdev_route *route; + struct v4l2_subdev_stream_configs *new_configs; + u32 idx; /* set state by vc */ ds5_s_state_pad(state, sensor->mux_pad); + if (sensor->routing_initialized) { + /* + * Fill in the 'pad' and stream' value for each item in the array from + * the routing table + */ + new_configs= &v4l2_state->stream_configs; + dev_dbg(sensor->sd.dev, "%s(): update %s route on stream-id=%u (num_configs=%u)\n", + __func__, + sensor->sd.name, + state->pad_to_vc[sensor->mux_pad], + new_configs->num_configs); + idx = 0; + for_each_active_route(&v4l2_state->routing, route) { + new_configs->configs[idx].pad = route->sink_pad; + new_configs->configs[idx].stream = state->pad_to_vc[sensor->mux_pad]; + + dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] sink pad/stream=%u/%u\n", + __func__, sd->name, i, route->sink_pad, state->pad_to_vc[sensor->mux_pad]); + + idx++; + + new_configs->configs[idx].pad = route->source_pad; + new_configs->configs[idx].stream = state->pad_to_vc[sensor->mux_pad]; + + dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] src pad/stream=%u/%u\n", + __func__, sd->name, i, route->sink_pad, state->pad_to_vc[sensor->mux_pad]); + + idx++; + } + return 0; + } + struct v4l2_subdev_route ds5_sensor_routes[] = { { .sink_stream = state->pad_to_vc[sensor->mux_pad], @@ -1847,8 +1906,15 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, __func__, sensor->sd.name); return ret; } - dev_dbg(sensor->sd.dev, "%s(): set %s stream-id=%u\n", - __func__, sensor->sd.name, state->pad_to_vc[sensor->mux_pad]); + + new_configs= &v4l2_state->stream_configs; + dev_dbg(sensor->sd.dev, "%s(): initialized %s route on stream-id=%u (num_configs=%u)\n", + __func__, + sensor->sd.name, + state->pad_to_vc[sensor->mux_pad], + new_configs->num_configs); + + sensor->routing_initialized = true; return 0; } @@ -3421,6 +3487,18 @@ static int ds5_board_setup(struct ds5 *state) .def_addr = 0x40, // todo: configurable .d4xx_hacks = 1, }; +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + static struct max96724_pdata max96724_pdata = { + .max_src = 4, + .csi_mode = GMSL_CSI_2X4_MODE, //GMSL_CSI_4X2_MODE, + .csi_phy = GMSL_CSI_CPHY, + .d4xx_hacks = 1, + }; + static struct i2c_board_info i2c_info_des = { + I2C_BOARD_INFO("d4xx-max96724", 0x48), + .platform_data = &max96724_pdata, + }; +#else static struct max9296_pdata max9296_pdata = { .max_src = 2, .csi_mode = GMSL_CSI_2X4_MODE, @@ -3430,6 +3508,7 @@ static int ds5_board_setup(struct ds5 *state) I2C_BOARD_INFO("d4xx-max9296", 0x48), .platform_data = &max9296_pdata, }; +#endif static struct i2c_board_info i2c_info_ser = { I2C_BOARD_INFO("d4xx-max9295", 0x42), .platform_data = &max9295_pdata, @@ -3469,18 +3548,25 @@ static int ds5_board_setup(struct ds5 *state) } } } - if ( pdata->subdev_info[0].aggregated_link ) { - snprintf(serdes_suffix, sizeof(serdes_suffix), "%s", pdata->subdev_info[0].suffix); - dev_info(dev, "PDATA GMSL port suffix %s\n", - i2c_info_des.addr, - serdes_suffix); - suffix = (const char) serdes_suffix[0]; /* d4xx suffix only uses subdevs first char */ - } else if (state->aggregated) + + /* + * suffix syntaxe for multiple D457 connected to 1 Deser : + * - IPU7 both standalone or aggregated links - + * - IPU6 when standalone when aggregated + */ + struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; + if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { + snprintf(serdes_suffix, sizeof(serdes_suffix), "%s", spdata->suffix); + } else if (state->aggregated) { suffix += 6; - dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", - suffix, - bus, pdata->subdev_info[0].board_info.addr, //48 - bus, pdata->subdev_info[0].ser_alias); //42 + snprintf(serdes_suffix, sizeof(serdes_suffix), "%c", spdata->suffix); + } else { + snprintf(serdes_suffix, sizeof(serdes_suffix), "%c", spdata->suffix); + } + dev_info(dev, "Init SerDes %s on %d@0x%x<->%d@0x%x\n", + serdes_suffix, + bus, pdata->subdev_info[0].board_info.addr, //48 + bus, pdata->subdev_info[0].ser_alias); //42 if (!state->dser_i2c) state->dser_i2c = i2c_new_client_device(adapter, &i2c_info_des); @@ -3511,8 +3597,8 @@ static int ds5_board_setup(struct ds5 *state) state->g_ctx.sdev_reg = state->client->addr; state->g_ctx.sdev_def = 0x10;// def-addr TODO: configurable // Address reassignment for d4xx-a 0x10->0x12 - dev_info(dev, "Address reassignment for %s-%c 0x%x->0x%x\n", - pdata->subdev_info[0].board_info.type, suffix, + dev_info(dev, "Address reassignment for %s-%s 0x%x->0x%x\n", + pdata->subdev_info[0].board_info.type, serdes_suffix, state->g_ctx.sdev_def, state->g_ctx.sdev_reg); //0x42, 0x44, 0x62, 0x64 state->g_ctx.ser_reg = pdata->subdev_info[0].ser_alias; @@ -3533,20 +3619,47 @@ static int ds5_board_setup(struct ds5 *state) state->dser_dev = &state->dser_i2c->dev; /* populate g_ctx from pdata */ +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + state->g_ctx.dst_csi_port = (suffix == 'a') ? GMSL_CSI_PORT_C : GMSL_CSI_PORT_B; + state->g_ctx.csi_mode = GMSL_CSI_2X4_MODE; //GMSL_CSI_4X2_MODE; +#else state->g_ctx.dst_csi_port = GMSL_CSI_PORT_A; - state->g_ctx.src_csi_port = GMSL_CSI_PORT_B; state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; - if (state->aggregated) { // aggregation +#endif + +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + /* Derive Deser CSI link mapping */ + switch (serdes_suffix[0]) { + case 'b': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; + break; + case 'c': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_C; + break; + case 'd': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_D; + break; + default: + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; + break; + }; +#else + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; +#endif + if (state->aggregated) { // dual aggregated-link fallback dev_info(dev, "configure GMSL port B\n"); state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; } else { dev_info(dev, "configure GMSL port A\n"); - state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; } state->g_ctx.st_vc = 0; state->g_ctx.dst_vc = 0; +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + state->g_ctx.num_csi_lanes = (state->g_ctx.csi_mode == GMSL_CSI_4X2_MODE) ? 2 : 4; +#else state->g_ctx.num_csi_lanes = 2; +#endif state->g_ctx.s_dev = dev; for (i = 0; i < MAX_DEV_NUM; i++) { @@ -3594,13 +3707,23 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) mutex_lock(&serdes_lock__); +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + max96724_power_off(state->dser_dev); + /* For now no separate power on required for serializer device */ + max96724_power_on(state->dser_dev); +#else max9296_power_off(state->dser_dev); /* For now no separate power on required for serializer device */ max9296_power_on(state->dser_dev); +#endif dev_dbg(dev, "Setup SERDES addressing and control pipeline\n"); /* setup serdes addressing and control pipeline */ +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + err = max96724_setup_link(state->dser_dev, &state->client->dev); +#else err = max9296_setup_link(state->dser_dev, &state->client->dev); +#endif if (err) { dev_err(dev, "gmsl deserializer link config failed\n"); goto error; @@ -3614,7 +3737,11 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) err= -ENOTSUPP; } +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + des_err = max96724_setup_control(state->dser_dev, &state->client->dev); +#else des_err = max9296_setup_control(state->dser_dev, &state->client->dev); +#endif if (des_err) { dev_warn(dev, "gmsl deserializer setup failed\n"); /* overwrite err only if deser setup also failed */ @@ -3627,10 +3754,17 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) } #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) +#ifdef CONFIG_VIDEO_D4XX_MAX96724 +static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,2,3, 3,2,0,1}; +module_param_array(sensor_vc, ushort, NULL, 0444); +MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" + "\t\tsensor_vc=0,1,2,3,2,3,0,1,1,0,2,3,3,2,0,1"); +#else static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" "\t\tsensor_vc=0,1,2,3,2,3,0,1"); +#endif //#define PLATFORM_AXIOMTEK 1 #ifdef PLATFORM_AXIOMTEK @@ -3639,8 +3773,13 @@ static short serdes_bus[4] = {5, 5, 5, 5}; static short serdes_bus[4] = {2, 2, 4, 4}; #endif module_param_array(serdes_bus, ushort, NULL, 0444); -MODULE_PARM_DESC(serdes_bus, "max9295/6 deserializer i2c bus\n" +#ifdef CONFIG_VIDEO_D4XX_MAX96724 +MODULE_PARM_DESC(serdes_bus, "d4xx-max96724 deserializer i2c bus\n" + "\t\tserdes_bus=2,2,4,4"); +#else +MODULE_PARM_DESC(serdes_bus, "d4xx-max9296 deserializer i2c bus\n" "\t\tserdes_bus=2,2,4,4"); +#endif // Deserializer addresses can be 0x40 0x48 0x4a #ifdef PLATFORM_AXIOMTEK @@ -3649,8 +3788,13 @@ static unsigned short des_addr[4] = {0x48, 0x4a, 0x68, 0x6c}; static unsigned short des_addr[4] = {0x48, 0x4a, 0x48, 0x4a}; #endif module_param_array(des_addr, ushort, NULL, 0444); -MODULE_PARM_DESC(des_addr, "max9296 deserializer i2c address\n" +#ifdef CONFIG_VIDEO_D4XX_MAX96724 +MODULE_PARM_DESC(des_addr, "d4xx-max96724 deserializer i2c address\n" + "\t\tdes_addr=0x27,0x27,0x27,0x27"); +#else +MODULE_PARM_DESC(des_addr, "d4xx-max9296 deserializer i2c address\n" "\t\tdes_addr=0x48,0x4a,0x48,0x4a"); +#endif static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) @@ -3661,8 +3805,13 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) for (i = 0; i < 4; i++) { if (c_bus == serdes_bus[i]) { c->addr = des_addr[i]; +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + dev_info(&c->dev, "Set max96724@%d-0x%x Link reset\n", + c_bus, c->addr); +#else dev_info(&c->dev, "Set max9296@%d-0x%x Link reset\n", c_bus, c->addr); +#endif ds5_write_8(state, 0x1000, 0x40); // sensor reset link } } @@ -3729,7 +3878,11 @@ static int ds5_serdes_setup(struct ds5 *state) } /* Register sensor to deserializer dev */ +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + ret = max96724_sdev_register(state->dser_dev, &state->g_ctx); +#else ret = max9296_sdev_register(state->dser_dev, &state->g_ctx); +#endif if (ret) { dev_err(&c->dev, "gmsl deserializer register failed\n"); return ret; @@ -3757,9 +3910,13 @@ static int ds5_serdes_setup(struct ds5 *state) return ret; } +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + ret = max96724_init_settings(state->dser_dev); +#else ret = max9296_init_settings(state->dser_dev); +#endif if (ret) { - dev_warn(&c->dev, "%s, failed to init max9296 settings\n", + dev_warn(&c->dev, "%s, failed to init max9x settings\n", __func__); return ret; } @@ -4013,12 +4170,18 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, sd->grp_id = *dev_num; v4l2_set_subdevdata(sd, state); #ifndef CONFIG_OF + struct d4xx_subdev_info *spdata = &dpdata->subdev_info[0]; /* - * TODO: suffix for 2 D457 connected to 1 Deser + * suffix syntaxe for multiple D457 connected to 1 Deser : + * - IPU7 - + * - IPU6 when standalone when agregated */ - if (state->aggregated & 1) + if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { + snprintf(sd->name, sizeof(sd->name), "D4XX %s %s", name, spdata->suffix); + }else if (state->aggregated & 1) { suffix += 6; - snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, suffix); + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, suffix); + } #else snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", name, i2c_adapter_id(c->adapter), c->addr); @@ -4075,6 +4238,11 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) dev_dbg(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); sensor->initialized = true; +#ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) + sensor->routing_initialized = false; +#endif +#endif return 0; @@ -4377,17 +4545,15 @@ static int _ds5_mux_set_routing(struct v4l2_subdev *sd, __func__, sd->name, ret); return ret; } - state->routing_initialized = 1; + struct v4l2_subdev_stream_configs *stream_configs= &v4l2_state->stream_configs; for (unsigned int i = 0; i < stream_configs->num_configs; i++) { u32 sink_pad = stream_configs->configs[i].pad; u32 sink_stream = stream_configs->configs[i].stream; + struct v4l2_subdev_state *remote_v4l2_state; struct ds5_sensor *s = NULL; - if (sink_pad != DS5_MUX_PAD_EXTERNAL) - state->pad_to_vc[sink_pad] = sink_stream; - switch (sink_pad) { case DS5_MUX_PAD_DEPTH: s = &state->depth.sensor; @@ -4413,11 +4579,13 @@ static int _ds5_mux_set_routing(struct v4l2_subdev *sd, } stream_configs->configs[i].fmt = *ffmt; - + state->pad_to_vc[sink_pad] = sink_stream; + dev_dbg(sd->dev, "%s:%d: stream_configs->configs[%u] pad/stream=%u/%u format: code %x, res %ux%u\n", __func__, __LINE__, i, sink_pad, sink_stream, ffmt->code, ffmt->width, ffmt->height); } + state->routing_initialized = true; return 0; } @@ -4426,13 +4594,22 @@ static int ds5_mux_set_routing(struct v4l2_subdev *sd, enum v4l2_subdev_format_whence which, struct v4l2_subdev_krouting *routing) { + struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + int ret = 0; + dev_dbg(sd->dev,"%s:%d: v4l2_state=%p, sd_format_whence=%s\n", __func__, __LINE__, v4l2_state, which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY" : "V4L2_SUBDEV_FORMAT_ACTIVE" ); - _ds5_mux_set_routing(sd, v4l2_state, routing); - - return 0; + ret = _ds5_mux_set_routing(sd, v4l2_state, routing); + if (ret < 0) { + dev_warn(sd->dev, "%s():%d: %s add .num_routes=%u failed (sd_state %p)\n", + __func__, __LINE__, sd->name, routing->num_routes, v4l2_state); + } else { + dev_dbg(sd->dev, "%s():%d: %s add .num_routes=%u applied (sd_state %p)\n", + __func__, __LINE__, sd->name, routing->num_routes, v4l2_state); + } + return ret; } #endif #endif @@ -4619,7 +4796,6 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, return -ENODEV; } - //fmt->stream = 0; dev_dbg(sd->dev, "%s(): %s remote pad/stream : %u/%u, \ fmt code/size: 0x%x/%ux%u\n", __func__, @@ -4948,9 +5124,15 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) // set manually, need to configure vc in pdata state->g_ctx.dst_vc = vc_id; #endif +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + sensor->pipe_id = + max96724_get_available_pipe_id(state->dser_dev, + (int)state->g_ctx.dst_vc); +#else sensor->pipe_id = max9296_get_available_pipe_id(state->dser_dev, (int)state->g_ctx.dst_vc); +#endif if (sensor->pipe_id < 0) { dev_err(&state->client->dev, "No free pipe in max9296\n"); @@ -4991,6 +5173,12 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) dev_dbg(&state->client->dev, "started after %dms\n", i * DS5_START_POLL_TIME); } +#ifdef CONFIG_VIDEO_D4XX_SERDES +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + max9295_check_status(state->ser_dev); + max96724_check_status(state->dser_dev); +#endif +#endif } else { // off ret = ds5_write(state, DS5_START_STOP_STREAM, DS5_STREAM_STOP | stream_id); @@ -5002,7 +5190,11 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) if (state->is_y8 && state->ir.sensor.config.format->data_type == GMSL_CSI_DT_RGB_888) { +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + max96724_reset_oneshot(state->dser_dev); +#else max9296_reset_oneshot(state->dser_dev); +#endif } #ifndef CONFIG_TEGRA_CAMERA_PLATFORM // reset for IPU6 @@ -5016,11 +5208,20 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) } #endif if (!streaming) { +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + dev_warn(&state->client->dev, "max96724_reset_oneshot\n"); + max96724_reset_oneshot(state->dser_dev); +#else dev_warn(&state->client->dev, "max9296_reset_oneshot\n"); max9296_reset_oneshot(state->dser_dev); +#endif } #endif +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + if (max96724_release_pipe(state->dser_dev, sensor->pipe_id) < 0) +#else if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) +#endif dev_warn(&state->client->dev, "release pipe failed\n"); sensor->pipe_id = -1; #else @@ -5044,7 +5245,11 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) restore_s_state: #ifdef CONFIG_VIDEO_D4XX_SERDES if (on && sensor->pipe_id >= 0) { +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + if (max96724_release_pipe(state->dser_dev, sensor->pipe_id) < 0) +#else if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) +#endif dev_warn(&state->client->dev, "release pipe failed\n"); sensor->pipe_id = -1; } @@ -5089,6 +5294,7 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, { unsigned int i; struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + struct v4l2_subdev_route *route; struct ds5_sensor *sensor = NULL; int ret = -EINVAL; @@ -5096,15 +5302,12 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, return ret; if (pad < 0 || pad >= DS5_MUX_PAD_COUNT) - return -EINVAL; - - struct v4l2_subdev_state *v4l2_state = v4l2_subdev_lock_and_get_active_state(sd); - struct v4l2_subdev_route *route; + return ret; - dev_dbg(sd->dev, "%s(): name=%s pad=%u state=%p \n", - __func__, sd->name, pad, v4l2_state); + dev_dbg(sd->dev, "%s(): set desc name=%s pad=%u from each route\n", + __func__, sd->name, pad); - desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + struct v4l2_subdev_state *v4l2_state = v4l2_subdev_lock_and_get_active_state(sd); for_each_active_route(&v4l2_state->routing, route) { if (route->source_pad != pad) @@ -5139,12 +5342,15 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, struct ds5_format *mf = sensor->config.format; - dev_dbg(sd->dev, "sensor %s stream:%u pad:%u size:%dx%d code::0x%x vc:%u mbus:0x%x dt:0x%x\n", + dev_dbg(sd->dev, + "sensor %s stream:%u pad:%u size:%dx%d code::0x%x vc:%u " \ + "mbus:0x%x dt:0x%x\n", sensor->sd.name, route->sink_stream, route->sink_pad, sensor->format.width, sensor->format.height, sensor->format.code, - state->pad_to_vc[route->sink_pad], mf->mbus_code, mf->data_type); + state->pad_to_vc[route->sink_pad], + mf->mbus_code, mf->data_type); - desc->entry[desc->num_entries].flags = 0; //V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + desc->entry[desc->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; desc->entry[desc->num_entries].stream = route->sink_stream; desc->entry[desc->num_entries].pixelcode = mf->mbus_code; desc->entry[desc->num_entries].length = 0; @@ -5152,6 +5358,15 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, desc->entry[desc->num_entries].bus.csi2.dt = mf->data_type; desc->num_entries++; } + /* for Metadata + source_desc.entry[desc_entries].flags = 0; + source_desc.entry[desc_entries].pixelcode = MEDIA_BUS_FMT_FIXED; + source_desc.entry[desc_entries].pixelcode = 0x12; + source_desc.entry[desc_entries].length = 68; + desc_entries++; + */ + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; ret = 0; out_unlock: @@ -5321,7 +5536,43 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, if (state->routing_initialized) return 0; + state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + /* Derive Deser CSI link mapping */ + switch (state->g_ctx.serdes_csi_link) { + case GMSL_SERDES_CSI_LINK_A: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + break; + case GMSL_SERDES_CSI_LINK_B: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[4]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[5]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; + break; + case GMSL_SERDES_CSI_LINK_C: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[8]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[9]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[10]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[11]; + break; + case GMSL_SERDES_CSI_LINK_D: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[12]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[13]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[14]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[16]; + break; + default: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + break; + } +#else if (!state->aggregated) { state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; @@ -5333,6 +5584,7 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; } +#endif struct v4l2_subdev_route ds5_mux_routes[] = { //DEPTH @@ -5478,9 +5730,18 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) snprintf(sd->name, sizeof(sd->name), "DS5 mux %d-%04x", i2c_adapter_id(c->adapter), c->addr); #else - if (state->aggregated) + struct d4xx_subdev_info *spdata = &dpdata->subdev_info[0]; + /* + * suffix syntaxe for multiple D457 connected to 1 Deser : + * - IPU7 - + * - IPU6 when standalone when agregated + */ + if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { + snprintf(sd->name, sizeof(sd->name), "DS5 mux %s", spdata->suffix); + }else if (state->aggregated & 1) { suffix += 6; - snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); + snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); + } #endif #ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -6188,10 +6449,20 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) *dev_num = MKDEV(MAJOR(*dev_num), MINOR(*dev_num)); /* Create a device node for this device. */ #ifndef CONFIG_OF - if (state->aggregated) + struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; + /* + * suffix syntaxe for multiple D457 connected to 1 Deser : + * - IPU7 both standalone or aggregated links - + * - IPU6 when standalone when aggregated + */ + if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { + snprintf(dev_name, sizeof(dev_name), "D4XX %s %s", + DS5_DRIVER_NAME_DFU, spdata->suffix); + }else if (state->aggregated & 1) { suffix += 6; - snprintf(dev_name, sizeof(dev_name), "%s-%c", - DS5_DRIVER_NAME_DFU, suffix); + snprintf(dev_name, sizeof(dev_name), "%s-%c", + DS5_DRIVER_NAME_DFU, suffix); + } #else snprintf (dev_name, sizeof(dev_name), "%s-%d-%04x", DS5_DRIVER_NAME_DFU, i2c_adapter_id(c->adapter), c->addr); @@ -6233,6 +6504,41 @@ static void ds5_substream_init(struct ds5 *state) unsigned int mipi_csi2_type; s64 *sub_stream = NULL; state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + /* Derive Deser CSI link mapping */ + switch (state->g_ctx.serdes_csi_link) { + case GMSL_SERDES_CSI_LINK_A: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + break; + case GMSL_SERDES_CSI_LINK_B: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[4]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[5]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; + break; + case GMSL_SERDES_CSI_LINK_C: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[8]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[9]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[10]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[11]; + break; + case GMSL_SERDES_CSI_LINK_D: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[12]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[13]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[14]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[16]; + break; + default: + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + break; + } +#else if (!state->aggregated) { state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; @@ -6244,10 +6550,46 @@ static void ds5_substream_init(struct ds5 *state) state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; } +#endif for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) state->pad_to_substream[i] = -1; /* match for IPU6 CSI2 BE SOC video capture pads */ +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + /* Derive Deser CSI link mapping */ + switch (state->g_ctx.serdes_csi_link) { + case GMSL_SERDES_CSI_LINK_A: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + break; + case GMSL_SERDES_CSI_LINK_B: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 6; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 8; + state->pad_to_substream[DS5_MUX_PAD_IR] = 10; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; + break; + case GMSL_SERDES_CSI_LINK_C: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 1; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 3; + state->pad_to_substream[DS5_MUX_PAD_IR] = 12; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 13; + break; + case GMSL_SERDES_CSI_LINK_D: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 7; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 9; + state->pad_to_substream[DS5_MUX_PAD_IR] = 14; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 15; + break; + default: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + break; + } +#else if (!state->aggregated) { state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; @@ -6260,6 +6602,7 @@ static void ds5_substream_init(struct ds5 *state) state->pad_to_substream[DS5_MUX_PAD_IR] = 10; state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; } +#endif dev_info(&state->client->dev, "%s() IPU6 CSI2 BE SOC video capture init : \n", __func__); for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) if (state->pad_to_substream[i] >= 0) @@ -6738,10 +7081,10 @@ static int ds5_probe(struct i2c_client *c) if (!state->g_ctx.serdev_found) dev_warn(&c->dev, "graceful fallback due to unresponsive max9295, isolated SerDes %s single-link\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B"); + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link)); else dev_warn(&c->dev, "graceful fallback due to unresponsive d4xx, isolated SerDes %s single-link\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B"); + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link)); mutex_lock(&serdes_lock__); if (state->ser_i2c) { @@ -6751,13 +7094,13 @@ static int ds5_probe(struct i2c_client *c) } if (state->dser_i2c && !state->aggregated) { dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link), state->dser_i2c->addr); i2c_unregister_device(state->dser_i2c); state->dser_st.isolated = true; } else if (state->dser_i2c && graceful_fallback) { dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link), state->dser_i2c->addr); i2c_unregister_device(state->dser_i2c); } @@ -6795,6 +7138,18 @@ static void ds5_remove(struct i2c_client *c) if (ret) dev_warn(&c->dev, "failed in 9295 reset control\n"); +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + if (state->dser_i2c) { + dev_info(&c->dev, "ignore 96724 reset control, already remove for bus %d\n", c_bus); + } else { + dev_info(&c->dev, "trigger 96724 reset control on bus %d\n", c_bus); + ret = max96724_reset_control(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed in 96724 reset control\n"); + } +#else if (state->dser_i2c) { dev_info(&c->dev, "ignore 9296 reset control, already remove for bus %d\n", c_bus); } else { @@ -6805,6 +7160,7 @@ static void ds5_remove(struct i2c_client *c) dev_warn(&c->dev, "failed in 9296 reset control\n"); } +#endif ret = max9295_sdev_unpair(state->ser_dev, state->g_ctx.s_dev); if (ret) @@ -6813,14 +7169,24 @@ static void ds5_remove(struct i2c_client *c) if (state->dser_i2c) { dev_info(&c->dev, "ignore 9296 unregister sdev, already remove for bus %d\n", c_bus); } else { +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + dev_info(&c->dev, "unregister 96724 sdev on bus %d\n", c_bus); + ret = max96724_sdev_unregister(state->dser_dev, + state->g_ctx.s_dev); +#else dev_info(&c->dev, "unregister 9296 sdev on bus %d\n", c_bus); ret = max9296_sdev_unregister(state->dser_dev, state->g_ctx.s_dev); +#endif if (ret) dev_warn(&c->dev, "failed to sdev unregister sdev\n"); +#ifdef CONFIG_VIDEO_D4XX_MAX96724 + max96724_power_off(state->dser_dev); +#else max9296_power_off(state->dser_dev); +#endif } mutex_unlock(&serdes_lock__); break; @@ -6834,11 +7200,11 @@ static void ds5_remove(struct i2c_client *c) if (state->dser_i2c && !state->aggregated && !state->dser_st.isolated) { i2c_unregister_device(state->dser_i2c); dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link), state->dser_i2c->addr); } else if (state->dser_i2c && graceful_fallback) { dev_info(&c->dev, "remove unresponding %s single-link deserializer i2c device 0x%x\n", - state->g_ctx.serdes_csi_link == GMSL_SERDES_CSI_LINK_A ? "GMSL A": "GMSL B", + d4xx_subdev_csi_link_id(state->g_ctx.serdes_csi_link), state->dser_i2c->addr); i2c_unregister_device(state->dser_i2c); } diff --git a/include/media/gmsl-link.h b/include/media/gmsl-link.h index 533f31eb..de18d921 100644 --- a/include/media/gmsl-link.h +++ b/include/media/gmsl-link.h @@ -46,8 +46,14 @@ #define GMSL_CSI_PORT_E 0x4 #define GMSL_CSI_PORT_F 0x5 +#define GMSL_CSI_CPHY 0x0 +#define GMSL_CSI_DPHY 0x1 + +#define GMSL_SERDES_CSI_LINK_UNKNOWN 0x0 #define GMSL_SERDES_CSI_LINK_A 0x1 #define GMSL_SERDES_CSI_LINK_B 0x2 +#define GMSL_SERDES_CSI_LINK_C 0x4 +#define GMSL_SERDES_CSI_LINK_D 0x8 /* Didn't find kernel defintions, for now adding here */ #define GMSL_CSI_DT_RAW_12 0x2C diff --git a/include/media/i2c/d4xx-max96724.h b/include/media/i2c/d4xx-max96724.h new file mode 100644 index 00000000..dc39d140 --- /dev/null +++ b/include/media/i2c/d4xx-max96724.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017-2026, INTEL CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MAX96724_H__ +#define __MAX96724_H__ + + +#include +#include +/** + * \defgroup max96724 MAX96724 deserializer driver + * + * Controls the MAX96724 deserializer module. + * + * @ingroup serdes_group + * @{ + */ +struct max96724_pdata { + bool splitter_enabled; + __u32 max_src; + __u32 csi_mode; + __u32 d4xx_hacks; + __u32 csi_phy; +}; + +int max96724_get_available_pipe_id(struct device *dev, int vc_id); +int max96724_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id); +int max96724_release_pipe(struct device *dev, int pipe_id); +void max96724_reset_oneshot(struct device *dev); + +/** + * Puts a deserializer device in single exclusive link mode, so link-specific + * I2C overrides can be performed for sensor and serializer devices. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_setup_link(struct device *dev, struct device *s_dev); + +/** + * @brief Sets up a deserializer link's control pipeline. + * + * Puts the deserializer in dual splitter mode. You must call this function + * during device boot, after max96724_setup_link(). + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_setup_control(struct device *dev, struct device *s_dev); + +/** + * @brief Resets a deserializer device's link control pipeline. + * + * The deserializer driver internally decrements the reference count and + * resets the deserializer device if all the source sensor devices are + * powered off, resetting all control and streaming configuration. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_reset_control(struct device *dev, struct device *s_dev); + +/** + * @brief Registers a source sensor device with a deserializer device. + * + * The deserializer driver internally checks all perquisites and compatibility + * factors. If it finds that the registration request is valid, + * it stores the source's @ref gmsl_link_ctx context handle in the source list + * maintained by the deserializer driver instance. + * + * @param [in] dev The deserializer device handle. + * @param [in] g_ctx A @c gmsl_link_ctx structure handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx); + +/** + * Unregisters a source sensor device from its deserializer device. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_sdev_unregister(struct device *dev, struct device *s_dev); + +/** + * @brief Powers on the max96724 deserializer module. + * + * Asserts shared reset GPIO and powers on the regulator; + * maintains the reference count internally for source devices. + * + * @param [in] dev The deserializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max96724_power_on(struct device *dev); + +/** + * @brief Powers off the max96724 deserializer module. + * + * Deasserts the shared reset GPIO and powers off the regulator based on + * the reference count. + * + * @param [in] dev The deserializer device handle. + */ +void max96724_power_off(struct device *dev); + +int max96724_init_settings(struct device *dev); +int max96724_set_mfp(struct device *dev, int pin, int val); +int max96724_check_status(struct device *dev); +/** @} */ + +#endif /* __MAX96724_H__ */ diff --git a/include/media/i2c/d4xx_pdata.h b/include/media/i2c/d4xx_pdata.h index 7f6037d0..da235846 100644 --- a/include/media/i2c/d4xx_pdata.h +++ b/include/media/i2c/d4xx_pdata.h @@ -4,11 +4,19 @@ #ifndef D457_H #define D457_H +#include + #define D457_NAME "d4xx" #define MAX9296_NAME "MAX9296" +#define MAX96724_NAME "MAX96724" #define D457_I2C_ADDRESS 0x10 +#define d4xx_subdev_csi_link_id(link) \ + link == GMSL_SERDES_CSI_LINK_A ? "GMSL A" : \ + GMSL_SERDES_CSI_LINK_B ? "GMSL B" : \ + GMSL_SERDES_CSI_LINK_C ? "GMSL C" : "GMSL D" + struct d4xx_subdev_info { struct i2c_board_info board_info; int i2c_adapter_id; @@ -16,6 +24,8 @@ struct d4xx_subdev_info { unsigned short phy_i2c_addr; unsigned short ser_alias; char suffix[5]; /* suffix for subdevs */ + unsigned short ser_phys_addr; + unsigned int sensor_dt; int aggregated_link; }; @@ -23,11 +33,14 @@ struct d4xx_pdata { unsigned int subdev_num; struct d4xx_subdev_info *subdev_info; unsigned int reset_gpio; - int FPD_gpio; - const char suffix; + unsigned int FPD_gpio; + char suffix; unsigned int link_freq_mbps; + enum v4l2_mbus_type bus_type; unsigned int deser_nlanes; unsigned int ser_nlanes; + unsigned int des_port; + char ser_name[I2C_NAME_SIZE]; struct i2c_board_info *deser_board_info; }; #endif From 3e9d38bf6981012d8d73e447c00f9f23c3edb04b Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 9 Jan 2026 11:52:47 -0700 Subject: [PATCH 38/72] d4xx-compat: fix dfu sysfs truncated devname issue ``` [ 68.734388] d4xx 1-0012: ds5_read(): i2c read 0x5020: 0xdead [ 68.734390] d4xx 1-0012: ds5_chrdev_init() [ 68.734411] sysfs: cannot create duplicate filename '/devices/virtual/d4xx-class/D4XX d4xx-dfu a-' [ 68.734413] CPU: 6 UID: 0 PID: 2327 Comm: modprobe Tainted: G W OE 6.17.0-1006-oem #6-Ubuntu PREEMPT(voluntary) [ 68.734415] Tainted: [W]=WARN, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE [ 68.734416] Hardware name: Intel Corporation Panther Lake Client Platform/PTL-UH DDR5 Embedded CRB, BIOS PTLPFWI1.R00.3332.D52.2509040547 09/04/2025 [ 68.734417] Call Trace: [ 68.734418] [ 68.734421] dump_stack_lvl+0x76/0xa0 [ 68.734425] dump_stack+0x10/0x20 [ 68.734426] sysfs_warn_dup+0x8a/0xb0 [ 68.734430] sysfs_create_dir_ns+0xde/0xf0 [ 68.734433] kobject_add_internal+0xa8/0x2f0 [ 68.734436] kobject_add+0x92/0x100 [ 68.734437] ? get_device_parent+0xd0/0x210 [ 68.734441] device_add+0xdf/0x720 [ 68.734443] device_create_groups_vargs+0x118/0x140 [ 68.734445] device_create+0x50/0x80 [ 68.734450] ds5_probe+0xcbd/0x17f0 [d4xx] [ 68.734455] i2c_device_probe+0x198/0x390 [ 68.734457] really_probe+0xee/0x3c0 [ 68.734459] __driver_probe_device+0x8c/0x180 [ 68.734461] driver_probe_device+0x24/0xd0 [ 68.734462] __device_attach_driver+0xcd/0x170 [ 68.734463] ? __pfx___device_attach_driver+0x10/0x10 [ 68.734464] bus_for_each_drv+0x93/0xf0 [ 68.734465] __device_attach+0xb5/0x1d0 [ 68.734467] device_initial_probe+0x13/0x20 [ 68.734468] bus_probe_device+0x9f/0xb0 [ 68.734469] device_add+0x511/0x720 [ 68.734470] device_register+0x1a/0x30 [ 68.734472] i2c_new_client_device+0x209/0x460 [ 68.734474] v4l2_i2c_new_subdev_board+0x5e/0x100 [videodev] [ 68.734486] isys_probe+0xa23/0xd90 [intel_ipu7_isys] [ 68.734493] ? __pfx_isys_probe+0x10/0x10 [intel_ipu7_isys] [ 68.734497] auxiliary_bus_probe+0x3e/0xa0 [ 68.734499] really_probe+0xee/0x3c0 [ 68.734500] __driver_probe_device+0x8c/0x180 [ 68.734501] driver_probe_device+0x24/0xd0 [ 68.734503] __driver_attach+0x10b/0x210 [ 68.734504] ? __pfx___driver_attach+0x10/0x10 [ 68.734505] bus_for_each_dev+0x89/0xf0 [ 68.734506] driver_attach+0x1e/0x30 [ 68.734507] bus_add_driver+0x14e/0x290 [ 68.734508] driver_register+0x5e/0x130 [ 68.734510] __auxiliary_driver_register+0x73/0xf0 [ 68.734511] ? __pfx_isys_driver_init+0x10/0x10 [intel_ipu7_isys] [ 68.734516] isys_driver_init+0x23/0xff0 [intel_ipu7_isys] [ 68.734519] do_one_initcall+0x5a/0x340 [ 68.734522] do_init_module+0x97/0x2c0 [ 68.734526] load_module+0x99c/0xac0 [ 68.734527] init_module_from_file+0x95/0x100 [ 68.734529] idempotent_init_module+0x10f/0x300 [ 68.734531] __x64_sys_finit_module+0x73/0xe0 [ 68.734533] x64_sys_call+0x1784/0x2680 [ 68.734534] do_syscall_64+0x80/0xa20 [ 68.734537] ? __rseq_handle_notify_resume+0x36/0x70 [ 68.734540] ? arch_exit_to_user_mode_prepare.isra.0+0xc2/0xe0 [ 68.734541] ? do_syscall_64+0xb6/0xa20 [ 68.734543] ? putname+0x65/0x90 [ 68.734546] ? do_sys_openat2+0xa2/0xf0 [ 68.734549] ? __x64_sys_openat+0x54/0xa0 [ 68.734550] ? arch_exit_to_user_mode_prepare.isra.0+0xd/0xe0 [ 68.734552] ? do_syscall_64+0xb6/0xa20 [ 68.734553] ? arch_exit_to_user_mode_prepare.isra.0+0xd/0xe0 [ 68.734554] ? do_syscall_64+0xb6/0xa20 [ 68.734555] ? exc_page_fault+0x90/0x1b0 [ 68.734556] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 68.734558] RIP: 0033:0x79620e92728d [ 68.734560] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b bb 0d 00 f7 d8 64 89 01 48 [ 68.734561] RSP: 002b:00007ffcdc3577e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 68.734562] RAX: ffffffffffffffda RBX: 000055d7420a7340 RCX: 000079620e92728d [ 68.734563] RDX: 0000000000000004 RSI: 000055d7420b21a0 RDI: 0000000000000010 [ 68.734564] RBP: 00007ffcdc3578a0 R08: 0000000000000040 R09: 00007ffcdc357830 [ 68.734564] R10: 000079620ea03b20 R11: 0000000000000246 R12: 000055d7420b21a0 [ 68.734565] R13: 0000000000040000 R14: 000055d7420a7070 R15: 000055d7420b21a0 [ 68.734566] [ 68.734566] kobject: kobject_add_internal failed for D4XX d4xx-dfu a- with -EEXIST, don't try to register things with the same name in the same directory. [ 68.734574] d4xx 1-0012: Could not create device [ 68.734592] d4xx 1-0012: graceful fallback due to unresponsive d4xx, isolated SerDes GMSL A single-link [ 68.734593] d4xx 1-0012: remove unresponding serializer i2c device 0x42 ``` Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 76fb0e77..e407563d 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -6414,7 +6414,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) char suffix = pdata->suffix; #endif struct device *chr_dev; - char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 8]; + char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 10]; dev_t *dev_num = &c->dev.devt; int ret; @@ -6452,8 +6452,8 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; /* * suffix syntaxe for multiple D457 connected to 1 Deser : - * - IPU7 both standalone or aggregated links - - * - IPU6 when standalone when aggregated + * - IPU7/IPU6 upstream both standalone or aggregated links - + * - IPU6 legacy when standalone when aggregated */ if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { snprintf(dev_name, sizeof(dev_name), "D4XX %s %s", From fb7ab49c129b68887a7a635bb5ab22ea651e51f6 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 30 Jan 2026 10:37:20 -0700 Subject: [PATCH 39/72] d4xx-compat: fix default v4l2 vc_id to stream_id mapping Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index e407563d..558f9fd3 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3755,10 +3755,10 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #ifdef CONFIG_VIDEO_D4XX_MAX96724 -static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,2,3, 3,2,0,1}; +static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,3,2, 3,2,1,0}; module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" - "\t\tsensor_vc=0,1,2,3,2,3,0,1,1,0,2,3,3,2,0,1"); + "\t\tsensor_vc=0,1,2,3,2,3,0,1,1,0,3,2,3,2,1,0"); #else static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; module_param_array(sensor_vc, ushort, NULL, 0444); From 04d03f046fee1acac8dc35d2b41d7b0edac508fb Mon Sep 17 00:00:00 2001 From: Florent Pirou Date: Mon, 2 Feb 2026 07:17:45 -0700 Subject: [PATCH 40/72] d4xx-compat: fix dfu char device sysfs naming Signed-off-by: Florent Pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 558f9fd3..447db269 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -6456,7 +6456,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) * - IPU6 legacy when standalone when aggregated */ if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { - snprintf(dev_name, sizeof(dev_name), "D4XX %s %s", + snprintf(dev_name, sizeof(dev_name), "%s-%s", DS5_DRIVER_NAME_DFU, spdata->suffix); }else if (state->aggregated & 1) { suffix += 6; From 13c9393166e583f91e7e660ec235d5e66139be3d Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 5 Feb 2026 00:21:23 -0700 Subject: [PATCH 41/72] d4xx-compat: simplified max9672x implementation * debian: add d4xx-max96712 build option * d4xx-max96724: configure X,Y,Z and U pipes on each GMSL inputs * d4xx-max96724: add HS/VS pipe status msgs * d4xx-max96724: delay CSI output enable after sensor stream start * d4xx-max96724: trigger link reset after set_pipe, before csi output enable * d4xx-max96724: comment out link reset after set_pipe, before csi output enable * d4xx-max96724: cleanup debug messages * d4xx-max96724: remove max9295 init error after gmsl a link reset * d4xx-max96724: revamp sdev register source mapping * d4xx-max96724: fix gmsl link source mapping * d4xx: revisit get_fram_desc mux impl, add sensor get_fram_desc support * d4xx-max96724: add link src dedicated pipe config build otion * d4xx: cleanup get frame desc impl * d4xx: change max96724 phy lanes mapping * d4xx: fix max9295 and max92724 pairing mismatchs * d4xx: fix max96724 csi mapping to ipu7 * d4xx: fix max96724 csi phy lane map to ipu7 * d4xx: add max96724 legacy mode compile option * d4xx: change to max96724 dser 4X2 mode * d4xx: configure max96724 vc_ext, ignore deskew in cphy * d4xx-max96724: fix vc_id 4bits pipe mapping * d4xx: tolerate max9295 and max96724 distinct csi mode and nlanes * d4xx-max96724: fix set pipe select and lanes configs * d4xx: clear max96724 vc_ext src 3bits msb * d4xx-max96724: remove deskew init, cleanup lane cnt config * d4xx: apply deskew init, fix csi lane config * d4xx-max96724: fix cphy link-freq, remove dphy wake seq * d4xx-max96724: fix cphy link-freq, remove dphy wake seq" * d4xx-max96724: allow alternative pipe mapping on max96712 * d4xx-max96724: add 2000bps cphy link-speed * d4xx-max96724: force dpll soft reset and config on all csi outputs * d4xx-max96724: fix dpll soft reset sequence, switch to alt2 mem8 * d4xx-max96724: remove phy and csi sync concatenation * d4xx: fix dual max96724 asymetric cphy pin map * d4xx: exclude servoss montain specific cphy mapping * d4xx: compile max96712 legacy mode by default * d4xx: compile Servoss Mountain CRD max96724 cphy pinmap expection * d4xx: compile Servoss Mountain CRD max96724 cphy pinmap expection ``` Makefile subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX96724) += -DCONFIG_VIDEO_D4XX_MAX96712_LEGACY -DCONFIG_VIDEO_D4XX_MAX96724_SERVOSS_MOUNTAIN ``` * d4xx: replace Servoss mountain by module param * d4xx-max96724: fix d4xx sensor SYNC config per pipe * d4xx-max96724: configure max9295 i2c channel A only * d4xx: init stream sink and source via substream array * d4xx: fix stream fmt, enforce channel control * d4xx: initial sensor get frame desc callback * d4xx: set mux and sensors media entity distinct funtions * d4xx: i2c channel control link mismatch * d4xx-max96724 : fix pipe virtual channel mapping Signed-off-by: florent pirou --- Makefile | 1 + drivers/media/i2c/d4xx-compat/d4xx-max9295.c | 13 + drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 2226 ++++++++++------- drivers/media/i2c/d4xx-compat/d4xx.c | 427 +++- include/media/i2c/d4xx-max96724.h | 3 + 5 files changed, 1590 insertions(+), 1080 deletions(-) diff --git a/Makefile b/Makefile index a04f2f60..e7789a1c 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,7 @@ subdir-ccflags-$(CONFIG_VIDEO_MAX9X) += -DCONFIG_VIDEO_MAX9X subdir-ccflags-$(CONFIG_VIDEO_ISX031) += -DCONFIG_VIDEO_ISX031 subdir-ccflags-$(CONFIG_VIDEO_AR0233) += -DCONFIG_VIDEO_AR0233 subdir-ccflags-$(CONFIG_VIDEO_D4XX_COMPAT) += -DCONFIG_VIDEO_D4XX +subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX96724) += -DCONFIG_VIDEO_D4XX_MAX96712_LEGACY #-DCONFIG_VIDEO_D4XX_MAX96724_DT_VC_EXT subdir-ccflags-$(CONFIG_VIDEO_AR0820) += -DCONFIG_VIDEO_AR0820 subdir-ccflags-$(CONFIG_VIDEO_AR0234) += -DCONFIG_VIDEO_AR0234 -DCONFIG_V4L2_CCI_I2C subdir-ccflags-$(CONFIG_IPU_BRIDGE) += -DCONFIG_IPU_BRIDGE diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c index b16c63cc..a2336a97 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c @@ -349,11 +349,13 @@ int max9295_setup_streaming(struct device *dev) * overrides TX_SRC_SEL. would be useful in * using same mappings in all ser devs. */ +#if !defined(CONFIG_VIDEO_D4XX_MAX96724) || !defined(CONFIG_VIDEO_D4XX_MAX96712) if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_B) { map_pipe_dtype[j].addr += 2; map_pipe_dtype[j].st_id += 1; } +#endif g_stream->st_id_sel = map_pipe_dtype[j].st_id; st_en = (map_pipe_dtype[j].addr == @@ -469,8 +471,16 @@ int max9295_setup_control(struct device *dev) prim_priv__ = NULL; } #endif +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A || + g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_B || + g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_C || + g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_D) + err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x21); +#else if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A) err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x21); +#endif else err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x22); @@ -628,6 +638,9 @@ int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx) priv->g_client.st_done = false; priv->g_client.g_ctx = g_ctx; + dev_dbg(dev, "paired 0x%x->0x%x SerDes context\n", + g_ctx->sdev_def, + g_ctx->sdev_reg); error: mutex_unlock(&priv->lock); diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c index cdf415fb..45b5de4e 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -30,6 +30,9 @@ #define MAX96724_REG3_ADDR 0x0003 /* I2C Remote Control Channel Enable */ +#define MAX96724_REG5_ADDR 0x0005 +#define MAX96724_REG4_ADDR 0x0004 +#define MAX96724_REG6_ADDR 0x0006 #define MAX96724_REG10_ADDR 0x0010 /* PHY A/B RX/TX rate control */ #define MAX96724_REG11_ADDR 0x0011 /* PHY C/D RX/TX rate control */ #define MAX96724_PWR1_ADDR 0x0013 /* RESET_ALL */ @@ -47,7 +50,10 @@ #define MAX96724_REG17_CTRL_EN_FIELD BIT(2) #define MAX96724_REG19_CTRL_EN_FIELD BIT(4) -#define MAX96724_LINK_CTRL_ADDR 0x0006 +#define MAX96724_REM_CC MAX96724_REG3_ADDR +#define MAX96724_REM_CC_DIS_PORT_FIELD(link, port) BIT(MAX96724_FLD_OFS(link, 2, 4) + (port % 2)) + +#define MAX96724_LINK_CTRL_ADDR MAX96724_REG6_ADDR #define MAX96724_LINK_CTRL_GMSL_FIELD(link) BIT(link) << 4 #define MAX96724_LINK_CTRL_EN_FIELD(link) BIT(link) @@ -73,6 +79,15 @@ #define MAX96724_MIPI_TX_LANE_CNT(csi) (MAX96724_MIPI_TX(csi) + 0x0A) #define MAX96724_MIPI_TX_ALT_MEM(csi) (MAX96724_MIPI_TX(csi) + 0x33) +#define MAX96724_MAP_CON_SYNC_PIPE_FIELD GENMASK(5, 4) +#define MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe) (((pipe) << 4) & MAX96724_MAP_CON_SYNC_PIPE_FIELD) +#define MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(pipe) BIT(pipe) +#define MAX96724_MAP_CON_SYNC_PIPE_0_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(0) +#define MAX96724_MAP_CON_SYNC_PIPE_1_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(1) +#define MAX96724_MAP_CON_SYNC_PIPE_2_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(2) +#define MAX96724_MAP_CON_SYNC_PIPE_3_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(3) + +#define MAX96724_MAP_STATUS(pipe) (MAX96724_MIPI_TX(pipe) + 0x02) #define MAX96724_MAP_EN_L(pipe) (MAX96724_MIPI_TX(pipe) + 0x0B) #define MAX96724_MAP_EN_H(pipe) (MAX96724_MIPI_TX(pipe) + 0x0C) #define MAX96724_MAP_SRCDST_EN_FIELD(map) BIT(map) @@ -83,6 +98,8 @@ #define MAX96724_MAP_PHY_DEST(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x2D + ((map) / 4)) #define MAX96724_MAP_DPHY_DEST_FIELD(map) GENMASK(1, 0) << MAX96724_FLD_OFS(map, 2, 4) +#define MAX96724_MIPI_TX_MAP_CON(pipe) (MAX96724_MIPI_TX(pipe) + 0x31) + #define MAX96724_MIPI_TX_EXT0 0x0800 #define MAX96724_MIPI_TX_EXT(pipe) (MAX96724_MIPI_TX_EXT0 + ((pipe) * 0x10)) #define MAX96724_MAP_SRCDST_H(pipe, map) (MAX96724_MIPI_TX_EXT(pipe) + (map)) @@ -105,6 +122,8 @@ #define MAX96724_MIPI_TX_VCX_EN_FIELD BIT(4) #define MAX96724_MIPI_TX_WAKEUP_CYC_FIELD GENMASK(2, 0) + + #define MAX96724_LANE_CTRL_MAP(num_lanes) \ (((num_lanes) << 6) & MAX96724_MIPI_TX_LANE_CNT_FIELD) @@ -117,25 +136,71 @@ /* Video Pipe MIPI Mapping Enable */ #define MAX96724_PIPE_X_MIPI_EN MAX96724_MAP_EN_L(0) #define MAX96724_MAP_EN_FIELD GENMASK(7, 0) +#define MAX96724_PIPE_X_MAP_CON_ADDR()AX96724_MAP_CON(0) /* Video Pipe X Cross Mapping baseline */ -#define MAX96724_PIPE_X_EN_ADDR MAX96724_MAP_EN_L(0) +#define MAX96724_PIPE_X_EN MAX96724_MAP_EN_L(0) +#define MAX96724_PIPE_X_EN_L_ADDR MAX96724_MAP_EN_L(0) +#define MAX96724_PIPE_X_EN_H_ADDR MAX96724_MAP_EN_H(0) #define MAX96724_PIPE_X_SRC_0_MAP_ADDR MAX96724_MAP_SRC_L(0,0) #define MAX96724_PIPE_X_DST_0_MAP_ADDR MAX96724_MAP_DST_L(0,0) #define MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR MAX96724_MAP_SRCDST_H(0,0) -#define MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR MAX96724_MAP_PHY_DEST(0,0) #define MAX96724_PIPE_X_SRC_1_MAP_ADDR MAX96724_MAP_SRC_L(0,1) #define MAX96724_PIPE_X_DST_1_MAP_ADDR MAX96724_MAP_DST_L(0,1) #define MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR MAX96724_MAP_SRCDST_H(0,1) -#define MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR MAX96724_MAP_PHY_DEST(0,1) #define MAX96724_PIPE_X_SRC_2_MAP_ADDR MAX96724_MAP_SRC_L(0,2) #define MAX96724_PIPE_X_DST_2_MAP_ADDR MAX96724_MAP_DST_L(0,2) -#define MAX96724_PIPE_X_PHY_DEST_2_MAP_ADDR MAX96724_MAP_PHY_DEST(0,2) #define MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR MAX96724_MAP_SRCDST_H(0,2) #define MAX96724_PIPE_X_SRC_3_MAP_ADDR MAX96724_MAP_SRC_L(0,3) #define MAX96724_PIPE_X_DST_3_MAP_ADDR MAX96724_MAP_DST_L(0,3) -#define MAX96724_PIPE_X_PHY_DEST_3_MAP_ADDR MAX96724_MAP_PHY_DEST(0,3) #define MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR MAX96724_MAP_SRCDST_H(0,3) +#define MAX96724_PIPE_X_SRC_4_MAP_ADDR MAX96724_MAP_SRC_L(0,4) +#define MAX96724_PIPE_X_DST_4_MAP_ADDR MAX96724_MAP_DST_L(0,4) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_4_MAP_ADDR MAX96724_MAP_SRCDST_H(0,4) +#define MAX96724_PIPE_X_SRC_5_MAP_ADDR MAX96724_MAP_SRC_L(0,5) +#define MAX96724_PIPE_X_DST_5_MAP_ADDR MAX96724_MAP_DST_L(0,5) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_5_MAP_ADDR MAX96724_MAP_SRCDST_H(0,5) +#define MAX96724_PIPE_X_SRC_6_MAP_ADDR MAX96724_MAP_SRC_L(0,6) +#define MAX96724_PIPE_X_DST_6_MAP_ADDR MAX96724_MAP_DST_L(0,6) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_6_MAP_ADDR MAX96724_MAP_SRCDST_H(0,6) +#define MAX96724_PIPE_X_SRC_7_MAP_ADDR MAX96724_MAP_SRC_L(0,7) +#define MAX96724_PIPE_X_DST_7_MAP_ADDR MAX96724_MAP_DST_L(0,7) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_7_MAP_ADDR MAX96724_MAP_SRCDST_H(0,7) +#define MAX96724_PIPE_X_SRC_8_MAP_ADDR MAX96724_MAP_SRC_L(0,8) +#define MAX96724_PIPE_X_DST_8_MAP_ADDR MAX96724_MAP_DST_L(0,8) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_8_MAP_ADDR MAX96724_MAP_SRCDST_H(0,8) +#define MAX96724_PIPE_X_SRC_9_MAP_ADDR MAX96724_MAP_SRC_L(0,9) +#define MAX96724_PIPE_X_DST_9_MAP_ADDR MAX96724_MAP_DST_L(0,9) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_9_MAP_ADDR MAX96724_MAP_SRCDST_H(0,9) +#define MAX96724_PIPE_X_SRC_10_MAP_ADDR MAX96724_MAP_SRC_L(0,10) +#define MAX96724_PIPE_X_DST_10_MAP_ADDR MAX96724_MAP_DST_L(0,10) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_10_MAP_ADDR MAX96724_MAP_SRCDST_H(0,10) +#define MAX96724_PIPE_X_SRC_11_MAP_ADDR MAX96724_MAP_SRC_L(0,11) +#define MAX96724_PIPE_X_DST_11_MAP_ADDR MAX96724_MAP_DST_L(0,11) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_11_MAP_ADDR MAX96724_MAP_SRCDST_H(0,11) +#define MAX96724_PIPE_X_SRC_12_MAP_ADDR MAX96724_MAP_SRC_L(0,12) +#define MAX96724_PIPE_X_DST_12_MAP_ADDR MAX96724_MAP_DST_L(0,12) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_12_MAP_ADDR MAX96724_MAP_SRCDST_H(0,12) +#define MAX96724_PIPE_X_SRC_13_MAP_ADDR MAX96724_MAP_SRC_L(0,13) +#define MAX96724_PIPE_X_DST_13_MAP_ADDR MAX96724_MAP_DST_L(0,13) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_13_MAP_ADDR MAX96724_MAP_SRCDST_H(0,13) +#define MAX96724_PIPE_X_SRC_14_MAP_ADDR MAX96724_MAP_SRC_L(0,14) +#define MAX96724_PIPE_X_DST_14_MAP_ADDR MAX96724_MAP_DST_L(0,14) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_14_MAP_ADDR MAX96724_MAP_SRCDST_H(0,14) +#define MAX96724_PIPE_X_SRC_15_MAP_ADDR MAX96724_MAP_SRC_L(0,15) +#define MAX96724_PIPE_X_DST_15_MAP_ADDR MAX96724_MAP_DST_L(0,15) +#define MAX96724_PIPE_X_SRCDST_VC_EXT_15_MAP_ADDR MAX96724_MAP_SRCDST_H(0,15) + +#define MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR MAX96724_MAP_PHY_DEST(0,0) +#define MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR MAX96724_MAP_PHY_DEST(0,4) +#define MAX96724_PIPE_X_PHY_DEST_2_MAP_ADDR MAX96724_MAP_PHY_DEST(0,8) +#define MAX96724_PIPE_X_PHY_DEST_3_MAP_ADDR MAX96724_MAP_PHY_DEST(0,12) + +#define MAX96724_PIPE_X_STATUS_0_MAP_ADDR MAX96724_MAP_STATUS(0) +#define MAX96724_PIPE_X_STATUS_1_MAP_ADDR MAX96724_MAP_STATUS(1) +#define MAX96724_PIPE_X_STATUS_2_MAP_ADDR MAX96724_MAP_STATUS(2) +#define MAX96724_PIPE_X_STATUS_3_MAP_ADDR MAX96724_MAP_STATUS(3) + #define MAX96724_MAP_SRC_L_VC_FIELD GENMASK(7, 6) #define MAX96724_MAP_SRC_L_DT_FIELD GENMASK(5, 0) #define MAX96724_MAP_DST_L_VC_FIELD GENMASK(7, 6) @@ -248,6 +313,8 @@ #define MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ (GENMASK(1, 0) << (MAX96724_FLD_OFS(csi, 4, 2) + MAX96724_FLD_OFS(lane, 2, 2))) +#define MAX96724_MIPI_PHY4 0x08A4 +#define MAX96724_MIPI_PHY5 0x08A5 #define MAX96724_MIPI_PHY13 0x08AD #define MAX96724_CPHY_PREAMBLE_ADDR MAX96724_MIPI_PHY13 #define MAX96724_CPHY_PREAMBLE_FIELD GENMASK(5, 0) @@ -305,10 +372,11 @@ #define MAX96724_CPHY_PREAMBLE_DEFAULT 0x1f #define MAX96724_CPHY_PREP_DEFAULT 0x5d -// write 0x06 for CPHY 1500Mbps/lane on CSI2 controller-1 & PHY1 -#define MAX96724_CPHY_CLK_1500BPS 0x25 -// write 0x15 for DPHY 1500Mbps/lane on CSI2 controller-1 & PHY1 -#define MAX96724_DPHY_CLK_1500BPS 0x35 +// write 0x20 for 1500Mbps/lane on CSI2 controller & CPHY +#define MAX96724_CPHY_CLK_1500BPS 0x26 +#define MAX96724_CPHY_CLK_2000BPS 0x29 +// write 0x2f for 1500Mbps/lane on CSI2 controller & DPHY +#define MAX96724_DPHY_CLK_1500BPS 0x34 #define MAX96724_RESET_ALL 0x80 @@ -521,6 +589,37 @@ static int max96724_write_reg(struct device *dev, return err; } +static const char *max96724_get_link_name(u32 link) +{ + switch (link) { + case GMSL_SERDES_CSI_LINK_A: + return "GMSL A"; + case GMSL_SERDES_CSI_LINK_B: + return "GMSL B"; + case GMSL_SERDES_CSI_LINK_C: + return "GMSL C"; + case GMSL_SERDES_CSI_LINK_D: + return "GMSL D"; + default: + return "GMSL UNKNOWN"; + } +} + +static u8 max96724_link_to_port(u32 link) +{ + switch (link) { + case GMSL_SERDES_CSI_LINK_B: + return 1; + case GMSL_SERDES_CSI_LINK_C: + return 2; + case GMSL_SERDES_CSI_LINK_D: + return 3; + case GMSL_SERDES_CSI_LINK_A: + default: + return 0; + } +} + static int max96724_get_sdev_idx(struct device *dev, struct device *s_dev, unsigned int *idx) { @@ -529,16 +628,27 @@ static int max96724_get_sdev_idx(struct device *dev, int err = 0; mutex_lock(&priv->lock); + for (i = 0; i < priv->max_src; i++) { + + if (!priv->sources[i].g_ctx || !priv->sources[i].g_ctx->s_dev) + continue; + if (priv->sources[i].g_ctx->s_dev == s_dev) break; } + if (i == priv->max_src) { dev_err(dev, "no sdev found\n"); err = -EINVAL; goto ret; } + dev_dbg(dev, "%s: sdev found on %s (link_id=%u)\n", + __func__, + max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link), + i); + if (idx) *idx = i; @@ -555,13 +665,13 @@ static void max96724_pipes_reset(struct max96724 *priv) */ struct pipe_ctx pipe_defaults[] = { {MAX96724_PIPE_X, GMSL_CSI_DT_RAW_12, - MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, {MAX96724_PIPE_Y, GMSL_CSI_DT_RAW_12, - MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, {MAX96724_PIPE_Z, GMSL_CSI_DT_EMBED, - MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, {MAX96724_PIPE_U, GMSL_CSI_DT_EMBED, - MAX96724_CSI_CTRL_ID_1, 0, MAX96724_INVAL_ST_ID} + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID} }; /* @@ -574,18 +684,15 @@ static void max96724_pipes_reset(struct max96724 *priv) static void max96724_reset_ctx(struct max96724 *priv) { - unsigned int i; + unsigned int i = (unsigned int) max96724_link_to_port(priv->src_link); priv->link_setup = false; priv->lane_setup = false; priv->num_src_found = 0; - priv->src_link = 0; - priv->dst_link = 0; - priv->dst_n_lanes = 2; + priv->sources[i].st_enabled = false; + //priv->src_link = 0; priv->splitter_enabled = false; max96724_pipes_reset(priv); - for (i = 0; i < priv->num_src; i++) - priv->sources[i].st_enabled = false; } int max96724_power_on(struct device *dev) @@ -654,38 +761,15 @@ void max96724_power_off(struct device *dev) } EXPORT_SYMBOL(max96724_power_off); -static const char *max96724_get_link_name(u32 link) -{ - switch (link) { - case GMSL_SERDES_CSI_LINK_A: - return "GMSL A"; - case GMSL_SERDES_CSI_LINK_B: - return "GMSL B"; - case GMSL_SERDES_CSI_LINK_C: - return "GMSL C"; - case GMSL_SERDES_CSI_LINK_D: - return "GMSL D"; - default: - return "GMSL UNKNOWN"; - } -} - -static u8 max96724_link_to_port(u32 link) -{ - switch (link) { - case GMSL_SERDES_CSI_LINK_B: - return 1; - case GMSL_SERDES_CSI_LINK_C: - return 2; - case GMSL_SERDES_CSI_LINK_D: - return 3; - case GMSL_SERDES_CSI_LINK_A: - default: - return 0; - } -} - - +/* CRITICAL: max96724_check_status wait for sensor video to stabilize before enabling CSI output! + * 1. Triggers all cameras via GPIO (MFP7/MFP8) + * 2. sleep 0.3 ← 300ms delay to let sensors stabilize! + * 3. i2ctransfer ... 0x04 0x0b 0x02 ← Enable CSI + * 4. i2ctransfer ... 0x08 0xa0 0x84 ← Enable continuous clock + * + * Without this delay, deserializer tries to lock video before sensor + * outputs stable video data, resulting in VIDEO_LOCK failure (0x0108=0x02) + */ int max96724_check_status(struct device *dev) { struct max96724 *priv = dev_get_drvdata(dev); @@ -694,6 +778,72 @@ int max96724_check_status(struct device *dev) mutex_lock(&priv->lock); + dev_info(dev, "%s: Wake-up %s CSI %u %s mode %s (num_lanes:x%u)\n", + __func__, + max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->dst_n_lanes); + + /* Enable MIPI TX controller and enable PHY CLK cycle + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), + MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to configure CSI %u %s wakeup cycles: %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); + } + */ + // enable CSI out link after initialization complet + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable csi output link: %d\n", + __func__, + err); + } + + /* Turn on MIPI PHY continuous clock mode (matches script line 520) + * Reference script does this AFTER CSI enable (0x040B = 0x02) + * This enables continuous clock on MIPI CSI-2 interface + * 0x84 = 0b10000100 + * Bit[7] = 1: Enable continuous clock mode + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U)); + if (err) + dev_err(dev, "%s: Failed to enable %s %s continuous clock : %d\n", + __func__, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + err); + else + dev_dbg(dev, "%s: %s CSI %u %s %s (num_lanes:x%u) continuous clock mode enabled\n", + __func__, + max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->dst_n_lanes); + + /* Turn on ALL link channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", + __func__, + err); + /* Re-Check GMSL link status after initial configuration */ { unsigned int link_status = 0; @@ -723,14 +873,53 @@ int max96724_check_status(struct device *dev) (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); - unsigned int pipe_de_status = 0; + unsigned int pipe_0_status = 0, pipe_1_status = 0, pipe_2_status = 0, pipe_3_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_0_MAP_ADDR, &pipe_0_status); + dev_dbg(dev, "%s: %s Video TX CSI 0 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_0_status, + (pipe_0_status & 0x01) ? "SYNC_EN " : "", + (pipe_0_status & 0x02) ? "in-SYNC " : "", + (pipe_0_status & 0x04) ? "LOST_SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_1_MAP_ADDR, &pipe_1_status); + dev_dbg(dev, "%s: %s Video TX CSI 1 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_1_status, + (pipe_1_status & 0x01) ? "EN-SYNC " : "", + (pipe_1_status & 0x02) ? "IN-SYNC " : "", + (pipe_1_status & 0x04) ? "LOST-SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_2_MAP_ADDR, &pipe_2_status); + dev_dbg(dev, "%s: %s Video TX CSI 2 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_2_status, + (pipe_2_status & 0x01) ? "SYNC_EN " : "", + (pipe_2_status & 0x02) ? "in-SYNC " : "", + (pipe_2_status & 0x04) ? "LOST_SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_3_MAP_ADDR, &pipe_3_status); + dev_dbg(dev, "%s: %s Video TX CSI 3 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_3_status, + (pipe_3_status & 0x01) ? "SYNC_EN " : "", + (pipe_3_status & 0x02) ? "in-SYNC " : "", + (pipe_3_status & 0x04) ? "LOST_SYNC " : ""); + unsigned int pipe_de_status = 0, pipe_hs_status = 0, pipe_vs_status = 0; max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); - dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + dev_dbg(dev, "%s: %s Video Pipeline DE status: 0x%02x (bit0-3: %s%s%s%s)\n", __func__, max96724_get_link_name(priv->src_link), pipe_de_status, (pipe_de_status & 0x01) ? "DE_DET_0 " : "", (pipe_de_status & 0x02) ? "DE_DET_1 " : "", (pipe_de_status & 0x04) ? "DE_DET_2 " : "", (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + max96724_read_reg(dev, MAX96724_PIPE_HS_STATUS_ADDR, &pipe_hs_status); + dev_dbg(dev, "%s: %s Video Pipeline HS status: 0x%02x (bit0-3: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_hs_status, + (pipe_hs_status & 0x01) ? "HS_DET_0 " : "", + (pipe_hs_status & 0x02) ? "HS_DET_1 " : "", + (pipe_hs_status & 0x04) ? "HS_DET_2 " : "", + (pipe_hs_status & 0x08) ? "HS_DET_3 " : ""); + max96724_read_reg(dev, MAX96724_PIPE_VS_STATUS_ADDR, &pipe_vs_status); + dev_dbg(dev, "%s: %s Video Pipeline VS status: 0x%02x (bit0-3: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_vs_status, + (pipe_vs_status & 0x01) ? "VS_DET_0 " : "", + (pipe_vs_status & 0x02) ? "VS_DET_1 " : "", + (pipe_vs_status & 0x04) ? "VS_DET_2 " : "", + (pipe_vs_status & 0x08) ? "VS_DET_3 " : ""); } mutex_unlock(&priv->lock); @@ -739,6 +928,37 @@ int max96724_check_status(struct device *dev) } EXPORT_SYMBOL(max96724_check_status); +int max96724_switch_link_channel(struct device *dev) +{ + struct max96724 *priv = NULL; + int err = 0; + + if (!dev) { + dev_warn(dev, "%s: invalid sdev\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + /* Turn on ALL link channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", + __func__, + err); + + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max96724_switch_link_channel); + int max96724_set_mfp(struct device *dev, int pin, int val) { struct max96724 *priv = dev_get_drvdata(dev); @@ -815,65 +1035,22 @@ static int max96724_write_link(struct device *dev, u32 link) int ret = -EINVAL; int err = 0; struct max96724 *priv = dev_get_drvdata(dev); - int link_id; - int port; + u8 src_port = 0; - port = max96724_link_to_port(link); - if (port < 0) { + src_port = max96724_link_to_port(link); + if (src_port < 0) { dev_err(dev, "%s: invalid link 0x%x\n", __func__, link); return ret; } - dev_dbg(dev, "%s: write %s link (id=0x%x, idx=%u)\n", - __func__, max96724_get_link_name(link), port); - - /* Configure 6Gbps GMSL2 mode BEFORE reset to match MAX9295 serializer - * Reference script: enable_6g sets 0x0010=0x22, 0x0011=0x22 - * These are CTRL0 and CTRL1 registers for GMSL2 6Gbps PHY rate - */ - err = MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(0), 0x22); - err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(2), 0x22); - if (err) { - dev_err(dev, "%s: Failed to enable %s 6Gbps link: %d\n", - __func__, - max96724_get_link_name(link), - err); - goto write_link_out; - } - - - /* Disable CSI output initially (matches script step 2) - * This is done early in initialization, before any video configuration - */ - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, - MAX96724_CSI_OUT_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0U)); - if (err) { - dev_err(dev, "%s: Failed to csi output link: %d\n", - __func__, - err); - goto write_link_out; - } + dev_dbg(dev, "%s: write %s link (idx=%u)\n", + __func__, max96724_get_link_name(link), src_port); /* Enable GMSL2 input sources links state */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_LINK_CTRL_ADDR, - MAX96724_LINK_CTRL_EN_FIELD(0) - | MAX96724_LINK_CTRL_GMSL_FIELD(0) - | MAX96724_LINK_CTRL_EN_FIELD(1) - | MAX96724_LINK_CTRL_GMSL_FIELD(1) - | MAX96724_LINK_CTRL_EN_FIELD(2) - | MAX96724_LINK_CTRL_GMSL_FIELD(2) - | MAX96724_LINK_CTRL_EN_FIELD(3) - | MAX96724_LINK_CTRL_GMSL_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(3), 1U) - | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(3), 1U)); + MAX96724_LINK_CTRL_EN_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(src_port), 1U)); if (err) { dev_err(dev, "%s: Failed to enable %s link control: %d\n", __func__, @@ -882,83 +1059,32 @@ static int max96724_write_link(struct device *dev, u32 link) goto write_link_out; } -#ifndef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE - /* Enable internal regulators (0x17 and 0x19) - before reset - */ - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG17_ADDR, - MAX96724_REG17_CTRL_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_REG17_CTRL_EN_FIELD, 1U)); - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG19_ADDR, - MAX96724_REG19_CTRL_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_REG19_CTRL_EN_FIELD, 1U)); - - /* Enable PoC (Power over Coax) for cameras - */ - dev_dbg(dev, "Enabling PoC (Power over Coax)...\n"); - ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL1, 0x80); - ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL2, 0x80); - ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL3, 0x01); - - msleep(100); /* Wait for power to stabilize */ -#endif - /* Reset GMSL2 links state */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_CTRL_FIELD(0) - | MAX96724_RESET_CTRL_FIELD(1) - | MAX96724_RESET_CTRL_FIELD(2) - | MAX96724_RESET_CTRL_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 1U)); + MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 1U)); if (err) { - dev_err(dev, "%s: Failed to reset %s link: %d\n", + dev_err(dev, "%s: Failed to reset link: %d\n", __func__, - max96724_get_link_name(link), err); goto write_link_out; } - /* delay to settle link */ - msleep(300); + msleep(10); // delay to settle link err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_CTRL_FIELD(0) - | MAX96724_RESET_CTRL_FIELD(1) - | MAX96724_RESET_CTRL_FIELD(2) - | MAX96724_RESET_CTRL_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 0U)); + MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); if (err) { - dev_err(dev, "%s: Failed to reset %s link: %d\n", + dev_err(dev, "%s: Failed to reset link: %d\n", __func__, - max96724_get_link_name(link), err); goto write_link_out; } - /* Check GMSL link status - hardware auto-negotiation should have completed */ - { - int i; - unsigned int link_status = 0; - for (i = 0; i < MAX96724_MAX_SOURCES; i++) { - max96724_read_reg(dev, MAX96724_LINK_STATUS(i), &link_status); - dev_dbg(dev, "%s: GMSL %c 6Gbps link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", - __func__, 'A' + i , link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), - (link_status & 0x01) ? "VID_LOCK " : "", - (link_status & 0x02) ? "CONFIG_DETECT " : "", - (link_status & 0x04) ? "VIDEO_DETECT " : "", - (link_status & 0x08) ? "LOCK " : "", - (link_status & 0x10) ? "ERROR " : "", - (link_status & 0x20) ? "bit5 " : "", - (link_status & 0x40) ? "bit6 " : "", - (link_status & 0x80) ? "LOCKED " : ""); - } - } + msleep(300); // delay to settle link + ret = 0; write_link_out: @@ -969,7 +1095,9 @@ int max96724_setup_link(struct device *dev, struct device *s_dev) { struct max96724 *priv = dev_get_drvdata(dev); int err = 0; - unsigned int i = 0; + u8 src_port = 0; + u32 link = 0; + unsigned int i = 0, j = 0; dev_dbg(dev, "%s: ENTER\n", __func__); @@ -979,21 +1107,253 @@ int max96724_setup_link(struct device *dev, struct device *s_dev) return err; } - dev_info(dev, "%s: Source index=%d, link=%s\n", __func__, i, - max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link)); + src_port = (u8) i; + link = priv->sources[i].g_ctx->serdes_csi_link; + + dev_info(dev, "%s: Source index=%u, link=%s\n", __func__, src_port, + max96724_get_link_name(link)); mutex_lock(&priv->lock); - if (!priv->splitter_enabled) { - err = max96724_write_link(dev, - priv->sources[i].g_ctx->serdes_csi_link); - if (err) - goto ret; + if (priv->splitter_enabled) + goto reset; - priv->link_setup = true; + /* Configure 6Gbps GMSL2 mode BEFORE reset to match MAX9295 serializer + * Reference script: enable_6g sets 0x0010=0x22, 0x0011=0x22 + * These are CTRL0 and CTRL1 registers for GMSL2 6Gbps PHY rate + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(0), 0x22); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_PHY_RATE_CTRL(2), 0x22); + if (err) { + dev_err(dev, "%s: Failed to enable %s 6Gbps link: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; } -ret: + /* Disable CSI output initially (matches script step 2) + * This is done early in initialization, before any video configuration + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0U)); + if (err) { + dev_err(dev, "%s: Failed to csi output link: %d\n", + __func__, + err); + goto ret; + } + +#ifndef CONFIG_VIDEO_D4XX_MAX96712 + /* Enable internal regulators (0x17 and 0x19) - before reset + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG17_ADDR, + MAX96724_REG17_CTRL_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_REG17_CTRL_EN_FIELD, 1U)); + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_REG19_ADDR, + MAX96724_REG19_CTRL_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_REG19_CTRL_EN_FIELD, 1U)); + */ + + /* Enable PoC (Power over Coax) for cameras + dev_dbg(dev, "Enabling PoC (Power over Coax)...\n"); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL1, 0x80); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL2, 0x80); + ret = MAX96724_WRITE_REG(priv->regmap, MAX96724_REG_POC_CTRL3, 0x01); + + msleep(100); // Wait for power to stabilize + */ +#endif + /* initialize default link to pipe selection + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, 0xF); + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(0), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) + | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), 1U)); + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(2), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2) + | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2), 2U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3), 3U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2), 2U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), 3U)); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_REG3_ADDR, 0xFF); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_REG4_ADDR, 0x0F); + if (err) { + dev_err(dev, "%s: Failed to set default %s link stream streams (X,Y,Z and/or U) selection: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; + } + + /* initialize GMSL2 input sources links state + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_LINK_CTRL_ADDR, + MAX96724_LINK_CTRL_EN_FIELD(0) + | MAX96724_LINK_CTRL_GMSL_FIELD(0) + | MAX96724_LINK_CTRL_EN_FIELD(1) + | MAX96724_LINK_CTRL_GMSL_FIELD(1) + | MAX96724_LINK_CTRL_EN_FIELD(2) + | MAX96724_LINK_CTRL_GMSL_FIELD(2) + | MAX96724_LINK_CTRL_EN_FIELD(3) + | MAX96724_LINK_CTRL_GMSL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(3), 1U) + | MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(3), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable %s link control: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; + } + + /* initialize GMSL2 all link & control state + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_CTRL_FIELD(0) + | MAX96724_RESET_LINK_FIELD(0) + | MAX96724_RESET_LINK_FIELD(1) + | MAX96724_RESET_LINK_FIELD(2) + | MAX96724_RESET_LINK_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 1U)); + + // delay to settle link + msleep(10); + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(0) + | MAX96724_RESET_LINK_FIELD(1) + | MAX96724_RESET_LINK_FIELD(2) + | MAX96724_RESET_LINK_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 0U)); + if (err) { + dev_err(dev, "%s: Failed to reset all ctrls: %d\n", + __func__, err); + goto ret; + } + + // delay to settle link + msleep(300); + +reset: + + // Enable GMSL2 input sources links state + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_LINK_CTRL_ADDR, + MAX96724_LINK_CTRL_EN_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(src_port), 1U)); + if (err) { + dev_err(dev, "%s: Failed to enable %s link control: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; + } + + /* Reset GMSL2 links state + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 1U)); + + // delay to settle link + msleep(10); + + /* Reset GMSL2 links state + */ + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_LINK_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 0U)); + if (err) { + dev_err(dev, "%s: Failed to reset %s link: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; + } + + // delay to settle link + msleep(300); + + //err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + // ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(src_port, 0), 1U))); + switch (link) { + case GMSL_SERDES_CSI_LINK_A: + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC,0xFE); + break; + case GMSL_SERDES_CSI_LINK_B: + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC,0xFB); + break; + case GMSL_SERDES_CSI_LINK_C: + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC,0xEF); + break; + case GMSL_SERDES_CSI_LINK_D: + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC,0xBF); + break; + default: + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + break; + } + if (err) { + dev_err(dev, "%s: Failed to switch %s link i2c channel: %d\n", + __func__, + max96724_get_link_name(link), + err); + goto ret; + } + + if (!priv->splitter_enabled) { + err = max96724_write_link(dev, link); + if (err) + goto ret; + + priv->link_setup = true; + } + + /* Check GMSL link status - hardware auto-negotiation should have completed */ + for (j = 0; j < priv->max_src; j++) { + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(j), &link_status); + dev_info(dev, "%s: GMSL %c 6Gbps link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, 'A' + j, link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); + } + } + +ret: mutex_unlock(&priv->lock); return err; @@ -1004,9 +1364,8 @@ int max96724_setup_control(struct device *dev, struct device *s_dev) { struct max96724 *priv = dev_get_drvdata(dev); int err = 0; - unsigned int i = 0; - - u8 src_port; + u8 src_port = 0; + unsigned int i=0, j=0; dev_dbg(dev, "%s: ENTER\n", __func__); @@ -1016,6 +1375,8 @@ int max96724_setup_control(struct device *dev, struct device *s_dev) return err; } + src_port = (u8) i; + mutex_lock(&priv->lock); if (!priv->link_setup) { @@ -1028,24 +1389,7 @@ int max96724_setup_control(struct device *dev, struct device *s_dev) priv->num_src_found++; priv->src_link = priv->sources[i].g_ctx->serdes_csi_link; priv->dst_link = priv->sources[i].g_ctx->dst_csi_port; - src_port = max96724_link_to_port(priv->src_link); priv->dst_n_lanes = priv->sources[i].g_ctx->num_csi_lanes; - - /* Check GMSL link status - hardware auto-negotiation should have completed */ - { - unsigned int link_status = 0; - max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); - dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), - (link_status & 0x01) ? "VID_LOCK " : "", - (link_status & 0x02) ? "CONFIG_DETECT " : "", - (link_status & 0x04) ? "VIDEO_DETECT " : "", - (link_status & 0x08) ? "LOCK " : "", - (link_status & 0x10) ? "ERROR " : "", - (link_status & 0x20) ? "bit5 " : "", - (link_status & 0x40) ? "bit6 " : "", - (link_status & 0x80) ? "LOCKED " : ""); - } } /* Enable splitter mode */ @@ -1062,44 +1406,12 @@ int max96724_setup_control(struct device *dev, struct device *s_dev) } priv->dev_id = dev_id; - dev_dbg(dev, "%s: set %s SerDes GMSL A & B & C & D quad-link)\n", + dev_dbg(dev, "%s: Switched %s SerDes GMSL %s link channel)\n", __func__, - priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712"); - /* - src_port = max96724_link_to_port(priv->src_link); - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 1U) | - MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); - if (err) { - dev_err(dev, "%s: Failed to trigger %s link reset: %d\n", - __func__, - max96724_get_link_name(priv->src_link), - err); - goto error; - } - - // delay to settle link - msleep(100); + priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712", + max96724_get_link_name(priv->src_link)); - // clear link reset - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 0U) | - MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); - if (err) { - dev_err(dev, "%s: Failed to clear %s link reset: %d\n", - __func__, - max96724_get_link_name(priv->src_link), - err); - goto error; - } - */ priv->splitter_enabled = true; - - dev_dbg(dev, "%s: reset %s link done\n", - __func__, - max96724_get_link_name(priv->src_link)); } //max96724_write_reg(dev, @@ -1114,47 +1426,56 @@ int max96724_setup_control(struct device *dev, struct device *s_dev) max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link)); /* Reset splitter mode if all devices are not found */ - if ((priv->sdev_ref == priv->max_src) && - (priv->splitter_enabled == true) && - (priv->num_src_found > 0U) && - (priv->num_src_found < priv->max_src)) { - dev_info(dev, "%s: restore SerDes %s single-link\n", - __func__, - max96724_get_link_name(priv->src_link)); - - /* Only restore link if it's valid (not UNKNOWN) */ - if (priv->src_link != GMSL_SERDES_CSI_LINK_UNKNOWN) { - err = max96724_write_link(dev, priv->src_link); - if (err) { - dev_info(dev, "%s: fail to write source link\n", - __func__); - goto error; + if ((priv->num_src_found < priv->sdev_ref) || + ((priv->sdev_ref == priv->max_src) && + (priv->splitter_enabled == true) && + (priv->num_src_found > 0U) && + (priv->num_src_found < priv->max_src))) { + + for (j = 0; j < priv->max_src; j++) { + + if (!priv->sources[j].g_ctx) + continue; + + if (!priv->sources[j].g_ctx->serdev_found) + continue; + + /* Check GMSL link status - hardware auto-negotiation should have completed */ + { + unsigned int link_status = 0; + max96724_read_reg(dev, MAX96724_LINK_STATUS(j), &link_status); + + if (!(link_status & MAX96724_LINK_LOCK_BIT)) + continue; + + dev_info(dev, "%s: GMSL %c 6Gbps link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", + __func__, 'A' + j , link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + (link_status & 0x01) ? "VID_LOCK " : "", + (link_status & 0x02) ? "CONFIG_DETECT " : "", + (link_status & 0x04) ? "VIDEO_DETECT " : "", + (link_status & 0x08) ? "LOCK " : "", + (link_status & 0x10) ? "ERROR " : "", + (link_status & 0x20) ? "bit5 " : "", + (link_status & 0x40) ? "bit6 " : "", + (link_status & 0x80) ? "LOCKED " : ""); } - } else { - dev_info(dev, "%s: skip restore (src_link is UNKNOWN)\n", - __func__); - } - priv->splitter_enabled = false; - } else if (priv->num_src_found < priv->sdev_ref) { + dev_dbg(dev, "%s: restore SerDes %s (link=%u)\n", + __func__, + max96724_get_link_name(priv->sources[j].g_ctx->serdes_csi_link), + j); - dev_info(dev, "%s: restore SerDes %s single-link\n", - __func__, - max96724_get_link_name(priv->src_link)); - - /* Only restore link if it's valid (not UNKNOWN) */ - if (priv->src_link != GMSL_SERDES_CSI_LINK_UNKNOWN) { - err = max96724_write_link(dev, priv->src_link); + /* Only restore link if it's valid */ + err = max96724_write_link(dev, priv->sources[j].g_ctx->serdes_csi_link); if (err) { - dev_info(dev, "%s: fail to write source link\n", - __func__); - goto error; - } - } else { - dev_info(dev, "%s: skip restore (src_link is UNKNOWN)\n", + dev_warn(dev, "%s: fail to restore source link\n", __func__); + continue; + } } - priv->splitter_enabled = false; + + if (priv->num_src_found < 2) + priv->splitter_enabled = false; } error: @@ -1194,8 +1515,10 @@ EXPORT_SYMBOL(max96724_reset_control); int max96724_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) { struct max96724 *priv = NULL; - unsigned int i; + unsigned int i=0; int err = 0; + bool csi_mode_ok = true; + bool csi_lanes_ok = true; if (!dev || !g_ctx || !g_ctx->s_dev) { dev_err(dev, "%s: invalid input params\n", __func__); @@ -1213,64 +1536,64 @@ int max96724_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) goto error; } - if (priv->d4xx_hacks) { - bool csi_mode_ok = false; - dev_dbg(dev, - "%s: Checking Deserializer g_ctx->csi_mode=%s\n", - __func__, - (g_ctx->csi_mode == GMSL_CSI_4X2_MODE) == GMSL_CSI_4X2_MODE ? "4X2" : "2X4"); - - switch (priv->csi_mode) { - case MAX96724_CSI_MODE_1X4: - csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_1X4_MODE); - break; - case MAX96724_CSI_MODE_2X4: - csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || - (g_ctx->csi_mode == GMSL_CSI_2X4_MODE); - break; - case MAX96724_CSI_MODE_4X2: - csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_4X2_MODE); - break; - default: - csi_mode_ok = false; - break; - } - - if (!csi_mode_ok) { - dev_err(dev, - "%s: csi mode not supported\n", __func__); - err = -EINVAL; - goto error; - } - dev_dbg(dev, "%s: d4xx specfic csi-mode set\n", __func__); + dev_dbg(dev, + "%s: Checking deserializer %s link map to CSI %u link (dst-mode:%s,dst-lanes:%u)\n", + __func__, + max96724_get_link_name(g_ctx->serdes_csi_link), + g_ctx->dst_csi_port, + g_ctx->csi_mode == GMSL_CSI_4X2_MODE ? "4X2" : "2X4", + g_ctx->num_csi_lanes); + + switch (priv->csi_mode) { + case MAX96724_CSI_MODE_1X4: + case MAX96724_CSI_MODE_2X4: + csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || + (g_ctx->csi_mode == GMSL_CSI_2X4_MODE); + csi_lanes_ok = (g_ctx->num_csi_lanes == 4); + break; + case MAX96724_CSI_MODE_4X2: + csi_mode_ok = (g_ctx->csi_mode == GMSL_CSI_4X2_MODE); + csi_lanes_ok = (g_ctx->num_csi_lanes == 2); + break; + default: + csi_mode_ok = false; + csi_lanes_ok = false; + break; } - for (i = 0; i < priv->num_src; i++) { - if (g_ctx->serdes_csi_link == - priv->sources[i].g_ctx->serdes_csi_link) { - dev_err(dev, - "%s: serdes csi link is in use\n", __func__); - err = -EINVAL; - goto error; - } - /* - * All sdevs should have same num-csi-lanes regardless of - * dst csi port selected. - * Later if there is any usecase which requires each port - * to be configured with different num-csi-lanes, then this - * check should be performed per port. - */ - if (g_ctx->num_csi_lanes != - priv->sources[i].g_ctx->num_csi_lanes) { - dev_err(dev, - "%s: csi num lanes mismatch\n", __func__); - err = -EINVAL; - goto error; - } - } + if (!csi_mode_ok) + dev_warn(dev, + "%s: csi %s mode mismatch, fallback on valid csi mode %s\n", + __func__, + g_ctx->csi_mode == GMSL_CSI_4X2_MODE ? "4X2" : "2X4", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4"); - priv->sources[priv->num_src].g_ctx = g_ctx; - priv->sources[priv->num_src].st_enabled = false; + if (!csi_lanes_ok) + dev_warn(dev, "%s: invalid nlanes for CSI %s mode, fallback on valid nlanes\n", + __func__, + g_ctx->csi_mode == GMSL_CSI_4X2_MODE ? "4X2" : "2X4"); + + i = (unsigned int) max96724_link_to_port(g_ctx->serdes_csi_link); + + priv->sources[i].g_ctx = g_ctx; + priv->sources[i].st_enabled = false; + + if (!csi_lanes_ok) + priv->sources[i].g_ctx->num_csi_lanes = ((priv->csi_mode == MAX96724_CSI_MODE_4X2) + ? 2 + : 4); + if (!csi_mode_ok) + priv->sources[i].g_ctx->csi_mode = ((priv->csi_mode == MAX96724_CSI_MODE_4X2) + ? GMSL_CSI_4X2_MODE + : GMSL_CSI_2X4_MODE); + + dev_dbg(dev, + "%s: Registered [src:%u] deserializer %s link map to CSI %u link (dst-mode:%s,dst-lanes:%u)\n", + __func__, i, + max96724_get_link_name(priv->sources[i].g_ctx->serdes_csi_link), + priv->sources[i].g_ctx->dst_csi_port, + priv->sources[i].g_ctx->csi_mode == GMSL_CSI_4X2_MODE ? "4X2" : "2X4", + priv->sources[i].g_ctx->num_csi_lanes); priv->num_src++; @@ -1300,14 +1623,14 @@ int max96724_sdev_unregister(struct device *dev, struct device *s_dev) goto error; } - for (i = 0; i < priv->num_src; i++) { + for (i = 0; i < priv->max_src; i++) { if (s_dev == priv->sources[i].g_ctx->s_dev) { priv->sources[i].g_ctx = NULL; break; } } - if (i == priv->num_src) { + if (i == priv->max_src) { dev_err(dev, "%s: requested device not found\n", __func__); err = -EINVAL; @@ -1336,7 +1659,10 @@ static int max96724_get_available_pipe(struct device *dev, * few more checks such as input stream id select, dst port etc. */ if ((priv->pipe[i].dt_type == st_data_type) && - ((dst_csi_port == GMSL_CSI_PORT_A) ? + ((dst_csi_port == GMSL_CSI_PORT_A || + dst_csi_port == GMSL_CSI_PORT_B || + dst_csi_port == GMSL_CSI_PORT_C || + dst_csi_port == GMSL_CSI_PORT_D) ? (priv->pipe[i].dst_csi_ctrl == MAX96724_CSI_CTRL_ID_0) || (priv->pipe[i].dst_csi_ctrl == @@ -1382,6 +1708,7 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id) { int i; int pipe_id = -ENOMEM; + int err = 0; struct max96724 *priv = dev_get_drvdata(dev); mutex_lock(&priv->lock); @@ -1392,6 +1719,19 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id) break; } } + + /* Turn on ALL link channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", + __func__, + err); + mutex_unlock(&priv->lock); return pipe_id; @@ -1417,9 +1757,8 @@ void max96724_reset_oneshot(struct device *dev) { struct max96724 *priv = dev_get_drvdata(dev); int err = 0; - u8 src_port = max96724_link_to_port(priv->src_link); - mutex_lock(&priv->lock); + u8 src_port = max96724_link_to_port(priv->src_link); /* Re-Check GMSL link status after initial configuration */ { @@ -1450,35 +1789,70 @@ void max96724_reset_oneshot(struct device *dev) (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); - unsigned int pipe_de_status = 0; + unsigned int pipe_0_status = 0, pipe_1_status = 0, pipe_2_status = 0, pipe_3_status = 0; + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_0_MAP_ADDR, &pipe_0_status); + dev_dbg(dev, "%s: %s Video TX CSI 0 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_0_status, + (pipe_0_status & 0x01) ? "SYNC_EN " : "", + (pipe_0_status & 0x02) ? "in-SYNC " : "", + (pipe_0_status & 0x04) ? "LOST_SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_1_MAP_ADDR, &pipe_1_status); + dev_dbg(dev, "%s: %s Video TX CSI 1 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_1_status, + (pipe_1_status & 0x01) ? "EN-SYNC " : "", + (pipe_1_status & 0x02) ? "IN-SYNC " : "", + (pipe_1_status & 0x04) ? "LOST-SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_2_MAP_ADDR, &pipe_2_status); + dev_dbg(dev, "%s: %s Video TX CSI 2 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_2_status, + (pipe_2_status & 0x01) ? "SYNC_EN " : "", + (pipe_2_status & 0x02) ? "in-SYNC " : "", + (pipe_2_status & 0x04) ? "LOST_SYNC " : ""); + max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_3_MAP_ADDR, &pipe_3_status); + dev_dbg(dev, "%s: %s Video TX CSI 3 status: 0x%02x (bit0-2: %s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_3_status, + (pipe_3_status & 0x01) ? "SYNC_EN " : "", + (pipe_3_status & 0x02) ? "in-SYNC " : "", + (pipe_3_status & 0x04) ? "LOST_SYNC " : ""); + unsigned int pipe_de_status = 0, pipe_hs_status = 0, pipe_vs_status = 0; max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); - dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", + dev_dbg(dev, "%s: %s Video Pipeline DE status: 0x%02x (bit0-3: %s%s%s%s)\n", __func__, max96724_get_link_name(priv->src_link), pipe_de_status, (pipe_de_status & 0x01) ? "DE_DET_0 " : "", (pipe_de_status & 0x02) ? "DE_DET_1 " : "", (pipe_de_status & 0x04) ? "DE_DET_2 " : "", (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); + max96724_read_reg(dev, MAX96724_PIPE_HS_STATUS_ADDR, &pipe_hs_status); + dev_dbg(dev, "%s: %s Video Pipeline HS status: 0x%02x (bit0-3: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_hs_status, + (pipe_hs_status & 0x01) ? "HS_DET_0 " : "", + (pipe_hs_status & 0x02) ? "HS_DET_1 " : "", + (pipe_hs_status & 0x04) ? "HS_DET_2 " : "", + (pipe_hs_status & 0x08) ? "HS_DET_3 " : ""); + max96724_read_reg(dev, MAX96724_PIPE_VS_STATUS_ADDR, &pipe_vs_status); + dev_dbg(dev, "%s: %s Video Pipeline VS status: 0x%02x (bit0-3: %s%s%s%s)\n", + __func__, max96724_get_link_name(priv->src_link), pipe_vs_status, + (pipe_vs_status & 0x01) ? "VS_DET_0 " : "", + (pipe_vs_status & 0x02) ? "VS_DET_1 " : "", + (pipe_vs_status & 0x04) ? "VS_DET_2 " : "", + (pipe_vs_status & 0x08) ? "VS_DET_3 " : ""); } err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 1U) | - MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); - if (err) { + MAX96724_RESET_CTRL_FIELD(src_port), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 1U)); + if (err) dev_err(dev, "%s: Failed to trigger %s link reset: %d\n", __func__, max96724_get_link_name(priv->src_link), err); - goto reset_link_out; - } /* delay to settle link */ msleep(100); /* clear link and ctrl reset */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(src_port) | MAX96724_RESET_CTRL_FIELD(src_port), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(src_port), 0U) | + MAX96724_RESET_CTRL_FIELD(src_port), MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 0U)); if (err) dev_err(dev, "%s: Failed to clear %s link reset: %d\n", @@ -1486,8 +1860,18 @@ void max96724_reset_oneshot(struct device *dev) max96724_get_link_name(priv->src_link), err); -reset_link_out: - mutex_unlock(&priv->lock); + /* Enable all channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", + __func__, + err); + } EXPORT_SYMBOL(max96724_reset_oneshot); @@ -1495,64 +1879,111 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ u8 data_type2, u32 vc_id, u32 csi_id ) { int err = 0; - int i = 0; + struct max96724 *priv = dev_get_drvdata(dev); + int i = 0, j = 0; +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + u8 en_mapping_num = 0xFF; +#else u8 en_mapping_num = 0x0F; +#endif u8 all_mapping_phy = 0x55; + u8 phy4_lanes_mapping = 0xE4; + u8 phy3_lanes_mapping = 0xE4; + u8 phy2_lanes_mapping = 0xF4; u8 dis_lim_heart = 0x0A; + u8 link_id = max96724_link_to_port(priv->src_link); + u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; - u8 vc_id_2b_msb = (((u8) vc_id) >> 2) & 0x3; + u8 vc_id_3b_src = 0x0; + u8 vc_id_3b_dst = ((u8) vc_id) & 0x7; if (data_type2 == 0x0) +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + en_mapping_num = 0x77; +#else en_mapping_num = 0x07; +#endif switch (csi_id) { case GMSL_CSI_PORT_A: - if (data_type2 == 0x0) - all_mapping_phy = 0x10; + all_mapping_phy = 0x00; + /* 0 (DA0) -> PHY0.0 + * 1 (DA1) -> PHY0.1 + */ + if (priv->csi_mode != MAX96724_CSI_MODE_4X2) + phy3_lanes_mapping = 0xE4; else - all_mapping_phy = 0x00; + phy3_lanes_mapping = 0x44; + phy2_lanes_mapping = 0x34; break; case GMSL_CSI_PORT_B: - if (data_type2 == 0x0) - all_mapping_phy = 0x15; + all_mapping_phy = 0x55; + /* 0 (DA0) -> PHY1.0 + * 1 (DA1) -> PHY1.1 + * 2 (DA2) -> PHY0.0 + * 3 (DA3) -> PHY0.1 + */ + if (priv->csi_mode != MAX96724_CSI_MODE_4X2) + phy3_lanes_mapping = 0x4E; else - all_mapping_phy = 0x55; + phy3_lanes_mapping = 0x44; + phy2_lanes_mapping = 0x34; break; case GMSL_CSI_PORT_C: - if (data_type2 == 0x0) - all_mapping_phy = 0x1A; + all_mapping_phy = 0xAA; + /* 0 (DB0) -> PHY2.0 + * 1 (DB1) -> PHY2.1 + * 2 (DB2) -> PHY3.0 + * 3 (DB3) -> PHY3.1 + */ + if (priv->csi_mode != MAX96724_CSI_MODE_4X2) + phy4_lanes_mapping = 0xE4; else - all_mapping_phy = 0xAA; + phy4_lanes_mapping = 0x44; + phy2_lanes_mapping = 0x44; break; case GMSL_CSI_PORT_D: - if (data_type2 == 0x0) - all_mapping_phy = 0x1F; + all_mapping_phy = 0xFF; + /* 0 (DB0) -> PHY3.0 + * 1 (DB1) -> PHY3.1 + */ + if (priv->csi_mode != MAX96724_CSI_MODE_4X2) + phy4_lanes_mapping = 0x4E; else - all_mapping_phy = 0xFF; + phy4_lanes_mapping = 0x44; + phy2_lanes_mapping = 0x84; break; default: dev_warn(dev, "%s: invalid deserializer csi port, default CSI 1!\n", __func__); - if (data_type2 == 0x0) - all_mapping_phy = 0x15; }; - struct reg_pair map_pipe_opt[] = { - // Do not un-double 8bpp (Un-double 8bpp data) - //{0x031C, 0x00},o - // Do not un-double 8bpp - //{0x031F, 0x00}, - // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 - {MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10}, + struct reg_pair map_pipe_select[] = { + {MAX96724_REG5_ADDR, 0x80}, // Enable lock + {MAX96724_REG6_ADDR, 0xFF}, // Enable all GMSL input sources + {MAX96724_MIPI_PHY0, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0U) | + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, priv->csi_mode) + }, + {MAX96724_MIPI_PHY2, phy2_lanes_mapping}, + {MAX96724_MIPI_PHY3, phy3_lanes_mapping}, + {MAX96724_MIPI_PHY4, phy4_lanes_mapping}, + {MAX96724_MIPI_PHY5, 0x00}, }; + struct reg_pair map_pipe_control[] = { +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + // Enable 16 mappings for Pipe X + {MAX96724_PIPE_X_EN_L_ADDR, 0xFF}, + {MAX96724_PIPE_X_EN_H_ADDR, 0xFF}, +#else // Enable 4 mappings for Pipe X - {MAX96724_PIPE_X_EN_ADDR, 0x0F}, - // Mappings to CSI 1 (master for port A) - {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, + {MAX96724_PIPE_X_EN_L_ADDR, 0x0F}, + {MAX96724_PIPE_X_EN_H_ADDR, 0x00}, +#endif // Map data_type1 on vc_id - {MAX96724_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, - {MAX96724_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + {MAX96724_PIPE_X_SRC_0_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, + {MAX96724_PIPE_X_DST_0_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, // Map frame_start on vc_id {MAX96724_PIPE_X_SRC_1_MAP_ADDR, 0x00}, {MAX96724_PIPE_X_DST_1_MAP_ADDR, 0x00}, @@ -1560,525 +1991,469 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ {MAX96724_PIPE_X_SRC_2_MAP_ADDR, 0x01}, {MAX96724_PIPE_X_DST_2_MAP_ADDR, 0x01}, // Map data_type2 on vc_id - {MAX96724_PIPE_X_SRC_3_MAP_ADDR, 0x12}, - {MAX96724_PIPE_X_DST_3_MAP_ADDR, 0x12}, - // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled - {MAX96724_VID_RX0_ADDR, 0x23}, // pipe X - // LIM_HEART : Disabled - {MAX96724_VID_RX6_ADDR, dis_lim_heart}, - // Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs - {MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR, 0x0}, - }; - - for (i = 0; i < 10; i++) { - map_pipe_control[i].addr += 0x40 * pipe_id; - } - map_pipe_control[10].addr += 0x12 * pipe_id; - map_pipe_control[11].addr += 0x12 * pipe_id; - map_pipe_control[12].addr += 0x10 * pipe_id; - map_pipe_control[13].addr += 0x10 * pipe_id; - map_pipe_control[14].addr += 0x10 * pipe_id; - map_pipe_control[15].addr += 0x10 * pipe_id; - - map_pipe_control[0].val = en_mapping_num; - map_pipe_control[1].val = all_mapping_phy; - map_pipe_control[2].val = (vc_id_2b_lsb << 6) | data_type1; - map_pipe_control[3].val = (vc_id_2b_lsb << 6) | data_type1; - map_pipe_control[4].val = (vc_id_2b_lsb << 6) | 0x01; - map_pipe_control[5].val = (vc_id_2b_lsb << 6) | 0x01; - map_pipe_control[6].val = (vc_id_2b_lsb << 6) | 0x00; - map_pipe_control[7].val = (vc_id_2b_lsb << 6) | 0x00; - map_pipe_control[8].val = (vc_id_2b_lsb << 6) | data_type2; - map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; - map_pipe_control[10].val = 0x23; - map_pipe_control[11].val = dis_lim_heart; - map_pipe_control[12].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[13].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[14].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[15].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - - dev_dbg(dev, "%s: pipe %u set on vc_id=%u [reg: src/dst:0x%x, dstext:0x%x] \n", - __func__, pipe_id, vc_id, - (vc_id_2b_lsb << 6), - vc_id_2b_msb); - - err |= max96724_set_registers(dev, map_pipe_control, - ARRAY_SIZE(map_pipe_control)); - - dev_dbg(dev, "%s: d4xx specfic pipe %u optimizations\n", - __func__, pipe_id, vc_id, - (vc_id_2b_lsb << 6), - vc_id_2b_msb); - - err |= max96724_set_registers(dev, map_pipe_opt, - ARRAY_SIZE(map_pipe_opt)); - - return err; -} - - -static int __max96724_set_pipe(struct device *dev, int pipe_id, u8 data_type1, - u8 data_type2, u32 vc_id, u32 csi_id ) -{ - int err = 0; - int i = 0; - u8 en_mapping_num = 0x0F; - u8 all_mapping_phy = 0x55; - u8 dis_lim_heart = 0x0A; - - if (data_type2 == 0x0) - en_mapping_num = 0x07; - - switch (csi_id) { - case GMSL_CSI_PORT_A: - if (data_type2 == 0x0) - all_mapping_phy = 0x10; - else - all_mapping_phy = 0x00; - break; - case GMSL_CSI_PORT_B: - if (data_type2 == 0x0) - all_mapping_phy = 0x15; - else - all_mapping_phy = 0x55; - break; - case GMSL_CSI_PORT_C: - if (data_type2 == 0x0) - all_mapping_phy = 0x1A; - else - all_mapping_phy = 0xAA; - break; - case GMSL_CSI_PORT_D: - if (data_type2 == 0x0) - all_mapping_phy = 0x1F; - else - all_mapping_phy = 0xFF; - break; - default: - dev_warn(dev, "%s: invalid deserializer csi port, default CSI 1!\n", __func__); - if (data_type2 == 0x0) - all_mapping_phy = 0x15; - }; - - u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; - u8 vc_id_2b_msb = (((u8) vc_id) >> 2) & 0x3; - - struct reg_pair map_pipe_control[] = { - // Enable 4 mappings for Pipe X - {MAX96724_PIPE_X_EN_ADDR, 0x0F}, - // Mappings to PHY1 (master for port A) - {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, - // Map data_type1 on vc_id - {MAX96724_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, - {MAX96724_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + {MAX96724_PIPE_X_SRC_3_MAP_ADDR, GMSL_CSI_DT_EMBED}, + {MAX96724_PIPE_X_DST_3_MAP_ADDR, GMSL_CSI_DT_EMBED}, +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + // Map UYVY data_type1 on vc_id + {MAX96724_PIPE_X_SRC_4_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_YUV422_8}, + {MAX96724_PIPE_X_DST_4_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_YUV422_8}, + // Map UYVY frame_start on vc_id + {MAX96724_PIPE_X_SRC_5_MAP_ADDR, (1 << 6) | 0x00}, + {MAX96724_PIPE_X_DST_5_MAP_ADDR, (1 << 6) | 0x00}, + // Map frame end on vc_id + {MAX96724_PIPE_X_SRC_6_MAP_ADDR, (1 << 6) | 0x01}, + {MAX96724_PIPE_X_DST_6_MAP_ADDR, (1 << 6) | 0x01}, + // Map data_type2 on vc_id + {MAX96724_PIPE_X_SRC_7_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_EMBED}, + {MAX96724_PIPE_X_DST_7_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_EMBED}, + // Map Y8I data_type1 on vc_id + {MAX96724_PIPE_X_SRC_8_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_YUV422_8}, + {MAX96724_PIPE_X_DST_8_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_YUV422_8}, // Map frame_start on vc_id - {MAX96724_PIPE_X_SRC_1_MAP_ADDR, 0x00}, - {MAX96724_PIPE_X_DST_1_MAP_ADDR, 0x00}, + {MAX96724_PIPE_X_SRC_9_MAP_ADDR, (2 << 6) | 0x00}, + {MAX96724_PIPE_X_DST_9_MAP_ADDR, (2 << 6) | 0x00}, // Map frame end on vc_id - {MAX96724_PIPE_X_SRC_2_MAP_ADDR, 0x01}, - {MAX96724_PIPE_X_DST_2_MAP_ADDR, 0x01}, + {MAX96724_PIPE_X_SRC_10_MAP_ADDR, (2 << 6) | 0x01}, + {MAX96724_PIPE_X_DST_10_MAP_ADDR, (2 << 6) | 0x01}, // Map data_type2 on vc_id - {MAX96724_PIPE_X_SRC_3_MAP_ADDR, 0x12}, - {MAX96724_PIPE_X_DST_3_MAP_ADDR, 0x12}, - // SEQ_MISS_EN: Disabled / LINE_CRC_EN: Enabled / DIS_PKT_DET: Disabled / LCRC_ERR = Disabled - {MAX96724_VID_RX0_ADDR, 0x23}, + {MAX96724_PIPE_X_SRC_11_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_EMBED}, + {MAX96724_PIPE_X_DST_11_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_EMBED}, + // Map Y8 data_type1 on vc_id + {MAX96724_PIPE_X_SRC_12_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_RAW_8}, + {MAX96724_PIPE_X_DST_12_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_RAW_8}, + // Map frame_start on vc_id + {MAX96724_PIPE_X_SRC_13_MAP_ADDR, (3 << 6) | 0x00}, + {MAX96724_PIPE_X_DST_13_MAP_ADDR, (3 << 6) | 0x00}, + // Map frame end on vc_id + {MAX96724_PIPE_X_SRC_14_MAP_ADDR, (3 << 6) | 0x01}, + {MAX96724_PIPE_X_DST_14_MAP_ADDR, (3 << 6) | 0x01}, + // Map data_type2 on vc_id + {MAX96724_PIPE_X_SRC_15_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_EMBED}, + {MAX96724_PIPE_X_DST_15_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_EMBED}, +#endif + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR, all_mapping_phy}, + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_2_MAP_ADDR, all_mapping_phy}, + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_3_MAP_ADDR, all_mapping_phy}, +#endif + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + {MAX96724_VID_RX0_ADDR, 0x23}, // pipe X // LIM_HEART : Disabled {MAX96724_VID_RX6_ADDR, dis_lim_heart}, - // Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ {MAX96724_PIPE_X_SRCDST_VC_EXT_0_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR, 0x0}, +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + {MAX96724_PIPE_X_SRCDST_VC_EXT_4_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_5_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_6_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_7_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_8_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_9_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_10_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_11_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_12_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_13_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_14_MAP_ADDR, 0x0}, + {MAX96724_PIPE_X_SRCDST_VC_EXT_15_MAP_ADDR, 0x0}, +#endif }; +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + // Input link is the + for (i = 0; i < 38; i++) { + map_pipe_control[i].addr += 0x40 * link_id; + } - for (i = 0; i < 10; i++) { + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = en_mapping_num; + /* Extend each SRC/DST Datatype + */ + i = 2 + (vc_id * 8); + map_pipe_control[i].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[i+1].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[i+2].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[i+3].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[i+4].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[i+5].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[i+6].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[i+7].val = (vc_id_2b_lsb << 6) | data_type2; + + map_pipe_control[34].val = all_mapping_phy; + map_pipe_control[35].val = all_mapping_phy; + map_pipe_control[36].val = all_mapping_phy; + map_pipe_control[37].val = all_mapping_phy; + + map_pipe_control[38].addr += 0x12 * link_id; + map_pipe_control[39].addr += 0x12 * link_id; + + map_pipe_control[38].val = 0x23; + map_pipe_control[39].val = dis_lim_heart; + + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ + i= 40 + (vc_id * 4); + map_pipe_control[i].addr += 0x10 * link_id; + map_pipe_control[i+1].addr += 0x10 * link_id; + map_pipe_control[i+2].addr += 0x10 * link_id; + map_pipe_control[i+3].addr += 0x10 * link_id; + + map_pipe_control[i].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[i+1].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[i+2].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[i+3].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + + dev_info(dev, "%s: %s pipe %u set on EVERY d4xx dt/vc possible match\n", + __func__, + max96724_get_link_name(priv->src_link), + link_id); +#else + for (i = 0; i < 11; i++) { map_pipe_control[i].addr += 0x40 * pipe_id; } - map_pipe_control[10].addr += 0x12 * pipe_id; - map_pipe_control[11].addr += 0x12 * pipe_id; - map_pipe_control[12].addr += 0x10 * pipe_id; - map_pipe_control[13].addr += 0x10 * pipe_id; - map_pipe_control[14].addr += 0x10 * pipe_id; - map_pipe_control[15].addr += 0x10 * pipe_id; map_pipe_control[0].val = en_mapping_num; - map_pipe_control[1].val = all_mapping_phy; - map_pipe_control[2].val = (vc_id_2b_lsb << 6) | data_type1; - map_pipe_control[3].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[1].val = 0x0; + map_pipe_control[2].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[3].val = (vc_id_2b_lsb << 6) | 0x00; map_pipe_control[4].val = (vc_id_2b_lsb << 6) | 0x01; map_pipe_control[5].val = (vc_id_2b_lsb << 6) | 0x01; - map_pipe_control[6].val = (vc_id_2b_lsb << 6) | 0x00; - map_pipe_control[7].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[6].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[7].val = (vc_id_2b_lsb << 6) | data_type1; map_pipe_control[8].val = (vc_id_2b_lsb << 6) | data_type2; map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; - map_pipe_control[10].val = 0x23; - map_pipe_control[11].val = dis_lim_heart; - map_pipe_control[12].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[13].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[14].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - map_pipe_control[15].val = (vc_id_2b_msb << 3) | vc_id_2b_msb; - - dev_dbg(dev, "%s: pipe %u set on vc_id=%u [src/dst_vc:0x%x, ext_vc:0x%x] \n", - __func__, pipe_id, - vc_id, - (vc_id_2b_lsb << 6), - vc_id_2b_msb); - - err |= max96724_set_registers(dev, map_pipe_control, - ARRAY_SIZE(map_pipe_control)); + map_pipe_control[10].val = all_mapping_phy; - return err; -} - -int max96724_init_settings(struct device *dev) -{ - int err = 0; - int i; - unsigned src_pipe = MAX96724_PIPE_X; - unsigned phy_lane_map[MAX96724_NUM_CSI_LINKS]; - unsigned rev; + map_pipe_control[11].addr += 0x12 * pipe_id; + map_pipe_control[12].addr += 0x12 * pipe_id; - struct max96724 *priv = dev_get_drvdata(dev); - struct regmap *map = priv->regmap; - u8 src_port = max96724_link_to_port(priv->src_link); + map_pipe_control[11].val = 0x23; + map_pipe_control[12].val = dis_lim_heart; - dev_dbg(dev, "%s: Clearing %s CSI %u %s mode %s (num_lanes:x%u)\n", - __func__, - max96724_get_link_name(priv->src_link), - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", - priv->dst_n_lanes); + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ + map_pipe_control[13].addr += 0x10 * pipe_id; + map_pipe_control[14].addr += 0x10 * pipe_id; + map_pipe_control[15].addr += 0x10 * pipe_id; + map_pipe_control[16].addr += 0x10 * pipe_id; - mutex_lock(&priv->lock); + map_pipe_control[13].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[14].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[15].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[16].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - // disable PHY CLK continuous mode - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, - MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0)); - if (err) { - dev_err(dev, "%s: Failed to disable %s continuous clock : %d\n", - __func__, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); - goto init_settings_out; - } + dev_info(dev, "%s: %s pipe %u set on vc_id=%u [vc_id_3bits: src:0x%x, dst:0x%x] \n", + __func__, + max96724_get_link_name(priv->src_link), + pipe_id, + vc_id, + vc_id_3b_src, + vc_id_3b_dst); +#endif // disable CSI out link during pipe re-configuration - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, MAX96724_CSI_OUT_EN_FIELD, MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0)); if (err) { dev_err(dev, "%s: Failed to enable csi output link: %d\n", __func__, err); - goto init_settings_out; } - /* reconfigure all Video Pipe Input Sources - * This tells MAX96724 which PHY (Link) feeds which Video Pipe. - * Without this, video data cannot flow from GMSL links to CSI-2 output. + /* Disable DPLL link during pipe re-configuration */ - -#ifdef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE - /* Map link A pipe X -> pipe 0 - * Map link B pipe X -> pipe 1 - * Map link C pipe X -> pipe 2 - * Map link D pipe X -> pipe 3 - * Enable pipes 0/1/2/3 - * Enable max96712 "legacy" mode - * Non "legacy" mode ignores pipe mapping, and selects all streams for pipe - * 0. The ipu doesn't know what to do with that and throws spurious data - * stream errors. - */ - err = MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_0_ADDR, 0x10); - err |= MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_1_ADDR, 0x32); - err |= MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD - | MAX96724_VIDEO_PIPE_EN_FIELD(0) - | MAX96724_VIDEO_PIPE_EN_FIELD(1) - | MAX96724_VIDEO_PIPE_EN_FIELD(2) - | MAX96724_VIDEO_PIPE_EN_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, - MAX96724_VIDEO_PIPE_LEGACY_MODE) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(3), 1U)); -#else - /* Map link A pipe Z -> pipe 0 - * Map link B pipe Z -> pipe 1 - * Map link C pipe Z -> pipe 2 - * Map link D pipe Z -> pipe 3 - * Enable pipes 0/1/2/3 - */ - err = MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_0_ADDR, 0x50); - err |= MAX96724_WRITE_REG(map, MAX96724_VIDEO_PIPE_SEL_1_ADDR, 0xfa); - err |= MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(0) - | MAX96724_VIDEO_PIPE_EN_FIELD(1) - | MAX96724_VIDEO_PIPE_EN_FIELD(2) - | MAX96724_VIDEO_PIPE_EN_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(3), 1U)); -#endif + for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_DPLL_RESET(i), + MAX96724_DPLL_RESET_SOFT_RST_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 0U)); if (err) { - dev_err(dev, "%s: Failed to configure %s pipe to Video pipe X/Y/Z/U : %d\n", + dev_err(dev, "%s: Failed to reset configure CSI %u %s DPLL : %d\n", __func__, - max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); - goto init_settings_out; } - for (i = 0; i < MAX96724_MAX_PIPES; i++) { - dev_dbg(dev, "%s enable GMSL link through pipe X source to Video pipe %d", - max96724_get_link_name(priv->src_link), - i); - - if (priv->d4xx_hacks) { - err |= __max96724_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, - GMSL_CSI_DT_EMBED, i, priv->dst_link); - } else { - err |= __max96724_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, - GMSL_CSI_DT_EMBED, i, priv->dst_link); - } - } - if (err) { - dev_err(dev, "%s: Failed to configure %s pipe X to Video pipes : %d\n", + /* Video Pipe Input Source Selection + * This tells MAX96724 which PHY (Link) feeds which Video Pipe. + * Without this, video data cannot flow from GMSL links to CSI-2 output. + * + * REG 0x00F0: Video Pipe 0/1 input selection + * Bit[7:6] = : Pipe 1 from GMSL A, B, C or D link + * Bit[5:4] = 01: Pipe 1 uses internal Pipe Y + * Bit[3:2] = : Pipe 0 from GMSL A, B, C or D link + * Bit[1:0] = 00: Pipe 0 uses internal Pipe X + * + * REG 0x00F1: Video Pipe 2/3 input selection + * Bit[7:6] = : Pipe 3 from GMSL A, B, C or D link + * Bit[5:4] = 11: Pipe 3 uses internal Pipe Z + * Bit[3:2] = : Pipe 2 from GMSL A, B, C or D link + * Bit[1:0] = 10: Pipe 2 uses internal Pipe U + * + * REG 0x00F4: Enable/disable Video Pipes + * Bit[3:0] = : Enable Pipe 3, 2, 1 or 0 + */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); + + err |= max96724_set_registers(dev, map_pipe_select, + ARRAY_SIZE(map_pipe_select)); + +#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + // Enable max96712 "legacy" mode + // Non "legacy" mode ignores pipe mapping, and selects all streams for pipe + // 0. The ipu doesn't know what to do with that and throws spurious data + // stream errors. + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_VIDEO_PIPE_LEGACY_MODE)); + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(0), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), link_id) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), pipe_id)); + if (err) + dev_err(dev, "%s: Failed to select max96712 legacy mode: %d\n", __func__, - max96724_get_link_name(priv->src_link), err); - goto init_settings_out; - } - -#ifndef CONFIG_VIDEO_D4XX_MAX96712_LEGACY_MODE - /* Write 0x1d00 register sequence - * This register is written twice with different values - appears to be - * some kind of command sequence or state machine control - */ - err = max96724_write_reg(dev, 0x1d00, 0xf4); - err |= max96724_write_reg(dev, 0x1d00, 0xf5); - if (err) { - dev_warn(dev, "%s: Failed to reset internal state machine : %d\n", + else + dev_dbg(dev, "%s: mapped max96712 legacy mode pipe 0 to streams (X,Y,Z and/or U)\n", + __func__); + +#elif defined(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) + // Enable all streams (X,Y,Z and/or U) source mapping +#if !defined(CONFIG_VIDEO_D4XX_MAX96712) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_VIDEO_PIPE_SRCMAP_MODE)); +#endif + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(0), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) + | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), 1U)); + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(2), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2) + | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2), 2U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3), 3U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2), 2U) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), 3U)); + if (err) + dev_err(dev, "%s: Failed to map pipes to streams (X,Y,Z and/or U) mode: %d\n", __func__, err); - } + else + dev_dbg(dev, "%s: mapped max967xx ALL pipe to streams (X,Y,Z and/or U) mode\n", + __func__); +#else + // Enable specific pipe streams (X,Y,Z and/or U) source mapping +#if !defined(CONFIG_VIDEO_D4XX_MAX96712) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_VIDEO_PIPE_SRCMAP_MODE)); #endif + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(pipe_id), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id), link_id) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), pipe_id)); + if (err) + dev_err(dev, "%s: Failed to select all streams (X,Y,Z and/or U) mode: %d\n", + __func__, + err); + else + dev_dbg(dev, "%s: mapped max967xx pipe %u to streams (X,Y,Z and/or U) mode\n", + __func__, + pipe_id); +#endif + err = max96724_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); - msleep(300); /* Wait after pipe configuration */ - - /* Configure MIPI D-PHY/C-PHY - * These registers configure the MIPI physical layer for CSI-2 output - */ - dev_info(dev, "%s: Configuring CSI %u %s mode %s: num_lanes x%u (%s)\n", - __func__, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", - priv->dst_n_lanes, - priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712"); + // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); - err = max967xx_phy_tuning(dev); + /* Configure per Pipe CSI and PHY SYNC + */ + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(pipe_id), + MAX96724_MAP_CON_SYNC_PIPE_0_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_FIELD, + MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id)); + + /* Configure per Pipe CSI and PHY type + */ + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(csi_id), + MAX96724_MIPI_TX_CPHY_EN_FIELD + | MAX96724_MIPI_TX_VCX_EN_FIELD + | MAX96724_MIPI_TX_LANE_CNT_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) + | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) + | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 1U)); if (err) { - dev_err(dev, "%s: Failed to apply %s tunning : %d\n", + dev_err(dev, "%s: Failed to configure pipe control %u %s %u lanes: %d\n", __func__, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->dst_n_lanes, err); - goto init_settings_out; } - err = MAX96724_UPDATE_BITS(map, MAX96724_DPLL_RESET(priv->dst_link), - MAX96724_DPLL_RESET_SOFT_RST_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 1U)); + err |= max967xx_phy_tuning(dev); if (err) { - dev_err(dev, "%s: Failed to reset configure CSI %u %s DPLL : %d\n", + dev_err(dev, "%s: Failed to apply %s tunning : %d\n", __func__, - priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); - goto init_settings_out; } - - /* Step 1: Configure MIPI PHY0 as 2-lane or 4-lanes (WITHOUT continuous clock first) - * Script: 0x08A0 = 0x02 (2-lane, no continuous clock) - * Script: 0x08A0 = 0x04 (4-lane, no continuous clock) - * Continuous clock will be added later in - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, - MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD - | MAX96724_MIPI_PHY0_MODE_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, priv->csi_mode)); + + /* Configure lane mapping DPLL Frequency + * Script: DPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x34 + * Script: CPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x26 + */ + for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_DPLL_FREQ(i), + MAX96724_DPLL_FREQ_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS + : MAX96724_DPHY_CLK_1500BPS)); if (err) { - dev_err(dev, "%s: Failed to enable %s %s lanes : %d\n", + dev_err(dev, "%s: Failed to set CSI %u %s at 1500bps: %d\n", __func__, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", err); - goto init_settings_out; } - /* Step 2: Configure MIPI data rate - * Script: 0x08A3/0x08A4 = 0xE4 (0xE4 = 800Mbps per lane for 1x4 mode) - * Script: 0x08A3/0x08A4 = 0x44 (0x44 = 800Mbps per lane for 4x2 mode) - * + /* Configure DPHY or CPHY for all pipes + * Script: 0x0903/0x0943/0x0983/0x09C3 = auto deskew enable + * Script: 0x8AD/0x8AE = cphy preamble */ - err = MAX96724_WRITE_REG(map, MAX96724_MIPI_PHY_LANE_MAP(GMSL_CSI_PORT_A), - (priv->csi_mode == MAX96724_CSI_MODE_2X4) ? 0xE4 : 0x44 ); - err |= MAX96724_WRITE_REG(map, MAX96724_MIPI_PHY_LANE_MAP(GMSL_CSI_PORT_C), - (priv->csi_mode == MAX96724_CSI_MODE_2X4) ? 0xE4 : 0x44 ); + for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_DESKEW_INIT(i), + MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD + || MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U) + || MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD, 7U)); if (err) { - dev_err(dev, "%s: Failed to configure CSI %u %s lanes %s map: %d\n", + dev_err(dev, "%s: Failed to set CSI %u %s deskew to 7: %d\n", __func__, - priv->dst_link, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", err); - goto init_settings_out; } - dev_dbg(dev, "%s: mapping %s %s mode x%u-lanes pinout : 0x8A3/0x8A4 applied\n", - __func__, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", - priv->dst_n_lanes); - - + /* Configure CPHY preamble + */ if (priv->csi_phy == MAX96724_CSI_CPHY) { - /* Step 3b: Configure CPHY preamble - */ - err = MAX96724_WRITE_REG(map, MAX96724_CPHY_PREAMBLE_ADDR, + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_CPHY_PREAMBLE_ADDR, MAX96724_FIELD_PREP(MAX96724_CPHY_PREAMBLE_FIELD, MAX96724_CPHY_PREAMBLE_DEFAULT)); if (err) { dev_err(dev, "%s: Failed to set CSI %u %s preamble : %d\n", __func__, - priv->dst_link, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); - goto init_settings_out; } - err = MAX96724_WRITE_REG(map, MAX96724_CPHY_PREP_ADDR, MAX96724_CPHY_PREP_DEFAULT); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_CPHY_PREP_ADDR, MAX96724_CPHY_PREP_DEFAULT); if (err) { dev_err(dev, "%s: Failed to set CSI %u %s prep and post : %d\n", __func__, - priv->dst_link, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); - goto init_settings_out; } dev_dbg(dev, "%s: preparing %s preamble : 0x8AD/0x8AE applied\n", __func__, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY"); } - /* Step 3: Configure per Pipe CSI and PHY CLK cycle + + /* Enable DPLL link after pipe re-configuration */ - for (err=0, i = GMSL_CSI_PORT_A; i < MAX96724_MAX_SOURCES; i++) - err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_LANE_CNT(i), - MAX96724_MIPI_TX_CPHY_EN_FIELD - | MAX96724_MIPI_TX_WAKEUP_CYC_FIELD - | MAX96724_MIPI_TX_VCX_EN_FIELD - | MAX96724_MIPI_TX_LANE_CNT_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) - | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) - | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); + for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_DPLL_RESET(i), + MAX96724_DPLL_RESET_SOFT_RST_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 1U)); if (err) { - dev_err(dev, "%s: Failed to configure CSI %u %s %u lanes: %d\n", + dev_err(dev, "%s: Failed to set/reset CSI %u %s: %d\n", __func__, - priv->dst_link, + csi_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->dst_n_lanes, err); - goto init_settings_out; } + return err; +} - /* Step 3b: Configure auto-deskew for all pipes - * Script: 0x0903/0x0943/0x0983/0x09C3 = auto deskew enable - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_A), - MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); - err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_B), - MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); - err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_C), - MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); - err |= MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_DESKEW_INIT(GMSL_CSI_PORT_D), - MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_FIELD, 1U)); - if (err) { - dev_err(dev, "%s: Failed to enable deskew CSI %u %s: %d\n", - __func__, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); - goto init_settings_out; - } +int max96724_init_settings(struct device *dev) +{ + int err = 0; + int i; + unsigned rev; - /* Step 4: Configure MIPI PHY lane control - * Script: 0x08A2 = 0xF0 (all lanes enabled) - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY_ENABLE, - MAX96724_MIPI_PHY_ENABLE_FIELD(0) - | MAX96724_MIPI_PHY_ENABLE_FIELD(1) - | MAX96724_MIPI_PHY_ENABLE_FIELD(2) - | MAX96724_MIPI_PHY_ENABLE_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(0), 1U) | - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(1), 1U) | - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(2), 1U) | - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(3), 1U)); - if (err) { - dev_err(dev, "%s: Failed to enable all %s output lanes: %d\n", + struct max96724 *priv = dev_get_drvdata(dev); + struct regmap *map = priv->regmap; + u8 src_port = max96724_link_to_port(priv->src_link); + + dev_dbg(dev, "%s: Clearing %s CSI %u %s mode %s (num_lanes:x%u)\n", + __func__, + max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->dst_n_lanes); + + mutex_lock(&priv->lock); + + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + dev_dbg(dev, "%s: enable %s link to Video pipe %d", __func__, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); - goto init_settings_out; - } + max96724_get_link_name(priv->src_link), + i); - /* Step 5: Configure lane mapping DPLL Frequency - * Script: DPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x35 - * Script: CPHY_CLK_1500BPS 0x0415/0x0418/0x041B/0x041E = 0x25 - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_A), - MAX96724_DPLL_FREQ_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS - : MAX96724_DPHY_CLK_1500BPS)); - - err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_B), - MAX96724_DPLL_FREQ_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS - : MAX96724_DPHY_CLK_1500BPS)); - err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_C), - MAX96724_DPLL_FREQ_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS - : MAX96724_DPHY_CLK_1500BPS)); - err |= MAX96724_UPDATE_BITS(map, MAX96724_DPLL_FREQ(GMSL_CSI_PORT_D), - MAX96724_DPLL_FREQ_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? MAX96724_CPHY_CLK_1500BPS - : MAX96724_DPHY_CLK_1500BPS)); + err |= __max96724_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i, priv->dst_link); + } if (err) { - dev_err(dev, "%s: Failed to set CSI %u %s at 1500bps: %d\n", + dev_err(dev, "%s: Failed to configure %s link to Video pipes : %d\n", __func__, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + max96724_get_link_name(priv->src_link), err); goto init_settings_out; } - /* Reset all GMSL links after configuration (matches script line 508) * Reference script does this AFTER all serializer config and BEFORE CSI enable: * 1. Restore all links (0x0006 = 0xFF) @@ -2088,59 +2463,64 @@ int max96724_init_settings(struct device *dev) * * This reset ensures all link configurations take effect properly * and synchronizes the GMSL links before CSI streaming starts. - err = MAX96724_UPDATE_BITS(map, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(0) - | MAX96724_RESET_LINK_FIELD(1) - | MAX96724_RESET_LINK_FIELD(2) - | MAX96724_RESET_LINK_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 1U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 1U)); - if (err) { - dev_err(dev, "%s: Failed to reset %s link: %d\n", - __func__, - max96724_get_link_name(priv->src_link), - err); - goto init_settings_out; - } + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_CTRL_FIELD(0) + | MAX96724_RESET_CTRL_FIELD(1) + | MAX96724_RESET_CTRL_FIELD(2) + | MAX96724_RESET_CTRL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 1U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 1U)); + if (err) + dev_err(dev, "%s: Failed to reset all links: %d\n", + __func__, err); // delay to settle link msleep(100); - // clear GMSL2 links Reset - err = MAX96724_UPDATE_BITS(map, MAX96724_RESET_CTRL_ADDR, - MAX96724_RESET_LINK_FIELD(0) - | MAX96724_RESET_LINK_FIELD(1) - | MAX96724_RESET_LINK_FIELD(2) - | MAX96724_RESET_LINK_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(0), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(1), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(2), 0U) - | MAX96724_FIELD_PREP(MAX96724_RESET_LINK_FIELD(3), 0U)); - if (err) { - dev_err(dev, "%s: Failed to reset %s link: %d\n", - __func__, - max96724_get_link_name(priv->src_link), - err); - goto init_settings_out; - } - */ + // clear GMSL2 links Reset + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, + MAX96724_RESET_CTRL_FIELD(0) + | MAX96724_RESET_CTRL_FIELD(1) + | MAX96724_RESET_CTRL_FIELD(2) + | MAX96724_RESET_CTRL_FIELD(3), + MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(0), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(1), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(2), 0U) + | MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(3), 0U)); + if (err) + dev_err(dev, "%s: Failed to reset all links: %d\n", + __func__, err); + */ - /* CRITICAL: DO NOT enable CSI_OUT_EN (0x040b) here! - * - * Reference script shows CSI should be enabled LAST, after: - * 1. All MAX967xx configuration complete - * 2. GMSL link is locked - * 3. Sensor is streaming valid data - * - * IPU6 behavior is CORRECT: - * - Firmware waits for initial capture BEFORE calling s_stream - * - This ensures IPU is ready to receive data - * - CSI should only output when sensor has valid data ready - * - * We enable CSI in s_stream AFTER sensor stream-on succeeds. - */ + /* Enable all channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch %s link i2c channel: %d\n", + __func__, + max96724_get_link_name(priv->src_link), + err); + else + dev_dbg(dev, "%s: Switched to %s link i2c channel\n", + __func__, + max96724_get_link_name(priv->src_link)); + + /* Configure MIPI D-PHY/C-PHY + * These registers configure the MIPI physical layer for CSI-2 output + */ + dev_info(dev, "%s: Configuring CSI %u %s mode %s: num_lanes x%u (%s)\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->dst_n_lanes, + priv->dev_id == MAX96724_DEV_REV_ID ? "MAX96724" : "MAX96712"); /* Re-Check GMSL link status after initial configuration */ { @@ -2171,14 +2551,6 @@ int max96724_init_settings(struct device *dev) (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); - unsigned int pipe_de_status = 0; - max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); - dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_de_status, - (pipe_de_status & 0x01) ? "DE_DET_0 " : "", - (pipe_de_status & 0x02) ? "DE_DET_1 " : "", - (pipe_de_status & 0x04) ? "DE_DET_2 " : "", - (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); } init_settings_out: @@ -2194,7 +2566,6 @@ int max96724_set_pipe(struct device *dev, int pipe_id, struct max96724 *priv = dev_get_drvdata(dev); unsigned src_pipe = MAX96724_PIPE_X; unsigned rev; - struct regmap *map = priv->regmap; int err = 0; u8 src_port = max96724_link_to_port(priv->src_link); @@ -2210,104 +2581,53 @@ int max96724_set_pipe(struct device *dev, int pipe_id, pipe_id, vc_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); mutex_lock(&priv->lock); - /* Step 1: Disable CSI output first (reverse order of enable) - * This stops data transmission to IPU before shutting down PHY - */ - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, - MAX96724_CSI_OUT_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0U)); - if (err) { - dev_err(dev, "%s: Failed to enable csi output link: %d\n", - __func__, - err); - goto set_pipe_out; - } - dev_dbg(dev, "%s: CSI output disabled\n", __func__); - - /* Step 2: Disable MIPI PHY continuous clock mode - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, - MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 0U)); - if (err) { - dev_err(dev, "%s: Failed to disable %s continuous clock : %d\n", - __func__, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); - goto set_pipe_out; - } - dev_dbg(dev, "%s: MIPI PHY continuous clock disabled\n", __func__); - - /* Small delay for clean shutdown */ - msleep(10); - - /* Enable PHY (clear standby bit) - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY_ENABLE, - MAX96724_MIPI_PHY_ENABLE_FIELD(priv->dst_link), - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(priv->dst_link), 1U)); - if (err) { - dev_err(dev, "%s: Failed to enable CSI %u %s : %d\n", - __func__, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); - goto set_pipe_out; - } - dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", __func__, max96724_get_link_name(priv->src_link), pipe_id, data_type1, data_type2, vc_id); - err = MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); - if (err) { - dev_err(dev, "%s: Failed to disable %s pipe X to Video pipe %u : %d\n", - __func__, - max96724_get_link_name(priv->src_link), - pipe_id, - err); - goto set_pipe_out; - } - - if (priv->d4xx_hacks) { - err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); - } else { - err = __max96724_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); - } - - err = MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 1U)); - if (err) { - dev_err(dev, "%s: Failed to enable %s pipe X to Video pipe %u : %d\n", - __func__, - max96724_get_link_name(priv->src_link), - pipe_id, - err); - goto set_pipe_out; - } + err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); - msleep(300); /* Wait after pipe configuration */ + /* Enable all channels + */ + err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, + ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) + | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); + if (err) + dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", + __func__, + err); - dev_dbg(dev, "%s: Wake-up %s pipe %u (vc_id %u) CSI %u %s mode %s (num_lanes:x%u)\n", + /* CRITICAL: DO NOT enable CSI_OUT_EN (0x040b) here! + * + * Reference script shows CSI should be enabled LAST, after: + * 1. All MAX967xx configuration complete + * 2. GMSL link is locked + * 3. Sensor is streaming valid data + * + * IPU behavior is CORRECT: + * - Firmware waits for initial capture BEFORE calling s_stream + * - This ensures IPU is ready to receive data + * - CSI should only output when sensor has valid data ready + * + * We enable CSI in s_stream AFTER sensor stream-on succeeds. + dev_info(dev, "%s: Wake-up %s CSI %u %s mode %s (num_lanes:x%u)\n", __func__, max96724_get_link_name(priv->src_link), - pipe_id, vc_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_1X4 || MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); - - /* Enable MIPI TX controller and enable PHY CLK cycle */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), + + // Enable MIPI TX controller and enable PHY CLK cycle + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); if (err) { @@ -2316,22 +2636,8 @@ int max96724_set_pipe(struct device *dev, int pipe_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); - goto set_pipe_out; } - /* CRITICAL: Wait for sensor video to stabilize before enabling CSI output! - * 1. Triggers all cameras via GPIO (MFP7/MFP8) - * 2. sleep 0.3 ← 300ms delay to let sensors stabilize! - * 3. i2ctransfer ... 0x04 0x0b 0x02 ← Enable CSI - * 4. i2ctransfer ... 0x08 0xa0 0x84 ← Enable continuous clock - * - * Without this delay, deserializer tries to lock video before sensor - * outputs stable video data, resulting in VIDEO_LOCK failure (0x0108=0x02) - */ - dev_info(dev, "%s: Waiting 300ms for sensor video to stabilize...\n", __func__); - msleep(300); - dev_info(dev, "%s: Sensor stabilization delay complete\n", __func__); - // enable CSI out link after initialization complet err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, MAX96724_CSI_OUT_EN_FIELD, @@ -2340,32 +2646,27 @@ int max96724_set_pipe(struct device *dev, int pipe_id, dev_err(dev, "%s: Failed to enable csi output link: %d\n", __func__, err); - goto set_pipe_out; } - dev_dbg(dev, "%s: CSI output enabled\n", __func__); - /* Small delay for CSI to stabilize */ - msleep(10); - - /* Turn on MIPI PHY continuous clock mode (matches script line 520) - * Reference script does this AFTER CSI enable (0x040B = 0x02) - * This enables continuous clock on MIPI CSI-2 interface - * 0x84 = 0b10000100 - * Bit[7] = 1: Enable continuous clock mode - */ - err = MAX96724_UPDATE_BITS(map, MAX96724_MIPI_PHY0, - MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD - | MAX96724_MIPI_PHY0_MODE_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U) | - MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, priv->csi_mode)); - if (err) { - dev_err(dev, "%s: Failed to enable %s continuous clock : %d\n", + // Turn on MIPI PHY continuous clock mode + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U)); + if (err) + dev_err(dev, "%s: Failed to enable %s %s continuous clock : %d\n", __func__, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", err); - goto set_pipe_out; - } - dev_dbg(dev, "%s: MIPI PHY continuous clock mode enabled\n", __func__); + else + dev_dbg(dev, "%s: %s CSI %u %s %s (num_lanes:x%u) continuous clock mode enabled\n", + __func__, + max96724_get_link_name(priv->src_link), + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", + priv->dst_n_lanes); + */ /* Double-Check GMSL link status after stream start configuration */ { @@ -2396,14 +2697,6 @@ int max96724_set_pipe(struct device *dev, int pipe_id, (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); - unsigned int pipe_de_status = 0; - max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); - dev_dbg(dev, "%s: %s Video Pipeline status: 0x%02x (bit0-3: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_de_status, - (pipe_de_status & 0x01) ? "DE_DET_0 " : "", - (pipe_de_status & 0x02) ? "DE_DET_1 " : "", - (pipe_de_status & 0x04) ? "DE_DET_2 " : "", - (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); } dev_info(dev, "%s: Successfully enabled %s pipe %u (vc_id %u) CSI %u %s lanes %s map\n", @@ -2412,7 +2705,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, pipe_id, vc_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_2X4 ? "2X4" : "4X2"); + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4"); set_pipe_out: mutex_unlock(&priv->lock); @@ -2519,26 +2812,31 @@ static int max96724_parse_pdata(struct max96724 *priv, } priv->max_src = pdata->max_src; + priv->src_link = pdata->src_link; if (pdata->d4xx_hacks) { priv->d4xx_hacks = pdata->d4xx_hacks; } else { priv->d4xx_hacks = 0; } + priv->dst_link = GMSL_CSI_PORT_B; } else { priv->csi_mode = MAX96724_CSI_MODE_2X4; priv->csi_phy = MAX96724_CSI_CPHY; + priv->src_link = GMSL_SERDES_CSI_LINK_A; + priv->dst_link = GMSL_CSI_PORT_B; priv->max_src = 1; priv->reset_gpio = 0; priv->vdd_cam_1v2 = NULL; - priv->d4xx_hacks = 0; + priv->d4xx_hacks = 1; } - dev_info(&client->dev, "%s: %s Deserializer PDATA set CSI %s %s lane config (max_src=%u) \n", + dev_info(&client->dev, "%s: %s Deserializer PDATA set %s link (max_src=%u) to CSI %s %s lane config\n", __func__, pdata->d4xx_hacks ? "d4xx" : "", + max96724_get_link_name(priv->src_link), + priv->max_src, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", - priv->max_src); + priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4"); return 0; } diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 447db269..9b009b80 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -31,8 +31,12 @@ #include #include #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) +#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT #include #include +#else +#include +#endif #endif #include #include @@ -42,7 +46,7 @@ #ifdef CONFIG_VIDEO_D4XX_SERDES #include -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) #include #else #include @@ -541,10 +545,7 @@ struct ds5 { #define NR_OF_CSI2_BE_SOC_STREAMS 16 #define NR_OF_DS5_SUB_STREAMS 6 /*d+d.md,c+c.md,ir,imu*/ int pad_to_vc[DS5_MUX_PAD_COUNT]; -#endif -#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT int pad_to_substream[NR_OF_CSI2_BE_SOC_STREAMS]; -#else struct mutex mutex; #endif }; @@ -686,6 +687,7 @@ static int ds5_write(struct ds5 *state, u16 reg, u16 val) static int ds5_raw_write(struct ds5 *state, u16 reg, const void *val, size_t val_len) { + int ret = regmap_raw_write(state->regmap, reg, val, val_len); if (ret < 0) dev_err(&state->client->dev, @@ -702,6 +704,7 @@ static int ds5_raw_write(struct ds5 *state, u16 reg, static int ds5_read(struct ds5 *state, u16 reg, u16 *val) { + int ret = regmap_raw_read(state->regmap, reg, val, 2); if (ret < 0) dev_err(&state->client->dev, "%s(): i2c read failed %d, 0x%04x\n", @@ -1631,12 +1634,15 @@ static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, "set pipe %d, data_type1: 0x%x, \ data_type2: 0x%x, vc_id: %u\n", pipe_id, data_type1, data_type2, vc_id); +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + ret = max96724_switch_link_channel(state->dser_dev); ret |= max9295_set_pipe(state->ser_dev, pipe_id, data_type1, data_type2, vc_id); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 ret |= max96724_set_pipe(state->dser_dev, pipe_id, data_type1, data_type2, vc_id); #else + ret = max9295_set_pipe(state->ser_dev, pipe_id, + data_type1, data_type2, vc_id); ret |= max9296_set_pipe(state->dser_dev, pipe_id, data_type1, data_type2, vc_id); #endif @@ -1714,7 +1720,7 @@ static int ds5_configure(struct ds5 *state) vc_id); // reset data path when switching to Y12I if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) max96724_reset_oneshot(state->dser_dev); #else max9296_reset_oneshot(state->dser_dev); @@ -1849,6 +1855,7 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, int ret = 0; struct v4l2_subdev_route *route; struct v4l2_subdev_stream_configs *new_configs; + struct v4l2_mbus_framefmt *ffmt = &sensor->format; u32 idx; /* set state by vc */ @@ -1860,26 +1867,31 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, * the routing table */ new_configs= &v4l2_state->stream_configs; - dev_dbg(sensor->sd.dev, "%s(): update %s route on stream-id=%u (num_configs=%u)\n", + dev_dbg(sensor->sd.dev, "%s(): update %s route on stream-id=%u vc-id=%u (num_configs=%u)\n", __func__, sensor->sd.name, + state->pad_to_substream[sensor->mux_pad], state->pad_to_vc[sensor->mux_pad], new_configs->num_configs); idx = 0; for_each_active_route(&v4l2_state->routing, route) { new_configs->configs[idx].pad = route->sink_pad; - new_configs->configs[idx].stream = state->pad_to_vc[sensor->mux_pad]; + new_configs->configs[idx].stream = state->pad_to_substream[sensor->mux_pad]; + new_configs->configs[idx].fmt = *ffmt; - dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] sink pad/stream=%u/%u\n", - __func__, sd->name, i, route->sink_pad, state->pad_to_vc[sensor->mux_pad]); + dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] sink pad/stream=%u/%u fmt: code %x, res %ux%u\n", + __func__, sd->name, i, route->sink_pad, state->pad_to_substream[sensor->mux_pad], + ffmt->code, ffmt->width, ffmt->height); idx++; new_configs->configs[idx].pad = route->source_pad; - new_configs->configs[idx].stream = state->pad_to_vc[sensor->mux_pad]; + new_configs->configs[idx].stream = state->pad_to_substream[sensor->mux_pad]; + new_configs->configs[idx].fmt = *ffmt; - dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] src pad/stream=%u/%u\n", - __func__, sd->name, i, route->sink_pad, state->pad_to_vc[sensor->mux_pad]); + dev_dbg(sd->dev, "%s(): update %s stream_configs->configs[%u] src pad/stream=%u/%u fmt: code %x, res %ux%u\n", + __func__, sd->name, i, route->sink_pad, state->pad_to_substream[sensor->mux_pad], + ffmt->code, ffmt->width, ffmt->height); idx++; } @@ -1888,8 +1900,8 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_route ds5_sensor_routes[] = { { - .sink_stream = state->pad_to_vc[sensor->mux_pad], - .source_stream = state->pad_to_vc[sensor->mux_pad], + .source_stream = state->pad_to_substream[sensor->mux_pad], + .sink_stream = state->pad_to_substream[sensor->mux_pad], .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; @@ -1908,9 +1920,10 @@ static int ds5_sensor_init_state(struct v4l2_subdev *sd, } new_configs= &v4l2_state->stream_configs; - dev_dbg(sensor->sd.dev, "%s(): initialized %s route on stream-id=%u (num_configs=%u)\n", + dev_dbg(sensor->sd.dev, "%s(): initialized %s route on stream-id=%u vc-id=%u (num_configs=%u)\n", __func__, sensor->sd.name, + state->pad_to_substream[sensor->mux_pad], state->pad_to_vc[sensor->mux_pad], new_configs->num_configs); @@ -1943,6 +1956,25 @@ static int ds5_sensor_disable_streams(struct v4l2_subdev *sd, return 0; } +static int ds5_sensor_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_frame_desc *desc) +{ + struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); + struct ds5 *state = v4l2_get_subdevdata(sd); + struct ds5_format *mf = sensor->config.format; + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + desc->num_entries = 0; + desc->entry[desc->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + desc->entry[desc->num_entries].stream = state->pad_to_substream[sensor->mux_pad]; + desc->entry[desc->num_entries].pixelcode = mf->mbus_code; + desc->entry[desc->num_entries].length = 0; + desc->entry[desc->num_entries].bus.csi2.vc = state->pad_to_vc[sensor->mux_pad]; + desc->entry[desc->num_entries].bus.csi2.dt = mf->data_type; + desc->num_entries++; + return 0; +} #endif #endif @@ -1954,6 +1986,7 @@ static const struct v4l2_subdev_pad_ops ds5_depth_pad_ops = { .set_fmt = ds5_sensor_set_fmt, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) #ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .get_frame_desc = ds5_sensor_get_frame_desc, .enable_streams = ds5_sensor_enable_streams, .disable_streams = ds5_sensor_disable_streams, #endif @@ -1988,6 +2021,7 @@ static const struct v4l2_subdev_pad_ops ds5_ir_pad_ops = { .set_fmt = ds5_sensor_set_fmt, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) #ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .get_frame_desc = ds5_sensor_get_frame_desc, .enable_streams = ds5_sensor_enable_streams, .disable_streams = ds5_sensor_disable_streams, #endif @@ -2012,6 +2046,7 @@ static const struct v4l2_subdev_pad_ops ds5_rgb_pad_ops = { .set_fmt = ds5_sensor_set_fmt, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) #ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .get_frame_desc = ds5_sensor_get_frame_desc, .enable_streams = ds5_sensor_enable_streams, .disable_streams = ds5_sensor_disable_streams, #endif @@ -2035,6 +2070,7 @@ static const struct v4l2_subdev_pad_ops ds5_imu_pad_ops = { .set_fmt = ds5_sensor_set_fmt, #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) #ifndef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT + .get_frame_desc = ds5_sensor_get_frame_desc, .enable_streams = ds5_sensor_enable_streams, .disable_streams = ds5_sensor_disable_streams, #endif @@ -2337,6 +2373,10 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) mutex_lock(&state->lock); +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + ret = max96724_switch_link_channel(state->dser_dev); +#endif + switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: ret = ds5_write(state, base | DS5_MANUAL_GAIN, ctrl->val); @@ -2774,6 +2814,10 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s \n", __func__, ds5_get_sensor_name(state), ctrl->name); +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + ret = max96724_switch_link_channel(state->dser_dev); +#endif + switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: @@ -3471,6 +3515,8 @@ static int ds5_board_setup(struct ds5 *state) // mux c - 4 0x62 0x68 // mux d - 4 0x64 0x6a +static unsigned short ds5_serdes_csi_output(int c_bus, int c_addr); + static int ds5_board_setup(struct ds5 *state) { struct device *dev = &state->client->dev; @@ -3480,22 +3526,59 @@ static int ds5_board_setup(struct ds5 *state) int err = 0; int i; char suffix = pdata->suffix; + struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; char serdes_suffix[5]; /* suffix string for subdevs */ +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + /* Derive Deser CSI link mapping */ + switch (spdata->suffix[0]) { + case 'b': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; + break; + case 'c': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_C; + break; + case 'd': + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_D; + break; + default: + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; + break; + }; +#endif + static struct max9295_pdata max9295_pdata = { .is_prim_ser = 1, // todo: configurable .def_addr = 0x40, // todo: configurable .d4xx_hacks = 1, }; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) + static struct max96724_pdata max96724_pdata = { + .max_src = 4, + .csi_mode = GMSL_CSI_4X2_MODE, //GMSL_CSI_2X4_MODE, + .d4xx_hacks = 1, + }; + max96724_pdata.src_link = state->g_ctx.serdes_csi_link; + max96724_pdata.csi_phy = (pdata->bus_type == V4L2_MBUS_CSI2_CPHY) ? GMSL_CSI_CPHY : GMSL_CSI_DPHY; + max96724_pdata.csi_out = ds5_serdes_csi_output(bus, pdata->subdev_info[0].board_info.addr); + + static struct i2c_board_info i2c_info_des = { + I2C_BOARD_INFO("d4xx-max96724", 0x27), + .platform_data = &max96724_pdata, + }; +#elif defined(CONFIG_VIDEO_D4XX_MAX96712) static struct max96724_pdata max96724_pdata = { .max_src = 4, .csi_mode = GMSL_CSI_2X4_MODE, //GMSL_CSI_4X2_MODE, - .csi_phy = GMSL_CSI_CPHY, + .csi_phy = GMSL_CSI_DPHY, + .csi_out = GMSL_CSI_PORT_A, .d4xx_hacks = 1, }; + + max96724_pdata.src_link = state->g_ctx.serdes_csi_link; + static struct i2c_board_info i2c_info_des = { - I2C_BOARD_INFO("d4xx-max96724", 0x48), + I2C_BOARD_INFO("d4xx-max96724", 0x6b), .platform_data = &max96724_pdata, }; #else @@ -3554,7 +3637,6 @@ static int ds5_board_setup(struct ds5 *state) * - IPU7 both standalone or aggregated links - * - IPU6 when standalone when aggregated */ - struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; if (strnlen(spdata->suffix, sizeof(spdata->suffix)) > 1) { snprintf(serdes_suffix, sizeof(serdes_suffix), "%s", spdata->suffix); } else if (state->aggregated) { @@ -3602,8 +3684,10 @@ static int ds5_board_setup(struct ds5 *state) state->g_ctx.sdev_def, state->g_ctx.sdev_reg); //0x42, 0x44, 0x62, 0x64 state->g_ctx.ser_reg = pdata->subdev_info[0].ser_alias; - dev_info(dev, "serializer: i2c-%d@0x%x\n", - state->ser_i2c->adapter->nr, state->g_ctx.ser_reg); + dev_info(dev, "serializer %s: i2c-%d@0x%x\n", + serdes_suffix, + state->ser_i2c->adapter->nr, + state->g_ctx.ser_reg); if (err < 0) { dev_err(dev, "serializer reg not found\n"); @@ -3612,54 +3696,40 @@ static int ds5_board_setup(struct ds5 *state) state->ser_dev = &state->ser_i2c->dev; - dev_info(dev, "deserializer: i2c-%d@0x%x\n", - state->dser_i2c->adapter->nr, state->dser_i2c->addr); + dev_info(dev, "deserializer %c: i2c-%d@0x%x\n", + suffix, + state->dser_i2c->adapter->nr, + state->dser_i2c->addr); state->dser_dev = &state->dser_i2c->dev; /* populate g_ctx from pdata */ -#ifdef CONFIG_VIDEO_D4XX_MAX96724 - state->g_ctx.dst_csi_port = (suffix == 'a') ? GMSL_CSI_PORT_C : GMSL_CSI_PORT_B; - state->g_ctx.csi_mode = GMSL_CSI_2X4_MODE; //GMSL_CSI_4X2_MODE; +#if defined(CONFIG_VIDEO_D4XX_MAX96724) + state->g_ctx.dst_csi_port = (max96724_pdata.csi_out == GMSL_CSI_PORT_A && + max96724_pdata.csi_mode == GMSL_CSI_2X4_MODE) ? + GMSL_CSI_PORT_B : max96724_pdata.csi_out; +#elif defined(CONFIG_VIDEO_D4XX_MAX96712) + state->g_ctx.dst_csi_port = (max96724_pdata.csi_mode == GMSL_CSI_4X2_MODE) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B; #else state->g_ctx.dst_csi_port = GMSL_CSI_PORT_A; - state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; #endif + state->g_ctx.src_csi_port = GMSL_CSI_PORT_B; + state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 - /* Derive Deser CSI link mapping */ - switch (serdes_suffix[0]) { - case 'b': - state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; - break; - case 'c': - state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_C; - break; - case 'd': - state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_D; - break; - default: - state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; - break; - }; -#else +#if !defined(CONFIG_VIDEO_D4XX_MAX96724) && !defined(CONFIG_VIDEO_D4XX_MAX96712) state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; -#endif if (state->aggregated) { // dual aggregated-link fallback dev_info(dev, "configure GMSL port B\n"); state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; } else { dev_info(dev, "configure GMSL port A\n"); } +#endif state->g_ctx.st_vc = 0; state->g_ctx.dst_vc = 0; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 - state->g_ctx.num_csi_lanes = (state->g_ctx.csi_mode == GMSL_CSI_4X2_MODE) ? 2 : 4; -#else state->g_ctx.num_csi_lanes = 2; -#endif state->g_ctx.s_dev = dev; for (i = 0; i < MAX_DEV_NUM; i++) { @@ -3667,7 +3737,8 @@ static int ds5_board_setup(struct ds5 *state) serdes_inited[i] = state; return 0; } else if (serdes_inited[i]->ser_dev == state->ser_dev) { - return -ENOTSUPP; + dev_warn(dev, "same max9295 i2c device (%p) already listed\n",state->ser_dev); + return 0; } } err = -EINVAL; @@ -3707,7 +3778,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) mutex_lock(&serdes_lock__); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) max96724_power_off(state->dser_dev); /* For now no separate power on required for serializer device */ max96724_power_on(state->dser_dev); @@ -3719,7 +3790,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) dev_dbg(dev, "Setup SERDES addressing and control pipeline\n"); /* setup serdes addressing and control pipeline */ -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) err = max96724_setup_link(state->dser_dev, &state->client->dev); #else err = max9296_setup_link(state->dser_dev, &state->client->dev); @@ -3737,7 +3808,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) err= -ENOTSUPP; } -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) des_err = max96724_setup_control(state->dser_dev, &state->client->dev); #else des_err = max9296_setup_control(state->dser_dev, &state->client->dev); @@ -3754,7 +3825,7 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) } #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,3,2, 3,2,1,0}; module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" @@ -3773,9 +3844,13 @@ static short serdes_bus[4] = {5, 5, 5, 5}; static short serdes_bus[4] = {2, 2, 4, 4}; #endif module_param_array(serdes_bus, ushort, NULL, 0444); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) MODULE_PARM_DESC(serdes_bus, "d4xx-max96724 deserializer i2c bus\n" - "\t\tserdes_bus=2,2,4,4"); + "\t\tserdes_bus=0,0,1,1"); +#elif defined(CONFIG_VIDEO_D4XX_MAX96712) +MODULE_PARM_DESC(serdes_bus, "d4xx-max96712 deserializer i2c bus\n" + "\t\tserdes_bus=0,0,0,0"); + #else MODULE_PARM_DESC(serdes_bus, "d4xx-max9296 deserializer i2c bus\n" "\t\tserdes_bus=2,2,4,4"); @@ -3788,14 +3863,35 @@ static unsigned short des_addr[4] = {0x48, 0x4a, 0x68, 0x6c}; static unsigned short des_addr[4] = {0x48, 0x4a, 0x48, 0x4a}; #endif module_param_array(des_addr, ushort, NULL, 0444); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) MODULE_PARM_DESC(des_addr, "d4xx-max96724 deserializer i2c address\n" "\t\tdes_addr=0x27,0x27,0x27,0x27"); + +static short des_csi[4] = {0, 2, 0, 2}; +module_param_array(des_csi, ushort, NULL, 0444); +MODULE_PARM_DESC(des_csi, "d4xx-max96724 deserializer csi output\n" + "\t\tdes_csi=0,2,0,2"); +#elif defined(CONFIG_VIDEO_D4XX_MAX96712) +MODULE_PARM_DESC(des_addr, "d4xx-max96712 deserializer i2c address\n" + "\t\tdes_addr=0x27,0x27,0x6b,0x6b"); #else MODULE_PARM_DESC(des_addr, "d4xx-max9296 deserializer i2c address\n" "\t\tdes_addr=0x48,0x4a,0x48,0x4a"); #endif +#if defined(CONFIG_VIDEO_D4XX_MAX96724) +static unsigned short ds5_serdes_csi_output(int c_bus, int c_addr) +{ + int i = 0; + for (i = 0; i < 4; i++) + if (serdes_bus[i] == c_bus && + des_addr[i] == c_addr && + des_csi[i] <= GMSL_CSI_PORT_D) + return des_csi[i]; + + return GMSL_CSI_PORT_A; +} +#endif static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) { @@ -3805,7 +3901,7 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) for (i = 0; i < 4; i++) { if (c_bus == serdes_bus[i]) { c->addr = des_addr[i]; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) dev_info(&c->dev, "Set max96724@%d-0x%x Link reset\n", c_bus, c->addr); #else @@ -3869,6 +3965,7 @@ static int ds5_serdes_setup(struct ds5 *state) dev_err(&c->dev, "board setup failed\n"); return ret; } + dev_info(&c->dev, "gmsl board ds5 setup\n"); /* Pair sensor to serializer dev */ ret = max9295_sdev_pair(state->ser_dev, &state->g_ctx); @@ -3876,9 +3973,10 @@ static int ds5_serdes_setup(struct ds5 *state) dev_err(&c->dev, "gmsl serializer pairing failed\n"); return ret; } + dev_info(&c->dev, "gmsl serdes paired \n"); /* Register sensor to deserializer dev */ -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) ret = max96724_sdev_register(state->dser_dev, &state->g_ctx); #else ret = max9296_sdev_register(state->dser_dev, &state->g_ctx); @@ -3910,7 +4008,7 @@ static int ds5_serdes_setup(struct ds5 *state) return ret; } -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) ret = max96724_init_settings(state->dser_dev); #else ret = max9296_init_settings(state->dser_dev); @@ -4508,13 +4606,6 @@ static int _ds5_mux_set_routing(struct v4l2_subdev *sd, unsigned int i; struct v4l2_mbus_framefmt *ffmt; - static const struct v4l2_mbus_framefmt format = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - }; - - ffmt = &format; /* * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until @@ -4579,7 +4670,6 @@ static int _ds5_mux_set_routing(struct v4l2_subdev *sd, } stream_configs->configs[i].fmt = *ffmt; - state->pad_to_vc[sink_pad] = sink_stream; dev_dbg(sd->dev, "%s:%d: stream_configs->configs[%u] pad/stream=%u/%u format: code %x, res %ux%u\n", __func__, __LINE__, i, sink_pad, sink_stream, @@ -4808,6 +4898,7 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, sd->name, fmt->pad, remote_sd->name, remote_pad->index, ret); return 0; } + #endif return ret; } @@ -5124,7 +5215,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) // set manually, need to configure vc in pdata state->g_ctx.dst_vc = vc_id; #endif -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) sensor->pipe_id = max96724_get_available_pipe_id(state->dser_dev, (int)state->g_ctx.dst_vc); @@ -5174,9 +5265,9 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) i * DS5_START_POLL_TIME); } #ifdef CONFIG_VIDEO_D4XX_SERDES -#ifdef CONFIG_VIDEO_D4XX_MAX96724 - max9295_check_status(state->ser_dev); +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) max96724_check_status(state->dser_dev); + max9295_check_status(state->ser_dev); #endif #endif } else { // off @@ -5190,7 +5281,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) if (state->is_y8 && state->ir.sensor.config.format->data_type == GMSL_CSI_DT_RGB_888) { -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) max96724_reset_oneshot(state->dser_dev); #else max9296_reset_oneshot(state->dser_dev); @@ -5208,7 +5299,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) } #endif if (!streaming) { -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) dev_warn(&state->client->dev, "max96724_reset_oneshot\n"); max96724_reset_oneshot(state->dser_dev); #else @@ -5217,7 +5308,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) #endif } #endif -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) if (max96724_release_pipe(state->dser_dev, sensor->pipe_id) < 0) #else if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) @@ -5245,7 +5336,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) restore_s_state: #ifdef CONFIG_VIDEO_D4XX_SERDES if (on && sensor->pipe_id >= 0) { -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) if (max96724_release_pipe(state->dser_dev, sensor->pipe_id) < 0) #else if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) @@ -5295,8 +5386,8 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, unsigned int i; struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); struct v4l2_subdev_route *route; - struct ds5_sensor *sensor = NULL; int ret = -EINVAL; + unsigned int num_active_routes = 0; if (NULL == sd) return ret; @@ -5309,6 +5400,8 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, struct v4l2_subdev_state *v4l2_state = v4l2_subdev_lock_and_get_active_state(sd); + desc->num_entries = 0; //initialize + for_each_active_route(&v4l2_state->routing, route) { if (route->source_pad != pad) continue; @@ -5319,7 +5412,21 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, goto out_unlock; } - // Get sensor based on sink pad from routing + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + + if (num_active_routes >= V4L2_FRAME_DESC_ENTRY_MAX - 2) { + ret = -EINVAL; + dev_err(sd->dev, "Too many routes %u per-CSI ports (max=%u)!", + num_active_routes, + V4L2_FRAME_DESC_ENTRY_MAX); + goto out_unlock; + } + + /* Get sensor based on sink pad from routing + */ + struct ds5_sensor *sensor = NULL; switch (route->sink_pad) { case DS5_MUX_PAD_DEPTH: sensor = &state->depth.sensor; @@ -5342,6 +5449,9 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, struct ds5_format *mf = sensor->config.format; + if (state->pad_to_substream[route->sink_pad] != route->sink_stream) + continue; + dev_dbg(sd->dev, "sensor %s stream:%u pad:%u size:%dx%d code::0x%x vc:%u " \ "mbus:0x%x dt:0x%x\n", @@ -5350,23 +5460,18 @@ static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, state->pad_to_vc[route->sink_pad], mf->mbus_code, mf->data_type); + // for Sensor desc->entry[desc->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; desc->entry[desc->num_entries].stream = route->sink_stream; desc->entry[desc->num_entries].pixelcode = mf->mbus_code; desc->entry[desc->num_entries].length = 0; - desc->entry[desc->num_entries].bus.csi2.vc = route->sink_stream; + desc->entry[desc->num_entries].bus.csi2.vc = state->pad_to_vc[route->sink_pad]; desc->entry[desc->num_entries].bus.csi2.dt = mf->data_type; desc->num_entries++; } - /* for Metadata - source_desc.entry[desc_entries].flags = 0; - source_desc.entry[desc_entries].pixelcode = MEDIA_BUS_FMT_FIXED; - source_desc.entry[desc_entries].pixelcode = 0x12; - source_desc.entry[desc_entries].length = 68; - desc_entries++; - */ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + ret = 0; out_unlock: @@ -5403,7 +5508,7 @@ static int ds5_mux_enable_streams(struct v4l2_subdev *sd, ret = ds5_mux_s_stream(sd, 1); - return ret; + return 0; } static int ds5_mux_disable_streams(struct v4l2_subdev *sd, @@ -5532,13 +5637,15 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *v4l2_state) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); + unsigned int i; int ret = 0; if (state->routing_initialized) return 0; state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 + +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) /* Derive Deser CSI link mapping */ switch (state->g_ctx.serdes_csi_link) { case GMSL_SERDES_CSI_LINK_A: @@ -5563,7 +5670,7 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[12]; state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[13]; state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[14]; - state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[16]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[15]; break; default: state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; @@ -5586,38 +5693,113 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, } #endif + /* match for IPU/IPU7 CSI2 video capture pads */ + for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) + state->pad_to_substream[i] = -1; + + /* single d4xx + * 0, vc 0, depth A + * 1, vc 1, depth C or vc 0, depth A meta data + * 2, vc 1, RGB A + * 3, vc 2, RGB C or vc 1, RGB A meta data + * 4, vc 2, IR A + * 5, vc 3, IMU A + */ + /* aggregated d4xx + * 6, vc 2, depth B + * 7, vc 3, depth D or vc 2, depth B meta data + * 8, vc 3, RGB B + * 9, vc 3, RGB D or vc 3, RGB B meta data + * 10, vc 0, IR B + * 11, vc 1, IMU B + * 12, vc 0, IR C + * 13, vc 1, IMU C + * 14, vc 0, IR D + * 15, vc 1, IMU D + */ + +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + /* Derive Deser CSI link mapping */ + switch (state->g_ctx.serdes_csi_link) { + case GMSL_SERDES_CSI_LINK_A: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + break; + case GMSL_SERDES_CSI_LINK_B: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 6; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 8; + state->pad_to_substream[DS5_MUX_PAD_IR] = 10; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; + break; + case GMSL_SERDES_CSI_LINK_C: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 1; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 3; + state->pad_to_substream[DS5_MUX_PAD_IR] = 12; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 13; + break; + case GMSL_SERDES_CSI_LINK_D: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 7; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 9; + state->pad_to_substream[DS5_MUX_PAD_IR] = 14; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 15; + break; + default: + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + break; + } +#else + if (!state->aggregated) { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + } + else { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 6; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 8; + state->pad_to_substream[DS5_MUX_PAD_IR] = 10; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; + } +#endif + + //TODO: add RGB and depth meta data (fmt: 1x68 MEDIA_BUS_FMT_SGRBG8_1X8) as V4L2_SUBDEV_ROUTE_FL_INACTIVE routes struct v4l2_subdev_route ds5_mux_routes[] = { //DEPTH { .sink_pad = DS5_MUX_PAD_DEPTH, - .sink_stream = state->pad_to_vc[DS5_MUX_PAD_DEPTH], + .sink_stream = state->pad_to_substream[DS5_MUX_PAD_DEPTH], .source_pad = 0, - .source_stream = state->pad_to_vc[DS5_MUX_PAD_DEPTH], + .source_stream = state->pad_to_substream[DS5_MUX_PAD_DEPTH], .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, //RGB { .sink_pad = DS5_MUX_PAD_RGB, - .sink_stream = state->pad_to_vc[DS5_MUX_PAD_RGB], + .sink_stream = state->pad_to_substream[DS5_MUX_PAD_RGB], .source_pad = 0, - .source_stream = state->pad_to_vc[DS5_MUX_PAD_RGB], + .source_stream = state->pad_to_substream[DS5_MUX_PAD_RGB], .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, //IR { .sink_pad = DS5_MUX_PAD_IR, - .sink_stream = state->pad_to_vc[DS5_MUX_PAD_IR], + .sink_stream = state->pad_to_substream[DS5_MUX_PAD_IR], .source_pad = 0, - .source_stream = state->pad_to_vc[DS5_MUX_PAD_IR], - .flags = 0, + .source_stream = state->pad_to_substream[DS5_MUX_PAD_IR], + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, //IMU { .sink_pad = DS5_MUX_PAD_IMU, - .sink_stream = state->pad_to_vc[DS5_MUX_PAD_IMU], + .sink_stream = state->pad_to_substream[DS5_MUX_PAD_IMU], .source_pad = 0, - .source_stream = state->pad_to_vc[DS5_MUX_PAD_IMU], - .flags = 0, + .source_stream = state->pad_to_substream[DS5_MUX_PAD_IMU], + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; @@ -5627,12 +5809,18 @@ static int ds5_mux_init_state(struct v4l2_subdev *sd, }; ret = _ds5_mux_set_routing(sd, v4l2_state, &ds5_mux_routing); - if (ret < 0) + if (ret < 0) { dev_warn(sd->dev, "%s():%d: %s initial .num_routes=%u ignored (sd_state %p)\n", __func__, __LINE__, sd->name, ds5_mux_routing.num_routes, v4l2_state); - else - dev_dbg(sd->dev, "%s():%d: %s initial .num_routes=%u applied (sd_state %p)\n", - __func__, __LINE__, sd->name, ds5_mux_routing.num_routes, v4l2_state); + return 0; + } + + dev_dbg(sd->dev, "%s(): %s initial .num_routes=%u applied (sd_state %p)\n", + __func__, sd->name, ds5_mux_routing.num_routes, v4l2_state); + for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) + if (state->pad_to_substream[i] >= 0) + dev_dbg(sd->dev, "pad[%d]->substream=%d\n", + i, state->pad_to_substream[i]); return 0; } #endif @@ -5750,7 +5938,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) entity->ops = &ds5_mux_entity_ops; #endif entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; - entity->function = MEDIA_ENT_F_CAM_SENSOR; + entity->function = MEDIA_ENT_F_VID_MUX; dev_info(&c->dev, "%s():%d init media_entity %s, type:%x, func:%x\n", @@ -6504,7 +6692,7 @@ static void ds5_substream_init(struct ds5 *state) unsigned int mipi_csi2_type; s64 *sub_stream = NULL; state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) /* Derive Deser CSI link mapping */ switch (state->g_ctx.serdes_csi_link) { case GMSL_SERDES_CSI_LINK_A: @@ -6529,7 +6717,7 @@ static void ds5_substream_init(struct ds5 *state) state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[12]; state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[13]; state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[14]; - state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[16]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[15]; break; default: state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; @@ -6555,7 +6743,7 @@ static void ds5_substream_init(struct ds5 *state) for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) state->pad_to_substream[i] = -1; /* match for IPU6 CSI2 BE SOC video capture pads */ -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) /* Derive Deser CSI link mapping */ switch (state->g_ctx.serdes_csi_link) { case GMSL_SERDES_CSI_LINK_A: @@ -7134,22 +7322,29 @@ static void ds5_remove(struct i2c_client *c) serdes_inited[i] = NULL; mutex_lock(&serdes_lock__); - ret = max9295_reset_control(state->ser_dev); +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) + ret = max96724_switch_link_channel(state->dser_dev); + ret |= max9295_reset_control(state->ser_dev); if (ret) dev_warn(&c->dev, - "failed in 9295 reset control\n"); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 + "failed in 9295 reset control\n"); + if (state->dser_i2c) { - dev_info(&c->dev, "ignore 96724 reset control, already remove for bus %d\n", c_bus); + dev_info(&c->dev, "ignore 967xx reset control, already remove for bus %d\n", c_bus); } else { - dev_info(&c->dev, "trigger 96724 reset control on bus %d\n", c_bus); + dev_info(&c->dev, "trigger 967xx reset control on bus %d\n", c_bus); ret = max96724_reset_control(state->dser_dev, state->g_ctx.s_dev); if (ret) dev_warn(&c->dev, - "failed in 96724 reset control\n"); + "failed in 967xx reset control\n"); } #else + ret = max9295_reset_control(state->ser_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9295 reset control\n"); + if (state->dser_i2c) { dev_info(&c->dev, "ignore 9296 reset control, already remove for bus %d\n", c_bus); } else { @@ -7167,9 +7362,9 @@ static void ds5_remove(struct i2c_client *c) dev_warn(&c->dev, "failed to unpair sdev\n"); if (state->dser_i2c) { - dev_info(&c->dev, "ignore 9296 unregister sdev, already remove for bus %d\n", c_bus); + dev_info(&c->dev, "ignore unregister sdev, already remove for bus %d\n", c_bus); } else { -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) dev_info(&c->dev, "unregister 96724 sdev on bus %d\n", c_bus); ret = max96724_sdev_unregister(state->dser_dev, state->g_ctx.s_dev); @@ -7182,7 +7377,7 @@ static void ds5_remove(struct i2c_client *c) dev_warn(&c->dev, "failed to sdev unregister sdev\n"); -#ifdef CONFIG_VIDEO_D4XX_MAX96724 +#if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) max96724_power_off(state->dser_dev); #else max9296_power_off(state->dser_dev); diff --git a/include/media/i2c/d4xx-max96724.h b/include/media/i2c/d4xx-max96724.h index dc39d140..71c757ae 100644 --- a/include/media/i2c/d4xx-max96724.h +++ b/include/media/i2c/d4xx-max96724.h @@ -31,9 +31,11 @@ struct max96724_pdata { bool splitter_enabled; __u32 max_src; + __u32 src_link; __u32 csi_mode; __u32 d4xx_hacks; __u32 csi_phy; + __u32 csi_out; }; int max96724_get_available_pipe_id(struct device *dev, int vc_id); @@ -130,6 +132,7 @@ void max96724_power_off(struct device *dev); int max96724_init_settings(struct device *dev); int max96724_set_mfp(struct device *dev, int pin, int val); int max96724_check_status(struct device *dev); +int max96724_switch_link_channel(struct device *dev); /** @} */ #endif /* __MAX96724_H__ */ From 7c6cd7d958795f17a1c037f01e9c1b5ee8acd194 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Wed, 20 Mar 2024 14:18:46 +0200 Subject: [PATCH 42/72] d4xx-compat: hwmc return success on ioctl LRS expects success on hwmc ioctl call. [Error] /tmp/sandbox/src/librealsense-exception.h:52 - xioctl(VIDIOC_G_EXT_CTRLS) failed Last Error: Bad message Signed-off-by: Dmitry Perchanov --- drivers/media/i2c/d4xx-compat/d4xx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 9b009b80..2cf8ac16 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -2990,8 +2990,10 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ret = 0; break; } - if (ret) + if (ret) { + ret = 0; // LRS doesn't expect to get errors with HWMC break; + } /* This is needed for librealsense, to align there code with UVC, * last word is length - 4 bytes header length */ dataLen -= 4; From 7d8657f920b27708f9d5c68332f040a51da702df Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Mon, 1 Apr 2024 14:36:16 +0200 Subject: [PATCH 43/72] d4xx-compat: return hwmc error code to lrs sdk. --- drivers/media/i2c/d4xx-compat/d4xx.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 2cf8ac16..21eaf757 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -2188,6 +2188,8 @@ enum DS5_HWMC_ERR { DS5_HWMC_ERR_CMD = -1, DS5_HWMC_ERR_PARAM = -6, DS5_HWMC_ERR_NODATA = -21, + DS5_HWMC_ERR_UNKNOWN = -64, + DS5_HWMC_ERR_LAST, }; static int ds5_get_hwmc_status(struct ds5 *state) @@ -2207,26 +2209,11 @@ static int ds5_get_hwmc_status(struct ds5 *state) if (ret || status != DS5_HWMC_STATUS_OK) { if (status == DS5_HWMC_STATUS_ERR) { ds5_raw_read(state, DS5_HWMC_DATA, &errorCode, sizeof(errorCode)); - switch(errorCode) { - case (DS5_HWMC_ERR_CMD): - case (DS5_HWMC_ERR_PARAM): - ret = -EBADMSG; - break; - case (DS5_HWMC_ERR_NODATA): - ret = -ENODATA; - break; - - default: - dev_dbg(&state->client->dev, - "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", - __func__, ret, status, errorCode); - ret = -EBADMSG; - break; - } + return errorCode; } } if (!ret && (status != DS5_HWMC_STATUS_OK)) - ret = -EBUSY; + ret = DS5_HWMC_ERR_LAST; return ret; } @@ -2246,7 +2233,13 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data, dev_dbg(&state->client->dev, "%s(): HWMC status not clear, ret: %d\n", __func__, ret); + if (ret != DS5_HWMC_ERR_LAST) { + int *p = (int *)data; + *p = ret; + return 0; + } else { return ret; + } } ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, From 34f0f38f8576923fa5256fc45876b7801637f9ca Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Mon, 20 May 2024 16:16:39 +0300 Subject: [PATCH 44/72] d4xx-compat: add recovery string to dfu device --- drivers/media/i2c/d4xx-compat/d4xx.c | 63 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 21eaf757..69685228 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -476,7 +476,7 @@ struct ds5_dfu_dev { enum dfu_state dfu_state_flag; unsigned char *dfu_msg; u16 msg_write_once; - unsigned char init_v4l_f; + // unsigned char init_v4l_f; u32 bus_clk_rate; }; @@ -6320,18 +6320,31 @@ static ssize_t ds5_dfu_device_read(struct file *flip, { struct ds5 *state = flip->private_data; u16 fw_ver, fw_build; - char msg[32]; + char msg[64]; int ret = 0; + struct __fw_status f = {0}; if (mutex_lock_interruptible(&state->lock)) return -ERESTARTSYS; - ret |= ds5_read(state, DS5_FW_VERSION, &fw_ver); - ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); - if (ret < 0) - goto e_dfu_read_failed; - snprintf(msg, sizeof(msg) ,"DFU info: \tver: %d.%d.%d.%d\n", + if (state->dfu_dev.dfu_state_flag == DS5_DFU_RECOVERY) { + ds5_write_with_check(state, 0x500c, 0x00); + ret = ds5_dfu_wait_for_get_dfu_status(state, dfuIDLE); + ret = ds5_dfu_get_dev_info(state, &f); + if (ret < 0) + goto e_dfu_read_failed; + snprintf(msg, sizeof(msg) , + "DFU info: \trecovery: %02x%02x%02x%02x%02x%02x\n", + f.ivcamSerialNum[0], f.ivcamSerialNum[1], f.ivcamSerialNum[2], + f.ivcamSerialNum[3], f.ivcamSerialNum[4], f.ivcamSerialNum[5] ); + } else { + ret |= ds5_read(state, DS5_FW_VERSION, &fw_ver); + ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); + if (ret < 0) + goto e_dfu_read_failed; + snprintf(msg, sizeof(msg) ,"DFU info: \tver: %d.%d.%d.%d\n", (fw_ver >> 8) & 0xff, fw_ver & 0xff, (fw_build >> 8) & 0xff, fw_build & 0xff); + } if (copy_to_user(buffer, msg, strlen(msg))) ret = -EFAULT; @@ -6373,7 +6386,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, goto dfu_write_error; } state->dfu_dev.dfu_state_flag = DS5_DFU_IN_PROGRESS; - state->dfu_dev.init_v4l_f = 1; + // state->dfu_dev.init_v4l_f = 1; /* fallthrough - procceed to download */ __attribute__((__fallthrough__)); case DS5_DFU_IN_PROGRESS: { @@ -6411,7 +6424,8 @@ static ssize_t ds5_dfu_device_write(struct file *flip, goto dfu_write_error; state->dfu_dev.dfu_state_flag = DS5_DFU_DONE; } - dev_notice(&state->client->dev, "%s(): DFU block (%d) bytes written\n", + if (len) + dev_notice(&state->client->dev, "%s(): DFU block (%d) bytes written\n", __func__, (int)len); break; } @@ -6540,10 +6554,10 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) state->dfu_dev.device_open_count--; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; - if (state->dfu_dev.dfu_state_flag == DS5_DFU_DONE - && state->dfu_dev.init_v4l_f) - ds5_v4l_init(state->client, state); - state->dfu_dev.init_v4l_f = 0; + // if (state->dfu_dev.dfu_state_flag == DS5_DFU_DONE + // && state->dfu_dev.init_v4l_f) + // ds5_v4l_init(state->client, state); + // state->dfu_dev.init_v4l_f = 0; if (state->dfu_dev.dfu_msg) devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg); state->dfu_dev.dfu_msg = NULL; @@ -7202,6 +7216,8 @@ static int ds5_probe(struct i2c_client *c) if (rec_state == 0x201) { dev_info(&c->dev, "%s(): D4XX recovery state\n", __func__); state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY; + /* Override I2C drvdata */ + i2c_set_clientdata(c, state); return 0; } @@ -7216,8 +7232,6 @@ static int ds5_probe(struct i2c_client *c) ret = ds5_v4l_init(c, state); if (ret < 0) goto e_chardev; - /* Override I2C drvdata */ - /* i2c_set_clientdata(c, state); */ /* regulators? clocks? * devm_regulator_bulk_get(&c->dev, DS5_N_SUPPLIES, state->supplies); @@ -7297,12 +7311,17 @@ static int ds5_remove(struct i2c_client *c) static void ds5_remove(struct i2c_client *c) #endif { - struct ds5 *state = container_of(i2c_get_clientdata(c), struct ds5, mux.sd.subdev); - #ifdef CONFIG_VIDEO_D4XX_SERDES int i, ret; int c_bus = c->adapter->nr; bool graceful_fallback = false; +#endif + struct ds5 *state = container_of(i2c_get_clientdata(c), struct ds5, mux.sd.subdev); + if (state && !state->mux.sd.subdev.v4l2_dev) { + state = i2c_get_clientdata(c); + } + +#ifdef CONFIG_VIDEO_D4XX_SERDES for (i = 0; i < MAX_DEV_NUM; i++) { if (serdes_inited[i] && state->dser_i2c && c_bus == serdes_inited[i]->dser_st.bus_nr @@ -7408,14 +7427,16 @@ static void ds5_remove(struct i2c_client *c) regulator_disable(state->vcc); // gpio_free(state->pwdn_gpio); - if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) { + if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY && \ + state->mux.sd.subdev.v4l2_dev) { #ifdef CONFIG_SYSFS sysfs_remove_group(&c->dev.kobj, &ds5_attr_group); #endif ds5_mux_remove(state); - if (state->is_depth) { - ds5_chrdev_remove(state); - } + } + + if (state->is_depth && state->dfu_dev.ds5_class) { + ds5_chrdev_remove(state); } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) From 7e858f5064504470c6defef202185f9b456738a9 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Mon, 27 May 2024 11:06:34 +0300 Subject: [PATCH 45/72] d4xx-compat: improve commentary --- drivers/media/i2c/d4xx-compat/d4xx.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 69685228..189e2848 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -476,7 +476,7 @@ struct ds5_dfu_dev { enum dfu_state dfu_state_flag; unsigned char *dfu_msg; u16 msg_write_once; - // unsigned char init_v4l_f; + // unsigned char init_v4l_f; // need refactoring u32 bus_clk_rate; }; @@ -6327,8 +6327,10 @@ static ssize_t ds5_dfu_device_read(struct file *flip, if (mutex_lock_interruptible(&state->lock)) return -ERESTARTSYS; if (state->dfu_dev.dfu_state_flag == DS5_DFU_RECOVERY) { - ds5_write_with_check(state, 0x500c, 0x00); - ret = ds5_dfu_wait_for_get_dfu_status(state, dfuIDLE); + /* Read device info in recovery mode */ + ret = ds5_dfu_detach(state); + if (ret < 0) + goto e_dfu_read_failed; ret = ds5_dfu_get_dev_info(state, &f); if (ret < 0) goto e_dfu_read_failed; @@ -6386,6 +6388,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, goto dfu_write_error; } state->dfu_dev.dfu_state_flag = DS5_DFU_IN_PROGRESS; + /* find a better way to reinitialize driver from recovery to operational */ // state->dfu_dev.init_v4l_f = 1; /* fallthrough - procceed to download */ __attribute__((__fallthrough__)); @@ -6554,6 +6557,8 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) state->dfu_dev.device_open_count--; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; + /* We disable this section as it has no effect when device in operational + mode and has not enough effect when device in recovery mode */ // if (state->dfu_dev.dfu_state_flag == DS5_DFU_DONE // && state->dfu_dev.init_v4l_f) // ds5_v4l_init(state->client, state); @@ -7216,7 +7221,7 @@ static int ds5_probe(struct i2c_client *c) if (rec_state == 0x201) { dev_info(&c->dev, "%s(): D4XX recovery state\n", __func__); state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY; - /* Override I2C drvdata */ + /* Override I2C drvdata with state for use in remove function */ i2c_set_clientdata(c, state); return 0; } From dbb356aa7b1ccb226ae5788f036bea584a5f588b Mon Sep 17 00:00:00 2001 From: Arun-Prasad-V Date: Tue, 2 Jul 2024 08:31:19 +0530 Subject: [PATCH 46/72] d4xx-compat: updated AE_SETPOINT_GET control's range --- drivers/media/i2c/d4xx-compat/d4xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 189e2848..42282f92 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3164,6 +3164,8 @@ static const struct v4l2_ctrl_config ds5_ctrl_ae_setpoint_get = { .name = "ae setpoint get", .type = V4L2_CTRL_TYPE_INTEGER, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .min = 0, + .max = 4095, .step = 1, }; From f38abb9a7af9924eba3a18ef497966aae7468ccd Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Tue, 13 Aug 2024 16:20:48 +0300 Subject: [PATCH 47/72] d4xx-compat: Locking on open dfu device --- drivers/media/i2c/d4xx-compat/d4xx.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 42282f92..35a69ccb 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -6463,16 +6463,20 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) state->client->adapter); #endif #endif - if (state->dfu_dev.device_open_count) + mutex_lock(&state->lock); + if (state->dfu_dev.device_open_count) { + mutex_unlock(&state->lock); return -EBUSY; + } state->dfu_dev.device_open_count++; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_OPEN; state->dfu_dev.dfu_msg = devm_kzalloc(&state->client->dev, DFU_BLOCK_SIZE, GFP_KERNEL); - if (!state->dfu_dev.dfu_msg) + if (!state->dfu_dev.dfu_msg) { + mutex_unlock(&state->lock); return -ENOMEM; - + } file->private_data = state; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) @@ -6480,9 +6484,10 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) while (parent && i2c_parent_is_i2c_adapter(parent)) parent = i2c_parent_is_i2c_adapter(state->client->adapter); - if (!parent) + if (!parent) { + mutex_unlock(&state->lock); return 0; - + } dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk = %d, set %d\n", __func__, i2c_adapter_id(parent), @@ -6493,6 +6498,7 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) i2c_set_adapter_bus_clk_rate(parent, DFU_I2C_BUS_CLK_RATE); #endif #endif + mutex_unlock(&state->lock); return 0; }; @@ -6556,6 +6562,7 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) #endif #endif int ret = 0, retry = 10; + mutex_lock(&state->lock); state->dfu_dev.device_open_count--; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; @@ -6573,8 +6580,10 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) /* get i2c controller and restore bus clock rate */ while (parent && i2c_parent_is_i2c_adapter(parent)) parent = i2c_parent_is_i2c_adapter(state->client->adapter); - if (!parent) + if (!parent) { + mutex_unlock(&state->lock); return 0; + } dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk %d, restore to %d\n", __func__, i2c_adapter_id(parent), i2c_get_adapter_bus_clk_rate(parent), @@ -6592,9 +6601,11 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) if (ret) { dev_warn(&state->client->dev, "%s(): no communication with d4xx\n", __func__); + mutex_unlock(&state->lock); return ret; } ret = ds5_read(state, DS5_FW_BUILD, &state->fw_build); + mutex_unlock(&state->lock); return ret; }; From cbd07745bc19750af8548f600ce008f614591e2e Mon Sep 17 00:00:00 2001 From: Arun-Prasad-V Date: Wed, 30 Oct 2024 20:14:48 +0530 Subject: [PATCH 48/72] d4xx-compat: Locking on mux open and close --- drivers/media/i2c/d4xx-compat/d4xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 35a69ccb..b081fceb 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -3290,9 +3290,16 @@ static int ds5_mux_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct ds5 *state = v4l2_get_subdevdata(sd); dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + + mutex_lock(&state->lock); if (state->dfu_dev.dfu_state_flag) + { + mutex_unlock(&state->lock); return -EBUSY; + } + state->dfu_dev.device_open_count++; + mutex_unlock(&state->lock); return 0; }; @@ -3302,7 +3309,9 @@ static int ds5_mux_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct ds5 *state = v4l2_get_subdevdata(sd); dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + mutex_lock(&state->lock); state->dfu_dev.device_open_count--; + mutex_unlock(&state->lock); return 0; }; From 6a1b6e50398a2a7562358f49f097a3f8ef972e8c Mon Sep 17 00:00:00 2001 From: Arun-Prasad-V Date: Mon, 4 Nov 2024 13:15:25 +0530 Subject: [PATCH 49/72] d4xx-compat: support for dynamic change of i2c bus_clk_rate --- drivers/media/i2c/d4xx-compat/d4xx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index b081fceb..50c9e6c3 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -6467,10 +6467,8 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); #ifdef CONFIG_TEGRA_CAMERA_PLATFORM -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( state->client->adapter); -#endif #endif mutex_lock(&state->lock); if (state->dfu_dev.device_open_count) { @@ -6488,7 +6486,6 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) } file->private_data = state; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) /* get i2c controller and set dfu bus clock rate */ while (parent && i2c_parent_is_i2c_adapter(parent)) parent = i2c_parent_is_i2c_adapter(state->client->adapter); @@ -6505,7 +6502,6 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) state->dfu_dev.bus_clk_rate = i2c_get_adapter_bus_clk_rate(parent); i2c_set_adapter_bus_clk_rate(parent, DFU_I2C_BUS_CLK_RATE); -#endif #endif mutex_unlock(&state->lock); return 0; @@ -6565,10 +6561,8 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) { struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); #ifdef CONFIG_TEGRA_CAMERA_PLATFORM -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( state->client->adapter); -#endif #endif int ret = 0, retry = 10; mutex_lock(&state->lock); @@ -6585,7 +6579,6 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg); state->dfu_dev.dfu_msg = NULL; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) /* get i2c controller and restore bus clock rate */ while (parent && i2c_parent_is_i2c_adapter(parent)) parent = i2c_parent_is_i2c_adapter(state->client->adapter); @@ -6599,7 +6592,6 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) state->dfu_dev.bus_clk_rate); i2c_set_adapter_bus_clk_rate(parent, state->dfu_dev.bus_clk_rate); -#endif #endif /* Verify communication */ do { From d27916f62870761ca3bc3807a08775745b3ad769 Mon Sep 17 00:00:00 2001 From: Remi Bettan Date: Thu, 20 Feb 2025 10:50:43 +0200 Subject: [PATCH 50/72] d4xx-compat: cherry-pick realsense_mipi commit 38fc023 --- drivers/media/i2c/d4xx-compat/d4xx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 50c9e6c3..d292916c 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1630,7 +1630,7 @@ static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, int pipe_id, u32 vc_id) { int ret = 0; - dev_dbg(&state->client->dev, + dev_warn(&state->client->dev, "set pipe %d, data_type1: 0x%x, \ data_type2: 0x%x, vc_id: %u\n", pipe_id, data_type1, data_type2, vc_id); @@ -1712,8 +1712,10 @@ static int ds5_configure(struct ds5 *state) #ifdef CONFIG_VIDEO_D4XX_SERDES data_type1 = sensor->config.format->data_type; - data_type2 = state->is_y8 ? 0x00 : md_fmt; - + data_type2 = state->is_imu ? 0x00 : md_fmt; + /* do not have metadata for y12i */ + if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) + data_type2 = 0; vc_id = state->g_ctx.dst_vc; ret = ds5_setup_pipeline(state, data_type1, data_type2, sensor->pipe_id, From 9da9fa440265d80a1f3f29947104c3a425e3cc0d Mon Sep 17 00:00:00 2001 From: ejgoldik <148076610+ejgoldik@users.noreply.github.com> Date: Wed, 16 Jul 2025 13:53:59 +0300 Subject: [PATCH 51/72] d4xx-compat: Add error for hwmc response len 0 + replace hard-coded addresses (#285) * Added err and dmesg in ds5_get_hwmc if response length is 0 * Added comments and replaced hard coded HWMC addresses --- drivers/media/i2c/d4xx-compat/d4xx.c | 58 +++++++++++++++------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index d292916c..6b474aa5 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -2245,18 +2245,24 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data, } ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, - &tmp_len, sizeof(tmp_len)); + &tmp_len, sizeof(tmp_len)); /* Read response length */ if (ret) return -EBADMSG; if (tmp_len > cmdDataLen) return -ENOBUFS; + if (tmp_len == 0) { + dev_err(&state->client->dev, + "%s(): HWMC response length is 0\n", __func__); + return -ENODATA; + } + dev_dbg(&state->client->dev, "%s(): HWMC read len: %d, lrs_len: %d\n", __func__, tmp_len, tmp_len - 4); - ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, tmp_len); + ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, tmp_len); /* Read response data */ if (dataLen) *dataLen = tmp_len; return ret; @@ -2272,8 +2278,8 @@ static int ds5_send_hwmc(struct ds5 *state, __func__, cmd->header, cmd->magic_word, cmd->opcode, cmdLen, cmd->param1, cmd->param2, cmd->param3, cmd->param4); - ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, cmdLen); - + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, cmdLen); /* Write command data */ + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ return 0; @@ -2286,7 +2292,7 @@ static int ds5_set_calibration_data(struct ds5 *state, int retries = 10; u16 status = 2; - ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, length); + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, length); /* Write command data */ ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ do { @@ -2691,12 +2697,12 @@ static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, memcpy(cmd, &get_calib_data, sizeof(get_calib_data)); cmd->param1 = id; - ds5_raw_write_with_check(state, 0x4900, cmd, sizeof(struct hwm_cmd)); - ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, sizeof(struct hwm_cmd)); /* Write command data */ + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ do { if (retries != 3) msleep_range(10); - ret = ds5_read(state, 0x4904, &status); + ret = ds5_read(state, DS5_HWMC_STATUS, &status); } while (ret && retries-- && status != 0); if (ret || status != 0) { @@ -2708,11 +2714,11 @@ static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, } // get table length from fw - ret = regmap_raw_read(state->regmap, 0x4908, - &table_length, sizeof(table_length)); + ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, + &table_length, sizeof(table_length)); /* Read response length */ // read table - ds5_raw_read_with_check(state, 0x4900, cmd->Data, table_length); + ds5_raw_read_with_check(state, DS5_HWMC_DATA, cmd->Data, table_length); /* Read table data */ // first 4 bytes are opcode HWM, not part of calibration table memcpy(table, cmd->Data + 4, length); @@ -2729,13 +2735,13 @@ static int ds5_gvd(struct ds5 *state, unsigned char *data) u8 retries = 3; memcpy(&cmd, &gvd, sizeof(gvd)); - ds5_raw_write_with_check(state, 0x4900, &cmd, sizeof(cmd)); - ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + ds5_raw_write_with_check(state, DS5_HWMC_DATA, &cmd, sizeof(cmd)); /* Write command data */ + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ do { if (retries != 3) msleep_range(10); - ret = ds5_read(state, 0x4904, &status); + ret = ds5_read(state, DS5_HWMC_STATUS, &status); } while (ret && retries-- && status != 0); if (ret || status != 0) { @@ -2745,8 +2751,8 @@ static int ds5_gvd(struct ds5 *state, unsigned char *data) return status; } - ret = regmap_raw_read(state->regmap, 0x4908, &length, sizeof(length)); - ds5_raw_read_with_check(state, 0x4900, data, length); + ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &length, sizeof(length)); /* Read response length */ + ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, length); /* Read response data */ return ret; } @@ -2865,19 +2871,19 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) // 1. prepare and send command // 2. send command // 3. execute command - // 4. wait for ccompletion - ret = regmap_raw_write(state->regmap, 0x4900, + // 4. wait for completion + ret = regmap_raw_write(state->regmap, DS5_HWMC_DATA, /* Write command data */ log_prepare, sizeof(log_prepare)); if (ret < 0) return ret; - ret = regmap_raw_write(state->regmap, 0x490C, - &execute_cmd, sizeof(execute_cmd)); + ret = regmap_raw_write(state->regmap, DS5_HWMC_EXEC, + &execute_cmd, sizeof(execute_cmd)); /* execute cmd */ if (ret < 0) return ret; for (i = 0; i < DS5_MAX_LOG_POLL; i++) { - ret = regmap_raw_read(state->regmap, 0x4904, + ret = regmap_raw_read(state->regmap, DS5_HWMC_STATUS, &data, sizeof(data)); dev_dbg(&state->client->dev, "%s(): log ready 0x%x\n", __func__, data); @@ -2891,7 +2897,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) // if (i == DS5_MAX_LOG_POLL) // return -ETIMEDOUT; - ret = regmap_raw_read(state->regmap, 0x4908, &data, sizeof(data)); + ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &data, sizeof(data)); /* Read response length */ dev_dbg(&state->client->dev, "%s(): log size 0x%x\n", __func__, data); if (ret < 0) return ret; @@ -2899,7 +2905,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return 0; if (data > 1024) return -ENOBUFS; - ret = regmap_raw_read(state->regmap, 0x4900, + ret = regmap_raw_read(state->regmap, DS5_HWMC_DATA, ctrl->p_new.p_u8, data); break; case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_GET: @@ -6227,9 +6233,9 @@ static int ds5_dfu_switch_to_dfu(struct ds5 *state) int i = DS5_START_MAX_COUNT; u16 status; - ds5_raw_write_with_check(state, 0x4900, - &cmd_switch_to_dfu, sizeof(cmd_switch_to_dfu)); - ds5_write_with_check(state, 0x490c, 0x01); /* execute cmd */ + ds5_raw_write_with_check(state, DS5_HWMC_DATA, + &cmd_switch_to_dfu, sizeof(cmd_switch_to_dfu)); /* Write command data */ + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ /*Wait for DFU fw to boot*/ do { msleep_range(DS5_START_POLL_TIME*10); From 2d65f577b600d99a4c48114fc9f1926b024248d9 Mon Sep 17 00:00:00 2001 From: ejgoldik <148076610+ejgoldik@users.noreply.github.com> Date: Wed, 27 Aug 2025 08:55:48 +0300 Subject: [PATCH 52/72] d4xx-compat: add D41x support (#266) * Add D41x support * Fix D41x resolutions --- drivers/media/i2c/d4xx-compat/d4xx.c | 224 ++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 6b474aa5..395ad192 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -76,6 +76,7 @@ #define DS5_FW_VERSION 0x030C #define DS5_FW_BUILD 0x030E #define DS5_DEVICE_TYPE 0x0310 +#define DS5_DEVICE_TYPE_D41X 7 #define DS5_DEVICE_TYPE_D45X 6 #define DS5_DEVICE_TYPE_D43X 5 #define DS5_DEVICE_TYPE_D46X 4 @@ -802,15 +803,65 @@ static const u16 ds5_framerate_30 = 30; static const u16 ds5_framerate_15_30[] = {15, 30}; static const u16 ds5_framerate_25 = 25; +static const u16 ds5_framerate_15_25[] = {15, 25}; static const u16 ds5_depth_framerate_to_30[] = {5, 15, 30}; static const u16 ds5_framerate_to_30[] = {5, 10, 15, 30}; static const u16 ds5_framerate_to_60[] = {5, 15, 30, 60}; static const u16 ds5_framerate_to_90[] = {5, 15, 30, 60, 90}; +static const u16 ds5_41x_depth_framerate_to_30[] = {6, 15, 30}; +static const u16 ds5_41x_framerate_to_30[] = {6, 15, 30}; +static const u16 ds5_41x_framerate_to_60_no_15[] = {6, 30, 60}; +static const u16 ds5_41x_framerate_to_60[] = {6, 15, 30, 60}; +static const u16 ds5_41x_framerate_to_90[] = {6, 15, 30, 60, 90}; static const u16 ds5_framerate_100[] = {100}; static const u16 ds5_framerate_90[] = {90}; static const u16 ds5_imu_framerates[] = {50, 100, 200, 400}; +static const struct ds5_resolution d41x_depth_sizes[] = { + { + .width = 1280, + .height = 720, + .framerates = ds5_41x_depth_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_41x_depth_framerate_to_30), + }, { + .width = 848, + .height = 480, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 848, + .height = 100, + .framerates = ds5_framerate_100, + .n_framerates = ARRAY_SIZE(ds5_framerate_100), + }, { + .width = 640, + .height = 480, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 640, + .height = 360, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 480, + .height = 270, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 424, + .height = 240, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 256, + .height = 144, + .framerates = ds5_framerate_90, + .n_framerates = ARRAY_SIZE(ds5_framerate_90), + }, +}; + static const struct ds5_resolution d43x_depth_sizes[] = { { .width = 1280, @@ -903,6 +954,104 @@ static const struct ds5_resolution y8_sizes[] = { } }; +static const struct ds5_resolution y8_41x_sizes[] = { + { + .width = 1920, + .height = 1080, + .framerates = ds5_framerate_15_25, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_25), + }, { + .width = 1280, + .height = 720, + .framerates = ds5_41x_depth_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_41x_depth_framerate_to_30), + }, { + .width = 960, + .height = 540, + .framerates = ds5_framerate_15_25, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_25), + }, { + .width = 848, + .height = 480, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 848, + .height = 100, + .framerates = ds5_framerate_100, + .n_framerates = ARRAY_SIZE(ds5_framerate_100), + }, { + .width = 640, + .height = 480, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 640, + .height = 360, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 480, + .height = 270, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + }, { + .width = 424, + .height = 240, + .framerates = ds5_41x_framerate_to_90, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_90), + } +}; + +static const struct ds5_resolution ds5_41x_rgb_sizes[] = { + { + .width = 1920, + .height = 1080, + .framerates = ds5_41x_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_30), + }, { + .width = 1280, + .height = 720, + .framerates = ds5_41x_framerate_to_30, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_30), + }, { + .width = 960, + .height = 540, + .framerates = ds5_41x_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60), + }, { + .width = 848, + .height = 480, + .framerates = ds5_41x_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60), + }, { + .width = 640, + .height = 480, + .framerates = ds5_41x_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60), + }, { + .width = 640, + .height = 360, + .framerates = ds5_41x_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60), + }, { + .width = 424, + .height = 240, + .framerates = ds5_41x_framerate_to_60, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60), + }, { + .width = 320, + .height = 240, + .framerates = ds5_41x_framerate_to_60_no_15, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60_no_15), + }, { + .width = 320, + .height = 180, + .framerates = ds5_41x_framerate_to_60_no_15, + .n_framerates = ARRAY_SIZE(ds5_41x_framerate_to_60_no_15), + }, +}; + static const struct ds5_resolution ds5_rlt_rgb_sizes[] = { { .width = 1280, @@ -978,6 +1127,15 @@ static const struct ds5_resolution ds5_size_w10 = { .n_framerates = 1, }; +static const struct ds5_resolution d41x_calibration_sizes[] = { + { + .width = 1920, + .height = 1080, + .framerates = ds5_framerate_15_25, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_25), + }, +}; + static const struct ds5_resolution d43x_calibration_sizes[] = { { .width = 1280, @@ -1015,6 +1173,26 @@ static const struct ds5_resolution ds5_size_imu_extended[] = { }, }; +static const struct ds5_format ds5_depth_formats_d41x[] = { + { + // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. + .data_type = GMSL_CSI_DT_YUV422_8, /* Z16 */ + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .n_resolutions = ARRAY_SIZE(d41x_depth_sizes), + .resolutions = d41x_depth_sizes, + }, { + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(d41x_depth_sizes), + .resolutions = d41x_depth_sizes, + }, { + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .n_resolutions = ARRAY_SIZE(d41x_calibration_sizes), + .resolutions = d41x_calibration_sizes, + }, +}; + static const struct ds5_format ds5_depth_formats_d43x[] = { { // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. @@ -1078,6 +1256,33 @@ static const struct ds5_format ds5_y_formats_ds5u[] = { }, }; +static const struct ds5_format ds5_y_formats_41x[] = { + { + /* First format: default */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(y8_41x_sizes), + .resolutions = y8_41x_sizes, + }, { + .data_type = GMSL_CSI_DT_YUV422_8, /* Y8I */ + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .n_resolutions = ARRAY_SIZE(y8_41x_sizes), + .resolutions = y8_41x_sizes, + }, { + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .n_resolutions = ARRAY_SIZE(d41x_calibration_sizes), + .resolutions = d41x_calibration_sizes, + }, +}; + +static const struct ds5_format ds5_41x_rgb_format = { + .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .n_resolutions = ARRAY_SIZE(ds5_41x_rgb_sizes), + .resolutions = ds5_41x_rgb_sizes, +}; + static const struct ds5_format ds5_rlt_rgb_format = { .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, @@ -6084,6 +6289,9 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor = &state->depth.sensor; switch (dev_type) { + case DS5_DEVICE_TYPE_D41X: + sensor->formats = ds5_depth_formats_d41x; + break; case DS5_DEVICE_TYPE_D43X: case DS5_DEVICE_TYPE_D45X: sensor->formats = ds5_depth_formats_d43x; @@ -6098,8 +6306,15 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->mux_pad = DS5_MUX_PAD_DEPTH; sensor = &state->ir.sensor; - sensor->formats = state->variant->formats; - sensor->n_formats = state->variant->n_formats; + switch (dev_type) { + case DS5_DEVICE_TYPE_D41X: + sensor->formats = ds5_y_formats_41x; + sensor->n_formats = ARRAY_SIZE(ds5_y_formats_41x); + break; + default: + sensor->formats = state->variant->formats; + sensor->n_formats = state->variant->n_formats; + } sensor->mux_pad = DS5_MUX_PAD_IR; sensor = &state->rgb.sensor; @@ -6109,6 +6324,10 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = &ds5_onsemi_rgb_format; sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; break; + case DS5_DEVICE_TYPE_D41X: + sensor->formats = &ds5_41x_rgb_format; + sensor->n_formats = DS5_RLT_RGB_N_FORMATS; + break; case DS5_DEVICE_TYPE_D45X: sensor->formats = &ds5_rlt_rgb_format; sensor->n_formats = DS5_RLT_RGB_N_FORMATS; @@ -7128,6 +7347,7 @@ static int ds5_probe(struct i2c_client *c) mutex_init(&state->lock); state->client = c; + dev_warn(&c->dev, "Probing driver for D4xx\n"); #ifdef CONFIG_VIDEO_D4XX_SERDES state->dser_st.isolated = false; From 1d2766720c5da41ee2568322e5758eec45e574dc Mon Sep 17 00:00:00 2001 From: ejgoldik <148076610+ejgoldik@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:44:40 +0300 Subject: [PATCH 53/72] d4xx-compat: relax start stream TO (#295) * Relax Start stream TO * Use only ds5_[raw]_ wrapper and not regmap_raw_ directly --- drivers/media/i2c/d4xx-compat/d4xx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 395ad192..54d132b2 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -193,7 +193,7 @@ enum ds5_mux_pad { #define DFU_WAIT_RET_LEN 6 #define DS5_START_POLL_TIME 10 -#define DS5_START_MAX_TIME 1000 +#define DS5_START_MAX_TIME 2000 #define DS5_START_MAX_COUNT (DS5_START_MAX_TIME / DS5_START_POLL_TIME) /* DFU definition section */ @@ -2449,7 +2449,7 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data, } } - ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, + ret = ds5_raw_read(state, DS5_HWMC_RESP_LEN, &tmp_len, sizeof(tmp_len)); /* Read response length */ if (ret) return -EBADMSG; @@ -2919,7 +2919,7 @@ static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, } // get table length from fw - ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, + ret = ds5_raw_read(state, DS5_HWMC_RESP_LEN, &table_length, sizeof(table_length)); /* Read response length */ // read table @@ -2956,7 +2956,7 @@ static int ds5_gvd(struct ds5 *state, unsigned char *data) return status; } - ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &length, sizeof(length)); /* Read response length */ + ret = ds5_raw_read(state, DS5_HWMC_RESP_LEN, &length, sizeof(length)); /* Read response length */ ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, length); /* Read response data */ return ret; @@ -3077,18 +3077,18 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) // 2. send command // 3. execute command // 4. wait for completion - ret = regmap_raw_write(state->regmap, DS5_HWMC_DATA, /* Write command data */ + ret = ds5_raw_write(state, DS5_HWMC_DATA, /* Write command data */ log_prepare, sizeof(log_prepare)); if (ret < 0) return ret; - ret = regmap_raw_write(state->regmap, DS5_HWMC_EXEC, + ret = ds5_raw_write(state, DS5_HWMC_EXEC, &execute_cmd, sizeof(execute_cmd)); /* execute cmd */ if (ret < 0) return ret; for (i = 0; i < DS5_MAX_LOG_POLL; i++) { - ret = regmap_raw_read(state->regmap, DS5_HWMC_STATUS, + ret = ds5_raw_read(state, DS5_HWMC_STATUS, &data, sizeof(data)); dev_dbg(&state->client->dev, "%s(): log ready 0x%x\n", __func__, data); @@ -3102,7 +3102,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) // if (i == DS5_MAX_LOG_POLL) // return -ETIMEDOUT; - ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &data, sizeof(data)); /* Read response length */ + ret = ds5_raw_read(state, DS5_HWMC_RESP_LEN, &data, sizeof(data)); /* Read response length */ dev_dbg(&state->client->dev, "%s(): log size 0x%x\n", __func__, data); if (ret < 0) return ret; @@ -3110,7 +3110,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return 0; if (data > 1024) return -ENOBUFS; - ret = regmap_raw_read(state->regmap, DS5_HWMC_DATA, + ret = ds5_raw_read(state, DS5_HWMC_DATA, ctrl->p_new.p_u8, data); break; case DS5_CAMERA_DEPTH_CALIBRATION_TABLE_GET: From 9e09edbeb1add5dd33b94d7a66f552479b5db5f0 Mon Sep 17 00:00:00 2001 From: Yogev Modlin <117653169+ymodlin@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:16:28 +0300 Subject: [PATCH 54/72] d4xx-compat: update version to 1.0.1.31 (#303) --- drivers/media/i2c/d4xx-compat/d4xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 54d132b2..f6f4d987 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -7723,7 +7723,7 @@ static struct i2c_driver ds5_i2c_driver = { module_i2c_driver(ds5_i2c_driver); -MODULE_DESCRIPTION("Intel RealSense D4XX Camera Driver"); +MODULE_DESCRIPTION("RealSense D4XX Camera Driver"); MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Nael Masalha ,\n\ Alexander Gantman ,\n\ @@ -7732,7 +7732,7 @@ MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Qingwu Zhang ,\n\ Evgeni Raikhel ,\n\ Shikun Ding ,\n\ - Florent Pirou "); -MODULE_AUTHOR("Dmitry Perchanov "); + Florent Pirou ,\n\ + Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRIVER_VERSION_SUFFIX); From 0f2da3c7454dbc16f1ba918243388d87854c9b70 Mon Sep 17 00:00:00 2001 From: KONTRA <154245937+Kontra2B@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:56:41 +0100 Subject: [PATCH 55/72] d4xx-compat: Enabling Y12I mode for IR calibration stream (#313) --- drivers/media/i2c/d4xx-compat/d4xx.c | 46 ++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index f6f4d987..92281843 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -799,12 +799,9 @@ static const u16 ds5_framerates[] = {5, 30}; #define DS5_FRAMERATE_DEFAULT_IDX 1 static const u16 ds5_framerate_30 = 30; - -static const u16 ds5_framerate_15_30[] = {15, 30}; - static const u16 ds5_framerate_25 = 25; +static const u16 ds5_framerate_15_30[] = {15, 30}; static const u16 ds5_framerate_15_25[] = {15, 25}; - static const u16 ds5_depth_framerate_to_30[] = {5, 15, 30}; static const u16 ds5_framerate_to_30[] = {5, 10, 15, 30}; static const u16 ds5_framerate_to_60[] = {5, 15, 30, 60}; @@ -1145,6 +1142,15 @@ static const struct ds5_resolution d43x_calibration_sizes[] = { }, }; +static const struct ds5_resolution d45x_calibration_sizes[] = { + { + .width = 1280, + .height = 800, + .framerates = ds5_framerate_15_25, + .n_framerates = ARRAY_SIZE(ds5_framerate_15_25), + }, +}; + static const struct ds5_resolution d46x_calibration_sizes[] = { { .width = 1600, @@ -1269,13 +1275,33 @@ static const struct ds5_format ds5_y_formats_41x[] = { .n_resolutions = ARRAY_SIZE(y8_41x_sizes), .resolutions = y8_41x_sizes, }, { - .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ - .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ + .data_type = GMSL_CSI_DT_RGB_888, /* Y12I, 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, .n_resolutions = ARRAY_SIZE(d41x_calibration_sizes), .resolutions = d41x_calibration_sizes, }, }; +static const struct ds5_format ds5_y_formats_45x[] = { + { + /* First format: default */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .n_resolutions = ARRAY_SIZE(y8_sizes), + .resolutions = y8_sizes, + }, { + .data_type = GMSL_CSI_DT_YUV422_8, /* Y8I */ + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .n_resolutions = ARRAY_SIZE(y8_sizes), + .resolutions = y8_sizes, + }, { + .data_type = GMSL_CSI_DT_RGB_888, /* Y12I, 24-bit Calibration */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .n_resolutions = ARRAY_SIZE(d45x_calibration_sizes), + .resolutions = d45x_calibration_sizes, + }, +}; + static const struct ds5_format ds5_41x_rgb_format = { .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, @@ -1918,9 +1944,7 @@ static int ds5_configure(struct ds5 *state) #ifdef CONFIG_VIDEO_D4XX_SERDES data_type1 = sensor->config.format->data_type; data_type2 = state->is_imu ? 0x00 : md_fmt; - /* do not have metadata for y12i */ - if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) - data_type2 = 0; + vc_id = state->g_ctx.dst_vc; ret = ds5_setup_pipeline(state, data_type1, data_type2, sensor->pipe_id, @@ -6311,6 +6335,10 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = ds5_y_formats_41x; sensor->n_formats = ARRAY_SIZE(ds5_y_formats_41x); break; + case DS5_DEVICE_TYPE_D45X: + sensor->formats = ds5_y_formats_45x; + sensor->n_formats = ARRAY_SIZE(ds5_y_formats_45x); + break; default: sensor->formats = state->variant->formats; sensor->n_formats = state->variant->n_formats; From 60620a199b316088c6fc62a0f1906a4d23b818db Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 5 May 2026 04:52:43 -0700 Subject: [PATCH 56/72] d4xx-compat: use common serdes ipu acpi headers Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx.c | 22 +++++++++++----------- include/media/i2c/d4xx_pdata.h | 28 ---------------------------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 92281843..e1f392cb 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -33,7 +33,6 @@ #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT #include -#include #else #include #endif @@ -45,6 +44,7 @@ #include #ifdef CONFIG_VIDEO_D4XX_SERDES +#include #include #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) #include @@ -3763,13 +3763,13 @@ static unsigned short ds5_serdes_csi_output(int c_bus, int c_addr); static int ds5_board_setup(struct ds5 *state) { struct device *dev = &state->client->dev; - struct d4xx_pdata *pdata = dev->platform_data; + struct serdes_platform_data *pdata = dev->platform_data; struct i2c_adapter *adapter = state->client->adapter; int bus = adapter->nr; int err = 0; int i; char suffix = pdata->suffix; - struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; + struct serdes_subdev_info *spdata = &pdata->subdev_info[0]; char serdes_suffix[5]; /* suffix string for subdevs */ #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) @@ -4500,7 +4500,7 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, struct media_pad *pad = &sensor->pad; dev_t *dev_num = &state->client->dev.devt; #ifndef CONFIG_OF - struct d4xx_pdata *dpdata = c->dev.platform_data; + struct serdes_platform_data *dpdata = c->dev.platform_data; char suffix = dpdata->suffix; #endif v4l2_i2c_subdev_init(sd, c, ops); @@ -4511,7 +4511,7 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, sd->grp_id = *dev_num; v4l2_set_subdevdata(sd, state); #ifndef CONFIG_OF - struct d4xx_subdev_info *spdata = &dpdata->subdev_info[0]; + struct serdes_subdev_info *spdata = &dpdata->subdev_info[0]; /* * suffix syntaxe for multiple D457 connected to 1 Deser : * - IPU7 - @@ -5364,7 +5364,7 @@ static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, #ifndef CONFIG_VIDEO_D4XX_SERDES static int d4xx_reset_oneshot(struct ds5 *state) { - struct d4xx_pdata *dpdata = state->client->dev.platform_data; + struct serdes_platform_data *dpdata = state->client->dev.platform_data; struct i2c_board_info *deser = dpdata->deser_board_info; int s_addr = state->client->addr; @@ -6148,7 +6148,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) unsigned int i; int ret; #ifndef CONFIG_OF - struct d4xx_pdata *dpdata = c->dev.platform_data; + struct serdes_platform_data *dpdata = c->dev.platform_data; char suffix = dpdata->suffix; #endif v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); @@ -6161,7 +6161,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) snprintf(sd->name, sizeof(sd->name), "DS5 mux %d-%04x", i2c_adapter_id(c->adapter), c->addr); #else - struct d4xx_subdev_info *spdata = &dpdata->subdev_info[0]; + struct serdes_subdev_info *spdata = &dpdata->subdev_info[0]; /* * suffix syntaxe for multiple D457 connected to 1 Deser : * - IPU7 - @@ -6881,7 +6881,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) struct cdev *ds5_cdev = &state->dfu_dev.ds5_cdev; struct class **ds5_class = &state->dfu_dev.ds5_class; #ifndef CONFIG_OF - struct d4xx_pdata *pdata = c->dev.platform_data; + struct serdes_platform_data *pdata = c->dev.platform_data; char suffix = pdata->suffix; #endif struct device *chr_dev; @@ -6920,7 +6920,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) *dev_num = MKDEV(MAJOR(*dev_num), MINOR(*dev_num)); /* Create a device node for this device. */ #ifndef CONFIG_OF - struct d4xx_subdev_info *spdata = &pdata->subdev_info[0]; + struct serdes_subdev_info *spdata = &pdata->subdev_info[0]; /* * suffix syntaxe for multiple D457 connected to 1 Deser : * - IPU7/IPU6 upstream both standalone or aggregated links - @@ -7282,7 +7282,7 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) int i; u8 val; int ret; - struct d4xx_pdata *dpdata = c->dev.platform_data; + struct serdes_platform_data *dpdata = c->dev.platform_data; unsigned short ser_alias; unsigned short sensor_alias; diff --git a/include/media/i2c/d4xx_pdata.h b/include/media/i2c/d4xx_pdata.h index da235846..e0578bc4 100644 --- a/include/media/i2c/d4xx_pdata.h +++ b/include/media/i2c/d4xx_pdata.h @@ -4,8 +4,6 @@ #ifndef D457_H #define D457_H -#include - #define D457_NAME "d4xx" #define MAX9296_NAME "MAX9296" #define MAX96724_NAME "MAX96724" @@ -17,30 +15,4 @@ GMSL_SERDES_CSI_LINK_B ? "GMSL B" : \ GMSL_SERDES_CSI_LINK_C ? "GMSL C" : "GMSL D" -struct d4xx_subdev_info { - struct i2c_board_info board_info; - int i2c_adapter_id; - unsigned short rx_port; - unsigned short phy_i2c_addr; - unsigned short ser_alias; - char suffix[5]; /* suffix for subdevs */ - unsigned short ser_phys_addr; - unsigned int sensor_dt; - int aggregated_link; -}; - -struct d4xx_pdata { - unsigned int subdev_num; - struct d4xx_subdev_info *subdev_info; - unsigned int reset_gpio; - unsigned int FPD_gpio; - char suffix; - unsigned int link_freq_mbps; - enum v4l2_mbus_type bus_type; - unsigned int deser_nlanes; - unsigned int ser_nlanes; - unsigned int des_port; - char ser_name[I2C_NAME_SIZE]; - struct i2c_board_info *deser_board_info; -}; #endif From dcd52767bf13af1a8efd3c8afff5c092b172cc16 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Sat, 7 Mar 2026 02:49:11 -0700 Subject: [PATCH 57/72] d4xx-max96724: drop max96712 legacy-mode, set and reset src-link pipe aggregation * d4xx-max96724: control pipe set and reset per deser link source * d4xx-max96724: enable csi output right after set pipes * d4xx-max96724: disable max96712 legacy-mode compile option * d4xx-max96724: reduce i2c channel-control scope, add src-link pipe aggregation Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 247 ++++++++++++------ drivers/media/i2c/d4xx-compat/d4xx.c | 44 ++-- include/media/i2c/d4xx-max96724.h | 8 +- 3 files changed, 197 insertions(+), 102 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c index 45b5de4e..382beee0 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -86,6 +86,7 @@ #define MAX96724_MAP_CON_SYNC_PIPE_1_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(1) #define MAX96724_MAP_CON_SYNC_PIPE_2_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(2) #define MAX96724_MAP_CON_SYNC_PIPE_3_FIELD MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(3) +#define MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD BIT(7) #define MAX96724_MAP_STATUS(pipe) (MAX96724_MIPI_TX(pipe) + 0x02) #define MAX96724_MAP_EN_L(pipe) (MAX96724_MIPI_TX(pipe) + 0x0B) @@ -525,6 +526,7 @@ struct pipe_ctx { u32 dst_csi_ctrl; u32 st_count; u32 st_id_sel; + u32 src_link; }; struct max96724 { @@ -665,13 +667,13 @@ static void max96724_pipes_reset(struct max96724 *priv) */ struct pipe_ctx pipe_defaults[] = { {MAX96724_PIPE_X, GMSL_CSI_DT_RAW_12, - MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID, GMSL_SERDES_CSI_LINK_A}, {MAX96724_PIPE_Y, GMSL_CSI_DT_RAW_12, - MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID, GMSL_SERDES_CSI_LINK_A}, {MAX96724_PIPE_Z, GMSL_CSI_DT_EMBED, - MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID}, + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID, GMSL_SERDES_CSI_LINK_A}, {MAX96724_PIPE_U, GMSL_CSI_DT_EMBED, - MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID} + MAX96724_CSI_CTRL_ID_0, 0, MAX96724_INVAL_ST_ID, GMSL_SERDES_CSI_LINK_A} }; /* @@ -770,23 +772,23 @@ EXPORT_SYMBOL(max96724_power_off); * Without this delay, deserializer tries to lock video before sensor * outputs stable video data, resulting in VIDEO_LOCK failure (0x0108=0x02) */ -int max96724_check_status(struct device *dev) +int max96724_check_status(struct device *dev, u32 src_link) { struct max96724 *priv = dev_get_drvdata(dev); int err = 0; - u8 src_port = max96724_link_to_port(priv->src_link); + u8 src_port = max96724_link_to_port(src_link); mutex_lock(&priv->lock); + /* Enable MIPI TX controller and enable PHY CLK cycle dev_info(dev, "%s: Wake-up %s CSI %u %s mode %s (num_lanes:x%u)\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); - /* Enable MIPI TX controller and enable PHY CLK cycle err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); @@ -797,7 +799,6 @@ int max96724_check_status(struct device *dev) priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); } - */ // enable CSI out link after initialization complet err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, MAX96724_CSI_OUT_EN_FIELD, @@ -807,13 +808,13 @@ int max96724_check_status(struct device *dev) __func__, err); } + */ /* Turn on MIPI PHY continuous clock mode (matches script line 520) * Reference script does this AFTER CSI enable (0x040B = 0x02) * This enables continuous clock on MIPI CSI-2 interface * 0x84 = 0b10000100 * Bit[7] = 1: Enable continuous clock mode - */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_PHY0, MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U)); @@ -826,14 +827,14 @@ int max96724_check_status(struct device *dev) else dev_dbg(dev, "%s: %s CSI %u %s %s (num_lanes:x%u) continuous clock mode enabled\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); + */ /* Turn on ALL link channels - */ err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) @@ -843,13 +844,14 @@ int max96724_check_status(struct device *dev) dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", __func__, err); + */ /* Re-Check GMSL link status after initial configuration */ { unsigned int link_status = 0; max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + __func__, max96724_get_link_name(src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), (link_status & 0x01) ? "VID_LOCK " : "", (link_status & 0x02) ? "CONFIG_DETECT " : "", (link_status & 0x04) ? "VIDEO_DETECT " : "", @@ -861,7 +863,7 @@ int max96724_check_status(struct device *dev) unsigned int pll_status = 0; max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pll_status, + __func__, max96724_get_link_name(src_link), pll_status, (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", @@ -869,53 +871,53 @@ int max96724_check_status(struct device *dev) unsigned int vid_status = 0; max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + __func__, max96724_get_link_name(src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); unsigned int pipe_0_status = 0, pipe_1_status = 0, pipe_2_status = 0, pipe_3_status = 0; max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_0_MAP_ADDR, &pipe_0_status); dev_dbg(dev, "%s: %s Video TX CSI 0 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_0_status, + __func__, max96724_get_link_name(src_link), pipe_0_status, (pipe_0_status & 0x01) ? "SYNC_EN " : "", (pipe_0_status & 0x02) ? "in-SYNC " : "", (pipe_0_status & 0x04) ? "LOST_SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_1_MAP_ADDR, &pipe_1_status); dev_dbg(dev, "%s: %s Video TX CSI 1 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_1_status, + __func__, max96724_get_link_name(src_link), pipe_1_status, (pipe_1_status & 0x01) ? "EN-SYNC " : "", (pipe_1_status & 0x02) ? "IN-SYNC " : "", (pipe_1_status & 0x04) ? "LOST-SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_2_MAP_ADDR, &pipe_2_status); dev_dbg(dev, "%s: %s Video TX CSI 2 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_2_status, + __func__, max96724_get_link_name(src_link), pipe_2_status, (pipe_2_status & 0x01) ? "SYNC_EN " : "", (pipe_2_status & 0x02) ? "in-SYNC " : "", (pipe_2_status & 0x04) ? "LOST_SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_3_MAP_ADDR, &pipe_3_status); dev_dbg(dev, "%s: %s Video TX CSI 3 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_3_status, + __func__, max96724_get_link_name(src_link), pipe_3_status, (pipe_3_status & 0x01) ? "SYNC_EN " : "", (pipe_3_status & 0x02) ? "in-SYNC " : "", (pipe_3_status & 0x04) ? "LOST_SYNC " : ""); unsigned int pipe_de_status = 0, pipe_hs_status = 0, pipe_vs_status = 0; max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); dev_dbg(dev, "%s: %s Video Pipeline DE status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + __func__, max96724_get_link_name(src_link), pipe_de_status, (pipe_de_status & 0x01) ? "DE_DET_0 " : "", (pipe_de_status & 0x02) ? "DE_DET_1 " : "", (pipe_de_status & 0x04) ? "DE_DET_2 " : "", (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); max96724_read_reg(dev, MAX96724_PIPE_HS_STATUS_ADDR, &pipe_hs_status); dev_dbg(dev, "%s: %s Video Pipeline HS status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_hs_status, + __func__, max96724_get_link_name(src_link), pipe_hs_status, (pipe_hs_status & 0x01) ? "HS_DET_0 " : "", (pipe_hs_status & 0x02) ? "HS_DET_1 " : "", (pipe_hs_status & 0x04) ? "HS_DET_2 " : "", (pipe_hs_status & 0x08) ? "HS_DET_3 " : ""); max96724_read_reg(dev, MAX96724_PIPE_VS_STATUS_ADDR, &pipe_vs_status); dev_dbg(dev, "%s: %s Video Pipeline VS status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_vs_status, + __func__, max96724_get_link_name(src_link), pipe_vs_status, (pipe_vs_status & 0x01) ? "VS_DET_0 " : "", (pipe_vs_status & 0x02) ? "VS_DET_1 " : "", (pipe_vs_status & 0x04) ? "VS_DET_2 " : "", @@ -955,6 +957,9 @@ int max96724_switch_link_channel(struct device *dev) err); mutex_unlock(&priv->lock); + + msleep(50); // delay to settle link + return err; } EXPORT_SYMBOL(max96724_switch_link_channel); @@ -1186,6 +1191,10 @@ int max96724_setup_link(struct device *dev, struct device *s_dev) | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3), 3U) | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2), 2U) | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), 3U)); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_MAP_CON(0), MAX96724_MAP_CON_SYNC_PIPE_MASTER(0)); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_MAP_CON(1), MAX96724_MAP_CON_SYNC_PIPE_MASTER(1)); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_MAP_CON(2), MAX96724_MAP_CON_SYNC_PIPE_MASTER(2)); + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_MAP_CON(3), MAX96724_MAP_CON_SYNC_PIPE_MASTER(3)); err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_REG3_ADDR, 0xFF); err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_REG4_ADDR, 0x0F); if (err) { @@ -1704,7 +1713,7 @@ static int max96724_set_registers(struct device *dev, struct reg_pair *map, return err; } -int max96724_get_available_pipe_id(struct device *dev, int vc_id) +int max96724_get_available_pipe_id(struct device *dev, int vc_id, u32 src_link) { int i; int pipe_id = -ENOMEM; @@ -1715,13 +1724,13 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id) for (i = 0; i < MAX96724_MAX_PIPES; i++) { if (i == vc_id && !priv->pipe[i].st_count) { priv->pipe[i].st_count++; + priv->pipe[i].src_link = src_link; pipe_id = i; break; } } /* Turn on ALL link channels - */ err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) @@ -1732,6 +1741,9 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id) __func__, err); + msleep(50); // delay to settle link + */ + mutex_unlock(&priv->lock); return pipe_id; @@ -1753,19 +1765,36 @@ int max96724_release_pipe(struct device *dev, int pipe_id) } EXPORT_SYMBOL(max96724_release_pipe); -void max96724_reset_oneshot(struct device *dev) +void max96724_reset_oneshot(struct device *dev, u32 src_link) { struct max96724 *priv = dev_get_drvdata(dev); int err = 0; + unsigned int i, st_count; - u8 src_port = max96724_link_to_port(priv->src_link); + u8 src_port = max96724_link_to_port(src_link); + for (i = 0, st_count = 0; i < MAX96724_MAX_PIPES; i++) { + + if (!priv->pipe[i].st_count) + continue; + + if (src_link != priv->pipe[i].src_link) + continue; + + st_count++; + } + + if (st_count > 1) { + dev_warn(dev, "%s: ignore %s Link reset ctrl\n", + __func__, max96724_get_link_name(src_link)); + return; + } /* Re-Check GMSL link status after initial configuration */ { unsigned int link_status = 0; max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + __func__, max96724_get_link_name(src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), (link_status & 0x01) ? "VID_LOCK " : "", (link_status & 0x02) ? "CONFIG_DETECT " : "", (link_status & 0x04) ? "VIDEO_DETECT " : "", @@ -1777,7 +1806,7 @@ void max96724_reset_oneshot(struct device *dev) unsigned int pll_status = 0; max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pll_status, + __func__, max96724_get_link_name(src_link), pll_status, (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", @@ -1785,66 +1814,66 @@ void max96724_reset_oneshot(struct device *dev) unsigned int vid_status = 0; max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + __func__, max96724_get_link_name(src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); unsigned int pipe_0_status = 0, pipe_1_status = 0, pipe_2_status = 0, pipe_3_status = 0; max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_0_MAP_ADDR, &pipe_0_status); dev_dbg(dev, "%s: %s Video TX CSI 0 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_0_status, + __func__, max96724_get_link_name(src_link), pipe_0_status, (pipe_0_status & 0x01) ? "SYNC_EN " : "", (pipe_0_status & 0x02) ? "in-SYNC " : "", (pipe_0_status & 0x04) ? "LOST_SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_1_MAP_ADDR, &pipe_1_status); dev_dbg(dev, "%s: %s Video TX CSI 1 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_1_status, + __func__, max96724_get_link_name(src_link), pipe_1_status, (pipe_1_status & 0x01) ? "EN-SYNC " : "", (pipe_1_status & 0x02) ? "IN-SYNC " : "", (pipe_1_status & 0x04) ? "LOST-SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_2_MAP_ADDR, &pipe_2_status); dev_dbg(dev, "%s: %s Video TX CSI 2 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_2_status, + __func__, max96724_get_link_name(src_link), pipe_2_status, (pipe_2_status & 0x01) ? "SYNC_EN " : "", (pipe_2_status & 0x02) ? "in-SYNC " : "", (pipe_2_status & 0x04) ? "LOST_SYNC " : ""); max96724_read_reg(dev, MAX96724_PIPE_X_STATUS_3_MAP_ADDR, &pipe_3_status); dev_dbg(dev, "%s: %s Video TX CSI 3 status: 0x%02x (bit0-2: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_3_status, + __func__, max96724_get_link_name(src_link), pipe_3_status, (pipe_3_status & 0x01) ? "SYNC_EN " : "", (pipe_3_status & 0x02) ? "in-SYNC " : "", (pipe_3_status & 0x04) ? "LOST_SYNC " : ""); unsigned int pipe_de_status = 0, pipe_hs_status = 0, pipe_vs_status = 0; max96724_read_reg(dev, MAX96724_PIPE_DE_STATUS_ADDR, &pipe_de_status); dev_dbg(dev, "%s: %s Video Pipeline DE status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_de_status, + __func__, max96724_get_link_name(src_link), pipe_de_status, (pipe_de_status & 0x01) ? "DE_DET_0 " : "", (pipe_de_status & 0x02) ? "DE_DET_1 " : "", (pipe_de_status & 0x04) ? "DE_DET_2 " : "", (pipe_de_status & 0x08) ? "DE_DET_3 " : ""); max96724_read_reg(dev, MAX96724_PIPE_HS_STATUS_ADDR, &pipe_hs_status); dev_dbg(dev, "%s: %s Video Pipeline HS status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_hs_status, + __func__, max96724_get_link_name(src_link), pipe_hs_status, (pipe_hs_status & 0x01) ? "HS_DET_0 " : "", (pipe_hs_status & 0x02) ? "HS_DET_1 " : "", (pipe_hs_status & 0x04) ? "HS_DET_2 " : "", (pipe_hs_status & 0x08) ? "HS_DET_3 " : ""); max96724_read_reg(dev, MAX96724_PIPE_VS_STATUS_ADDR, &pipe_vs_status); dev_dbg(dev, "%s: %s Video Pipeline VS status: 0x%02x (bit0-3: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pipe_vs_status, + __func__, max96724_get_link_name(src_link), pipe_vs_status, (pipe_vs_status & 0x01) ? "VS_DET_0 " : "", (pipe_vs_status & 0x02) ? "VS_DET_1 " : "", (pipe_vs_status & 0x04) ? "VS_DET_2 " : "", (pipe_vs_status & 0x08) ? "VS_DET_3 " : ""); } - + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_RESET_CTRL_ADDR, MAX96724_RESET_CTRL_FIELD(src_port), MAX96724_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(src_port), 1U)); if (err) dev_err(dev, "%s: Failed to trigger %s link reset: %d\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), err); /* delay to settle link */ @@ -1857,11 +1886,10 @@ void max96724_reset_oneshot(struct device *dev) if (err) dev_err(dev, "%s: Failed to clear %s link reset: %d\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), err); /* Enable all channels - */ err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) @@ -1871,16 +1899,18 @@ void max96724_reset_oneshot(struct device *dev) dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", __func__, err); - + */ } EXPORT_SYMBOL(max96724_reset_oneshot); static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_type1, - u8 data_type2, u32 vc_id, u32 csi_id ) + u8 data_type2, u32 vc_id, u32 csi_id, u32 src_link) { int err = 0; struct max96724 *priv = dev_get_drvdata(dev); int i = 0, j = 0; + unsigned int st_count; + #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT u8 en_mapping_num = 0xFF; #else @@ -1892,11 +1922,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ u8 phy2_lanes_mapping = 0xF4; u8 dis_lim_heart = 0x0A; - u8 link_id = max96724_link_to_port(priv->src_link); + u8 link_id = max96724_link_to_port(src_link); u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; - u8 vc_id_3b_src = 0x0; - u8 vc_id_3b_dst = ((u8) vc_id) & 0x7; + u8 vc_id_3b_src = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_SRC_VC_FIELD, 0U); + u8 vc_id_3b_dst = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_DST_VC_FIELD, 0U); if (data_type2 == 0x0) #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT @@ -2105,14 +2135,14 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ map_pipe_control[i+2].addr += 0x10 * link_id; map_pipe_control[i+3].addr += 0x10 * link_id; - map_pipe_control[i].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[i+1].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[i+2].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[i+3].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[i].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[i+1].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[i+2].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[i+3].val = (vc_id_3b_src | vc_id_3b_dst); dev_info(dev, "%s: %s pipe %u set on EVERY d4xx dt/vc possible match\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), link_id); #else for (i = 0; i < 11; i++) { @@ -2144,14 +2174,14 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ map_pipe_control[15].addr += 0x10 * pipe_id; map_pipe_control[16].addr += 0x10 * pipe_id; - map_pipe_control[13].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[14].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[15].val = (vc_id_3b_src | vc_id_3b_dst) << 2; - map_pipe_control[16].val = (vc_id_3b_src | vc_id_3b_dst) << 2; + map_pipe_control[13].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[14].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[15].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[16].val = (vc_id_3b_src | vc_id_3b_dst); dev_info(dev, "%s: %s pipe %u set on vc_id=%u [vc_id_3bits: src:0x%x, dst:0x%x] \n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), pipe_id, vc_id, vc_id_3b_src, @@ -2197,14 +2227,20 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ * Bit[5:4] = 11: Pipe 3 uses internal Pipe Z * Bit[3:2] = : Pipe 2 from GMSL A, B, C or D link * Bit[1:0] = 10: Pipe 2 uses internal Pipe U - * + */ +#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + /* * REG 0x00F4: Enable/disable Video Pipes - * Bit[3:0] = : Enable Pipe 3, 2, 1 or 0 + * Bit[3:0] = : Enable Pipe X */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(0), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 0U)); +#else err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); - +#endif err |= max96724_set_registers(dev, map_pipe_select, ARRAY_SIZE(map_pipe_select)); @@ -2222,13 +2258,13 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), link_id) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), pipe_id)); + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), 0U)); if (err) dev_err(dev, "%s: Failed to select max96712 legacy mode: %d\n", __func__, err); else - dev_dbg(dev, "%s: mapped max96712 legacy mode pipe 0 to streams (X,Y,Z and/or U)\n", + dev_dbg(dev, "%s: mapped max96712 legacy mode ALL pipe to streams (Pipe X Only) mode\n", __func__); #elif defined(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) @@ -2293,19 +2329,51 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); - /* Configure per Pipe CSI and PHY SYNC - */ - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(pipe_id), + /* Configure per Pipe 0, 1, 2 and 3 MIPI interleaved-SYNC FCFS aggregation + * Aggregator A, B, C and D corresponds to GMSL A, B, C and D input src + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), MAX96724_MAP_CON_SYNC_PIPE_0_FIELD | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_FIELD, + | MAX96724_MAP_CON_SYNC_PIPE_FIELD + | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) - | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id)); + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); + */ + for (i = 0, st_count = 0; i < MAX96724_MAX_PIPES; i++) { + + if (!priv->pipe[i].st_count) + continue; + + if (src_link != priv->pipe[i].src_link) + continue; + + st_count++; + } + + if (st_count < 2) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), + MAX96724_MAP_CON_SYNC_PIPE_0_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_FIELD + | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, + MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); + else + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), + MAX96724_MAP_CON_SYNC_PIPE_FIELD, + MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id)); /* Configure per Pipe CSI and PHY type */ @@ -2318,9 +2386,19 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); +#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + /* + * REG 0x00F4: Enable/disable Video Pipes + * Bit[3:0] = : Enable Pipe X + */ + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(0), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U)); +#else err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 1U)); +#endif if (err) { dev_err(dev, "%s: Failed to configure pipe control %u %s %u lanes: %d\n", __func__, @@ -2444,7 +2522,7 @@ int max96724_init_settings(struct device *dev) i); err |= __max96724_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, - GMSL_CSI_DT_EMBED, i, priv->dst_link); + GMSL_CSI_DT_EMBED, i, priv->dst_link, priv->src_link); } if (err) { dev_err(dev, "%s: Failed to configure %s link to Video pipes : %d\n", @@ -2561,13 +2639,13 @@ int max96724_init_settings(struct device *dev) EXPORT_SYMBOL(max96724_init_settings); int max96724_set_pipe(struct device *dev, int pipe_id, - u8 data_type1, u8 data_type2, u32 vc_id) + u8 data_type1, u8 data_type2, u32 vc_id, u32 src_link) { struct max96724 *priv = dev_get_drvdata(dev); unsigned src_pipe = MAX96724_PIPE_X; unsigned rev; int err = 0; - u8 src_port = max96724_link_to_port(priv->src_link); + u8 src_port = max96724_link_to_port(src_link); if (pipe_id > (MAX96724_MAX_PIPES - 1)) { dev_info(dev, "%s, input pipe_id: %d exceed max96724 max pipes\n", @@ -2577,7 +2655,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, dev_dbg(dev, "%s: disabling %s pipe %u (vc_id %u) CSI %u %s mode %s (num_lanes:x%u)\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), pipe_id, vc_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", @@ -2588,13 +2666,12 @@ int max96724_set_pipe(struct device *dev, int pipe_id, dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), pipe_id, data_type1, data_type2, vc_id); - err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link); + err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link, src_link); /* Enable all channels - */ err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) @@ -2604,6 +2681,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", __func__, err); + */ /* CRITICAL: DO NOT enable CSI_OUT_EN (0x040b) here! * @@ -2618,15 +2696,16 @@ int max96724_set_pipe(struct device *dev, int pipe_id, * - CSI should only output when sensor has valid data ready * * We enable CSI in s_stream AFTER sensor stream-on succeeds. + */ dev_info(dev, "%s: Wake-up %s CSI %u %s mode %s (num_lanes:x%u)\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); - // Enable MIPI TX controller and enable PHY CLK cycle + /* Enable MIPI TX controller and enable PHY CLK cycle err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); @@ -2637,6 +2716,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); } + */ // enable CSI out link after initialization complet err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, @@ -2648,7 +2728,13 @@ int max96724_set_pipe(struct device *dev, int pipe_id, err); } - // Turn on MIPI PHY continuous clock mode + + /* Turn on MIPI PHY continuous clock mode (matches script line 520) + * Reference script does this AFTER CSI enable (0x040B = 0x02) + * This enables continuous clock on MIPI CSI-2 interface + * 0x84 = 0b10000100 + * Bit[7] = 1: Enable continuous clock mode + */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_PHY0, MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_PHY0_CLK_FORCE_EN_FIELD, 1U)); @@ -2661,19 +2747,18 @@ int max96724_set_pipe(struct device *dev, int pipe_id, else dev_dbg(dev, "%s: %s CSI %u %s %s (num_lanes:x%u) continuous clock mode enabled\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); - */ /* Double-Check GMSL link status after stream start configuration */ { unsigned int link_status = 0; max96724_read_reg(dev, MAX96724_LINK_STATUS(src_port), &link_status); dev_dbg(dev, "%s: %s Link status: 0x%02x (LOCK=%d, bit0-7: %s%s%s%s%s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), + __func__, max96724_get_link_name(src_link), link_status, !!(link_status & MAX96724_LINK_LOCK_BIT), (link_status & 0x01) ? "VID_LOCK " : "", (link_status & 0x02) ? "CONFIG_DETECT " : "", (link_status & 0x04) ? "VIDEO_DETECT " : "", @@ -2685,7 +2770,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, unsigned int pll_status = 0; max96724_read_reg(dev, MAX96724_DPLL_STATUS_ADDR, &pll_status); dev_dbg(dev, "%s: %s DPLL status: 0x%02x (bit0-7: %s%s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), pll_status, + __func__, max96724_get_link_name(src_link), pll_status, (pll_status & MAX96724_DPLL_STATUS_FIELD(0)) ? "CSIPLL0_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(1)) ? "CSIPLL1_LOCK " : "", (pll_status & MAX96724_DPLL_STATUS_FIELD(2)) ? "CSIPLL2_LOCK " : "", @@ -2693,7 +2778,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, unsigned int vid_status = 0; max96724_read_reg(dev, MAX96724_VID_STATUS_ADDR(src_port), &vid_status); dev_dbg(dev, "%s: %s Video RX status: 0x%02x (LOCK=%d, bit4-6: %s%s%s)\n", - __func__, max96724_get_link_name(priv->src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), + __func__, max96724_get_link_name(src_link), vid_status, !!(link_status & MAX96724_VID_LOCK_BIT), (vid_status & 0x40) ? "VID_LOCK " : "", (vid_status & 0x20) ? "VID_PKT_DET " : "", (vid_status & 0x10) ? "VID_SEQ_ERR " : ""); @@ -2701,7 +2786,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, dev_info(dev, "%s: Successfully enabled %s pipe %u (vc_id %u) CSI %u %s lanes %s map\n", __func__, - max96724_get_link_name(priv->src_link), + max96724_get_link_name(src_link), pipe_id, vc_id, priv->dst_link, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index e1f392cb..c1ef5338 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -729,9 +729,9 @@ static int ds5_raw_read(struct ds5 *state, u16 reg, void *val, size_t val_len) return ret; } +static u8 d4xx_set_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; #ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT static s64 d4xx_query_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; -static u8 d4xx_set_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; static void set_sub_stream_fmt(int index, u32 code) { d4xx_query_sub_stream[index] &= 0xFFFFFFFFFFFF0000; @@ -1870,7 +1870,7 @@ static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, ret |= max9295_set_pipe(state->ser_dev, pipe_id, data_type1, data_type2, vc_id); ret |= max96724_set_pipe(state->dser_dev, pipe_id, - data_type1, data_type2, vc_id); + data_type1, data_type2, vc_id, state->g_ctx.serdes_csi_link); #else ret = max9295_set_pipe(state->ser_dev, pipe_id, data_type1, data_type2, vc_id); @@ -1952,7 +1952,7 @@ static int ds5_configure(struct ds5 *state) // reset data path when switching to Y12I if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - max96724_reset_oneshot(state->dser_dev); + max96724_reset_oneshot(state->dser_dev, state->g_ctx.serdes_csi_link); #else max9296_reset_oneshot(state->dser_dev); #endif @@ -2604,7 +2604,7 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) mutex_lock(&state->lock); #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - ret = max96724_switch_link_channel(state->dser_dev); + //ret = max96724_switch_link_channel(state->dser_dev); #endif switch (ctrl->id) { @@ -3045,7 +3045,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) __func__, ds5_get_sensor_name(state), ctrl->name); #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - ret = max96724_switch_link_channel(state->dser_dev); + //ret = max96724_switch_link_channel(state->dser_dev); #endif switch (ctrl->id) { @@ -5461,15 +5461,19 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) sensor->pipe_id = max96724_get_available_pipe_id(state->dser_dev, - (int)state->g_ctx.dst_vc); + (int)state->g_ctx.dst_vc, + state->g_ctx.serdes_csi_link); + if (sensor->pipe_id < 0) { + dev_err(&state->client->dev, + "No free pipe in max967xx\n"); #else sensor->pipe_id = max9296_get_available_pipe_id(state->dser_dev, (int)state->g_ctx.dst_vc); -#endif if (sensor->pipe_id < 0) { dev_err(&state->client->dev, "No free pipe in max9296\n"); +#endif ret = -(ENOSR); goto restore_s_state; } @@ -5507,12 +5511,14 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) dev_dbg(&state->client->dev, "started after %dms\n", i * DS5_START_POLL_TIME); } + /* #ifdef CONFIG_VIDEO_D4XX_SERDES #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - max96724_check_status(state->dser_dev); + max96724_check_status(state->dser_dev, state->g_ctx.serdes_csi_link); max9295_check_status(state->ser_dev); #endif #endif + */ } else { // off ret = ds5_write(state, DS5_START_STOP_STREAM, DS5_STREAM_STOP | stream_id); @@ -5525,7 +5531,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) state->ir.sensor.config.format->data_type == GMSL_CSI_DT_RGB_888) { #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - max96724_reset_oneshot(state->dser_dev); + max96724_reset_oneshot(state->dser_dev, state->g_ctx.serdes_csi_link); #else max9296_reset_oneshot(state->dser_dev); #endif @@ -5533,24 +5539,24 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) #ifndef CONFIG_TEGRA_CAMERA_PLATFORM // reset for IPU6 streaming = 0; -#ifdef CONFIG_VIDEO_INTEL_IPU6_BACKWARD_COMPAT for (i = 0; i < ARRAY_SIZE(d4xx_set_sub_stream); i++) { if (d4xx_set_sub_stream[i]) { streaming = 1; break; } } -#endif - if (!streaming) { + #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - dev_warn(&state->client->dev, "max96724_reset_oneshot\n"); - max96724_reset_oneshot(state->dser_dev); + dev_warn(&state->client->dev, "max96724_reset_oneshot\n"); + max96724_reset_oneshot(state->dser_dev, + state->g_ctx.serdes_csi_link); #else + if (!streaming) { dev_warn(&state->client->dev, "max9296_reset_oneshot\n"); max9296_reset_oneshot(state->dser_dev); -#endif } #endif +#endif #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) if (max96724_release_pipe(state->dser_dev, sensor->pipe_id) < 0) #else @@ -5749,6 +5755,8 @@ static int ds5_mux_enable_streams(struct v4l2_subdev *sd, ds5_s_state_pad(state, sink_pad); + d4xx_set_sub_stream[i] = 1; + ret = ds5_mux_s_stream(sd, 1); return 0; @@ -5782,6 +5790,8 @@ static int ds5_mux_disable_streams(struct v4l2_subdev *sd, ds5_s_state_pad(state, sink_pad); + d4xx_set_sub_stream[i] = 0; + ret = ds5_mux_s_stream(sd, 0); return 0; @@ -7612,8 +7622,8 @@ static void ds5_remove(struct i2c_client *c) mutex_lock(&serdes_lock__); #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - ret = max96724_switch_link_channel(state->dser_dev); - ret |= max9295_reset_control(state->ser_dev); + //ret = max96724_switch_link_channel(state->dser_dev); + ret = max9295_reset_control(state->ser_dev); if (ret) dev_warn(&c->dev, "failed in 9295 reset control\n"); diff --git a/include/media/i2c/d4xx-max96724.h b/include/media/i2c/d4xx-max96724.h index 71c757ae..8d4a5e37 100644 --- a/include/media/i2c/d4xx-max96724.h +++ b/include/media/i2c/d4xx-max96724.h @@ -38,11 +38,11 @@ struct max96724_pdata { __u32 csi_out; }; -int max96724_get_available_pipe_id(struct device *dev, int vc_id); +int max96724_get_available_pipe_id(struct device *dev, int vc_id, u32 src_link); int max96724_set_pipe(struct device *dev, int pipe_id, u8 data_type1, - u8 data_type2, u32 vc_id); + u8 data_type2, u32 vc_id, u32 src_link); int max96724_release_pipe(struct device *dev, int pipe_id); -void max96724_reset_oneshot(struct device *dev); +void max96724_reset_oneshot(struct device *dev, u32 src_link); /** * Puts a deserializer device in single exclusive link mode, so link-specific @@ -131,7 +131,7 @@ void max96724_power_off(struct device *dev); int max96724_init_settings(struct device *dev); int max96724_set_mfp(struct device *dev, int pin, int val); -int max96724_check_status(struct device *dev); +int max96724_check_status(struct device *dev, u32 src_link); int max96724_switch_link_channel(struct device *dev); /** @} */ From 07d956ded2676b65e2676c29faea85c2bbfaf1c9 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 5 May 2026 03:19:58 -0700 Subject: [PATCH 58/72] d4xx-max96724 : WA multicam pipe and aggregator affinity issues Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 152 ++++++++++-------- 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c index 382beee0..bb3b7c1e 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -1909,7 +1909,7 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ int err = 0; struct max96724 *priv = dev_get_drvdata(dev); int i = 0, j = 0; - unsigned int st_count; + unsigned int st_count, src_link_st_count; #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT u8 en_mapping_num = 0xFF; @@ -1987,6 +1987,25 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ default: dev_warn(dev, "%s: invalid deserializer csi port, default CSI 1!\n", __func__); }; + + for (i = 0, st_count = 0, src_link_st_count = 0; i < MAX96724_MAX_PIPES; i++) { + + if (!priv->pipe[i].st_count) + continue; + + st_count++; + + if (src_link != priv->pipe[i].src_link) + continue; + + src_link_st_count++; + } + + dev_info(dev, "%s: %s stream %u/%u active pipes\n", + __func__, + max96724_get_link_name(src_link), + src_link_st_count, + st_count); struct reg_pair map_pipe_select[] = { {MAX96724_REG5_ADDR, 0x80}, // Enable lock @@ -2188,28 +2207,29 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ vc_id_3b_dst); #endif - // disable CSI out link during pipe re-configuration - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, - MAX96724_CSI_OUT_EN_FIELD, - MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0)); - if (err) { - dev_err(dev, "%s: Failed to enable csi output link: %d\n", - __func__, - err); - } - - /* Disable DPLL link during pipe re-configuration + /* ONLY on initial streaming, skip if CSI already streaming data */ - for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_DPLL_RESET(i), - MAX96724_DPLL_RESET_SOFT_RST_FIELD, - MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 0U)); - if (err) { - dev_err(dev, "%s: Failed to reset configure CSI %u %s DPLL : %d\n", - __func__, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - err); + if (st_count < 2) { + // disable CSI out link during initial pipe re-configuration + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, + MAX96724_CSI_OUT_EN_FIELD, + MAX96724_FIELD_PREP(MAX96724_CSI_OUT_EN_FIELD, 0)); + if (err) + dev_err(dev, "%s: Failed to enable csi output link: %d\n", + __func__, + err); + + // Disable DPLL link during pipe re-configuration + for (i = 0; i < MAX96724_NUM_CSI_LINKS; i++) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_DPLL_RESET(i), + MAX96724_DPLL_RESET_SOFT_RST_FIELD, + MAX96724_FIELD_PREP(MAX96724_DPLL_RESET_SOFT_RST_FIELD, 0U)); + if (err) + dev_err(dev, "%s: Failed to reset configure CSI %u %s DPLL : %d\n", + __func__, + priv->dst_link, + priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", + err); } /* Video Pipe Input Source Selection @@ -2241,8 +2261,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); #endif - err |= max96724_set_registers(dev, map_pipe_select, - ARRAY_SIZE(map_pipe_select)); + /* ONLY on initial streaming, skip if CSI already streaming data + */ + if (st_count < 2) + err |= max96724_set_registers(dev, map_pipe_select, + ARRAY_SIZE(map_pipe_select)); #if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) // Enable max96712 "legacy" mode @@ -2326,37 +2349,14 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ err = max96724_set_registers(dev, map_pipe_control, ARRAY_SIZE(map_pipe_control)); - // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 - err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); + if (st_count < 2) + // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 + err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); /* Configure per Pipe 0, 1, 2 and 3 MIPI interleaved-SYNC FCFS aggregation * Aggregator A, B, C and D corresponds to GMSL A, B, C and D input src - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), - MAX96724_MAP_CON_SYNC_PIPE_0_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_FIELD - | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, - MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) - | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); */ - for (i = 0, st_count = 0; i < MAX96724_MAX_PIPES; i++) { - - if (!priv->pipe[i].st_count) - continue; - - if (src_link != priv->pipe[i].src_link) - continue; - - st_count++; - } - - if (st_count < 2) + if (src_link_st_count < 2) err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), MAX96724_MAP_CON_SYNC_PIPE_0_FIELD | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD @@ -2375,16 +2375,18 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_MAP_CON_SYNC_PIPE_FIELD, MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id)); - /* Configure per Pipe CSI and PHY type + /* ONLY on initial streaming, skip if CSI already streaming data + * Configure per Pipe CSI and PHY type */ - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(csi_id), - MAX96724_MIPI_TX_CPHY_EN_FIELD - | MAX96724_MIPI_TX_VCX_EN_FIELD - | MAX96724_MIPI_TX_LANE_CNT_FIELD, - MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, - priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) - | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) - | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); + if (st_count < 2) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(csi_id), + MAX96724_MIPI_TX_CPHY_EN_FIELD + | MAX96724_MIPI_TX_VCX_EN_FIELD + | MAX96724_MIPI_TX_LANE_CNT_FIELD, + MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, + priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) + | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) + | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); #if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) /* @@ -2408,6 +2410,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ err); } + /* ONLY on initial streaming, skip if CSI already streaming data + */ + if (st_count > 1) + return err; + err |= max967xx_phy_tuning(dev); if (err) { dev_err(dev, "%s: Failed to apply %s tunning : %d\n", @@ -2646,6 +2653,8 @@ int max96724_set_pipe(struct device *dev, int pipe_id, unsigned rev; int err = 0; u8 src_port = max96724_link_to_port(src_link); + unsigned int st_count; + int i = 0; if (pipe_id > (MAX96724_MAX_PIPES - 1)) { dev_info(dev, "%s, input pipe_id: %d exceed max96724 max pipes\n", @@ -2653,17 +2662,16 @@ int max96724_set_pipe(struct device *dev, int pipe_id, return -EINVAL; } - dev_dbg(dev, "%s: disabling %s pipe %u (vc_id %u) CSI %u %s mode %s (num_lanes:x%u)\n", - __func__, - max96724_get_link_name(src_link), - pipe_id, vc_id, - priv->dst_link, - priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", - priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", - priv->dst_n_lanes); - mutex_lock(&priv->lock); + for (i = 0, st_count = 0; i < MAX96724_MAX_PIPES; i++) { + + if (!priv->pipe[i].st_count) + continue; + + st_count++; + } + dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", __func__, max96724_get_link_name(src_link), @@ -2671,6 +2679,9 @@ int max96724_set_pipe(struct device *dev, int pipe_id, err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link, src_link); + if (st_count > 1) + goto status; + /* Enable all channels err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) @@ -2728,7 +2739,6 @@ int max96724_set_pipe(struct device *dev, int pipe_id, err); } - /* Turn on MIPI PHY continuous clock mode (matches script line 520) * Reference script does this AFTER CSI enable (0x040B = 0x02) * This enables continuous clock on MIPI CSI-2 interface @@ -2752,7 +2762,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", priv->csi_mode == MAX96724_CSI_MODE_4X2 ? "4X2" : "2X4", priv->dst_n_lanes); - +status: /* Double-Check GMSL link status after stream start configuration */ { unsigned int link_status = 0; From bf96ab41767918daaf77278afc6456e4a971f3d0 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 18 Mar 2026 12:15:19 -0700 Subject: [PATCH 59/72] d4xx-max9295: initial virtual-channel extension implementation * d4xx-max9295: enable cphy/dphy 5/4-bits vc-extension Signed-off-by: florent pirou --- drivers/media/i2c/d4xx-compat/d4xx-max9295.c | 29 ++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c index a2336a97..32658e76 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max9295.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max9295.c @@ -745,8 +745,13 @@ static int __max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, map_pipe_control[0].val = 0x40 | data_type1; map_pipe_control[1].val = 0x40 | data_type2; - map_pipe_control[2].val = 1 << vc_id; +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + map_pipe_control[2].val = vc_id > 7 ? 0x00 : 1 << (vc_id & 0x07); + map_pipe_control[3].val = vc_id > 7 ? 1 << (vc_id & 0x07) : 0x00; +#else + map_pipe_control[2].val = 1 << (vc_id % MAX9295_MAX_PIPES); map_pipe_control[3].val = 0x00; +#endif if (pipe_id == 0) pipe_x_val = map_pipe_control[1].val; @@ -805,8 +810,13 @@ static int __max9295_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_type map_pipe_control[0].val = 0x40 | data_type1; map_pipe_control[1].val = 0x40 | data_type2; - map_pipe_control[2].val = 1 << vc_id; +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + map_pipe_control[2].val = vc_id > 7 ? 0x00 : 1 << (vc_id & 0x07); + map_pipe_control[3].val = vc_id > 7 ? 1 << (vc_id & 0x07) : 0x00; +#else + map_pipe_control[2].val = 1 << (vc_id % MAX9295_MAX_PIPES); map_pipe_control[3].val = 0x00; +#endif map_pipe_control[4].val = bpp; map_pipe_control[5].val = 0x0E; @@ -832,8 +842,13 @@ int max9295_init_settings(struct device *dev) struct reg_pair map_pipe_opt[] = { // Enable all pipes {MAX9295_PIPE_EN_ADDR, 0xF3}, - // Write 0x33 for 4 lanes +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + // Write 0xBB for 4 lanes - Extended VC Enabled + {MAX9295_MIPI_RX1_ADDR, 0x99}, +#else + // Write 0x33 for 4 lanes - Extended VC Disabled {MAX9295_MIPI_RX1_ADDR, 0x11}, +#endif // All pipes pull clock from port B {MAX9295_CSI_PORT_SEL_ADDR, 0x6F}, // All pipes pull data from port B @@ -939,9 +954,13 @@ int max9295_set_pipe(struct device *dev, int pipe_id, __func__, pipe_id); return -EINVAL; } - - dev_dbg(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + dev_info(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_ext_id %u\n", __func__, pipe_id, data_type1, data_type2, vc_id); +#else + dev_info(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", + __func__, pipe_id, data_type1, data_type2, (vc_id % MAX9295_MAX_PIPES)); +#endif mutex_lock(&priv->lock); From 550d8f5b5b65fe0e82f43f18ab8c03b2604a1ad4 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 7 May 2026 06:35:18 -0700 Subject: [PATCH 60/72] d4xx-max96724: enable cphy/dphy 5/4-bits vc-extension * d4xx: initialized sensor virtual-channel upto 8 * d4xx-max96724: extend dt/vc remap and aggregate upto 8 Signed-off-by: florent pirou --- Makefile | 7 +- drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 437 +++++++++++------- drivers/media/i2c/d4xx-compat/d4xx.c | 8 + 3 files changed, 278 insertions(+), 174 deletions(-) diff --git a/Makefile b/Makefile index e7789a1c..6f88255e 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,9 @@ export CONFIG_VIDEO_MAX9X=m export CONFIG_VIDEO_INTEL_IPU6=m export CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET=y export CONFIG_INTEL_IPU_ACPI=m +export CONFIG_INTEL_IPU_VC_EXT=y +export CONFIG_VIDEO_D4XX_MAX9295_VC_EXT = n +export CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT = y export CONFIG_VIDEO_D4XX_MAX9295 = m export CONFIG_VIDEO_D4XX_MAX9296 = m export CONFIG_VIDEO_D4XX_COMPAT=m @@ -41,7 +44,8 @@ subdir-ccflags-$(CONFIG_VIDEO_MAX9X) += -DCONFIG_VIDEO_MAX9X subdir-ccflags-$(CONFIG_VIDEO_ISX031) += -DCONFIG_VIDEO_ISX031 subdir-ccflags-$(CONFIG_VIDEO_AR0233) += -DCONFIG_VIDEO_AR0233 subdir-ccflags-$(CONFIG_VIDEO_D4XX_COMPAT) += -DCONFIG_VIDEO_D4XX -subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX96724) += -DCONFIG_VIDEO_D4XX_MAX96712_LEGACY #-DCONFIG_VIDEO_D4XX_MAX96724_DT_VC_EXT +subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) += -DCONFIG_VIDEO_D4XX_MAX9295_VC_EXT +subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) += -DCONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT subdir-ccflags-$(CONFIG_VIDEO_AR0820) += -DCONFIG_VIDEO_AR0820 subdir-ccflags-$(CONFIG_VIDEO_AR0234) += -DCONFIG_VIDEO_AR0234 -DCONFIG_V4L2_CCI_I2C subdir-ccflags-$(CONFIG_IPU_BRIDGE) += -DCONFIG_IPU_BRIDGE @@ -68,6 +72,7 @@ subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET) += -DCONFIG_VIDEO_INTEL_IPU7_ISYS_RESET subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS) += -DCONFIG_VIDEO_INTEL_IPU6_EXT_CTRLS subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS) += -DCONFIG_VIDEO_INTEL_IPU7_EXT_CTRLS +subdir-ccflags-$(CONFIG_INTEL_IPU_VC_EXT) += -DCONFIG_INTEL_IPU_VC_EXT # Build IPU7 drivers from submodule obj-m += ipu7-drivers/drivers/media/pci/intel/ipu7/ diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c index bb3b7c1e..a4f846cf 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -1,3 +1,5 @@ + + // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017-2024, INTEL CORPORATION. All rights reserved. @@ -262,6 +264,8 @@ */ #define MAX96724_VIDEO_PIPE_SEL_0_ADDR 0x00F0 /* Pipe 0/1 input: GMSL A/B */ #define MAX96724_VIDEO_PIPE_SEL_1_ADDR 0x00F1 /* Pipe 2/3 input: GMSL C/D */ +#define MAX96724_VIDEO_PIPE_SEL_2_ADDR 0x00F2 /* Pipe 4/5 input: GMSL A/B max96712 only */ +#define MAX96724_VIDEO_PIPE_SEL_3_ADDR 0x00F3 /* Pipe 6/7 input: GMSL C/D max96712 only */ #define MAX96724_VIDEO_PIPE_EN_ADDR 0x00F4 /* Enable Video Pipes */ #define MAX96724_VIDEO_PIPE_SEL(pipe) (MAX96724_VIDEO_PIPE_SEL_0_ADDR + ((pipe) / 2)) #define MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(link) GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2) @@ -389,6 +393,7 @@ #define MAX96724_MAX_SOURCES 4 #define MAX96724_NUM_CSI_LINKS 4 #define MAX96724_MAX_PIPES 4 +#define MAX96724_MAX_VCX 8 #define MAX96724_DEFAULT_SER_ADDR_A 0x28 #define MAX96724_DEFAULT_SER_ADDR_B 0x2A @@ -1719,10 +1724,15 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id, u32 src_link) int pipe_id = -ENOMEM; int err = 0; struct max96724 *priv = dev_get_drvdata(dev); + int _vc_id = vc_id % MAX96724_MAX_PIPES; mutex_lock(&priv->lock); for (i = 0; i < MAX96724_MAX_PIPES; i++) { +#ifdef CONFIG_INTEL_IPU_VC_EXT + if (i == _vc_id && !priv->pipe[i].st_count) { +#else if (i == vc_id && !priv->pipe[i].st_count) { +#endif priv->pipe[i].st_count++; priv->pipe[i].src_link = src_link; pipe_id = i; @@ -1789,6 +1799,22 @@ void max96724_reset_oneshot(struct device *dev, u32 src_link) __func__, max96724_get_link_name(src_link)); return; } + + /* disable each video pipe configuration corresponding to the reset link */ + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + + if (src_link != priv->pipe[i].src_link) + continue; + + dev_dbg(dev, "%s: disable %s video pipe %d\n", + __func__, max96724_get_link_name(src_link), i); + + /* disable video pipe if is no longer used */ + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(i), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(i), 0U)); + } + /* Re-Check GMSL link status after initial configuration */ { unsigned int link_status = 0; @@ -1911,11 +1937,6 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ int i = 0, j = 0; unsigned int st_count, src_link_st_count; -#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - u8 en_mapping_num = 0xFF; -#else - u8 en_mapping_num = 0x0F; -#endif u8 all_mapping_phy = 0x55; u8 phy4_lanes_mapping = 0xE4; u8 phy3_lanes_mapping = 0xE4; @@ -1925,8 +1946,24 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ u8 link_id = max96724_link_to_port(src_link); u8 vc_id_2b_lsb = ((u8) vc_id) & 0x3; +#ifdef CONFIG_VIDEO_D4XX_MAX9295_VC_EXT + u8 vc_id_3b_src = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_SRC_VC_FIELD, (vc_id >> 2)); +#else u8 vc_id_3b_src = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_SRC_VC_FIELD, 0U); +#endif +#ifdef CONFIG_INTEL_IPU_VC_EXT + u8 vc_id_3b_dst = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_DST_VC_FIELD, (vc_id >> 2)); +#else u8 vc_id_3b_dst = MAX96724_FIELD_PREP(MAX96724_MAP_SRCDST_H_DST_VC_FIELD, 0U); +#endif + +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + int _pipe_id = vc_id_3b_dst > 0 ? (MAX96724_MAX_PIPES - 1) - (vc_id % MAX96724_MAX_PIPES) : pipe_id; + u8 en_mapping_num = 0xFF; +#else + int _pipe_id = pipe_id; + u8 en_mapping_num = 0x0F; +#endif if (data_type2 == 0x0) #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT @@ -2006,7 +2043,7 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ max96724_get_link_name(src_link), src_link_st_count, st_count); - + struct reg_pair map_pipe_select[] = { {MAX96724_REG5_ADDR, 0x80}, // Enable lock {MAX96724_REG6_ADDR, 0xFF}, // Enable all GMSL input sources @@ -2021,15 +2058,9 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ }; struct reg_pair map_pipe_control[] = { -#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - // Enable 16 mappings for Pipe X - {MAX96724_PIPE_X_EN_L_ADDR, 0xFF}, - {MAX96724_PIPE_X_EN_H_ADDR, 0xFF}, -#else // Enable 4 mappings for Pipe X {MAX96724_PIPE_X_EN_L_ADDR, 0x0F}, {MAX96724_PIPE_X_EN_H_ADDR, 0x00}, -#endif // Map data_type1 on vc_id {MAX96724_PIPE_X_SRC_0_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, {MAX96724_PIPE_X_DST_0_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, @@ -2042,54 +2073,8 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ // Map data_type2 on vc_id {MAX96724_PIPE_X_SRC_3_MAP_ADDR, GMSL_CSI_DT_EMBED}, {MAX96724_PIPE_X_DST_3_MAP_ADDR, GMSL_CSI_DT_EMBED}, -#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - // Map UYVY data_type1 on vc_id - {MAX96724_PIPE_X_SRC_4_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_YUV422_8}, - {MAX96724_PIPE_X_DST_4_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_YUV422_8}, - // Map UYVY frame_start on vc_id - {MAX96724_PIPE_X_SRC_5_MAP_ADDR, (1 << 6) | 0x00}, - {MAX96724_PIPE_X_DST_5_MAP_ADDR, (1 << 6) | 0x00}, - // Map frame end on vc_id - {MAX96724_PIPE_X_SRC_6_MAP_ADDR, (1 << 6) | 0x01}, - {MAX96724_PIPE_X_DST_6_MAP_ADDR, (1 << 6) | 0x01}, - // Map data_type2 on vc_id - {MAX96724_PIPE_X_SRC_7_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_EMBED}, - {MAX96724_PIPE_X_DST_7_MAP_ADDR, (1 << 6) | GMSL_CSI_DT_EMBED}, - // Map Y8I data_type1 on vc_id - {MAX96724_PIPE_X_SRC_8_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_YUV422_8}, - {MAX96724_PIPE_X_DST_8_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_YUV422_8}, - // Map frame_start on vc_id - {MAX96724_PIPE_X_SRC_9_MAP_ADDR, (2 << 6) | 0x00}, - {MAX96724_PIPE_X_DST_9_MAP_ADDR, (2 << 6) | 0x00}, - // Map frame end on vc_id - {MAX96724_PIPE_X_SRC_10_MAP_ADDR, (2 << 6) | 0x01}, - {MAX96724_PIPE_X_DST_10_MAP_ADDR, (2 << 6) | 0x01}, - // Map data_type2 on vc_id - {MAX96724_PIPE_X_SRC_11_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_EMBED}, - {MAX96724_PIPE_X_DST_11_MAP_ADDR, (2 << 6) | GMSL_CSI_DT_EMBED}, - // Map Y8 data_type1 on vc_id - {MAX96724_PIPE_X_SRC_12_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_RAW_8}, - {MAX96724_PIPE_X_DST_12_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_RAW_8}, - // Map frame_start on vc_id - {MAX96724_PIPE_X_SRC_13_MAP_ADDR, (3 << 6) | 0x00}, - {MAX96724_PIPE_X_DST_13_MAP_ADDR, (3 << 6) | 0x00}, - // Map frame end on vc_id - {MAX96724_PIPE_X_SRC_14_MAP_ADDR, (3 << 6) | 0x01}, - {MAX96724_PIPE_X_DST_14_MAP_ADDR, (3 << 6) | 0x01}, - // Map data_type2 on vc_id - {MAX96724_PIPE_X_SRC_15_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_EMBED}, - {MAX96724_PIPE_X_DST_15_MAP_ADDR, (3 << 6) | GMSL_CSI_DT_EMBED}, -#endif // Mappings to CSI 1 (master for port A) {MAX96724_PIPE_X_PHY_DEST_0_MAP_ADDR, all_mapping_phy}, -#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - // Mappings to CSI 1 (master for port A) - {MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR, all_mapping_phy}, - // Mappings to CSI 1 (master for port A) - {MAX96724_PIPE_X_PHY_DEST_2_MAP_ADDR, all_mapping_phy}, - // Mappings to CSI 1 (master for port A) - {MAX96724_PIPE_X_PHY_DEST_3_MAP_ADDR, all_mapping_phy}, -#endif // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled {MAX96724_VID_RX0_ADDR, 0x23}, // pipe X // LIM_HEART : Disabled @@ -2100,72 +2085,112 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ {MAX96724_PIPE_X_SRCDST_VC_EXT_1_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_2_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_3_MAP_ADDR, 0x0}, + }; + #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + + struct reg_pair map_pipe_control_dt_ext[] = { + // Enable 16 mappings for Pipe X + {MAX96724_PIPE_X_EN_L_ADDR, 0xFF}, + {MAX96724_PIPE_X_EN_H_ADDR, 0x00}, + // Map UYVY data_type1 on vc_id + {MAX96724_PIPE_X_SRC_4_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, + {MAX96724_PIPE_X_DST_4_MAP_ADDR, GMSL_CSI_DT_YUV422_8}, + // Map UYVY frame_start on vc_id + {MAX96724_PIPE_X_SRC_5_MAP_ADDR, 0x00}, + {MAX96724_PIPE_X_DST_5_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX96724_PIPE_X_SRC_6_MAP_ADDR, 0x01}, + {MAX96724_PIPE_X_DST_6_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX96724_PIPE_X_SRC_7_MAP_ADDR, GMSL_CSI_DT_EMBED}, + {MAX96724_PIPE_X_DST_7_MAP_ADDR, GMSL_CSI_DT_EMBED}, + // Mappings to CSI 1 (master for port A) + {MAX96724_PIPE_X_PHY_DEST_1_MAP_ADDR, all_mapping_phy}, + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + {MAX96724_VID_RX0_ADDR, 0x23}, // pipe X + // LIM_HEART : Disabled + {MAX96724_VID_RX6_ADDR, dis_lim_heart}, + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ {MAX96724_PIPE_X_SRCDST_VC_EXT_4_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_5_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_6_MAP_ADDR, 0x0}, {MAX96724_PIPE_X_SRCDST_VC_EXT_7_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_8_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_9_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_10_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_11_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_12_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_13_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_14_MAP_ADDR, 0x0}, - {MAX96724_PIPE_X_SRCDST_VC_EXT_15_MAP_ADDR, 0x0}, -#endif }; -#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - // Input link is the - for (i = 0; i < 38; i++) { - map_pipe_control[i].addr += 0x40 * link_id; + + for (i = 0; i < 11; i++) { + map_pipe_control[i].addr += 0x40 * _pipe_id; + map_pipe_control_dt_ext[i].addr += 0x40 * _pipe_id; } - map_pipe_control[0].val = en_mapping_num; - map_pipe_control[1].val = en_mapping_num; - /* Extend each SRC/DST Datatype - */ - i = 2 + (vc_id * 8); - map_pipe_control[i].val = (vc_id_2b_lsb << 6) | 0x00; - map_pipe_control[i+1].val = (vc_id_2b_lsb << 6) | 0x00; - map_pipe_control[i+2].val = (vc_id_2b_lsb << 6) | 0x01; - map_pipe_control[i+3].val = (vc_id_2b_lsb << 6) | 0x01; - map_pipe_control[i+4].val = (vc_id_2b_lsb << 6) | data_type1; - map_pipe_control[i+5].val = (vc_id_2b_lsb << 6) | data_type1; - map_pipe_control[i+6].val = (vc_id_2b_lsb << 6) | data_type2; - map_pipe_control[i+7].val = (vc_id_2b_lsb << 6) | data_type2; - - map_pipe_control[34].val = all_mapping_phy; - map_pipe_control[35].val = all_mapping_phy; - map_pipe_control[36].val = all_mapping_phy; - map_pipe_control[37].val = all_mapping_phy; - - map_pipe_control[38].addr += 0x12 * link_id; - map_pipe_control[39].addr += 0x12 * link_id; - - map_pipe_control[38].val = 0x23; - map_pipe_control[39].val = dis_lim_heart; + if (vc_id_3b_dst > 0) { - /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs - */ - i= 40 + (vc_id * 4); - map_pipe_control[i].addr += 0x10 * link_id; - map_pipe_control[i+1].addr += 0x10 * link_id; - map_pipe_control[i+2].addr += 0x10 * link_id; - map_pipe_control[i+3].addr += 0x10 * link_id; - - map_pipe_control[i].val = (vc_id_3b_src | vc_id_3b_dst); - map_pipe_control[i+1].val = (vc_id_3b_src | vc_id_3b_dst); - map_pipe_control[i+2].val = (vc_id_3b_src | vc_id_3b_dst); - map_pipe_control[i+3].val = (vc_id_3b_src | vc_id_3b_dst); - - dev_info(dev, "%s: %s pipe %u set on EVERY d4xx dt/vc possible match\n", - __func__, - max96724_get_link_name(src_link), - link_id); + map_pipe_control_dt_ext[0].val = en_mapping_num; + map_pipe_control_dt_ext[1].val = 0x0; + map_pipe_control_dt_ext[2].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control_dt_ext[3].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control_dt_ext[4].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control_dt_ext[5].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control_dt_ext[6].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control_dt_ext[7].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control_dt_ext[8].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control_dt_ext[9].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control_dt_ext[10].val = all_mapping_phy; + + map_pipe_control_dt_ext[11].addr += 0x12 * _pipe_id; + map_pipe_control_dt_ext[12].addr += 0x12 * _pipe_id; + + map_pipe_control_dt_ext[11].val = 0x23; + map_pipe_control_dt_ext[12].val = dis_lim_heart; + + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ + map_pipe_control_dt_ext[13].addr += 0x10 * _pipe_id; + map_pipe_control_dt_ext[14].addr += 0x10 * _pipe_id; + map_pipe_control_dt_ext[15].addr += 0x10 * _pipe_id; + map_pipe_control_dt_ext[16].addr += 0x10 * _pipe_id; + + map_pipe_control_dt_ext[13].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control_dt_ext[14].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control_dt_ext[15].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control_dt_ext[16].val = (vc_id_3b_src | vc_id_3b_dst); + + } else { + + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = 0x0; + map_pipe_control[2].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[3].val = (vc_id_2b_lsb << 6) | 0x00; + map_pipe_control[4].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[5].val = (vc_id_2b_lsb << 6) | 0x01; + map_pipe_control[6].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[7].val = (vc_id_2b_lsb << 6) | data_type1; + map_pipe_control[8].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; + map_pipe_control[10].val = all_mapping_phy; + + map_pipe_control[11].addr += 0x12 * _pipe_id; + map_pipe_control[12].addr += 0x12 * _pipe_id; + + map_pipe_control[11].val = 0x23; + map_pipe_control[12].val = dis_lim_heart; + + /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs + */ + map_pipe_control[13].addr += 0x10 * _pipe_id; + map_pipe_control[14].addr += 0x10 * _pipe_id; + map_pipe_control[15].addr += 0x10 * _pipe_id; + map_pipe_control[16].addr += 0x10 * _pipe_id; + + map_pipe_control[13].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[14].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[15].val = (vc_id_3b_src | vc_id_3b_dst); + map_pipe_control[16].val = (vc_id_3b_src | vc_id_3b_dst); + } #else for (i = 0; i < 11; i++) { - map_pipe_control[i].addr += 0x40 * pipe_id; + map_pipe_control[i].addr += 0x40 * _pipe_id; } map_pipe_control[0].val = en_mapping_num; @@ -2180,31 +2205,39 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ map_pipe_control[9].val = (vc_id_2b_lsb << 6) | data_type2; map_pipe_control[10].val = all_mapping_phy; - map_pipe_control[11].addr += 0x12 * pipe_id; - map_pipe_control[12].addr += 0x12 * pipe_id; + map_pipe_control[11].addr += 0x12 * _pipe_id; + map_pipe_control[12].addr += 0x12 * _pipe_id; map_pipe_control[11].val = 0x23; map_pipe_control[12].val = dis_lim_heart; /* Extend each SRC/DST to CPHY 5-bits / DPHY 4-bits VCs */ - map_pipe_control[13].addr += 0x10 * pipe_id; - map_pipe_control[14].addr += 0x10 * pipe_id; - map_pipe_control[15].addr += 0x10 * pipe_id; - map_pipe_control[16].addr += 0x10 * pipe_id; + map_pipe_control[13].addr += 0x10 * _pipe_id; + map_pipe_control[14].addr += 0x10 * _pipe_id; + map_pipe_control[15].addr += 0x10 * _pipe_id; + map_pipe_control[16].addr += 0x10 * _pipe_id; map_pipe_control[13].val = (vc_id_3b_src | vc_id_3b_dst); map_pipe_control[14].val = (vc_id_3b_src | vc_id_3b_dst); map_pipe_control[15].val = (vc_id_3b_src | vc_id_3b_dst); map_pipe_control[16].val = (vc_id_3b_src | vc_id_3b_dst); +#endif - dev_info(dev, "%s: %s pipe %u set on vc_id=%u [vc_id_3bits: src:0x%x, dst:0x%x] \n", +#ifdef CONFIG_INTEL_IPU_VC_EXT + dev_info(dev, "%s: %s pipe %u set on vc_ext_id=%u [vc_id_3bits: src:0x%x, dst:0x%x] \n", __func__, max96724_get_link_name(src_link), - pipe_id, + _pipe_id, vc_id, vc_id_3b_src, vc_id_3b_dst); +#else + dev_info(dev, "%s: %s pipe %u set on vc_id=%u\n", + __func__, + max96724_get_link_name(src_link), + _pipe_id, + vc_id_2b_lsb); #endif /* ONLY on initial streaming, skip if CSI already streaming data @@ -2257,9 +2290,28 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_VIDEO_PIPE_EN_FIELD(0), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 0U)); #else - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 0U)); + /* Video Pipe Disable Selection + * if same link source, disable all before re-nable one-by-one. + * + * REG 0x00F4: Enable/disable Video Pipes + * Bit[3:0] = : Enable Pipe X + */ + if (src_link_st_count > 1) { + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + if (!priv->pipe[i].st_count) + continue; + + if (src_link != priv->pipe[i].src_link) + continue; + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(i), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(i), 0U)); + } + } else + err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), 0U)); #endif /* ONLY on initial streaming, skip if CSI already streaming data */ @@ -2290,40 +2342,6 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ dev_dbg(dev, "%s: mapped max96712 legacy mode ALL pipe to streams (Pipe X Only) mode\n", __func__); -#elif defined(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) - // Enable all streams (X,Y,Z and/or U) source mapping -#if !defined(CONFIG_VIDEO_D4XX_MAX96712) - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, - MAX96724_VIDEO_PIPE_SRCMAP_MODE)); -#endif - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(0), - MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) - | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), 0U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(1), 1U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), 0U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(1), 1U)); - - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(2), - MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2) - | MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(2), 2U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(3), 3U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(2), 2U) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(3), 3U)); - if (err) - dev_err(dev, "%s: Failed to map pipes to streams (X,Y,Z and/or U) mode: %d\n", - __func__, - err); - else - dev_dbg(dev, "%s: mapped max967xx ALL pipe to streams (X,Y,Z and/or U) mode\n", - __func__); #else // Enable specific pipe streams (X,Y,Z and/or U) source mapping #if !defined(CONFIG_VIDEO_D4XX_MAX96712) @@ -2332,11 +2350,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, MAX96724_VIDEO_PIPE_SRCMAP_MODE)); #endif - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(pipe_id), - MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id), link_id) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), pipe_id)); + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(_pipe_id), + MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(_pipe_id) + | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(_pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(_pipe_id), link_id) + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(_pipe_id), _pipe_id)); if (err) dev_err(dev, "%s: Failed to select all streams (X,Y,Z and/or U) mode: %d\n", __func__, @@ -2344,18 +2362,45 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ else dev_dbg(dev, "%s: mapped max967xx pipe %u to streams (X,Y,Z and/or U) mode\n", __func__, - pipe_id); + _pipe_id); #endif + +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + if (vc_id_3b_dst > 0) + err = max96724_set_registers(dev, map_pipe_control_dt_ext, + ARRAY_SIZE(map_pipe_control_dt_ext)); + else + err = max96724_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); +#else err = max96724_set_registers(dev, map_pipe_control, ARRAY_SIZE(map_pipe_control)); +#endif if (st_count < 2) // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); + /* Configure per Pipe 0, 1, 2 and 3 MIPI interleaved-SYNC FCFS aggregation * Aggregator A, B, C and D corresponds to GMSL A, B, C and D input src */ +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + if (src_link_st_count < 2) + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(_pipe_id), + MAX96724_MAP_CON_SYNC_PIPE_0_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_FIELD + | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, + MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(_pipe_id) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); +#else if (src_link_st_count < 2) err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), MAX96724_MAP_CON_SYNC_PIPE_0_FIELD @@ -2368,12 +2413,13 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) - | MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id) + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(_pipe_id) | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); else err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), MAX96724_MAP_CON_SYNC_PIPE_FIELD, - MAX96724_MAP_CON_SYNC_PIPE_MASTER(pipe_id)); + MAX96724_MAP_CON_SYNC_PIPE_MASTER(_pipe_id)); +#endif /* ONLY on initial streaming, skip if CSI already streaming data * Configure per Pipe CSI and PHY type @@ -2385,7 +2431,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ | MAX96724_MIPI_TX_LANE_CNT_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, priv->csi_phy == MAX96724_CSI_CPHY ? 1U : 0U) +#ifdef CONFIG_INTEL_IPU_VC_EXT + | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_4_BITS) +#else | MAX96724_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS) +#endif | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); #if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) @@ -2397,9 +2447,28 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_VIDEO_PIPE_EN_FIELD(0), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U)); #else - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), 1U)); + /* Video Pipe Enable Selection + * if same link source activate at least twice, disable all concurrent streams before re-nable one-by-one. + * + * REG 0x00F4: Enable/disable Video Pipes + * Bit[3:0] = : Enable Pipe X + */ + if (src_link_st_count > 1) { + for (i = 0; i < MAX96724_MAX_PIPES; i++) { + if (!priv->pipe[i].st_count) + continue; + + if (src_link != priv->pipe[i].src_link) + continue; + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(i), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(i), 1U)); + } + } else + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), 1U)); #endif if (err) { dev_err(dev, "%s: Failed to configure pipe control %u %s %u lanes: %d\n", @@ -2506,6 +2575,7 @@ int max96724_init_settings(struct device *dev) { int err = 0; int i; + int _pipe_id; unsigned rev; struct max96724 *priv = dev_get_drvdata(dev); @@ -2522,14 +2592,28 @@ int max96724_init_settings(struct device *dev) mutex_lock(&priv->lock); +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + for (i = 0; i < MAX96724_MAX_VCX; i++) { + _pipe_id = (i % MAX96724_MAX_PIPES); +#else for (i = 0; i < MAX96724_MAX_PIPES; i++) { + _pipe_id = i; +#endif dev_dbg(dev, "%s: enable %s link to Video pipe %d", __func__, max96724_get_link_name(priv->src_link), - i); + _pipe_id); - err |= __max96724_set_pipe_d4xx(dev, i, GMSL_CSI_DT_YUV422_8, + err |= __max96724_set_pipe_d4xx(dev, _pipe_id, GMSL_CSI_DT_YUV422_8, GMSL_CSI_DT_EMBED, i, priv->dst_link, priv->src_link); + + /* + * REG 0x00F4: disable Video Pipes by default + * Bit[3:0] = : Disable Pipe 0/1/2/3. + */ + err |= MAX96724_UPDATE_BITS(map, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), 0U)); } if (err) { dev_err(dev, "%s: Failed to configure %s link to Video pipes : %d\n", @@ -2672,10 +2756,17 @@ int max96724_set_pipe(struct device *dev, int pipe_id, st_count++; } - dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", +#ifdef CONFIG_INTEL_IPU_VC_EXT + dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_ext_id %u\n", __func__, max96724_get_link_name(src_link), pipe_id, data_type1, data_type2, vc_id); +#else + dev_info(dev, "%s() : %s Re-configuring pipe_id %d, data_type1 %x, data_type2 %x, vc_id %u\n", + __func__, + max96724_get_link_name(src_link), + pipe_id, data_type1, data_type2, (vc_id % MAX96724_MAX_PIPES)); +#endif err = __max96724_set_pipe_d4xx(dev, pipe_id, data_type1, data_type2, vc_id, priv->dst_link, src_link); @@ -2717,6 +2808,7 @@ int max96724_set_pipe(struct device *dev, int pipe_id, priv->dst_n_lanes); /* Enable MIPI TX controller and enable PHY CLK cycle + */ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_LANE_CNT(priv->dst_link), MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, MAX96724_FIELD_PREP(MAX96724_MIPI_TX_WAKEUP_CYC_FIELD, 1U)); @@ -2727,7 +2819,6 @@ int max96724_set_pipe(struct device *dev, int pipe_id, priv->csi_phy == MAX96724_CSI_CPHY ? "CPHY" : "DPHY", err); } - */ // enable CSI out link after initialization complet err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_CSI_OUT_EN_ADDR, diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index c1ef5338..03434d35 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -4069,10 +4069,18 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT +static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,7,2,5, 2,5,0,7, 1,6,3,4, 3,4,1,6}; +#else static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,3,2, 3,2,1,0}; +#endif module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" +#ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + "\t\tsensor_vc=0,7,2,5,2,5,0,7,1,6,3,4,3,4,1,6"); +#else "\t\tsensor_vc=0,1,2,3,2,3,0,1,1,0,3,2,3,2,1,0"); +#endif #else static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; module_param_array(sensor_vc, ushort, NULL, 0444); From 44e45c3359882a9fe57b25fc200de8ccd2012bea Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 27 May 2026 06:42:06 -0700 Subject: [PATCH 61/72] d4xx-max96724: modify d4xx sensors vc default assignment d4xx-max96724: fix aggregator assignement d4xx-max96724: fix legacy-mode pipe selection d4xx-max96724: force max96712 legacy-mode Signed-off-by: florent pirou --- Makefile | 2 +- drivers/media/i2c/d4xx-compat/d4xx-max96724.c | 214 +++++++++--------- drivers/media/i2c/d4xx-compat/d4xx.c | 24 +- 3 files changed, 118 insertions(+), 122 deletions(-) diff --git a/Makefile b/Makefile index 6f88255e..3efe0ad2 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ subdir-ccflags-$(CONFIG_VIDEO_ISX031) += -DCONFIG_VIDEO_ISX031 subdir-ccflags-$(CONFIG_VIDEO_AR0233) += -DCONFIG_VIDEO_AR0233 subdir-ccflags-$(CONFIG_VIDEO_D4XX_COMPAT) += -DCONFIG_VIDEO_D4XX subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) += -DCONFIG_VIDEO_D4XX_MAX9295_VC_EXT -subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) += -DCONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT +subdir-ccflags-$(CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT) += -DCONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT -DCONFIG_VIDEO_D4XX_MAX96712_LEGACY subdir-ccflags-$(CONFIG_VIDEO_AR0820) += -DCONFIG_VIDEO_AR0820 subdir-ccflags-$(CONFIG_VIDEO_AR0234) += -DCONFIG_VIDEO_AR0234 -DCONFIG_V4L2_CCI_I2C subdir-ccflags-$(CONFIG_IPU_BRIDGE) += -DCONFIG_IPU_BRIDGE diff --git a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c index a4f846cf..d3fea6f0 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx-max96724.c +++ b/drivers/media/i2c/d4xx-compat/d4xx-max96724.c @@ -1,5 +1,3 @@ - - // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017-2024, INTEL CORPORATION. All rights reserved. @@ -839,18 +837,6 @@ int max96724_check_status(struct device *dev, u32 src_link) priv->dst_n_lanes); */ - /* Turn on ALL link channels - err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, - ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); - if (err) - dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", - __func__, - err); - */ - /* Re-Check GMSL link status after initial configuration */ { unsigned int link_status = 0; @@ -963,7 +949,7 @@ int max96724_switch_link_channel(struct device *dev) mutex_unlock(&priv->lock); - msleep(50); // delay to settle link + msleep(5); // delay to settle link return err; } @@ -1729,7 +1715,7 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id, u32 src_link) mutex_lock(&priv->lock); for (i = 0; i < MAX96724_MAX_PIPES; i++) { #ifdef CONFIG_INTEL_IPU_VC_EXT - if (i == _vc_id && !priv->pipe[i].st_count) { + if (i == _vc_id && priv->pipe[i].st_count < 2) { #else if (i == vc_id && !priv->pipe[i].st_count) { #endif @@ -1740,20 +1726,6 @@ int max96724_get_available_pipe_id(struct device *dev, int vc_id, u32 src_link) } } - /* Turn on ALL link channels - err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, - ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); - if (err) - dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", - __func__, - err); - - msleep(50); // delay to settle link - */ - mutex_unlock(&priv->lock); return pipe_id; @@ -1767,8 +1739,15 @@ int max96724_release_pipe(struct device *dev, int pipe_id) if (pipe_id < 0 || pipe_id >= MAX96724_MAX_PIPES) return -EINVAL; + if (!priv->pipe[pipe_id].st_count) + return 0; + mutex_lock(&priv->lock); +#ifdef CONFIG_INTEL_IPU_VC_EXT + priv->pipe[pipe_id].st_count--; +#else priv->pipe[pipe_id].st_count = 0; +#endif mutex_unlock(&priv->lock); return 0; @@ -1916,6 +1895,7 @@ void max96724_reset_oneshot(struct device *dev, u32 src_link) err); /* Enable all channels + */ err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) @@ -1925,7 +1905,9 @@ void max96724_reset_oneshot(struct device *dev, u32 src_link) dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", __func__, err); - */ + + /* delay to settle link */ + msleep(100); } EXPORT_SYMBOL(max96724_reset_oneshot); @@ -1959,8 +1941,11 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT int _pipe_id = vc_id_3b_dst > 0 ? (MAX96724_MAX_PIPES - 1) - (vc_id % MAX96724_MAX_PIPES) : pipe_id; + int _fwd_id = link_id; + int _aggregator_id = csi_id; u8 en_mapping_num = 0xFF; #else + int _fwd_id = pipe_id; int _pipe_id = pipe_id; u8 en_mapping_num = 0x0F; #endif @@ -2038,12 +2023,6 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ src_link_st_count++; } - dev_info(dev, "%s: %s stream %u/%u active pipes\n", - __func__, - max96724_get_link_name(src_link), - src_link_st_count, - st_count); - struct reg_pair map_pipe_select[] = { {MAX96724_REG5_ADDR, 0x80}, // Enable lock {MAX96724_REG6_ADDR, 0xFF}, // Enable all GMSL input sources @@ -2225,19 +2204,23 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ #endif #ifdef CONFIG_INTEL_IPU_VC_EXT - dev_info(dev, "%s: %s pipe %u set on vc_ext_id=%u [vc_id_3bits: src:0x%x, dst:0x%x] \n", + dev_info(dev, "%s: %s pipe %u set on vc_ext_id=%u [vc_id_3bits: src:0x%x, dst:0x%x], %u/%u active pipes\n", __func__, max96724_get_link_name(src_link), _pipe_id, vc_id, vc_id_3b_src, - vc_id_3b_dst); + vc_id_3b_dst, + src_link_st_count, + st_count); #else - dev_info(dev, "%s: %s pipe %u set on vc_id=%u\n", + dev_info(dev, "%s: %s pipe %u set on vc_id=%u, %u/%u active pipes\n", __func__, max96724_get_link_name(src_link), _pipe_id, - vc_id_2b_lsb); + vc_id_2b_lsb, + src_link_st_count, + st_count); #endif /* ONLY on initial streaming, skip if CSI already streaming data @@ -2281,15 +2264,7 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ * Bit[3:2] = : Pipe 2 from GMSL A, B, C or D link * Bit[1:0] = 10: Pipe 2 uses internal Pipe U */ -#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) - /* - * REG 0x00F4: Enable/disable Video Pipes - * Bit[3:0] = : Enable Pipe X - */ - err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(0), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 0U)); -#else + /* Video Pipe Disable Selection * if same link source, disable all before re-nable one-by-one. * @@ -2312,14 +2287,20 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ err = MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), 0U)); -#endif + /* ONLY on initial streaming, skip if CSI already streaming data */ if (st_count < 2) err |= max96724_set_registers(dev, map_pipe_select, ARRAY_SIZE(map_pipe_select)); -#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) +#if !defined(CONFIG_VIDEO_D4XX_MAX96712) && !defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + // Enable specific pipe streams (X,Y,Z and/or U) source mapping + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, + MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, + MAX96724_VIDEO_PIPE_SRCMAP_MODE)); +#else // Enable max96712 "legacy" mode // Non "legacy" mode ignores pipe mapping, and selects all streams for pipe // 0. The ipu doesn't know what to do with that and throws spurious data @@ -2329,40 +2310,30 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, MAX96724_VIDEO_PIPE_LEGACY_MODE)); - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(0), - MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0) - | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(0), link_id) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(0), 0U)); - if (err) - dev_err(dev, "%s: Failed to select max96712 legacy mode: %d\n", - __func__, - err); - else - dev_dbg(dev, "%s: mapped max96712 legacy mode ALL pipe to streams (Pipe X Only) mode\n", - __func__); - -#else - // Enable specific pipe streams (X,Y,Z and/or U) source mapping -#if !defined(CONFIG_VIDEO_D4XX_MAX96712) - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, - MAX96724_VIDEO_PIPE_SRCMAP_MODE)); + //WA: override Multiple fwd-pipe source to a single video-pipe sink + _fwd_id = vc_id_2b_lsb; #endif err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_SEL(_pipe_id), MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(_pipe_id) | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(_pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(_pipe_id), link_id) - | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(_pipe_id), _pipe_id)); + | MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(_pipe_id), _fwd_id)); if (err) - dev_err(dev, "%s: Failed to select all streams (X,Y,Z and/or U) mode: %d\n", + dev_err(dev, "%s: Failed to select streams: %d\n", __func__, err); else - dev_dbg(dev, "%s: mapped max967xx pipe %u to streams (X,Y,Z and/or U) mode\n", +#if !defined(CONFIG_VIDEO_D4XX_MAX96712) && !defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + dev_dbg(dev, "%s: mapped max967xx link %c to video pipe %u (through all X,U,Z and U pipes)\n", __func__, + 'A' + link_id, _pipe_id); +#else + dev_dbg(dev, "%s: mapped max967xx link %c to video pipe %u (through %c input pipe)\n", + __func__, + 'A' + link_id, + _pipe_id, + _fwd_id == 3 ? 'U' : 'X' + _fwd_id); #endif #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT @@ -2382,25 +2353,62 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ err |= MAX96724_WRITE_REG(priv->regmap, MAX96724_MIPI_TX_ALT_MEM(csi_id), 0x10); - /* Configure per Pipe 0, 1, 2 and 3 MIPI interleaved-SYNC FCFS aggregation - * Aggregator A, B, C and D corresponds to GMSL A, B, C and D input src - */ #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT + // Trial #0 - uncomment to test single link-aggregator + + /* Trial #1 - uncomment to test two-alternative link-aggregation + if (vc_id_3b_dst > 0) { + _aggregator_id = 0U; + } else { + _aggregator_id = 1U; + } + */ + + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(_aggregator_id), + MAX96724_MAP_CON_SYNC_PIPE_0_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD + | MAX96724_MAP_CON_SYNC_PIPE_FIELD + | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, + MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) + | MAX96724_MAP_CON_SYNC_PIPE_MASTER(_pipe_id) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); + + /* Trial #2 - uncomment to test concatenation if (src_link_st_count < 2) - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(_pipe_id), - MAX96724_MAP_CON_SYNC_PIPE_0_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_1_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_2_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_3_FIELD - | MAX96724_MAP_CON_SYNC_PIPE_FIELD - | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, - MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_0_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_1_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_2_FIELD, 0U) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_3_FIELD, 0U) - | MAX96724_MAP_CON_SYNC_PIPE_MASTER(_pipe_id) - | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 0U)); + else + err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(_aggregator_id), + MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(_pipe_id) + | MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, + MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_PIPE_SEL_FIELD(_pipe_id), 1U) + | MAX96724_FIELD_PREP(MAX96724_MAP_CON_SYNC_EN_4WxH_FIELD, 1U)); + */ + +#if !defined(CONFIG_VIDEO_D4XX_MAX96712) && !defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) + dev_info(dev, "%s: mapped fwd-link %c through X,Y,Z & U input pipes\t->\tVideo pipe %u through Aggregator %c to CSI %u\n", + __func__, + 'A' + link_id, + _pipe_id, + 'A' + _aggregator_id, + csi_id); +#else + dev_info(dev, "%s: mapped fwd-link %c through %c input pipe\t->\tVideo pipe %u through Aggregator %c to CSI %u\n", + __func__, + 'A' + link_id, + _fwd_id == 3 ? 'U' : 'X' + _fwd_id, + _pipe_id, + 'A' + _aggregator_id, + csi_id); +#endif + #else + /* Configure per Pipe 0, 1, 2 and 3 MIPI interleaved-SYNC FCFS aggregation + * Aggregator A, B, C and D corresponds to GMSL A, B, C and D input src + */ if (src_link_st_count < 2) err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_MIPI_TX_MAP_CON(link_id), MAX96724_MAP_CON_SYNC_PIPE_0_FIELD @@ -2438,15 +2446,7 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ #endif | MAX96724_LANE_CTRL_MAP(priv->dst_n_lanes-1)); -#if defined(CONFIG_VIDEO_D4XX_MAX96712_LEGACY) - /* - * REG 0x00F4: Enable/disable Video Pipes - * Bit[3:0] = : Enable Pipe X - */ - err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, - MAX96724_VIDEO_PIPE_EN_FIELD(0), - MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(0), 1U)); -#else + /* Video Pipe Enable Selection * if same link source activate at least twice, disable all concurrent streams before re-nable one-by-one. * @@ -2469,7 +2469,7 @@ static int __max96724_set_pipe_d4xx(struct device *dev, int pipe_id, u8 data_typ err |= MAX96724_UPDATE_BITS(priv->regmap, MAX96724_VIDEO_PIPE_EN_ADDR, MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), MAX96724_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(_pipe_id), 1U)); -#endif + if (err) { dev_err(dev, "%s: Failed to configure pipe control %u %s %u lanes: %d\n", __func__, @@ -2773,18 +2773,6 @@ int max96724_set_pipe(struct device *dev, int pipe_id, if (st_count > 1) goto status; - /* Enable all channels - err = MAX96724_WRITE_REG(priv->regmap, MAX96724_REM_CC, - ~(MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(0, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(1, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(2, 0), 1U) - | MAX96724_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(3, 0), 1U))); - if (err) - dev_err(dev, "%s: Failed to switch ALL link i2c channel: %d\n", - __func__, - err); - */ - /* CRITICAL: DO NOT enable CSI_OUT_EN (0x040b) here! * * Reference script shows CSI should be enabled LAST, after: diff --git a/drivers/media/i2c/d4xx-compat/d4xx.c b/drivers/media/i2c/d4xx-compat/d4xx.c index 03434d35..e92abdfa 100644 --- a/drivers/media/i2c/d4xx-compat/d4xx.c +++ b/drivers/media/i2c/d4xx-compat/d4xx.c @@ -1862,9 +1862,13 @@ static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, { int ret = 0; dev_warn(&state->client->dev, - "set pipe %d, data_type1: 0x%x, \ - data_type2: 0x%x, vc_id: %u\n", - pipe_id, data_type1, data_type2, vc_id); +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && !defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + "set pipe %d, data_type1: 0x%x, data_type2: 0x%x, vc_ext_id: %u (vc_ds5_id: %u)\n", + pipe_id, data_type1, data_type2, vc_id, (vc_id % NR_OF_DS5_STREAMS)); +#else + "set pipe %d, data_type1: 0x%x, data_type2: 0x%x, vc_id: %u\n", + pipe_id, data_type1, data_type2, vc_id); +#endif #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) ret = max96724_switch_link_channel(state->dser_dev); ret |= max9295_set_pipe(state->ser_dev, pipe_id, @@ -1976,7 +1980,11 @@ static int ds5_configure(struct ds5 *state) if (ret < 0) return ret; +#if (defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712)) && !defined(CONFIG_VIDEO_D4XX_MAX9295_VC_EXT) + ret = ds5_write(state, md_addr, ((vc_id % NR_OF_DS5_STREAMS) << 8) | md_fmt); +#else ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); +#endif if (ret < 0) return ret; @@ -2601,12 +2609,12 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s, value: %d\n", __func__, ds5_get_sensor_name(state), ctrl->name, ctrl->val); - mutex_lock(&state->lock); - #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) - //ret = max96724_switch_link_channel(state->dser_dev); + ret = max96724_switch_link_channel(state->dser_dev); #endif + mutex_lock(&state->lock); + switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: ret = ds5_write(state, base | DS5_MANUAL_GAIN, ctrl->val); @@ -4070,14 +4078,14 @@ static int ds5_gmsl_serdes_setup(struct ds5 *state) #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) || IS_ENABLED(CONFIG_VIDEO_INTEL_IPU7) #if defined(CONFIG_VIDEO_D4XX_MAX96724) || defined(CONFIG_VIDEO_D4XX_MAX96712) #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT -static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,7,2,5, 2,5,0,7, 1,6,3,4, 3,4,1,6}; +static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,6,5,3, 1,4,2,7, 2,7,4,1, 3,5,0,6}; #else static short sensor_vc[NR_OF_DS5_STREAMS * 4] = {0,1,2,3, 2,3,0,1, 1,0,3,2, 3,2,1,0}; #endif module_param_array(sensor_vc, ushort, NULL, 0444); MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" #ifdef CONFIG_VIDEO_D4XX_MAX967XX_DT_VC_EXT - "\t\tsensor_vc=0,7,2,5,2,5,0,7,1,6,3,4,3,4,1,6"); + "\t\tsensor_vc=0,6,5,3,1,4,2,7,2,7,4,1,3,5,0,6"); #else "\t\tsensor_vc=0,1,2,3,2,3,0,1,1,0,3,2,3,2,1,0"); #endif From 5c62dab045213257480b7b7f09c172c120b62afa Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 12 Feb 2026 16:22:18 +0000 Subject: [PATCH 62/72] helpers: add v4l2 media controller ipu helper scripts Signed-off-by: florent pirou --- helpers/99-ipu-gmsl-mipi.rules | 21 ++++ helpers/blacklist-intel-ipu.conf | 7 ++ helpers/ipu_max9x_bind.sh | 188 +++++++++++++++++++++++++++++++ helpers/ipu_ops.conf | 5 + 4 files changed, 221 insertions(+) create mode 100755 helpers/99-ipu-gmsl-mipi.rules create mode 100644 helpers/blacklist-intel-ipu.conf create mode 100755 helpers/ipu_max9x_bind.sh create mode 100644 helpers/ipu_ops.conf diff --git a/helpers/99-ipu-gmsl-mipi.rules b/helpers/99-ipu-gmsl-mipi.rules new file mode 100755 index 00000000..2380e500 --- /dev/null +++ b/helpers/99-ipu-gmsl-mipi.rules @@ -0,0 +1,21 @@ +# Device rules for 3rd party GMSL2 ISX031 IMX390 and AR0234 camera modules. +# video links for SDK, binding for ipu7 or ipu6 + +SUBSYSTEM=="video4linux", ATTR{name}=="i2c-isx031-*", RUN+="/bin/bash -c '/usr/share/camera/ipu_max9x_bind.sh -s isx031 -n > /dev/kmsg'" +SUBSYSTEM=="video4linux", ATTR{name}=="i2c-imx390-*", RUN+="/bin/bash -c '/usr/share/camera/ipu_max9x_bind.sh -s imx390 -n > /dev/kmsg'" +SUBSYSTEM=="video4linux", ATTR{name}=="i2c-ar0234-*", RUN+="/bin/bash -c '/usr/share/camera/ipu_max9x_bind.sh -s ar0234 -n > /dev/kmsg'" + +# TEMPORARY : Device rules for Intel RealSense MIPI devices. +# DFU rules +# SUBSYSTEM=="d4xx-class", KERNEL=="d4xx-dfu*", GROUP="video", MODE="0660" + +# video links for SDK, binding for ipu7 +#SUBSYSTEM=="video4linux", ATTR{name}=="DS5 mux *", RUN+="/bin/bash -c '/usr/share/camera/rs_ipu_d457_bind.sh -n > /dev/kmsg; /usr/share/camera/rs-enum-ipu.sh -n -q > /dev/kmsg'" + +# default max96724 gmsl link_freq override for PTL ipu7 DWC DPHY +# (e.g .used udevadm info -a -p $(udevadm info -q path -n $(media-ctl -e "i2c-isx031-a-0")) ) +#SUBSYSTEM=="video4linux", ATTR{name}=="i2c-isx031-*", RUN+="/usr/bin/v4l2-ctl --set-ctrl v4l2_cid_link_freq=1 -d '%E{DEVNAME}'" +#SUBSYSTEM=="video4linux", ATTR{name}=="i2c-imx390-*", RUN+="/usr/bin/v4l2-ctl --set-ctrl v4l2_cid_link_freq=1 -d '%E{DEVNAME}'" +#SUBSYSTEM=="video4linux", ATTR{name}=="i2c-ar0234-*", RUN+="/usr/bin/v4l2-ctl --set-ctrl v4l2_cid_link_freq=1 -d '%E{DEVNAME}'" +# (e.g .used udevadm info -a -p $(udevadm info -q path -n $(media-ctl -e "DS5 mux a-0")) ) +#SUBSYSTEM=="video4linux", ATTR{name}=="DS5 mux *", RUN+="/usr/bin/v4l2-ctl --set-ctrl v4l2_cid_link_freq=1 -d '%E{DEVNAME}'" \ No newline at end of file diff --git a/helpers/blacklist-intel-ipu.conf b/helpers/blacklist-intel-ipu.conf new file mode 100644 index 00000000..f59cf472 --- /dev/null +++ b/helpers/blacklist-intel-ipu.conf @@ -0,0 +1,7 @@ +# avpoid intel CCG & Canonical MIPI-driect only kernel builtin IPU6 / IPU7 clashes with GMSL2 enabled intel-ipu-dkms +blacklist intel_ipu7_isys +blacklist intel_ipu7_psys +blacklist intel_ipu7 +blacklist intel_ipu6_isys +blacklist intel_ipu6_psys +blacklist intel_ipu6 diff --git a/helpers/ipu_max9x_bind.sh b/helpers/ipu_max9x_bind.sh new file mode 100755 index 00000000..606b4e68 --- /dev/null +++ b/helpers/ipu_max9x_bind.sh @@ -0,0 +1,188 @@ +#!/bin/bash +# +# Dependency: v4l-utils +v4l2_util=$(which v4l2-ctl) +media_util=$(which media-ctl) +if [ -z ${v4l2_util} ]; then + echo "v4l2-ctl not found, install with: sudo apt install v4l-utils" + exit 1 +fi +while [[ $# -gt 0 ]]; do + case $1 in + -q|--quiet) + quiet=1 + shift + ;; + -m|--mux) + shift + if [ ${#1} -eq 3 ]; then + mux_param=$1 + fi + shift + ;; + -s|--sensor) + sensor=$2 + shift + ;; + -h|--help) + echo "-q -m -h" + ;; + -f) + fmt="$2" + shift + ;; + *) + quiet=0 + shift + ;; + esac +done + +sensor="${sensor:-isx031}" +if [ ${sensor} = "isx031" ]; then + fmt="${fmt:-[fmt:UYVY8_1X16/1920x1536]}" + # fmt="${fmt:-[fmt:UYVY8_1X16/1600x1280]}" + # fmt="${fmt:-[fmt:UYVY8_1X16/1024x768]}" +elif [ ${sensor} = "imx390" ]; then + fmt="${fmt:-[fmt:SGRBG12_1X12/1920x1200]}" + # fmt="${fmt:-[fmt:SRGGB12_1X12/1936x1096]}" + # fmt="${fmt:-[fmt:SRGGB12_1X12/1280x720]}" + # fmt="${fmt:-[fmt:SRGGB12_1X12/1024x768]}" +elif [ ${sensor} = "ar0234" ]; then + fmt="${fmt:-[fmt:SGRBG10_1X10/1280x960]}" +fi + +declare entities=() +while IFS= read -r line; do + entities+=("$line") +done < <(media-ctl -p | grep entity | sed -e 's/.*: //;s/ (.*$//') + +declare -A mux_to_index=([a]=0 [b]=1 [c]=2 [d]=3) + +find_entity() { + local name=$1 + for e in "${entities[@]}"; do + if [[ "${e}" = *"${name}"* ]]; then + echo -n "${e}" + return + fi + done + #echo "$1 not found" >&2 + exit 1 +} + +des_node() { + local mux=$1 + case ${mux} in + a-0|b-0|c-0|d-0) echo -n "max9x a";; + a-1|b-1|c-1|d-1) echo -n "max9x b";; + a-2|b-2|c-2|d-2) echo -n "max9x c";; + a-3|b-3|c-3|d-3) echo -n "max9x d";; + a-4|b-4|c-4|d-4) echo -n "max9x e";; + a-5|b-5|c-5|d-5) echo -n "max9x f";; + esac +} +des_src_pad() { + local mux=$1 + echo -n "\"$(des_node ${mux})\":0" +} +des_sink_pad() { + local mux=$1 + echo -n "\"$(des_node ${mux})\":$(( 4 + ${mux_to_index[${mux:0:1}]} ))" +} + +ser_node() { + local mux=$1 + find_entity "max9x ${mux}" +} +ser_src_pad() { + local mux=$1 + echo -n "\"$(ser_node ${mux})\":2" +} +ser_sink_pad() { + local mux=$1 + echo -n "\"$(ser_node ${mux})\":0" +} + +sen_node() { + local mux=$1 + find_entity "${sensor} ${mux}" +} +sen_src_pad() { + local mux=$1 + echo -n "\"$(sen_node ${mux})\":0" +} + +# mapping for ISX031/IMX390/AR0234 max9x entity to IPU7|IPU6 CSI-2 entity matching. +declare -A media_mux_capture_link=( [0]=0 [1]=16 [2]=32 [3]=48 [4]=64 [5]=80) + +declare -A media_mux_capture_pad=( + [a]=0 + [b]=1 + [c]=2 + [d]=3 +) + +# all available ISX031/IMX390/AR0234 max9x entity, each one represent physically connected camera. +# muxes prefix a, b, c, d referes to max96724 or max9292 aggregated link cameras +# muxes suffix 0, 1, 2, 3, 4, 5 is referes to csi2 mipi port mapping +mux_list=${mux_param:-'a-0 b-0 c-0 d-0 a-1 b-1 c-1 d-1 a-2 b-2 c-2 d-2 a-3 b-3 c-3 d-3 a-4 b-4 c-4 d-4 a-5 b-5 c-5 d-5'} + +# Find media device. +# For case with usb camera plugged in during the boot, +# usb media controller will occupy index 0 +mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media) +[[ -z "${mdev}" ]] && exit 0 + +# Find IPU PCI device gen name +cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6|7]\).*/\1/' | tr '[:lower:]' '[:upper:]') +[[ -z "${cap_prefix}" ]] && exit 0 + +out() { + echo -n "${@} " >&2 + "${@}" + echo " RET=$?" >&2 +} + +media_ctl_cmd="${media_util} -d ${mdev}" +media-ctl -r # <- this can be used to clean-up all bindings from media controller +# cache media-ctl output +dot=$($media_ctl_cmd --print-dot) + +# loop over all available IMX390/ISX031/AR0234 max9x, each one represent physically connected camera. +for camera in $mux_list; do + e="$(sen_node ${camera})" + if [ -z "${e}" ]; then + continue; + fi + + [[ $quiet -eq 0 ]] && echo "Bind $cap_prefix to ${sensor} ${camera} through max9x serdes .. " >&2 + + csi2="$((${camera:2:1}))" + mux=${camera:0:1} + + # mapping for MAX9x mux entity to IPU7|IPU6 ISYS matching entity. + cap_pad="${media_mux_capture_pad[${mux}]}" + isys_cap="$((${media_mux_capture_link[${csi2}]}+${cap_pad}))" + vc_id=$((${cap_pad} % 4)) + + out $media_ctl_cmd -l "$(des_src_pad ${camera}) -> \"Intel ${cap_prefix} CSI2 ${csi2}\":0[1]" + out $media_ctl_cmd -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+1)) -> \"Intel ${cap_prefix} ISYS Capture ${isys_cap}\":0[1]" + + # subdev entity '['pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ']' ; + out $media_ctl_cmd -R "\"$(des_node ${camera})\"[4/0->0/0[1], 5/1->0/1[1], 6/2->0/2[1], 7/3->0/3[1]]" + out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[0/0->1/0[1], 0/1->2/1[1], 0/2->3/2[1], 0/3->4/3[1]]" + + out $media_ctl_cmd -V "$(sen_src_pad ${camera}) ${fmt}" + out $media_ctl_cmd -V "$(ser_sink_pad ${camera}) ${fmt}" + out $media_ctl_cmd -V "$(ser_src_pad ${camera}) ${fmt}" + out $media_ctl_cmd -V "$(des_sink_pad ${camera})/${vc_id} ${fmt}" + out $media_ctl_cmd -V "$(des_src_pad ${camera})/${vc_id} ${fmt}" + + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/${vc_id} ${fmt}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+1))/${vc_id} ${fmt}" + + cap_dev=$($media_ctl_cmd -e "Intel ${cap_prefix} ISYS Capture ${isys_cap}") + out ln -snf ${cap_dev} /dev/video-${sensor}-${camera} + +done diff --git a/helpers/ipu_ops.conf b/helpers/ipu_ops.conf new file mode 100644 index 00000000..6b8564b9 --- /dev/null +++ b/helpers/ipu_ops.conf @@ -0,0 +1,5 @@ +options intel-ipu6 isys_freq_override=400 ipu_trace_enable=Y +options intel-ipu6-isys csi2_port_optimized=n +options intel-ipu6-psys enable_power_gating=n +#options intel-ipu7-isys dyndbg +#options intel-ipu7 dyndbg From a104e9f1490c1bed44ed5f50d254653caac15255 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Fri, 13 Feb 2026 09:48:19 +0000 Subject: [PATCH 63/72] helpers: bind d4xx either from ipu6 or ipu7, extend ipu6 upto csi port 5 * assume d457 fw 5.16.0 or higher by default * fix d4xx binding and v4l2 streamid mapping * fix rs-depth Z16 pixel-format error * add d4xx to udev rules * d4xx bind media controller reset * force d4xx isys vidcap in video group * add d4xx module param max96724 csi mapping * derive stream sink and source from isys pad index * workaround csi subdev sink stream d4xx format mismatch * fix d4xx C and D set_fmt * manage inactive routes during binding * helper: ignore pad fmt init on d4x inactive routes * helper: activate all sensor routes for d4xx single camera binding * helpers: detect d4xx vidcap with inactive routes * helpers: fix d4xx media-ctl link binding syntaxe * helpers: workaround the GMSL D eight active route per-csi2 limit * helpers: bind all d4xx sensors if singular camera * helpers: add d4xx modprobe configs extra examples Signed-off-by: florent pirou --- helpers/99-ipu-gmsl-mipi.rules | 4 +- helpers/d4xx.conf | 28 ++++ helpers/ipu_ops.conf | 6 + helpers/rs-enum-ipu.sh | 225 +++++++++++++++++++++++++ helpers/rs_ipu_d457_bind.sh | 290 +++++++++++++++++++++++++++++++++ 5 files changed, 551 insertions(+), 2 deletions(-) create mode 100644 helpers/d4xx.conf create mode 100755 helpers/rs-enum-ipu.sh create mode 100755 helpers/rs_ipu_d457_bind.sh diff --git a/helpers/99-ipu-gmsl-mipi.rules b/helpers/99-ipu-gmsl-mipi.rules index 2380e500..7a8c099f 100755 --- a/helpers/99-ipu-gmsl-mipi.rules +++ b/helpers/99-ipu-gmsl-mipi.rules @@ -7,10 +7,10 @@ SUBSYSTEM=="video4linux", ATTR{name}=="i2c-ar0234-*", RUN+="/bin/bash -c '/usr/s # TEMPORARY : Device rules for Intel RealSense MIPI devices. # DFU rules -# SUBSYSTEM=="d4xx-class", KERNEL=="d4xx-dfu*", GROUP="video", MODE="0660" +SUBSYSTEM=="d4xx-class", KERNEL=="d4xx-dfu*", GROUP="video", MODE="0660" # video links for SDK, binding for ipu7 -#SUBSYSTEM=="video4linux", ATTR{name}=="DS5 mux *", RUN+="/bin/bash -c '/usr/share/camera/rs_ipu_d457_bind.sh -n > /dev/kmsg; /usr/share/camera/rs-enum-ipu.sh -n -q > /dev/kmsg'" +SUBSYSTEM=="video4linux", ATTR{name}=="DS5 mux *", RUN+="/bin/bash -c '/usr/share/camera/rs_ipu_d457_bind.sh -n > /dev/kmsg; /usr/share/camera/rs-enum-ipu.sh -n -q > /dev/kmsg'" # default max96724 gmsl link_freq override for PTL ipu7 DWC DPHY # (e.g .used udevadm info -a -p $(udevadm info -q path -n $(media-ctl -e "i2c-isx031-a-0")) ) diff --git a/helpers/d4xx.conf b/helpers/d4xx.conf new file mode 100644 index 00000000..179fa9eb --- /dev/null +++ b/helpers/d4xx.conf @@ -0,0 +1,28 @@ +# uncomment d4xx driver boot options corresponding to your Intel platform +# +# MTL GMSL2 AIC max9296 device generally on i2c-2 and i2c-3 buses +#options d4xx des_addr=0x48,0x4a,0x48,0x4a serdes_bus=2,2,3,3 +# ARL GMSL2 AIC max9296 device generally on i2c-0 and i2c-3 buses +#options d4xx des_addr=0x48,0x4a,0x48,0x4a serdes_bus=0,0,3,3 +# ADL/RPL/ASL GMSL2 AIC max9296 device generally on i2c-4 bus +#options d4xx des_addr=0x48,0x4a,0x68,0x6c serdes_bus=4,4,4,4 +# ARL GMSL2 AIC dual-max96712 device generally on i2c-0 bus +#options d4xx des_addr=0x6b,0x6b,0x29,0x29 serdes_bus=0,0,0,0 + +# PTL GMSL2 AIC dual-max96724 device generally on i2c-0 and i2c-1 buses +options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 +# PTL GMSL2 AIC dual-max96724 device generally on i2c-0 and i2c-1 buses (with VC_EXTENSION enabled) +# try #1 - RGB+DEPTH on the same video pipes +#options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 sensor_vc=0,7,1,6,1,6,0,7,2,5,3,4,3,4,2,5 +# try #2 - RGB+DEPTH on the distinct video pipes +#options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 sensor_vc=0,6,5,3,1,4,2,7,2,7,4,1,3,5,0,6 +# !!WARNING!! Intel Servoss Mountain 1.0 (Fab A) dual-max96724 board cphy CRD connectivity is inverted +# max96724 csi 0 CPHY-2T DA0/DA1 maps to ipu7 isys csi 2 +# max96724 csi 2 CPHY-2T DB0/DB1 maps to ipu7 isys csi 0 +#options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=1,1,2,2 des_csi=2,2,0,0 +# + +#options d4xx dyndbg +#options d4xx-max9295 dyndbg +#options d4xx-max9296 dyndbg +#options d4xx-max96724 dyndbg diff --git a/helpers/ipu_ops.conf b/helpers/ipu_ops.conf index 6b8564b9..5ebd5b45 100644 --- a/helpers/ipu_ops.conf +++ b/helpers/ipu_ops.conf @@ -1,5 +1,11 @@ options intel-ipu6 isys_freq_override=400 ipu_trace_enable=Y options intel-ipu6-isys csi2_port_optimized=n options intel-ipu6-psys enable_power_gating=n +#options intel-ipu7-psys dyndbg #options intel-ipu7-isys dyndbg #options intel-ipu7 dyndbg +#options intel-ipu6-psys dyndbg +#options intel-ipu6-isys dyndbg +#options intel-ipu6 dyndbg +options ipu_acpi dyndbg +options ipu_acpi_pdata dyndbg diff --git a/helpers/rs-enum-ipu.sh b/helpers/rs-enum-ipu.sh new file mode 100755 index 00000000..e9b68e07 --- /dev/null +++ b/helpers/rs-enum-ipu.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# Create symbolic links for video nodes and for metadata nodes - /dev/video-rs-[|-md]-[camera-index] +# This script intended for mipi devices on IPU7. +# After running this script in enumeration mode, it will create links as follow for example: +# Example of the output: +# +# Pantherlake: +#$ ./rs-enum.sh +#Bus Camera Sensor Node Type Video Node RS Link +# ipu7 0 depth Streaming /dev/video4 /dev/video-rs-depth-a-0 +# ipu7 0 depth Metadata /dev/video5 /dev/video-rs-depth-md-a-0 +# ipu7 0 ir Streaming /dev/video8 /dev/video-rs-ir-a-0 +# ipu7 0 imu Streaming /dev/video9 /dev/video-rs-imu-a-0 +# ipu7 0 color Streaming /dev/video6 /dev/video-rs-color-a-0 +# ipu7 0 color Metadata /dev/video7 /dev/video-rs-color-md-a-0 +# i2c 0 d4xx Firmware /dev/d4xx-dfu-a /dev/d4xx-dfu-a-0 +# ipu7 2 depth Streaming /dev/video36 /dev/video-rs-depth-a-2 +# ipu7 2 depth Metadata /dev/video37 /dev/video-rs-depth-md-a-2 +# ipu7 2 ir Streaming /dev/video40 /dev/video-rs-ir-a-2 +# ipu7 2 imu Streaming /dev/video41 /dev/video-rs-imu-a-2 +# ipu7 2 color Streaming /dev/video38 /dev/video-rs-color-a-2 +# ipu7 2 color Metadata /dev/video39 /dev/video-rs-color-md-a-2 +# i2c 2 d4xx Firmware /dev/d4xx-dfu-c /dev/d4xx-dfu-a-2 + +# Dependency: v4l-utils +v4l2_util=$(which v4l2-ctl) +media_util=$(which media-ctl) +if [ -z ${v4l2_util} ]; then + echo "v4l2-ctl not found, install with: sudo apt install v4l-utils" + exit 1 +fi +metadata_enabled=1 +# +# parse command line parameters +# for '-i' parameter, print links only +while [[ $# -gt 0 ]]; do + case $1 in + -i|--info) + info=1 + shift + ;; + -q|--quiet) + quiet=1 + shift + ;; + -m|--mux) + shift + if [ ${#1} -eq 3 ]; then + mux_param=$1 + fi + shift + ;; + -n|--no-metadata) + metadata_enabled=0 + shift + ;; + *) + info=0 + quiet=0 + shift + ;; + esac +done +#set -x +if [[ $info -eq 0 ]]; then + if [ "$(id -u)" -ne 0 ]; then + echo "Please run as root." >&2 + exit 1 + fi +fi + + +# all available DS5 muxes, each one represent physically connected camera. +# muxes prefix a, b, c, d referes to max96724 aggregated link cameras +# muxes suffix 0, 1, 2, 3, 4, 5 is referes to csi2 mipi port mapping + +mux_list=${mux_param:-'a-0 b-0 c-0 d-0 a-1 b-1 c-1 d-1 a-2 b-2 c-2 d-2 a-3 b-3 c-3 d-3 a-4 b-4 c-4 d-4 a-5 b-5 c-5 d-5'} + +# mapping for DS5 mux entity to IPU6/IPU7 CSI-2 entity matching. +declare -A camera_idx=( [a]=0 [b]=1 [c]=2 [d]=3 ) +declare -A mipi_csi2_idx=( [a]=0 [b]=0 [c]=0) +declare -A d4xx_vc_named=([depth]=1 [rgb]=3 [ir]=5 [imu]=6) +declare -A camera_names=( [depth]=depth [rgb]=color [ir]=ir [imu]=imu ) + +camera_vid=("depth" "depth-md" "color" "color-md" "ir" "ir-md" "imu") + +#IPU6 or IPU7 ISYS +mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media) +cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6|7]\).*/\1/' | tr '[:lower:]' '[:upper:]') + +if [ -n "${mdev}" ]; then +[[ $quiet -eq 0 ]] && printf "Bus\tCamera\tSensor\tNode Type\tVideo Node\tRS Link\n" +# cache media-ctl output +dot=$(${media_util} -d ${mdev} --print-dot | grep -v dashed) +# for all d457 muxes a-0, b-0, c-0, a-1, b-1, c-1, a-2, b-2, c-2, +for camera in $mux_list; do + create_dfu_dev=0 + update_mux_link_freq=0 + csi2="$((${camera:2:1}))" + mux=${camera:0:1} + suffix="$((${camera_idx[${mux}]}+(4*${csi2})))" + vpad=0 + [[ "${mux}" == "b" ]] && vpad=$((${vpad}+6)) + [[ "${mux}" == "c" ]] && vpad=$((${vpad}+1)) + [[ "${mux}" == "d" ]] && vpad=$((${vpad}+7)) + + for sens in "${!d4xx_vc_named[@]}"; do + # get sensor binding from media controller + d4xx_sens=$(echo "${dot}" | grep "D4XX $sens $camera" | awk '{print $1}') + + [[ -z $d4xx_sens ]] && continue; # echo "SENS $sens NOT FOUND" && continue + + if [ "${mux}" \= "c" ]; then + # remap IR amd IMU suffix c to CSI2 input 13 and 14 + [[ ${camera_names["${sens}"]} == 'depth' ]] && vpad=1 + [[ ${camera_names["${sens}"]} == 'color' ]] && vpad=1 + [[ ${camera_names["${sens}"]} == 'ir' ]] && vpad=$((${vpad}+7)) + [[ ${camera_names["${sens}"]} == 'imu' ]] && vpad=$((${vpad}+7)) + #echo "${camera_names["${sens}"]}:${d4xx_vc_named[${sens}]} -> $((${d4xx_vc_named[${sens}]}+${vpad}))" + fi + + if [ "${mux}" \= "d" ]; then + # remap IR amd IMU suffix d to CSI2 input 15 and 16 + [[ ${camera_names["${sens}"]} == 'depth' ]] && vpad=7 + [[ ${camera_names["${sens}"]} == 'color' ]] && vpad=7 + [[ ${camera_names["${sens}"]} == 'ir' ]] && vpad=$((${vpad}+3)) + [[ ${camera_names["${sens}"]} == 'imu' ]] && vpad=$((${vpad}+3)) + #echo "${camera_names["${sens}"]}:${d4xx_vc_named[${sens}]} -> $((${d4xx_vc_named[${sens}]}+${vpad}))" + fi + + d4xx_sens_mux=$(echo "${dot}" | grep $d4xx_sens:port0 | awk '{print $3}' | awk -F':' '{print $1}') + csi2=$(echo "${dot}" | grep $d4xx_sens_mux:port0 | awk '{print $3}' | awk -F':' '{print $1}') + vid_nd=$(echo "${dot}" | grep -w "$csi2:port$((${d4xx_vc_named[${sens}]}+${vpad}))" | awk '{print $3}' | awk -F':' '{print $1}') + [[ -z $vid_nd ]] && continue; # echo "SENS $sens NOT FOUND" && continue + + vid=$(echo "${dot}" | grep "${vid_nd}" | grep video | tr '\\n' '\n' | grep video | awk -F'"' '{print $1}') + [[ -z $vid ]] && continue; + dev_entity=$(echo "${dot}" | grep "${vid_nd}" | grep label | awk -F'"' '{print $2}' | awk -F'/dev' '{print $1}' | awk -F'\' '{print $1}') + csi2_pad=$(${media_util} -d ${mdev} -e "${dev_entity}" -p | awk -F'<-' '{print $2}' | awk -F'[' '{print $1}' ) + csi2_entity=$(echo $csi2_pad | awk -F':' '{print $1}' | tr -d '"') + csi2_stream=$(($(echo $csi2_pad | awk -F':' '{print $2}')-1)) + dev_ln="/dev/video-rs-${camera_names["${sens}"]}-${suffix}" + dev_name=$(${v4l2_util} -d $vid -D | grep "Card type" | awk -F':' '{print $2}') + is_route_disabled=$(${media_util} -d ${mdev} -e "${csi2_entity}" -p | grep "0/${csi2_stream} ->" | grep INACTIVE) + if [[ -z ${is_route_disabled} ]]; then + link_state="Stream(id=$csi2_stream)" + else + link_state="Inactive" + fi + mux_nd=$(echo "${dot}" | grep -w "$d4xx_sens_mux:port1" | awk '{print $3}' | awk -F':' '{print $1}') + dev_mux=$(echo "${dot}" | grep "${mux_nd}" | grep subdev | tr '\\n' '\n' | grep subdev | awk -F'"' '{print $1}' | awk -F'|' '{print $1}') + + [[ $quiet -eq 0 ]] && printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$dev_name" ${camera} ${camera_names["${sens}"]} $link_state $vid $dev_ln + + # create link only in case we choose not only to show it + if [[ $info -eq 0 ]]; then + [[ -e $dev_ln ]] && unlink $dev_ln + ln -s $vid $dev_ln + + # change group + chown root:video $vid + + # enable ipu7 link enumeration feature + ${v4l2_util} -d $dev_ln -c enumerate_graph_link=1 + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'depth' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='Z16 ' + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'color' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='YUYV' + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'ir' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='Y8I ' + # For fimware version starting from: 5.16, + # IMU will have 32bit axis values. + # 5.16.x.y = firmware version: 0x0510 + # state->fw_version < 0x510 + #[[ -e $dev_ln && ${camera_names["${sens}"]} == 'imu' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=32,height=1,pixelformat='GREY' + # state->fw_version >= 0x510 + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'imu' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=38,height=1,pixelformat='GREY' + fi + + update_mux_link_freq=1 # will update link freq for camera + create_dfu_dev=1 # will create DFU device link for camera + + # metadata link + if [ "$metadata_enabled" -eq 0 ]; then + continue; + fi + # skip IR metadata node for now. + [[ ${camera_names["${sens}"]} == 'ir' ]] && continue + # skip IMU metadata node. + [[ ${camera_names["${sens}"]} == 'imu' ]] && continue + # skip metadata node for c and d + [[ "${mux}" == "c" ]] && continue + [[ "${mux}" == "d" ]] && continue + + vid_num=$(echo $vid | grep -o '[0-9]\+') + dev_md_ln="/dev/video-rs-${camera_names["${sens}"]}-md-${suffix}" + dev_name=$(${v4l2_util} -d "/dev/video$(($vid_num+1))" -D | grep Model | awk -F':' '{print $2}') + + [[ $quiet -eq 0 ]] && printf '%s\t%s\t%s\tMetadata\t/dev/video%s\t%s\n' "$dev_name" ${camera} ${camera_names["${sens}"]} $(($vid_num+1)) $dev_md_ln + # create link only in case we choose not only to show it + if [[ $info -eq 0 ]]; then + [[ -e $dev_md_ln ]] && unlink $dev_md_ln + ln -s "/dev/video$(($vid_num+1))" $dev_md_ln + ${v4l2_util} -d $dev_md_ln -c enumerate_graph_link=3 + fi + + done + + # update link_freq for D457 camera + if [[ ${update_mux_link_freq} -eq 1 ]]; then + ${v4l2_util} -d $dev_mux --set-ctrl v4l2_cid_link_freq=6 + fi + + # DFU device link for camera + if [[ ${create_dfu_dev} -eq 1 ]]; then + dev_dfu_name="/dev/d4xx-dfu-${camera}" + dev_dfu_ln="/dev/d4xx-dfu-${suffix}" + if [[ $info -eq 0 ]]; then + [[ -L "$dev_dfu_ln" ]] && unlink $dev_dfu_ln + ln -s $dev_dfu_name $dev_dfu_ln + fi + [[ $quiet -eq 0 ]] && printf '%s\t%s\t%s\tFirmware \t%s\t%s\n' " i2c " ${camera} "d4xx " $dev_dfu_name $dev_dfu_ln + fi + +done +fi +# end of file + diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh new file mode 100755 index 00000000..322eed20 --- /dev/null +++ b/helpers/rs_ipu_d457_bind.sh @@ -0,0 +1,290 @@ +#!/bin/bash +# file: rs_ipu7_d457_bind.sh +# This script used for binding media links of v4l2 subdevice entities +# The configuration we had is DS5 mux and IPU7 entities. +# _______ _______ _______ _________ +# |depth|->| mux |->|CSI-2|->|capture| +# |_____| |_____| |_____| |_______| +# +# Full graph can be viewed on http://magjac.com/graphviz-visual-editor/ +# media graph links can be generated by running 'media-ctl --print-dot' +# +# Dependency: v4l-utils +v4l2_util=$(which v4l2-ctl) +media_util=$(which media-ctl) +if [ -z ${v4l2_util} ]; then + echo "v4l2-ctl not found, install with: sudo apt install v4l-utils" + exit 1 +fi +# enable metadata capture nodes for default +metadata_enabled=1 +while [[ $# -gt 0 ]]; do + case $1 in + -q|--quiet) + quiet=1 + shift + ;; + -m|--mux) + shift + if [ ${#1} -eq 3 ]; then + mux_param=$1 + fi + shift + ;; + -n|--no-metadata) + metadata_enabled=0 + shift + ;; + -h|--help) + echo "-n OR -n -m " + exit 1 + ;; + *) + quiet=0 + shift + ;; + esac +done + +# mapping for DS5 mux entity to IPU6 CSI-2 entity matching. +declare -A media_mux_capture_link=( [0]=0 [1]=16 [2]=32 [3]=48 [4]=64 [5]=80) + +# all available DS5 muxes, each one represent physically connected camera. +# muxes prefix a, b, c, d referes to max96724 aggregated link cameras +# muxes suffix 0, 1, 2, 3, 4, 5 is referes to csi2 mipi port mapping + +mux_list=${mux_param:-'a-0 b-0 c-0 d-0 a-1 b-1 c-1 d-1 a-2 b-2 c-2 d-2 a-3 b-3 c-3 d-3 a-4 b-4 c-4 d-4 a-5 b-5 c-5 d-5'} + +# vc_id mapping for D4XX sensor entity to IPU6|IPU7 CSI-2 pad. +# IPU7 CSI-2 pad 1, vc 0, depth a-? +# IPU7 CSI-2 pad 2, vc 1, depth c-? or meta data a-? +# IPU7 CSI-2 pad 3, vc 1, RGB a-? +# IPU7 CSI-2 pad 4, vc 0, RGB c-? or meta data a-? +# IPU7 CSI-2 pad 5, vc 2, IR a-? +# IPU7 CSI-2 pad 6, vc 3, IMU a-? +# Aggregated-links +# ipu7 CSI-2 pad 7, vc 2, depth b-? +# IPU7 CSI-2 pad 8, vc 3, depth d-? or meta data b-? +# IPU7 CSI-2 pad 9, vc 3, RGB b-? +# IPU7 CSI-2 pad 10, vc 2, RGB d-? or meta data b-? +# IPU7 CSI-2 pad 11, vc 0, IR b-? +# IPU7 CSI-2 pad 12, vc 1, IMU b-? +# IPU7 CSI-2 pad 13, vc 3, IR c-? +# IPU7 CSI-2 pad 14, vc 2, IMU c-? +# IPU7 CSI-2 pad 15, vc 1, IR d-? +# IPU7 CSI-2 pad 16, vc 0, IMU d-? +declare -A media_sensor_csi_route=( [1]=0 [2]=1 [3]=1 [4]=0 [5]=2 [6]=3 [7]=2 [8]=3 [9]=3 [10]=2 [11]=0 [12]=1 [13]=3 [14]=2 [15]=1 [16]=0 ) + +# Find media device. +out() { + [[ $quiet -eq 0 ]] && echo -n "${@} " >&2 + "${@}" + [[ $quiet -eq 0 ]] && echo " RET=$?" >&2 +} + +# For case with usb camera plugged in during the boot, +# usb media controller will occupy index 0 +mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media) +[[ -z "${mdev}" ]] && exit 0 + +# Find IPU PCI device gen name +cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6|7]\).*/\1/' | tr '[:lower:]' '[:upper:]') +[[ -z "${cap_prefix}" ]] && exit 0 + +media_ctl_cmd="${media_util} -d ${mdev}" +[[ -n "${mux_param}" ]] && out $media_ctl_cmd -r # <- this can be used to clean-up all bindings from media controller +# cache media-ctl output +dot=$($media_ctl_cmd --print-dot) + +fmt_depth="${fmt:-[fmt:UYVY8_1X16/640x480 field:none]}" +fmt_rgb="${fmt:-[fmt:YUYV8_1X16/640x480 field:none]}" +fmt_ir="${fmt:-[fmt:VYUY8_1X16/640x480 field:none]}" +# For fimware version starting from: 5.16, +# IMU will have 32bit axis values. +# 5.16.x.y = firmware version: 0x0510 +# state->fw_version < 0x510 +#fmt_imu="${fmt:-[fmt:Y8_1X8/32x1 field:none]}" +# state->fw_version >= 0x510 +fmt_imu="${fmt:-[fmt:Y8_1X8/38x1 field:none]}" + +count=0 +pre_csi2=0 + +# DS5 MUX. Can be max9296 or max96724 aggregated {a, b, c, d} accross mipi port {0, 1, 2, 3, 4, 5}. +for camera in ${mux_list}; do + + is_mux=$(echo "${dot}" | grep "DS5 mux ${camera}") + # skip for non-existing muxes + [[ -z $is_mux ]] && continue; + + [[ $quiet -eq 0 ]] && echo -n "Bind $cap_prefix to DS5 mux ${camera} ..." + + csi2="$((${camera:2:1}))" + mux=${camera:0:1} + + [[ $csi2 -ne $pre_csi2 ]] && count=0 + + count=$((${count}+1)) + + echo "(count=${count} on $cap_prefix CSI ${csi2})..." + + # mapping for DS5 mux entity to IPU7 ISYS entity matching. + # for DS5 aggregated mux (above a) - use ISYS capture pads with offset 6 + # this offset used as capture sensor set - dept,rgb,ir,imu = 4 + cap_pad="$((${media_mux_capture_link[${csi2}]}))" + isys_pad=0 + ir_active=1 + imu_active=1 + if [ "${mux}" \= "b" ]; then + isys_pad=6 + if [ $count -gt 1 ] && [ -z "${mux_param}" ]; then + ir_active=0 + imu_active=0 + fi + else + if [ "${mux}" \= "c" ]; then + metadata_enabled=0 + isys_pad=1 + if [ $count -gt 1 ] && [ -z "${mux_param}" ]; then + ir_active=0 + imu_active=0 + fi + else + if [ "${mux}" \= "d" ]; then + metadata_enabled=0 + isys_pad=7 + if [ $count -gt 1 ] && [ -z "${mux_param}" ]; then + ir_active=0 + imu_active=0 + fi + fi + fi + fi + cap_pad=$((${cap_pad}+${isys_pad})) + + # DEPTH video streaming node (no metadata) + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${isys_pad}+1)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+0))\":0[1]" 1>/dev/null + # DEPTH metadata node + if [[ $metadata_enabled -eq 1 ]]; then + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${isys_pad}+2)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+1))\":0[1]" 1>/dev/null + fi + + # RGB link video streaming node (no metadata) + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${isys_pad}+3)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+2))\":0[1]" 1>/dev/null + # RGB metadata node + if [[ $metadata_enabled -eq 1 ]]; then + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+4)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+3))\":0[1]" 1>/dev/null + fi + + stream_id_depth=$((${isys_pad}+0)) + stream_id_rgb=$((${isys_pad}+2)) + isys_pad_depth=$((${isys_pad}+1)) + isys_pad_rgb=$((${isys_pad}+3)) + isys_vc_depth=$((${media_sensor_csi_route[${isys_pad_depth}]}+0)) + isys_vc_rgb=$((${media_sensor_csi_route[${isys_pad_rgb}]}+0)) + + if [ "${mux}" \= "c" ]; then + # remap IR amd IMU suffix c to CSI2 input 13 and 14 + isys_pad=$((${isys_pad}+7)) + cap_pad=$((${cap_pad}+7)) + else + if [ "${mux}" \= "d" ]; then + # remap IR amd IMU suffix d to CSI2 input 15 and 16 + isys_pad=$((${isys_pad}+3)) + cap_pad=$((${cap_pad}+3)) + fi + fi + # IR link video streaming node + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${isys_pad}+5)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+4))\":0[1]" 1>/dev/null + # IMU link streaming node + out $media_ctl_cmd -v -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${isys_pad}+6)) -> \"Intel ${cap_prefix} ISYS Capture $((${cap_pad}+5))\":0[1]" 1>/dev/null + + stream_id_ir=$((${isys_pad}+4)) + stream_id_imu=$((${isys_pad}+5)) + isys_pad_ir=$((${isys_pad}+5)) + isys_pad_imu=$((${isys_pad}+6)) + isys_vc_ir=$((${media_sensor_csi_route[${isys_pad_ir}]}+0)) + isys_vc_imu=$((${media_sensor_csi_route[${isys_pad_imu}]}+0)) + + # bind (enable) link DS5 mux pad 0 to CSI-2 pad 0 + out $media_ctl_cmd -l "\"DS5 mux ${camera}\":0 -> \"Intel ${cap_prefix} CSI2 ${csi2}\":0[1]" + # subdev entity '['pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ']' ; + if [[ $metadata_enabled -eq 1 ]]; then + route_md="0/${stream_id_depth}->${isys_pad_depth}/${stream_id_depth}[1], 0/${stream_id_depth}->$((${isys_pad_depth}+1))/${stream_id_depth}[1], 0/${stream_id_rgb}->${isys_pad_rgb}/${stream_id_rgb}[1], 0/${stream_id_rgb}->$((${isys_pad_rgb}+1))/${stream_id_rgb}[1], 0/${stream_id_ir}->${isys_pad_ir}/${stream_id_ir}[${ir_active}], 0/${stream_id_imu}->${isys_pad_imu}/${stream_id_imu}[${imu_active}]" + + # Set DEPTH+DEPTH-md+RGB+RGB-md+IR+IMU vc_id routes + if [ $count -eq 1 ]; then + csi_route=${route_md} + else + csi_route=${csi_route}", "${route_md} + fi + else + # Set DEPTH+RGB+IR+IMU vc_id routes + route_no_md="0/${stream_id_depth}->${isys_pad_depth}/${stream_id_depth}[1], 0/${stream_id_rgb}->${isys_pad_rgb}/${stream_id_rgb}[1], 0/${stream_id_ir}->${isys_pad_ir}/${stream_id_ir}[${ir_active}], 0/${stream_id_imu}->${isys_pad_imu}/${stream_id_imu}[${imu_active}]" + if [ $count -eq 1 ]; then + csi_route=${route_no_md} + else + csi_route=${csi_route}", "${route_no_md} + + #WA: disable prior IR and IMU if 4 camera are used + if [ "${mux}" \= "d" ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/4\[1|\/4[0|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/5\[1|\/5[0|g' <<< $csi_route)) + fi + fi + fi + + # example: /usr/bin/media-ctl -d /dev/media0 -R "'Intel ${cap_prefix} CSI2 2'[0/1->3/1,0/0->1/0]" + out $media_ctl_cmd -R "\"DS5 mux ${camera}\"[1/${stream_id_depth}->0/${stream_id_depth}[1], 2/${stream_id_rgb}->0/${stream_id_rgb}[1], 3/${stream_id_ir}->0/${stream_id_ir}[${ir_active}], 4/${stream_id_imu}->0/${stream_id_imu}[${imu_active}]]" + out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[${csi_route}]" + + # DEPTH default media bus format + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_depth}/${stream_id_depth} ${fmt_depth}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_depth} ${fmt_depth}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":1/${stream_id_depth} ${fmt_depth}" + out $media_ctl_cmd -V "\"D4XX depth ${camera}\":0/${stream_id_depth} ${fmt_depth}" + # RGB default media bus format + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_rgb}/${stream_id_rgb} ${fmt_rgb}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_rgb} ${fmt_rgb}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":2/${stream_id_rgb} ${fmt_rgb}" + out $media_ctl_cmd -V "\"D4XX rgb ${camera}\":0/${stream_id_rgb} ${fmt_rgb}" + # IR default media bus format + if [[ $ir_active -eq 1 ]]; then + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_ir}/${stream_id_ir} ${fmt_ir}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_ir} ${fmt_ir}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":3/${stream_id_ir} ${fmt_ir}" + fi + out $media_ctl_cmd -V "\"D4XX ir ${camera}\":0/${stream_id_ir} ${fmt_ir}" + # IMU default media bus format + if [[ $imu_active -eq 1 ]]; then + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_imu}/${stream_id_imu} ${fmt_imu}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_imu} ${fmt_imu}" + out $media_ctl_cmd -V "\"DS5 mux ${camera}\":4/${stream_id_imu} ${fmt_imu}" + fi + out $media_ctl_cmd -V "\"D4XX imu ${camera}\":0/${stream_id_imu} ${fmt_imu}" + + # WA: must repeat all the v4l2 set-fmt on Intel IPU7/IPU6 CSI2 2 v4l2 subdev pad 0 + if [ ${count} -gt 1 ]; then + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/0 ${fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/2 ${fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/4 ${fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/5 ${fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/6 ${fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/8 ${fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/10 ${fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/11 ${fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/1 ${fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/3 ${fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/12 ${fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/13 ${fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/7 ${fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/9 ${fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/14 ${fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/15 ${fmt_imu}" 1>/dev/null + fi + pre_csi2=$csi2 + +done From e4d7cae86fdbf2b6421d1032bc58168d626c7b2d Mon Sep 17 00:00:00 2001 From: florent pirou Date: Mon, 16 Feb 2026 18:39:35 +0100 Subject: [PATCH 64/72] helper: make max9x v4l2 stream and route binding generic --- helpers/ipu_max9x_bind.sh | 58 +++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/helpers/ipu_max9x_bind.sh b/helpers/ipu_max9x_bind.sh index 606b4e68..c3e2bfd0 100755 --- a/helpers/ipu_max9x_bind.sh +++ b/helpers/ipu_max9x_bind.sh @@ -124,7 +124,7 @@ declare -A media_mux_capture_pad=( ) # all available ISX031/IMX390/AR0234 max9x entity, each one represent physically connected camera. -# muxes prefix a, b, c, d referes to max96724 or max9292 aggregated link cameras +# muxes prefix a, b, c, d referes to max96724 or max9296 aggregated link cameras # muxes suffix 0, 1, 2, 3, 4, 5 is referes to csi2 mipi port mapping mux_list=${mux_param:-'a-0 b-0 c-0 d-0 a-1 b-1 c-1 d-1 a-2 b-2 c-2 d-2 a-3 b-3 c-3 d-3 a-4 b-4 c-4 d-4 a-5 b-5 c-5 d-5'} @@ -149,14 +149,23 @@ media-ctl -r # <- this can be used to clean-up all bindings from media controlle # cache media-ctl output dot=$($media_ctl_cmd --print-dot) +des_route="" +csi_route="" +des_suffix="a" +streamid=0 # loop over all available IMX390/ISX031/AR0234 max9x, each one represent physically connected camera. for camera in $mux_list; do e="$(sen_node ${camera})" if [ -z "${e}" ]; then continue; fi + d="$(des_node ${camera})" + if [ "${des_suffix}" != "${d:6:1}" ]; then + des_suffix="${d:6:1}" + streamid=0 + fi - [[ $quiet -eq 0 ]] && echo "Bind $cap_prefix to ${sensor} ${camera} through max9x serdes .. " >&2 + [[ $quiet -eq 0 ]] && echo "Bind $cap_prefix to ${sensor} ${camera} through max9x ${des_suffix} .. " >&2 csi2="$((${camera:2:1}))" mux=${camera:0:1} @@ -164,25 +173,52 @@ for camera in $mux_list; do # mapping for MAX9x mux entity to IPU7|IPU6 ISYS matching entity. cap_pad="${media_mux_capture_pad[${mux}]}" isys_cap="$((${media_mux_capture_link[${csi2}]}+${cap_pad}))" - vc_id=$((${cap_pad} % 4)) out $media_ctl_cmd -l "$(des_src_pad ${camera}) -> \"Intel ${cap_prefix} CSI2 ${csi2}\":0[1]" out $media_ctl_cmd -l "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+1)) -> \"Intel ${cap_prefix} ISYS Capture ${isys_cap}\":0[1]" # subdev entity '['pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ']' ; - out $media_ctl_cmd -R "\"$(des_node ${camera})\"[4/0->0/0[1], 5/1->0/1[1], 6/2->0/2[1], 7/3->0/3[1]]" - out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[0/0->1/0[1], 0/1->2/1[1], 0/2->3/2[1], 0/3->4/3[1]]" + if [ $streamid -eq 0 ]; then + des_route="$((${cap_pad} + 4))/${streamid}->0/${streamid}[1]" + csi_route="0/${streamid}->$((${cap_pad} + 1))/${streamid}[1]" + else + des_route=${des_route}",$((${cap_pad} + 4))/${streamid}->0/${streamid}[1]" + csi_route=${csi_route}",0/${streamid}->$((${cap_pad} + 1))/${streamid}[1]" + fi + out $media_ctl_cmd -R "\"$(ser_node ${camera})\"[0/0->2/${streamid}[1]]" + out $media_ctl_cmd -R "\"$(des_node ${camera})\"[${des_route}]" + out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[${csi_route}]" out $media_ctl_cmd -V "$(sen_src_pad ${camera}) ${fmt}" out $media_ctl_cmd -V "$(ser_sink_pad ${camera}) ${fmt}" - out $media_ctl_cmd -V "$(ser_src_pad ${camera}) ${fmt}" - out $media_ctl_cmd -V "$(des_sink_pad ${camera})/${vc_id} ${fmt}" - out $media_ctl_cmd -V "$(des_src_pad ${camera})/${vc_id} ${fmt}" + out $media_ctl_cmd -V "$(ser_src_pad ${camera})/${streamid} ${fmt}" + out $media_ctl_cmd -V "$(des_sink_pad ${camera})/${streamid} ${fmt}" + out $media_ctl_cmd -V "$(des_src_pad ${camera})/${streamid} ${fmt}" - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/${vc_id} ${fmt}" - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+1))/${vc_id} ${fmt}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/${streamid} ${fmt}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":$((${cap_pad}+1))/${streamid} ${fmt}" cap_dev=$($media_ctl_cmd -e "Intel ${cap_prefix} ISYS Capture ${isys_cap}") - out ln -snf ${cap_dev} /dev/video-${sensor}-${camera} + dev_ln="/dev/video-${sensor}-${camera}" + out ln -snf ${cap_dev} ${dev_ln} + # set default v4l2 fmt value +if [ ${sensor} = "isx031" ]; then + out ${v4l2_util} -d ${dev_ln} --set-fmt-video=width=1920,height=1536,pixelformat=UYVY +elif [ ${sensor} = "imx390" ]; then + out ${v4l2_util} -d ${dev_ln} --set-fmt-video=width=1920,height=1200,pixelformat=NV12 +elif [ ${sensor} = "ar0234" ]; then + out ${v4l2_util} -d ${dev_ln} --set-fmt-video=width=1280,height=960,pixelformat=NV12 +fi + # disable ipu link enumeration feature + out ${v4l2_util} -d $dev_ln -c enumerate_graph_link=0 + + # change group + out chown root:video $cap_dev + + # WA: must repeat all the v4l2 set-fmt on Intel IPU7 CSI2 2 v4l2 subdev pad 0 + if [ $streamid -gt 0 ]; then + for i in $(seq 0 $streamid); do out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/$((${i})) ${fmt}"; done + fi + streamid=$(($streamid + 1)) done From 5adf076c76dab5a77dfcd765da3475b42fc24e0f Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 17 Mar 2026 08:44:52 +0000 Subject: [PATCH 65/72] helpers: add max9x v4l2 graph binding quiet mode Signed-off-by: florent pirou --- helpers/ipu_max9x_bind.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/helpers/ipu_max9x_bind.sh b/helpers/ipu_max9x_bind.sh index c3e2bfd0..0a686203 100755 --- a/helpers/ipu_max9x_bind.sh +++ b/helpers/ipu_max9x_bind.sh @@ -7,6 +7,7 @@ if [ -z ${v4l2_util} ]; then echo "v4l2-ctl not found, install with: sudo apt install v4l-utils" exit 1 fi +quiet=0 while [[ $# -gt 0 ]]; do case $1 in -q|--quiet) @@ -32,7 +33,6 @@ while [[ $# -gt 0 ]]; do shift ;; *) - quiet=0 shift ;; esac @@ -139,13 +139,13 @@ cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6 [[ -z "${cap_prefix}" ]] && exit 0 out() { - echo -n "${@} " >&2 - "${@}" - echo " RET=$?" >&2 + [[ $quiet -eq 0 ]] && echo -n "${@} " >&2 + "${@}" + [[ $quiet -eq 0 ]] && echo " RET=$?" >&2 } media_ctl_cmd="${media_util} -d ${mdev}" -media-ctl -r # <- this can be used to clean-up all bindings from media controller +out media-ctl -r # <- this can be used to clean-up all bindings from media controller # cache media-ctl output dot=$($media_ctl_cmd --print-dot) @@ -165,7 +165,7 @@ for camera in $mux_list; do streamid=0 fi - [[ $quiet -eq 0 ]] && echo "Bind $cap_prefix to ${sensor} ${camera} through max9x ${des_suffix} .. " >&2 + echo "Bind $cap_prefix to ${sensor} ${camera} through max9x ${des_suffix} .. " >&2 csi2="$((${camera:2:1}))" mux=${camera:0:1} @@ -189,8 +189,8 @@ for camera in $mux_list; do out $media_ctl_cmd -R "\"$(des_node ${camera})\"[${des_route}]" out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[${csi_route}]" - out $media_ctl_cmd -V "$(sen_src_pad ${camera}) ${fmt}" - out $media_ctl_cmd -V "$(ser_sink_pad ${camera}) ${fmt}" + out $media_ctl_cmd -V "$(sen_src_pad ${camera})/0 ${fmt}" + out $media_ctl_cmd -V "$(ser_sink_pad ${camera})/0 ${fmt}" out $media_ctl_cmd -V "$(ser_src_pad ${camera})/${streamid} ${fmt}" out $media_ctl_cmd -V "$(des_sink_pad ${camera})/${streamid} ${fmt}" out $media_ctl_cmd -V "$(des_src_pad ${camera})/${streamid} ${fmt}" From acf505ea555927081eea1b37a712045491e22ec3 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 19 Mar 2026 01:41:24 -0700 Subject: [PATCH 66/72] helpers: fix d4xx stream v4l2 routes on input-src D Signed-off-by: florent pirou --- helpers/rs_ipu_d457_bind.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh index 322eed20..aa88a423 100755 --- a/helpers/rs_ipu_d457_bind.sh +++ b/helpers/rs_ipu_d457_bind.sh @@ -227,7 +227,7 @@ for camera in ${mux_list}; do csi_route=${csi_route}", "${route_no_md} #WA: disable prior IR and IMU if 4 camera are used - if [ "${mux}" \= "d" ] && [ $ir_active -eq 0 ]; then + if [ "${mux}" \= "d" ] && [ $count -gt 3 ] && [ $ir_active -eq 0 ]; then csi_route=$(echo $(sed 's|\/4\[1|\/4[0|g' <<< $csi_route)) csi_route=$(echo $(sed 's|\/5\[1|\/5[0|g' <<< $csi_route)) fi From 9bcab745ff01bf388980ab58b33fb5c3c2ad9b2d Mon Sep 17 00:00:00 2001 From: florent pirou Date: Wed, 25 Mar 2026 19:10:27 -0700 Subject: [PATCH 67/72] helpers: enable all d4xx routes streams when 2 camera only * helpers: fix edge case if only 2 cameras per ipu7 csi ``` Bus Camera Sensor Node Type Video Node RS Link ipu6 a-0 ir Stream(id=4) /dev/video5 /dev/video-rs-ir-0 ipu6 a-0 depth Stream(id=0) /dev/video1 /dev/video-rs-depth-0 ipu6 a-0 imu Stream(id=5) /dev/video6 /dev/video-rs-imu-0 ipu6 a-0 color Stream(id=2) /dev/video3 /dev/video-rs-color-0 i2c a-0 d4xx Firmware /dev/d4xx-dfu-a-0 /dev/d4xx-dfu-0 ipu6 b-0 ir Stream(id=10) /dev/video11 /dev/video-rs-ir-1 ipu6 b-0 depth Stream(id=6) /dev/video7 /dev/video-rs-depth-1 ipu6 b-0 imu Stream(id=11) /dev/video12 /dev/video-rs-imu-1 ipu6 b-0 color Stream(id=8) /dev/video9 /dev/video-rs-color-1 i2c b-0 d4xx Firmware /dev/d4xx-dfu-b-0 /dev/d4xx-dfu-1 ``` * helpers: fix edge case if more than 2 cameras per ipu7 csi ``` Bus Camera Sensor Node Type Video Node RS Link ipu7 a-2 ir Stream(id=4) /dev/video37 /dev/video-rs-ir-8 ipu7 a-2 depth Stream(id=0) /dev/video33 /dev/video-rs-depth-8 ipu7 a-2 imu Stream(id=5) /dev/video38 /dev/video-rs-imu-8 ipu7 a-2 color Stream(id=2) /dev/video35 /dev/video-rs-color-8 i2c a-2 d4xx Firmware /dev/d4xx-dfu-a-2 /dev/d4xx-dfu-8 ipu7 c-2 ir Inactive /dev/video45 /dev/video-rs-ir-10 ipu7 c-2 depth Stream(id=1) /dev/video34 /dev/video-rs-depth-10 ipu7 c-2 imu Inactive /dev/video46 /dev/video-rs-imu-10 ipu7 c-2 color Stream(id=3) /dev/video36 /dev/video-rs-color-10 i2c c-2 d4xx Firmware /dev/d4xx-dfu-c-2 /dev/d4xx-dfu-10 ipu7 d-2 ir Inactive /dev/video47 /dev/video-rs-ir-11 ipu7 d-2 depth Stream(id=7) /dev/video40 /dev/video-rs-depth-11 ipu7 d-2 imu Inactive /dev/video48 /dev/video-rs-imu-11 ipu7 d-2 color Stream(id=9) /dev/video42 /dev/video-rs-color-11 i2c d-2 d4xx Firmware /dev/d4xx-dfu-d-2 /dev/d4xx-dfu-11 ``` Signed-off-by: florent pirou --- helpers/rs_ipu_d457_bind.sh | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh index aa88a423..5503b1ce 100755 --- a/helpers/rs_ipu_d457_bind.sh +++ b/helpers/rs_ipu_d457_bind.sh @@ -226,16 +226,44 @@ for camera in ${mux_list}; do else csi_route=${csi_route}", "${route_no_md} - #WA: disable prior IR and IMU if 4 camera are used + #WA: enable IR and IMU if 2 camera are used + if [ "${mux}" \= "b" ] && [ $count -lt 3 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/10\[0|\/10[1|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/11\[0|\/11[1|g' <<< $csi_route)) + fi + + if [ "${mux}" \= "c" ] && [ $count -lt 3 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/12\[0|\/12[1|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/13\[0|\/13[1|g' <<< $csi_route)) + fi + + if [ "${mux}" \= "d" ] && [ $count -lt 3 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/14\[0|\/14[1|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/15\[0|\/15[1|g' <<< $csi_route)) + fi + + #WA: disable prior GMSL B or C IR and IMU if 3 or 4 camera are used + if [ "${mux}" \= "c" ] && [ $count -gt 2 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/10\[1|\/10[0|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/11\[1|\/11[0|g' <<< $csi_route)) + fi + + if [ "${mux}" \= "d" ] && [ $count -gt 2 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/12\[1|\/12[0|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/13\[1|\/13[0|g' <<< $csi_route)) + fi + + #WA: disable prior GMSL A IR and IMU if 4 camera are used if [ "${mux}" \= "d" ] && [ $count -gt 3 ] && [ $ir_active -eq 0 ]; then csi_route=$(echo $(sed 's|\/4\[1|\/4[0|g' <<< $csi_route)) csi_route=$(echo $(sed 's|\/5\[1|\/5[0|g' <<< $csi_route)) fi + fi fi # example: /usr/bin/media-ctl -d /dev/media0 -R "'Intel ${cap_prefix} CSI2 2'[0/1->3/1,0/0->1/0]" - out $media_ctl_cmd -R "\"DS5 mux ${camera}\"[1/${stream_id_depth}->0/${stream_id_depth}[1], 2/${stream_id_rgb}->0/${stream_id_rgb}[1], 3/${stream_id_ir}->0/${stream_id_ir}[${ir_active}], 4/${stream_id_imu}->0/${stream_id_imu}[${imu_active}]]" + out $media_ctl_cmd -R "\"DS5 mux ${camera}\"[1/${stream_id_depth}->0/${stream_id_depth}[1], 2/${stream_id_rgb}->0/${stream_id_rgb}[1], 3/${stream_id_ir}->0/${stream_id_ir}[1], 4/${stream_id_imu}->0/${stream_id_imu}[1]]" out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[${csi_route}]" # DEPTH default media bus format From 0323e85869950a90e5ff694fcf3a0e6f8ae8a0e9 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Thu, 9 Apr 2026 08:21:33 +0000 Subject: [PATCH 68/72] helpers: set initial d4xx frame size, fix binding edge-case * helpers: fix edge-cases on binding 2 and 3 cameras. * helpers: d4xx binding option to set initial frame size and intervals ``` ./helpers/rs_ipu_d457_bind.sh -n -f 1280x720@1/3 && ./helpers/rs-enum-ipu.sh -n ``` Signed-off-by: florent pirou --- helpers/rs-enum-ipu.sh | 21 ++++------ helpers/rs_ipu_d457_bind.sh | 82 ++++++++++++++++++++++++------------- 2 files changed, 63 insertions(+), 40 deletions(-) diff --git a/helpers/rs-enum-ipu.sh b/helpers/rs-enum-ipu.sh index e9b68e07..7d91f9d1 100755 --- a/helpers/rs-enum-ipu.sh +++ b/helpers/rs-enum-ipu.sh @@ -149,8 +149,10 @@ for camera in $mux_list; do fi mux_nd=$(echo "${dot}" | grep -w "$d4xx_sens_mux:port1" | awk '{print $3}' | awk -F':' '{print $1}') dev_mux=$(echo "${dot}" | grep "${mux_nd}" | grep subdev | tr '\\n' '\n' | grep subdev | awk -F'"' '{print $1}' | awk -F'|' '{print $1}') - - [[ $quiet -eq 0 ]] && printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$dev_name" ${camera} ${camera_names["${sens}"]} $link_state $vid $dev_ln + d4xx_subdev_mux=$(echo "${dot}" | grep $d4xx_sens | grep label | tr '\\n' '\n' | grep subdev | awk -F'"' '{print $1}' | awk -F'|' '{print $1}') + csi2_fmt=$(${v4l2_util} -d ${d4xx_subdev_mux} --get-subdev-fmt pad=0,stream=${csi2_stream} | grep "Width" | awk -F': ' '{print $2}' ) + + [[ $quiet -eq 0 ]] && printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$dev_name" ${camera} ${camera_names["${sens}"]} $link_state $vid $dev_ln [$csi2_fmt] # create link only in case we choose not only to show it if [[ $info -eq 0 ]]; then @@ -162,16 +164,11 @@ for camera in $mux_list; do # enable ipu7 link enumeration feature ${v4l2_util} -d $dev_ln -c enumerate_graph_link=1 - [[ -e $dev_ln && ${camera_names["${sens}"]} == 'depth' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='Z16 ' - [[ -e $dev_ln && ${camera_names["${sens}"]} == 'color' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='YUYV' - [[ -e $dev_ln && ${camera_names["${sens}"]} == 'ir' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=640,height=480,pixelformat='Y8I ' - # For fimware version starting from: 5.16, - # IMU will have 32bit axis values. - # 5.16.x.y = firmware version: 0x0510 - # state->fw_version < 0x510 - #[[ -e $dev_ln && ${camera_names["${sens}"]} == 'imu' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=32,height=1,pixelformat='GREY' - # state->fw_version >= 0x510 - [[ -e $dev_ln && ${camera_names["${sens}"]} == 'imu' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=38,height=1,pixelformat='GREY' + #echo "--set-fmt-video=width=${csi2_fmt%/*},height=${csi2_fmt#*/}" + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'depth' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=${csi2_fmt%/*},height=${csi2_fmt#*/},pixelformat='Z16 ' + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'color' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=${csi2_fmt%/*},height=${csi2_fmt#*/},pixelformat='YUYV' + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'ir' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=${csi2_fmt%/*},height=${csi2_fmt#*/},pixelformat='Y8I ' + [[ -e $dev_ln && ${camera_names["${sens}"]} == 'imu' ]] && ${v4l2_util} -d $dev_ln --set-fmt-video=width=${csi2_fmt%/*},height=${csi2_fmt#*/},pixelformat='GREY' fi update_mux_link_freq=1 # will update link freq for camera diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh index 5503b1ce..0e220d6c 100755 --- a/helpers/rs_ipu_d457_bind.sh +++ b/helpers/rs_ipu_d457_bind.sh @@ -39,6 +39,10 @@ while [[ $# -gt 0 ]]; do echo "-n OR -n -m " exit 1 ;; + -f) + fmt="$2" + shift + ;; *) quiet=0 shift @@ -46,6 +50,8 @@ while [[ $# -gt 0 ]]; do esac done +fmt="${fmt:-640x480@1/30}" + # mapping for DS5 mux entity to IPU6 CSI-2 entity matching. declare -A media_mux_capture_link=( [0]=0 [1]=16 [2]=32 [3]=48 [4]=64 [5]=80) @@ -96,16 +102,26 @@ media_ctl_cmd="${media_util} -d ${mdev}" # cache media-ctl output dot=$($media_ctl_cmd --print-dot) -fmt_depth="${fmt:-[fmt:UYVY8_1X16/640x480 field:none]}" -fmt_rgb="${fmt:-[fmt:YUYV8_1X16/640x480 field:none]}" -fmt_ir="${fmt:-[fmt:VYUY8_1X16/640x480 field:none]}" +fmt_rgb="[fmt:YUYV8_1X16/$fmt field:none]" +csi_fmt_rgb="[fmt:YUYV8_1X16/${fmt%@*} field:none]" + +#WA make sure depth and rgb don't exceed +if [ "${fmt%@*}" == "1280x800" ]; then + fmt="1280x720@${fmt#*@}" +fi +fmt_depth="[fmt:UYVY8_1X16/$fmt field:none]" +fmt_ir="[fmt:VYUY8_1X16/$fmt field:none]" +csi_fmt_depth="[fmt:UYVY8_1X16/${fmt%@*} field:none]" +csi_fmt_ir="[fmt:VYUY8_1X16/${fmt%@*} field:none]" + # For fimware version starting from: 5.16, # IMU will have 32bit axis values. # 5.16.x.y = firmware version: 0x0510 # state->fw_version < 0x510 -#fmt_imu="${fmt:-[fmt:Y8_1X8/32x1 field:none]}" +#fmt_imu="[fmt:Y8_1X8/32x1 field:none]" # state->fw_version >= 0x510 -fmt_imu="${fmt:-[fmt:Y8_1X8/38x1 field:none]}" +fmt_imu="[fmt:Y8_1X8/38x1@1/30 field:none]" +csi_fmt_imu="[fmt:Y8_1X8/38x1 field:none]" count=0 pre_csi2=0 @@ -226,6 +242,16 @@ for camera in ${mux_list}; do else csi_route=${csi_route}", "${route_no_md} + #WA: disable prior IR and IMU if 4 camera are used + if [ "${mux}" \= "d" ] && [ $count -gt 3 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/4\[1|\/4[0|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/5\[1|\/5[0|g' <<< $csi_route)) + fi + #WA: disable prior IR and IMU if 3 camera are used + if [ "${mux}" \= "d" ] && [ $count -gt 2 ] && [ $ir_active -eq 0 ]; then + csi_route=$(echo $(sed 's|\/12\[1|\/12[0|g' <<< $csi_route)) + csi_route=$(echo $(sed 's|\/13\[1|\/13[0|g' <<< $csi_route)) + fi #WA: enable IR and IMU if 2 camera are used if [ "${mux}" \= "b" ] && [ $count -lt 3 ] && [ $ir_active -eq 0 ]; then csi_route=$(echo $(sed 's|\/10\[0|\/10[1|g' <<< $csi_route)) @@ -267,25 +293,25 @@ for camera in ${mux_list}; do out $media_ctl_cmd -R "\"Intel ${cap_prefix} CSI2 ${csi2}\"[${csi_route}]" # DEPTH default media bus format - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_depth}/${stream_id_depth} ${fmt_depth}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_depth}/${stream_id_depth} ${csi_fmt_depth}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_depth} ${fmt_depth}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":1/${stream_id_depth} ${fmt_depth}" out $media_ctl_cmd -V "\"D4XX depth ${camera}\":0/${stream_id_depth} ${fmt_depth}" # RGB default media bus format - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_rgb}/${stream_id_rgb} ${fmt_rgb}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_rgb}/${stream_id_rgb} ${csi_fmt_rgb}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_rgb} ${fmt_rgb}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":2/${stream_id_rgb} ${fmt_rgb}" out $media_ctl_cmd -V "\"D4XX rgb ${camera}\":0/${stream_id_rgb} ${fmt_rgb}" # IR default media bus format if [[ $ir_active -eq 1 ]]; then - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_ir}/${stream_id_ir} ${fmt_ir}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_ir}/${stream_id_ir} ${csi_fmt_ir}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_ir} ${fmt_ir}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":3/${stream_id_ir} ${fmt_ir}" fi out $media_ctl_cmd -V "\"D4XX ir ${camera}\":0/${stream_id_ir} ${fmt_ir}" # IMU default media bus format if [[ $imu_active -eq 1 ]]; then - out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_imu}/${stream_id_imu} ${fmt_imu}" + out $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":${isys_pad_imu}/${stream_id_imu} ${csi_fmt_imu}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":0/${stream_id_imu} ${fmt_imu}" out $media_ctl_cmd -V "\"DS5 mux ${camera}\":4/${stream_id_imu} ${fmt_imu}" fi @@ -293,25 +319,25 @@ for camera in ${mux_list}; do # WA: must repeat all the v4l2 set-fmt on Intel IPU7/IPU6 CSI2 2 v4l2 subdev pad 0 if [ ${count} -gt 1 ]; then - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/0 ${fmt_depth}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/2 ${fmt_rgb}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/4 ${fmt_ir}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/5 ${fmt_imu}" 1>/dev/null - - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/6 ${fmt_depth}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/8 ${fmt_rgb}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/10 ${fmt_ir}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/11 ${fmt_imu}" 1>/dev/null - - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/1 ${fmt_depth}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/3 ${fmt_rgb}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/12 ${fmt_ir}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/13 ${fmt_imu}" 1>/dev/null - - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/7 ${fmt_depth}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/9 ${fmt_rgb}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/14 ${fmt_ir}" 1>/dev/null - $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/15 ${fmt_imu}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/0 ${csi_fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/2 ${csi_fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/4 ${csi_fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/5 ${csi_fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/6 ${csi_fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/8 ${csi_fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/10 ${csi_fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/11 ${csi_fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/1 ${csi_fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/3 ${csi_fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/12 ${csi_fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/13 ${csi_fmt_imu}" 1>/dev/null + + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/7 ${csi_fmt_depth}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/9 ${csi_fmt_rgb}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/14 ${csi_fmt_ir}" 1>/dev/null + $media_ctl_cmd -V "\"Intel ${cap_prefix} CSI2 ${csi2}\":0/15 ${csi_fmt_imu}" 1>/dev/null fi pre_csi2=$csi2 From 946274e55e066b605991c4f1f058179e44e36abb Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 21 Apr 2026 06:36:44 -0700 Subject: [PATCH 69/72] helpers: bind media detects ipu isys capture device limits * helpers: bisect ipu isys v4l2 media device from usb media Signed-off-by: florent pirou --- helpers/ipu_max9x_bind.sh | 16 +++++++++++----- helpers/rs_ipu_d457_bind.sh | 8 ++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/helpers/ipu_max9x_bind.sh b/helpers/ipu_max9x_bind.sh index 0a686203..afa18b0c 100755 --- a/helpers/ipu_max9x_bind.sh +++ b/helpers/ipu_max9x_bind.sh @@ -114,8 +114,6 @@ sen_src_pad() { } # mapping for ISX031/IMX390/AR0234 max9x entity to IPU7|IPU6 CSI-2 entity matching. -declare -A media_mux_capture_link=( [0]=0 [1]=16 [2]=32 [3]=48 [4]=64 [5]=80) - declare -A media_mux_capture_pad=( [a]=0 [b]=1 @@ -131,9 +129,17 @@ mux_list=${mux_param:-'a-0 b-0 c-0 d-0 a-1 b-1 c-1 d-1 a-2 b-2 c-2 d-2 a-3 b-3 c # Find media device. # For case with usb camera plugged in during the boot, # usb media controller will occupy index 0 -mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media) +mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media | head -n 1) [[ -z "${mdev}" ]] && exit 0 +# IPU7/IPU6 ISYS Capture devices can bind to either on IPU7/IPU6 ISYS CSI subdev 1-16 or 1-8 srcpads +mdev_capdev_count=$(${v4l2_util} -d ${mdev} -A | wc -l) +if [ $((${mdev_capdev_count}-2)) -gt 48 ]; then + declare -A media_mux_capture_link=( [0]=0 [1]=16 [2]=32 [3]=48 [4]=64 [5]=80) +else + declare -A media_mux_capture_link=( [0]=0 [1]=8 [2]=16 [3]=24 [4]=32 [5]=40) +fi + # Find IPU PCI device gen name cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6|7]\).*/\1/' | tr '[:lower:]' '[:upper:]') [[ -z "${cap_prefix}" ]] && exit 0 @@ -210,8 +216,8 @@ elif [ ${sensor} = "imx390" ]; then elif [ ${sensor} = "ar0234" ]; then out ${v4l2_util} -d ${dev_ln} --set-fmt-video=width=1280,height=960,pixelformat=NV12 fi - # disable ipu link enumeration feature - out ${v4l2_util} -d $dev_ln -c enumerate_graph_link=0 + # disable ipu link enumeration feature, if exists + [[ -e "$(${v4l2_util} -d $dev_ln -L | grep enumerate_graph_link)" ]] && out ${v4l2_util} -d $dev_ln -c enumerate_graph_link=0 # change group out chown root:video $cap_dev diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh index 0e220d6c..9cea0247 100755 --- a/helpers/rs_ipu_d457_bind.sh +++ b/helpers/rs_ipu_d457_bind.sh @@ -90,13 +90,17 @@ out() { # For case with usb camera plugged in during the boot, # usb media controller will occupy index 0 -mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media) +mdev=$(${v4l2_util} --list-devices | grep -A100 ipu | grep media | head -n 1) [[ -z "${mdev}" ]] && exit 0 - # Find IPU PCI device gen name cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6|7]\).*/\1/' | tr '[:lower:]' '[:upper:]') [[ -z "${cap_prefix}" ]] && exit 0 +# D4XX requires IPU7/IPU6 ISYS Capture devices to bind to IPU7/IPU6 ISYS CSI subdev srcpads from 1-16 +# exit if IPU7/IPU6 ISYS CSI subdev 1-8 srcpads limitation detected +mdev_capdev_count=$(${v4l2_util} -d ${mdev} -A | wc -l) +[[ $((${mdev_capdev_count}-2)) -le 48 ]] && exit 0 + media_ctl_cmd="${media_util} -d ${mdev}" [[ -n "${mux_param}" ]] && out $media_ctl_cmd -r # <- this can be used to clean-up all bindings from media controller # cache media-ctl output From 02ca5a31a7a583e186301e36b0469b340a582fc5 Mon Sep 17 00:00:00 2001 From: florent pirou Date: Tue, 5 May 2026 03:07:06 -0700 Subject: [PATCH 70/72] helpers: cleanup d4xx video device symlink before bind helpers: bind d4xx args as camera suffix list. Signed-off-by: florent pirou --- helpers/rs_ipu_d457_bind.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/helpers/rs_ipu_d457_bind.sh b/helpers/rs_ipu_d457_bind.sh index 9cea0247..741b9906 100755 --- a/helpers/rs_ipu_d457_bind.sh +++ b/helpers/rs_ipu_d457_bind.sh @@ -26,7 +26,7 @@ while [[ $# -gt 0 ]]; do ;; -m|--mux) shift - if [ ${#1} -eq 3 ]; then + if [ ${#1} -ge 3 ]; then mux_param=$1 fi shift @@ -101,6 +101,9 @@ cap_prefix=$(${v4l2_util} --list-devices | grep ipu | grep PCI | sed 's/^\(ipu[6 mdev_capdev_count=$(${v4l2_util} -d ${mdev} -A | wc -l) [[ $((${mdev_capdev_count}-2)) -le 48 ]] && exit 0 +clean_symlink_cmd="find /dev -type l -name *video-rs* -delete" +out $clean_symlink_cmd # <- this can be used to clean-up all bindings from librealsense2 + media_ctl_cmd="${media_util} -d ${mdev}" [[ -n "${mux_param}" ]] && out $media_ctl_cmd -r # <- this can be used to clean-up all bindings from media controller # cache media-ctl output From 7da11046094e50bfe0f4b9cb3bec7b6c03590653 Mon Sep 17 00:00:00 2001 From: "Khai Wen, Ng" Date: Tue, 20 Jan 2026 17:22:11 +0800 Subject: [PATCH 71/72] DONOTMERGE: debian: initial dpkg rules, control and debconf * debian: initial dpkg rules, control and debconf * submodule: Pin IPU6 drivers depends to 71e2e426f3e16f8bc42bf2f88a21fa95eeca5923 * submodule: Pin IPU7 drivers depends to 57304c286317ece4c0a74a52c4733486c57444ce * dkms: remove one-size-don't-fit-all dkms conf * README: rewrite the cumbersome getting started * patches: WA `media-ctl` 1-digit stream-id limitation on `v4l-utils` v1.32 latest stable * helpers: add mtbf gmsl test scripts for isx031 * debian: fetch v4l2-core in dh configure step, fix dkms file Signed-off-by: florent pirou --- README.md | 480 ++++++++++-- debian/changelog | 59 ++ debian/control | 29 + debian/dkms.conf.in | 250 +++++++ debian/gbp.conf | 5 + debian/intel-mipi-gmsl-dkms.config.in | 28 + debian/intel-mipi-gmsl-dkms.install.in | 18 + debian/intel-mipi-gmsl-dkms.modaliases | 9 + debian/intel-mipi-gmsl-dkms.postinst.in | 50 ++ debian/intel-mipi-gmsl-dkms.templates.in | 7 + debian/rules | 69 ++ debian/source/format | 1 + debian/source/options | 26 + helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh | 688 ++++++++++++++++++ ...l-add-pad-support-2-digits-stream-id.patch | 40 + 15 files changed, 1705 insertions(+), 54 deletions(-) create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/dkms.conf.in create mode 100644 debian/gbp.conf create mode 100644 debian/intel-mipi-gmsl-dkms.config.in create mode 100644 debian/intel-mipi-gmsl-dkms.install.in create mode 100644 debian/intel-mipi-gmsl-dkms.modaliases create mode 100644 debian/intel-mipi-gmsl-dkms.postinst.in create mode 100644 debian/intel-mipi-gmsl-dkms.templates.in create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/options create mode 100755 helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh create mode 100644 patches/libv4l/0001-media-ctl-add-pad-support-2-digits-stream-id.patch diff --git a/README.md b/README.md index 6e59d849..0bdc6e05 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ This repository contains reference drivers and configurations for Intel MIPI CSI | ISX031 | GMSL | Leopard Imaging | IPU6EP, IPU6EPMTL, IPU75XA | | ISX031 | GMSL | Sensing | IPU6EP, IPU6EPMTL, IPU75XA | | ISX031 | MIPI CSI-2 | D3 Embedded | IPU6EP, IPU6EPMTL, IPU75XA | +| D4XX | GMSL | Realsenseai | IPU6EP, IPU6EPMTL, IPU75XA | > **Note:** \ IPU6EP represents ADL, TWL, ASL and RPL platforms; \ @@ -56,60 +57,431 @@ IPU75XA represents PTL platforms. ## Directory Structure -| Directory | Description | -|-----------|-------------| -| [drivers/](drivers) | Host Linux kernel drivers for supported sensors | -| [config/](config) | Host middleware configuration files | -| [doc/](doc) | Documentation guide for kernelspace and userspace configuration | -| [include/](include) | Header files for driver compilation | - -## Getting Started Guide - -1. Download `Getting Started Guide` (table below), and setup according to your target `Platform`. -2. Inside the guide, follow all instructions under section `Getting Started with Ubuntu with Kernel Overlay`. -3. Under section `Auto Script Installation`, download and use `Ubuntu Kernel Overlay Auto Installar Script` from platform-respective Software Packages. - -> **Note:** All collaterals belows can be downloaded in [rdc.intel.com](https://www.intel.com/content/www/us/en/resources-documentation/developer.html) with proper granted access. - -| Platform | Getting Started Guide | Software Package | -|---|---|---| -| ARL | [828853](https://www.intel.com/content/www/us/en/secure/content-details/828853/ubuntu-with-kernel-overlay-on-intel-core-ultra-200u-and-200h-series-processors-code-named-arrow-lake-u-h-for-edge-platforms-get-started-guide.html?DocID=828853) | [831484](https://www.intel.com/content/www/us/en/secure/design/confidential/software-kits/kit-details.html?kitId=831484) | -| MTL | [779460](https://www.intel.com/content/www/us/en/secure/content-details/779460/ubuntu-with-kernel-overlay-on-intel-core-mobile-processors-code-named-meteor-lake-u-h-for-edge-platforms-get-started-guide.html?DocID=779460) | [790840](https://www.intel.com/content/www/us/en/secure/content-details/790840/meteor-lake-ps-ubuntu-with-kernel-overlay-software-packages.html?DocID=790840) | -| TWL | [793827](https://www.intel.com/content/www/us/en/secure/content-details/793827/ubuntu-with-kernel-overlay-intel-atom-x7000re-x7000c-x7000fe-processor-series-intel-processor-n150-n250-intel-core-3-processor-n355-for-edge-applications-get-started-guide-amston-lake-mr5-amston-lake-fusa-pv-twin-lake-mr2.html?DocID=793827) | [803960](https://www.intel.com/content/www/us/en/secure/design/confidential/software-kits/kit-details.html?kitId=803960) | -| PTL | [858119](https://edc.intel.com/content/www/us/en/secure/design/confidential/products-and-solutions/processors-and-chipsets/panther-lake-h/with-linux-os-get-started-guide-for-edge-compute-applications/) | [871556](https://www.intel.com/content/www/us/en/secure/design/confidential/software-kits/kit-details.html?kitId=860689) | - -Reference: [Intel® IPU6 Enabling Partners Technical Collaterals Advisory](https://www.intel.com/content/www/us/en/secure/content-details/817101/intel-ipu6-enabling-partners-technical-collaterals-advisory.html?DocID=817101) - -## Software Dependencies - -Install these software dependencies in your target system: - -- ipu-camera-bins (E.g. [ipu6-camera-bins](https://github.com/intel/ipu6-camera-bins/tree/iotg_ipu6) / [ipu7-camera-bins](https://github.com/intel/ipu7-camera-bins)) -- ipu-camera-hal (E.g. [ipu6-camera-hal](https://github.com/intel/ipu6-camera-hal/tree/iotg_ipu6) / [ipu7-camera-hal](https://github.com/intel/ipu7-camera-hal)) -- [icamerasrc](https://github.com/intel/icamerasrc/tree/icamerasrc_slim_api) - -| IPU Version | ipu-camera-bins | ipu-camera-hal | icamerasrc | -|-------------|------------------------------------------|------------------------------------------|------------------------------------------| -| IPU6 | 0b102acf2d95f86ec85f0299e0dc779af5fdfb81 | a647a0a0c660c1e43b00ae9e06c0a74428120f3a | 4fb31db76b618aae72184c59314b839dedb42689 | -| IPU7 | 2ef0857570b2dde3c2072fdacf22fdfff1a89bf2 | 3b9388ecdb682b6e7e9f57a4192b4612bfb43410 | 4fb31db76b618aae72184c59314b839dedb42689 | - -## Setup Procedure - -### Kernel Driver DKMS Build - -Initialize and update current repository recursively to ensure all dependencies are correctly fetched. - - git checkout main - git submodule update --init --recursive - -Build and install modules using DKMS - - 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 --force +- `6.xx.0/`: Kernel Dependent upstream/unpatched v4l2-core API codebase +- `drivers/`: Host Linux kernel drivers for supported sensors +- `helpers/`: v4l2 utils helpers script and udev to easily create gmsl camera media pipeline +- `config/`: Host Middleware Configuration files, BIOS setting and sample commands for supported sensors +- `doc/`: Documentation on Kernel Driver dependency on ipu6-drivers repository +- `patches/`: Host Dependent IPU v4l2 patches to enable specific sensors +- `include/`: Header files for driver compilation + +## Getting Developper Started with Reference GMSL Camera + +1. Clone this repository and checkout debian dev branch + +``` +export $HOME=$(pwd) +cd $HOME +git clone --recursive https://github.com/Pirouf/Intel-mipi-gmsl-modules.git -b 20260421/Q1.2-d4xx-max9672x intel-mipi-gmsl-drivers-debian-20260421 +``` + +2. Create origin debian source archive + +``` +tar czvf intel-mipi-gmsl-drivers_20260421.orig.tar.gz --exclude=".git" --exclude=".gitignore" --exclude=".gitmodules" --exclude="debian" --exclude=".pc" intel-mipi-gmsl-drivers-debian-20260421 +``` + +3. Install debian package dependencies (if done once already, you can skip it) + +``` +sudo apt install devscripts +cd $HOME/intel-mipi-gmsl-drivers-debian-20260421 +mk-build-deps -i --host-arch amd64 --build-arch amd64 -t "apt-get -y -q -o Debug::pkgProblemResolver=yes --no-install-recommends --allow-downgrades" debian/control +rm *.buildinfo *.changes *.deb +dpkg-buildpackage +``` + +4. Stitch the `0001-media-ctl-add-pad-support-2-digits-stream-id.patch` fix/workaround for `media-ctl` 1-digit stream-id limitation onto Debian latest `v4l-utils` v1.32 stable + +``` +cd $HOME +git clone https://salsa.debian.org/debian/libv4l.git -b debian/1.32.0-2 +cd $HOME/libv4l +mk-build-deps -i --host-arch amd64 --build-arch amd64 -t "apt-get -y -q -o Debug::pkgProblemResolver=yes --no-install-recommends --allow-downgrades" debian/control +rm *.buildinfo *.changes *.deb +export QUILT_PATCHES=debian/patches +export QUILT_REFRESH_ARGS="-p ab --no-timestamps --no-index" +quilt pop -af || true +quilt import -f -d n ${HOME}/intel-mipi-gmsl-drivers-debian-20260211/patches/libv4l/0001-media-ctl-add-pad-support-2-digits-stream-id.patch +quilt push -a +dpkg-buildpackage +``` + +5. Install local dkms and v4l-utils debian packages + +``` +cd $HOME +sudo apt install ./intel-mipi-gmsl-dkms_20260421-1_amd64.deb ./v4l-utils_1.32.0-2_amd64.deb ./libv4l*_1.32.0-2_amd64.deb +``` +6. Select max929x or max967xx deserializer to compile certain linux v4l2 i2c sensors driver with. + +7. Reboot in UEFI/BIOS menu, to enable Intel IPU in and configure Camera ACPI PDATA i2c and CSI2 controls under + +``` +systemctl reboot --firmware-setup + +``` + +Select `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> < Enable >`. +Configure each Camera ACPI device individually `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> Link Options`. +Select `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> Link Options -> User Custom`. + +For example Realsense D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) - one ACPI entry each D457 Cameras + +| UEFI Custom Sensor (D457) | Camera 1 | Camera 2 | Camera 3 | Camera 4 | +|------------------------------------------|------------|-----------|------------|------------| +| GMSL deserializer suffix | c | c | c | c | +| Custom HID | INTC10CD | INTC10CD | INTC10CD | INTC10CD | +| CSI bus-type | CPHY | CPHY | CPHY | CPHY | +| Rotation (d4xx deserializer input-link) | 0 | 90 | 180 | 270 | +| PPR Value (deserializer # of lanes) | 2 | 2 | 2 | 2 | +| PPR Unit (# of Cameras per-device) | 1 | 1 | 1 | 1 | +| Camera module label | d4xx | d4xx | d4xx | d4xx | +| MIPI Port (Index) | 2 | 2 | 2 | 2 | +| LaneUsed (serializer # of lanes) | x2 | x2 | x2 | x2 | +| Number of I2C | 3 | 3 | 3 | 3 | +| I2C Channel | I2C2 | I2C2 | I2C2 | I2C2 | +| Device0 I2C Address (sensor) | 12 | 13 | 14 | 15 | +| Device1 I2C Address (serializer) | 42 | 43 | 44 | 45 | +| Device2 I2C Address (deserializer) | 27 | 27 | 27 | 27 | + +Note: Using `INTC10CD` HID, the `Rotation` selection defines `d4xx-max96724` deserializer input-link mapping: GMSL A (`Rotation=0`) or GMSL B (`Rotation=90`) or GMSL C (`Rotation=180`) or D (`Rotation=270`) + +For example d3embedded ISX031 GMSL modules on IPU7 (Intel Core Ultra 3 - Panther Lake) - only one ACPI entry for all 4 ISX031 Cameras + +| UEFI Custom Sensor (ISX031) | Camera 1 | +|------------------------------------------|------------| +| GMSL deserializer suffix | c | +| Custom HID | INTC031M | +| CSI bus-type | CPHY | +| Rotation (max9x deserializer out-link) | 180 | +| PPR Value (deserializer # of lanes) | 2 | +| PPR Unit (# of Cameras per-device) | 4 | +| Camera module label | d4xx | +| MIPI Port (Index) | 2 | +| LaneUsed (serializer # of lanes) | x4 | +| Number of I2C | 3 | +| I2C Channel | I2C2 | +| Device0 I2C Address (deserilizer) | 27 | +| Device1 I2C Address (serializer) | 42 | +| Device2 I2C Address (sensor) | 12 | + +Note: Using `INTC031M` HID, the `Rotation` selection defines `max9x` deserializer output-link mapping: CSI PHY A (`Rotation=0`) or CSI PHY C (`Rotation=180`) + +8. User to need to double check and edit `modprobe.d` module config file if necessary to configure it for `max9296` or `max967xx` add-on board + +For example of 4x Realsense D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) + +``` +# modinfo d4xx +filename: /lib/modules/6.17.0-1010-oem/updates/dkms/d4xx.ko.zst +version: 20260421-0eci1 +license: GPL v2 +author: Guennadi Liakhovetski , + Nael Masalha , + Alexander Gantman , + Emil Jahshan , + Xin Zhang , + Qingwu Zhang , + Evgeni Raikhel , + Shikun Ding , + Florent Pirou , + Dmitry Perchanov +description: RealSense D4XX Camera Driver +srcversion: B6A9500EDADC591780A52F9 +alias: i2c:d4xx-awg +alias: i2c:d4xx-asr +alias: i2c:d4xx +alias: of:N*T*Cintel,d4xxC* +alias: of:N*T*Cintel,d4xx +depends: d4xx-max9295,d4xx-max96724,v4l2-async,videodev,mc +name: d4xx +retpoline: Y +vermagic: 6.17.0-1010-oem SMP preempt mod_unload modversions +sig_id: PKCS#7 +signer: eci-rvp-ptl-gmsl-4 Secure Boot Module Signature key +sig_key: 1F:18:1B:36:4E:5E:1C:D9:C7:D9:7D:AB:F2:A6:78:C8:A5:07:8F:A5 +sig_hashalgo: sha512 +signature: 24:9D:08:A3:72:A4:97:CF:F1:B5:47:B3:A3:07:88:7F:0B:1B:66:FD: + 7F:A8:AD:02:A9:A8:BB:EC:B5:AE:AB:6E:A2:F2:5C:8A:E6:E2:68:97: + BA:A2:CA:53:00:54:B6:44:4D:FF:CE:80:ED:4D:BE:16:2D:DF:C0:65: + 1B:DD:D0:24:E6:B6:1E:06:06:C3:A1:87:92:2F:E4:8E:AE:C7:88:12: + C0:1B:6A:B6:A4:6B:5D:A9:A5:B3:57:61:38:1A:E1:05:FB:4A:E3:A3: + 8F:26:C3:EC:2C:3C:2F:04:EA:96:CE:3A:8D:D0:F5:8F:64:0F:5E:49: + A3:03:5F:B6:40:E0:37:9C:E6:12:92:34:AE:53:6B:38:74:6E:8B:D9: + EA:B4:AA:EE:50:21:41:84:4E:51:9E:0A:B7:5E:40:D3:82:A2:8E:92: + 2E:86:35:31:AE:64:AC:8C:4A:27:FF:F5:81:FB:D8:EC:7C:E2:87:11: + D7:E9:23:FB:A2:BE:E2:75:CC:AC:58:2F:67:38:BE:61:C6:64:FF:5E: + 1A:EF:E3:35:F9:3D:96:CF:CC:71:08:F1:5A:BD:50:FD:A4:2F:C0:30: + 99:E2:85:11:BD:5F:D9:07:A9:BA:AE:6B:20:78:16:AE:F3:DE:97:5A: + 7A:4B:E4:EB:28:BF:94:BE:AE:E4:26:6A:18:54:84:AE +parm: sensor_vc:VC set for sensors + sensor_vc=0,1,2,3,2,3,0,1,1,0,3,2,3,2,1,0 (array of ushort) +parm: serdes_bus:d4xx-max96724 deserializer i2c bus + serdes_bus=0,0,1,1 (array of ushort) +parm: des_addr:d4xx-max96724 deserializer i2c address + des_addr=0x27,0x27,0x27,0x27 (array of ushort) +parm: des_csi:d4xx-max96724 deserializer csi output + des_csi=0,2,0,2 (array of ushort) + +``` + +``` +# uncomment d4xx driver boot options corresponding to your Intel platform +# +# MTL GMSL2 AIC max9296 device generally on i2c-2 and i2c-3 buses +#options d4xx des_addr=0x48,0x4a,0x48,0x4a serdes_bus=2,2,3,3 +# ARL GMSL2 AIC max9296 device generally on i2c-0 and i2c-3 buses +#options d4xx des_addr=0x48,0x4a,0x48,0x4a serdes_bus=0,0,3,3 +# ADL/RPL/ASL GMSL2 AIC max9296 device generally on i2c-4 bus +#options d4xx des_addr=0x48,0x4a,0x68,0x6c serdes_bus=4,4,4,4 +# ARL GMSL2 AIC dual-max96712 device generally on i2c-0 bus +#options d4xx des_addr=0x6b,0x6b,0x29,0x29 serdes_bus=0,0,0,0 + +# PTL GMSL2 AIC dual-max96724 device generally on i2c-0 and i2c-1 buses +options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 +# PTL GMSL2 AIC dual-max96724 device generally on i2c-0 and i2c-1 buses (with VC_EXTENSION enabled) +#options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 sensor_vc=0,1,2,3,2,3,0,1,5,4,7,6,7,6,5,4 +# !!WARNING!! Intel Servoss Mountain 1.0 (Fab A) dual-max96724 board cphy CRD connectivity is inverted +# max96724 csi 0 CPHY-2T DA0/DA1 maps to ipu7 isys csi 2 +# max96724 csi 2 CPHY-2T DB0/DB1 maps to ipu7 isys csi 0 +#options d4xx des_addr=0x27,0x27,0x27,0x27 serdes_bus=1,1,2,2 des_csi=2,2,0,0 +# + +#options d4xx dyndbg +#options d4xx-max9295 dyndbg +#options d4xx-max9296 dyndbg +#options d4xx-max96724 dyndbg + +``` + +``` +modprobe --show-depends d4xx +insmod /lib/modules/6.17.0-1010-oem/kernel/drivers/media/mc/mc.ko.zst +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/videodev.ko.zst +insmod /lib/modules/6.17.0-1010-oem/kernel/drivers/media/v4l2-core/v4l2-async.ko.zst +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/d4xx-max96724.ko.zst dyndbg +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/d4xx-max9295.ko.zst +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/d4xx.ko.zst des_addr=0x27,0x27,0x27,0x27 serdes_bus=0,0,1,1 des_csi=0,0,2,2 + +``` + +For example of 4x D3 ISX031 + +``` +modinfo isx031 +filename: /lib/modules/6.17.0-1010-oem/updates/dkms/isx031.ko.zst +license: GPL v2 +author: Wei Khang, Goh +author: Jonathan Lui +author: Hao Yao +description: isx031 sensor driver +srcversion: 12A145D4C2F70B04B1E8D25 +alias: i2c:isx031 +alias: acpi*:INTC113C:* +depends: videodev,mc,v4l2-fwnode,v4l2-async +name: isx031 +retpoline: Y +vermagic: 6.17.0-1010-oem SMP preempt mod_unload modversions +sig_id: PKCS#7 +signer: eci-rvp-ptl-gmsl-4 Secure Boot Module Signature key +sig_key: 1F:18:1B:36:4E:5E:1C:D9:C7:D9:7D:AB:F2:A6:78:C8:A5:07:8F:A5 +sig_hashalgo: sha512 +signature: 57:81:3B:A6:5C:99:C4:26:46:7D:32:A0:5C:49:4D:8A:31:1B:EA:E1: + 51:A5:B6:CF:2A:8D:F7:AD:8E:F3:01:F2:D3:1E:8E:3F:32:69:FF:59: + 40:27:13:98:56:2F:72:AD:8E:F5:52:FC:91:CB:68:81:A2:9C:DE:23: + 08:9F:51:DB:4D:16:B6:FD:2F:E4:DC:EF:B3:76:59:3B:DF:4D:64:03: + E1:20:9D:06:83:7F:94:3E:84:AA:47:57:F6:CA:58:24:89:72:42:9A: + 4B:9A:24:FF:BD:B5:17:78:D4:F3:B8:7F:18:0E:F8:33:62:D7:71:04: + 52:75:B8:B9:4D:2F:68:53:31:08:12:3E:D9:97:76:16:FA:1A:62:9C: + 00:ED:12:2E:B3:B7:4D:92:6A:DD:1D:74:20:4A:89:58:29:0A:C8:8A: + 01:89:32:15:55:3C:38:94:71:F7:40:98:80:5D:15:28:E0:2B:8E:0B: + E7:DD:E7:38:E6:A8:47:61:8D:25:66:A3:2A:FB:4A:45:46:3A:5E:EE: + 9B:9E:73:16:69:8A:31:90:D2:71:D0:BA:A5:94:79:C5:E1:59:12:4C: + C0:8E:F6:76:2C:A5:B8:91:5F:B4:DA:21:53:F5:AF:87:CA:8F:67:A4: + 68:79:FC:22:8C:B9:96:F2:19:94:8D:42:30:25:BF:2C +``` + +``` +modprobe --show-depends isx031 +insmod /lib/modules/6.17.0-1010-oem/kernel/drivers/media/mc/mc.ko.zst +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/videodev.ko.zst +insmod /lib/modules/6.17.0-1010-oem/kernel/drivers/media/v4l2-core/v4l2-async.ko.zst +insmod /lib/modules/6.17.0-1010-oem/kernel/drivers/media/v4l2-core/v4l2-fwnode.ko.zst +insmod /lib/modules/6.17.0-1010-oem/updates/dkms/isx031.ko.zst +``` + +9. load IPU ISYS module and bind ALL sensors as v4l2 media pipeline graph. + +``` +modprobe intel-ipu7-isys +``` + +For example of 4x D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) - + +``` +/usr/share/camera/rs_ipu_d457_bind.sh -n -q && /usr/share/camera/rs-enum-ipu.sh -n +(count=1 on IPU7 CSI 2)... +(count=2 on IPU7 CSI 2)... +(count=3 on IPU7 CSI 2)... +(count=4 on IPU7 CSI 2)... +Bus Camera Sensor Node Type Video Node RS Link + ipu7 a-2 ir Inactive /dev/video37 /dev/video-rs-ir-8 + ipu7 a-2 depth Stream(id=0) /dev/video33 /dev/video-rs-depth-8 + ipu7 a-2 imu Inactive /dev/video38 /dev/video-rs-imu-8 + ipu7 a-2 color Stream(id=2) /dev/video35 /dev/video-rs-color-8 + i2c a-2 d4xx Firmware /dev/d4xx-dfu-a-2 /dev/d4xx-dfu-8 + ipu7 b-2 ir Inactive /dev/video43 /dev/video-rs-ir-9 + ipu7 b-2 depth Stream(id=6) /dev/video39 /dev/video-rs-depth-9 + ipu7 b-2 imu Inactive /dev/video44 /dev/video-rs-imu-9 + ipu7 b-2 color Stream(id=8) /dev/video41 /dev/video-rs-color-9 + i2c b-2 d4xx Firmware /dev/d4xx-dfu-b-2 /dev/d4xx-dfu-9 + ipu7 c-2 ir Inactive /dev/video45 /dev/video-rs-ir-10 + ipu7 c-2 depth Stream(id=1) /dev/video34 /dev/video-rs-depth-10 + ipu7 c-2 imu Inactive /dev/video46 /dev/video-rs-imu-10 + ipu7 c-2 color Stream(id=3) /dev/video36 /dev/video-rs-color-10 + i2c c-2 d4xx Firmware /dev/d4xx-dfu-c-2 /dev/d4xx-dfu-10 + ipu7 d-2 ir Inactive /dev/video47 /dev/video-rs-ir-11 + ipu7 d-2 depth Stream(id=7) /dev/video40 /dev/video-rs-depth-11 + ipu7 d-2 imu Inactive /dev/video48 /dev/video-rs-imu-11 + ipu7 d-2 color Stream(id=9) /dev/video42 /dev/video-rs-color-11 + i2c d-2 d4xx Firmware /dev/d4xx-dfu-d-2 /dev/d4xx-dfu-11 +``` +note: v4l2 limites to 8 streams/routes max per CSI ports has priortized depth+rgb + +For example of selecting only 1-out-of-4 D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) + +``` +# /usr/share/camera/rs_ipu_d457_bind.sh -n -m a-2 -q && /usr/share/camera/rs-enum-ipu.sh -n +(count=1 on IPU7 CSI 2)... +Bus Camera Sensor Node Type Video Node RS Link + ipu7 a-2 ir Stream(id=4) /dev/video37 /dev/video-rs-ir-8 + ipu7 a-2 depth Stream(id=0) /dev/video33 /dev/video-rs-depth-8 + ipu7 a-2 imu Stream(id=5) /dev/video38 /dev/video-rs-imu-8 + ipu7 a-2 color Stream(id=2) /dev/video35 /dev/video-rs-color-8 + i2c a-2 d4xx Firmware /dev/d4xx-dfu-a-2 /dev/d4xx-dfu-8 +``` + +For example of 4x D3embedded ISX031 on IPU7 (Intel Core Ultra 3 - Panther Lake) + +``` +./helpers/ipu_max9x_bind.sh -q -s isx031 +Bind IPU7 to isx031 a-2 through max9x c .. +Bind IPU7 to isx031 b-2 through max9x c .. +Bind IPU7 to isx031 c-2 through max9x c .. +Bind IPU7 to isx031 d-2 through max9x c .. +``` + +10. Enjoy your GMSL cameras streaming on Intel IPU MIPI CSI2 ports! + +For example 4x D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) - + +``` +rs-enumerate-devices -S +Device Name Serial Number Firmware Version +Intel RealSense D457 241122303302 5.17.0.10 +Intel RealSense D457 242422303692 5.17.0.10 +Intel RealSense D457 242422304793 5.17.0.10 +Intel RealSense D457 242422303456 5.17.0.10 +Device info: + Name : Intel RealSense D457 + Serial Number : 241122303302 + Firmware Version : 5.17.0.10 + Recommended Firmware Version : 5.17.0.9 + Physical Port : /dev/video-rs-depth-10 + Debug Op Code : 15 + Advanced Mode : YES + Product Id : ABCD + Camera Locked : YES + Product Line : D400 + Asic Serial Number : 321543110732 + Firmware Update Id : 321543110732 + Dfu Device Path : /dev/d4xx-dfu-10 + Connection Type : GMSL + +Device info: + Name : Intel RealSense D457 + Serial Number : 242422303692 + Firmware Version : 5.17.0.10 + Recommended Firmware Version : 5.17.0.9 + Physical Port : /dev/video-rs-depth-11 + Debug Op Code : 15 + Advanced Mode : YES + Product Id : ABCD + Camera Locked : YES + Product Line : D400 + Asic Serial Number : 250343111497 + Firmware Update Id : 250343111497 + Dfu Device Path : /dev/d4xx-dfu-11 + Connection Type : GMSL + +Device info: + Name : Intel RealSense D457 + Serial Number : 242422304793 + Firmware Version : 5.17.0.10 + Recommended Firmware Version : 5.17.0.9 + Physical Port : /dev/video-rs-depth-8 + Debug Op Code : 15 + Advanced Mode : YES + Product Id : ABCD + Camera Locked : YES + Product Line : D400 + Asic Serial Number : 252143110007 + Firmware Update Id : 252143110007 + Dfu Device Path : /dev/d4xx-dfu-8 + Connection Type : GMSL + +Device info: + Name : Intel RealSense D457 + Serial Number : 242422303456 + Firmware Version : 5.17.0.10 + Recommended Firmware Version : 5.17.0.9 + Physical Port : /dev/video-rs-depth-9 + Debug Op Code : 15 + Advanced Mode : YES + Product Id : ABCD + Camera Locked : YES + Product Line : D400 + Asic Serial Number : 245543111541 + Firmware Update Id : 245543111541 + Dfu Device Path : /dev/d4xx-dfu-9 + Connection Type : GMSL +``` + +or + +``` +gst-launch-1.0 -e -v \ +v4l2src device=/dev/video-rs-color-8 ! 'video/x-raw, width=640, height=480, format=YUY2, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_0 \ +v4l2src device=/dev/video-rs-color-9 ! 'video/x-raw, width=640, height=480, format=YUY2, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_1 \ +v4l2src device=/dev/video-rs-color-10 ! 'video/x-raw, width=640, height=480, format=YUY2, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_2 \ +v4l2src device=/dev/video-rs-color-11 ! 'video/x-raw, width=640, height=480, format=YUY2, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_3 \ +compositor name=comp \ +sink_0::xpos=0 sink_0::ypos=0 sink_0::width=640 sink_0::height=480 sink_0::zorder=1 \ +sink_1::xpos=640 sink_1::ypos=0 sink_1::width=640 sink_1::height=480 sink_1::zorder=2 \ +sink_2::xpos=0 sink_2::ypos=480 sink_0::width=640 sink_2::height=480 sink_2::zorder=3 \ +sink_3::xpos=640 sink_3::ypos=480 sink_3::width=640 sink_3::height=480 sink_3::zorder=2 ! videoconvert ! fakesink +``` + +For example 4x D3embedded ISX031 GMSL modules on IPU7 (Intel Core Ultra 3 - Panther Lake) + +``` +gst-launch-1.0 -e -v \ +v4l2src device=/dev/video-isx031-a-2 ! 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_0 \ +v4l2src device=/dev/video-isx031-b-2 ! 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_1 \ +v4l2src device=/dev/video-isx031-c-2 ! 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_2 \ +v4l2src device=/dev/video-isx031-d-2 ! 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_3 \ +compositor name=comp \ +sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ +sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ +sink_2::xpos=0 sink_2::ypos=1536 sink_2::width=1920 sink_2::height=1536 sink_2::zorder=3 \ +sink_3::xpos=1920 sink_3::ypos=1536 sink_3::width=1920 sink_3::height=1536 sink_3::zorder=4 ! videoconvert ! fakesink + +``` + +## Documentation + +Detailed documentation for each sensor can be found in the [doc/](doc) directory, organized by sensor model. ### BIOS Configuration diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..83a77ee4 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,59 @@ +intel-mipi-gmsl-drivers (20260421-1) UNRELEASED; urgency=low + + * initial package based of Intel MIPI-CSI Camera Reference drivers [Release 2026 Q1.2](https://github.com/intel/Intel-MIPI-CSI-Camera-Reference-Driver/commit/9ae0a6ace8aeb7d35b7c77fc79c188e620f964e8) + + -- ECI Maintainer Wed, 29 Apr 2026 20:16:24 +0000 + +intel-mipi-gmsl-drivers (20260211-4) UNRELEASED; urgency=low + + * ipu7-isys: workaround isys-queue noise in HW_ERR_BAD_FRAME_DIM conditions + * helpers: add isx031 mtbf testcases + * helpers: bind 8-only srcpad ipu7-isys and ipu6-isys subdevs media graph + + -- ECI Maintainer Wed, 25 Mar 2026 12:32:36 +0000 + +intel-mipi-gmsl-drivers (20260211-3) UNRELEASED; urgency=low + + * ipu-acpi : fallback for uefi camera parameters without the `rotation` entry + * ipu6-isys: workaround ipu6-isys STREAM_CAPTURE fw cmd race-conditions + + -- ECI Maintainer Wed, 25 Mar 2026 12:32:36 +0000 + +intel-mipi-gmsl-drivers (20260211-2) UNRELEASED; urgency=low + + * d4xx : fix max96724 realsense2 multicam and multistream support + * ipu7-isys: workaround csi2 unsupported YUYV8_1X16 css pixelformat + + -- ECI Maintainer Fri, 20 Mar 2026 13:57:30 +0000 + +intel-mipi-gmsl-drivers (20260211-1) UNRELEASED; urgency=low + + * d4xx : add d4xx realsense2 custom max9295, max9296 and max96724 implementations + * d4xx : add d4xx realsense2 v4l2 graph binding helper scripts + * ipu6-isys, ipu6-isys: add d4xx video VIDIOC_S_EXT_CTRLS by streamid + + -- ECI Maintainer Mon, 15 Feb 2026 13:20:02 +0000 + +intel-mipi-gmsl-drivers (20260211-0) UNRELEASED; urgency=low + + * initial package based of Intel MIPI-CSI Camera Reference drivers [Release 2026 Q1.1.1a](https://github.com/intel/Intel-MIPI-CSI-Camera-Reference-Driver/commit/96ae361c20e778efe53426848b89528205d18ad3) + + -- ECI Maintainer Wed, 11 Feb 2026 09:24:00 +0000 + +intel-mipi-gmsl-drivers (20260115-2) UNRELEASED; urgency=low + + * debian : add postinstall and debconf + * d4xx : add d4xx realsense2 custom max9295, max9296 and max96724 implementations + * d4xx : add d4xx realsense2 v4l2 graph binding helper scripts + * ipu6-isys, ipu6-isys: add d4xx video VIDIOC_S_EXT_CTRLS by streamid + + -- ECI Maintainer Thu, 19 Jan 2026 14:46:17 +0000 + +intel-mipi-gmsl-drivers (20260115-1) UNRELEASED; urgency=low + + * debian : initial package rules and control + * ipu7-drivers : add baseline IPU7 20251226_1140_191_PTL_PV_IoT + * ipu6-drivers : add baseline IPU6 & PTL release for iot on 2025-12-30 + * v4l2-core : add linux 6.12, 6.17 and 6.18 stream and route v4l2 api enabled videodev + + -- ECI Maintainer Thu, 19 Jan 2026 14:46:17 +0000 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..5c16c1c5 --- /dev/null +++ b/debian/control @@ -0,0 +1,29 @@ +Source: intel-mipi-gmsl-drivers +Section: kernel +Priority: optional +Maintainer: ECI Maintainer +Rules-Requires-Root: no +Build-Depends: + debhelper-compat (= 13), + dh-modaliases, + dkms, + dh-sequence-dkms, + quilt, +Standards-Version: 4.7.0 +Homepage: https://github.com/intel/Intel-MIPI-CSI-Camera-Reference-Driver + +Package: intel-mipi-gmsl-dkms +Architecture: amd64 +Depends: + dkms, + pahole, + v4l-utils, + ${misc:Depends}, +Provides: ipu7-modules +Conflicts: ipu7-modules, intel-ipu6-dkms +Replaces: ipu7-modules, intel-ipu6-dkms +XB-Modaliases: ${modaliases} +Description: Intel Integrated Image Processing Unit 6/7 (IPU6/IPU7) drivers + This package provides kernel drivers for GMSL2 cameras through the Intel IPU7 + on Intel Panther Lake and the Intel IPU6 + on Intel Alder Lake, Raptor Lake, Meteor Lake and Arrow lake platforms. diff --git a/debian/dkms.conf.in b/debian/dkms.conf.in new file mode 100644 index 00000000..51a9b5bc --- /dev/null +++ b/debian/dkms.conf.in @@ -0,0 +1,250 @@ +PACKAGE_NAME="intel-mipi-gmsl-dkms" +PACKAGE_VERSION="@DKMS_PACKAGE_VERSION@" +BUILD_EXCLUSIVE_KERNEL="^(7\.([0])\.)|^(6\.(1[278])\.)|^6\.(1[27])rt-intel|^6\.(1[27])-intel" +VERSION_SUFFIX="${PACKAGE_VERSION}" + +KBASE=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build +#Extract kernel version substring that is used to identify source directory for kernel modules +# i.e 6.12.8-intel-ese-experimental-lts-rt ==> 6.12.0 +# i.e 6.17.0-oem-1011 ==> 6.17.0 +# i.e 6.17rt-intel ==> 6.17.0 +# i.e 6.18.6-intel-ese-experimental-lts-rt ==> 6.18.0 +SELECTED_KERNEL=$(echo $kernelver | cut -d- -f1 | sed -r 's/([0-9]+\.[0-9]+).*/\1.0/g') +KERNEL_VER=$(echo $kernelver | cut -d. -f1) +KERNEL_PATCH=$(echo $kernelver | cut -d. -f2 | cut -d- -f1) + +RUNTIME_KERNEL_VER=$(uname -r) + +#This variable is required to address includes from videodev.ko +srctree=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build + +#We will invoke multiple make directives to build all the required kernel modules, then manually copy them to the staged directory for dkms install rule +MAKE="if [ '${RUNTIME_KERNEL_VER}' == '${kernelver}' ] && [ -f $kernel_source_dir/tools/bpf/resolve_btfids/resolve_btfids ] && [ -f /sys/kernel/btf/vmlinux ]; then ln -s /sys/kernel/btf/vmlinux $kernel_source_dir/vmlinux; fi ;make V=1 KERNELRELEASE=$kernelver DRIVER_VERSION_SUFFIX='${VERSION_SUFFIX}' CFLAGS_MODULE=\"$(echo $MAKE_OPTS)\" KERNEL_SRC=$kernel_source_dir" + +MAKE_MATCH[1]="^(7\.([0])\.)|^(6.1[278])" + +CLEAN="if [ -f $kernel_source_dir/vmlinux ]; then rm $kernel_source_dir/vmlinux; fi;make V=1 KERNELRELEASE=$kernelver KERNEL_SRC=$kernel_source_dir clean" + +AUTOINSTALL="yes" +BUILD_EXCLUSIVE_CONFIG="CONFIG_VIDEO_V4L2_I2C" + +MODULE_PATH="drivers/media/i2c" +MODULE_DEST="/kernel/drivers/media/i2c/" + +j=0 + +# V4l2 core patches OoT patches +# kernel patches for Linux 6.12 +PATCH[$j]="0001-dkms-v4l2-core-makefile-adaptation-6.12.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0002-dkms-disable-tuner-build-in-v4l2-core-6.12.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0003-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(6.1[2])" +# kernel patches for Linux 6.17 +PATCH[$((++j))]="0004-dkms-v4l2-core-makefile-adaptation-6.17.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0005-dkms-disable-tuner-build-in-v4l2-core-6.17.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0006-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(6.1[7])" +# kernel patches for Linux 6.18 +PATCH[$((++j))]="0007-dkms-v4l2-core-makefile-adaptation-6.18.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0008-dkms-disable-tuner-build-in-v4l2-core-6.18.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0009-Revert-media-v4l2-subdev-Make-struct-v4l2_subdev_str.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0010-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(6.1[8])" +# kernel patches for Linux 7.0 +PATCH[$((++j))]="0011-dkms-v4l2-core-makefile-adaptation-7.0.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0012-dkms-disable-tuner-build-in-v4l2-core-7.0.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0013-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch" +PATCH_MATCH[$j]="^(7.[0])" + +# IPU6 drivers OoT patches +PATCH[$((++j))]="0050-isys-video-let-v4l2-set-default-colorspace.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0051-ipu6-dkms-add-isys-makefile-adaptation-6.12.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0052-ipu6-isys-disable-src_pad-validation-for-dkms.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0053-Revert-ipu6-isys-lt6911uxc-2-pads-linked-to-ipu-2-po.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0054-module-Add-version-conditional-for-symbol-namespace-.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" + +# IPU7 drivers OoT patches +PATCH[$((++j))]="0100-isys-video-let-v4l2-set-default-colorspace.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0101-ipu7-isys-make-modules-suffix-versioning-same-as-dkm.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" + +i=0 + +BUILT_MODULE_NAME[$i]="ipu-acpi" +BUILT_MODULE_LOCATION[$i]="drivers/media/platform/intel" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ipu-acpi-pdata" +BUILT_MODULE_LOCATION[$i]="drivers/media/platform/intel" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ipu-acpi-common" +BUILT_MODULE_LOCATION[$i]="drivers/media/platform/intel" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ar0233" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ar0234" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ar0830" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="ar0820" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="isx031" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="max9x" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c/max9x" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +# IPU6 drivers d4xx OoT patches +PATCH[$((++j))]="0055-ipu6-isys-allow-virtual-channels-on-16-csi2-input-vi.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0056-ipu6-isys-add-d4xx-pixel-format-support.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0057-ipu6-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0058-ipu6-isys-video-force-START-then-CAPTURE-distinct-st.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" + +# IPU7 drivers d4xx OoT patches +PATCH[$((++j))]="0102-ipu7-isys-allow-virtual-channels-on-16-csi2-input-vi.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0103-ipu7-isys-add-d4xx-pixel-format-support.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0104-ipu7-isys-add-video-VIDIOC_S_EXT_CTRLS-callbacks-by-.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0105-isys-queue-ignore-HW_ERR_BAD_FRAME_DIM-led-expected-.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" +PATCH[$((++j))]="0106-ipu7-isys-enable-cphy-dphy-5-4-bits-vc-extension.patch" +PATCH_MATCH[$j]="^(6.1[278])|^(7.[0])" + +BUILT_MODULE_NAME[$((++i))]="d4xx" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c/d4xx-compat" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="d4xx-max9295" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c/d4xx-compat" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="d4xx-max9296" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c/d4xx-compat" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="d4xx-max96724" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c/d4xx-compat" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +# IPU6 drivers from submodule +BUILT_MODULE_NAME[$((++i))]="intel-ipu6" +BUILT_MODULE_LOCATION[$i]="ipu6-drivers/drivers/media/pci/intel/ipu6" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="intel-ipu6-isys" +BUILT_MODULE_LOCATION[$i]="ipu6-drivers/drivers/media/pci/intel/ipu6" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="intel-ipu6-psys" +BUILT_MODULE_LOCATION[$i]="ipu6-drivers/drivers/media/pci/intel/ipu6/psys" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +# kernel patches for Linux 6.12 +PATCH[$((++j))]="0200-ipu-bridge-port-6.12.0-to-bkc.patch" +PATCH_MATCH[$j]="^(6.1[2]-intel)" +PATCH[$((++j))]="0201-ipu-bridge-6.12-makefile.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0202-ipu-bridge-6.12-bkc-backport.patch" +PATCH_MATCH[$j]="^(6.1[2])" +PATCH[$((++j))]="0203-ipu-bridge-6.12-add-sensors.patch" +PATCH_MATCH[$j]="^(6.1[2])" +# kernel patches for Linux 6.17 +PATCH[$((++j))]="0204-ipu-bridge-6.17-makefile.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0205-ipu-bridge-6.17-bkc-backport.patch" +PATCH_MATCH[$j]="^(6.1[7])" +PATCH[$((++j))]="0206-ipu-bridge-6.17-add-sensors.patch" +PATCH_MATCH[$j]="^(6.1[7])" +# kernel patches for Linux 6.18 +PATCH[$((++j))]="0207-ipu-bridge-6.18-makefile.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0208-ipu-bridge-6.18-bkc-backport.patch" +PATCH_MATCH[$j]="^(6.1[8])" +PATCH[$((++j))]="0209-ipu-bridge-6.18-add-sensors.patch" +PATCH_MATCH[$j]="^(6.1[8])" +# kernel patches for Linux 7.0 +PATCH[$((++j))]="0210-ipu-bridge-7.0-makefile.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0211-ipu-bridge-7.0-bkc-backport.patch" +PATCH_MATCH[$j]="^(7.[0])" +PATCH[$((++j))]="0212-ipu-bridge-7.0-add-sensors.patch" +PATCH_MATCH[$j]="^(7.[0])" + +BUILT_MODULE_NAME[$((++i))]="ipu-bridge" +BUILT_MODULE_LOCATION[$i]="${SELECTED_KERNEL}/drivers/media/pci/intel" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="videodev" +BUILT_MODULE_LOCATION[$i]="${SELECTED_KERNEL}/drivers/media/v4l2-core/" +DEST_MODULE_NAME[$i]="videodev" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +if [ "$KERNEL_VER" -ge 6 ] && [ "$KERNEL_PATCH" -ge 17 ]; then +# IPU7 drivers from submodule +BUILT_MODULE_NAME[$((++i))]="intel-ipu7" +BUILT_MODULE_LOCATION[$i]="ipu7-drivers/drivers/media/pci/intel/ipu7" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="intel-ipu7-isys" +BUILT_MODULE_LOCATION[$i]="ipu7-drivers/drivers/media/pci/intel/ipu7" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +BUILT_MODULE_NAME[$((++i))]="intel-ipu7-psys" +BUILT_MODULE_LOCATION[$i]="ipu7-drivers/drivers/media/pci/intel/ipu7/psys" +DEST_MODULE_LOCATION[$i]="/updates" +STRIP[$i]=no + +fi diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 00000000..f1e4983c --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,5 @@ +[DEFAULT] +debian-branch = debian/latest +upstream-branch = upstream/latest +pristine-tar = False +git-ignore-new = True diff --git a/debian/intel-mipi-gmsl-dkms.config.in b/debian/intel-mipi-gmsl-dkms.config.in new file mode 100644 index 00000000..0f0df01f --- /dev/null +++ b/debian/intel-mipi-gmsl-dkms.config.in @@ -0,0 +1,28 @@ +#!/bin/bash +# inspired by http://www.fifi.org/doc/debconf-doc/tutorial.html + +# Exit on error +set -e +# Source debconf library. +. /usr/share/debconf/confmodule +db_version 2.0 + +# This conf script is capable of backing up +#db_capb backup + +declare -A FEATURES=@deserlist@ +STATE="max9x" + +while [ "$STATE" != "quit" -a "$STATE" != "done" ]; do + case "$STATE" in + max9x) + # query max9x + db_input critical intel-mipi-gmsl-dkms/max9x || true + if db_go; then + STATE="quit" + else + STATE="done" + fi + ;; + esac +done diff --git a/debian/intel-mipi-gmsl-dkms.install.in b/debian/intel-mipi-gmsl-dkms.install.in new file mode 100644 index 00000000..9951e145 --- /dev/null +++ b/debian/intel-mipi-gmsl-dkms.install.in @@ -0,0 +1,18 @@ +debian/dkms.conf /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +Makefile /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +drivers /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +include /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +ipu6-drivers /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +ipu7-drivers /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +6.12.0 /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +6.17.0 /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +6.18.0 /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +patches /usr/src/@DKMS_PACKAGE_NAME@-@DKMS_PACKAGE_VERSION@ +helpers/blacklist-intel-ipu.conf /etc/modprobe.d +helpers/d4xx.conf /etc/modprobe.d +helpers/ipu_ops.conf /etc/modprobe.d +helpers/rs-enum-ipu.sh /usr/share/camera +helpers/ipu_max9x_bind.sh /usr/share/camera +helpers/rs_ipu_d457_bind.sh /usr/share/camera +helpers/99-ipu-gmsl-mipi.rules /lib/udev/rules.d +helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh /usr/share/camera diff --git a/debian/intel-mipi-gmsl-dkms.modaliases b/debian/intel-mipi-gmsl-dkms.modaliases new file mode 100644 index 00000000..d8536b3b --- /dev/null +++ b/debian/intel-mipi-gmsl-dkms.modaliases @@ -0,0 +1,9 @@ +alias pci:v00008086d0000645Dsv*sd*bc*sc*i* intel-ipu7 +alias pci:v00008086d0000B05Dsv*sd*bc*sc*i* intel-ipu7 +alias pci:v00008086d0000D719sv*sd*bc*sc*i* intel-ipu7 +alias pci:v00008086d0000462Esv*sd*bc*sc*i* intel-ipu6-psys +alias pci:v00008086d0000465Dsv*sd*bc*sc*i* intel-ipu6-psys +alias pci:v00008086d00004E19sv*sd*bc*sc*i* intel-ipu6-psys +alias pci:v00008086d00007D19sv*sd*bc*sc*i* intel-ipu6-psys +alias pci:v00008086d00009A19sv*sd*bc*sc*i* intel-ipu6-psys +alias pci:v00008086d0000A75Dsv*sd*bc*sc*i* intel-ipu6-psys diff --git a/debian/intel-mipi-gmsl-dkms.postinst.in b/debian/intel-mipi-gmsl-dkms.postinst.in new file mode 100644 index 00000000..49573227 --- /dev/null +++ b/debian/intel-mipi-gmsl-dkms.postinst.in @@ -0,0 +1,50 @@ +# Automatically added by dh_dkms/3.0.10-7 +# The original file can be found in template-dkms-mkdeb/debian/postinst +# in the DKMS tarball, check it for copyright notices. + +DKMS_NAME=@DKMS_PACKAGE_NAME@ +DKMS_PACKAGE_NAME=@DKMS_PACKAGE_NAME@ +DKMS_VERSION=@DKMS_PACKAGE_VERSION@ + +postinst_found=0 + +set -e + +. /usr/share/debconf/confmodule + +db_get ${DKMS_PACKAGE_NAME}/max9x +MAX9X=${RET} + +case "$1" in + configure) + echo "postinst d4xx build for ${MAX9X}" >&2 + if [ "$MAX9X" = "max9296" ]; then + echo "clear MAKE_OPTS" >&2 + export MAKE_OPTS="" + fi + if [ "$MAX9X" = "max96724" ]; then + echo "apply MAKE_OPTS=\"-DCONFIG_VIDEO_D4XX_MAX96724\"" >&2 + export MAKE_OPTS="-DCONFIG_VIDEO_D4XX_MAX96724" + fi + if [ "$MAX9X" = "max96712" ]; then + echo "apply MAKE_OPTS=\"-DCONFIG_VIDEO_D4XX_MAX96712\"" >&2 + export MAKE_OPTS="-DCONFIG_VIDEO_D4XX_MAX96712" + fi + + for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do + if [ -f $DKMS_POSTINST ]; then + $DKMS_POSTINST $DKMS_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2 + postinst_found=1 + break + fi + done + if [ "$postinst_found" -eq 0 ]; then + echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not" + echo "built with legacy DKMS support." + echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst" + echo "support or upgrade DKMS to a more current version." + exit 1 + fi + ;; +esac +# End automatically added section diff --git a/debian/intel-mipi-gmsl-dkms.templates.in b/debian/intel-mipi-gmsl-dkms.templates.in new file mode 100644 index 00000000..4171df32 --- /dev/null +++ b/debian/intel-mipi-gmsl-dkms.templates.in @@ -0,0 +1,7 @@ +Template: intel-mipi-gmsl-dkms/max9x +Type: select +Choices: @marchlist@ +Default: @marchdefault@ +Description: Select d4xx GMSL2 Deserializer type [defaut:@marchdefault@] + build d4xx drivers with compile options either for @marchlist@. + \ No newline at end of file diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..1310d45e --- /dev/null +++ b/debian/rules @@ -0,0 +1,69 @@ +#!/usr/bin/make -f + +export DH_VERBOSE = 1 +include /usr/share/dpkg/pkg-info.mk + +# Deserializer arch +deserlist := "max9296 max96712 max96724" +defaultdeser := max9296 + +# special characters +empty := +space := $(empty) $(empty) +comma := , +semicol := ; + +# helper to create comma separated list from space separated list +commasep = $(subst $(space),$(comma)$(space),$1) +semicolsep = $(subst $(space),$(comma),$1) + +# remove double quotes +unquote = $(subst $\",,$1) + +# recursive wildcard +rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) + +DKMS_PACKAGE_NAME = $(shell grep '^PACKAGE_NAME=' debian/dkms.conf.in | cut -d\" -f2) +DKMS_PACKAGE_VERSION = $(DEB_VERSION_UPSTREAM) +DKMS_PACKAGE_REVISION = $(DEB_VERSION_UPSTREAM_REVISION) + +generated_dkms_files = \ + debian/dkms.conf \ + debian/intel-mipi-gmsl-dkms.install \ + debian/intel-mipi-gmsl-dkms.config \ + debian/intel-mipi-gmsl-dkms.templates \ + debian/intel-mipi-gmsl-dkms.postinst \ + $(empty) + +%: + dh $@ --with modaliases + +override_dh_auto_configure: + ./dkms-kernel-source.sh "drivers/media/v4l2-core" "include/media" "drivers/media/pci/intel/Kconfig" "drivers/media/pci/intel/Makefile" "drivers/media/pci/intel/ipu-bridge.c" + +override_dh_auto_build: +override_dh_auto_test: +override_dh_auto_install: +override_dh_auto_clean: + +override_dh_dkms: debian/dkms.conf debian/intel-mipi-gmsl-dkms.postinst + dh_dkms -V $(DEB_VERSION_UPSTREAM_REVISION) -- debian/dkms.conf + cp -a debian/intel-mipi-gmsl-dkms.postinst debian/intel-mipi-gmsl-dkms.postinst.debhelper + +override_dh_installdebconf-arch: debian/intel-mipi-gmsl-dkms.config debian/intel-mipi-gmsl-dkms.templates + dh_installdebconf -a + +override_dh_install: debian/intel-mipi-gmsl-dkms.install debian/dkms.conf + dh_install --exclude=.gitignore --exclude=.gitmodules + +override_dh_clean: + -rm -f $(generated_dkms_files) + dh_clean + +$(generated_dkms_files) : $(generated_dkms_files:=.in) debian/rules + sed -e 's,@DKMS_PACKAGE_NAME@,$(DKMS_PACKAGE_NAME),g' \ + -e 's,@DKMS_PACKAGE_VERSION@,$(DEB_VERSION_UPSTREAM_REVISION),g' \ + -e 's/@deserlist@/$(call unquote,$(deserlist))/g' \ + -e 's/@marchdefault@/$(defaultdeser)/g' \ + -e 's/@marchlist@/$(call commasep,$(call unquote,$(deserlist)))/g' \ + <$(@:=.in) >$@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 00000000..163aaf8d --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 00000000..80441c80 --- /dev/null +++ b/debian/source/options @@ -0,0 +1,26 @@ +tar-ignore=.git +tar-ignore=.gitignore +tar-ignore=.gitmodules +tar-ignore=.ci +tar-ignore=ci +tar-ignore=dkms.conf +tar-ignore=patch/* +tar-ignore=ipu6-drivers/.git/* +tar-ignore=ipu6-drivers/patch/* +tar-ignore=ipu6-drivers/kernel_patches/* +tar-ignore=ipu6-drivers/README.md +tar-ignore=ipu6-drivers/dkms.conf +tar-ignore=ipu6-drivers/Makefile +tar-ignore=ipu7-drivers/.git/* +tar-ignore=ipu7-drivers/CODE_OF_CONDUCT.md +tar-ignore=ipu7-drivers/CONTRIBUTING.md +tar-ignore=ipu7-drivers/patch/* +tar-ignore=ipu7-drivers/README.md +tar-ignore=ipu7-drivers/SECURITY.md +tar-ignore=ipu7-drivers/dkms.conf +tar-ignore=ipu7-drivers/Makefile +tar-ignore=debian/*.tmpl +tar-ignore=.pc +tar-ignore=*.deb +tar-ignore=*.changes +tar-ignore=*.buildinfo diff --git a/helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh b/helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh new file mode 100755 index 00000000..ee07af6e --- /dev/null +++ b/helpers/mtbf-test-isx031-gmsl_gst-v4l2src.sh @@ -0,0 +1,688 @@ +#!/bin/bash +# Dependency: gstreamer1.0-plugins-base +gst_util=$(which gst-launch-1.0) +if [ -z ${gst_util} ]; then + echo "Gstreamer1.0 not found, install with: sudo apt install gstreamer1.0-tools gstreamer1.0-plugins-base" + exit 1 +fi +i2c_util=$(which i2ctransfer) +if [ -z ${i2c_util} ]; then + echo "i2ctransfer not found, install with: sudo apt install i2c-tools" + exit 1 +fi + +# Flag to indicate shutdown requested +shutdown_requested=false + +###### to be MODIFIED if max9x AIC GMSL deserializer board is mapped differently +desA_addr=27 +desA_i2c=0 +desA_csi=0 +desA_tuple="b|c" +#-------- +desB_addr=27 +desB_i2c=1 +desB_csi=2 +desB_tuple="a|d" +###### + +nox=false +watch=false +while [[ $# -gt 0 ]]; do + case $1 in + -l|--loop) + shift + loop=$1 + shift + ;; + -t|--testcase) + shift + testcase=$1 + shift + ;; + -d|--duration) + shift + duration=$1 + shift + ;; + -n|--nox) + nox=true + shift + ;; + -w|--watch) + watch=true + shift + ;; + -h|--help) + echo "-t,--testcase <2by2|4by4|6by6|8by8> -d,--duration -l,--loop -n,-nox -w,-watch " + echo "Examples:" + echo " ./mtbf-test-isx031-gmsl_gst-v4l2src.sh -t 2by2 -d 1 -l 1 -n #isx031 a|d-$desA_csi and a|d-$desB_csi GMSL links back-to-back 10s test, 1-loop : gst-launch-1.0 v4l2src ...! fakesink..." + echo " ./mtbf-test-isx031-gmsl_gst-v4l2src.sh -t 4by4 -d 6 -l 10 #isx031 a|b|c|d-$desA_csi and a|b|c|d-$desB_csi GMSL links back-to-back 1min test, 10-loops : gst-launch-1.0 v4l2src ...! xvimagesink..." + echo " ./mtbf-test-isx031-gmsl_gst-v4l2src.sh -t 6by6 -d 180 -l 5 -n #isx031 a|b|c|d-$desA_csi|$desB_csi and b|c-$desB_csi|$desA_csi GMSL links back-to-back 30min test, 5-loops : gst-launch-1.0 v4l2src ...! fakesink..." + echo " ./mtbf-test-isx031-gmsl_gst-v4l2src.sh -t 8by8 -d 360 -l 1 -n -w # isx031 a|b|c|d-$desA_csi|$desB_csi GMSL links back-to-back 1h test, 1-loops and watch max9x link-status : gst-launch-1.0 v4l2src ...! fakesink..." + exit 1 + ;; + *) + shift + ;; + esac +done + +handle_sigterm() { + echo "Shutdown signal received" + pkill gst-launch-1.0 + shutdown_requested=true +} + +# Handle multiple signals with the same handler +trap handle_sigterm SIGTERM SIGINT SIGHUP + +RED='\033[0;31m' +GREEN='\033[0;32m' +GREY='\033[0;37m' +NC='\033[0m' # No Color + +# Helper function to check specific bits +# Usage: check_bit +check_bit() { + local val=$((16#${1#0x})) + local bit=$2 + local label=$3 + if [ $(( (val >> bit) & 1 )) -eq 1 ]; then + echo "1 ($label)" + else + echo "0 ($label)" + fi +} +# Usage: check_cng +check_cnt() { + local val=$((16#${1#0x})) + local bit=$2 + local mask=$((16#${3#0x})) + echo "$(( (val >> bit) & mask ))" +} + +reg_max9x_id () { + local BUS=$1 + local DEV=$2 + + # GMSL a|b|c|d Link/Lock Status + ADDR=0x000D + # Split 16-bit address for w2 transfer (e.g., 0x0005 -> 0x0 0x5) + HIGH_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) >> 8 ))) + LOW_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) & 0xFF ))) + + VAL=$(i2ctransfer -y -f $BUS w2@0x$DEV $HIGH_BYTE $LOW_BYTE r1) + echo $VAL + #return $VAL +} + + +reg_max9x_lnklock () { + local BUS=$1 + local DEV=$2 + + # GMSL a|b|c|d Link/Lock Status + echo "max9x i2c-$BUS@0x$DEV LINK_LOCK Status:" + if [[ $DNAME == "max9296" ]]; then + REGS=("0x001A:3/LNKA_LOCKED" "0x000A:3/LNKB_LOCKED") + else + REGS=("0x001A:3/LNKA_LOCKED" "0x000A:3/LNKB_LOCKED" "0x000B:3/LNKC_LOCKED" "0x000C:3/LNKD_LOCKED" \ + "0x0108:6/VIDA_LOCK" "0x011A:6/VIDB_LOCK" "0x012C:6/VIDC_LOCK" "0x013E:6/VIDD_LOCK") + fi + for REG in "${REGS[@]}"; do + ADDR=${REG%:*} + ARGS=${REG#*:} + BIT=${ARGS%/*} + LBL=${ARGS#*/} + # Split 16-bit address for w2 transfer (e.g., 0x0005 -> 0x0 0x5) + HIGH_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) >> 8 ))) + LOW_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) & 0xFF ))) + + VAL=$(i2ctransfer -y -f $BUS w2@0x$DEV $HIGH_BYTE $LOW_BYTE r1) + RESULT=$(check_bit $VAL $BIT $LBL) + echo " Reg $ADDR bit$BIT: $VAL -> status: $RESULT" + done +} + +reg_max9x_vidlock () { + local BUS=$1 + local DEV=$2 + + # GMSL a|b|c|d video Lock Status + echo "max9x i2c-$BUS@0x$DEV VID_LOCK Status:" + # Mapping: RegAddr:Bit + DEEP_REGS=("0x0108:5/VIDA_PKT_DET" "0x011A:5/VIDB_PKT_DET" "0x012C:5/VIDC_PKT_DET" "0x013E:5/VIDD_PKT_DET") + + for ENTRY in "${DEEP_REGS[@]}"; do + ADDR=${ENTRY%:*} + ARGS=${ENTRY#*:} + BIT=${ARGS%/*} + LBL=${ARGS#*/} + HIGH_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) >> 8 ))) + LOW_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) & 0xFF ))) + + VAL=$(i2ctransfer -y -f $BUS w2@0x$DEV $HIGH_BYTE $LOW_BYTE r1) + RESULT=$(check_bit $VAL $BIT $LBL) + echo " Reg $ADDR bit$BIT: $VAL -> status: $RESULT" + done +} + +reg_max9x_deserr () { + local BUS=$1 + local DEV=$2 + local DNAME=$3 + + # GMSL a|b|c|d video Lock Status + echo "$DNAME i2c-$BUS@0x$DEV ERR_CNT Status:" + # Mapping: RegAddr:Bit + if [[ $DNAME == "max9296" ]]; then + REGS=("0x01dc:5/VPRBS_FAIL" "0x01fc:5/VPRBS_FAIL"\ + "0x0100:0/VIDA_LCRC_ERR" "0x0112:4/VIDB_LCRC_ERR" "0x0124:0/VIDC_LCRC_ERR" "0x0136:0/VIDD_LCRC_ERR") + else + REGS=("0x01dc:5/VPRBS_FAIL" "0x01fc:5/VPRBS_FAIL" \ + "0x0108:4/VIDA_SEQ_ERR" "0x011A:4/VIDB_SEQ_ERR" "0x012C:4/VIDC_SEQ_ERR" "0x013E:4/VIDD_SEQ_ERR" \ + "0x0B1B:2/VIDA_LCRC_ERR" "0x0C1B:2/VIDB_LCRC_ERR" "0x0D1B:2/VIDC_LCRC_ERR" "0x0E1B:2/VIDD_LCRC_ERR") + fi + + for REG in "${REGS[@]}"; do + ADDR=${REG%:*} + ARGS=${REG#*:} + BIT=${ARGS%/*} + LBL=${ARGS#*/} + # Split 16-bit address for w2 transfer (e.g., 0x0005 -> 0x0 0x5) + HIGH_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) >> 8 ))) + LOW_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) & 0xFF ))) + + VAL=$(i2ctransfer -y -f $BUS w2@0x$DEV $HIGH_BYTE $LOW_BYTE r1) + RESULT=$(check_bit $VAL $BIT $LBL) + echo " Reg $ADDR bit$BIT: $VAL -> status: $RESULT" + done + + if [[ $DNAME == "max9296" ]]; then + DEEP_REGS=("0x0022:0-FF/DEC_ERR_A" "0x0023:0-FF/DEC_ERR_B" "0x0025:0-FF/PKT_CNT") + else + DEEP_REGS=("0x0035:0-FF/DEC_ERR_A" "0x0036:0-FF/DEC_ERR_B" "0x0037:0-FF/DEC_ERR_C" "0x0038:0-FF/DEC_ERR_D" \ + "0x0039:0-FF/IDL_ERR_A" "0x003A:0-FF/IDL_ERR_B" "0x003B:0-FF/IDL_ERR_C" "0x003C:0-FF/IDL_ERR_D" \ + "0x0B19:0-FF/CC_CRC_ERR_A" "0x0C19:0-FF/CC_CRC_ERR_B" "0x0D19:0-FF/CC_CRC_ERR_C" "0x0E19:0-FF/CC_CRC_ERR_D" \ + "0x0B1A:0-FF/RC_CRC_ERR_A" "0x0C1A:0-FF/RC_CRC_ERR_B" "0x0D1A:0-FF/RC_CRC_ERR_C" "0x0E1A:0-FF/RC_CRC_ERR_D" \ + "0x0B1C:0-3F/EOM_EYE_WIDTH_A" "0x0C1C:0-3F/EOM_EYE_WIDTH_B" "0x0D1C:0-3F/EOM_EYE_WIDTH_C" "0x0E1C:0-3F/EOM_EYE_WIDTH_D" \ + "0x08D0:0-F/TX0_PKT_CNT" "0x08D2:0-F/PHY0_PKT_CNT" \ + "0x08D1:0-F/TX2_PKT_CNT" "0x08D3:0-F/PHY2_PKT_CNT") + fi + for ENTRY in "${DEEP_REGS[@]}"; do + ADDR=${ENTRY%:*} + ARGS=${ENTRY#*:} + ARG1=${ARGS%/*} + LBL=${ARGS#*/} + ARG2=${ARG1%/*} + BIT=${ARG2%-*} + MSK=${ARG2#*-*} + #echo $BIT + #echo $MASK + HIGH_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) >> 8 ))) + LOW_BYTE=$(printf "0x%x" $(( (16#${ADDR#0x}) & 0xFF ))) + + VAL=$(i2ctransfer -y -f $BUS w2@0x$DEV $HIGH_BYTE $LOW_BYTE r1) + RESULT=$(check_cnt $VAL $BIT $MSK) + echo " Reg $ADDR [$LBL] mask0x$MSK: $VAL -> count: $RESULT" + done +} + +# Wait with timeout (Bash 4.3+) +wait_with_timeout() { + local pid=$1 + local timeout=$2 + local dname=$3 + local bus=$4 + local addr=$5 + local count=0 + local wcount=0 + local ret=0 + + echo "$(reg_max9x_deserr $bus $addr ${dname})" 1>&2 # clear max9x error status + + + while kill -0 "$pid" 2>/dev/null; do + + #echo $count + + err_cnt=$(dmesg | grep -i "error group 3 code 18" | wc -l) + if [[ $(($err_cnt - $err_initial)) -ge 1 ]]; then + err_tailmsg=$(dmesg -T | grep -i "error group 3 code 18" | tail -n 1) + echo "Oops: $err_tailmsg" + echo "Error waiting for PID $pid [HW_ERR_BAD_FRAME_DIM cnt=$(($err_cnt - $err_initial))]" + reg_max9x_deserr $bus $addr ${dname} #Check Deserializer status + return 0 + fi + + if [[ $count -ge $timeout ]]; then + echo "Timeout waiting for PID $pid" + return 1 + fi + + sleep 1 + count=$((count + 1)) + if [[ "$watch" == "true" ]]; then + wcount=$((wcount + 1)) + if [[ $wcount -gt 10 ]]; then + reg_max9x_deserr $bus $addr ${dname} #Check Deserializer status + wcount=0 + fi + fi + done + + wait "$pid" + ret=$? + #echo $ret + if [[ $ret -ne 0 ]]; then + return $((-1)) + else + return 1 + fi +} + +test_v4l2src_1920x1536_2by2() { + local i=$1 + local csi=$2 + local fcnt=$3 + local tout=$4 + local dname=$5 + local bus=$6 + local addr=$7 + local tuple=${8:-'a|d'} + local pid=0 + local ret=0 + local out="videoconvert ! xvimagesink" + + [[ "$nox" == "true" ]] && out="fakesink sync=false enable-last-sample=false" + + echo "Starting ... $dname GMSL $tuple-$csi links $testcase test-run $i with PID: $pid" + + gst-launch-1.0 \ + v4l2src device=/dev/video-isx031-${tuple%|*}-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_0 \ + v4l2src device=/dev/video-isx031-${tuple#*|}-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_1 \ + compositor name=comp \ + sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ + sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ + ! ${out} & + + # Get the PID of the last background process + pid=$! + + if [[ "$watch" == "true" ]]; then + reg_max9x_vidlock $bus $addr #Check Deserializer status + fi + + wait_with_timeout $pid $(($tout+1)) $dname $bus $addr + ret=$? + + # kill any background process + kill $pid 2>/dev/null + + return $ret +} + +test_v4l2src_1920x1536_4by4() { + local i=$1 + local csi=$2 + local fcnt=$3 + local tout=$4 + local dname=$5 + local bus=$6 + local addr=$7 + local tuple=${8:-'a|d'} + local dmux=${9:-'a-0'} + local pid=0 + local ret=0 + local out="videoconvert ! xvimagesink" + + [[ "$nox" == "true" ]] && out="fakesink sync=false enable-last-sample=false" + + echo "Starting... $dname GMSL a|b|c|d-$csi links $testcase test-run $i with PID: $pid" + + gst-launch-1.0 \ + v4l2src device=/dev/video-isx031-a-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_0 \ + v4l2src device=/dev/video-isx031-b-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_1 \ + v4l2src device=/dev/video-isx031-c-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_2 \ + v4l2src device=/dev/video-isx031-d-$csi num-buffers=$fcnt ! \ + 'video/x-raw, width=1920, height=1536, format=UYVY, pixel-aspect-ratio=1/1, framerate=30/1' ! comp.sink_3 \ + compositor name=comp \ + sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ + sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ + sink_2::xpos=0 sink_2::ypos=1536 sink_2::width=1920 sink_2::height=1536 sink_2::zorder=3 \ + sink_3::xpos=1920 sink_3::ypos=1536 sink_3::width=1920 sink_3::height=1536 sink_3::zorder=4 \ + ! ${out} & + + # Get the PID of the last background process + pid=$! + + if [[ "$watch" == "true" ]]; then + reg_max9x_vidlock $bus $addr #Check Deserializer status + fi + + wait_with_timeout $pid $(($tout+1)) $dname $bus $addr + ret=$? + + # kill any background process + kill $pid 2>/dev/null + + return $ret +} + +test_v4l2src_1920x1536_6by6() { + local i=$1 + local csi=$2 + local fcnt=$3 + local tout=$4 + local dname=$5 + local bus=$6 + local addr=$7 + local tuple=${8:-'b|c'} + local pid=0 + local ret=0 + local out="videoconvert ! xvimagesink" + + [[ "$nox" == "true" ]] && out="fakesink sync=false enable-last-sample=false" + + if [[ $csi -eq $desA_csi ]]; then + echo "Starting... $dname GMSL a|b|c|d-$desA_csi and $tuple-$desB_csi links $testcase test-run $i with PID: $pid" + gst-launch-1.0 \ + v4l2src device=/dev/video-isx031-a-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_0 \ + v4l2src device=/dev/video-isx031-b-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_1 \ + v4l2src device=/dev/video-isx031-c-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_2 \ + v4l2src device=/dev/video-isx031-d-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_3 \ + v4l2src device=/dev/video-isx031-${tuple%|*}-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_4 \ + v4l2src device=/dev/video-isx031-${tuple#*|}-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_5 \ + compositor name=comp \ + sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ + sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ + sink_2::xpos=3840 sink_2::ypos=0 sink_2::width=1920 sink_2::height=1536 sink_2::zorder=3 \ + sink_3::xpos=5760 sink_3::ypos=0 sink_3::width=1920 sink_3::height=1536 sink_3::zorder=4 \ + sink_4::xpos=0 sink_4::ypos=1536 sink_4::width=1920 sink_4::height=1536 sink_4::zorder=5 \ + sink_5::xpos=1920 sink_5::ypos=1536 sink_5::width=1920 sink_5::height=1536 sink_5::zorder=6 \ + ! ${out} & + else + echo "Starting... $dname GMSL a|b|c|d-$desB_csi and $tuple-$desA_csi links $testcase test-run $i with PID: $pid" + gst-launch-1.0 \ + v4l2src device=/dev/video-isx031-a-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_0 \ + v4l2src device=/dev/video-isx031-b-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_1 \ + v4l2src device=/dev/video-isx031-c-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_2 \ + v4l2src device=/dev/video-isx031-d-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_3 \ + v4l2src device=/dev/video-isx031-${tuple%|*}-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_4 \ + v4l2src device=/dev/video-isx031-${tuple#*|}-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_5 \ + compositor name=comp \ + sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ + sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ + sink_2::xpos=3840 sink_2::ypos=0 sink_2::width=1920 sink_2::height=1536 sink_2::zorder=3 \ + sink_3::xpos=5760 sink_3::ypos=0 sink_3::width=1920 sink_3::height=1536 sink_3::zorder=4 \ + sink_4::xpos=0 sink_4::ypos=1536 sink_4::width=1920 sink_4::height=1536 sink_4::zorder=5 \ + sink_5::xpos=1920 sink_5::ypos=1536 sink_5::width=1920 sink_5::height=1536 sink_5::zorder=6 \ + ! ${out} & + fi + + # Get the PID of the last background process + pid=$! + + if [[ "$watch" == "true" ]]; then + reg_max9x_vidlock $desA_i2c $desA_addr #Check Deserializer status + reg_max9x_vidlock $desB_i2c $desB_addr #Check Deserializer status + fi + + wait_with_timeout $pid $(($tout+1)) $dname $bus $addr + ret=$? + + # kill any background process + kill $pid 2>/dev/null + + return $ret +} + +test_v4l2src_1920x1536_8by8() { + local i=$1 + local csi=$2 + local fcnt=$3 + local tout=$4 + local dname=$5 + local bus=$6 + local addr=$7 + local tuple=${8:-'b|c'} + local pid=0 + local ret=0 + local out="videoconvert ! xvimagesink" + + [[ "$nox" == "true" ]] && out="fakesink sync=false enable-last-sample=false" + + gst-launch-1.0 \ + v4l2src device=/dev/video-isx031-a-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_0 \ + v4l2src device=/dev/video-isx031-b-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_1 \ + v4l2src device=/dev/video-isx031-c-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_2 \ + v4l2src device=/dev/video-isx031-d-$desA_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_3 \ + v4l2src device=/dev/video-isx031-a-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_4 \ + v4l2src device=/dev/video-isx031-b-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_5 \ + v4l2src device=/dev/video-isx031-c-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_6 \ + v4l2src device=/dev/video-isx031-d-$desB_csi num-buffers=$fcnt ! \ + 'video/x-raw,width=1920,height=1536,format=UYVY,pixel-aspect-ratio=1/1,framerate=30/1' ! comp.sink_7 \ + compositor name=comp \ + sink_0::xpos=0 sink_0::ypos=0 sink_0::width=1920 sink_0::height=1536 sink_0::zorder=1 \ + sink_1::xpos=1920 sink_1::ypos=0 sink_1::width=1920 sink_1::height=1536 sink_1::zorder=2 \ + sink_2::xpos=3840 sink_2::ypos=0 sink_2::width=1920 sink_2::height=1536 sink_2::zorder=3 \ + sink_3::xpos=5760 sink_3::ypos=0 sink_3::width=1920 sink_3::height=1536 sink_3::zorder=4 \ + sink_4::xpos=0 sink_4::ypos=1536 sink_4::width=1920 sink_4::height=1536 sink_4::zorder=5 \ + sink_5::xpos=1920 sink_5::ypos=1536 sink_5::width=1920 sink_5::height=1536 sink_5::zorder=6 \ + sink_6::xpos=3840 sink_6::ypos=1536 sink_6::width=1920 sink_6::height=1536 sink_6::zorder=7 \ + sink_7::xpos=5760 sink_7::ypos=1536 sink_7::width=1920 sink_7::height=1536 sink_7::zorder=8 \ + ! ${out} & + + # Get the PID of the last background process + pid=$! + + echo "Started GMSL a|b|c|d-0 and a|b|c|d-2 links $testcase test-run $i with PID: $pid" + if [[ "$watch" == "true" ]]; then + reg_max9x_vidlock $desA_i2c $desA_addr #Check Deserializer status + reg_max9x_vidlock $desB_i2c $desB_addr #Check Deserializer status + fi + + wait_with_timeout $pid $(($tout+1)) $dname $bus $addr + ret=$? + + # kill any background process + kill $pid 2>/dev/null + + return $ret +} + +# Main loop checks shutdown flag +i=0 +pass=0 +fail=0 +ign=0 +pass1=0 +fail1=0 +ign1=0 +err_baseline=$(dmesg | grep -i "error group 3 code 18" | wc -l) +err_count=$err_baseline + +################################## +framecnt=330 #10s of frames +testvar1=${duration:-1} #default 1=10s +testvar2=${loop:-10} # default one loop +testvar3=${mux:-'a-0'} # watch a-0 deserilizer +testcase=${testcase:-'4by4'} +################################## +timeout=$(($testvar1*10)) +framecnt=$(($framecnt*$testvar1)) + + +echo "${NC}########################################" + +#Check A/B Deserializers DEV_ID +val=$(reg_max9x_id $desA_i2c $desA_addr) +case ${val} in + 0x94) + desA_name="max9296" + ;; + 0xa2|0xa3|0xa4) + desA_name="max96724" + ;; + *) + echo "NO max9x Deserializer Found at i2c-$desA_i2c@0x$desA_addr!" + exit 0 + ;; +esac +echo " Found $desA_name GMSL Deserializer at i2c-$desA_i2c@0x$desA_addr!" + +case "$(reg_max9x_id $desB_i2c $desB_addr)" in + 0x94) + desB_name="max9296" + ;; + 0xa2|0xa3|0xa4) + desB_name="max96724" + ;; + *) + echo "NO max9x Deserializer Found at i2c-$desB_i2c@0x$desB_addr!" + exit 0 + ;; +esac +echo " Found $desB_name GMSL Deserializer at i2c-$desB_i2c@0x$desA_addr!" + +[[ $duration -eq 0 ]] && exit 0 + +#echo "$timeout = $framecnt" + +[[ "$testcase" != "2by2" && "$testcase" != "4by4" && "$testcase" != "6by6" && "$testcase" != "8by8" ]] && exit 0 + +#echo "${testvar3%-*}" +#echo "${testvar3#*-}" + +[[ "${testvar3%-*}" != "a" && "${testvar3%-*}" != "b" && "${testvar3%-*}" != "c" && "${testvar3%-*}" != "d" ]] && exit 0 +[[ ${testvar3#*-} -gt 5 ]] && exit 0 + +echo "GMSL $desA_name A/B back-to-back $testcase #ofFrames=$framecnt [timeout=${timeout} sec streaming 1920x1536@30fps] #ofIteration=$testvar2" +reg_max9x_lnklock $desA_i2c $desA_addr #Check A Deserializer link status +reg_max9x_lnklock $desB_i2c $desB_addr #Check B Deserializer link status + +while [[ "$shutdown_requested" == "false" ]]; do + + # back-to-back Testcases, ignore after 5 attempt for the rest of the test-runner + if [[ $ign -lt 5 ]]; then + + echo -e "\n========================================" + + # Change 2by2 or 6by6 permutation + [[ "${testcase}" == "2by2" && $desA_name == "max9296" ]] && desA_tuple="a|b" + [[ "${testcase}" == "6by6" ]] && desA_tuple="b|c" + + is_ignored=false + err_initial=$(dmesg | grep -i "error group 3 code 18" | wc -l) + test_v4l2src_1920x1536_${testcase} $i $desA_csi $framecnt $(($timeout+2)) $desA_name $desA_i2c $desA_addr $desA_tuple + ret=$? + #echo $ret + if [[ $ret -eq 0 ]]; then + reg_max9x_lnklock $desA_i2c $desA_addr #Check A Deserializer link status + echo "completed $testcase testrun $i unsuccessfully" + fail=$((fail+1)) + else + if [[ $ret -eq 255 ]]; then + echo "ignored $testcase testrun $i" + ign=$((ign+1)) + is_ignored=true + else + echo "completed $testcase testrun $i successfully" + pass=$((pass+1)) + fi + fi + + if [[ "$is_ignored" == "false" ]]; then + sleep 5 + echo "########################################" + fi + + i=$((i+1)) + + fi + + [[ $i -ge $testvar2 ]] && shutdown_requested=true + + # in any testcase if camera are not connected to the respective ports, + # exit after 5 attempt + [[ $ign -ge 5 && $ign1 -ge 5 ]] && exit 0 + + # in 8by8 testcase if camera are not connected to the respective ports, + # exit after 5 attempt + [[ "${testcase}" == "8by8" && $ign -gt 5 ]] && exit 0 + + # ignore after 5 attempt or in 8by8 testcase + [[ "${testcase}" == "8by8" || $ign1 -gt 5 ]] && continue + + # Change 2by2 or 6by6 permutation + [[ "${testcase}" == "2by2" && $desB_name == "max9296" ]] && desB_tuple="a|b" + [[ "${testcase}" == "6by6" ]] && desB_tuple="b|c" + + echo -e "$\n========================================" + is_ignored=false + err_initial=$(dmesg | grep -i "error group 3 code 18" | wc -l) + test_v4l2src_1920x1536_${testcase} $i $desB_csi $framecnt $(($timeout+2)) $desB_name $desB_i2c $desB_addr $desB_tuple + ret=$? + #echo $ret + if [[ $ret -eq 0 ]]; then + reg_max9x_lnklock $desB_i2c $desB_addr #Check B Deserializer link status + echo "completed $testcase testrun $i unsuccessfully" + fail1=$((fail1+1)) + else + if [[ $ret -eq 255 ]]; then + echo "ignored $testcase testrun $i" + ign1=$((ign1+1)) + is_ignored=true + else + echo "completed $testcase testrun $i successfully" + pass1=$((pass1+1)) + fi + fi + + if [[ "$is_ignored" == "false" ]]; then + sleep 5 + echo "########################################" + fi + + i=$((i+1)) + +done + +err_count=$(dmesg | grep -i "error group 3 code 18" | wc -l) +echo "---------------------------------" +echo "GMSL isx031 camera links $testcase Back-to-Back v4l2src Stress-Test" +echo "Testvars: #ofFrames=$framecnt [1920x1536@30fps streaming for ~$timeout sec.] #ofIteration=$testvar2" +[[ "${testcase}" == "2by2" ]] && echo "GMSL $desA_name a|d-$desA_csi testruns=$(($pass+$fail+$ign)) (pass/fail/ignore=$pass/$fail/$ign)" +[[ "${testcase}" == "2by2" ]] && echo "GMSL $desB_name a|d-$desB_csi testruns=$(($pass1+$fail1+$ign1)) (pass/fail/ignore=$pass1/$fail1/$ign1)" +[[ "${testcase}" == "4by4" ]] && echo "GMSL $desA_name a|b|c|d-desA_csi testruns=$(($pass+$fail+$ign)) (pass/fail/ignore=$pass/$fail/$ign)" +[[ "${testcase}" == "4by4" ]] && echo "GMSL $desB_name a|b|c|d-$desB_csi testruns=$(($pass1+$fail1+$ign1)) (pass/fail/ignore=$pass1/$fail1/$ign1)" +[[ "${testcase}" == "6by6" ]] && echo "GMSL $desA_name a|b|c|d-desA_csi and $desB_name b|c-$desB_csi testruns=$(($pass+$fail+$ign)) (pass/fail/ignore=$pass/$fail/$ign)" +[[ "${testcase}" == "6by6" ]] && echo "GMSL $desB_name a|b|c|d-$desB_csi and $desA_name b|c-desA_csi testruns=$(($pass1+$fail1+$ign1)) (pass/fail/ignore=$pass1/$fail1/$ign1)" +[[ "${testcase}" == "8by8" ]] && echo "GMSL $desA_name a|b|c|d-$desA_csi and $desB_name a|b|c|d-$desB_csi testruns=$(($pass+$fail+$ign)) (pass/fail/ignore=$pass/$fail/$ign)" +echo "[HW_ERR_BAD_FRAME_DIM cnt=$(($err_count - $err_baseline)))]" +echo "exiting..." diff --git a/patches/libv4l/0001-media-ctl-add-pad-support-2-digits-stream-id.patch b/patches/libv4l/0001-media-ctl-add-pad-support-2-digits-stream-id.patch new file mode 100644 index 00000000..7eef7230 --- /dev/null +++ b/patches/libv4l/0001-media-ctl-add-pad-support-2-digits-stream-id.patch @@ -0,0 +1,40 @@ +From 87db5a82cd6f71ac73b825ad335ae306ce64ce60 Mon Sep 17 00:00:00 2001 +From: florent pirou +Date: Tue, 3 Mar 2026 08:08:47 -0700 +Subject: [PATCH] media-ctl: add pad support 2-digits stream-id + +* v4l2: workaround pad stream-id single-digit limit +``` +media-ctl -d /dev/media0 -V '"Intel IPU7 CSI2 2":"11/10" [fmt:VYUY8_1X16/640x480 field:none]' -v +... +Enumerating pads and links +Expected '[' + + "Intel IPU7 CSI2 2":"11/10" [fmt:VYUY8_1X16/640x480 field:none] + ^ + Unable to parse format + Unable to setup formats: Invalid argument (22) + +``` + +Signed-off-by: florent pirou +--- + utils/media-ctl/libmediactl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c +index 64ac8cf1..1d5f9b81 100644 +--- a/utils/media-ctl/libmediactl.c ++++ b/utils/media-ctl/libmediactl.c +@@ -909,7 +909,7 @@ struct media_pad *media_parse_pad_stream(struct media_device *media, + *stream = 0; + } + +- for (; isspace(*p); ++p); ++ for (p = ep + 1; isspace(*p); ++p); + + if (endp) + *endp = (char*)p; +-- +2.25.1 + From 7ebd5f6124a067d2b841cad110683b28f7504b6f Mon Sep 17 00:00:00 2001 From: Pirouf Date: Mon, 22 Jun 2026 08:31:10 +0200 Subject: [PATCH 72/72] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0bdc6e05..e281c00a 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ systemctl reboot --firmware-setup ``` Select `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> < Enable >`. -Configure each Camera ACPI device individually `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> Link Options`. +Configure each Camera ACPI device individualy `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> Link Options`. Select `Intel Advanced Menu -> System Agent (SA) Configuration -> MIPI Camera Configuration -> Link Options -> User Custom`. For example Realsense D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) - one ACPI entry each D457 Cameras @@ -140,7 +140,7 @@ For example Realsense D457 on IPU7 (Intel Core Ultra 3 - Panther Lake) - one ACP | MIPI Port (Index) | 2 | 2 | 2 | 2 | | LaneUsed (serializer # of lanes) | x2 | x2 | x2 | x2 | | Number of I2C | 3 | 3 | 3 | 3 | -| I2C Channel | I2C2 | I2C2 | I2C2 | I2C2 | +| I2C Channel | I2C1 | I2C1 | I2C1 | I2C1 | | Device0 I2C Address (sensor) | 12 | 13 | 14 | 15 | | Device1 I2C Address (serializer) | 42 | 43 | 44 | 45 | | Device2 I2C Address (deserializer) | 27 | 27 | 27 | 27 | @@ -157,11 +157,11 @@ For example d3embedded ISX031 GMSL modules on IPU7 (Intel Core Ultra 3 - Panthe | Rotation (max9x deserializer out-link) | 180 | | PPR Value (deserializer # of lanes) | 2 | | PPR Unit (# of Cameras per-device) | 4 | -| Camera module label | d4xx | +| Camera module label | max96724 | | MIPI Port (Index) | 2 | | LaneUsed (serializer # of lanes) | x4 | | Number of I2C | 3 | -| I2C Channel | I2C2 | +| I2C Channel | I2C1 | | Device0 I2C Address (deserilizer) | 27 | | Device1 I2C Address (serializer) | 42 | | Device2 I2C Address (sensor) | 12 |