diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/changelog ipu6-drivers-0~git202310180730.3f813580/debian/changelog --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/changelog 2023-07-28 18:36:03.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/changelog 2023-09-01 17:51:53.000000000 +0800 @@ -1,3 +1,24 @@ +ipu6-drivers (0~git202310180730.3f813580-0ubuntu0.24.04.1) noble; urgency=low + + [ Hao Yao ] + * New upstream 20230802_1500_mtl_plat release. + * New upstream 20230911_0620_mtl_plat_pv release. + + [ Hans de Goede ] + * Fix compilation with kernels >= 6.5.0 (LP: #2026402) + - ipu-psys: Fix compilation with kernels >= 6.5.0 + - ipu6: Fix compilation with kernels >= 6.6.0 + - ipu6: Fix sensor driver compilation with kernels >= 6.6.0 + + [ You-Sheng Yang ] + * Support mipi camera on Intel Meteor Lake platform (LP: #2031412) + - debian: refresh patches + * dkms: add CONFIG_VIDEO_V4L2_I2C to BUILD_EXCLUSIVE_CONFIG (LP: #2012407) + * debian: add modaliases (LP: #2021740) + * UBUNTU: SAUCE: i2c: compile omitted sensor drivers + + -- You-Sheng Yang Fri, 01 Sep 2023 17:51:53 +0800 + ipu6-drivers (0~git202302081010.7fdfb5eb-0ubuntu0.23.10.2) mantic; urgency=medium * debian/patches/0010-linux-6.5-get_user_pages-dropped-vmas.patch: diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/control ipu6-drivers-0~git202310180730.3f813580/debian/control --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/control 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/control 2023-09-01 17:51:53.000000000 +0800 @@ -21,6 +21,7 @@ dkms, ${misc:Depends}, Provides: ipu6-modules +XB-Modaliases: ${modaliases} Description: Intel Integrated Image Processing Unit 6 (IPU6) driver This package provides kernel drivers for MIPI cameras through the Intel IPU6 on Intel Tiger Lake and Alder Lake platforms. diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/intel-ipu6-dkms.modaliases ipu6-drivers-0~git202310180730.3f813580/debian/intel-ipu6-dkms.modaliases --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/intel-ipu6-dkms.modaliases 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/intel-ipu6-dkms.modaliases 2023-09-01 17:51:53.000000000 +0800 @@ -0,0 +1,6 @@ +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 -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0003-build-fix-kernel-feature-macro-definitions.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0003-build-fix-kernel-feature-macro-definitions.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0003-build-fix-kernel-feature-macro-definitions.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0003-build-fix-kernel-feature-macro-definitions.patch 2023-09-01 17:51:53.000000000 +0800 @@ -12,14 +12,14 @@ Signed-off-by: You-Sheng Yang (vicamo) --- - Makefile | 13 +++++-------- - 1 file changed, 5 insertions(+), 8 deletions(-) + Makefile | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile -index f4ca9b1..f8bc96b 100644 +index bb7e220..a6d4095 100644 --- a/Makefile +++ b/Makefile -@@ -32,11 +32,10 @@ mei_pse-y := drivers/misc/ivsc/mei_pse.o +@@ -32,12 +32,11 @@ mei_pse-y := drivers/misc/ivsc/mei_pse.o obj-m += mei_ace_debug.o mei_ace_debug-y := drivers/misc/ivsc/mei_ace_debug.o @@ -28,23 +28,31 @@ export CONFIG_VIDEO_INTEL_IPU6 = m export CONFIG_IPU_ISYS_BRIDGE = y + export CONFIG_IPU_SINGLE_BE_SOC_DEVICE = n -export CONFIG_INTEL_SKL_INT3472 = m obj-y += drivers/media/pci/intel/ export CONFIG_VIDEO_HM11B1 = m -@@ -58,13 +57,11 @@ ccflags-y += -I$(src)/backport-include/drivers/misc/mei/ - subdir-ccflags-y += -I$(src)/include/ +@@ -59,18 +58,16 @@ MODSRC := $(shell pwd) + ccflags-y += -I$(src)/backport-include/drivers/misc/mei/ + + subdir-ccflags-y += -I$(src)/include/ \ +- -DCONFIG_VIDEO_V4L2_SUBDEV_API ++ -DCONFIG_VIDEO_V4L2_SUBDEV_API=1 subdir-ccflags-$(CONFIG_INTEL_VSC) += \ - -DCONFIG_INTEL_VSC + -DCONFIG_INTEL_VSC_MODULE=1 subdir-ccflags-$(CONFIG_IPU_ISYS_BRIDGE) += \ - -DCONFIG_IPU_ISYS_BRIDGE ++ -DCONFIG_IPU_ISYS_BRIDGE=1 + subdir-ccflags-$(CONFIG_IPU_SINGLE_BE_SOC_DEVICE) += \ +- -DCONFIG_IPU_SINGLE_BE_SOC_DEVICE -subdir-ccflags-$(CONFIG_INTEL_SKL_INT3472) += \ - -DCONFIG_INTEL_SKL_INT3472 -subdir-ccflags-$(CONFIG_POWER_CTRL_LOGIC) += \ - -DCONFIG_POWER_CTRL_LOGIC -+ -DCONFIG_IPU_ISYS_BRIDGE=1 ++ -DCONFIG_IPU_SINGLE_BE_SOC_DEVICE=1 +# subdir-ccflags-$(CONFIG_POWER_CTRL_LOGIC) += \ +# -DCONFIG_POWER_CTRL_LOGIC_MODULE=1 subdir-ccflags-y += $(subdir-ccflags-m) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0004-build-drop-inlined-vsc-builds.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0004-build-drop-inlined-vsc-builds.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0004-build-drop-inlined-vsc-builds.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0004-build-drop-inlined-vsc-builds.patch 2023-09-01 17:51:53.000000000 +0800 @@ -4,12 +4,12 @@ Signed-off-by: You-Sheng Yang (vicamo) --- - Makefile | 33 ------------------- - dkms.conf | 109 +++++++++++++++++++++++--------------------------------------- - 2 files changed, 40 insertions(+), 102 deletions(-) + Makefile | 33 ----------------- + dkms.conf | 125 ++++++++++++++++++++++++-------------------------------------- + 2 files changed, 48 insertions(+), 110 deletions(-) diff --git a/Makefile b/Makefile -index f8bc96b..43c459b 100644 +index fdb8fa1..93b01d7 100644 --- a/Makefile +++ b/Makefile @@ -1,37 +1,6 @@ @@ -50,20 +50,20 @@ export CONFIG_INTEL_VSC = m export CONFIG_VIDEO_INTEL_IPU6 = m -@@ -52,8 +21,6 @@ KERNELRELEASE ?= $(shell uname -r) +@@ -56,8 +25,6 @@ KERNELRELEASE ?= $(shell uname -r) KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build MODSRC := $(shell pwd) -ccflags-y += -I$(src)/backport-include/drivers/misc/mei/ - - subdir-ccflags-y += -I$(src)/include/ + subdir-ccflags-y += -I$(src)/include/ \ + -DCONFIG_VIDEO_V4L2_SUBDEV_API=1 - subdir-ccflags-$(CONFIG_INTEL_VSC) += \ diff --git a/dkms.conf b/dkms.conf -index 55c2c42..d160443 100644 +index 9bdf320..43045fe 100644 --- a/dkms.conf +++ b/dkms.conf -@@ -4,74 +4,45 @@ PACKAGE_VERSION="#MODULE_VERSION#" +@@ -4,82 +4,53 @@ PACKAGE_VERSION="#MODULE_VERSION#" MAKE="make KERNELRELEASE=$kernelver KERNEL_SRC=$kernel_source_dir" CLEAN="make KERNELRELEASE=$kernelver KERNEL_SRC=$kernel_source_dir clean" @@ -133,9 +133,17 @@ -BUILT_MODULE_LOCATION[18]="drivers/media/i2c" -DEST_MODULE_LOCATION[18]="/updates" - --BUILT_MODULE_NAME[19]="hi556" +-BUILT_MODULE_NAME[19]="hm2172" -BUILT_MODULE_LOCATION[19]="drivers/media/i2c" -DEST_MODULE_LOCATION[19]="/updates" +- +-BUILT_MODULE_NAME[20]="hi556" +-BUILT_MODULE_LOCATION[20]="drivers/media/i2c" +-DEST_MODULE_LOCATION[20]="/updates" +- +-BUILT_MODULE_NAME[21]="ov02e10" +-BUILT_MODULE_LOCATION[21]="drivers/media/i2c" +-DEST_MODULE_LOCATION[21]="/updates" +i=0 +BUILT_MODULE_NAME[$i]="intel-ipu6" +BUILT_MODULE_LOCATION[$i]="drivers/media/pci/intel/ipu6" @@ -173,8 +181,16 @@ +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" + ++BUILT_MODULE_NAME[$((++i))]="hm2172" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" ++ +BUILT_MODULE_NAME[$((++i))]="hi556" +BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" +DEST_MODULE_LOCATION[$i]="/updates" ++ ++BUILT_MODULE_NAME[$((++i))]="ov02e10" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" AUTOINSTALL="yes" diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0006-ivsc-load-symbols-by-kprobe.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0006-ivsc-load-symbols-by-kprobe.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0006-ivsc-load-symbols-by-kprobe.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0006-ivsc-load-symbols-by-kprobe.patch 2023-09-01 17:51:53.000000000 +0800 @@ -6,28 +6,28 @@ --- Makefile | 4 ++- backport-include/linux/vsc.h | 63 ++++++++++++++++++++++++++++++++++++++++---- - drivers/media/i2c/hi556.c | 4 +++ - drivers/media/i2c/hm2170.c | 4 +++ - drivers/media/i2c/ov01a10.c | 4 +++ + drivers/media/i2c/hi556.c | 6 +++++ + drivers/media/i2c/hm2170.c | 6 +++++ + drivers/media/i2c/ov01a10.c | 6 +++++ drivers/media/i2c/ov01a1s.c | 6 +++++ - drivers/media/i2c/ov02c10.c | 4 +++ - 7 files changed, 83 insertions(+), 6 deletions(-) + drivers/media/i2c/ov02c10.c | 6 +++++ + 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile -index 43c459b..32f3d70 100644 +index 6e57503..3e6ce16 100644 --- a/Makefile +++ b/Makefile -@@ -21,7 +21,9 @@ KERNELRELEASE ?= $(shell uname -r) +@@ -24,7 +24,9 @@ KERNELRELEASE ?= $(shell uname -r) KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build MODSRC := $(shell pwd) --subdir-ccflags-y += -I$(src)/include/ -+NOSTDINC_FLAGS += \ -+ -I$(M)/backport-include/ \ -+ -I$(M)/include/ +-subdir-ccflags-y += -I$(src)/include/ \ ++subdir-ccflags-y += \ ++ -I$(src)/backport-include/ \ ++ -I$(src)/include/ \ + -DCONFIG_VIDEO_V4L2_SUBDEV_API=1 subdir-ccflags-$(CONFIG_INTEL_VSC) += \ - -DCONFIG_INTEL_VSC_MODULE=1 diff --git a/backport-include/linux/vsc.h b/backport-include/linux/vsc.h index 8f8d404..71486dd 100644 --- a/backport-include/linux/vsc.h @@ -123,57 +123,63 @@ #endif diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c -index 7096e04..f513490 100644 +index daabdb6..235bcc7 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c -@@ -1111,6 +1111,10 @@ static int hi556_probe(struct i2c_client *client) - #endif +@@ -1350,6 +1350,12 @@ static int hi556_probe(struct i2c_client *client) + bool full_power; + int ret; - #if IS_ENABLED(CONFIG_INTEL_VSC) ++#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = init_vsc_symbols(); + if (ret) + return ret; ++#endif + - conf.lane_num = HI556_DATA_LANES; - /* frequency unit 100k */ - conf.freq = HI556_LINK_FREQ_437MHZ / 100000; + ret = hi556_check_hwcfg(&client->dev); + if (ret) + return dev_err_probe(&client->dev, ret, diff --git a/drivers/media/i2c/hm2170.c b/drivers/media/i2c/hm2170.c -index 08ca2fe..264a6ff 100644 +index d48329f..596b6dd 100644 --- a/drivers/media/i2c/hm2170.c +++ b/drivers/media/i2c/hm2170.c -@@ -921,6 +921,10 @@ static int hm2170_probe(struct i2c_client *client) - #endif +@@ -1225,6 +1225,12 @@ static int hm2170_probe(struct i2c_client *client) + struct hm2170 *hm2170; + int ret = 0; - #if IS_ENABLED(CONFIG_INTEL_VSC) ++#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = init_vsc_symbols(); + if (ret) + return ret; ++#endif + - conf.lane_num = HM2170_DATA_LANES; - /* frequency unit 100k */ - conf.freq = HM2170_LINK_FREQ_384MHZ / 100000; + hm2170 = devm_kzalloc(&client->dev, sizeof(*hm2170), GFP_KERNEL); + if (!hm2170) { + ret = -ENOMEM; diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c -index ccd1cc7..fc06d63 100644 +index d243db1..180ced5 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c -@@ -885,6 +885,10 @@ static int ov01a10_probe(struct i2c_client *client) - #endif +@@ -926,6 +926,12 @@ static int ov01a10_probe(struct i2c_client *client) + struct ov01a10 *ov01a10; + int ret = 0; - #if IS_ENABLED(CONFIG_INTEL_VSC) ++#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = init_vsc_symbols(); + if (ret) + return ret; ++#endif + - conf.lane_num = OV01A10_DATA_LANES; - /* frequency unit 100k */ - conf.freq = OV01A10_LINK_FREQ_400MHZ / 100000; + ov01a10 = devm_kzalloc(&client->dev, sizeof(*ov01a10), GFP_KERNEL); + if (!ov01a10) + return -ENOMEM; diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c -index 48e3a3e..7f62fa8 100644 +index 50618d0..137d051 100644 --- a/drivers/media/i2c/ov01a1s.c +++ b/drivers/media/i2c/ov01a1s.c -@@ -967,6 +967,12 @@ static int ov01a1s_probe(struct i2c_client *client) - s64 link_freq; - #endif +@@ -1078,6 +1078,12 @@ static int ov01a1s_probe(struct i2c_client *client) + struct ov01a1s *ov01a1s; + int ret = 0; +#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = init_vsc_symbols(); @@ -185,17 +191,19 @@ if (!ov01a1s) return -ENOMEM; diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c -index 06e1c80..ebaa149 100644 +index e807a70..176840f 100644 --- a/drivers/media/i2c/ov02c10.c +++ b/drivers/media/i2c/ov02c10.c -@@ -1298,6 +1298,10 @@ static int ov02c10_probe(struct i2c_client *client) - #endif +@@ -1314,6 +1314,12 @@ static int ov02c10_probe(struct i2c_client *client) + struct ov02c10 *ov02c10; + int ret = 0; - #if IS_ENABLED(CONFIG_INTEL_VSC) ++#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = init_vsc_symbols(); + if (ret) + return ret; ++#endif + - conf.lane_num = OV02C10_DATA_LANES; - /* frequency unit 100k */ - conf.freq = OV02C10_LINK_FREQ_400MHZ / 100000; + ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL); + if (!ov02c10) + return -ENOMEM; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0007-dkms-add-CONFIG_VIDEO_V4L2_I2C-to-BUILD_EXCLUSIVE_CO.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0007-dkms-add-CONFIG_VIDEO_V4L2_I2C-to-BUILD_EXCLUSIVE_CO.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0007-dkms-add-CONFIG_VIDEO_V4L2_I2C-to-BUILD_EXCLUSIVE_CO.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0007-dkms-add-CONFIG_VIDEO_V4L2_I2C-to-BUILD_EXCLUSIVE_CO.patch 2023-09-01 17:51:53.000000000 +0800 @@ -0,0 +1,19 @@ +From: You-Sheng Yang +Date: Fri, 7 Jul 2023 18:19:19 +0800 +Subject: dkms: add CONFIG_VIDEO_V4L2_I2C to BUILD_EXCLUSIVE_CONFIG + +BugLink: https://bugs.launchpad.net/bugs/2012407 +Signed-off-by: You-Sheng Yang +--- + dkms.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dkms.conf b/dkms.conf +index 43045fe..e9e9a2b 100644 +--- a/dkms.conf ++++ b/dkms.conf +@@ -54,3 +54,4 @@ BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" + + AUTOINSTALL="yes" ++BUILD_EXCLUSIVE_CONFIG="CONFIG_VIDEO_V4L2_I2C" diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0007-Don-t-rename-the-already-registered-PCI-device.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0007-Don-t-rename-the-already-registered-PCI-device.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0007-Don-t-rename-the-already-registered-PCI-device.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0007-Don-t-rename-the-already-registered-PCI-device.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,46 +0,0 @@ -From: Hans de Goede -Date: Wed, 1 Feb 2023 16:14:08 +0100 -Subject: Don't rename the already registered PCI-device - -devices must not be renamed after they have already been -added to the device hierarchy. - -dev_set_name() MUST only be called before dev_add() -and the pci_dev passed to ipu_pci_probe() has been -added long before ipu_pci_probe() runs, so it must -not rename it. - -Renaming it is confusing udevd which now all of a sudden sees -a device it already knows about change name. - -This is causing udev rules to not work properly with -the ipu6-driver. Drop the clearly wrong dev_set_name() call. - -Signed-off-by: Hans de Goede -(cherry picked from commit 2d1b1ad0334e2e54216a12fead50c4a33e6cdf03 https://github.com/intel/ipu6-drivers) -Signed-off-by: You-Sheng Yang (vicamo) ---- - drivers/media/pci/intel/ipu.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu.c b/drivers/media/pci/intel/ipu.c -index c9968fd..1d3afcc 100644 ---- a/drivers/media/pci/intel/ipu.c -+++ b/drivers/media/pci/intel/ipu.c -@@ -299,7 +299,7 @@ static int ipu_init_debugfs(struct ipu_device *isp) - struct dentry *file; - struct dentry *dir; - -- dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -+ dir = debugfs_create_dir(IPU_NAME, NULL); - if (!dir) - return -ENOMEM; - -@@ -438,7 +438,6 @@ static int ipu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - if (!isp) - return -ENOMEM; - -- dev_set_name(&pdev->dev, "intel-ipu"); - isp->pdev = pdev; - INIT_LIST_HEAD(&isp->devices); - diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0008-media-hm2170-Support-ver-D-silicon.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0008-media-hm2170-Support-ver-D-silicon.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0008-media-hm2170-Support-ver-D-silicon.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0008-media-hm2170-Support-ver-D-silicon.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,514 +0,0 @@ -From: Hao Yao -Date: Fri, 10 Feb 2023 16:08:20 +0800 -Subject: media: hm2170: Support ver D silicon - -Signed-off-by: Hao Yao -(cherry picked from commit f2ec481813c9d950debfceb8b4558919d0e041b4 https://github.com/intel/ipu6-drivers) -Signed-off-by: You-Sheng Yang (vicamo) ---- - drivers/media/i2c/hm2170.c | 350 ++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 313 insertions(+), 37 deletions(-) - -diff --git a/drivers/media/i2c/hm2170.c b/drivers/media/i2c/hm2170.c -index 2037e51..f99c827 100644 ---- a/drivers/media/i2c/hm2170.c -+++ b/drivers/media/i2c/hm2170.c -@@ -27,6 +27,7 @@ - - #define HM2170_REG_CHIP_ID 0x0000 - #define HM2170_CHIP_ID 0x2170 -+#define HM2170_REG_SILICON_REV 0x0002 - - #define HM2170_REG_MODE_SELECT 0x0100 - #define HM2170_MODE_STANDBY 0x00 -@@ -74,6 +75,11 @@ enum { - HM2170_LINK_FREQ_384MHZ_INDEX, - }; - -+enum hm2170_silicon_revision { -+ HM2170_SILICON_REV_B = 0, -+ HM2170_SILICON_REV_D = 1, -+}; -+ - struct hm2170_reg { - u16 address; - u8 val; -@@ -111,7 +117,7 @@ struct hm2170_mode { - const struct hm2170_reg_list reg_list; - }; - --static const struct hm2170_reg mode_1928x1088_regs[] = { -+static const struct hm2170_reg mode_1928x1088_ver_b_regs[] = { - {0x0103, 0x00}, - {0xffff, 0x10}, - {0x0202, 0x03}, -@@ -120,6 +126,7 @@ static const struct hm2170_reg mode_1928x1088_regs[] = { - {0x0301, 0x3F}, - {0x0302, 0x07}, - {0x0303, 0x04}, -+ {0x0350, 0x61}, - {0x1000, 0xC3}, - {0x1001, 0xC0}, - {0x2000, 0x00}, -@@ -153,44 +160,57 @@ static const struct hm2170_reg mode_1928x1088_regs[] = { - {0x302A, 0x30}, - {0x3042, 0x00}, - {0x3070, 0x01}, -+ {0x307B, 0x08}, - {0x30C4, 0x20}, - {0x30D0, 0x01}, - {0x30D2, 0x8E}, -- {0x30D7, 0x02}, -+ {0x30D4, 0x14}, -+ {0x30D7, 0x21}, - {0x30D9, 0x9E}, -- {0x30DE, 0x03}, -+ {0x30DA, 0x14}, -+ {0x30DE, 0x41}, - {0x30E0, 0x9E}, -- {0x30E5, 0x04}, -+ {0x30E2, 0x14}, -+ {0x30E5, 0x61}, - {0x30E7, 0x9F}, -- {0x30EC, 0x24}, -+ {0x30E9, 0x14}, -+ {0x30EC, 0x43}, - {0x30EE, 0x9F}, -- {0x30F3, 0x44}, -+ {0x30F0, 0x14}, -+ {0x30F3, 0x63}, - {0x30F5, 0x9F}, -+ {0x30F6, 0x14}, - {0x30F8, 0x00}, - {0x3101, 0x02}, - {0x3103, 0x9E}, -- {0x3108, 0x03}, -+ {0x3104, 0x14}, -+ {0x3108, 0x22}, - {0x310A, 0x9E}, -- {0x310F, 0x04}, -+ {0x310B, 0x14}, -+ {0x310F, 0x42}, - {0x3111, 0x9E}, -- {0x3116, 0x24}, -+ {0x3112, 0x14}, -+ {0x3116, 0x62}, - {0x3118, 0x9F}, -+ {0x3119, 0x14}, - {0x311D, 0x44}, - {0x311F, 0x9F}, -+ {0x3120, 0x14}, - {0x3124, 0x64}, - {0x3126, 0x9F}, -+ {0x3127, 0x14}, - {0x3135, 0x01}, - {0x3137, 0x03}, - {0x313C, 0x52}, - {0x313E, 0x68}, - {0x3144, 0x3E}, -- {0x3145, 0x68}, -+ {0x3145, 0x24}, - {0x3146, 0x08}, -- {0x3147, 0x03}, -- {0x3148, 0x0F}, -- {0x3149, 0xFF}, -+ {0x3147, 0x13}, -+ {0x3148, 0x13}, -+ {0x3149, 0x6C}, - {0x314A, 0x13}, -- {0x314B, 0x0F}, -+ {0x314B, 0x03}, - {0x314C, 0xF8}, - {0x314D, 0x04}, - {0x314E, 0x10}, -@@ -211,7 +231,7 @@ static const struct hm2170_reg mode_1928x1088_regs[] = { - {0x4801, 0x10}, - {0x4802, 0x00}, - {0x4803, 0x00}, -- {0x4804, 0x7F}, -+ {0x4804, 0x3F}, - {0x4805, 0x7F}, - {0x4806, 0x3F}, - {0x4807, 0x1F}, -@@ -220,9 +240,9 @@ static const struct hm2170_reg mode_1928x1088_regs[] = { - {0x480B, 0x08}, - {0x480C, 0x90}, - {0x480D, 0x00}, -- {0x480E, 0x01}, -+ {0x480E, 0x00}, - {0x480F, 0x04}, -- {0x4810, 0x40}, -+ {0x4810, 0x3F}, - {0x4811, 0x00}, - {0x4812, 0x00}, - {0x4813, 0x00}, -@@ -282,7 +302,236 @@ static const struct hm2170_reg mode_1928x1088_regs[] = { - {0x48C9, 0x00}, - {0x48CA, 0x00}, - {0x48CB, 0x00}, -- {0x48CC, 0x00}, -+ {0x48CC, 0x1F}, -+ {0x48F0, 0x00}, -+ {0x48F1, 0x00}, -+ {0x48F2, 0x04}, -+ {0x48F3, 0x01}, -+ {0x48F4, 0xE0}, -+ {0x48F5, 0x01}, -+ {0x48F6, 0x10}, -+ {0x48F7, 0x00}, -+ {0x48F8, 0x00}, -+ {0x48F9, 0x00}, -+ {0x48FA, 0x00}, -+ {0x48FB, 0x01}, -+ {0x4931, 0x2B}, -+ {0x4932, 0x01}, -+ {0x4933, 0x01}, -+ {0x4934, 0x00}, -+ {0x4935, 0x0F}, -+ {0x4980, 0x00}, -+ {0x4A72, 0x01}, -+ {0x4A73, 0x01}, -+ {0x4C30, 0x00}, -+ {0x4CF2, 0x01}, -+ {0x4CF3, 0x01}, -+ {0x0104, 0x00}, -+}; -+ -+static const struct hm2170_reg mode_1928x1088_ver_d_regs[] = { -+ {0x0103, 0x00}, -+ {0xffff, 0x10}, -+ {0x0202, 0x03}, -+ {0x0203, 0x60}, -+ {0x0300, 0x5E}, -+ {0x0301, 0x3F}, -+ {0x0302, 0x07}, -+ {0x0303, 0x04}, -+ {0x0350, 0x61}, -+ {0x1000, 0xC3}, -+ {0x1001, 0xC0}, -+ {0x2000, 0x00}, -+ {0x2088, 0x01}, -+ {0x2089, 0x00}, -+ {0x208A, 0xC8}, -+ {0x2700, 0x00}, -+ {0x2711, 0x01}, -+ {0x2713, 0x04}, -+ {0x272F, 0x01}, -+ {0x2800, 0x01}, -+ {0x2821, 0x8E}, -+ {0x2823, 0x01}, -+ {0x282E, 0x01}, -+ {0x282F, 0xC0}, -+ {0x2839, 0x13}, -+ {0x283A, 0x01}, -+ {0x283B, 0x0F}, -+ {0x2842, 0x0C}, -+ {0x2846, 0x01}, -+ {0x2847, 0x94}, -+ {0x3001, 0x00}, -+ {0x3002, 0x88}, -+ {0x3004, 0x02}, -+ {0x3024, 0x20}, -+ {0x3025, 0x12}, -+ {0x3026, 0x00}, -+ {0x3027, 0x81}, -+ {0x3028, 0x01}, -+ {0x3029, 0x00}, -+ {0x302A, 0x30}, -+ {0x3042, 0x00}, -+ {0x3070, 0x01}, -+ {0x307B, 0x08}, -+ {0x30C4, 0x20}, -+ {0x30D0, 0x02}, -+ {0x30D1, 0x03}, -+ {0x30D2, 0x3F}, -+ {0x30D3, 0x15}, -+ {0x30D7, 0x03}, -+ {0x30D8, 0x03}, -+ {0x30D9, 0x3F}, -+ {0x30DA, 0x15}, -+ {0x30DE, 0x04}, -+ {0x30DF, 0x03}, -+ {0x30E0, 0x3F}, -+ {0x30E1, 0x15}, -+ {0x30E5, 0x24}, -+ {0x30E6, 0x03}, -+ {0x30E7, 0x3F}, -+ {0x30E8, 0x15}, -+ {0x30EC, 0x2C}, -+ {0x30ED, 0x03}, -+ {0x30EE, 0x3F}, -+ {0x30EF, 0x15}, -+ {0x30F3, 0x2C}, -+ {0x30F4, 0x03}, -+ {0x30F5, 0x3F}, -+ {0x30F6, 0x15}, -+ {0x30F8, 0x01}, -+ {0x3101, 0x02}, -+ {0x3102, 0x01}, -+ {0x3103, 0x1F}, -+ {0x3104, 0x15}, -+ {0x3108, 0x03}, -+ {0x3109, 0x01}, -+ {0x310A, 0x1F}, -+ {0x310B, 0x14}, -+ {0x310F, 0x04}, -+ {0x3110, 0x01}, -+ {0x3111, 0x1F}, -+ {0x3112, 0x13}, -+ {0x3116, 0x24}, -+ {0x3117, 0x01}, -+ {0x3118, 0x3F}, -+ {0x3119, 0x13}, -+ {0x311D, 0x2C}, -+ {0x311E, 0x01}, -+ {0x311F, 0x3F}, -+ {0x3120, 0x13}, -+ {0x3121, 0x94}, -+ {0x3124, 0x2C}, -+ {0x3125, 0x01}, -+ {0x3126, 0x3F}, -+ {0x3127, 0x13}, -+ {0x3129, 0x01}, -+ {0x3135, 0x01}, -+ {0x3137, 0x03}, -+ {0x3139, 0x37}, -+ {0x313C, 0x52}, -+ {0x313E, 0x68}, -+ {0x3144, 0x3E}, -+ {0x3145, 0xE4}, -+ {0x3146, 0x58}, -+ {0x3147, 0x13}, -+ {0x3148, 0x11}, -+ {0x3149, 0x27}, -+ {0x314A, 0x13}, -+ {0x314B, 0x03}, -+ {0x314C, 0x0C}, -+ {0x314D, 0x00}, -+ {0x314E, 0x10}, -+ {0x3158, 0x01}, -+ {0x3161, 0x11}, -+ {0x3171, 0x05}, -+ {0x317A, 0x21}, -+ {0x317B, 0xF0}, -+ {0x317C, 0x0C}, -+ {0x317D, 0x09}, -+ {0x3182, 0x88}, -+ {0x3183, 0x18}, -+ {0x3184, 0x40}, -+ {0x318E, 0x88}, -+ {0x318F, 0x00}, -+ {0x3190, 0x00}, -+ {0x4003, 0x02}, -+ {0x4004, 0x02}, -+ {0x4800, 0x26}, -+ {0x4801, 0x21}, -+ {0x4802, 0x10}, -+ {0x4803, 0x00}, -+ {0x4804, 0x3F}, -+ {0x4805, 0x7F}, -+ {0x4806, 0x3F}, -+ {0x4807, 0x1F}, -+ {0x4809, 0x04}, -+ {0x480A, 0x84}, -+ {0x480B, 0x04}, -+ {0x480C, 0x48}, -+ {0x480D, 0x00}, -+ {0x480E, 0x00}, -+ {0x480F, 0x04}, -+ {0x4810, 0x3F}, -+ {0x4811, 0x00}, -+ {0x4812, 0x00}, -+ {0x4813, 0x00}, -+ {0x4814, 0x00}, -+ {0x4815, 0x00}, -+ {0x4816, 0x00}, -+ {0x4817, 0x00}, -+ {0x4818, 0x00}, -+ {0x4819, 0x02}, -+ {0x481F, 0x00}, -+ {0x4820, 0x0E}, -+ {0x4821, 0x0E}, -+ {0x4840, 0x00}, -+ {0x4844, 0x00}, -+ {0x4845, 0x00}, -+ {0x4846, 0x00}, -+ {0x4847, 0x00}, -+ {0x4848, 0x00}, -+ {0x4849, 0xF1}, -+ {0x484A, 0x00}, -+ {0x484B, 0x88}, -+ {0x484C, 0x01}, -+ {0x484D, 0x04}, -+ {0x484E, 0x64}, -+ {0x484F, 0x50}, -+ {0x4850, 0x04}, -+ {0x4851, 0x00}, -+ {0x4852, 0x01}, -+ {0x4853, 0x19}, -+ {0x4854, 0x50}, -+ {0x4855, 0x04}, -+ {0x4856, 0x00}, -+ {0x4863, 0x02}, -+ {0x4864, 0x3D}, -+ {0x4865, 0x02}, -+ {0x4866, 0xB0}, -+ {0x4880, 0x00}, -+ {0x48A0, 0x00}, -+ {0x48A1, 0x04}, -+ {0x48A2, 0x01}, -+ {0x48A3, 0xDD}, -+ {0x48A4, 0x0C}, -+ {0x48A5, 0x3B}, -+ {0x48A6, 0x20}, -+ {0x48A7, 0x20}, -+ {0x48A8, 0x20}, -+ {0x48A9, 0x20}, -+ {0x48AA, 0x00}, -+ {0x48C0, 0x3F}, -+ {0x48C1, 0x29}, -+ {0x48C3, 0x14}, -+ {0x48C4, 0x00}, -+ {0x48C5, 0x07}, -+ {0x48C6, 0x88}, -+ {0x48C7, 0x04}, -+ {0x48C8, 0x40}, -+ {0x48C9, 0x00}, -+ {0x48CA, 0x00}, -+ {0x48CB, 0x00}, -+ {0x48CC, 0x1F}, - {0x48F0, 0x00}, - {0x48F1, 0x00}, - {0x48F2, 0x04}, -@@ -321,18 +570,36 @@ static const s64 link_freq_menu_items[] = { - HM2170_LINK_FREQ_384MHZ, - }; - --static const struct hm2170_mode supported_modes[] = { -+static const struct hm2170_mode supported_modes[][1] = { -+ { -+ { -+ .width = 1928, -+ .height = 1088, -+ .hts = 2192, -+ .vts_def = HM2170_VTS_DEF, -+ .vts_min = HM2170_VTS_MIN, -+ .reg_list = { -+ .num_of_regs = ARRAY_SIZE(mode_1928x1088_ver_b_regs), -+ .regs = mode_1928x1088_ver_b_regs, -+ }, -+ .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, -+ }, -+ -+ }, - { -- .width = 1928, -- .height = 1088, -- .hts = 2192, -- .vts_def = HM2170_VTS_DEF, -- .vts_min = HM2170_VTS_MIN, -- .reg_list = { -- .num_of_regs = ARRAY_SIZE(mode_1928x1088_regs), -- .regs = mode_1928x1088_regs, -+ { -+ .width = 1928, -+ .height = 1088, -+ .hts = 2192, -+ .vts_def = HM2170_VTS_DEF, -+ .vts_min = HM2170_VTS_MIN, -+ .reg_list = { -+ .num_of_regs = ARRAY_SIZE(mode_1928x1088_ver_d_regs), -+ .regs = mode_1928x1088_ver_d_regs, -+ }, -+ .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, -+ - }, -- .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, - }, - }; - -@@ -352,6 +619,8 @@ struct hm2170 { - #endif - /* Current mode */ - const struct hm2170_mode *cur_mode; -+ /* HM2170 silicon revision, B or D */ -+ enum hm2170_silicon_revision rev; - - /* To serialize asynchronus callbacks */ - struct mutex mutex; -@@ -770,9 +1039,9 @@ static int hm2170_set_format(struct v4l2_subdev *sd, - const struct hm2170_mode *mode; - s32 vblank_def, h_blank; - -- mode = v4l2_find_nearest_size(supported_modes, -- ARRAY_SIZE(supported_modes), width, -- height, fmt->format.width, -+ mode = v4l2_find_nearest_size(supported_modes[hm2170->rev], -+ ARRAY_SIZE(supported_modes[hm2170->rev]), -+ width, height, fmt->format.width, - fmt->format.height); - - mutex_lock(&hm2170->mutex); -@@ -836,15 +1105,17 @@ static int hm2170_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) - { -- if (fse->index >= ARRAY_SIZE(supported_modes)) -+ struct hm2170 *hm2170 = to_hm2170(sd); -+ -+ if (fse->index >= ARRAY_SIZE(supported_modes[hm2170->rev])) - return -EINVAL; - - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - -- fse->min_width = supported_modes[fse->index].width; -+ fse->min_width = supported_modes[hm2170->rev][fse->index].width; - fse->max_width = fse->min_width; -- fse->min_height = supported_modes[fse->index].height; -+ fse->min_height = supported_modes[hm2170->rev][fse->index].height; - fse->max_height = fse->min_height; - - return 0; -@@ -855,7 +1126,7 @@ static int hm2170_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - struct hm2170 *hm2170 = to_hm2170(sd); - - mutex_lock(&hm2170->mutex); -- hm2170_update_pad_format(&supported_modes[0], -+ hm2170_update_pad_format(&supported_modes[hm2170->rev][0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); - mutex_unlock(&hm2170->mutex); - -@@ -891,17 +1162,22 @@ static int hm2170_identify_module(struct hm2170 *hm2170) - struct i2c_client *client = v4l2_get_subdevdata(&hm2170->sd); - int ret; - u32 val; -+ char rev; - -- ret = hm2170_read_reg(hm2170, HM2170_REG_CHIP_ID, 2, &val); -+ ret = hm2170_read_reg(hm2170, HM2170_REG_CHIP_ID, 3, &val); - - if (ret) - return ret; - -+ rev = val & 0xff; -+ val = val >> 8; - if (val != HM2170_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", - HM2170_CHIP_ID, val); - return -ENXIO; - } -+ hm2170->rev = rev < 0x4 ? HM2170_SILICON_REV_B : -+ HM2170_SILICON_REV_D; - - return 0; - } -@@ -966,7 +1242,7 @@ static int hm2170_probe(struct i2c_client *client) - } - - mutex_init(&hm2170->mutex); -- hm2170->cur_mode = &supported_modes[0]; -+ hm2170->cur_mode = &supported_modes[hm2170->rev][0]; - ret = hm2170_init_controls(hm2170); - if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0009-cio2-bridge-Fix-compilation-with-kernel-6.3.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0009-cio2-bridge-Fix-compilation-with-kernel-6.3.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0009-cio2-bridge-Fix-compilation-with-kernel-6.3.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0009-cio2-bridge-Fix-compilation-with-kernel-6.3.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,91 +0,0 @@ -From: Hans de Goede -Date: Wed, 3 May 2023 10:01:23 +0200 -Subject: cio2-bridge: Fix compilation with kernel >= 6.3 - -Fix cio2-bridge compilation with kernel 6.3 and later. - -Signed-off-by: Hans de Goede -(cherry picked from commit c61b117487a720084f4a645f728eed9b953690fa github.com/jwrdegoede/ipu6-drivers) -Signed-off-by: You-Sheng Yang ---- - drivers/media/pci/intel/cio2-bridge.c | 29 +++++++++++++++++++++++++++++ - drivers/media/pci/intel/cio2-bridge.h | 3 +++ - 2 files changed, 32 insertions(+) - -diff --git a/drivers/media/pci/intel/cio2-bridge.c b/drivers/media/pci/intel/cio2-bridge.c -index fe7175d..526d0c0 100644 ---- a/drivers/media/pci/intel/cio2-bridge.c -+++ b/drivers/media/pci/intel/cio2-bridge.c -@@ -293,13 +293,33 @@ static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) - - for (i = 0; i < bridge->n_sensors; i++) { - sensor = &bridge->sensors[i]; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -+ software_node_unregister_node_group(sensor->group); -+#else - software_node_unregister_nodes(sensor->swnodes); -+#endif - ACPI_FREE(sensor->pld); - acpi_dev_put(sensor->adev); - i2c_unregister_device(sensor->vcm_i2c_client); - } - } - -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -+static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor) -+{ -+ struct software_node *nodes = sensor->swnodes; -+ -+ sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; -+ sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; -+ sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; -+ sensor->group[SWNODE_CIO2_PORT] = &nodes[SWNODE_CIO2_PORT]; -+ sensor->group[SWNODE_CIO2_ENDPOINT] = &nodes[SWNODE_CIO2_ENDPOINT]; -+ if (sensor->ssdb.vcmtype && -+ sensor->ssdb.vcmtype <= ARRAY_SIZE(cio2_vcm_types)) -+ sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; -+} -+#endif -+ - static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, - struct cio2_bridge *bridge, - struct pci_dev *cio2) -@@ -352,7 +372,12 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, - cio2_bridge_create_fwnode_properties(sensor, bridge, cfg); - cio2_bridge_create_connection_swnodes(bridge, sensor); - -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -+ cio2_bridge_init_swnode_group(sensor); -+ software_node_register_node_group(sensor->group); -+#else - ret = software_node_register_nodes(sensor->swnodes); -+#endif - if (ret) - goto err_free_pld; - -@@ -379,7 +404,11 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, - return 0; - - err_free_swnodes: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -+ software_node_unregister_node_group(sensor->group); -+#else - software_node_unregister_nodes(sensor->swnodes); -+#endif - err_free_pld: - ACPI_FREE(sensor->pld); - err_put_adev: -diff --git a/drivers/media/pci/intel/cio2-bridge.h b/drivers/media/pci/intel/cio2-bridge.h -index a0c99ba..77d76dd 100644 ---- a/drivers/media/pci/intel/cio2-bridge.h -+++ b/drivers/media/pci/intel/cio2-bridge.h -@@ -121,6 +121,9 @@ struct cio2_sensor { - - /* SWNODE_COUNT + 1 for terminating empty node */ - struct software_node swnodes[SWNODE_COUNT + 1]; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) -+ const struct software_node *group[SWNODE_COUNT + 1]; -+#endif - struct cio2_node_names node_names; - - struct cio2_sensor_ssdb ssdb; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0009-ipu6-Fix-compilation-with-kernels-6.6.0.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0009-ipu6-Fix-compilation-with-kernels-6.6.0.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0009-ipu6-Fix-compilation-with-kernels-6.6.0.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0009-ipu6-Fix-compilation-with-kernels-6.6.0.patch 2023-09-01 17:51:53.000000000 +0800 @@ -0,0 +1,210 @@ +From: Hans de Goede +Date: Mon, 28 Aug 2023 17:05:16 +0200 +Subject: ipu6: Fix compilation with kernels >= 6.6.0 + +BugLink: https://bugs.launchpad.net/bugs/2026402 + +Kernel 6.6 has made some significant changes to how v4l2-async +(sub)dev registration works. Adjust the code accordingly. + +Signed-off-by: Hans de Goede +(cherry picked from commit 931ceecc44e2aec6e703cc1ecd1a281114678756 github.com/jwrdegoede/ipu6-drivers) +Signed-off-by: You-Sheng Yang +--- + drivers/media/pci/intel/ipu-isys.c | 78 +++++++++++++++++++++++++++- + drivers/media/pci/intel/ipu6/ipu6-isys-phy.c | 14 ++++- + include/media/ipu-isys.h | 5 ++ + 3 files changed, 94 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/pci/intel/ipu-isys.c b/drivers/media/pci/intel/ipu-isys.c +index 5cab1bb..de68e3a 100644 +--- a/drivers/media/pci/intel/ipu-isys.c ++++ b/drivers/media/pci/intel/ipu-isys.c +@@ -725,7 +725,11 @@ static int isys_iwake_watermark_cleanup(struct ipu_isys *isys) + /* The .bound() notifier callback when a match is found */ + static int isys_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + struct v4l2_async_subdev *asd) ++#else ++ struct v4l2_async_connection *asd) ++#endif + { + struct ipu_isys *isys = container_of(notifier, + struct ipu_isys, notifier); +@@ -741,7 +745,11 @@ static int isys_notifier_bound(struct v4l2_async_notifier *notifier, + + static void isys_notifier_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + struct v4l2_async_subdev *asd) ++#else ++ struct v4l2_async_connection *asd) ++#endif + { + struct ipu_isys *isys = container_of(notifier, + struct ipu_isys, notifier); +@@ -765,6 +773,7 @@ static const struct v4l2_async_notifier_operations isys_async_ops = { + .complete = isys_notifier_complete, + }; + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + static int isys_fwnode_parse(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) +@@ -777,6 +786,7 @@ static int isys_fwnode_parse(struct device *dev, + + return 0; + } ++#endif + + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) && LINUX_VERSION_CODE != KERNEL_VERSION(5, 15, 71) + static int isys_notifier_init(struct ipu_isys *isys) +@@ -819,7 +829,7 @@ static void isys_notifier_cleanup(struct ipu_isys *isys) + v4l2_async_notifier_unregister(&isys->notifier); + v4l2_async_notifier_cleanup(&isys->notifier); + } +-#else ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + static int isys_notifier_init(struct ipu_isys *isys) + { + struct ipu_device *isp = isys->adev->isp; +@@ -852,7 +862,73 @@ static int isys_notifier_init(struct ipu_isys *isys) + + return ret; + } ++#else ++static int isys_notifier_init(struct ipu_isys *isys) ++{ ++ const struct ipu_isys_internal_csi2_pdata *csi2 = ++ &isys->pdata->ipdata->csi2; ++ struct ipu_device *isp = isys->adev->isp; ++ struct device *dev = &isp->pdev->dev; ++ unsigned int i; ++ int ret; ++ ++ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); ++ ++ for (i = 0; i < csi2->nports; i++) { ++ struct v4l2_fwnode_endpoint vep = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ struct sensor_async_subdev *s_asd; ++ struct fwnode_handle *ep; ++ ++ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, ++ FWNODE_GRAPH_ENDPOINT_NEXT); ++ if (!ep) ++ continue; ++ ++ ret = v4l2_fwnode_endpoint_parse(ep, &vep); ++ if (ret) ++ goto err_parse; ++ ++ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, ++ struct ++ sensor_async_subdev); ++ if (IS_ERR(s_asd)) { ++ ret = PTR_ERR(s_asd); ++ goto err_parse; ++ } ++ ++ s_asd->csi2.port = vep.base.port; ++ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; ++ ++ fwnode_handle_put(ep); ++ ++ continue; ++ ++err_parse: ++ fwnode_handle_put(ep); ++ return ret; ++ } ++ ++ if (list_empty(&isys->notifier.waiting_list)) { ++ /* isys probe could continue with async subdevs missing */ ++ dev_warn(&isys->adev->dev, "no subdev found in graph\n"); ++ return 0; ++ } ++ ++ isys->notifier.ops = &isys_async_ops; ++ ret = v4l2_async_nf_register(&isys->notifier); ++ if (ret) { ++ dev_err(&isys->adev->dev, ++ "failed to register async notifier : %d\n", ret); ++ v4l2_async_nf_cleanup(&isys->notifier); ++ } ++ ++ return ret; ++} ++#endif + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) + static void isys_notifier_cleanup(struct ipu_isys *isys) + { + v4l2_async_nf_unregister(&isys->notifier); +diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-phy.c +index c267801..d2f4f74 100644 +--- a/drivers/media/pci/intel/ipu6/ipu6-isys-phy.c ++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-phy.c +@@ -504,11 +504,16 @@ int ipu6_isys_phy_common_init(struct ipu_isys *isys) + struct ipu_bus_device *adev = to_ipu_bus_device(&isys->adev->dev); + struct ipu_device *isp = adev->isp; + void __iomem *isp_base = isp->base; +- struct v4l2_async_subdev *asd; + struct sensor_async_subdev *s_asd; + unsigned int i; + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) ++ struct v4l2_async_subdev *asd; + list_for_each_entry(asd, &isys->notifier.asd_list, asd_list) { ++#else ++ struct v4l2_async_connection *asd; ++ list_for_each_entry(asd, &isys->notifier.done_list, asc_entry) { ++#endif + s_asd = container_of(asd, struct sensor_async_subdev, asd); + phy_id = s_asd->csi2.port / 4; + phy_base = isp_base + IPU6_ISYS_PHY_BASE(phy_id); +@@ -562,12 +567,17 @@ int ipu6_isys_phy_config(struct ipu_isys *isys) + struct ipu_device *isp = adev->isp; + void __iomem *isp_base = isp->base; + const struct phy_reg **phy_config_regs; +- struct v4l2_async_subdev *asd; + struct sensor_async_subdev *s_asd; + struct ipu_isys_csi2_config cfg; + int i; + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) ++ struct v4l2_async_subdev *asd; + list_for_each_entry(asd, &isys->notifier.asd_list, asd_list) { ++#else ++ struct v4l2_async_connection *asd; ++ list_for_each_entry(asd, &isys->notifier.done_list, asc_entry) { ++#endif + s_asd = container_of(asd, struct sensor_async_subdev, asd); + cfg.port = s_asd->csi2.port; + cfg.nlanes = s_asd->csi2.nlanes; +diff --git a/include/media/ipu-isys.h b/include/media/ipu-isys.h +index b75febf..0b0caab 100644 +--- a/include/media/ipu-isys.h ++++ b/include/media/ipu-isys.h +@@ -6,6 +6,7 @@ + + #include + #include ++#include + #include + + #define IPU_ISYS_MAX_CSI2_LANES 4 +@@ -37,7 +38,11 @@ struct ipu_isys_subdev_pdata { + }; + + struct sensor_async_subdev { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + struct v4l2_async_subdev asd; ++#else ++ struct v4l2_async_connection asd; ++#endif + struct ipu_isys_csi2_config csi2; + }; + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0010-ipu6-Fix-sensor-driver-compilation-with-kernels-6.6..patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0010-ipu6-Fix-sensor-driver-compilation-with-kernels-6.6..patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0010-ipu6-Fix-sensor-driver-compilation-with-kernels-6.6..patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0010-ipu6-Fix-sensor-driver-compilation-with-kernels-6.6..patch 2023-09-01 17:51:53.000000000 +0800 @@ -0,0 +1,201 @@ +From: Hans de Goede +Date: Thu, 28 Sep 2023 12:09:27 +0200 +Subject: ipu6: Fix sensor driver compilation with kernels >= 6.6.0 + +Kernel 6.6.0 has dropped the probe_new i2c_driver callback, +all drivers must now use the probe callback which now uses +the same func-prototype as probe_new used to. + +Signed-off-by: Hans de Goede +(cherry picked from commit 0398e112f2f1dc293183f6e6720985b1d861ea39 github.com/jwrdegoede/ipu6-drivers) +Signed-off-by: You-Sheng Yang +--- + drivers/media/i2c/gc5035.c | 4 ++++ + drivers/media/i2c/hi556.c | 4 ++++ + drivers/media/i2c/hm11b1.c | 4 ++++ + drivers/media/i2c/hm2170.c | 4 ++++ + drivers/media/i2c/hm2172.c | 4 ++++ + drivers/media/i2c/ov01a10.c | 4 ++++ + drivers/media/i2c/ov01a1s.c | 4 ++++ + drivers/media/i2c/ov02c10.c | 4 ++++ + drivers/media/i2c/ov08a10.c | 4 ++++ + drivers/media/i2c/ov2740.c | 4 ++++ + drivers/media/i2c/ov8856.c | 4 ++++ + 11 files changed, 44 insertions(+) + +diff --git a/drivers/media/i2c/gc5035.c b/drivers/media/i2c/gc5035.c +index b46fd54..25f08cc 100644 +--- a/drivers/media/i2c/gc5035.c ++++ b/drivers/media/i2c/gc5035.c +@@ -2188,7 +2188,11 @@ static struct i2c_driver gc5035_i2c_driver = { + .acpi_match_table = ACPI_PTR(gc5035_acpi_ids), + .of_match_table = gc5035_of_match, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = gc5035_probe, ++#else ++ .probe = gc5035_probe, ++#endif + .remove = gc5035_remove, + }; + module_i2c_driver(gc5035_i2c_driver); +diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c +index 4f99c94..03061fc 100644 +--- a/drivers/media/i2c/hi556.c ++++ b/drivers/media/i2c/hi556.c +@@ -1344,7 +1344,11 @@ static struct i2c_driver hi556_i2c_driver = { + .pm = &hi556_pm_ops, + .acpi_match_table = ACPI_PTR(hi556_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = hi556_probe, ++#else ++ .probe = hi556_probe, ++#endif + .remove = hi556_remove, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) + .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, +diff --git a/drivers/media/i2c/hm11b1.c b/drivers/media/i2c/hm11b1.c +index 39f60a7..fa9da1b 100644 +--- a/drivers/media/i2c/hm11b1.c ++++ b/drivers/media/i2c/hm11b1.c +@@ -1229,7 +1229,11 @@ static struct i2c_driver hm11b1_i2c_driver = { + .pm = &hm11b1_pm_ops, + .acpi_match_table = ACPI_PTR(hm11b1_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = hm11b1_probe, ++#else ++ .probe = hm11b1_probe, ++#endif + .remove = hm11b1_remove, + }; + +diff --git a/drivers/media/i2c/hm2170.c b/drivers/media/i2c/hm2170.c +index 596b6dd..284067e 100644 +--- a/drivers/media/i2c/hm2170.c ++++ b/drivers/media/i2c/hm2170.c +@@ -1330,7 +1330,11 @@ static struct i2c_driver hm2170_i2c_driver = { + .pm = &hm2170_pm_ops, + .acpi_match_table = hm2170_acpi_ids, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = hm2170_probe, ++#else ++ .probe = hm2170_probe, ++#endif + .remove = hm2170_remove, + }; + +diff --git a/drivers/media/i2c/hm2172.c b/drivers/media/i2c/hm2172.c +index 52d7274..0c54793 100644 +--- a/drivers/media/i2c/hm2172.c ++++ b/drivers/media/i2c/hm2172.c +@@ -1620,7 +1620,11 @@ static struct i2c_driver hm2172_i2c_driver = { + .pm = &hm2172_pm_ops, + .acpi_match_table = hm2172_acpi_ids, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = hm2172_probe, ++#else ++ .probe = hm2172_probe, ++#endif + .remove = hm2172_remove, + }; + +diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c +index 180ced5..e7ac490 100644 +--- a/drivers/media/i2c/ov01a10.c ++++ b/drivers/media/i2c/ov01a10.c +@@ -1031,7 +1031,11 @@ static struct i2c_driver ov01a10_i2c_driver = { + .pm = &ov01a10_pm_ops, + .acpi_match_table = ACPI_PTR(ov01a10_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov01a10_probe, ++#else ++ .probe = ov01a10_probe, ++#endif + .remove = ov01a10_remove, + }; + +diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c +index 137d051..8c8911b 100644 +--- a/drivers/media/i2c/ov01a1s.c ++++ b/drivers/media/i2c/ov01a1s.c +@@ -1178,7 +1178,11 @@ static struct i2c_driver ov01a1s_i2c_driver = { + .pm = &ov01a1s_pm_ops, + .acpi_match_table = ACPI_PTR(ov01a1s_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov01a1s_probe, ++#else ++ .probe = ov01a1s_probe, ++#endif + .remove = ov01a1s_remove, + }; + +diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c +index 176840f..56370ef 100644 +--- a/drivers/media/i2c/ov02c10.c ++++ b/drivers/media/i2c/ov02c10.c +@@ -1423,7 +1423,11 @@ static struct i2c_driver ov02c10_i2c_driver = { + .pm = &ov02c10_pm_ops, + .acpi_match_table = ACPI_PTR(ov02c10_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov02c10_probe, ++#else ++ .probe = ov02c10_probe, ++#endif + .remove = ov02c10_remove, + }; + +diff --git a/drivers/media/i2c/ov08a10.c b/drivers/media/i2c/ov08a10.c +index 3ae4662..d474735 100644 +--- a/drivers/media/i2c/ov08a10.c ++++ b/drivers/media/i2c/ov08a10.c +@@ -1131,7 +1131,11 @@ static struct i2c_driver ov08a10_i2c_driver = { + .pm = &ov08a10_pm_ops, + .acpi_match_table = ov08a10_acpi_ids, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov08a10_probe, ++#else ++ .probe = ov08a10_probe, ++#endif + .remove = ov08a10_remove, + }; + +diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c +index 1b26535..0b16d5a 100644 +--- a/drivers/media/i2c/ov2740.c ++++ b/drivers/media/i2c/ov2740.c +@@ -1627,7 +1627,11 @@ static struct i2c_driver ov2740_i2c_driver = { + .pm = &ov2740_pm_ops, + .acpi_match_table = ov2740_acpi_ids, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov2740_probe, ++#else ++ .probe = ov2740_probe, ++#endif + .remove = ov2740_remove, + }; + +diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c +index 07b4d16..b1e938e 100644 +--- a/drivers/media/i2c/ov8856.c ++++ b/drivers/media/i2c/ov8856.c +@@ -1308,7 +1308,11 @@ static struct i2c_driver ov8856_i2c_driver = { + .pm = &ov8856_pm_ops, + .acpi_match_table = ACPI_PTR(ov8856_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov8856_probe, ++#else ++ .probe = ov8856_probe, ++#endif + .remove = ov8856_remove, + .id_table = ov8856_id_table, + }; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0010-linux-6.5-get_user_pages-dropped-vmas.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0010-linux-6.5-get_user_pages-dropped-vmas.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0010-linux-6.5-get_user_pages-dropped-vmas.patch 2023-07-28 18:36:03.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0010-linux-6.5-get_user_pages-dropped-vmas.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,31 +0,0 @@ -From 98656466fdb80079fb3b49d8fccc32121e325df5 Mon Sep 17 00:00:00 2001 -From: Paolo Pisati -Date: Fri, 28 Jul 2023 10:34:48 +0000 -Subject: [PATCH] linux 6.5: get_user_pages() dropped vmas - -Signed-off-by: Paolo Pisati ---- - drivers/media/pci/intel/ipu-psys.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/drivers/media/pci/intel/ipu-psys.c b/drivers/media/pci/intel/ipu-psys.c -index ca2830e..0f6d939 100644 ---- a/drivers/media/pci/intel/ipu-psys.c -+++ b/drivers/media/pci/intel/ipu-psys.c -@@ -218,7 +218,12 @@ static int ipu_psys_get_userpages(struct ipu_dma_buf_attach *attach) - #else - FOLL_WRITE, - #endif -- pages, NULL); -+ pages -+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) -+ , NULL); -+#else -+ ); -+#endif - if (nr < npages) - goto error_up_read; - } --- -2.40.1 - diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0011-UBUNTU-SAUCE-i2c-compile-omitted-sensor-drivers.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/0011-UBUNTU-SAUCE-i2c-compile-omitted-sensor-drivers.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/0011-UBUNTU-SAUCE-i2c-compile-omitted-sensor-drivers.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/0011-UBUNTU-SAUCE-i2c-compile-omitted-sensor-drivers.patch 2023-09-01 17:51:53.000000000 +0800 @@ -0,0 +1,257 @@ +From: You-Sheng Yang +Date: Tue, 17 Oct 2023 16:07:55 +0800 +Subject: UBUNTU: SAUCE: i2c: compile omitted sensor drivers + +Signed-off-by: You-Sheng Yang +--- + Makefile | 3 +++ + dkms.conf | 16 +++++++++++++ + drivers/media/i2c/Makefile | 6 ++++- + drivers/media/i2c/ov02e10.c | 4 ++++ + drivers/media/i2c/ov08a10.c | 1 + + drivers/media/i2c/ov13858_intel.c | 47 +++++++++++++++++++++++++++++++++++++-- + 6 files changed, 74 insertions(+), 3 deletions(-) + +diff --git a/Makefile b/Makefile +index d1b1db6..3eb9b59 100644 +--- a/Makefile ++++ b/Makefile +@@ -14,7 +14,10 @@ export CONFIG_VIDEO_OV01A1S = m + export CONFIG_VIDEO_OV01A10 = m + export CONFIG_VIDEO_OV02C10 = m + export CONFIG_VIDEO_OV02E10 = m ++export CONFIG_VIDEO_OV08A10 = m ++export CONFIG_VIDEO_OV13858 = m + export CONFIG_VIDEO_OV2740 = m ++export CONFIG_VIDEO_OV8856 = m + export CONFIG_VIDEO_HM2170 = m + export CONFIG_VIDEO_HM2172 = m + export CONFIG_VIDEO_HI556 = m +diff --git a/dkms.conf b/dkms.conf +index e9e9a2b..1474485 100644 +--- a/dkms.conf ++++ b/dkms.conf +@@ -21,6 +21,10 @@ BUILT_MODULE_NAME[$((++i))]="hm11b1" + BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" + ++BUILT_MODULE_NAME[$((++i))]="gc5035" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" ++ + BUILT_MODULE_NAME[$((++i))]="ov01a1s" + BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" +@@ -33,10 +37,22 @@ BUILT_MODULE_NAME[$((++i))]="ov02c10" + BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" + ++BUILT_MODULE_NAME[$((++i))]="ov08a10" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" ++ ++BUILT_MODULE_NAME[$((++i))]="ov13858" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" ++ + BUILT_MODULE_NAME[$((++i))]="ov2740" + BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" + ++BUILT_MODULE_NAME[$((++i))]="ov8856" ++BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" ++DEST_MODULE_LOCATION[$i]="/updates" ++ + BUILT_MODULE_NAME[$((++i))]="hm2170" + BUILT_MODULE_LOCATION[$i]="drivers/media/i2c" + DEST_MODULE_LOCATION[$i]="/updates" +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 1f39544..b8b37e8 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -7,8 +7,12 @@ obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o + obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o + obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o + obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o ++obj-$(CONFIG_VIDEO_OV08A10) += ov08a10.o ++obj-$(CONFIG_VIDEO_OV13858) += ov13858.o ++ov13858-objs += ov13858_intel.o + obj-$(CONFIG_VIDEO_OV2740) += ov2740.o ++obj-$(CONFIG_VIDEO_OV8856) += ov8856.o + obj-$(CONFIG_VIDEO_HM2170) += hm2170.o +-obj-$(CONFIG_VIDEO_HM2170) += hm2172.o ++obj-$(CONFIG_VIDEO_HM2172) += hm2172.o + obj-$(CONFIG_VIDEO_HI556) += hi556.o + obj-$(CONFIG_POWER_CTRL_LOGIC) += power_ctrl_logic.o +diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c +index 08ccc8a..f3505f7 100644 +--- a/drivers/media/i2c/ov02e10.c ++++ b/drivers/media/i2c/ov02e10.c +@@ -1025,7 +1025,11 @@ static struct i2c_driver ov02e10_i2c_driver = { + .pm = &ov02e10_pm_ops, + .acpi_match_table = ov02e10_acpi_ids, + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + .probe_new = ov02e10_probe, ++#else ++ .probe = ov02e10_probe, ++#endif + .remove = ov02e10_remove, + }; + +diff --git a/drivers/media/i2c/ov08a10.c b/drivers/media/i2c/ov08a10.c +index d474735..c6e67ee 100644 +--- a/drivers/media/i2c/ov08a10.c ++++ b/drivers/media/i2c/ov08a10.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/drivers/media/i2c/ov13858_intel.c b/drivers/media/i2c/ov13858_intel.c +index 43885cf..596368b 100644 +--- a/drivers/media/i2c/ov13858_intel.c ++++ b/drivers/media/i2c/ov13858_intel.c +@@ -1381,7 +1381,11 @@ static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) + { + struct ov13858 *ov13858 = to_ov13858(sd); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + fh->pad, ++#else ++ fh->state, ++#endif + 0); + + mutex_lock(&ov13858->mutex); +@@ -1516,7 +1520,11 @@ static const struct v4l2_ctrl_ops ov13858_ctrl_ops = { + }; + + static int ov13858_enum_mbus_code(struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, ++#else ++ struct v4l2_subdev_state *sd_state, ++#endif + struct v4l2_subdev_mbus_code_enum *code) + { + /* Only one bayer order(GRBG) is supported */ +@@ -1529,7 +1537,11 @@ static int ov13858_enum_mbus_code(struct v4l2_subdev *sd, + } + + static int ov13858_enum_frame_size(struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, ++#else ++ struct v4l2_subdev_state *sd_state, ++#endif + struct v4l2_subdev_frame_size_enum *fse) + { + if (fse->index >= ARRAY_SIZE(supported_modes)) +@@ -1556,14 +1568,22 @@ static void ov13858_update_pad_format(const struct ov13858_mode *mode, + } + + static int ov13858_do_get_pad_format(struct ov13858 *ov13858, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, ++#else ++ struct v4l2_subdev_state *sd_state, ++#endif + struct v4l2_subdev_format *fmt) + { + struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev *sd = &ov13858->sd; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); ++#else ++ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); ++#endif + fmt->format = *framefmt; + } else { + ov13858_update_pad_format(ov13858->cur_mode, fmt); +@@ -1573,14 +1593,22 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858, + } + + static int ov13858_get_pad_format(struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, ++#else ++ struct v4l2_subdev_state *sd_state, ++#endif + struct v4l2_subdev_format *fmt) + { + struct ov13858 *ov13858 = to_ov13858(sd); + int ret; + + mutex_lock(&ov13858->mutex); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + ret = ov13858_do_get_pad_format(ov13858, cfg, fmt); ++#else ++ ret = ov13858_do_get_pad_format(ov13858, sd_state, fmt); ++#endif + mutex_unlock(&ov13858->mutex); + + return ret; +@@ -1588,7 +1616,11 @@ static int ov13858_get_pad_format(struct v4l2_subdev *sd, + + static int + ov13858_set_pad_format(struct v4l2_subdev *sd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, ++#else ++ struct v4l2_subdev_state *sd_state, ++#endif + struct v4l2_subdev_format *fmt) + { + struct ov13858 *ov13858 = to_ov13858(sd); +@@ -1612,7 +1644,11 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, + fmt->format.width, fmt->format.height); + ov13858_update_pad_format(mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); ++#else ++ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); ++#endif + *framefmt = fmt->format; + } else { + ov13858->cur_mode = mode; +@@ -1944,8 +1980,7 @@ static void ov13858_free_controls(struct ov13858 *ov13858) + mutex_destroy(&ov13858->mutex); + } + +-static int ov13858_probe(struct i2c_client *client, +- const struct i2c_device_id *devid) ++static int ov13858_probe(struct i2c_client *client) + { + struct ov13858 *ov13858; + int ret; +@@ -1990,7 +2025,11 @@ static int ov13858_probe(struct i2c_client *client, + goto error_handler_free; + } + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + ret = v4l2_async_register_subdev_sensor_common(&ov13858->sd); ++#else ++ ret = v4l2_async_register_subdev_sensor(&ov13858->sd); ++#endif + if (ret < 0) + goto error_media_entity; + +@@ -2060,7 +2099,11 @@ static struct i2c_driver ov13858_i2c_driver = { + .pm = &ov13858_pm_ops, + .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), + }, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) ++ .probe_new = ov13858_probe, ++#else + .probe = ov13858_probe, ++#endif + .remove = ov13858_remove, + .id_table = ov13858_id_table, + }; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/fix-ftbfs-v5.19.patch ipu6-drivers-0~git202310180730.3f813580/debian/patches/fix-ftbfs-v5.19.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/fix-ftbfs-v5.19.patch 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/fix-ftbfs-v5.19.patch 2023-09-01 17:51:53.000000000 +0800 @@ -1,10 +1,16 @@ -Description: Fix ftbfs against v5.19 kernels without any prior ipu6 patches -Author: Dimitri John Ledkov +From: Dimitri John Ledkov +Date: Fri, 1 Sep 2023 16:42:30 +0800 +Subject: Fix ftbfs against v5.19 kernels without any prior ipu6 patches +--- + drivers/media/pci/intel/ipu6/Makefile | 1 + + 1 file changed, 1 insertion(+) ---- ipu6-drivers-0~git202211220708.278b7e3d.orig/drivers/media/pci/intel/ipu6/Makefile -+++ ipu6-drivers-0~git202211220708.278b7e3d/drivers/media/pci/intel/ipu6/Makefile -@@ -55,3 +55,4 @@ obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel +diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile +index 69783b2..50ed86c 100644 +--- a/drivers/media/pci/intel/ipu6/Makefile ++++ b/drivers/media/pci/intel/ipu6/Makefile +@@ -55,3 +55,4 @@ obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-psys.o ccflags-y += -I$(src)/.. ccflags-y += -I$(src) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/series ipu6-drivers-0~git202310180730.3f813580/debian/patches/series --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/patches/series 2023-07-28 18:36:03.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/patches/series 2023-09-01 17:51:53.000000000 +0800 @@ -4,7 +4,7 @@ 0005-ivsc-import-headers.patch 0006-ivsc-load-symbols-by-kprobe.patch fix-ftbfs-v5.19.patch -0007-Don-t-rename-the-already-registered-PCI-device.patch -0008-media-hm2170-Support-ver-D-silicon.patch -0009-cio2-bridge-Fix-compilation-with-kernel-6.3.patch -0010-linux-6.5-get_user_pages-dropped-vmas.patch +0007-dkms-add-CONFIG_VIDEO_V4L2_I2C-to-BUILD_EXCLUSIVE_CO.patch +0009-ipu6-Fix-compilation-with-kernels-6.6.0.patch +0010-ipu6-Fix-sensor-driver-compilation-with-kernels-6.6..patch +0011-UBUNTU-SAUCE-i2c-compile-omitted-sensor-drivers.patch diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/debian/rules ipu6-drivers-0~git202310180730.3f813580/debian/rules --- ipu6-drivers-0~git202302081010.7fdfb5eb/debian/rules 2023-05-01 22:13:43.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/debian/rules 2023-09-01 17:51:53.000000000 +0800 @@ -11,7 +11,7 @@ $(empty) %: - dh $@ --with dkms + dh $@ --with dkms,modaliases override_dh_auto_configure: override_dh_auto_build: diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/dkms.conf ipu6-drivers-0~git202310180730.3f813580/dkms.conf --- ipu6-drivers-0~git202302081010.7fdfb5eb/dkms.conf 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/dkms.conf 2023-10-18 15:30:24.000000000 +0800 @@ -70,8 +70,16 @@ BUILT_MODULE_LOCATION[18]="drivers/media/i2c" DEST_MODULE_LOCATION[18]="/updates" -BUILT_MODULE_NAME[19]="hi556" +BUILT_MODULE_NAME[19]="hm2172" BUILT_MODULE_LOCATION[19]="drivers/media/i2c" DEST_MODULE_LOCATION[19]="/updates" +BUILT_MODULE_NAME[20]="hi556" +BUILT_MODULE_LOCATION[20]="drivers/media/i2c" +DEST_MODULE_LOCATION[20]="/updates" + +BUILT_MODULE_NAME[21]="ov02e10" +BUILT_MODULE_LOCATION[21]="drivers/media/i2c" +DEST_MODULE_LOCATION[21]="/updates" + AUTOINSTALL="yes" diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/gc5035.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/gc5035.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/gc5035.c 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/gc5035.c 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,2200 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Bitland Inc. +// Copyright 2020 Google LLC.. +// Copyright (c) 2022 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* External clock frequency supported by the driver */ +#define GC5035_MCLK_RATE 24000000UL +/* Number of lanes supported by this driver */ +#define GC5035_DATA_LANES 2 +/* Bits per sample of sensor output */ +#define GC5035_BITS_PER_SAMPLE 10 + +#define MIPI_FREQ 438000000LL + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC5035_PIXEL_RATE (MIPI_FREQ * 2LL * 2LL / 10) + +/* System registers (accessible regardless of the page. */ + +/* Chip ID */ +#define GC5035_REG_CHIP_ID_H 0xf0 +#define GC5035_REG_CHIP_ID_L 0xf1 +#define GC5035_CHIP_ID 0x5035 +#define GC5035_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) + +/* Register page selection register */ +#define GC5035_PAGE_REG 0xfe + +/* Page 0 registers */ + +/* Exposure control */ +#define GC5035_REG_EXPOSURE_H 0x03 +#define GC5035_REG_EXPOSURE_L 0x04 +#define GC5035_EXPOSURE_H_MASK 0x3f +#define GC5035_EXPOSURE_MIN 4 +#define GC5035_EXPOSURE_STEP 1 + +/* Analog gain control */ +#define GC5035_REG_ANALOG_GAIN 0xb6 +#define GC5035_ANALOG_GAIN_MIN 256 +#define GC5035_ANALOG_GAIN_MAX (16 * GC5035_ANALOG_GAIN_MIN) +#define GC5035_ANALOG_GAIN_STEP 1 +#define GC5035_ANALOG_GAIN_DEFAULT GC5035_ANALOG_GAIN_MIN + +/* Digital gain control */ +#define GC5035_REG_DIGI_GAIN_H 0xb1 +#define GC5035_REG_DIGI_GAIN_L 0xb2 +#define GC5035_DGAIN_H_MASK 0x0f +#define GC5035_DGAIN_L_MASK 0xfc +#define GC5035_DGAIN_L_SHIFT 2 +#define GC5035_DIGI_GAIN_MIN 0 +#define GC5035_DIGI_GAIN_MAX 1023 +#define GC5035_DIGI_GAIN_STEP 1 +#define GC5035_DIGI_GAIN_DEFAULT GC5035_DIGI_GAIN_MAX + +/* Vblank control */ +#define GC5035_REG_VTS_H 0x41 +#define GC5035_REG_VTS_L 0x42 +#define GC5035_VTS_H_MASK 0x3f +#define GC5035_VTS_MAX 16383 +#define GC5035_EXPOSURE_MARGIN 16 + +#define GC5035_REG_CTRL_MODE 0x3e +#define GC5035_MODE_SW_STANDBY 0x01 +#define GC5035_MODE_STREAMING 0x91 + +/* Page 1 registers */ + +/* Test pattern control */ +#define GC5035_REG_TEST_PATTERN 0x8c +#define GC5035_TEST_PATTERN_ENABLE 0x11 +#define GC5035_TEST_PATTERN_DISABLE 0x10 + +/* Page 2 registers */ + +/* OTP access registers */ +#define GC5035_REG_OTP_MODE 0xf3 +#define GC5035_OTP_PRE_READ 0x20 +#define GC5035_OTP_READ_MODE 0x12 +#define GC5035_OTP_READ_DONE 0x00 +#define GC5035_REG_OTP_DATA 0x6c +#define GC5035_REG_OTP_ACCESS_ADDR_H 0x69 +#define GC5035_REG_OTP_ACCESS_ADDR_L 0x6a +#define GC5035_OTP_ACCESS_ADDR_H_MASK 0x1f +#define GC5035_OTP_ADDR_MASK 0x1fff +#define GC5035_OTP_ADDR_SHIFT 3 +#define GC5035_REG_DD_TOTALNUM_H 0x01 +#define GC5035_REG_DD_TOTALNUM_L 0x02 +#define GC5035_DD_TOTALNUM_H_MASK 0x07 +#define GC5035_REG_DD_LOAD_STATUS 0x06 +#define GC5035_OTP_BIT_LOAD BIT(0) + +/* OTP-related definitions */ + +#define GC5035_OTP_ID_SIZE 9 +#define GC5035_OTP_ID_DATA_OFFSET 0x0020 +#define GC5035_OTP_DATA_LENGTH 1024 + +/* OTP DPC parameters */ +#define GC5035_OTP_DPC_FLAG_OFFSET 0x0068 +#define GC5035_OTP_DPC_FLAG_MASK 0x03 +#define GC5035_OTP_FLAG_EMPTY 0x00 +#define GC5035_OTP_FLAG_VALID 0x01 +#define GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET 0x0070 +#define GC5035_OTP_DPC_ERROR_NUMBER_OFFSET 0x0078 + +/* OTP register parameters */ +#define GC5035_OTP_REG_FLAG_OFFSET 0x0880 +#define GC5035_OTP_REG_DATA_OFFSET 0x0888 +#define GC5035_OTP_REG_ADDR_OFFSET 1 +#define GC5035_OTP_REG_VAL_OFFSET 2 +#define GC5035_OTP_PAGE_FLAG_OFFSET 3 +#define GC5035_OTP_PER_PAGE_SIZE 4 +#define GC5035_OTP_REG_PAGE_MASK 0x07 +#define GC5035_OTP_REG_MAX_GROUP 5 +#define GC5035_OTP_REG_BYTE_PER_GROUP 5 +#define GC5035_OTP_REG_PER_GROUP 2 +#define GC5035_OTP_REG_BYTE_PER_REG 2 +#define GC5035_OTP_REG_DATA_SIZE 25 +#define GC5035_OTP_REG_SIZE 10 + +#define GC5035_DD_DELAY_US (10 * 1000) +#define GC5035_DD_TIMEOUT_US (100 * 1000) + +/* The clock source index in INT3472 CLDB */ +#define INT3472_CLDB_CLKSRC_INDEX 14 + +/* + * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 + * This _DSM GUID calls CLKC and CLKF. + */ +static const guid_t clock_ctrl_guid = + GUID_INIT(0x82c0d13a, 0x78c5, 0x4244, + 0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11); + +static const char * const gc5035_supplies[] = { + /* + * Requested separately due to power sequencing needs: + * "iovdd", * Power supply for I/O circuits * + */ + "dvdd12", /* Digital core power */ + "avdd21", /* Analog power */ +}; + +struct gc5035_regval { + u8 addr; + u8 val; +}; + +struct gc5035_reg { + u8 page; + struct gc5035_regval regval; +}; + +struct gc5035_otp_regs { + unsigned int num_regs; + struct gc5035_reg regs[GC5035_OTP_REG_SIZE]; +}; + +struct gc5035_dpc { + bool valid; + unsigned int total_num; +}; + +struct gc5035_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct gc5035_regval *reg_list; + size_t num_regs; +}; + + +struct gc5035_power_ctrl { + /* Control Logic ACPI device */ + struct acpi_device *ctrl_logic; + /* GPIO for reset */ + struct gpio_desc *reset_gpio; + /* GPIO for power enable */ + struct gpio_desc *pwren_gpio; + /* GPIO for privacy LED */ + struct gpio_desc *pled_gpio; + + int status; + /* Clock source index */ + u8 clk_source_index; +}; + +struct gc5035 { + struct i2c_client *client; + struct clk *mclk; + unsigned long mclk_rate; + //struct gpio_desc *resetb_gpio; + ///struct gpio_desc *pwdn_gpio; + struct regulator *iovdd_supply; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc5035_supplies)]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + + u32 Dgain_ratio; + bool otp_read; + u8 otp_id[GC5035_OTP_ID_SIZE]; + struct gc5035_dpc dpc; + struct gc5035_otp_regs otp_regs; + + /* + * Serialize control access, get/set format, get selection + * and start streaming. + */ + struct mutex mutex; + struct gc5035_power_ctrl power; + bool streaming; + const struct gc5035_mode *cur_mode; +}; + +static inline struct gc5035 *to_gc5035(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc5035, subdev); +} + +static const struct gc5035_regval gc5035_otp_init_regs[] = { + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xf7, 0x01}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0xfa, 0x10}, + {0xf5, 0xe9}, + {0xfe, 0x02}, + {0x67, 0xc0}, + {0x59, 0x3f}, + {0x55, 0x84}, + {0x65, 0x80}, + {0x66, 0x03}, + {0xfe, 0x00}, +}; + +static const struct gc5035_regval gc5035_otp_exit_regs[] = { + {0xfe, 0x02}, + {0x67, 0x00}, + {0xfe, 0x00}, + {0xfa, 0x00}, +}; + +static const struct gc5035_regval gc5035_dd_auto_load_regs[] = { + {0xfe, 0x02}, + {0xbe, 0x00}, + {0xa9, 0x01}, + {0x09, 0x33}, +}; + +static const struct gc5035_regval gc5035_otp_dd_regs[] = { + {0x03, 0x00}, + {0x04, 0x80}, + {0x95, 0x0a}, + {0x96, 0x30}, + {0x97, 0x0a}, + {0x98, 0x32}, + {0x99, 0x07}, + {0x9a, 0xa9}, + {0xf3, 0x80}, +}; + +static const struct gc5035_regval gc5035_otp_dd_enable_regs[] = { + {0xbe, 0x01}, + {0x09, 0x00}, + {0xfe, 0x01}, + {0x80, 0x02}, + {0xfe, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_global_regs[] = { + /*init*/ + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x11, 0x02}, + {0x17, 0x80}, + {0x19, 0x05}, + {0xfe, 0x02}, + {0x30, 0x03}, + {0x31, 0x03}, + {0xfe, 0x00}, + {0xd9, 0xc0}, + {0x1b, 0x20}, + {0x21, 0x48}, + {0x28, 0x22}, + {0x29, 0x58}, + {0x44, 0x20}, + {0x4b, 0x10}, + {0x4e, 0x1a}, + {0x50, 0x11}, + {0x52, 0x33}, + {0x53, 0x44}, + {0x55, 0x10}, + {0x5b, 0x11}, + {0xc5, 0x02}, + {0x8c, 0x1a}, + {0xfe, 0x02}, + {0x33, 0x05}, + {0x32, 0x38}, + {0xfe, 0x00}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x16, 0x0c}, + {0x1a, 0x1a}, + {0x1f, 0x11}, + {0x20, 0x10}, + {0x46, 0x83}, + {0x4a, 0x04}, + {0x54, 0x02}, + {0x62, 0x00}, + {0x72, 0x8f}, + {0x73, 0x89}, + {0x7a, 0x05}, + {0x7d, 0xcc}, + {0x90, 0x00}, + {0xce, 0x18}, + {0xd0, 0xb2}, + {0xd2, 0x40}, + {0xe6, 0xe0}, + {0xfe, 0x02}, + {0x12, 0x01}, + {0x13, 0x01}, + {0x14, 0x01}, + {0x15, 0x02}, + {0x22, 0x7c}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* Gain */ + {0xfe, 0x00}, + {0xb0, 0x6e}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb3, 0x00}, + {0xb4, 0x00}, + {0xb6, 0x00}, + + /* ISP */ + {0xfe, 0x01}, + {0x53, 0x00}, + {0x89, 0x03}, + {0x60, 0x40}, + + /* BLK */ + {0xfe, 0x01}, + {0x42, 0x21}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + {0x55, 0x00}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x41, 0x28}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0x3c}, + {0x44, 0x08}, + {0x48, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x07}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x03, 0xb7}, + {0x15, 0x14}, + {0x18, 0x0f}, + {0x21, 0x22}, + {0x22, 0x06}, + {0x23, 0x48}, + {0x24, 0x12}, + {0x25, 0x28}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2a, 0x58}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + + {0xfe, 0x00}, + {0x3e, 0x01}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_2592x1944_regs[] = { + /* System */ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x58}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + /*Analog & CISCTL*/ + {0x03, 0x03},//0x06 + {0x04, 0xd8}, + {0x41, 0x07},//08 + {0x42, 0xd8},//58 + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x18}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x11, 0x02}, + {0x17, 0x80}, + {0x19, 0x05}, + {0xfe, 0x02}, + {0x30, 0x03}, + {0x31, 0x03}, + {0xfe, 0x00}, + {0xd9, 0xc0}, + {0x1b, 0x20}, + {0x21, 0x40}, + {0x28, 0x22}, + {0x29, 0x56}, + {0x44, 0x20}, + {0x4b, 0x10}, + {0x4e, 0x1a}, + {0x50, 0x11}, + {0x52, 0x33}, + {0x53, 0x44}, + {0x55, 0x10}, + {0x5b, 0x11}, + {0xc5, 0x02}, + {0x8c, 0x1a}, + {0xfe, 0x02}, + {0x33, 0x05}, + {0x32, 0x38}, + {0xfe, 0x00}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x16, 0x0c}, + {0x1a, 0x1a}, + {0x1f, 0x11}, + {0x20, 0x10}, + {0x46, 0x83}, + {0x4a, 0x04}, + {0x54, 0x02}, + {0x62, 0x00}, + {0x72, 0x8f}, + {0x73, 0x89}, + {0x7a, 0x05}, + {0x7d, 0xcc}, + {0x90, 0x00}, + {0xce, 0x18}, + {0xd0, 0xb2}, + {0xd2, 0x40}, + {0xe6, 0xe0}, + {0xfe, 0x02}, + {0x12, 0x01}, + {0x13, 0x01}, + {0x14, 0x01}, + {0x15, 0x02}, + {0x22, 0x7c}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + /*GAIN*/ + {0xfe, 0x00}, + {0xb0, 0x6e}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb3, 0x00}, + {0xb4, 0x00}, + {0xb6, 0x00}, + /*ISP*/ + {0xfe, 0x01}, + {0x53, 0x00}, + {0x89, 0x03}, + {0x60, 0x40}, + /*BLK*/ + {0xfe, 0x01}, + {0x42, 0x21}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + {0x55, 0x00}, + /*anti_blooming*/ + {0xfe, 0x01}, + {0x41, 0x28}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0x3c}, + {0x44, 0x08}, + {0x48, 0x02}, + /*CROP*/ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x08}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + /*MIPI*/ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x03, 0xb7}, + {0x15, 0x14}, + {0x18, 0x0f}, + {0x21, 0x22}, + {0x22, 0x06}, + {0x23, 0x48}, + {0x24, 0x12}, + {0x25, 0x28}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2a, 0x58}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + {0xfe, 0x00}, + {0x3e, 0x01}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 1296 + * grabwindow_height 972 + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_1296x972_regs[] = { + /*NULL*/ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe4}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x12}, + {0xfa, 0x01}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x20}, + {0xfe, 0x03}, + {0x01, 0x87}, + {0xf7, 0x11}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x21, 0x60}, + {0x29, 0x30}, + {0x44, 0x18}, + {0x4e, 0x20}, + {0x8c, 0x20}, + {0x91, 0x15}, + {0x92, 0x3a}, + {0x93, 0x20}, + {0x95, 0x45}, + {0x96, 0x35}, + {0xd5, 0xf0}, + {0x97, 0x20}, + {0x1f, 0x19}, + {0xce, 0x18}, + {0xd0, 0xb3}, + {0xfe, 0x02}, + {0x14, 0x02}, + {0x15, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* BLK */ + {0xfe, 0x01}, + {0x49, 0x00}, + {0x4a, 0x01}, + {0x4b, 0xf8}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x4e, 0x06}, + {0x44, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x04}, + {0x93, 0x00}, + {0x94, 0x03}, + {0x95, 0x03}, + {0x96, 0xcc}, + {0x97, 0x05}, + {0x98, 0x10}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x58}, + {0x22, 0x03}, + {0x26, 0x06}, + {0x29, 0x03}, + {0x2b, 0x06}, + {0xfe, 0x01}, + {0x8c, 0x10}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * linelength 672{0x2a0) + * framelength 2232{0x8b8) + * grabwindow_width 1280 + * grabwindow_height 720 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_1280x720_regs[] = { + /* System */ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe4}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x12}, + {0xfa, 0x01}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x20}, + {0xfe, 0x03}, + {0x01, 0x87}, + {0xf7, 0x11}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x21, 0x60}, + {0x29, 0x30}, + {0x44, 0x18}, + {0x4e, 0x20}, + {0x8c, 0x20}, + {0x91, 0x15}, + {0x92, 0x3a}, + {0x93, 0x20}, + {0x95, 0x45}, + {0x96, 0x35}, + {0xd5, 0xf0}, + {0x97, 0x20}, + {0x1f, 0x19}, + {0xce, 0x18}, + {0xd0, 0xb3}, + {0xfe, 0x02}, + {0x14, 0x02}, + {0x15, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* BLK */ + {0xfe, 0x01}, + {0x49, 0x00}, + {0x4a, 0x01}, + {0x4b, 0xf8}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x4e, 0x06}, + {0x44, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x0a}, + {0x93, 0x00}, + {0x94, 0x0b}, + {0x95, 0x02}, + {0x96, 0xd0}, + {0x97, 0x05}, + {0x98, 0x00}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x58}, + {0x22, 0x03}, + {0x26, 0x06}, + {0x29, 0x03}, + {0x2b, 0x06}, + {0xfe, 0x01}, + {0x8c, 0x10}, + {0xfe, 0x00}, + {0x3e, 0x91}, +}; + +static const struct gc5035_mode gc5035_modes[] = { + { + .width = 2592, + .height = 1944, + .max_fps = 30, + .exp_def = 0x258, + .hts_def = 2920, + .vts_def = 2008, + .reg_list = gc5035_2592x1944_regs, + .num_regs = ARRAY_SIZE(gc5035_2592x1944_regs), + }, + { + .width = 1296, + .height = 972, + .max_fps = 30, + .exp_def = 0x258, + .hts_def = 1460, + .vts_def = 2008, + .reg_list = gc5035_1296x972_regs, + .num_regs = ARRAY_SIZE(gc5035_1296x972_regs), + }, + { + .width = 1280, + .height = 720, + .max_fps = 60, + .exp_def = 0x258, + .hts_def = 1896, + .vts_def = 1536, + .reg_list = gc5035_1280x720_regs, + .num_regs = ARRAY_SIZE(gc5035_1280x720_regs), + }, +}; + +static const char * const gc5035_test_pattern_menu[] = { + "Disabled", + "Color Bar", +}; + +static const s64 gc5035_link_freqs[] = { + 438000000, +}; + +static u64 __maybe_unused gc5035_link_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = gc5035_link_freqs[f_index] * 2 * GC5035_DATA_LANES; + + do_div(pixel_rate, GC5035_BITS_PER_SAMPLE); + + return pixel_rate; +} + +static struct gpio_desc* gc5035_get_gpio(struct gc5035 *gc5035, + const char* name) +{ + struct device *dev = &gc5035->client->dev; + struct gpio_desc* gpio; + int ret; + + gpio = devm_gpiod_get(dev, name, GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(gpio); + if (ret < 0) { + gpio = NULL; + dev_warn(dev, "failed to get %s gpio: %d\n", name, ret); + } + + return gpio; +} + +static void gc5035_init_power_ctrl(struct gc5035 *gc5035) +{ + struct gc5035_power_ctrl* power = &gc5035->power; + acpi_handle handle = ACPI_HANDLE(&gc5035->client->dev); + struct acpi_handle_list dep_devices; + acpi_status status; + int i = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + power->ctrl_logic = NULL; + if (!acpi_has_method(handle, "_DEP")) + return; + + /* Call _DEP method of OV13B */ + status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); + return; + } + + /* Find INT3472 in _DEP */ + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device *dep_device = NULL; + const char* dep_hid = NULL; + + if (dep_devices.handles[i]) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) + acpi_bus_get_device(dep_devices.handles[i], &dep_device); + #else + dep_device = acpi_fetch_acpi_dev(dep_devices.handles[i]); + #endif + } + if (dep_device) + dep_hid = acpi_device_hid(dep_device); + if (dep_hid && strcmp("INT3472", dep_hid) == 0) { + power->ctrl_logic = dep_device; + break; + } + } + + /* If we got INT3472 acpi device, read which clock source we'll use */ + if (power->ctrl_logic == NULL) + return; + status = acpi_evaluate_object(power->ctrl_logic->handle, + "CLDB", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(&gc5035->client->dev, "Read INT3472 CLDB failed"); + return; + } + + obj = buffer.pointer; + if (!obj) + dev_warn(&gc5035->client->dev, "INT3472 CLDB return NULL"); + if (obj->type != ACPI_TYPE_BUFFER) { + acpi_handle_err(power->ctrl_logic->handle, + "CLDB object is not an ACPI buffer\n"); + kfree(obj); + return; + } + if (obj->buffer.length < INT3472_CLDB_CLKSRC_INDEX + 1) { + acpi_handle_err(power->ctrl_logic->handle, + "The CLDB buffer size is wrong\n"); + kfree(obj); + return; + } + + /* Get the clock source index */ + gc5035->power.clk_source_index = + obj->buffer.pointer[INT3472_CLDB_CLKSRC_INDEX]; + kfree(obj); + + /* Get gpios mapped by INT3472 driver */ + power->reset_gpio = gc5035_get_gpio(gc5035, "reset"); + power->pwren_gpio = gc5035_get_gpio(gc5035, "pwren"); + power->pled_gpio = gc5035_get_gpio(gc5035, "pled"); + power->status = 0; +} + +static void gc5035_set_power(struct gc5035 *gc5035, int on) +{ + struct gc5035_power_ctrl* power = &gc5035->power; + + on = (on ? 1 : 0); + if (on == power->status) + return; + + /* First, set reset pin as low */ + if (power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 0); + msleep(5); + } + + /* Use _DSM of INT3472 to enable clock */ + if (power->ctrl_logic) { + u8 clock_args[] = { power->clk_source_index, on, 0x01,}; + union acpi_object clock_ctrl_args = { + .buffer = { + .type = ACPI_TYPE_BUFFER, + .length = 3, + .pointer = clock_args, + }, + }; + acpi_evaluate_dsm(power->ctrl_logic->handle, + &clock_ctrl_guid, 0x00, 0x01, + &clock_ctrl_args); + } + + /* Set power pin and privacy LED pin */ + if (power->pwren_gpio) + gpiod_set_value_cansleep(power->pwren_gpio, on); + if (power->pled_gpio) + gpiod_set_value_cansleep(power->pled_gpio, on); + + /* If we need to power on, set reset pin to high at last */ + if (on && power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 1); + msleep(5); + } + power->status = on; +} + +static int gc5035_write_reg(struct gc5035 *gc5035, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(gc5035->client, reg, val); +} + +static int gc5035_write_array(struct gc5035 *gc5035, + const struct gc5035_regval *regs, + size_t num_regs) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_regs; i++) { + ret = gc5035_write_reg(gc5035, regs[i].addr, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +static int gc5035_read_reg(struct gc5035 *gc5035, u8 reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(gc5035->client, reg); + if (ret < 0) + return ret; + + *val = (unsigned char)ret; + + return 0; +} + +static int gc5035_otp_read_data(struct gc5035 *gc5035, u16 bit_addr, u8 *data, + size_t length) +{ + size_t i; + int ret; + + if (WARN_ON(bit_addr % 8)) + return -EINVAL; + + if (WARN_ON(bit_addr / 8 + length > GC5035_OTP_DATA_LENGTH)) + return -EINVAL; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 2); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_H, + (bit_addr >> 8) & + GC5035_OTP_ACCESS_ADDR_H_MASK); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_L, + bit_addr & 0xff); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, + GC5035_OTP_PRE_READ); + if (ret) + goto out_read_done; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, + GC5035_OTP_READ_MODE); + if (ret) + goto out_read_done; + + for (i = 0; i < length; i++) { + ret = gc5035_read_reg(gc5035, GC5035_REG_OTP_DATA, &data[i]); + if (ret) + goto out_read_done; + } + +out_read_done: + gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, GC5035_OTP_READ_DONE); + + return ret; +} + +static int gc5035_read_otp_regs(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; + u8 regs[GC5035_OTP_REG_DATA_SIZE] = {0}; + unsigned int i, j; + u8 flag; + int ret; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_FLAG_OFFSET, + &flag, 1); + if (ret) { + dev_err(dev, "failed to read otp reg flag\n"); + return ret; + } + + dev_dbg(dev, "register update flag = 0x%x\n", flag); + + gc5035->otp_regs.num_regs = 0; + if (flag != GC5035_OTP_FLAG_VALID) + return 0; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_DATA_OFFSET, + regs, sizeof(regs)); + if (ret) { + dev_err(dev, "failed to read otp reg data\n"); + return ret; + } + + for (i = 0; i < GC5035_OTP_REG_MAX_GROUP; i++) { + unsigned int base_group = i * GC5035_OTP_REG_BYTE_PER_GROUP; + + for (j = 0; j < GC5035_OTP_REG_PER_GROUP; j++) { + struct gc5035_reg *reg; + + if (!(regs[base_group] & + BIT((GC5035_OTP_PER_PAGE_SIZE * j + + GC5035_OTP_PAGE_FLAG_OFFSET)))) + continue; + + reg = &otp_regs->regs[otp_regs->num_regs++]; + reg->page = (regs[base_group] >> + (GC5035_OTP_PER_PAGE_SIZE * j)) & + GC5035_OTP_REG_PAGE_MASK; + reg->regval.addr = regs[base_group + j * + GC5035_OTP_REG_BYTE_PER_REG + + GC5035_OTP_REG_ADDR_OFFSET]; + reg->regval.val = regs[base_group + j * + GC5035_OTP_REG_BYTE_PER_REG + + GC5035_OTP_REG_VAL_OFFSET]; + } + } + + return 0; +} + +static int gc5035_read_dpc(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_dpc *dpc = &gc5035->dpc; + u8 dpc_flag = 0; + u8 error_number = 0; + u8 total_number = 0; + int ret; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_DPC_FLAG_OFFSET, + &dpc_flag, 1); + if (ret) { + dev_err(dev, "failed to read dpc flag\n"); + return ret; + } + + dev_dbg(dev, "dpc flag = 0x%x\n", dpc_flag); + + dpc->valid = false; + + switch (dpc_flag & GC5035_OTP_DPC_FLAG_MASK) { + case GC5035_OTP_FLAG_EMPTY: + dev_dbg(dev, "dpc info is empty!!\n"); + break; + + case GC5035_OTP_FLAG_VALID: + dev_dbg(dev, "dpc info is valid!\n"); + ret = gc5035_otp_read_data(gc5035, + GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET, + &total_number, 1); + if (ret) { + dev_err(dev, "failed to read dpc total number\n"); + return ret; + } + + ret = gc5035_otp_read_data(gc5035, + GC5035_OTP_DPC_ERROR_NUMBER_OFFSET, + &error_number, 1); + if (ret) { + dev_err(dev, "failed to read dpc error number\n"); + return ret; + } + + dpc->total_num = total_number + error_number; + dpc->valid = true; + dev_dbg(dev, "total_num = %d\n", dpc->total_num); + break; + + default: + break; + } + + return ret; +} + +static int gc5035_otp_read_sensor_info(struct gc5035 *gc5035) +{ + int ret; + + ret = gc5035_read_dpc(gc5035); + if (ret) + return ret; + + return gc5035_read_otp_regs(gc5035); +} + +static int gc5035_check_dd_load_status(struct gc5035 *gc5035) +{ + u8 status; + int ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_DD_LOAD_STATUS, &status); + if (ret) + return ret; + + if (status & GC5035_OTP_BIT_LOAD) + return status; + else + return 0; +} + +static int gc5035_otp_update_dd(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_dpc *dpc = &gc5035->dpc; + int val, ret; + + if (!dpc->valid) { + dev_dbg(dev, "DPC table invalid, not updating DD.\n"); + return 0; + } + + dev_dbg(dev, "DD auto load start\n"); + + ret = gc5035_write_array(gc5035, gc5035_dd_auto_load_regs, + ARRAY_SIZE(gc5035_dd_auto_load_regs)); + if (ret) { + dev_err(dev, "failed to write dd auto load reg\n"); + return ret; + } + + ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_H, + (dpc->total_num >> 8) & + GC5035_DD_TOTALNUM_H_MASK); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_L, + dpc->total_num & 0xff); + if (ret) + return ret; + + ret = gc5035_write_array(gc5035, gc5035_otp_dd_regs, + ARRAY_SIZE(gc5035_otp_dd_regs)); + if (ret) + return ret; + + /* Wait for DD to finish loading automatically */ + ret = readx_poll_timeout(gc5035_check_dd_load_status, gc5035, + val, val <= 0, GC5035_DD_DELAY_US, + GC5035_DD_TIMEOUT_US); + if (ret < 0) { + dev_err(dev, "DD load timeout\n"); + return -EFAULT; + } + if (val < 0) { + dev_err(dev, "DD load failure\n"); + return val; + } + + ret = gc5035_write_array(gc5035, gc5035_otp_dd_enable_regs, + ARRAY_SIZE(gc5035_otp_dd_enable_regs)); + if (ret) + return ret; + + return 0; +} + +static int gc5035_otp_update_regs(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; + unsigned int i; + int ret; + + dev_dbg(dev, "reg count = %d\n", otp_regs->num_regs); + + for (i = 0; i < otp_regs->num_regs; i++) { + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, + otp_regs->regs[i].page); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, + otp_regs->regs[i].regval.addr, + otp_regs->regs[i].regval.val); + if (ret) + return ret; + } + + return 0; +} + +static int gc5035_otp_update(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + int ret; + + ret = gc5035_otp_update_dd(gc5035); + if (ret) { + dev_err(dev, "failed to update otp dd\n"); + return ret; + } + + ret = gc5035_otp_update_regs(gc5035); + if (ret) { + dev_err(dev, "failed to update otp regs\n"); + return ret; + } + + return ret; +} + +static int gc5035_set_otp_config(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + u8 otp_id[GC5035_OTP_ID_SIZE]; + int ret; + + ret = gc5035_write_array(gc5035, gc5035_otp_init_regs, + ARRAY_SIZE(gc5035_otp_init_regs)); + if (ret) { + dev_err(dev, "failed to write otp init reg\n"); + return ret; + } + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_ID_DATA_OFFSET, + &otp_id[0], GC5035_OTP_ID_SIZE); + if (ret) { + dev_err(dev, "failed to read otp id\n"); + goto out_otp_exit; + } + + if (!gc5035->otp_read || memcmp(gc5035->otp_id, otp_id, sizeof(otp_id))) { + dev_dbg(dev, "reading OTP configuration\n"); + + memset(&gc5035->otp_regs, 0, sizeof(gc5035->otp_regs)); + memset(&gc5035->dpc, 0, sizeof(gc5035->dpc)); + + memcpy(gc5035->otp_id, otp_id, sizeof(gc5035->otp_id)); + + ret = gc5035_otp_read_sensor_info(gc5035); + if (ret < 0) { + dev_err(dev, "failed to read otp info\n"); + goto out_otp_exit; + } + + gc5035->otp_read = true; + } + + ret = gc5035_otp_update(gc5035); + if (ret < 0) + return ret; + +out_otp_exit: + ret = gc5035_write_array(gc5035, gc5035_otp_exit_regs, + ARRAY_SIZE(gc5035_otp_exit_regs)); + if (ret) { + dev_err(dev, "failed to write otp exit reg\n"); + return ret; + } + + return ret; +} + +static int gc5035_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode; + s64 h_blank, vblank_def; + + mode = v4l2_find_nearest_size(gc5035_modes, + ARRAY_SIZE(gc5035_modes), width, + height, fmt->format.width, + fmt->format.height); + + + //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + + mutex_lock(&gc5035->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + } else { + gc5035->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5035->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = round_up(mode->vts_def, 4) - mode->height; + __v4l2_ctrl_modify_range(gc5035->vblank, vblank_def, + GC5035_VTS_MAX - mode->height, + 1, vblank_def); + } + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode = gc5035->cur_mode; + + mutex_lock(&gc5035->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + //code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int gc5035_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(gc5035_modes)) + return -EINVAL; + + //if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = gc5035_modes[fse->index].width; + fse->max_width = gc5035_modes[fse->index].width; + fse->max_height = gc5035_modes[fse->index].height; + fse->min_height = gc5035_modes[fse->index].height; + + return 0; +} + +static int __gc5035_start_stream(struct gc5035 *gc5035) +{ + int ret; + + gc5035_set_power(gc5035, 1); + + ret = gc5035_write_array(gc5035, gc5035_global_regs, + ARRAY_SIZE(gc5035_global_regs)); + if (ret) + return ret; + + ret = gc5035_set_otp_config(gc5035); + if (ret) + return ret; + + ret = gc5035_write_array(gc5035, gc5035->cur_mode->reg_list, + gc5035->cur_mode->num_regs); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&gc5035->ctrl_handler); + if (ret) + return ret; + + gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, + GC5035_MODE_STREAMING); +} + +static void __gc5035_stop_stream(struct gc5035 *gc5035) +{ + int ret; + struct i2c_client *client = gc5035->client; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + dev_err(&client->dev, "failed to stop streaming!"); + + if(gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, + GC5035_MODE_SW_STANDBY)) + dev_err(&client->dev, "failed to stop streaming"); + gc5035_set_power(gc5035, 0); +} + +static int gc5035_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + struct i2c_client *client = gc5035->client; + int ret = 0; + + mutex_lock(&gc5035->mutex); + on = !!on; + if (on == gc5035->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5035_start_stream(gc5035); + if (ret) { + dev_err(&client->dev, "start stream failed\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5035_stop_stream(gc5035); + pm_runtime_put(&client->dev); + } + + gc5035->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5035->mutex); + + return ret; +} + +static int gc5035_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + int ret; + + if (gc5035->streaming) { + ret = __gc5035_start_stream(gc5035); + if (ret) + goto error; + } + + return 0; + +error: + __gc5035_stop_stream(gc5035); + gc5035->streaming = false; + + return ret; +} + +static int gc5035_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + if (gc5035->streaming) + __gc5035_stop_stream(gc5035); + + return 0; +} + +static int gc5035_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .width = 2592, + .height = 1944, + } + }; + + gc5035_set_fmt(subdev, sd_state, &fmt); + + return 0; +} + +static const struct dev_pm_ops gc5035_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(gc5035_runtime_suspend, + gc5035_runtime_resume, NULL) +}; + +static const struct v4l2_subdev_video_ops gc5035_video_ops = { + .s_stream = gc5035_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc5035_pad_ops = { + .init_cfg = gc5035_entity_init_cfg, + .enum_mbus_code = gc5035_enum_mbus_code, + .enum_frame_size = gc5035_enum_frame_sizes, + .get_fmt = gc5035_get_fmt, + .set_fmt = gc5035_set_fmt, +}; + +static const struct v4l2_subdev_ops gc5035_subdev_ops = { + .video = &gc5035_video_ops, + .pad = &gc5035_pad_ops, +}; + +static const struct media_entity_operations gc5035_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int gc5035_set_exposure(struct gc5035 *gc5035, u32 val) +{ + u32 caltime = 0; + int ret = 0; + + caltime = val / 2; + caltime = caltime * 2; + gc5035->Dgain_ratio = 256 * val / caltime; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_H, + (val >> 8) & GC5035_EXPOSURE_H_MASK); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_L, val & 0xff); +} + +static u32 GC5035_AGC_Param[17][2] = { + { 256, 0 }, + { 302, 1 }, + { 358, 2 }, + { 425, 3 }, + { 502, 8 }, + { 599, 9 }, + { 717, 10 }, + { 845, 11 }, + { 998, 12 }, + { 1203, 13 }, + { 1434, 14 }, + { 1710, 15 }, + { 1997, 16 }, + { 2355, 17 }, + { 2816, 18 }, + { 3318, 19 }, + { 3994, 20 }, +}; + +static int gc5035_set_analogue_gain(struct gc5035 *gc5035, u32 a_gain) +{ + int ret = 0, i = 0; + u32 temp_gain = 0; + + if (a_gain < 0x100) + a_gain = 0x100; + else if (a_gain > GC5035_ANALOG_GAIN_MAX) + a_gain = GC5035_ANALOG_GAIN_MAX; + for (i = 16; i >= 0; i--) { + if (a_gain >= GC5035_AGC_Param[i][0]) + break; + } + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + ret |= gc5035_write_reg(gc5035, + GC5035_REG_ANALOG_GAIN, GC5035_AGC_Param[i][1]); + temp_gain = a_gain; + temp_gain = temp_gain * gc5035->Dgain_ratio / GC5035_AGC_Param[i][0]; + + ret |= gc5035_write_reg(gc5035, + GC5035_REG_DIGI_GAIN_H, + (temp_gain >> 8) & 0x0f); + ret |= gc5035_write_reg(gc5035, + GC5035_REG_DIGI_GAIN_L, + temp_gain & 0xfc); + return ret; +} + +static int gc5035_set_vblank(struct gc5035 *gc5035, u32 val) +{ + int frame_length = 0; + int ret; + + frame_length = val + gc5035->cur_mode->height; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_VTS_H, + (frame_length >> 8) & GC5035_VTS_H_MASK); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_VTS_L, frame_length & 0xff); +} + +static int gc5035_enable_test_pattern(struct gc5035 *gc5035, u32 pattern) +{ + int ret; + + if (pattern) + pattern = GC5035_TEST_PATTERN_ENABLE; + else + pattern = GC5035_TEST_PATTERN_DISABLE; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 1); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_TEST_PATTERN, pattern); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); +} + +static int gc5035_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5035 *gc5035 = container_of(ctrl->handler, + struct gc5035, ctrl_handler); + struct i2c_client *client = gc5035->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5035->cur_mode->height + ctrl->val + - GC5035_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(gc5035->exposure, + gc5035->exposure->minimum, max, + gc5035->exposure->step, + gc5035->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = gc5035_set_exposure(gc5035, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc5035_set_analogue_gain(gc5035, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + break; + case V4L2_CID_VBLANK: + ret = gc5035_set_vblank(gc5035, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc5035_enable_test_pattern(gc5035, ctrl->val); + break; + default: + ret = -EINVAL; + break; + }; + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc5035_ctrl_ops = { + .s_ctrl = gc5035_set_ctrl, +}; + +static int gc5035_initialize_controls(struct gc5035 *gc5035) +{ + const struct gc5035_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + u64 exposure_max; + u32 h_blank, vblank_def; + int ret; + + handler = &gc5035->ctrl_handler; + mode = gc5035->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + + handler->lock = &gc5035->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, gc5035_link_freqs); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC5035_PIXEL_RATE, 1, GC5035_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc5035->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5035->hblank) + gc5035->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = round_up(mode->vts_def, 4) - mode->height; + gc5035->vblank = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5035_VTS_MAX - mode->height, + 4, vblank_def); + + exposure_max = mode->vts_def - GC5035_EXPOSURE_MARGIN; + gc5035->exposure = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5035_EXPOSURE_MIN, exposure_max, + GC5035_EXPOSURE_STEP, + mode->exp_def); + + v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + GC5035_ANALOG_GAIN_MIN, GC5035_ANALOG_GAIN_MAX, + GC5035_ANALOG_GAIN_STEP, GC5035_ANALOG_GAIN_DEFAULT); + + v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + GC5035_DIGI_GAIN_MIN, GC5035_DIGI_GAIN_MAX, + GC5035_DIGI_GAIN_STEP, GC5035_DIGI_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(handler, &gc5035_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc5035_test_pattern_menu) - 1, + 0, 0, gc5035_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&gc5035->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5035->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc5035_check_sensor_id(struct gc5035 *gc5035, + struct i2c_client *client) +{ + struct device *dev = &gc5035->client->dev; + u16 id; + u8 pid = 0; + u8 ver = 0; + int ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_H, &pid); + if (ret) + return ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_L, &ver); + if (ret) + return ret; + + id = GC5035_ID(pid, ver); + if (id != GC5035_CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x)\n", id); + return -EINVAL; + } + + dev_dbg(dev, "Detected GC%04x sensor\n", id); + + return 0; +} + +static int __maybe_unused gc5035_get_hwcfg(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned long link_freq_mask = 0; + unsigned int i, j; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENODEV; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) + goto out; + + dev_dbg(dev, "num of link freqs: %d", bus_cfg.nr_of_link_frequencies); + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined"); + goto out; + } + + for (i = 0; i < ARRAY_SIZE(gc5035_link_freqs); ++i) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (bus_cfg.link_frequencies[j] + == gc5035_link_freqs[i]) { + link_freq_mask |= BIT(i); + dev_dbg(dev, "Link frequency %lld supported\n", + gc5035_link_freqs[i]); + break; + } + } + } + if (!link_freq_mask) { + dev_err(dev, "No supported link frequencies found\n"); + ret = -EINVAL; + goto out; + } + +out: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return ret; +} + +static int gc5035_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc5035 *gc5035; + struct v4l2_subdev *sd; + int ret, i; + u32 freq = 192000000UL; + + gc5035 = devm_kzalloc(dev, sizeof(*gc5035), GFP_KERNEL); + if (!gc5035) + return -ENOMEM; + + gc5035->client = client; + gc5035_init_power_ctrl(gc5035); + gc5035_set_power(gc5035, 1); + + gc5035->cur_mode = &gc5035_modes[0]; + + ret = clk_set_rate(gc5035->mclk, freq); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set mclk rate\n"); + gc5035->mclk_rate = clk_get_rate(gc5035->mclk); + if (gc5035->mclk_rate != freq) + dev_warn(dev, "mclk rate set to %lu instead of requested %u\n", + gc5035->mclk_rate, freq); + gc5035->iovdd_supply = devm_regulator_get(dev, "iovdd"); + if (IS_ERR(gc5035->iovdd_supply)) + return dev_err_probe(dev, PTR_ERR(gc5035->iovdd_supply), + "Failed to get iovdd regulator\n"); + + for (i = 0; i < ARRAY_SIZE(gc5035_supplies); i++) + gc5035->supplies[i].supply = gc5035_supplies[i]; + ret = devm_regulator_bulk_get(&gc5035->client->dev, + ARRAY_SIZE(gc5035_supplies), + gc5035->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + mutex_init(&gc5035->mutex); + sd = &gc5035->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5035_subdev_ops); + ret = gc5035_initialize_controls(gc5035); + if (ret) { + dev_err_probe(dev, ret, "Failed to initialize controls\n"); + goto err_destroy_mutex; + } + ret = gc5035_runtime_resume(dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to power on\n"); + goto err_free_handler; + } + ret = gc5035_check_sensor_id(gc5035, client); + if (ret) { + dev_err_probe(dev, ret, "Sensor ID check failed\n"); + goto err_power_off; + } + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &gc5035_subdev_entity_ops; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + gc5035->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 1, &gc5035->pad); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to initialize pads\n"); + goto err_power_off; + } + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + gc5035_set_power(gc5035, 0); + + return 0; + +err_clean_entity: + media_entity_cleanup(&sd->entity); +err_power_off: + gc5035_runtime_suspend(dev); +err_free_handler: + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); + gc5035_set_power(gc5035, 0); +err_destroy_mutex: + mutex_destroy(&gc5035->mutex); + + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int gc5035_remove(struct i2c_client *client) +#else +static void gc5035_remove(struct i2c_client *client) +#endif +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); + mutex_destroy(&gc5035->mutex); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc5035_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; + #endif +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id gc5035_acpi_ids[] = { + {"GCTI5035"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, gc5035_acpi_ids); +#endif + +static const struct of_device_id gc5035_of_match[] = { + { .compatible = "galaxycore,gc5035" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5035_of_match); + +static struct i2c_driver gc5035_i2c_driver = { + .driver = { + .name = "gc5035", + .pm = &gc5035_pm_ops, + .acpi_match_table = ACPI_PTR(gc5035_acpi_ids), + .of_match_table = gc5035_of_match, + }, + .probe_new = gc5035_probe, + .remove = gc5035_remove, +}; +module_i2c_driver(gc5035_i2c_driver); + +MODULE_AUTHOR("Liang Wang "); +MODULE_AUTHOR("Hao He "); +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Tomasz Figa "); +MODULE_DESCRIPTION("GalaxyCore gc5035 sensor driver"); +MODULE_LICENSE("GPL v2"); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hi556.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hi556.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hi556.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hi556.c 2023-10-18 15:30:24.000000000 +0800 @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,14 @@ #include #if IS_ENABLED(CONFIG_INTEL_VSC) #include + +static const struct acpi_device_id cvfd_ids[] = { + { "INTC1059", 0 }, + { "INTC1095", 0 }, + { "INTC100A", 0 }, + { "INTC10CF", 0 }, + {} +}; #endif #define HI556_REG_VALUE_08BIT 1 @@ -490,10 +499,21 @@ struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + + /* GPIO for reset */ + struct gpio_desc *reset; + /* GPIO for Lattice handshake */ + struct gpio_desc *handshake; + /* regulator */ + struct regulator *avdd; + /* Clock provider */ + struct clk *img_clk; + #if IS_ENABLED(CONFIG_INTEL_VSC) + struct vsc_mipi_config conf; + struct vsc_camera_status status; struct v4l2_ctrl *privacy_status; #endif - /* Current mode */ const struct hi556_mode *cur_mode; @@ -505,6 +525,9 @@ /* True if the device has been identified */ bool identified; +#if IS_ENABLED(CONFIG_INTEL_VSC) + bool use_intel_vsc; +#endif }; static u64 to_pixel_rate(u32 f_index) @@ -745,7 +768,8 @@ hi556->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; #if IS_ENABLED(CONFIG_INTEL_VSC) hi556->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_PRIVACY, 0, 1, 1, 0); + V4L2_CID_PRIVACY, 0, 1, 1, + !(hi556->status.status)); #endif v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, @@ -808,7 +832,7 @@ #if IS_ENABLED(CONFIG_INTEL_VSC) static void hi556_vsc_privacy_callback(void *handle, - enum vsc_privacy_status status) + enum vsc_privacy_status status) { struct hi556 *hi556 = handle; @@ -821,21 +845,6 @@ struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); const struct hi556_reg_list *reg_list; int link_freq_index, ret; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - - conf.lane_num = HI556_DATA_LANES; - /* frequency unit 100k */ - conf.freq = HI556_LINK_FREQ_437MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, hi556_vsc_privacy_callback, - hi556, &status); - if (ret) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } - __v4l2_ctrl_s_ctrl(hi556->privacy_status, !(status.status)); -#endif ret = hi556_identify_module(hi556); if (ret) @@ -874,17 +883,10 @@ static void hi556_stop_streaming(struct hi556 *hi556) { struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_camera_status status; -#endif if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) dev_err(&client->dev, "failed to set stream"); -#if IS_ENABLED(CONFIG_INTEL_VSC) - if (vsc_release_camera_sensor(&status)) - dev_err(&client->dev, "Release VSC failed"); -#endif } static int hi556_set_stream(struct v4l2_subdev *sd, int enable) @@ -921,6 +923,81 @@ return ret; } +static int hi556_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hi556 *hi556 = to_hi556(sd); + int ret; + +#if IS_ENABLED(CONFIG_INTEL_VSC) + if (hi556->use_intel_vsc) { + ret = vsc_release_camera_sensor(&hi556->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Release VSC failed"); + + return ret; + } +#endif + gpiod_set_value_cansleep(hi556->reset, 1); + gpiod_set_value_cansleep(hi556->handshake, 0); + if (hi556->avdd) + ret = regulator_disable(hi556->avdd); + clk_disable_unprepare(hi556->img_clk); + + return ret; +} + +static int hi556_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hi556 *hi556 = to_hi556(sd); + int ret = 0; + +#if IS_ENABLED(CONFIG_INTEL_VSC) + if (hi556->use_intel_vsc) { + hi556->conf.lane_num = HI556_DATA_LANES; + /* frequency unit 100k */ + hi556->conf.freq = HI556_LINK_FREQ_437MHZ / 100000; + ret = vsc_acquire_camera_sensor(&hi556->conf, + hi556_vsc_privacy_callback, + hi556, &hi556->status); + if (ret == -EAGAIN) + return -EPROBE_DEFER; + if (ret) { + dev_err(dev, "Acquire VSC failed"); + return ret; + } + if (hi556->privacy_status) + __v4l2_ctrl_s_ctrl(hi556->privacy_status, + !(hi556->status.status)); + + return ret; + } +#endif + ret = clk_prepare_enable(hi556->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + if (hi556->avdd) { + ret = regulator_enable(hi556->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(hi556->img_clk); + return ret; + } + } + gpiod_set_value_cansleep(hi556->handshake, 1); + gpiod_set_value_cansleep(hi556->reset, 0); + + /* Lattice MIPI aggregator with some version FW needs longer delay + after handshake triggered. We set 25ms as a safe value and wait + for a stable version FW. */ + msleep_interruptible(25); + + return ret; +} + static int __maybe_unused hi556_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -960,7 +1037,11 @@ } static int hi556_set_format(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, +#else struct v4l2_subdev_state *sd_state, +#endif struct v4l2_subdev_format *fmt) { struct hi556 *hi556 = to_hi556(sd); @@ -975,7 +1056,11 @@ mutex_lock(&hi556->mutex); hi556_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#endif } else { hi556->cur_mode = mode; __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); @@ -1002,16 +1087,25 @@ } static int hi556_get_format(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, +#else struct v4l2_subdev_state *sd_state, +#endif struct v4l2_subdev_format *fmt) { struct hi556 *hi556 = to_hi556(sd); mutex_lock(&hi556->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + fmt->format = + *v4l2_subdev_get_try_format(&hi556->sd, cfg, fmt->pad); +#else fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, sd_state, fmt->pad); +#endif else hi556_assign_pad_format(hi556->cur_mode, &fmt->format); @@ -1021,7 +1115,11 @@ } static int hi556_enum_mbus_code(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, +#else struct v4l2_subdev_state *sd_state, +#endif struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -1033,7 +1131,11 @@ } static int hi556_enum_frame_size(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + struct v4l2_subdev_pad_config *cfg, +#else struct v4l2_subdev_state *sd_state, +#endif struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1055,8 +1157,13 @@ struct hi556 *hi556 = to_hi556(sd); mutex_lock(&hi556->mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + hi556_assign_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); +#else hi556_assign_pad_format(&supported_modes[0], v4l2_subdev_get_try_format(sd, fh->state, 0)); +#endif mutex_unlock(&hi556->mutex); return 0; @@ -1086,7 +1193,142 @@ .open = hi556_open, }; +static int hi556_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hi556 *hi556 = to_hi556(sd); + int ret; + +#if IS_ENABLED(CONFIG_INTEL_VSC) + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_handle_list dep_devices; + acpi_status status; + int i = 0; + + hi556->use_intel_vsc = false; + if (!acpi_has_method(handle, "_DEP")) + return false; + + status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); + return false; + } + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device *dep_device = NULL; + + if (dep_devices.handles[i]) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) + acpi_bus_get_device(dep_devices.handles[i], &dep_device); +#else + dep_device = + acpi_fetch_acpi_dev(dep_devices.handles[i]); +#endif + + if (dep_device && acpi_match_device_ids(dep_device, cvfd_ids) == 0) { + hi556->use_intel_vsc = true; + return 0; + } + } +#endif + hi556->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(hi556->reset)) + return dev_err_probe(dev, PTR_ERR(hi556->reset), + "failed to get reset gpio\n"); + + hi556->handshake = devm_gpiod_get_optional(dev, "handshake", + GPIOD_OUT_LOW); + if (IS_ERR(hi556->handshake)) + return dev_err_probe(dev, PTR_ERR(hi556->handshake), + "failed to get handshake gpio\n"); + + hi556->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hi556->img_clk)) + return dev_err_probe(dev, PTR_ERR(hi556->img_clk), + "failed to get imaging clock\n"); + + hi556->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(hi556->avdd)) { + ret = PTR_ERR(hi556->avdd); + hi556->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + return 0; +} + +static int __maybe_unused hi556_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret = 0; + unsigned int i, j; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EPROBE_DEFER; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != HI556_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int hi556_remove(struct i2c_client *client) +#else static void hi556_remove(struct i2c_client *client) +#endif { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct hi556 *hi556 = to_hi556(sd); @@ -1096,39 +1338,23 @@ v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); mutex_destroy(&hi556->mutex); -} #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) -static int hi556_remove_bp(struct i2c_client *client) -{ - hi556_remove(client); return 0; -} #endif +} static int hi556_probe(struct i2c_client *client) { struct hi556 *hi556; bool full_power; int ret; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; -#endif -#if IS_ENABLED(CONFIG_INTEL_VSC) - conf.lane_num = HI556_DATA_LANES; - /* frequency unit 100k */ - conf.freq = HI556_LINK_FREQ_437MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); - if (ret == -EAGAIN) { - dev_dbg(&client->dev, "VSC not ready, will re-probe"); - return -EPROBE_DEFER; - } else if (ret) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } -#endif + ret = hi556_check_hwcfg(&client->dev); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to check HW configuration: %d", + ret); hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); if (!hi556) { @@ -1137,12 +1363,22 @@ } v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) full_power = acpi_dev_state_d0(&client->dev); #else full_power = true; #endif if (full_power) { + ret = hi556_get_pm_resources(&client->dev); + if (ret) + return ret; + ret = hi556_power_on(&client->dev); + if (ret) { + dev_err_probe(&client->dev, ret, + "failed to power on\n"); + goto probe_error_ret; + } ret = hi556_identify_module(hi556); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); @@ -1176,9 +1412,6 @@ goto probe_error_media_entity_cleanup; } -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif /* Set the device's state to active if it's in D0 state. */ if (full_power) pm_runtime_set_active(&client->dev); @@ -1195,14 +1428,13 @@ mutex_destroy(&hi556->mutex); probe_error_ret: -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif + hi556_power_off(&client->dev); return ret; } static const struct dev_pm_ops hi556_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(hi556_suspend, hi556_resume) + SET_RUNTIME_PM_OPS(hi556_power_off, hi556_power_on, NULL) }; #ifdef CONFIG_ACPI @@ -1221,11 +1453,7 @@ .acpi_match_table = ACPI_PTR(hi556_acpi_ids), }, .probe_new = hi556_probe, -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) - .remove = hi556_remove_bp, -#else .remove = hi556_remove, -#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, #endif diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm11b1.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm11b1.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm11b1.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm11b1.c 2023-10-18 15:30:24.000000000 +0800 @@ -468,10 +468,8 @@ struct gpio_desc *reset_gpio; /* GPIO for powerdown */ struct gpio_desc *powerdown_gpio; - /* GPIO for clock enable */ - struct gpio_desc *clken_gpio; - /* GPIO for privacy LED */ - struct gpio_desc *pled_gpio; + /* Clock provider */ + struct clk *clk; #endif /* Streaming on/off */ @@ -501,21 +499,6 @@ return ppl; } -static void hm11b1_set_power(struct hm11b1 *hm11b1, int on) -{ -#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) - if (!(hm11b1->reset_gpio && hm11b1->powerdown_gpio)) - return; - gpiod_set_value_cansleep(hm11b1->reset_gpio, on); - gpiod_set_value_cansleep(hm11b1->powerdown_gpio, on); - gpiod_set_value_cansleep(hm11b1->clken_gpio, on); - gpiod_set_value_cansleep(hm11b1->pled_gpio, on); - msleep(20); -#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) - power_ctrl_logic_set_power(on); -#endif -} - static int hm11b1_read_reg(struct hm11b1 *hm11b1, u16 reg, u16 len, u32 *val) { struct i2c_client *client = hm11b1->client; @@ -771,7 +754,6 @@ int link_freq_index; int ret = 0; - hm11b1_set_power(hm11b1, 1); link_freq_index = hm11b1->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = hm11b1_write_reg_list(hm11b1, reg_list); @@ -806,7 +788,6 @@ if (hm11b1_write_reg(hm11b1, HM11B1_REG_MODE_SELECT, 1, HM11B1_MODE_STANDBY)) dev_err(&client->dev, "failed to stop streaming"); - hm11b1_set_power(hm11b1, 0); } static int hm11b1_set_stream(struct v4l2_subdev *sd, int enable) @@ -844,6 +825,42 @@ return ret; } +static int hm11b1_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm11b1 *hm11b1 = to_hm11b1(sd); + int ret = 0; + +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) + gpiod_set_value_cansleep(hm11b1->reset_gpio, 1); + gpiod_set_value_cansleep(hm11b1->powerdown_gpio, 1); + clk_disable_unprepare(hm11b1->clk); + msleep(20); +#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + ret = power_ctrl_logic_set_power(0); +#endif + + return ret; +} + +static int hm11b1_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm11b1 *hm11b1 = to_hm11b1(sd); + int ret = 0; + +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) + ret = clk_prepare_enable(hm11b1->clk); + gpiod_set_value_cansleep(hm11b1->powerdown_gpio, 0); + gpiod_set_value_cansleep(hm11b1->reset_gpio, 0); + msleep(20); +#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + ret = power_ctrl_logic_set_power(1); +#endif + + return ret; +} + static int __maybe_unused hm11b1_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1074,36 +1091,33 @@ } #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) -static int hm11b1_parse_dt(struct hm11b1 *hm11b1) +static int hm11b1_parse_power(struct hm11b1 *hm11b1) { struct device *dev = &hm11b1->client->dev; - int ret; - - hm11b1->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(hm11b1->reset_gpio); - if (ret < 0) { - dev_err(dev, "error while getting reset gpio: %d\n", ret); - return ret; - } - - hm11b1->powerdown_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(hm11b1->powerdown_gpio); - if (ret < 0) { - dev_err(dev, "error while getting powerdown gpio: %d\n", ret); - return ret; - } + long ret; - hm11b1->clken_gpio = devm_gpiod_get(dev, "clken", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(hm11b1->clken_gpio); - if (ret < 0) { - dev_err(dev, "error while getting clken_gpio gpio: %d\n", ret); + hm11b1->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(hm11b1->reset_gpio)) { + ret = PTR_ERR(hm11b1->reset_gpio); + dev_err(dev, "error while getting reset gpio: %ld\n", ret); + hm11b1->reset_gpio = NULL; + return (int)ret; + } + + hm11b1->powerdown_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); + if (IS_ERR(hm11b1->powerdown_gpio)) { + ret = PTR_ERR(hm11b1->powerdown_gpio); + dev_err(dev, "error while getting powerdown gpio: %ld\n", ret); + hm11b1->powerdown_gpio = NULL; return ret; } - hm11b1->pled_gpio = devm_gpiod_get(dev, "pled", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(hm11b1->pled_gpio); - if (ret < 0) { - dev_err(dev, "error while getting pled gpio: %d\n", ret); + hm11b1->clk = devm_clk_get_optional(dev, "clk"); + if (IS_ERR(hm11b1->clk)) { + ret = PTR_ERR(hm11b1->clk); + dev_err(dev, "error while getting clk: %ld\n", ret); + hm11b1->clk = NULL; return ret; } @@ -1121,17 +1135,21 @@ return -ENOMEM; hm11b1->client = client; + v4l2_i2c_subdev_init(&hm11b1->sd, client, &hm11b1_subdev_ops); + #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) - ret = hm11b1_parse_dt(hm11b1); - if (ret < 0) - return -EPROBE_DEFER; + ret = hm11b1_parse_power(hm11b1); + if (ret) + return ret; + ret = hm11b1_power_on(&client->dev); + if (ret) + return ret; #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) - if (power_ctrl_logic_set_power(1)) - return -EPROBE_DEFER; + ret = power_ctrl_logic_set_power(1); + if (ret) + return ret; #endif - hm11b1_set_power(hm11b1, 1); - v4l2_i2c_subdev_init(&hm11b1->sd, client, &hm11b1_subdev_ops); ret = hm11b1_identify_module(hm11b1); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); @@ -1176,7 +1194,6 @@ pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); - hm11b1_set_power(hm11b1, 0); return 0; probe_error_media_entity_cleanup: @@ -1187,12 +1204,14 @@ mutex_destroy(&hm11b1->mutex); probe_error_power_off: - hm11b1_set_power(hm11b1, 0); + hm11b1_power_off(&client->dev); + return ret; } static const struct dev_pm_ops hm11b1_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(hm11b1_suspend, hm11b1_resume) + SET_RUNTIME_PM_OPS(hm11b1_power_off, hm11b1_power_on, NULL) }; #ifdef CONFIG_ACPI diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm2170.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm2170.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm2170.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm2170.c 2023-10-18 15:30:24.000000000 +0800 @@ -27,6 +27,7 @@ #define HM2170_REG_CHIP_ID 0x0000 #define HM2170_CHIP_ID 0x2170 +#define HM2170_REG_SILICON_REV 0x0002 #define HM2170_REG_MODE_SELECT 0x0100 #define HM2170_MODE_STANDBY 0x00 @@ -74,6 +75,11 @@ HM2170_LINK_FREQ_384MHZ_INDEX, }; +enum hm2170_silicon_revision { + HM2170_SILICON_REV_B = 0, + HM2170_SILICON_REV_D = 1, +}; + struct hm2170_reg { u16 address; u8 val; @@ -111,7 +117,7 @@ const struct hm2170_reg_list reg_list; }; -static const struct hm2170_reg mode_1928x1088_regs[] = { +static const struct hm2170_reg mode_1928x1088_ver_b_regs[] = { {0x0103, 0x00}, {0xffff, 0x10}, {0x0202, 0x03}, @@ -120,6 +126,7 @@ {0x0301, 0x3F}, {0x0302, 0x07}, {0x0303, 0x04}, + {0x0350, 0x61}, {0x1000, 0xC3}, {0x1001, 0xC0}, {0x2000, 0x00}, @@ -153,44 +160,57 @@ {0x302A, 0x30}, {0x3042, 0x00}, {0x3070, 0x01}, + {0x307B, 0x08}, {0x30C4, 0x20}, {0x30D0, 0x01}, {0x30D2, 0x8E}, - {0x30D7, 0x02}, + {0x30D4, 0x14}, + {0x30D7, 0x21}, {0x30D9, 0x9E}, - {0x30DE, 0x03}, + {0x30DA, 0x14}, + {0x30DE, 0x41}, {0x30E0, 0x9E}, - {0x30E5, 0x04}, + {0x30E2, 0x14}, + {0x30E5, 0x61}, {0x30E7, 0x9F}, - {0x30EC, 0x24}, + {0x30E9, 0x14}, + {0x30EC, 0x43}, {0x30EE, 0x9F}, - {0x30F3, 0x44}, + {0x30F0, 0x14}, + {0x30F3, 0x63}, {0x30F5, 0x9F}, + {0x30F6, 0x14}, {0x30F8, 0x00}, {0x3101, 0x02}, {0x3103, 0x9E}, - {0x3108, 0x03}, + {0x3104, 0x14}, + {0x3108, 0x22}, {0x310A, 0x9E}, - {0x310F, 0x04}, + {0x310B, 0x14}, + {0x310F, 0x42}, {0x3111, 0x9E}, - {0x3116, 0x24}, + {0x3112, 0x14}, + {0x3116, 0x62}, {0x3118, 0x9F}, + {0x3119, 0x14}, {0x311D, 0x44}, {0x311F, 0x9F}, + {0x3120, 0x14}, {0x3124, 0x64}, {0x3126, 0x9F}, + {0x3127, 0x14}, {0x3135, 0x01}, {0x3137, 0x03}, {0x313C, 0x52}, {0x313E, 0x68}, {0x3144, 0x3E}, - {0x3145, 0x68}, + {0x3145, 0x24}, {0x3146, 0x08}, - {0x3147, 0x03}, - {0x3148, 0x0F}, - {0x3149, 0xFF}, + {0x3147, 0x13}, + {0x3148, 0x13}, + {0x3149, 0x6C}, {0x314A, 0x13}, - {0x314B, 0x0F}, + {0x314B, 0x03}, {0x314C, 0xF8}, {0x314D, 0x04}, {0x314E, 0x10}, @@ -211,7 +231,7 @@ {0x4801, 0x10}, {0x4802, 0x00}, {0x4803, 0x00}, - {0x4804, 0x7F}, + {0x4804, 0x3F}, {0x4805, 0x7F}, {0x4806, 0x3F}, {0x4807, 0x1F}, @@ -220,9 +240,9 @@ {0x480B, 0x08}, {0x480C, 0x90}, {0x480D, 0x00}, - {0x480E, 0x01}, + {0x480E, 0x00}, {0x480F, 0x04}, - {0x4810, 0x40}, + {0x4810, 0x3F}, {0x4811, 0x00}, {0x4812, 0x00}, {0x4813, 0x00}, @@ -282,7 +302,236 @@ {0x48C9, 0x00}, {0x48CA, 0x00}, {0x48CB, 0x00}, - {0x48CC, 0x00}, + {0x48CC, 0x1F}, + {0x48F0, 0x00}, + {0x48F1, 0x00}, + {0x48F2, 0x04}, + {0x48F3, 0x01}, + {0x48F4, 0xE0}, + {0x48F5, 0x01}, + {0x48F6, 0x10}, + {0x48F7, 0x00}, + {0x48F8, 0x00}, + {0x48F9, 0x00}, + {0x48FA, 0x00}, + {0x48FB, 0x01}, + {0x4931, 0x2B}, + {0x4932, 0x01}, + {0x4933, 0x01}, + {0x4934, 0x00}, + {0x4935, 0x0F}, + {0x4980, 0x00}, + {0x4A72, 0x01}, + {0x4A73, 0x01}, + {0x4C30, 0x00}, + {0x4CF2, 0x01}, + {0x4CF3, 0x01}, + {0x0104, 0x00}, +}; + +static const struct hm2170_reg mode_1928x1088_ver_d_regs[] = { + {0x0103, 0x00}, + {0xffff, 0x10}, + {0x0202, 0x03}, + {0x0203, 0x60}, + {0x0300, 0x5E}, + {0x0301, 0x3F}, + {0x0302, 0x07}, + {0x0303, 0x04}, + {0x0350, 0x61}, + {0x1000, 0xC3}, + {0x1001, 0xC0}, + {0x2000, 0x00}, + {0x2088, 0x01}, + {0x2089, 0x00}, + {0x208A, 0xC8}, + {0x2700, 0x00}, + {0x2711, 0x01}, + {0x2713, 0x04}, + {0x272F, 0x01}, + {0x2800, 0x01}, + {0x2821, 0x8E}, + {0x2823, 0x01}, + {0x282E, 0x01}, + {0x282F, 0xC0}, + {0x2839, 0x13}, + {0x283A, 0x01}, + {0x283B, 0x0F}, + {0x2842, 0x0C}, + {0x2846, 0x01}, + {0x2847, 0x94}, + {0x3001, 0x00}, + {0x3002, 0x88}, + {0x3004, 0x02}, + {0x3024, 0x20}, + {0x3025, 0x12}, + {0x3026, 0x00}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x3042, 0x00}, + {0x3070, 0x01}, + {0x307B, 0x08}, + {0x30C4, 0x20}, + {0x30D0, 0x02}, + {0x30D1, 0x03}, + {0x30D2, 0x3F}, + {0x30D3, 0x15}, + {0x30D7, 0x03}, + {0x30D8, 0x03}, + {0x30D9, 0x3F}, + {0x30DA, 0x15}, + {0x30DE, 0x04}, + {0x30DF, 0x03}, + {0x30E0, 0x3F}, + {0x30E1, 0x15}, + {0x30E5, 0x24}, + {0x30E6, 0x03}, + {0x30E7, 0x3F}, + {0x30E8, 0x15}, + {0x30EC, 0x2C}, + {0x30ED, 0x03}, + {0x30EE, 0x3F}, + {0x30EF, 0x15}, + {0x30F3, 0x2C}, + {0x30F4, 0x03}, + {0x30F5, 0x3F}, + {0x30F6, 0x15}, + {0x30F8, 0x01}, + {0x3101, 0x02}, + {0x3102, 0x01}, + {0x3103, 0x1F}, + {0x3104, 0x15}, + {0x3108, 0x03}, + {0x3109, 0x01}, + {0x310A, 0x1F}, + {0x310B, 0x14}, + {0x310F, 0x04}, + {0x3110, 0x01}, + {0x3111, 0x1F}, + {0x3112, 0x13}, + {0x3116, 0x24}, + {0x3117, 0x01}, + {0x3118, 0x3F}, + {0x3119, 0x13}, + {0x311D, 0x2C}, + {0x311E, 0x01}, + {0x311F, 0x3F}, + {0x3120, 0x13}, + {0x3121, 0x94}, + {0x3124, 0x2C}, + {0x3125, 0x01}, + {0x3126, 0x3F}, + {0x3127, 0x13}, + {0x3129, 0x01}, + {0x3135, 0x01}, + {0x3137, 0x03}, + {0x3139, 0x37}, + {0x313C, 0x52}, + {0x313E, 0x68}, + {0x3144, 0x3E}, + {0x3145, 0xE4}, + {0x3146, 0x58}, + {0x3147, 0x13}, + {0x3148, 0x11}, + {0x3149, 0x27}, + {0x314A, 0x13}, + {0x314B, 0x03}, + {0x314C, 0x0C}, + {0x314D, 0x00}, + {0x314E, 0x10}, + {0x3158, 0x01}, + {0x3161, 0x11}, + {0x3171, 0x05}, + {0x317A, 0x21}, + {0x317B, 0xF0}, + {0x317C, 0x0C}, + {0x317D, 0x09}, + {0x3182, 0x88}, + {0x3183, 0x18}, + {0x3184, 0x40}, + {0x318E, 0x88}, + {0x318F, 0x00}, + {0x3190, 0x00}, + {0x4003, 0x02}, + {0x4004, 0x02}, + {0x4800, 0x26}, + {0x4801, 0x21}, + {0x4802, 0x10}, + {0x4803, 0x00}, + {0x4804, 0x3F}, + {0x4805, 0x7F}, + {0x4806, 0x3F}, + {0x4807, 0x1F}, + {0x4809, 0x04}, + {0x480A, 0x84}, + {0x480B, 0x04}, + {0x480C, 0x48}, + {0x480D, 0x00}, + {0x480E, 0x00}, + {0x480F, 0x04}, + {0x4810, 0x3F}, + {0x4811, 0x00}, + {0x4812, 0x00}, + {0x4813, 0x00}, + {0x4814, 0x00}, + {0x4815, 0x00}, + {0x4816, 0x00}, + {0x4817, 0x00}, + {0x4818, 0x00}, + {0x4819, 0x02}, + {0x481F, 0x00}, + {0x4820, 0x0E}, + {0x4821, 0x0E}, + {0x4840, 0x00}, + {0x4844, 0x00}, + {0x4845, 0x00}, + {0x4846, 0x00}, + {0x4847, 0x00}, + {0x4848, 0x00}, + {0x4849, 0xF1}, + {0x484A, 0x00}, + {0x484B, 0x88}, + {0x484C, 0x01}, + {0x484D, 0x04}, + {0x484E, 0x64}, + {0x484F, 0x50}, + {0x4850, 0x04}, + {0x4851, 0x00}, + {0x4852, 0x01}, + {0x4853, 0x19}, + {0x4854, 0x50}, + {0x4855, 0x04}, + {0x4856, 0x00}, + {0x4863, 0x02}, + {0x4864, 0x3D}, + {0x4865, 0x02}, + {0x4866, 0xB0}, + {0x4880, 0x00}, + {0x48A0, 0x00}, + {0x48A1, 0x04}, + {0x48A2, 0x01}, + {0x48A3, 0xDD}, + {0x48A4, 0x0C}, + {0x48A5, 0x3B}, + {0x48A6, 0x20}, + {0x48A7, 0x20}, + {0x48A8, 0x20}, + {0x48A9, 0x20}, + {0x48AA, 0x00}, + {0x48C0, 0x3F}, + {0x48C1, 0x29}, + {0x48C3, 0x14}, + {0x48C4, 0x00}, + {0x48C5, 0x07}, + {0x48C6, 0x88}, + {0x48C7, 0x04}, + {0x48C8, 0x40}, + {0x48C9, 0x00}, + {0x48CA, 0x00}, + {0x48CB, 0x00}, + {0x48CC, 0x1F}, {0x48F0, 0x00}, {0x48F1, 0x00}, {0x48F2, 0x04}, @@ -321,18 +570,36 @@ HM2170_LINK_FREQ_384MHZ, }; -static const struct hm2170_mode supported_modes[] = { +static const struct hm2170_mode supported_modes[][1] = { + { + { + .width = 1928, + .height = 1088, + .hts = 2192, + .vts_def = HM2170_VTS_DEF, + .vts_min = HM2170_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_ver_b_regs), + .regs = mode_1928x1088_ver_b_regs, + }, + .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, + }, + + }, { - .width = 1928, - .height = 1088, - .hts = 2192, - .vts_def = HM2170_VTS_DEF, - .vts_min = HM2170_VTS_MIN, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1928x1088_regs), - .regs = mode_1928x1088_regs, + { + .width = 1928, + .height = 1088, + .hts = 2192, + .vts_def = HM2170_VTS_DEF, + .vts_min = HM2170_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_ver_d_regs), + .regs = mode_1928x1088_ver_d_regs, + }, + .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, + }, - .link_freq_index = HM2170_LINK_FREQ_384MHZ_INDEX, }, }; @@ -348,10 +615,14 @@ struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; #if IS_ENABLED(CONFIG_INTEL_VSC) + struct vsc_mipi_config conf; + struct vsc_camera_status status; struct v4l2_ctrl *privacy_status; #endif /* Current mode */ const struct hm2170_mode *cur_mode; + /* HM2170 silicon revision, B or D */ + enum hm2170_silicon_revision rev; /* To serialize asynchronus callbacks */ struct mutex mutex; @@ -591,7 +862,8 @@ hm2170->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; #if IS_ENABLED(CONFIG_INTEL_VSC) hm2170->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr, &hm2170_ctrl_ops, - V4L2_CID_PRIVACY, 0, 1, 1, 0); + V4L2_CID_PRIVACY, 0, 1, 1, + !(hm2170->status.status)); #endif v4l2_ctrl_new_std(ctrl_hdlr, &hm2170_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, @@ -642,21 +914,7 @@ struct i2c_client *client = v4l2_get_subdevdata(&hm2170->sd); const struct hm2170_reg_list *reg_list; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - conf.lane_num = HM2170_DATA_LANES; - /* frequency unit 100k */ - conf.freq = HM2170_LINK_FREQ_384MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, hm2170_vsc_privacy_callback, - hm2170, &status); - if (ret) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } - __v4l2_ctrl_s_ctrl(hm2170->privacy_status, !(status.status)); -#endif reg_list = &hm2170->cur_mode->reg_list; ret = hm2170_write_reg_list(hm2170, reg_list); if (ret) { @@ -679,17 +937,10 @@ static void hm2170_stop_streaming(struct hm2170 *hm2170) { struct i2c_client *client = v4l2_get_subdevdata(&hm2170->sd); -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_camera_status status; -#endif if (hm2170_write_reg(hm2170, HM2170_REG_MODE_SELECT, 1, HM2170_MODE_STANDBY)) dev_err(&client->dev, "failed to stop streaming"); -#if IS_ENABLED(CONFIG_INTEL_VSC) - if (vsc_release_camera_sensor(&status)) - dev_err(&client->dev, "Release VSC failed"); -#endif } static int hm2170_set_stream(struct v4l2_subdev *sd, int enable) @@ -727,6 +978,42 @@ return ret; } +#if IS_ENABLED(CONFIG_INTEL_VSC) +static int hm2170_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2170 *hm2170 = to_hm2170(sd); + int ret; + + ret = vsc_release_camera_sensor(&hm2170->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Release VSC failed"); + + return ret; +} + +static int hm2170_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2170 *hm2170 = to_hm2170(sd); + int ret; + + hm2170->conf.lane_num = HM2170_DATA_LANES; + /* frequency unit 100k */ + hm2170->conf.freq = HM2170_LINK_FREQ_384MHZ / 100000; + ret = vsc_acquire_camera_sensor(&hm2170->conf, + hm2170_vsc_privacy_callback, + hm2170, &hm2170->status); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Acquire VSC failed"); + return ret; + } + __v4l2_ctrl_s_ctrl(hm2170->privacy_status, !(hm2170->status.status)); + + return ret; +} +#endif + static int __maybe_unused hm2170_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -770,9 +1057,9 @@ const struct hm2170_mode *mode; s32 vblank_def, h_blank; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), width, - height, fmt->format.width, + mode = v4l2_find_nearest_size(supported_modes[hm2170->rev], + ARRAY_SIZE(supported_modes[hm2170->rev]), + width, height, fmt->format.width, fmt->format.height); mutex_lock(&hm2170->mutex); @@ -836,15 +1123,17 @@ struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct hm2170 *hm2170 = to_hm2170(sd); + + if (fse->index >= ARRAY_SIZE(supported_modes[hm2170->rev])) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) return -EINVAL; - fse->min_width = supported_modes[fse->index].width; + fse->min_width = supported_modes[hm2170->rev][fse->index].width; fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[hm2170->rev][fse->index].height; fse->max_height = fse->min_height; return 0; @@ -855,7 +1144,7 @@ struct hm2170 *hm2170 = to_hm2170(sd); mutex_lock(&hm2170->mutex); - hm2170_update_pad_format(&supported_modes[0], + hm2170_update_pad_format(&supported_modes[hm2170->rev][0], v4l2_subdev_get_try_format(sd, fh->state, 0)); mutex_unlock(&hm2170->mutex); @@ -891,17 +1180,22 @@ struct i2c_client *client = v4l2_get_subdevdata(&hm2170->sd); int ret; u32 val; + char rev; - ret = hm2170_read_reg(hm2170, HM2170_REG_CHIP_ID, 2, &val); + ret = hm2170_read_reg(hm2170, HM2170_REG_CHIP_ID, 3, &val); if (ret) return ret; + rev = val & 0xff; + val = val >> 8; if (val != HM2170_CHIP_ID) { dev_err(&client->dev, "chip id mismatch: %x!=%x", HM2170_CHIP_ID, val); return -ENXIO; } + hm2170->rev = rev < 0x4 ? HM2170_SILICON_REV_B : + HM2170_SILICON_REV_D; return 0; } @@ -930,31 +1224,29 @@ { struct hm2170 *hm2170; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; -#endif + hm2170 = devm_kzalloc(&client->dev, sizeof(*hm2170), GFP_KERNEL); + if (!hm2170) { + ret = -ENOMEM; + goto probe_error_ret; + } + + v4l2_i2c_subdev_init(&hm2170->sd, client, &hm2170_subdev_ops); #if IS_ENABLED(CONFIG_INTEL_VSC) - conf.lane_num = HM2170_DATA_LANES; + hm2170->conf.lane_num = HM2170_DATA_LANES; /* frequency unit 100k */ - conf.freq = HM2170_LINK_FREQ_384MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); + hm2170->conf.freq = HM2170_LINK_FREQ_384MHZ / 100000; + ret = vsc_acquire_camera_sensor(&hm2170->conf, + hm2170_vsc_privacy_callback, + hm2170, &hm2170->status); if (ret == -EAGAIN) { - dev_dbg(&client->dev, "VSC not ready, will re-probe"); return -EPROBE_DEFER; } else if (ret) { dev_err(&client->dev, "Acquire VSC failed"); return ret; } #endif - hm2170 = devm_kzalloc(&client->dev, sizeof(*hm2170), GFP_KERNEL); - if (!hm2170) { - ret = -ENOMEM; - goto probe_error_ret; - } - v4l2_i2c_subdev_init(&hm2170->sd, client, &hm2170_subdev_ops); ret = hm2170_identify_module(hm2170); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); @@ -962,7 +1254,7 @@ } mutex_init(&hm2170->mutex); - hm2170->cur_mode = &supported_modes[0]; + hm2170->cur_mode = &supported_modes[hm2170->rev][0]; ret = hm2170_init_controls(hm2170); if (ret) { dev_err(&client->dev, "failed to init controls: %d", ret); @@ -987,9 +1279,6 @@ goto probe_error_media_entity_cleanup; } -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1009,13 +1298,17 @@ probe_error_ret: #if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); + hm2170_power_off(&client->dev); #endif + return ret; } static const struct dev_pm_ops hm2170_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(hm2170_suspend, hm2170_resume) +#if IS_ENABLED(CONFIG_INTEL_VSC) + SET_RUNTIME_PM_OPS(hm2170_power_off, hm2170_power_on, NULL) +#endif }; static const struct acpi_device_id hm2170_acpi_ids[] = { diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm2172.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm2172.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/hm2172.c 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/hm2172.c 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,1639 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HM2172_LINK_FREQ_384MHZ 384000000ULL +#define HM2172_SCLK 76000000LL +#define HM2172_MCLK 19200000 +#define HM2172_DATA_LANES 2 +#define HM2172_RGB_DEPTH 10 + +#define HM2172_REG_DELAY 0xffff + +#define HM2172_REG_CHIP_ID 0x0000 +#define HM2172_CHIP_ID 0x2172 + +#define HM2172_REG_MODE_SELECT 0x0100 +#define HM2172_MODE_STANDBY 0x00 +#define HM2172_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define HM2172_REG_VTS 0x4809 +#define HM2172_VTS_DEF 0x0484 +#define HM2172_VTS_MIN 0x0484 +#define HM2172_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define HM2172_REG_HTS 0x480B + +/* Exposure controls from sensor */ +#define HM2172_REG_EXPOSURE 0x0202 +#define HM2172_EXPOSURE_MIN 2 +#define HM2172_EXPOSURE_MAX_MARGIN 2 +#define HM2172_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define HM2172_REG_ANALOG_GAIN 0x0208 +#define HM2172_ANAL_GAIN_MIN 0 +#define HM2172_ANAL_GAIN_MAX 80 +#define HM2172_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define HM2172_REG_DIGITAL_GAIN 0x020A +#define HM2172_DGTL_GAIN_MIN 256 +#define HM2172_DGTL_GAIN_MAX 1020 +#define HM2172_DGTL_GAIN_STEP 1 +#define HM2172_DGTL_GAIN_DEFAULT 256 + +/* Register update control */ +#define HM2172_REG_COMMAND_UPDATE 0x0104 +#define HM2172_COMMAND_UPDATE 0x00 +#define HM2172_COMMAND_HOLD 0x01 + +/* Test Pattern Control */ +#define HM2172_REG_TEST_PATTERN 0x0601 +#define HM2172_TEST_PATTERN_ENABLE BIT(0) +#define HM2172_TEST_PATTERN_BAR_SHIFT 1 + +enum { + HM2172_LINK_FREQ_384MHZ_INDEX, +}; + +struct hm2172_reg { + u16 address; + u8 val; +}; + +struct hm2172_reg_list { + u32 num_of_regs; + const struct hm2172_reg *regs; +}; + +struct hm2172_link_freq_config { + const struct hm2172_reg_list reg_list; +}; + +struct hm2172_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct hm2172_reg_list reg_list; +}; + +static const struct hm2172_reg mode_1928x1088_30fps_2lane[] = { + {0x0103, 0x00}, + {0xffff, 0x0A}, + {0x0203, 0xEE}, + {0x0300, 0x6A}, + {0x0202, 0x08}, + {0x0301, 0x19}, + {0x0302, 0x08}, + {0x0303, 0x04}, + {0x030F, 0x04}, + {0x0350, 0x61}, + {0x1000, 0xC3}, + {0x1001, 0xC0}, + {0x2000, 0x00}, + {0x2088, 0x01}, + {0x2089, 0x00}, + {0x208A, 0xC8}, + {0x2700, 0x00}, + {0x2711, 0x01}, + {0x2713, 0x04}, + {0x272F, 0x01}, + {0x2800, 0x01}, + {0x281B, 0x02}, + {0x2821, 0x8E}, + {0x2823, 0x01}, + {0x282E, 0x01}, + {0x282F, 0xC0}, + {0x2839, 0x59}, + {0x283A, 0x08}, + {0x283B, 0x09}, + {0x2842, 0x0C}, + {0x2846, 0x01}, + {0x2847, 0x94}, + {0x2848, 0x31}, + {0x3001, 0x00}, + {0x3002, 0x88}, + {0x3004, 0x02}, + {0x3024, 0x20}, + {0x3025, 0x12}, + {0x3026, 0x00}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x3030, 0x68}, + {0x3042, 0x00}, + {0x3070, 0x01}, + {0x307B, 0x08}, + {0x30C4, 0x20}, + {0x30D0, 0x02}, + {0x30D1, 0x00}, + {0x30D2, 0x1E}, + {0x30D3, 0x31}, + {0x30D4, 0x14}, + {0x30D7, 0x22}, + {0x30D8, 0x00}, + {0x30D9, 0x1E}, + {0x30DA, 0x31}, + {0x30DB, 0x14}, + {0x30DE, 0x04}, + {0x30DF, 0x00}, + {0x30E0, 0x1E}, + {0x30E1, 0x31}, + {0x30E2, 0x14}, + {0x30E5, 0x24}, + {0x30E6, 0x00}, + {0x30E7, 0x1E}, + {0x30E8, 0x31}, + {0x30E9, 0x14}, + {0x30EC, 0x2C}, + {0x30ED, 0x00}, + {0x30EE, 0x1E}, + {0x30EF, 0x31}, + {0x30F0, 0x14}, + {0x30F3, 0x2C}, + {0x30F4, 0x00}, + {0x30F5, 0x1E}, + {0x30F6, 0x31}, + {0x30F7, 0x14}, + {0x30F8, 0x01}, + {0x3101, 0x02}, + {0x3102, 0x00}, + {0x3103, 0x1E}, + {0x3104, 0x31}, + {0x3105, 0x14}, + {0x3108, 0x22}, + {0x3109, 0x00}, + {0x310A, 0x1E}, + {0x310B, 0x31}, + {0x310C, 0x14}, + {0x310F, 0x04}, + {0x3110, 0x00}, + {0x3111, 0x1E}, + {0x3112, 0x31}, + {0x3113, 0x14}, + {0x3116, 0x24}, + {0x3117, 0x00}, + {0x3118, 0x1E}, + {0x3119, 0x31}, + {0x311A, 0x14}, + {0x311D, 0x2C}, + {0x311E, 0x00}, + {0x311F, 0x1E}, + {0x3120, 0x31}, + {0x3121, 0x14}, + {0x3124, 0x2C}, + {0x3125, 0x00}, + {0x3126, 0x1E}, + {0x3127, 0x31}, + {0x3128, 0x14}, + {0x3129, 0x01}, + {0x3135, 0x01}, + {0x3137, 0x03}, + {0x3138, 0x10}, + {0x3139, 0x25}, + {0x313C, 0x6A}, + {0x313D, 0x3A}, + {0x313E, 0xE8}, + {0x3140, 0x0C}, + {0x3144, 0x3E}, + {0x3145, 0xF4}, + {0x3146, 0x32}, + {0x3147, 0x11}, + {0x3148, 0x10}, + {0x3149, 0xA7}, + {0x314A, 0x80}, + {0x314B, 0x33}, + {0x314C, 0x1C}, + {0x314D, 0x34}, + {0x314E, 0x13}, + {0x314F, 0x00}, + {0x3151, 0x08}, + {0x3152, 0x00}, + {0x3158, 0x00}, + {0x315B, 0x80}, + {0x3161, 0x11}, + {0x3166, 0x4F}, + {0x316B, 0x0A}, + {0x3171, 0x05}, + {0x3172, 0x1F}, + {0x3179, 0x0F}, + {0x317A, 0x21}, + {0x317B, 0x84}, + {0x317C, 0x1C}, + {0x317D, 0x00}, + {0x317E, 0x04}, + {0x3182, 0x88}, + {0x3183, 0x18}, + {0x3184, 0x40}, + {0x318E, 0x88}, + {0x318F, 0x00}, + {0x3190, 0x00}, + {0x3191, 0x84}, + {0x4003, 0x02}, + {0x4004, 0x02}, + {0x4800, 0x26}, + {0x4801, 0x10}, + {0x4802, 0x00}, + {0x4803, 0x00}, + {0x4804, 0x3F}, + {0x4805, 0x7F}, + {0x4806, 0x3F}, + {0x4807, 0x1F}, + {0x4809, 0x08}, + {0x480A, 0xF0}, + {0x480B, 0x04}, + {0x480C, 0x54}, + {0x480D, 0x00}, + {0x480E, 0x00}, + {0x480F, 0x04}, + {0x4810, 0x3F}, + {0x4811, 0x00}, + {0x4812, 0x00}, + {0x4813, 0x00}, + {0x4814, 0x00}, + {0x4815, 0x00}, + {0x4816, 0x00}, + {0x4817, 0x00}, + {0x4818, 0x00}, + {0x4819, 0x02}, + {0x481E, 0x10}, + {0x481F, 0x00}, + {0x4820, 0x0E}, + {0x4821, 0x0E}, + {0x4822, 0xA7}, + {0x4823, 0x33}, + {0x4824, 0x1C}, + {0x4840, 0x00}, + {0x4844, 0x00}, + {0x4845, 0x00}, + {0x4846, 0x00}, + {0x4847, 0x00}, + {0x4848, 0x00}, + {0x4849, 0xF1}, + {0x484A, 0x00}, + {0x484B, 0x88}, + {0x484C, 0x01}, + {0x484D, 0x04}, + {0x484E, 0x64}, + {0x484F, 0x50}, + {0x4850, 0x04}, + {0x4851, 0x00}, + {0x4852, 0x01}, + {0x4853, 0x19}, + {0x4854, 0x50}, + {0x4855, 0x04}, + {0x4856, 0x00}, + {0x4863, 0x02}, + {0x4864, 0x3C}, + {0x4865, 0x02}, + {0x4866, 0xAE}, + {0x4880, 0x00}, + {0x48A0, 0x00}, + {0x48A1, 0x04}, + {0x48A2, 0x01}, + {0x48A3, 0xDD}, + {0x48A4, 0x0C}, + {0x48A5, 0x3B}, + {0x48A6, 0x20}, + {0x48A7, 0x20}, + {0x48A8, 0x20}, + {0x48A9, 0x20}, + {0x48AA, 0x00}, + {0x48C0, 0x3F}, + {0x48C1, 0x29}, + {0x48C3, 0x14}, + {0x48C4, 0x00}, + {0x48C5, 0x07}, + {0x48C6, 0x88}, + {0x48C7, 0x04}, + {0x48C8, 0x40}, + {0x48C9, 0x00}, + {0x48CA, 0x00}, + {0x48CB, 0x00}, + {0x48CC, 0x1F}, + {0x48F0, 0x00}, + {0x48F1, 0x00}, + {0x48F2, 0x04}, + {0x48F3, 0x01}, + {0x48F4, 0xE0}, + {0x48F5, 0x01}, + {0x48F6, 0x10}, + {0x48F7, 0x00}, + {0x48F8, 0x00}, + {0x48F9, 0x00}, + {0x48FA, 0x00}, + {0x48FB, 0x01}, + {0x4931, 0x2B}, + {0x4932, 0x01}, + {0x4933, 0x01}, + {0x4934, 0x04}, + {0x4935, 0xBA}, + {0x495E, 0x10}, + {0x4962, 0xA7}, + {0x4963, 0x33}, + {0x4964, 0x1C}, + {0x4980, 0x00}, + {0x4A72, 0x01}, + {0x4A73, 0x01}, + {0x4C1E, 0x10}, + {0x4C22, 0xA7}, + {0x4C23, 0x33}, + {0x4C24, 0x1C}, + {0x4C30, 0x00}, + {0x4CF2, 0x01}, + {0x4CF3, 0x01}, + {0x0104, 0x01}, +}; + +static const struct hm2172_reg mode_1928x1088_60fps_2lane[] = { + {0x0103, 0x00}, + {0xffff, 0x10}, + {0x0202, 0x03}, + {0x0203, 0x60}, + {0x0300, 0x5E},//ver:0513 5E + {0x0301, 0x3F}, + {0x0302, 0x07},//ver:0513 07 + {0x0303, 0x04}, + {0x1000, 0xC3}, + {0x1001, 0xC0}, + {0x2000, 0x00}, + {0x2088, 0x01}, + {0x2089, 0x00}, + {0x208A, 0xC8}, + {0x2700, 0x00}, + {0x2711, 0x01}, + {0x2713, 0x04}, + {0x272F, 0x01}, + {0x2800, 0x01}, + {0x2821, 0x8E}, + {0x2823, 0x01}, + {0x282E, 0x01}, + {0x282F, 0xC0}, + {0x2839, 0x13}, + {0x283A, 0x01}, + {0x283B, 0x0F}, + {0x2842, 0x0C}, + {0x2846, 0x01}, + {0x2847, 0x94}, + {0x3001, 0x00}, + {0x3002, 0x88}, + {0x3004, 0x02}, + {0x3024, 0x20}, + {0x3025, 0x12}, + {0x3026, 0x00}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x3042, 0x00}, + {0x3070, 0x01}, + {0x30C4, 0x20}, + {0x30D0, 0x01}, + {0x30D2, 0x8E}, + {0x30D7, 0x02}, + {0x30D9, 0x9E}, + {0x30DE, 0x03}, + {0x30E0, 0x9E}, + {0x30E5, 0x04}, + {0x30E7, 0x9F}, + {0x30EC, 0x24}, + {0x30EE, 0x9F}, + {0x30F3, 0x44}, + {0x30F5, 0x9F}, + {0x30F8, 0x00}, + {0x3101, 0x02}, + {0x3103, 0x9E}, + {0x3108, 0x03}, + {0x310A, 0x9E}, + {0x310F, 0x04}, + {0x3111, 0x9E}, + {0x3116, 0x24}, + {0x3118, 0x9F}, + {0x311D, 0x44}, + {0x311F, 0x9F}, + {0x3124, 0x64}, + {0x3126, 0x9F}, + {0x3135, 0x01}, + {0x3137, 0x03}, + {0x313C, 0x52}, + {0x313E, 0x68}, + {0x3144, 0x3E}, + {0x3145, 0x68}, + {0x3146, 0x08}, + {0x3147, 0x03}, + {0x3148, 0x0F}, + {0x3149, 0xFF}, + {0x314A, 0x13}, + {0x314B, 0x0F}, + {0x314C, 0xF8}, + {0x314D, 0x04}, + {0x314E, 0x10}, + {0x3161, 0x11}, + {0x3171, 0x05}, + {0x317A, 0x21}, + {0x317B, 0xF0}, + {0x317C, 0x07}, + {0x317D, 0x09}, + {0x3183, 0x18}, + {0x3184, 0x4A}, + {0x318E, 0x88}, + {0x318F, 0x00}, + {0x3190, 0x00}, + {0x4003, 0x02}, + {0x4004, 0x02}, + {0x4800, 0x26}, + {0x4801, 0x10}, + {0x4802, 0x00}, + {0x4803, 0x00}, + {0x4804, 0x7F}, + {0x4805, 0x7F}, + {0x4806, 0x3F}, + {0x4807, 0x1F}, + {0x4809, 0x04}, + {0x480A, 0x7A}, + {0x480B, 0x04}, + {0x480C, 0x50}, + {0x480D, 0x00}, + {0x480E, 0x01},//00 + {0x480F, 0x04}, + {0x4810, 0x40},//3F + {0x4811, 0x00}, + {0x4812, 0x00}, + {0x4813, 0x00}, + {0x4814, 0x00}, + {0x4815, 0x00}, + {0x4816, 0x00}, + {0x4817, 0x00}, + {0x4818, 0x00}, + {0x4819, 0x03}, + {0x481F, 0x00}, + {0x4820, 0x0E}, + {0x4821, 0x0E}, + {0x4840, 0x00}, + {0x4844, 0x00}, + {0x4845, 0x00}, + {0x4846, 0x00}, + {0x4847, 0x00}, + {0x4848, 0x00}, + {0x4849, 0xF1}, + {0x484A, 0x00}, + {0x484B, 0x88}, + {0x484C, 0x01}, + {0x484D, 0x04}, + {0x484E, 0x64}, + {0x484F, 0x50}, + {0x4850, 0x04}, + {0x4851, 0x00}, + {0x4852, 0x01}, + {0x4853, 0x19}, + {0x4854, 0x50}, + {0x4855, 0x04}, + {0x4856, 0x00}, + {0x4863, 0x02}, + {0x4864, 0x3D}, + {0x4865, 0x02}, + {0x4866, 0xB0}, + {0x4880, 0x00}, + {0x48A0, 0x00}, + {0x48A1, 0x04}, + {0x48A2, 0x01}, + {0x48A3, 0xDD}, + {0x48A4, 0x0C}, + {0x48A5, 0x3B}, + {0x48A6, 0x20}, + {0x48A7, 0x20}, + {0x48A8, 0x20}, + {0x48A9, 0x20}, + {0x48AA, 0x00}, + {0x48C0, 0x3F}, + {0x48C1, 0x29}, + {0x48C3, 0x14}, + {0x48C4, 0x00}, + {0x48C5, 0x07}, + {0x48C6, 0x88}, + {0x48C7, 0x04}, + {0x48C8, 0x40}, + {0x48C9, 0x00}, + {0x48CA, 0x00}, + {0x48CB, 0x00}, + {0x48CC, 0x00}, + {0x48F0, 0x00}, + {0x48F1, 0x00}, + {0x48F2, 0x04}, + {0x48F3, 0x01}, + {0x48F4, 0xE0}, + {0x48F5, 0x01}, + {0x48F6, 0x10}, + {0x48F7, 0x00}, + {0x48F8, 0x00}, + {0x48F9, 0x00}, + {0x48FA, 0x00}, + {0x48FB, 0x01}, + {0x4931, 0x2B}, + {0x4932, 0x01}, + {0x4933, 0x01}, + {0x4934, 0x00}, + {0x4935, 0x0F}, + {0x4980, 0x00}, + {0x4A72, 0x01}, + {0x4A73, 0x01}, + {0x4C30, 0x00}, + {0x4CF2, 0x01}, + {0x4CF3, 0x01}, + {0x0104, 0x00} +}; + +static const struct hm2172_reg mode_1928x1088_HDR_60fps[] = { + {0x0103, 0x00}, + {0xffff, 0x10}, + {0x0202, 0x08}, + {0x0203, 0xEE}, + {0x0300, 0x6A}, + {0x0301, 0x19}, + {0x0302, 0x08}, + {0x0303, 0x04}, + {0x030F, 0x04}, + {0x0350, 0x61}, + {0x1000, 0xC3}, + {0x1001, 0xC0}, + {0x2000, 0x00}, + {0x2088, 0x01}, + {0x2089, 0x00}, + {0x208A, 0xC8}, + {0x2700, 0x00}, + {0x2711, 0x01}, + {0x2713, 0x04}, + {0x272F, 0x01}, + {0x2800, 0x01}, + {0x281B, 0x02}, + {0x2821, 0x8E}, + {0x2823, 0x01}, + {0x282E, 0x01}, + {0x282F, 0xC0}, + {0x2839, 0x59}, + {0x283A, 0x08}, + {0x283B, 0x09}, + {0x2842, 0x0C}, + {0x2846, 0x01}, + {0x2847, 0x94}, + {0x2848, 0x31},//LS_OFFSET 1/4 of FLINE:0x121, 07/25; orig:0x0088 + {0x3001, 0x00}, + {0x3002, 0x88}, + {0x3004, 0x02}, + {0x3024, 0x20}, + {0x3025, 0x12}, + {0x3026, 0x00}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x3030, 0x68}, + {0x3042, 0x00}, + {0x3070, 0x01}, + {0x307B, 0x08}, + {0x30C4, 0x20}, + {0x30D0, 0x02}, + {0x30D1, 0x00}, + {0x30D2, 0x1E}, + {0x30D3, 0x31}, + {0x30D4, 0x14}, + {0x30D7, 0x22}, + {0x30D8, 0x00}, + {0x30D9, 0x1E}, + {0x30DA, 0x31}, + {0x30DB, 0x14}, + {0x30DE, 0x04}, + {0x30DF, 0x00}, + {0x30E0, 0x1E}, + {0x30E1, 0x31}, + {0x30E2, 0x14}, + {0x30E5, 0x24}, + {0x30E6, 0x00}, + {0x30E7, 0x1E}, + {0x30E8, 0x31}, + {0x30E9, 0x14}, + {0x30EC, 0x2C}, + {0x30ED, 0x00}, + {0x30EE, 0x1E}, + {0x30EF, 0x31}, + {0x30F0, 0x14}, + {0x30F3, 0x2C}, + {0x30F4, 0x00}, + {0x30F5, 0x1E}, + {0x30F6, 0x31}, + {0x30F7, 0x14}, + {0x30F8, 0x01}, + {0x3101, 0x02}, + {0x3102, 0x00}, + {0x3103, 0x1E}, + {0x3104, 0x31}, + {0x3105, 0x14}, + {0x3108, 0x22}, + {0x3109, 0x00}, + {0x310A, 0x1E}, + {0x310B, 0x31}, + {0x310C, 0x14}, + {0x310F, 0x04}, + {0x3110, 0x00}, + {0x3111, 0x1E}, + {0x3112, 0x31}, + {0x3113, 0x14}, + {0x3116, 0x24}, + {0x3117, 0x00}, + {0x3118, 0x1E}, + {0x3119, 0x31}, + {0x311A, 0x14}, + {0x311D, 0x2C}, + {0x311E, 0x00}, + {0x311F, 0x1E}, + {0x3120, 0x31}, + {0x3121, 0x14}, + {0x3124, 0x2C}, + {0x3125, 0x00}, + {0x3126, 0x1E}, + {0x3127, 0x31}, + {0x3128, 0x14}, + {0x3129, 0x01}, + {0x3135, 0x01}, + {0x3137, 0x03}, + {0x3138, 0x10}, + {0x3139, 0x25}, + {0x313C, 0x6A}, + {0x313D, 0x3A}, + {0x313E, 0xE8}, + {0x3140, 0x0C}, + {0x3144, 0x3E}, + {0x3145, 0xF4}, + {0x3146, 0x32}, + {0x3147, 0x11}, + {0x3148, 0x10}, + {0x3149, 0xA7}, + {0x314A, 0x80}, + {0x314B, 0x33}, + {0x314C, 0x1C}, + {0x314D, 0x34}, + {0x314E, 0x13}, + {0x314F, 0x00}, + {0x3151, 0x08}, + {0x3152, 0x00}, + {0x3158, 0x00}, + {0x315B, 0x80}, + {0x3161, 0x11}, + {0x3166, 0x4F}, + {0x316B, 0x0A}, + {0x3171, 0x05}, + {0x3172, 0x1F}, + {0x3179, 0x0F}, + {0x317A, 0x21}, + {0x317B, 0x84}, + {0x317C, 0x1C}, + {0x317D, 0x00}, + {0x317E, 0x04}, + {0x3182, 0x88}, + {0x3183, 0x18}, + {0x3184, 0x40}, + {0x318E, 0x88}, + {0x318F, 0x00}, + {0x3190, 0x00}, + {0x3191, 0x84}, + {0x4003, 0x02}, + {0x4004, 0x02}, + {0x4800, 0x26}, + {0x4801, 0x10}, + {0x4802, 0x00}, + {0x4803, 0x00}, + {0x4804, 0x3F}, + {0x4805, 0x7F}, + {0x4806, 0x3F}, + {0x4807, 0x1F}, + {0x4809, 0x08}, + {0x480A, 0xF0}, + {0x480B, 0x04}, + {0x480C, 0x54}, + {0x480D, 0x00}, + {0x480E, 0x00}, + {0x480F, 0x04}, + {0x4810, 0x3F}, + {0x4811, 0x00}, + {0x4812, 0x00}, + {0x4813, 0x00}, + {0x4814, 0x00}, + {0x4815, 0x00}, + {0x4816, 0x00}, + {0x4817, 0x00}, + {0x4818, 0x00}, + {0x4819, 0x02}, + {0x481E, 0x10}, + {0x481F, 0x00}, + {0x4820, 0x0E}, + {0x4821, 0x0E}, + {0x4822, 0xA7}, + {0x4823, 0x33}, + {0x4824, 0x1C}, + {0x4840, 0x00}, + {0x4844, 0x00}, + {0x4845, 0x00}, + {0x4846, 0x00}, + {0x4847, 0x00}, + {0x4848, 0x00}, + {0x4849, 0xF1}, + {0x484A, 0x00}, + {0x484B, 0x88}, + {0x484C, 0x01}, + {0x484D, 0x04}, + {0x484E, 0x64}, + {0x484F, 0x50}, + {0x4850, 0x04}, + {0x4851, 0x00}, + {0x4852, 0x01}, + {0x4853, 0x19}, + {0x4854, 0x50}, + {0x4855, 0x04}, + {0x4856, 0x00}, + {0x4863, 0x02}, + {0x4864, 0x3C}, + {0x4865, 0x02}, + {0x4866, 0xAE}, + {0x4880, 0x00}, + {0x48A0, 0x00}, + {0x48A1, 0x04}, + {0x48A2, 0x01}, + {0x48A3, 0xDD}, + {0x48A4, 0x0C}, + {0x48A5, 0x3B}, + {0x48A6, 0x20}, + {0x48A7, 0x20}, + {0x48A8, 0x20}, + {0x48A9, 0x20}, + {0x48AA, 0x00}, + {0x48C0, 0x3F}, + {0x48C1, 0x29}, + {0x48C3, 0x14}, + {0x48C4, 0x00}, + {0x48C5, 0x07}, + {0x48C6, 0x88}, + {0x48C7, 0x04}, + {0x48C8, 0x40}, + {0x48C9, 0x00}, + {0x48CA, 0x00}, + {0x48CB, 0x00}, + {0x48CC, 0x1F}, + {0x48F0, 0x00}, + {0x48F1, 0x00}, + {0x48F2, 0x04}, + {0x48F3, 0x01}, + {0x48F4, 0xE0}, + {0x48F5, 0x01}, + {0x48F6, 0x10}, + {0x48F7, 0x00}, + {0x48F8, 0x00}, + {0x48F9, 0x00}, + {0x48FA, 0x00}, + {0x48FB, 0x01}, + {0x4931, 0x2B}, + {0x4932, 0x01}, + {0x4933, 0x01}, + {0x4934, 0x04}, + {0x4935, 0xBA}, + {0x495E, 0x10}, + {0x4962, 0xA7}, + {0x4963, 0x33}, + {0x4964, 0x1C}, + {0x4980, 0x00}, + {0x4A72, 0x01}, + {0x4A73, 0x01}, + {0x4C1E, 0x10}, + {0x4C22, 0xA7}, + {0x4C23, 0x33}, + {0x4C24, 0x1C}, + {0x4C30, 0x00}, + {0x4CF2, 0x01}, + {0x4CF3, 0x01}, + {0x0104, 0x00}, +}; + +static const char * const hm2172_test_pattern_menu[] = { + "Disabled", + "Solid Color", + "Color Bar", + "Color Bar With Blending", + "PN11", +}; + +static const s64 link_freq_menu_items[] = { + HM2172_LINK_FREQ_384MHZ, +}; + +static const struct hm2172_mode supported_modes[] = { + { + .width = 1928, + .height = 1088, + .hts = 2192, + .vts_def = HM2172_VTS_DEF, + .vts_min = HM2172_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane), + .regs = mode_1928x1088_30fps_2lane, + }, + .link_freq_index = HM2172_LINK_FREQ_384MHZ_INDEX, + }, + { + .width = 1928, + .height = 1088, + .hts = 2192, + .vts_def = HM2172_VTS_DEF, + .vts_min = HM2172_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_60fps_2lane), + .regs = mode_1928x1088_60fps_2lane, + }, + .link_freq_index = HM2172_LINK_FREQ_384MHZ_INDEX, + }, + { + .width = 1928, + .height = 1088, + .hts = 2192, + .vts_def = HM2172_VTS_DEF, + .vts_min = HM2172_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_HDR_60fps), + .regs = mode_1928x1088_HDR_60fps, + }, + .link_freq_index = HM2172_LINK_FREQ_384MHZ_INDEX, + + }, +}; + +struct hm2172 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + struct clk *img_clk; + struct regulator *avdd; + struct gpio_desc *reset; + struct gpio_desc *handshake; + + /* Current mode */ + const struct hm2172_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct hm2172 *to_hm2172(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct hm2172, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * HM2172_DATA_LANES; + + do_div(pixel_rate, HM2172_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, HM2172_SCLK); + + return ppl; +} + +static int hm2172_read_reg(struct hm2172 *hm2172, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret = 0; + + if (len > sizeof(data_buf)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[sizeof(data_buf) - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return ret < 0 ? ret : -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int hm2172_write_reg(struct hm2172 *hm2172, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + u8 buf[6]; + int ret = 0; + + if (len > 4) + return -EINVAL; + + if (reg == HM2172_REG_DELAY) { + msleep(val); + return 0; + } + dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, val); + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) { + dev_err(&client->dev, "failed to write reg %d val %d", reg, val); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int hm2172_write_reg_list(struct hm2172 *hm2172, + const struct hm2172_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + unsigned int i; + int ret = 0; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = hm2172_write_reg(hm2172, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int hm2172_test_pattern(struct hm2172 *hm2172, u32 pattern) +{ + if (pattern) + pattern = pattern << HM2172_TEST_PATTERN_BAR_SHIFT | + HM2172_TEST_PATTERN_ENABLE; + + return hm2172_write_reg(hm2172, HM2172_REG_TEST_PATTERN, 1, pattern); +} + +static int hm2172_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hm2172 *hm2172 = container_of(ctrl->handler, + struct hm2172, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = hm2172->cur_mode->height + ctrl->val - + HM2172_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(hm2172->exposure, + hm2172->exposure->minimum, + exposure_max, hm2172->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + ret = hm2172_write_reg(hm2172, HM2172_REG_COMMAND_UPDATE, 1, + HM2172_COMMAND_HOLD); + if (ret) + dev_dbg(&client->dev, "failed to hold command"); + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = hm2172_write_reg(hm2172, HM2172_REG_ANALOG_GAIN, 1, + ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = hm2172_write_reg(hm2172, HM2172_REG_DIGITAL_GAIN, 2, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = hm2172_write_reg(hm2172, HM2172_REG_EXPOSURE, 2, ctrl->val); + break; + + case V4L2_CID_VBLANK: + ret = hm2172_write_reg(hm2172, HM2172_REG_VTS, 2, + hm2172->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = hm2172_test_pattern(hm2172, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + ret |= hm2172_write_reg(hm2172, HM2172_REG_COMMAND_UPDATE, 1, + HM2172_COMMAND_UPDATE); + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops hm2172_ctrl_ops = { + .s_ctrl = hm2172_set_ctrl, +}; + +static int hm2172_init_controls(struct hm2172 *hm2172) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct hm2172_mode *cur_mode; + s64 exposure_max, h_blank, pixel_rate; + u32 vblank_min, vblank_max, vblank_default; + int size; + int ret = 0; + + ctrl_hdlr = &hm2172->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + + if (ret) + return ret; + + ctrl_hdlr->lock = &hm2172->mutex; + cur_mode = hm2172->cur_mode; + size = ARRAY_SIZE(link_freq_menu_items); + + hm2172->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_LINK_FREQ, + size - 1, 0, + link_freq_menu_items); + if (hm2172->link_freq) + hm2172->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(HM2172_LINK_FREQ_384MHZ_INDEX); + hm2172->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + vblank_min = cur_mode->vts_min - cur_mode->height; + vblank_max = HM2172_VTS_MAX - cur_mode->height; + vblank_default = cur_mode->vts_def - cur_mode->height; + hm2172->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + + h_blank = to_pixels_per_line(cur_mode->hts, cur_mode->link_freq_index); + h_blank -= cur_mode->width; + hm2172->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (hm2172->hblank) + hm2172->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + HM2172_ANAL_GAIN_MIN, HM2172_ANAL_GAIN_MAX, + HM2172_ANAL_GAIN_STEP, HM2172_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + HM2172_DGTL_GAIN_MIN, HM2172_DGTL_GAIN_MAX, + HM2172_DGTL_GAIN_STEP, HM2172_DGTL_GAIN_DEFAULT); + exposure_max = cur_mode->vts_def - HM2172_EXPOSURE_MAX_MARGIN; + hm2172->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_EXPOSURE, + HM2172_EXPOSURE_MIN, exposure_max, + HM2172_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hm2172_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(hm2172_test_pattern_menu) - 1, + 0, 0, hm2172_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + hm2172->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void hm2172_update_pad_format(const struct hm2172_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int hm2172_start_streaming(struct hm2172 *hm2172) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + const struct hm2172_reg_list *reg_list; + int ret = 0; + + reg_list = &hm2172->cur_mode->reg_list; + ret = hm2172_write_reg_list(hm2172, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(hm2172->sd.ctrl_handler); + if (ret) + return ret; + + ret = hm2172_write_reg(hm2172, HM2172_REG_MODE_SELECT, 1, + HM2172_MODE_STREAMING); + if (ret) + dev_err(&client->dev, "failed to start streaming"); + + return ret; +} + +static void hm2172_stop_streaming(struct hm2172 *hm2172) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + + if (hm2172_write_reg(hm2172, HM2172_REG_MODE_SELECT, 1, + HM2172_MODE_STANDBY)) + dev_err(&client->dev, "failed to stop streaming"); +} + +static int hm2172_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct hm2172 *hm2172 = to_hm2172(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (hm2172->streaming == enable) + return 0; + + mutex_lock(&hm2172->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&hm2172->mutex); + return ret; + } + + ret = hm2172_start_streaming(hm2172); + if (ret) { + enable = 0; + hm2172_stop_streaming(hm2172); + pm_runtime_put(&client->dev); + } + } else { + hm2172_stop_streaming(hm2172); + pm_runtime_put(&client->dev); + } + + hm2172->streaming = enable; + mutex_unlock(&hm2172->mutex); + + return ret; +} + +static int hm2172_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2172 *hm2172 = to_hm2172(sd); + int ret = 0; + + gpiod_set_value_cansleep(hm2172->reset, 1); + gpiod_set_value_cansleep(hm2172->handshake, 0); + + if (hm2172->avdd) + ret = regulator_disable(hm2172->avdd); + + clk_disable_unprepare(hm2172->img_clk); + + return ret; +} + +static int hm2172_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2172 *hm2172 = to_hm2172(sd); + int ret; + + ret = clk_prepare_enable(hm2172->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + if (hm2172->avdd) { + ret = regulator_enable(hm2172->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(hm2172->img_clk); + return ret; + } + } + + gpiod_set_value_cansleep(hm2172->handshake, 1); + gpiod_set_value_cansleep(hm2172->reset, 0); + + /* Lattice MIPI aggregator with some version FW needs longer delay + after handshake triggered. We set 25ms as a safe value and wait + for a stable version FW. */ + msleep_interruptible(25); + + return ret; +} + +// This function tries to get power control resources +static int hm2172_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2172 *hm2172 = to_hm2172(sd); + int ret = 0; + + hm2172->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(hm2172->reset)) + return dev_err_probe(dev, PTR_ERR(hm2172->reset), + "failed to get reset gpio\n"); + + hm2172->handshake = devm_gpiod_get_optional(dev, "handshake", + GPIOD_OUT_LOW); + if (IS_ERR(hm2172->handshake)) + return dev_err_probe(dev, PTR_ERR(hm2172->handshake), + "failed to get handshake gpio\n"); + + hm2172->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hm2172->img_clk)) + return dev_err_probe(dev, PTR_ERR(hm2172->img_clk), + "failed to get imaging clock\n"); + + hm2172->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(hm2172->avdd)) { + ret = PTR_ERR(hm2172->avdd); + hm2172->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + return ret; +} + +static int __maybe_unused hm2172_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2172 *hm2172 = to_hm2172(sd); + + mutex_lock(&hm2172->mutex); + if (hm2172->streaming) + hm2172_stop_streaming(hm2172); + + mutex_unlock(&hm2172->mutex); + + return 0; +} + +static int __maybe_unused hm2172_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct hm2172 *hm2172 = to_hm2172(sd); + int ret = 0; + + mutex_lock(&hm2172->mutex); + if (!hm2172->streaming) + goto exit; + + ret = hm2172_start_streaming(hm2172); + if (ret) { + hm2172->streaming = false; + hm2172_stop_streaming(hm2172); + } + +exit: + mutex_unlock(&hm2172->mutex); + return ret; +} + +static int hm2172_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct hm2172 *hm2172 = to_hm2172(sd); + const struct hm2172_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->format.width, + fmt->format.height); + + mutex_lock(&hm2172->mutex); + hm2172_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + } else { + hm2172->cur_mode = mode; + __v4l2_ctrl_s_ctrl(hm2172->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(hm2172->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(hm2172->vblank, + mode->vts_min - mode->height, + HM2172_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(hm2172->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(hm2172->hblank, h_blank, h_blank, 1, + h_blank); + } + mutex_unlock(&hm2172->mutex); + + return 0; +} + +static int hm2172_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct hm2172 *hm2172 = to_hm2172(sd); + + mutex_lock(&hm2172->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&hm2172->sd, + sd_state, fmt->pad); + else + hm2172_update_pad_format(hm2172->cur_mode, &fmt->format); + + mutex_unlock(&hm2172->mutex); + + return 0; +} + +static int hm2172_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int hm2172_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int hm2172_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct hm2172 *hm2172 = to_hm2172(sd); + + mutex_lock(&hm2172->mutex); + hm2172_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&hm2172->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops hm2172_video_ops = { + .s_stream = hm2172_set_stream, +}; + +static const struct v4l2_subdev_pad_ops hm2172_pad_ops = { + .set_fmt = hm2172_set_format, + .get_fmt = hm2172_get_format, + .enum_mbus_code = hm2172_enum_mbus_code, + .enum_frame_size = hm2172_enum_frame_size, +}; + +static const struct v4l2_subdev_ops hm2172_subdev_ops = { + .video = &hm2172_video_ops, + .pad = &hm2172_pad_ops, +}; + +static const struct media_entity_operations hm2172_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops hm2172_internal_ops = { + .open = hm2172_open, +}; + +static int hm2172_identify_module(struct hm2172 *hm2172) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hm2172->sd); + int ret; + u32 val; + char rev; + + ret = hm2172_read_reg(hm2172, HM2172_REG_CHIP_ID, 3, &val); + + if (ret) + return ret; + + rev = val & 0xff; + val = val >> 8; + if (val != HM2172_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + HM2172_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int hm2172_remove(struct i2c_client *client) +#else +static void hm2172_remove(struct i2c_client *client) +#endif +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hm2172 *hm2172 = to_hm2172(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&hm2172->mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +static int hm2172_probe(struct i2c_client *client) +{ + struct hm2172 *hm217; + int ret; + + hm217 = devm_kzalloc(&client->dev, sizeof(*hm217), GFP_KERNEL); + if (!hm217) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&hm217->sd, client, &hm2172_subdev_ops); + hm2172_get_pm_resources(&client->dev); + + ret = hm2172_power_on(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + goto error_power_off; + } + + /* Check module identity */ + ret = hm2172_identify_module(hm217); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + hm217->cur_mode = &supported_modes[0]; + + ret = hm2172_init_controls(hm217); + if (ret) + return ret; + + /* Initialize subdev */ + hm217->sd.internal_ops = &hm2172_internal_ops; + hm217->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + hm217->sd.entity.ops = &hm2172_subdev_entity_ops; + hm217->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + hm217->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&hm217->sd.entity, 1, &hm217->pad); + if (ret) { + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&hm217->sd); + if (ret < 0) + goto error_media_entity; + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&hm217->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(hm217->sd.ctrl_handler); + mutex_destroy(&hm217->mutex); + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); +error_power_off: + hm2172_power_off(&client->dev); + + return ret; +} + +static const struct dev_pm_ops hm2172_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hm2172_suspend, hm2172_resume) + SET_RUNTIME_PM_OPS(hm2172_power_off, hm2172_power_on, NULL) +}; + +static const struct acpi_device_id hm2172_acpi_ids[] = { + {"HIMX2172"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, hm2172_acpi_ids); + +static struct i2c_driver hm2172_i2c_driver = { + .driver = { + .name = "hm2172", + .pm = &hm2172_pm_ops, + .acpi_match_table = hm2172_acpi_ids, + }, + .probe_new = hm2172_probe, + .remove = hm2172_remove, +}; + +module_i2c_driver(hm2172_i2c_driver); + +MODULE_AUTHOR("Jiabin He "); +MODULE_DESCRIPTION("Himax HM2172 sensor driver"); +MODULE_LICENSE("GPL v2"); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/Makefile ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/Makefile --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/Makefile 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/Makefile 2023-10-18 15:30:24.000000000 +0800 @@ -2,10 +2,13 @@ # Copyright (c) 2021 Intel Corporation. obj-$(CONFIG_VIDEO_HM11B1) += hm11b1.o +obj-$(CONFIG_VIDEO_GC5035) += gc5035.o obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o +obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_HM2170) += hm2170.o +obj-$(CONFIG_VIDEO_HM2170) += hm2172.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_POWER_CTRL_LOGIC) += power_ctrl_logic.o diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov01a10.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov01a10.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov01a10.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov01a10.c 2023-10-18 15:30:24.000000000 +0800 @@ -295,6 +295,12 @@ /* To serialize asynchronus callbacks */ struct mutex mutex; +#if IS_ENABLED(CONFIG_INTEL_VSC) + struct vsc_mipi_config conf; + struct vsc_camera_status status; + struct v4l2_ctrl *privacy_status; +#endif + /* Streaming on/off */ bool streaming; }; @@ -461,6 +467,12 @@ ret = ov01a10_test_pattern(ov01a10, ctrl->val); break; +#if IS_ENABLED(CONFIG_INTEL_VSC) + case V4L2_CID_PRIVACY: + dev_dbg(&client->dev, "set privacy to %d", ctrl->val); + break; +#endif + default: ret = -EINVAL; break; @@ -485,7 +497,11 @@ int ret = 0; ctrl_hdlr = &ov01a10->ctrl_handler; +#if IS_ENABLED(CONFIG_INTEL_VSC) + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); +#else ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); +#endif if (ret) return ret; @@ -519,6 +535,13 @@ if (ov01a10->hblank) ov01a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; +#if IS_ENABLED(CONFIG_INTEL_VSC) + ov01a10->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr, + &ov01a10_ctrl_ops, + V4L2_CID_PRIVACY, 0, 1, 1, + !(ov01a10->status.status)); +#endif + v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, OV01A10_ANAL_GAIN_MIN, OV01A10_ANAL_GAIN_MAX, OV01A10_ANAL_GAIN_STEP, OV01A10_ANAL_GAIN_MIN); @@ -559,19 +582,7 @@ const struct ov01a10_reg_list *reg_list; int link_freq_index; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - conf.lane_num = OV01A10_DATA_LANES; - /* frequency unit 100k */ - conf.freq = OV01A10_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); - if (ret) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } -#endif link_freq_index = ov01a10->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov01a10_write_reg_list(ov01a10, reg_list); @@ -603,19 +614,11 @@ { struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_camera_status status; -#endif ret = ov01a10_write_reg(ov01a10, OV01A10_REG_MODE_SELECT, 1, OV01A10_MODE_STANDBY); if (ret) dev_err(&client->dev, "failed to stop streaming"); -#if IS_ENABLED(CONFIG_INTEL_VSC) - ret = vsc_release_camera_sensor(&status); - if (ret) - dev_err(&client->dev, "Release VSC failed"); -#endif } static int ov01a10_set_stream(struct v4l2_subdev *sd, int enable) @@ -653,6 +656,43 @@ return ret; } +#if IS_ENABLED(CONFIG_INTEL_VSC) +static void ov01a10_vsc_privacy_callback(void *handle, + enum vsc_privacy_status status) +{ + struct ov01a10 *ov01a10 = handle; + + v4l2_ctrl_s_ctrl(ov01a10->privacy_status, !status); +} + +static int ov01a10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a10 *ov01a10 = to_ov01a10(sd); + int ret; + + ret = vsc_release_camera_sensor(&ov01a10->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Release VSC failed"); + + return ret; +} + +static int ov01a10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a10 *ov01a10 = to_ov01a10(sd); + int ret; + + ret = vsc_acquire_camera_sensor(&ov01a10->conf, NULL, + ov01a10, &ov01a10->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Acquire VSC failed"); + + return ret; +} +#endif + static int __maybe_unused ov01a10_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -885,31 +925,26 @@ { struct ov01a10 *ov01a10; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; -#endif + ov01a10 = devm_kzalloc(&client->dev, sizeof(*ov01a10), GFP_KERNEL); + if (!ov01a10) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); #if IS_ENABLED(CONFIG_INTEL_VSC) - conf.lane_num = OV01A10_DATA_LANES; + ov01a10->conf.lane_num = OV01A10_DATA_LANES; /* frequency unit 100k */ - conf.freq = OV01A10_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); + ov01a10->conf.freq = OV01A10_LINK_FREQ_400MHZ / 100000; + ret = vsc_acquire_camera_sensor(&ov01a10->conf, + ov01a10_vsc_privacy_callback, + ov01a10, &ov01a10->status); if (ret == -EAGAIN) { - dev_dbg(&client->dev, "VSC not ready, will re-probe"); return -EPROBE_DEFER; } else if (ret) { dev_err(&client->dev, "Acquire VSC failed"); return ret; } #endif - ov01a10 = devm_kzalloc(&client->dev, sizeof(*ov01a10), GFP_KERNEL); - if (!ov01a10) { - ret = -ENOMEM; - goto probe_error_ret; - } - - v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); ret = ov01a10_identify_module(ov01a10); if (ret) { @@ -943,9 +978,6 @@ goto probe_error_media_entity_cleanup; } -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -965,13 +997,17 @@ probe_error_ret: #if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); + ov01a10_power_off(&client->dev); #endif + return ret; } static const struct dev_pm_ops ov01a10_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov01a10_suspend, ov01a10_resume) +#if IS_ENABLED(CONFIG_INTEL_VSC) + SET_RUNTIME_PM_OPS(ov01a10_power_off, ov01a10_power_on, NULL) +#endif }; #ifdef CONFIG_ACPI diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov01a1s.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov01a1s.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov01a1s.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov01a1s.c 2023-10-18 15:30:24.000000000 +0800 @@ -304,6 +304,10 @@ struct v4l2_ctrl *exposure; #if IS_ENABLED(CONFIG_INTEL_VSC) struct v4l2_ctrl *privacy_status; + + /* VSC settings */ + struct vsc_mipi_config conf; + struct vsc_camera_status status; #endif /* Current mode */ @@ -320,11 +324,21 @@ struct gpio_desc *reset_gpio; /* GPIO for powerdown */ struct gpio_desc *powerdown_gpio; - /* GPIO for clock enable */ - struct gpio_desc *clken_gpio; - /* GPIO for privacy LED */ - struct gpio_desc *pled_gpio; + /* Power enable */ + struct regulator *avdd; + /* Clock provider */ + struct clk *clk; +#endif + + enum { + OV01A1S_USE_DEFAULT = 0, +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + OV01A1S_USE_INT3472 = 1, +#endif +#if IS_ENABLED(CONFIG_INTEL_VSC) + OV01A1S_USE_INTEL_VSC = 2, #endif + } power_type; /* Streaming on/off */ bool streaming; @@ -335,21 +349,6 @@ return container_of(subdev, struct ov01a1s, sd); } -static void ov01a1s_set_power(struct ov01a1s *ov01a1s, int on) -{ -#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) - if (!(ov01a1s->reset_gpio && ov01a1s->powerdown_gpio)) - return; - gpiod_set_value_cansleep(ov01a1s->reset_gpio, on); - gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, on); - gpiod_set_value_cansleep(ov01a1s->clken_gpio, on); - gpiod_set_value_cansleep(ov01a1s->pled_gpio, on); - msleep(20); -#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) - power_ctrl_logic_set_power(on); -#endif -} - static int ov01a1s_read_reg(struct ov01a1s *ov01a1s, u16 reg, u16 len, u32 *val) { struct i2c_client *client = ov01a1s->client; @@ -630,22 +629,7 @@ const struct ov01a1s_reg_list *reg_list; int link_freq_index; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - conf.lane_num = OV01A1S_DATA_LANES; - /* frequency unit 100k */ - conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, ov01a1s_vsc_privacy_callback, - ov01a1s, &status); - if (ret && ret != -EAGAIN) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } - __v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, !(status.status)); -#endif - ov01a1s_set_power(ov01a1s, 1); link_freq_index = ov01a1s->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov01a1s_write_reg_list(ov01a1s, reg_list); @@ -677,20 +661,11 @@ { struct i2c_client *client = ov01a1s->client; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_camera_status status; -#endif ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_MODE_SELECT, 1, OV01A1S_MODE_STANDBY); if (ret) dev_err(&client->dev, "failed to stop streaming"); -#if IS_ENABLED(CONFIG_INTEL_VSC) - ret = vsc_release_camera_sensor(&status); - if (ret && ret != -EAGAIN) - dev_err(&client->dev, "Release VSC failed"); -#endif - ov01a1s_set_power(ov01a1s, 0); } static int ov01a1s_set_stream(struct v4l2_subdev *sd, int enable) @@ -728,6 +703,76 @@ return ret; } +static int ov01a1s_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a1s *ov01a1s = to_ov01a1s(sd); + int ret = 0; + +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) + if (ov01a1s->power_type == OV01A1S_USE_INT3472) { + gpiod_set_value_cansleep(ov01a1s->reset_gpio, 1); + gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 1); + if (ov01a1s->avdd) + ret = regulator_disable(ov01a1s->avdd); + clk_disable_unprepare(ov01a1s->clk); + msleep(20); + } +#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + if (ov01a1s->power_type == OV01A1S_USE_INT3472) + ret = power_ctrl_logic_set_power(0); +#endif +#if IS_ENABLED(CONFIG_INTEL_VSC) + if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) { + ret = vsc_release_camera_sensor(&ov01a1s->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Release VSC failed"); + } +#endif + + return ret; +} + +static int ov01a1s_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a1s *ov01a1s = to_ov01a1s(sd); + int ret = 0; + +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) + if (ov01a1s->power_type == OV01A1S_USE_INT3472) { + ret = clk_prepare_enable(ov01a1s->clk); + if (ret) + return ret; + if (ov01a1s->avdd) + ret = regulator_enable(ov01a1s->avdd); + if (ret) + return ret; + gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 0); + gpiod_set_value_cansleep(ov01a1s->reset_gpio, 0); + msleep(20); + } +#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + if (ov01a1s->power_type == OV01A1S_USE_INT3472) + ret = power_ctrl_logic_set_power(1); +#endif +#if IS_ENABLED(CONFIG_INTEL_VSC) + if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) { + ret = vsc_acquire_camera_sensor(&ov01a1s->conf, + ov01a1s_vsc_privacy_callback, + ov01a1s, &ov01a1s->status); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Acquire VSC failed"); + return ret; + } + __v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, + !(ov01a1s->status.status)); + } +#endif + + return ret; +} + static int __maybe_unused ov01a1s_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -957,79 +1002,97 @@ } #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) -static int ov01a1s_parse_dt(struct ov01a1s *ov01a1s) +static int ov01a1s_parse_gpio(struct ov01a1s *ov01a1s) { struct device *dev = &ov01a1s->client->dev; - int ret; - ov01a1s->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov01a1s->reset_gpio); - if (ret < 0) { - dev_err(dev, "error while getting reset gpio: %d\n", ret); + ov01a1s->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov01a1s->reset_gpio)) { + dev_warn(dev, "error while getting reset gpio: %ld\n", + PTR_ERR(ov01a1s->reset_gpio)); + ov01a1s->reset_gpio = NULL; return -EPROBE_DEFER; } - ov01a1s->powerdown_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov01a1s->powerdown_gpio); - if (ret < 0) { - dev_err(dev, "error while getting powerdown gpio: %d\n", ret); - return -EPROBE_DEFER; + /* For optional, don't return or print warn if can't get it */ + ov01a1s->powerdown_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); + if (IS_ERR(ov01a1s->powerdown_gpio)) { + dev_dbg(dev, "no powerdown gpio: %ld\n", + PTR_ERR(ov01a1s->powerdown_gpio)); + ov01a1s->powerdown_gpio = NULL; } - ov01a1s->clken_gpio = devm_gpiod_get(dev, "clken", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov01a1s->clken_gpio); - if (ret < 0) { - dev_err(dev, "error while getting clken_gpio gpio: %d\n", ret); - return -EPROBE_DEFER; + ov01a1s->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov01a1s->avdd)) { + dev_dbg(dev, "no regulator avdd: %ld\n", + PTR_ERR(ov01a1s->avdd)); + ov01a1s->avdd = NULL; } - ov01a1s->pled_gpio = devm_gpiod_get(dev, "pled", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov01a1s->pled_gpio); - if (ret < 0) { - dev_err(dev, "error while getting pled gpio: %d\n", ret); - return -EPROBE_DEFER; + ov01a1s->clk = devm_clk_get_optional(dev, "clk"); + if (IS_ERR(ov01a1s->clk)) { + dev_dbg(dev, "no clk: %ld\n", PTR_ERR(ov01a1s->clk)); + ov01a1s->clk = NULL; } return 0; } #endif -static int ov01a1s_probe(struct i2c_client *client) +static int ov01a1s_parse_power(struct ov01a1s *ov01a1s) { - struct ov01a1s *ov01a1s; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - s64 link_freq; -#endif - - ov01a1s = devm_kzalloc(&client->dev, sizeof(*ov01a1s), GFP_KERNEL); - if (!ov01a1s) - return -ENOMEM; - ov01a1s->client = client; #if IS_ENABLED(CONFIG_INTEL_VSC) - conf.lane_num = OV01A1S_DATA_LANES; + ov01a1s->conf.lane_num = OV01A1S_DATA_LANES; /* frequency unit 100k */ - conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); + ov01a1s->conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000; + ret = vsc_acquire_camera_sensor(&ov01a1s->conf, NULL, NULL, &ov01a1s->status); + if (!ret) { + ov01a1s->power_type = OV01A1S_USE_INTEL_VSC; + return 0; + } else if (ret != -EAGAIN) { + return ret; + } #endif #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) - if (ret == -EAGAIN) - ret = ov01a1s_parse_dt(ov01a1s); + ret = ov01a1s_parse_gpio(ov01a1s); #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) - if (ret == -EAGAIN) - ret = power_ctrl_logic_set_power(1); + ret = power_ctrl_logic_set_power(1); +#endif +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) + if (!ret) { + ov01a1s->power_type = OV01A1S_USE_INT3472; + return 0; + } #endif if (ret == -EAGAIN) return -EPROBE_DEFER; - else if (ret) - return ret; - ov01a1s_set_power(ov01a1s, 1); + return ret; +} + +static int ov01a1s_probe(struct i2c_client *client) +{ + struct ov01a1s *ov01a1s; + int ret = 0; + + ov01a1s = devm_kzalloc(&client->dev, sizeof(*ov01a1s), GFP_KERNEL); + if (!ov01a1s) + return -ENOMEM; + + ov01a1s->client = client; + ret = ov01a1s_parse_power(ov01a1s); + if (ret) + return ret; v4l2_i2c_subdev_init(&ov01a1s->sd, client, &ov01a1s_subdev_ops); +#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) + /* In other cases, power is up in ov01a1s_parse_power */ + if (ov01a1s->power_type == OV01A1S_USE_INT3472) + ov01a1s_power_on(&client->dev); +#endif ret = ov01a1s_identify_module(ov01a1s); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); @@ -1066,9 +1129,6 @@ goto probe_error_media_entity_cleanup; } -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1077,7 +1137,6 @@ pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); - ov01a1s_set_power(ov01a1s, 0); return 0; probe_error_media_entity_cleanup: @@ -1088,15 +1147,14 @@ mutex_destroy(&ov01a1s->mutex); probe_error_power_off: -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif - ov01a1s_set_power(ov01a1s, 0); + ov01a1s_power_off(&client->dev); + return ret; } static const struct dev_pm_ops ov01a1s_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov01a1s_suspend, ov01a1s_resume) + SET_RUNTIME_PM_OPS(ov01a1s_power_off, ov01a1s_power_on, NULL) }; #ifdef CONFIG_ACPI diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov02c10.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov02c10.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov02c10.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov02c10.c 2023-10-18 15:30:24.000000000 +0800 @@ -52,6 +52,12 @@ #define OV02C10_DGTL_GAIN_STEP 1 #define OV02C10_DGTL_GAIN_DEFAULT 0x0400 +/* Rotate */ +#define OV02C10_ROTATE_CONTROL 0x3820 +#define OV02C10_ISP_X_WIN_CONTROL 0x3811 +#define OV02C10_ISP_Y_WIN_CONTROL 0x3813 +#define OV02C10_CONFIG_ROTATE 0x18 + /* Test Pattern Control */ #define OV02C10_REG_TEST_PATTERN 0x4503 #define OV02C10_TEST_PATTERN_ENABLE BIT(7) @@ -61,6 +67,14 @@ OV02C10_LINK_FREQ_400MHZ_INDEX, }; +enum module_names { + MODULE_EMPTY = 0, + MODULE_2BG203N3, + MODULE_CJFME32, + /* etc. */ + MODULE_MAX +}; + struct ov02c10_reg { u16 address; u8 val; @@ -130,6 +144,15 @@ u8 reserved2[13]; } __packed; +/* + * 822ace8f-2814-4174-a56b-5f029fe079ee + * This _DSM GUID returns a string from the sensor device, which acts as a + * module identifier. + */ +static const guid_t cio2_sensor_module_guid = + GUID_INIT(0x822ace8f, 0x2814, 0x4174, + 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); + static const struct ov02c10_reg mipi_data_rate_960mbps[] = { }; @@ -598,6 +621,12 @@ "Color Bar type 4", }; +static const char * const ov02c10_module_names[] = { + [MODULE_EMPTY] = "", + [MODULE_CJFME32] = "CJFME32", + [MODULE_2BG203N3] = "2BG203N3", +}; + static const s64 link_freq_menu_items[] = { OV02C10_LINK_FREQ_400MHZ, }; @@ -652,6 +681,8 @@ struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; #if IS_ENABLED(CONFIG_INTEL_VSC) + struct vsc_mipi_config conf; + struct vsc_camera_status status; struct v4l2_ctrl *privacy_status; #endif /* Current mode */ @@ -665,6 +696,9 @@ /* Streaming on/off */ bool streaming; + + /* Module name index */ + u8 module_name_index; }; static inline struct ov02c10 *to_ov02c10(struct v4l2_subdev *subdev) @@ -868,8 +902,10 @@ if (ov02c10->hblank) ov02c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; #if IS_ENABLED(CONFIG_INTEL_VSC) - ov02c10->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, - V4L2_CID_PRIVACY, 0, 1, 1, 0); + ov02c10->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr, + &ov02c10_ctrl_ops, + V4L2_CID_PRIVACY, 0, 1, 1, + !(ov02c10->status.status)); #endif v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, @@ -906,37 +942,13 @@ fmt->field = V4L2_FIELD_NONE; } -#if IS_ENABLED(CONFIG_INTEL_VSC) -static void ov02c10_vsc_privacy_callback(void *handle, - enum vsc_privacy_status status) -{ - struct ov02c10 *ov02c10 = handle; - - v4l2_ctrl_s_ctrl(ov02c10->privacy_status, !status); -} -#endif - static int ov02c10_start_streaming(struct ov02c10 *ov02c10) { struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); const struct ov02c10_reg_list *reg_list; int link_freq_index; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; - conf.lane_num = ov02c10->mipi_lanes; - /* frequency unit 100k */ - conf.freq = OV02C10_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, ov02c10_vsc_privacy_callback, - ov02c10, &status); - if (ret) { - dev_err(&client->dev, "Acquire VSC failed"); - return ret; - } - __v4l2_ctrl_s_ctrl(ov02c10->privacy_status, !(status.status)); -#endif link_freq_index = ov02c10->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov02c10_write_reg_list(ov02c10, reg_list); @@ -956,6 +968,51 @@ if (ret) return ret; + if ((ov02c10->module_name_index == MODULE_CJFME32) || + (ov02c10->module_name_index == MODULE_2BG203N3)) { + u32 rotateReg, shiftXReg, shiftYReg; + + ret = ov02c10_read_reg(ov02c10, OV02C10_ROTATE_CONTROL, + 1, &rotateReg); + if (ret) + dev_err(&client->dev, + "read ROTATE_CONTROL fail: %d", ret); + + ret = ov02c10_read_reg(ov02c10, OV02C10_ISP_X_WIN_CONTROL, + 1, &shiftXReg); + if (ret) + dev_err(&client->dev, + "read ISP_X_WIN_CONTROL fail: %d", ret); + + ret = ov02c10_read_reg(ov02c10, OV02C10_ISP_Y_WIN_CONTROL, + 1, &shiftYReg); + if (ret) + dev_err(&client->dev, + "read ISP_Y_WIN_CONTROL fail: %d", ret); + + rotateReg ^= OV02C10_CONFIG_ROTATE; + shiftXReg = shiftXReg - 1; + shiftYReg = shiftYReg - 1; + + ret = ov02c10_write_reg(ov02c10, OV02C10_ROTATE_CONTROL, + 1, rotateReg); + if (ret) + dev_err(&client->dev, + "write ROTATE_CONTROL fail: %d", ret); + + ret = ov02c10_write_reg(ov02c10, OV02C10_ISP_X_WIN_CONTROL, + 1, shiftXReg); + if (ret) + dev_err(&client->dev, + "write ISP_X_WIN_CONTROL fail: %d", ret); + + ret = ov02c10_write_reg(ov02c10, OV02C10_ISP_Y_WIN_CONTROL, + 1, shiftYReg); + if (ret) + dev_err(&client->dev, + "write ISP_Y_WIN_CONTROL fail: %d", ret); + } + ret = ov02c10_write_reg(ov02c10, OV02C10_REG_MODE_SELECT, 1, OV02C10_MODE_STREAMING); if (ret) @@ -968,19 +1025,11 @@ { struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_camera_status status; -#endif ret = ov02c10_write_reg(ov02c10, OV02C10_REG_MODE_SELECT, 1, OV02C10_MODE_STANDBY); if (ret) dev_err(&client->dev, "failed to stop streaming"); -#if IS_ENABLED(CONFIG_INTEL_VSC) - ret = vsc_release_camera_sensor(&status); - if (ret) - dev_err(&client->dev, "Release VSC failed"); -#endif } static int ov02c10_set_stream(struct v4l2_subdev *sd, int enable) @@ -1018,6 +1067,50 @@ return ret; } +#if IS_ENABLED(CONFIG_INTEL_VSC) +static void ov02c10_vsc_privacy_callback(void *handle, + enum vsc_privacy_status status) +{ + struct ov02c10 *ov02c10 = handle; + + v4l2_ctrl_s_ctrl(ov02c10->privacy_status, !status); +} + +static int ov02c10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int ret; + + ret = vsc_release_camera_sensor(&ov02c10->status); + if (ret && ret != -EAGAIN) + dev_err(dev, "Release VSC failed"); + + return ret; +} + +static int ov02c10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int ret; + + ov02c10->conf.lane_num = ov02c10->mipi_lanes; + /* frequency unit 100k */ + ov02c10->conf.freq = OV02C10_LINK_FREQ_400MHZ / 100000; + ret = vsc_acquire_camera_sensor(&ov02c10->conf, + ov02c10_vsc_privacy_callback, + ov02c10, &ov02c10->status); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Acquire VSC failed"); + return ret; + } + __v4l2_ctrl_s_ctrl(ov02c10->privacy_status, !(ov02c10->status.status)); + + return ret; +} +#endif + static int __maybe_unused ov02c10_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1222,7 +1315,6 @@ union acpi_object *obj; acpi_status status; - ov02c10->mipi_lanes = OV02C10_DATA_LANES; if (!adev) { dev_info(&client->dev, "Not ACPI device\n"); return; @@ -1294,35 +1386,62 @@ #endif } +static int ov02c10_read_module_name(struct ov02c10 *ov02c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); + struct device *dev = &client->dev; + int i = 0; + union acpi_object *obj; + struct acpi_device *adev = ACPI_COMPANION(dev); + + ov02c10->module_name_index = 0; + if (!adev) + return 0; + + obj = acpi_evaluate_dsm_typed(adev->handle, + &cio2_sensor_module_guid, 0x00, + 0x01, NULL, ACPI_TYPE_STRING); + + if (obj && obj->string.type == ACPI_TYPE_STRING) { + for (i = 1; i < ARRAY_SIZE(ov02c10_module_names); i++) { + if (!strcmp(ov02c10_module_names[i], obj->string.pointer)) { + ov02c10->module_name_index = i; + break; + } + } + } + ACPI_FREE(obj); + + return 0; +} + static int ov02c10_probe(struct i2c_client *client) { struct ov02c10 *ov02c10; int ret = 0; -#if IS_ENABLED(CONFIG_INTEL_VSC) - struct vsc_mipi_config conf; - struct vsc_camera_status status; -#endif + + ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL); + if (!ov02c10) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov02c10->sd, client, &ov02c10_subdev_ops); + ov02c10_read_module_name(ov02c10); #if IS_ENABLED(CONFIG_INTEL_VSC) - conf.lane_num = OV02C10_DATA_LANES; + ov02c10->mipi_lanes = OV02C10_DATA_LANES; + ov02c10->conf.lane_num = ov02c10->mipi_lanes; /* frequency unit 100k */ - conf.freq = OV02C10_LINK_FREQ_400MHZ / 100000; - ret = vsc_acquire_camera_sensor(&conf, NULL, NULL, &status); + ov02c10->conf.freq = OV02C10_LINK_FREQ_400MHZ / 100000; + ret = vsc_acquire_camera_sensor(&ov02c10->conf, + ov02c10_vsc_privacy_callback, + ov02c10, &ov02c10->status); if (ret == -EAGAIN) { - dev_dbg(&client->dev, "VSC not ready, will re-probe"); return -EPROBE_DEFER; } else if (ret) { dev_err(&client->dev, "Acquire VSC failed"); return ret; } #endif - ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL); - if (!ov02c10) { - ret = -ENOMEM; - goto probe_error_ret; - } - - v4l2_i2c_subdev_init(&ov02c10->sd, client, &ov02c10_subdev_ops); ret = ov02c10_identify_module(ov02c10); if (ret) { @@ -1359,9 +1478,6 @@ goto probe_error_media_entity_cleanup; } -#if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); -#endif /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1381,13 +1497,17 @@ probe_error_ret: #if IS_ENABLED(CONFIG_INTEL_VSC) - vsc_release_camera_sensor(&status); + ov02c10_power_off(&client->dev); #endif + return ret; } static const struct dev_pm_ops ov02c10_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov02c10_suspend, ov02c10_resume) +#if IS_ENABLED(CONFIG_INTEL_VSC) + SET_RUNTIME_PM_OPS(ov02c10_power_off, ov02c10_power_on, NULL) +#endif }; #ifdef CONFIG_ACPI diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov02e10.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov02e10.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov02e10.c 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov02e10.c 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV02E10_LINK_FREQ_360MHZ 360000000ULL +#define OV02E10_SCLK 36000000LL +#define OV02E10_MCLK 19200000 +#define OV02E10_DATA_LANES 2 +#define OV02E10_RGB_DEPTH 10 + +#define OV02E10_REG_DELAY 0xffff + +#define OV02E10_REG_PAGE_FLAG 0xfd +#define OV02E10_PAGE_0 0x0 +#define OV02E10_PAGE_1 0x1 +#define OV02E10_PAGE_2 0x2 +#define OV02E10_PAGE_3 0x3 +#define OV02E10_PAGE_5 0x4 +#define OV02E10_PAGE_7 0x5 +#define OV02E10_PAGE_8 0x6 +#define OV02E10_PAGE_9 0xF +#define OV02E10_PAGE_D 0x8 +#define OV02E10_PAGE_E 0x9 +#define OV02E10_PAGE_F 0xA + +#define OV02E10_REG_CHIP_ID 0x00 +#define OV02E10_CHIP_ID 0x45025610 + +#define OV02E10_REG_MODE_SELECT 0x0100 +#define OV02E10_MODE_STANDBY 0x00 +#define OV02E10_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV02E10_REG_VTS 0x35 +#define OV02E10_VTS_DEF 2244 +#define OV02E10_VTS_MIN 2244 +#define OV02E10_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV02E10_REG_HTS 0x37 + +/* Exposure controls from sensor */ +#define OV02E10_REG_EXPOSURE 0x03 +#define OV02E10_EXPOSURE_MIN 1 +#define OV02E10_EXPOSURE_MAX_MARGIN 2 +#define OV02E10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV02E10_REG_ANALOG_GAIN 0x24 +#define OV02E10_ANAL_GAIN_MIN 0x10 +#define OV02E10_ANAL_GAIN_MAX 0xf8 +#define OV02E10_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV02E10_REG_DIGITAL_GAIN 0x21 +#define OV02E10_DGTL_GAIN_MIN 256 +#define OV02E10_DGTL_GAIN_MAX 1020 +#define OV02E10_DGTL_GAIN_STEP 1 +#define OV02E10_DGTL_GAIN_DEFAULT 256 + +/* Register update control */ +#define OV02E10_REG_COMMAND_UPDATE 0xE7 +#define OV02E10_COMMAND_UPDATE 0x00 +#define OV02E10_COMMAND_HOLD 0x01 + +/* Test Pattern Control */ +#define OV02E10_REG_TEST_PATTERN 0x12 +#define OV02E10_TEST_PATTERN_ENABLE BIT(0) +#define OV02E10_TEST_PATTERN_BAR_SHIFT 1 + +enum { + OV02E10_LINK_FREQ_360MHZ_INDEX, +}; + +struct ov02e10_reg { + u16 address; + u8 val; +}; + +struct ov02e10_reg_list { + u32 num_of_regs; + const struct ov02e10_reg *regs; +}; + +struct ov02e10_link_freq_config { + const struct ov02e10_reg_list reg_list; +}; + +struct ov02e10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov02e10_reg_list reg_list; +}; + +static const struct ov02e10_reg ov02e10_standby[] = { + { OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_0 }, + { 0xa0, 0x00 }, + { OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_1 }, + { 0x01, 0x02 } +}; + +static const struct ov02e10_reg_list ov02e10_standby_list = { + .num_of_regs = ARRAY_SIZE(ov02e10_standby), + .regs = ov02e10_standby +}; + +static const struct ov02e10_reg ov02e10_streaming[] = { + { OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_0 }, + { 0xa0, 0x01 }, + { OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_1 }, + { 0x01, 0x02 } +}; + +static const struct ov02e10_reg_list ov02e10_streaming_list = { + .num_of_regs = ARRAY_SIZE(ov02e10_streaming), + .regs = ov02e10_streaming +}; + +static const struct ov02e10_reg mode_1928x1088_30fps_2lane[] = { + { 0xfd, 0x00 }, + { 0x20, 0x00 }, + { 0x20, 0x0b }, + { 0x21, 0x02 }, + { 0x10, 0x23 }, + { 0xc5, 0x04 }, + { 0x21, 0x00 }, + { 0x14, 0x96 }, + { 0x17, 0x01 }, + { 0xfd, 0x01 }, + { 0x03, 0x00 }, + { 0x04, 0x04 }, + { 0x05, 0x04 }, + { 0x06, 0x62 }, + { 0x07, 0x01 }, + { 0x22, 0x80 }, + { 0x24, 0xff }, + { 0x40, 0xc6 }, + { 0x41, 0x18 }, + { 0x45, 0x3f }, + { 0x48, 0x0c }, + { 0x4c, 0x08 }, + { 0x51, 0x12 }, + { 0x52, 0x10 }, + { 0x57, 0x98 }, + { 0x59, 0x06 }, + { 0x5a, 0x04 }, + { 0x5c, 0x38 }, + { 0x5e, 0x10 }, + { 0x67, 0x11 }, + { 0x7b, 0x04 }, + { 0x81, 0x12 }, + { 0x90, 0x51 }, + { 0x91, 0x09 }, + { 0x92, 0x21 }, + { 0x93, 0x28 }, + { 0x95, 0x54 }, + { 0x9d, 0x20 }, + { 0x9e, 0x04 }, + { 0xb1, 0x9a }, + { 0xb2, 0x86 }, + { 0xb6, 0x3f }, + { 0xb9, 0x30 }, + { 0xc1, 0x01 }, + { 0xc5, 0xa0 }, + { 0xc6, 0x73 }, + { 0xc7, 0x04 }, + { 0xc8, 0x25 }, + { 0xc9, 0x05 }, + { 0xca, 0x28 }, + { 0xcb, 0x00 }, + { 0xcf, 0x16 }, + { 0xd2, 0xd0 }, + { 0xd7, 0x3f }, + { 0xd8, 0x40 }, + { 0xd9, 0x40 }, + { 0xda, 0x44 }, + { 0xdb, 0x3d }, + { 0xdc, 0x3d }, + { 0xdd, 0x3d }, + { 0xde, 0x3d }, + { 0xdf, 0xf0 }, + { 0xea, 0x0f }, + { 0xeb, 0x04 }, + { 0xec, 0x29 }, + { 0xee, 0x47 }, + { 0xfd, 0x01 }, + { 0x31, 0x01 }, + { 0x27, 0x00 }, + { 0x2f, 0x41 }, + { 0xfd, 0x02 }, + { 0xa1, 0x01 }, + { 0xfd, 0x02 }, + { 0x9a, 0x03 }, + { 0xfd, 0x03 }, + { 0x9d, 0x0f }, + { 0xfd, 0x07 }, + { 0x42, 0x00 }, + { 0x43, 0xad }, + { 0x44, 0x00 }, + { 0x45, 0xa8 }, + { 0x46, 0x00 }, + { 0x47, 0xa8 }, + { 0x48, 0x00 }, + { 0x49, 0xad }, + { 0xfd, 0x00 }, + { 0xc4, 0x01 }, + { 0xa0, 0x01 }, + { 0xfd, 0x01 }, + { 0x33, 0x03 }, + { 0x01, 0x02 }, + { 0xfd, 0x00 }, + { 0x20, 0x1f }, +}; + +static const char *const ov02e10_test_pattern_menu[] = { + "Disabled", + "Color Bar", +}; + +static const s64 link_freq_menu_items[] = { + OV02E10_LINK_FREQ_360MHZ, +}; + +static const struct ov02e10_mode supported_modes[] = { + { + .width = 1928, + .height = 1088, + .hts = 534, + .vts_def = 2244, + .vts_min = 2244, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane), + .regs = mode_1928x1088_30fps_2lane, + }, + + .link_freq_index = OV02E10_LINK_FREQ_360MHZ_INDEX, + }, +}; + +struct ov02e10 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + struct clk *img_clk; + struct regulator *avdd; + struct gpio_desc *reset; + struct gpio_desc *handshake; + + /* Current mode */ + const struct ov02e10_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct ov02e10 *to_ov02e10(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov02e10, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV02E10_DATA_LANES; + + do_div(pixel_rate, OV02E10_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV02E10_SCLK); + + return ppl; +} + +static int ov02e10_read_reg(struct ov02e10 *ov02e10, u8 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + struct i2c_msg msgs[2]; + u8 data_buf[4] = { 0 }; + int ret = 0; + + if (len > sizeof(data_buf)) + return -EINVAL; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = ® + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[sizeof(data_buf) - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return ret < 0 ? ret : -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov02e10_write_reg(struct ov02e10 *ov02e10, u8 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + u8 buf[5]; + int ret = 0; + + if (len > 4) + return -EINVAL; + + if (reg == OV02E10_REG_DELAY) { + msleep(val); + return 0; + } + dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, + val); + buf[0] = reg; + put_unaligned_be32(val << 8 * (4 - len), buf + 1); + + ret = i2c_master_send(client, buf, len + 1); + if (ret != len + 1) { + dev_err(&client->dev, "failed to write reg %d val %d", reg, + val); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int ov02e10_write_reg_list(struct ov02e10 *ov02e10, + const struct ov02e10_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + unsigned int i; + int ret = 0; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov02e10_write_reg(ov02e10, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov02e10_test_pattern(struct ov02e10 *ov02e10, u32 pattern) +{ + if (pattern) + pattern = pattern << OV02E10_TEST_PATTERN_BAR_SHIFT | + OV02E10_TEST_PATTERN_ENABLE; + + return ov02e10_write_reg(ov02e10, (u8) OV02E10_REG_TEST_PATTERN, 1, + pattern); +} + +static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02e10 *ov02e10 = container_of(ctrl->handler, + struct ov02e10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov02e10->cur_mode->height + ctrl->val - + OV02E10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov02e10->exposure, + ov02e10->exposure->minimum, + exposure_max, ov02e10->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_COMMAND_UPDATE, 1, + OV02E10_COMMAND_HOLD); + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + dev_dbg(&client->dev, "set analog gain\n"); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_1); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_ANALOG_GAIN, 1, + ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + dev_dbg(&client->dev, "set digital gain\n"); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_1); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_DIGITAL_GAIN, 2, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + dev_dbg(&client->dev, "set exposure\n"); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_1); + ret = + ov02e10_write_reg(ov02e10, OV02E10_REG_EXPOSURE, 2, + ctrl->val); + break; + + case V4L2_CID_VBLANK: + dev_dbg(&client->dev, "set vblank\n"); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_1); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_VTS, 2, + ov02e10->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + dev_dbg(&client->dev, "set test pattern\n"); + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_1); + ret = ov02e10_test_pattern(ov02e10, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + dev_dbg(&client->dev, "will update cmd\n"); + ret |= ov02e10_write_reg(ov02e10, OV02E10_REG_COMMAND_UPDATE, 1, + OV02E10_COMMAND_UPDATE); + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov02e10_ctrl_ops = { + .s_ctrl = ov02e10_set_ctrl, +}; + +static int ov02e10_init_controls(struct ov02e10 *ov02e10) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct ov02e10_mode *cur_mode; + s64 exposure_max, h_blank, pixel_rate; + u32 vblank_min, vblank_max, vblank_default; + int size; + int ret = 0; + + ctrl_hdlr = &ov02e10->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + + if (ret) + return ret; + + ctrl_hdlr->lock = &ov02e10->mutex; + cur_mode = ov02e10->cur_mode; + size = ARRAY_SIZE(link_freq_menu_items); + + ov02e10->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_LINK_FREQ, size - 1, 0, + link_freq_menu_items); + if (ov02e10->link_freq) + ov02e10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(OV02E10_LINK_FREQ_360MHZ_INDEX); + ov02e10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + vblank_min = cur_mode->vts_min - cur_mode->height; + vblank_max = OV02E10_VTS_MAX - cur_mode->height; + vblank_default = cur_mode->vts_def - cur_mode->height; + ov02e10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + + h_blank = to_pixels_per_line(cur_mode->hts, cur_mode->link_freq_index); + h_blank -= cur_mode->width; + ov02e10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov02e10->hblank) + ov02e10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV02E10_ANAL_GAIN_MIN, OV02E10_ANAL_GAIN_MAX, + OV02E10_ANAL_GAIN_STEP, OV02E10_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV02E10_DGTL_GAIN_MIN, OV02E10_DGTL_GAIN_MAX, + OV02E10_DGTL_GAIN_STEP, OV02E10_DGTL_GAIN_DEFAULT); + exposure_max = cur_mode->vts_def - OV02E10_EXPOSURE_MAX_MARGIN; + ov02e10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02E10_EXPOSURE_MIN, + exposure_max, + OV02E10_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02e10_test_pattern_menu) - 1, + 0, 0, ov02e10_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov02e10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov02e10_update_pad_format(const struct ov02e10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02e10_start_streaming(struct ov02e10 *ov02e10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + const struct ov02e10_reg_list *reg_list; + int ret = 0; + + dev_dbg(&client->dev, "start to set sensor settings\n"); + reg_list = &ov02e10->cur_mode->reg_list; + ret = ov02e10_write_reg_list(ov02e10, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + dev_dbg(&client->dev, "start to set ctrl_handler\n"); + ret = __v4l2_ctrl_handler_setup(ov02e10->sd.ctrl_handler); + if (ret) + dev_err(&client->dev, "setup V4L2 ctrl handler fail\n"); + return ret; + + dev_dbg(&client->dev, "start to streaming\n"); + ret = ov02e10_write_reg_list(ov02e10, &ov02e10_streaming_list); + if (ret) { + dev_err(&client->dev, "failed to streaming mode"); + return ret; + } + + return ret; +} + +static void ov02e10_stop_streaming(struct ov02e10 *ov02e10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + int ret = 0; + + ret = ov02e10_write_reg_list(ov02e10, &ov02e10_standby_list); + if (ret) + dev_err(&client->dev, "failed to stop streaming"); +} + +static int ov02e10_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov02e10->streaming == enable) + return 0; + + mutex_lock(&ov02e10->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov02e10->mutex); + return ret; + } + + ret = ov02e10_start_streaming(ov02e10); + if (ret) { + dev_dbg(&client->dev, "start streaming failed\n"); + enable = 0; + ov02e10_stop_streaming(ov02e10); + pm_runtime_put(&client->dev); + } + } else { + ov02e10_stop_streaming(ov02e10); + pm_runtime_put(&client->dev); + } + + ov02e10->streaming = enable; + mutex_unlock(&ov02e10->mutex); + + return ret; +} + +/* This function tries to get power control resources */ +static int ov02e10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret; + + ov02e10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov02e10->reset)) + return dev_err_probe(dev, PTR_ERR(ov02e10->reset), + "failed to get reset gpio\n"); + + ov02e10->handshake = devm_gpiod_get_optional(dev, "handshake", + GPIOD_OUT_LOW); + if (IS_ERR(ov02e10->handshake)) + return dev_err_probe(dev, PTR_ERR(ov02e10->handshake), + "failed to get handshake gpio\n"); + + ov02e10->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov02e10->img_clk)) + return dev_err_probe(dev, PTR_ERR(ov02e10->img_clk), + "failed to get imaging clock\n"); + + ov02e10->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov02e10->avdd)) { + ret = PTR_ERR(ov02e10->avdd); + ov02e10->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + return 0; +} + +static int ov02e10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret = 0; + + gpiod_set_value_cansleep(ov02e10->reset, 1); + gpiod_set_value_cansleep(ov02e10->handshake, 0); + + if (ov02e10->avdd) + regulator_disable(ov02e10->avdd); + + clk_disable_unprepare(ov02e10->img_clk); + + return ret; +} + +static int ov02e10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret; + + ret = clk_prepare_enable(ov02e10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + if (ov02e10->avdd) { + ret = regulator_enable(ov02e10->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(ov02e10->img_clk); + return ret; + } + } + gpiod_set_value_cansleep(ov02e10->handshake, 1); + gpiod_set_value_cansleep(ov02e10->reset, 0); + + /* Lattice MIPI aggregator with some version FW needs longer delay + after handshake triggered. We set 25ms as a safe value and wait + for a stable version FW. */ + msleep_interruptible(25); + + return ret; +} + +static int __maybe_unused ov02e10_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + mutex_lock(&ov02e10->mutex); + if (ov02e10->streaming) + ov02e10_stop_streaming(ov02e10); + + mutex_unlock(&ov02e10->mutex); + + return 0; +} + +static int __maybe_unused ov02e10_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret = 0; + + mutex_lock(&ov02e10->mutex); + if (!ov02e10->streaming) + goto exit; + + ret = ov02e10_start_streaming(ov02e10); + if (ret) { + ov02e10->streaming = false; + ov02e10_stop_streaming(ov02e10); + } + +exit: + mutex_unlock(&ov02e10->mutex); + return ret; +} + +static int ov02e10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + const struct ov02e10_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov02e10->mutex); + ov02e10_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + fmt->format; + } else { + ov02e10->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov02e10->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov02e10->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov02e10->vblank, + mode->vts_min - mode->height, + OV02E10_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov02e10->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov02e10->hblank, h_blank, h_blank, 1, + h_blank); + } + mutex_unlock(&ov02e10->mutex); + + return 0; +} + +static int ov02e10_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + mutex_lock(&ov02e10->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov02e10->sd, + sd_state, fmt->pad); + else + ov02e10_update_pad_format(ov02e10->cur_mode, &fmt->format); + + mutex_unlock(&ov02e10->mutex); + + return 0; +} + +static int ov02e10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov02e10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov02e10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + mutex_lock(&ov02e10->mutex); + ov02e10_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&ov02e10->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov02e10_video_ops = { + .s_stream = ov02e10_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov02e10_pad_ops = { + .set_fmt = ov02e10_set_format, + .get_fmt = ov02e10_get_format, + .enum_mbus_code = ov02e10_enum_mbus_code, + .enum_frame_size = ov02e10_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov02e10_subdev_ops = { + .video = &ov02e10_video_ops, + .pad = &ov02e10_pad_ops, +}; + +static const struct media_entity_operations ov02e10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov02e10_internal_ops = { + .open = ov02e10_open, +}; + +static int ov02e10_identify_module(struct ov02e10 *ov02e10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + int ret; + u32 val; + + ret = ov02e10_write_reg(ov02e10, OV02E10_REG_PAGE_FLAG, 1, + OV02E10_PAGE_0); + if (ret) + return ret; + + ret = ov02e10_read_reg(ov02e10, OV02E10_REG_CHIP_ID, 4, &val); + if (ret) + return ret; + + if (val != OV02E10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV02E10_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int ov02e10_remove(struct i2c_client *client) +#else +static void ov02e10_remove(struct i2c_client *client) +#endif +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov02e10->mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +// After this module is loaded, probe function is called +// if this device HID is enumerated by ACPI table. +static int ov02e10_probe(struct i2c_client *client) +{ + struct ov02e10 *ov02e; + int ret; + + ov02e = devm_kzalloc(&client->dev, sizeof(*ov02e), GFP_KERNEL); + if (!ov02e) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&ov02e->sd, client, &ov02e10_subdev_ops); + ov02e10_get_pm_resources(&client->dev); + + ov02e10_power_on(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + goto error_power_off; + } + + /* Check module identity */ + ret = ov02e10_identify_module(ov02e); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + ov02e->cur_mode = &supported_modes[0]; + + dev_dbg(&client->dev, "will Init controls\n"); + ret = ov02e10_init_controls(ov02e); + if (ret) + return ret; + + /* Initialize subdev */ + ov02e->sd.internal_ops = &ov02e10_internal_ops; + ov02e->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02e->sd.entity.ops = &ov02e10_subdev_entity_ops; + ov02e->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov02e->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov02e->sd.entity, 1, &ov02e->pad); + if (ret) { + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&ov02e->sd); + if (ret < 0) { + dev_err(&client->dev, "async reg subdev error\n"); + goto error_media_entity; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&ov02e->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(ov02e->sd.ctrl_handler); + mutex_destroy(&ov02e->mutex); + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); +error_power_off: + ov02e10_power_off(&client->dev); + + dev_dbg(&client->dev, "probe done\n"); + return ret; +} + +static const struct dev_pm_ops ov02e10_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov02e10_suspend, ov02e10_resume) + SET_RUNTIME_PM_OPS(ov02e10_power_off, ov02e10_power_on, NULL) +}; + +static const struct acpi_device_id ov02e10_acpi_ids[] = { + { "OVTI02E1" }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids); + +static struct i2c_driver ov02e10_i2c_driver = { + .driver = { + .name = "ov02e10", + .pm = &ov02e10_pm_ops, + .acpi_match_table = ov02e10_acpi_ids, + }, + .probe_new = ov02e10_probe, + .remove = ov02e10_remove, +}; + +module_i2c_driver(ov02e10_i2c_driver); + +MODULE_AUTHOR("Jingjing Xiong "); +MODULE_DESCRIPTION("OmniVision OV02E10 sensor driver"); +MODULE_LICENSE("GPL v2"); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov08a10.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov08a10.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov08a10.c 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov08a10.c 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,1143 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022-2023 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV08A10_REG_VALUE_08BIT 1 +#define OV08A10_REG_VALUE_16BIT 2 +#define OV08A10_REG_VALUE_24BIT 3 + +#define OV08A10_LINK_FREQ_500MHZ 500000000ULL //1008Mbps +#define OV08A10_SCLK 120000000LL +#define OV08A10_MCLK 19200000 +#define OV08A10_DATA_LANES 4 +#define OV08A10_RGB_DEPTH 10 + +#define OV08A10_REG_CHIP_ID 0x300a +#define OV08A10_CHIP_ID 0x560841 //ToDo + +#define OV08A10_REG_MODE_SELECT 0x0100 +#define OV08A10_MODE_STANDBY 0x00 +#define OV08A10_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV08A10_REG_VTS 0x380e +#define OV08A10_VTS_30FPS 0x171a +#define OV08A10_VTS_30FPS_MIN 0x171a +#define OV08A10_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV08A10_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV08A10_REG_EXPOSURE 0x3501 +#define OV08A10_EXPOSURE_MIN 8 +#define OV08A10_EXPOSURE_MAX_MARGIN 28 +#define OV08A10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV08A10_REG_ANALOG_GAIN 0x3508 +#define OV08A10_ANAL_GAIN_MIN 128 +#define OV08A10_ANAL_GAIN_MAX 2047 +#define OV08A10_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV08A10_REG_DIG_GAIN 0x350a +#define OV08A10_DGTL_GAIN_MIN 1024 +#define OV08A10_DGTL_GAIN_MAX 16383 +#define OV08A10_DGTL_GAIN_STEP 1 +#define OV08A10_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV08A10_REG_TEST_PATTERN 0x5081 +#define OV08A10_TEST_PATTERN_ENABLE BIT(0) +#define OV08A10_TEST_PATTERN_BAR_SHIFT 4 + +#define to_ov08a10(_sd) container_of(_sd, struct ov08a10, sd) + +enum { + OV08A10_LINK_FREQ_500MHZ_INDEX, +}; + +struct ov08a10_reg { + u16 address; + u8 val; +}; + +struct ov08a10_reg_list { + u32 num_of_regs; + const struct ov08a10_reg *regs; +}; + +struct ov08a10_link_freq_config { + const struct ov08a10_reg_list reg_list; +}; + +struct ov08a10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov08a10_reg_list reg_list; +}; + +static const struct ov08a10_reg ov08a10_global_setting[] = { + {0x0103, 0x01}, +//sl 1 1 ; delay 1ms + {0x0100, 0x00}, + {0x0102, 0x01}, + {0x0304, 0x01},//01;02;01 ;MCLK=19.2Mhz , 800 Mbps + {0x0305, 0xe0},//f4;76;f8 + {0x0306, 0x01}, + {0x0307, 0x00}, + {0x0323, 0x04}, + {0x0324, 0x01}, + {0x0325, 0x90},//;40 ;MCLK=19.2Mhz + {0x4837, 0x15},//14;10;14 ;MCLK=19.2Mhz , 800 Mbps + {0x3009, 0x06}, + {0x3012, 0x41}, + {0x301e, 0x98}, + {0x3026, 0x10}, + {0x3027, 0x08}, + {0x3106, 0x00}, + {0x3216, 0x01}, + {0x3217, 0x00}, + {0x3218, 0x00}, + {0x3219, 0x55}, + {0x3400, 0x00}, + {0x3408, 0x02}, + {0x340c, 0x20}, + {0x340d, 0x00}, + {0x3410, 0x00}, + {0x3412, 0x00}, + {0x3413, 0x00}, + {0x3414, 0x00}, + {0x3415, 0x00}, + {0x3501, 0x16}, + {0x3502, 0xfa}, + {0x3504, 0x08}, + {0x3508, 0x04}, + {0x3509, 0x00}, + {0x353c, 0x04}, + {0x353d, 0x00}, + {0x3600, 0x20}, + {0x3608, 0x87}, + {0x3609, 0xe0}, + {0x360a, 0x66}, + {0x360c, 0x20}, + {0x361a, 0x80}, + {0x361b, 0xd0}, + {0x361c, 0x11}, + {0x361d, 0x63}, + {0x361e, 0x76}, + {0x3620, 0x50}, + {0x3621, 0x0a}, + {0x3622, 0x8a}, + {0x3625, 0x88}, + {0x3626, 0x49}, + {0x362a, 0x80}, + {0x3632, 0x00}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x0e}, + {0x3659, 0x11}, + {0x365a, 0x23}, + {0x365b, 0x38}, + {0x365c, 0x80}, + {0x3661, 0x0c}, + {0x3663, 0x40}, + {0x3665, 0x12}, + {0x3668, 0xf0}, + {0x3669, 0x0a}, + {0x366a, 0x10}, + {0x366b, 0x43}, + {0x366c, 0x02}, + {0x3674, 0x00}, + {0x3706, 0x1b}, + {0x3709, 0x25}, + {0x370b, 0x3f}, + {0x370c, 0x03}, + {0x3713, 0x02}, + {0x3714, 0x63}, + {0x3726, 0x20}, + {0x373b, 0x06}, + {0x373d, 0x0a}, + {0x3752, 0x00}, + {0x3753, 0x00}, + {0x3754, 0xee}, + {0x3767, 0x08}, + {0x3768, 0x0e}, + {0x3769, 0x02}, + {0x376a, 0x00}, + {0x376b, 0x00}, + {0x37d9, 0x08}, + {0x37dc, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa7}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x03}, + {0x380d, 0x86}, + {0x380e, 0x17}, + {0x380f, 0x1a}, +//@@ GRBG + {0x3810, 0x00}, + {0x3811, 0x11}, + {0x3812, 0x00}, + {0x3813, 0x09}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x80}, + {0x3821, 0x04}, + {0x3823, 0x00}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382b, 0x08}, + {0x3834, 0xf4}, + {0x3836, 0x14}, + {0x3837, 0x04}, + {0x3898, 0x00}, + {0x38a0, 0x02}, + {0x38a1, 0x02}, + {0x38a2, 0x02}, + {0x38a3, 0x04}, + {0x38c3, 0x00}, + {0x38c4, 0x00}, + {0x38c5, 0x00}, + {0x38c6, 0x00}, + {0x38c7, 0x00}, + {0x38c8, 0x00}, + {0x3d8c, 0x60}, + {0x3d8d, 0x30}, + {0x3f00, 0x8b}, + {0x4000, 0xf7}, + {0x4001, 0x60}, + {0x4002, 0x00}, + {0x4003, 0x40}, + {0x4008, 0x02}, + {0x4009, 0x11}, + {0x400a, 0x01}, + {0x400b, 0x00}, + {0x4020, 0x00}, + {0x4021, 0x00}, + {0x4022, 0x00}, + {0x4023, 0x00}, + {0x4024, 0x00}, + {0x4025, 0x00}, + {0x4026, 0x00}, + {0x4027, 0x00}, + {0x4030, 0x00}, + {0x4031, 0x00}, + {0x4032, 0x00}, + {0x4033, 0x00}, + {0x4034, 0x00}, + {0x4035, 0x00}, + {0x4036, 0x00}, + {0x4037, 0x00}, + {0x4040, 0x00}, + {0x4041, 0x07}, + {0x4201, 0x00}, + {0x4202, 0x00}, + {0x4204, 0x09}, + {0x4205, 0x00}, + {0x4300, 0xff}, + {0x4301, 0x00}, + {0x4302, 0x0f}, + {0x4500, 0x08}, + {0x4501, 0x00}, + {0x450b, 0x00}, + {0x4640, 0x01}, + {0x4641, 0x04}, + {0x4645, 0x03}, + {0x4800, 0x00}, + {0x4803, 0x18}, + {0x4809, 0x2b}, + {0x480e, 0x02}, + {0x4813, 0x90}, + {0x481b, 0x3c}, + {0x4847, 0x01}, + {0x4856, 0x58}, + {0x4888, 0x90}, + {0x4901, 0x00}, + {0x4902, 0x00}, + {0x4904, 0x09}, + {0x4905, 0x00}, + {0x5000, 0x89}, + {0x5001, 0x5a}, + {0x5002, 0x51}, + {0x5005, 0xd0}, + {0x5007, 0xa0}, + {0x500a, 0x02}, + {0x500b, 0x02}, + {0x500c, 0x0a}, + {0x500d, 0x0a}, + {0x500e, 0x02}, + {0x500f, 0x06}, + {0x5010, 0x0a}, + {0x5011, 0x0e}, + {0x5013, 0x00}, + {0x5015, 0x00}, + {0x5017, 0x10}, + {0x5019, 0x00}, + {0x501b, 0xc0}, + {0x501d, 0xa0}, + {0x501e, 0x00}, + {0x501f, 0x40}, + {0x5058, 0x00}, + {0x5081, 0x00}, + {0x5180, 0x00}, + {0x5181, 0x3c}, + {0x5182, 0x01}, + {0x5183, 0xfc}, + {0x5200, 0x4f}, + {0x5203, 0x07}, + {0x5208, 0xff}, + {0x520a, 0x3f}, + {0x520b, 0xc0}, + {0x520c, 0x05}, + {0x520d, 0xc8}, + {0x520e, 0x3f}, + {0x520f, 0x0f}, + {0x5210, 0x0a}, + {0x5218, 0x02}, + {0x5219, 0x01}, + {0x521b, 0x02}, + {0x521c, 0x01}, + {0x58cb, 0x03}, +}; + +static const struct ov08a10_reg mode_3264x2448_regs[] = { + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa7}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x03}, + {0x380d, 0x86}, + {0x380e, 0x17}, + {0x380f, 0x1a}, +//@@ GRBG + {0x3810, 0x00}, + {0x3811, 0x11}, + {0x3812, 0x00}, + {0x3813, 0x09}, + {0x3814, 0x11}, + {0x3815, 0x11}, +}; + +static const char * const ov08a10_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar" +}; + +static const s64 link_freq_menu_items[] = { + OV08A10_LINK_FREQ_500MHZ, +}; + +static const struct ov08a10_link_freq_config link_freq_configs[] = { + [OV08A10_LINK_FREQ_500MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(ov08a10_global_setting), + .regs = ov08a10_global_setting, + } + } +}; + +static const struct ov08a10_mode supported_modes[] = { + { + .width = 3264, + .height = 2448, + .hts = 902, //0x0386 + .vts_def = OV08A10_VTS_30FPS, + .vts_min = OV08A10_VTS_30FPS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + .link_freq_index = OV08A10_LINK_FREQ_500MHZ_INDEX, + }, +}; + +struct ov08a10 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov08a10_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* i2c client */ + struct i2c_client *client; + + /* Power management */ + struct clk *imgclk; + struct gpio_desc *reset_gpio; + struct regulator *avdd; + + bool identified; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV08A10_DATA_LANES; + + do_div(pixel_rate, OV08A10_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV08A10_SCLK); + + return ppl; +} + +static int ov08a10_read_reg(struct ov08a10 *ov08a10, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov08a10_write_reg(struct ov08a10 *ov08a10, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov08a10_write_reg_list(struct ov08a10 *ov08a10, + const struct ov08a10_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov08a10_write_reg(ov08a10, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov08a10_test_pattern(struct ov08a10 *ov08a10, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV08A10_TEST_PATTERN_BAR_SHIFT | + OV08A10_TEST_PATTERN_ENABLE; + + return ov08a10_write_reg(ov08a10, OV08A10_REG_TEST_PATTERN, + OV08A10_REG_VALUE_08BIT, pattern); +} + +static int ov08a10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov08a10 *ov08a10 = container_of(ctrl->handler, + struct ov08a10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov08a10->cur_mode->height + ctrl->val - + OV08A10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov08a10->exposure, + ov08a10->exposure->minimum, + exposure_max, ov08a10->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov08a10_write_reg(ov08a10, OV08A10_REG_ANALOG_GAIN, + OV08A10_REG_VALUE_16BIT, + ctrl->val << 1); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov08a10_write_reg(ov08a10, OV08A10_REG_DIG_GAIN, + OV08A10_REG_VALUE_24BIT, + ctrl->val << 6); + break; + + case V4L2_CID_EXPOSURE: + ret = ov08a10_write_reg(ov08a10, OV08A10_REG_EXPOSURE, + OV08A10_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_VBLANK: + ret = ov08a10_write_reg(ov08a10, OV08A10_REG_VTS, + OV08A10_REG_VALUE_16BIT, + ov08a10->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov08a10_test_pattern(ov08a10, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov08a10_ctrl_ops = { + .s_ctrl = ov08a10_set_ctrl, +}; + +static int ov08a10_init_controls(struct ov08a10 *ov08a10) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct ov08a10_mode *cur_mode; + s64 exposure_max, h_blank; + int ret = 0; + int size; + + ctrl_hdlr = &ov08a10->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov08a10->mutex; + cur_mode = ov08a10->cur_mode; + size = ARRAY_SIZE(link_freq_menu_items); + ov08a10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov08a10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE + (link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (ov08a10->link_freq) + ov08a10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov08a10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate + (OV08A10_LINK_FREQ_500MHZ_INDEX), + 1, + to_pixel_rate + (OV08A10_LINK_FREQ_500MHZ_INDEX)); + + ov08a10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, + V4L2_CID_VBLANK, + ov08a10->cur_mode->vts_min, + OV08A10_VTS_MAX, 1, + ov08a10->cur_mode->vts_def); + + h_blank = to_pixels_per_line(ov08a10->cur_mode->hts, + ov08a10->cur_mode->link_freq_index) - + ov08a10->cur_mode->width; + ov08a10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov08a10->hblank) + ov08a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV08A10_ANAL_GAIN_MIN, OV08A10_ANAL_GAIN_MAX, + OV08A10_ANAL_GAIN_STEP, OV08A10_ANAL_GAIN_MIN); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV08A10_DGTL_GAIN_MIN, OV08A10_DGTL_GAIN_MAX, + OV08A10_DGTL_GAIN_STEP, OV08A10_DGTL_GAIN_DEFAULT); + exposure_max = (ov08a10->cur_mode->vts_def - + OV08A10_EXPOSURE_MAX_MARGIN); + + ov08a10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov08a10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV08A10_EXPOSURE_MIN, + exposure_max, + OV08A10_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov08a10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov08a10_test_pattern_menu) - 1, + 0, 0, ov08a10_test_pattern_menu); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov08a10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov08a10_update_pad_format(const struct ov08a10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov08a10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08a10 *ov08a10 = to_ov08a10(sd); + + gpiod_set_value_cansleep(ov08a10->reset_gpio, 1); + if (ov08a10->avdd) + regulator_disable(ov08a10->avdd); + + clk_disable_unprepare(ov08a10->imgclk); + msleep(50); + + return 0; +} + +static int ov08a10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08a10 *ov08a10 = to_ov08a10(sd); + int ret; + + ret = clk_prepare_enable(ov08a10->imgclk); + if (ret < 0) { + dev_err(dev, "failed to enable imgclk: %d", ret); + return ret; + } + + if (ov08a10->avdd) + ret = regulator_enable(ov08a10->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + return ret; + } + + gpiod_set_value_cansleep(ov08a10->reset_gpio, 0); + msleep(50); + + return 0; +} + +static int ov08a10_start_streaming(struct ov08a10 *ov08a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + const struct ov08a10_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov08a10->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + + ret = ov08a10_write_reg_list(ov08a10, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov08a10->cur_mode->reg_list; + ret = ov08a10_write_reg_list(ov08a10, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov08a10->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov08a10_write_reg(ov08a10, OV08A10_REG_MODE_SELECT, + OV08A10_REG_VALUE_08BIT, + OV08A10_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void ov08a10_stop_streaming(struct ov08a10 *ov08a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + + if (ov08a10_write_reg(ov08a10, OV08A10_REG_MODE_SELECT, + OV08A10_REG_VALUE_08BIT, OV08A10_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int ov08a10_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov08a10 *ov08a10 = to_ov08a10(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov08a10->streaming == enable) + return 0; + + mutex_lock(&ov08a10->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov08a10->mutex); + return ret; + } + + ret = ov08a10_start_streaming(ov08a10); + if (ret) { + dev_err(&client->dev, "start streaming failed\n"); + enable = 0; + ov08a10_stop_streaming(ov08a10); + pm_runtime_put(&client->dev); + } + } else { + ov08a10_stop_streaming(ov08a10); + pm_runtime_put(&client->dev); + } + + ov08a10->streaming = enable; + mutex_unlock(&ov08a10->mutex); + + return ret; +} + +static int __maybe_unused ov08a10_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08a10 *ov08a10 = to_ov08a10(sd); + + mutex_lock(&ov08a10->mutex); + if (ov08a10->streaming) + ov08a10_stop_streaming(ov08a10); + + mutex_unlock(&ov08a10->mutex); + + return 0; +} + +static int __maybe_unused ov08a10_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08a10 *ov08a10 = to_ov08a10(sd); + int ret; + + mutex_lock(&ov08a10->mutex); + if (ov08a10->streaming) { + ret = ov08a10_start_streaming(ov08a10); + if (ret) { + ov08a10->streaming = false; + ov08a10_stop_streaming(ov08a10); + mutex_unlock(&ov08a10->mutex); + return ret; + } + } + + mutex_unlock(&ov08a10->mutex); + + return 0; +} + +static int ov08a10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08a10 *ov08a10 = to_ov08a10(sd); + const struct ov08a10_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov08a10->mutex); + ov08a10_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad) = fmt->format; + } else { + ov08a10->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov08a10->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov08a10->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov08a10->vblank, + mode->vts_min - mode->height, + OV08A10_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov08a10->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov08a10->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov08a10->mutex); + + return 0; +} + +static int ov08a10_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08a10 *ov08a10 = to_ov08a10(sd); + + mutex_lock(&ov08a10->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov08a10->sd, + sd_state, + fmt->pad); + else + ov08a10_update_pad_format(ov08a10->cur_mode, &fmt->format); + + mutex_unlock(&ov08a10->mutex); + + return 0; +} + +static int ov08a10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov08a10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov08a10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov08a10 *ov08a10 = to_ov08a10(sd); + + mutex_lock(&ov08a10->mutex); + ov08a10_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&ov08a10->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov08a10_video_ops = { + .s_stream = ov08a10_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov08a10_pad_ops = { + .set_fmt = ov08a10_set_format, + .get_fmt = ov08a10_get_format, + .enum_mbus_code = ov08a10_enum_mbus_code, + .enum_frame_size = ov08a10_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov08a10_subdev_ops = { + .video = &ov08a10_video_ops, + .pad = &ov08a10_pad_ops, +}; + +static const struct media_entity_operations ov08a10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov08a10_internal_ops = { + .open = ov08a10_open, +}; + +static int ov08a10_identify_module(struct ov08a10 *ov08a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08a10->sd); + int ret; + u32 val; + + if (ov08a10->identified) + return 0; + + ret = ov08a10_read_reg(ov08a10, OV08A10_REG_CHIP_ID, + OV08A10_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV08A10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV08A10_CHIP_ID, val); + return -ENXIO; + } + + ov08a10->identified = true; + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int ov08a10_remove(struct i2c_client *client) +#else +static void ov08a10_remove(struct i2c_client *client) +#endif + +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08a10 *ov08a10 = to_ov08a10(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov08a10->mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +static void ov08a10_get_hwcfg(struct ov08a10 *ov08a10, struct device *dev) +{ + ov08a10->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov08a10->reset_gpio)) { + dev_dbg(dev, "could not get gpio reset: %ld", + PTR_ERR(ov08a10->reset_gpio)); + ov08a10->reset_gpio = NULL; + } + + ov08a10->imgclk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov08a10->imgclk)) { + dev_dbg(dev, "could not get imgclk: %ld", + PTR_ERR(ov08a10->imgclk)); + ov08a10->imgclk = NULL; + } + + ov08a10->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov08a10->avdd)) { + dev_dbg(dev, "could not get regulator avdd: %ld", + PTR_ERR(ov08a10->avdd)); + ov08a10->avdd = NULL; + } +} + +static int ov08a10_probe(struct i2c_client *client) +{ + struct ov08a10 *ov08a10; + int ret = 0; + + ov08a10 = devm_kzalloc(&client->dev, sizeof(*ov08a10), GFP_KERNEL); + if (!ov08a10) + return -ENOMEM; + ov08a10->client = client; + + ov08a10_get_hwcfg(ov08a10, &client->dev); + v4l2_i2c_subdev_init(&ov08a10->sd, client, &ov08a10_subdev_ops); + ov08a10_power_on(&client->dev); + ret = ov08a10_identify_module(ov08a10); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + goto probe_error_power_down; + } + + mutex_init(&ov08a10->mutex); + + ov08a10->cur_mode = &supported_modes[0]; + ret = ov08a10_init_controls(ov08a10); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov08a10->sd.internal_ops = &ov08a10_internal_ops; + ov08a10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov08a10->sd.entity.ops = &ov08a10_subdev_entity_ops; + ov08a10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov08a10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov08a10->sd.entity, 1, &ov08a10->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&ov08a10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + dev_err(&client->dev, "media_entity_cleanup , probe error\n"); + media_entity_cleanup(&ov08a10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + dev_err(&client->dev, "v4l2_ctrl_handle_free\n"); + v4l2_ctrl_handler_free(ov08a10->sd.ctrl_handler); + mutex_destroy(&ov08a10->mutex); + +probe_error_power_down: + ov08a10_power_off(&client->dev); + + return ret; +} + +static const struct dev_pm_ops ov08a10_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov08a10_suspend, ov08a10_resume) + SET_RUNTIME_PM_OPS(ov08a10_power_off, ov08a10_power_on, NULL) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov08a10_acpi_ids[] = { + {"OVTI08A1"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov08a10_acpi_ids); +#endif + +static struct i2c_driver ov08a10_i2c_driver = { + .driver = { + .name = "ov08a10", + .pm = &ov08a10_pm_ops, + .acpi_match_table = ov08a10_acpi_ids, + }, + .probe_new = ov08a10_probe, + .remove = ov08a10_remove, +}; + +module_i2c_driver(ov08a10_i2c_driver); + +MODULE_AUTHOR("Jason Chen "); +MODULE_AUTHOR("Shawn Tu "); +MODULE_DESCRIPTION("OmniVision OV08A10 sensor driver"); +MODULE_LICENSE("GPL v2"); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov2740.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov2740.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/i2c/ov2740.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/i2c/ov2740.c 2023-10-18 15:30:24.000000000 +0800 @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -15,8 +14,11 @@ #include #include +#include +#include + #define OV2740_LINK_FREQ_360MHZ 360000000ULL -#define OV2740_CJFLE23_LINK_FREQ_360MHZ 360000000ULL +#define OV2740_LINK_FREQ_180MHZ 180000000ULL #define OV2740_SCLK 72000000LL #define OV2740_MCLK 19200000 #define OV2740_DATA_LANES 2 @@ -87,7 +89,7 @@ enum { OV2740_LINK_FREQ_360MHZ_INDEX, - OV2740_CJFLE23_LINK_FREQ_360MHZ_INDEX, + OV2740_LINK_FREQ_180MHZ_INDEX, }; struct ov2740_reg { @@ -149,7 +151,7 @@ {0x0312, 0x11}, }; -static const struct ov2740_reg mipi_data_rate_cjfle23_720mbps[] = { +static const struct ov2740_reg mipi_data_rate_360mbps[] = { {0x0103, 0x01}, {0xffff, 0x10}, {0x0302, 0x4b}, @@ -482,7 +484,7 @@ static const s64 link_freq_menu_items[] = { OV2740_LINK_FREQ_360MHZ, - OV2740_CJFLE23_LINK_FREQ_360MHZ, + OV2740_LINK_FREQ_180MHZ, }; static const struct ov2740_link_freq_config link_freq_configs[] = { @@ -493,10 +495,10 @@ } }, - [OV2740_CJFLE23_LINK_FREQ_360MHZ_INDEX] = { + [OV2740_LINK_FREQ_180MHZ_INDEX] = { .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_cjfle23_720mbps), - .regs = mipi_data_rate_cjfle23_720mbps, + .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, } }, }; @@ -517,7 +519,7 @@ }, }; -static const struct ov2740_mode cjfle23_supported_modes[] = { +static const struct ov2740_mode supported_modes_360mbps[] = { { .width = 1932, .height = 1092, @@ -529,7 +531,7 @@ .num_of_regs = ARRAY_SIZE(mode_cjfle23_1932x1092_regs), .regs = mode_cjfle23_1932x1092_regs, }, - .link_freq_index = OV2740_CJFLE23_LINK_FREQ_360MHZ_INDEX, + .link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX, }, }; @@ -562,8 +564,8 @@ /* GPIO for reset */ struct gpio_desc *reset_gpio; - /* GPIO for privacy LED */ - struct gpio_desc *pled_gpio; + /* Clock provider */ + struct clk *clk; /* Module name index */ u8 module_name_index; @@ -592,56 +594,6 @@ return ppl; } -static void ov2740_set_power(struct ov2740 *ov2740, int on) -{ - if (!(ov2740->reset_gpio && ov2740->pled_gpio)) - return; - gpiod_set_value_cansleep(ov2740->reset_gpio, on); - gpiod_set_value_cansleep(ov2740->pled_gpio, on); - msleep(20); -} - -static int ov2740_parse_dt(struct ov2740 *ov2740) -{ - struct device *dev = &ov2740->client->dev; - int ret; - int i = 0; - union acpi_object *obj; - - obj = acpi_evaluate_dsm_typed(ACPI_COMPANION(dev)->handle, - &cio2_sensor_module_guid, 0x00, - 0x01, NULL, ACPI_TYPE_STRING); - - ov2740->module_name_index = 0; - if (obj && obj->string.type == ACPI_TYPE_STRING) { - for (i = 1; i < ARRAY_SIZE(ov2740_module_names); i++) { - if (!strcmp(ov2740_module_names[i], obj->string.pointer)) { - ov2740->module_name_index = i; - break; - } - } - } - ACPI_FREE(obj); - - if (ov2740->module_name_index == 0) - return 0; - - ov2740->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov2740->reset_gpio); - if (ret < 0) { - dev_err(dev, "error while getting reset gpio: %d\n", ret); - return ret; - } - - ov2740->pled_gpio = devm_gpiod_get(dev, "pled", GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(ov2740->pled_gpio); - if (ret < 0) { - dev_err(dev, "error while getting pled gpio: %d\n", ret); - return ret; - } - return 0; -} - static int ov2740_read_reg(struct ov2740 *ov2740, u16 reg, u16 len, u32 *val) { struct i2c_client *client = ov2740->client; @@ -1001,7 +953,6 @@ int link_freq_index; int ret = 0; - ov2740_set_power(ov2740, 1); ov2740_load_otp_data(nvm); link_freq_index = ov2740->cur_mode->link_freq_index; @@ -1038,7 +989,6 @@ if (ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, OV2740_MODE_STANDBY)) dev_err(&client->dev, "failed to stop streaming"); - ov2740_set_power(ov2740, 0); } static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) @@ -1075,6 +1025,32 @@ return ret; } +static int ov2740_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2740 *ov2740 = to_ov2740(sd); + int ret = 0; + + gpiod_set_value_cansleep(ov2740->reset_gpio, 1); + clk_disable_unprepare(ov2740->clk); + msleep(20); + + return ret; +} + +static int ov2740_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2740 *ov2740 = to_ov2740(sd); + int ret = 0; + + ret = clk_prepare_enable(ov2740->clk); + gpiod_set_value_cansleep(ov2740->reset_gpio, 0); + msleep(20); + + return ret; +} + static int __maybe_unused ov2740_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1123,8 +1099,8 @@ s32 vblank_def, h_blank; if (ov2740->module_name_index == 1) { - mode = v4l2_find_nearest_size(cjfle23_supported_modes, - ARRAY_SIZE(cjfle23_supported_modes), + mode = v4l2_find_nearest_size(supported_modes_360mbps, + ARRAY_SIZE(supported_modes_360mbps), width, height, fmt->format.width, fmt->format.height); } else { @@ -1220,15 +1196,15 @@ { struct ov2740 *ov2740 = to_ov2740(sd); if (ov2740->module_name_index == 1) { - if (fse->index >= ARRAY_SIZE(cjfle23_supported_modes)) + if (fse->index >= ARRAY_SIZE(supported_modes_360mbps)) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) return -EINVAL; - fse->min_width = cjfle23_supported_modes[fse->index].width; + fse->min_width = supported_modes_360mbps[fse->index].width; fse->max_width = fse->min_width; - fse->min_height = cjfle23_supported_modes[fse->index].height; + fse->min_height = supported_modes_360mbps[fse->index].height; fse->max_height = fse->min_height; } else { if (fse->index >= ARRAY_SIZE(supported_modes)) @@ -1317,8 +1293,9 @@ int ret; unsigned int i, j; - if (!fwnode) - return -ENXIO; + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EPROBE_DEFER; ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); if (ret) @@ -1329,10 +1306,6 @@ return -EINVAL; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) @@ -1469,6 +1442,59 @@ return ret; } +static int ov2740_read_module_name(struct ov2740 *ov2740) +{ + struct device *dev = &ov2740->client->dev; + int i = 0; + union acpi_object *obj; + struct acpi_device *adev = ACPI_COMPANION(dev); + + ov2740->module_name_index = 0; + if (!adev) + return 0; + + obj = acpi_evaluate_dsm_typed(adev->handle, + &cio2_sensor_module_guid, 0x00, + 0x01, NULL, ACPI_TYPE_STRING); + + if (obj && obj->string.type == ACPI_TYPE_STRING) { + for (i = 1; i < ARRAY_SIZE(ov2740_module_names); i++) { + if (!strcmp(ov2740_module_names[i], obj->string.pointer)) { + ov2740->module_name_index = i; + break; + } + } + } + ACPI_FREE(obj); + + return 0; +} + +static int ov2740_parse_power(struct ov2740 *ov2740) +{ + struct device *dev = &ov2740->client->dev; + long ret; + + ov2740->reset_gpio = + devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov2740->reset_gpio)) { + ret = PTR_ERR(ov2740->reset_gpio); + dev_err(dev, "error while getting reset gpio: %ld\n", ret); + ov2740->reset_gpio = NULL; + return (int)ret; + } + + ov2740->clk = devm_clk_get_optional(dev, "clk"); + if (IS_ERR(ov2740->clk)) { + ret = PTR_ERR(ov2740->clk); + dev_err(dev, "error while getting clk: %ld\n", ret); + ov2740->clk = NULL; + return (int)ret; + } + + return 0; +} + static int ov2740_probe(struct i2c_client *client) { struct ov2740 *ov2740; @@ -1479,22 +1505,22 @@ return -ENOMEM; ov2740->client = client; - ret = ov2740_parse_dt(ov2740); - if (ret < 0) - return -EINVAL; + ov2740_read_module_name(ov2740); - /* for Thinkpad X1 Yoga, module CJFLE23, skip hardware config check */ - if (ov2740->module_name_index != 1) { - ret = ov2740_check_hwcfg(&client->dev); - if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", - ret); - return ret; - } - } + ret = ov2740_check_hwcfg(&client->dev); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to check HW configuration: %d", + ret); - ov2740_set_power(ov2740, 1); v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); + + ret = ov2740_parse_power(ov2740); + if (ret) + return ret; + + ov2740_power_on(&client->dev); + ret = ov2740_identify_module(ov2740); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); @@ -1508,7 +1534,7 @@ ov2740->module_name_index); } if (ov2740->module_name_index == 1) - ov2740->cur_mode = &cjfle23_supported_modes[0]; + ov2740->cur_mode = &supported_modes_360mbps[0]; else ov2740->cur_mode = &supported_modes[0]; @@ -1552,7 +1578,6 @@ pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); - ov2740_set_power(ov2740, 0); return 0; probe_error_media_entity_cleanup: @@ -1563,13 +1588,14 @@ mutex_destroy(&ov2740->mutex); probe_error_power_down: - ov2740_set_power(ov2740, 0); + ov2740_power_off(&client->dev); return ret; } static const struct dev_pm_ops ov2740_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) + SET_RUNTIME_PM_OPS(ov2740_power_off, ov2740_power_on, NULL) }; static const struct acpi_device_id ov2740_acpi_ids[] = { diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/cio2-bridge.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/cio2-bridge.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/cio2-bridge.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/cio2-bridge.c 2023-10-18 15:30:24.000000000 +0800 @@ -39,16 +39,22 @@ /* Omnivision ov8856 */ CIO2_SENSOR_CONFIG("OVTI8856", 0, 0), /* Omnivision ov2740 */ - CIO2_SENSOR_CONFIG("INT3474", 0, 0), + CIO2_SENSOR_CONFIG("INT3474", 2, 360000000, 180000000), /* Hynix hi556 */ - CIO2_SENSOR_CONFIG("INT3537", 0, 0), + CIO2_SENSOR_CONFIG("INT3537", 1, 437000000), /* Himax hm2170 */ CIO2_SENSOR_CONFIG("HIMX2170", 0, 0), + /* Himax hm2172 */ + CIO2_SENSOR_CONFIG("HIMX2172", 0, 0), + /* Omnivision ov02e10 */ + CIO2_SENSOR_CONFIG("OVTI02E1", 0, 0), /* Himax hm11b1 */ CIO2_SENSOR_CONFIG("HIMX11B1", 0, 0), /* Omnivision ov13b10 */ - CIO2_SENSOR_CONFIG("OVTIDB10", 0, 0), - CIO2_SENSOR_CONFIG("OVTI13B1", 0, 0), + CIO2_SENSOR_CONFIG("OVTIDB10", 1, 560000000), + CIO2_SENSOR_CONFIG("OVTI13B1", 1, 560000000), + /* Omnivision ov08a10 */ + CIO2_SENSOR_CONFIG("OVTI08A1", 0, 0), }; static const struct cio2_property_names prop_names = { @@ -221,6 +227,20 @@ SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ } +static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor) +{ + struct software_node *nodes = sensor->swnodes; + + sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; + sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; + sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; + sensor->group[SWNODE_CIO2_PORT] = &nodes[SWNODE_CIO2_PORT]; + sensor->group[SWNODE_CIO2_ENDPOINT] = &nodes[SWNODE_CIO2_ENDPOINT]; + if (sensor->ssdb.vcmtype && + sensor->ssdb.vcmtype <= ARRAY_SIZE(cio2_vcm_types)) + sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; +} + static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, struct cio2_sensor *sensor) { @@ -254,6 +274,8 @@ sensor->ssdb.link); nodes[SWNODE_VCM] = NODE_VCM(vcm_node_name); } + + cio2_bridge_init_swnode_group(sensor); } static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) @@ -293,7 +315,7 @@ for (i = 0; i < bridge->n_sensors; i++) { sensor = &bridge->sensors[i]; - software_node_unregister_nodes(sensor->swnodes); + software_node_unregister_node_group(sensor->group); ACPI_FREE(sensor->pld); acpi_dev_put(sensor->adev); i2c_unregister_device(sensor->vcm_i2c_client); @@ -352,7 +374,7 @@ cio2_bridge_create_fwnode_properties(sensor, bridge, cfg); cio2_bridge_create_connection_swnodes(bridge, sensor); - ret = software_node_register_nodes(sensor->swnodes); + ret = software_node_register_node_group(sensor->group); if (ret) goto err_free_pld; @@ -379,7 +401,7 @@ return 0; err_free_swnodes: - software_node_unregister_nodes(sensor->swnodes); + software_node_unregister_node_group(sensor->group); err_free_pld: ACPI_FREE(sensor->pld); err_put_adev: diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/cio2-bridge.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/cio2-bridge.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/cio2-bridge.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/cio2-bridge.h 2023-10-18 15:30:24.000000000 +0800 @@ -119,8 +119,9 @@ struct acpi_device *adev; struct i2c_client *vcm_i2c_client; - /* SWNODE_COUNT + 1 for terminating empty node */ - struct software_node swnodes[SWNODE_COUNT + 1]; + /* SWNODE_COUNT + 1 for terminating NULL */ + const struct software_node *group[SWNODE_COUNT + 1]; + struct software_node swnodes[SWNODE_COUNT]; struct cio2_node_names node_names; struct cio2_sensor_ssdb ssdb; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c 2023-10-18 15:30:24.000000000 +0800 @@ -164,11 +164,6 @@ primary = port & ~1; secondary = primary + 1; if (on) { - /* do rext flow for PHY-E */ - ret = ipu6_isys_dwc_phy_termcal_rext(isys, mbps); - if (ret) - return ret; - if (nlanes == 4) { dev_dbg(&isys->adev->dev, "config phy %u and %u in aggregation mode", diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c 2023-10-18 15:30:24.000000000 +0800 @@ -262,69 +262,69 @@ #define DPHY_FREQ_RANGE_NUM (63) #define DPHY_FREQ_RANGE_INVALID_INDEX (0xff) const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { - {0x00, 80, 97, 80, 448}, - {0x10, 80, 107, 90, 448}, - {0x20, 84, 118, 100, 448}, - {0x30, 93, 128, 110, 448}, - {0x01, 103, 139, 120, 448}, - {0x11, 112, 149, 130, 448}, - {0x21, 122, 160, 140, 448}, - {0x31, 131, 170, 150, 448}, - {0x02, 141, 181, 160, 448}, - {0x12, 150, 191, 170, 448}, - {0x22, 160, 202, 180, 448}, - {0x32, 169, 212, 190, 448}, - {0x03, 183, 228, 205, 448}, - {0x13, 198, 244, 220, 448}, - {0x23, 212, 259, 235, 448}, - {0x33, 226, 275, 250, 448}, - {0x04, 250, 301, 275, 448}, - {0x14, 274, 328, 300, 448}, - {0x25, 297, 354, 325, 448}, - {0x35, 321, 380, 350, 448}, - {0x05, 369, 433, 400, 448}, - {0x16, 416, 485, 450, 448}, - {0x26, 464, 538, 500, 448}, - {0x37, 511, 590, 550, 448}, - {0x07, 559, 643, 600, 448}, - {0x18, 606, 695, 650, 448}, - {0x28, 654, 748, 700, 448}, - {0x39, 701, 800, 750, 448}, - {0x09, 749, 853, 800, 448}, - {0x19, 796, 905, 850, 448}, - {0x29, 844, 958, 900, 448}, - {0x3a, 891, 1010, 950, 448}, - {0x0a, 939, 1063, 1000, 448}, - {0x1a, 986, 1115, 1050, 448}, - {0x2a, 1034, 1168, 1100, 448}, - {0x3b, 1081, 1220, 1150, 448}, - {0x0b, 1129, 1273, 1200, 448}, - {0x1b, 1176, 1325, 1250, 448}, - {0x2b, 1224, 1378, 1300, 448}, - {0x3c, 1271, 1430, 1350, 448}, - {0x0c, 1319, 1483, 1400, 448}, - {0x1c, 1366, 1535, 1450, 448}, - {0x2c, 1414, 1588, 1500, 448}, - {0x3d, 1461, 1640, 1550, 278}, - {0x0d, 1509, 1693, 1600, 287}, - {0x1d, 1556, 1745, 1650, 296}, - {0x2e, 1604, 1798, 1700, 305}, - {0x3e, 1651, 1850, 1750, 314}, - {0x0e, 1699, 1903, 1800, 323}, - {0x1e, 1746, 1955, 1850, 331}, - {0x2f, 1794, 2008, 1900, 340}, - {0x3f, 1841, 2060, 1950, 349}, - {0x0f, 1889, 2113, 2000, 358}, - {0x40, 1936, 2165, 2050, 367}, - {0x41, 1984, 2218, 2100, 376}, - {0x42, 2031, 2270, 2150, 385}, - {0x43, 2079, 2323, 2200, 394}, - {0x44, 2126, 2375, 2250, 403}, - {0x45, 2174, 2428, 2300, 412}, - {0x46, 2221, 2480, 2350, 421}, - {0x47, 2269, 2500, 2400, 430}, - {0x48, 2316, 2500, 2450, 439}, - {0x49, 2364, 2500, 2500, 448}, + {0x00, 80, 97, 80, 335}, + {0x10, 80, 107, 90, 335}, + {0x20, 84, 118, 100, 335}, + {0x30, 93, 128, 110, 335}, + {0x01, 103, 139, 120, 335}, + {0x11, 112, 149, 130, 335}, + {0x21, 122, 160, 140, 335}, + {0x31, 131, 170, 150, 335}, + {0x02, 141, 181, 160, 335}, + {0x12, 150, 191, 170, 335}, + {0x22, 160, 202, 180, 335}, + {0x32, 169, 212, 190, 335}, + {0x03, 183, 228, 205, 335}, + {0x13, 198, 244, 220, 335}, + {0x23, 212, 259, 235, 335}, + {0x33, 226, 275, 250, 335}, + {0x04, 250, 301, 275, 335}, + {0x14, 274, 328, 300, 335}, + {0x25, 297, 354, 325, 335}, + {0x35, 321, 380, 350, 335}, + {0x05, 369, 433, 400, 335}, + {0x16, 416, 485, 450, 335}, + {0x26, 464, 538, 500, 335}, + {0x37, 511, 590, 550, 335}, + {0x07, 559, 643, 600, 335}, + {0x18, 606, 695, 650, 335}, + {0x28, 654, 748, 700, 335}, + {0x39, 701, 800, 750, 335}, + {0x09, 749, 853, 800, 335}, + {0x19, 796, 905, 850, 335}, + {0x29, 844, 958, 900, 335}, + {0x3a, 891, 1010, 950, 335}, + {0x0a, 939, 1063, 1000, 335}, + {0x1a, 986, 1115, 1050, 335}, + {0x2a, 1034, 1168, 1100, 335}, + {0x3b, 1081, 1220, 1150, 335}, + {0x0b, 1129, 1273, 1200, 335}, + {0x1b, 1176, 1325, 1250, 335}, + {0x2b, 1224, 1378, 1300, 335}, + {0x3c, 1271, 1430, 1350, 335}, + {0x0c, 1319, 1483, 1400, 335}, + {0x1c, 1366, 1535, 1450, 335}, + {0x2c, 1414, 1588, 1500, 335}, + {0x3d, 1461, 1640, 1550, 208}, + {0x0d, 1509, 1693, 1600, 214}, + {0x1d, 1556, 1745, 1650, 221}, + {0x2e, 1604, 1798, 1700, 228}, + {0x3e, 1651, 1850, 1750, 234}, + {0x0e, 1699, 1903, 1800, 241}, + {0x1e, 1746, 1955, 1850, 248}, + {0x2f, 1794, 2008, 1900, 255}, + {0x3f, 1841, 2060, 1950, 261}, + {0x0f, 1889, 2113, 2000, 268}, + {0x40, 1936, 2165, 2050, 275}, + {0x41, 1984, 2218, 2100, 281}, + {0x42, 2031, 2270, 2150, 288}, + {0x43, 2079, 2323, 2200, 294}, + {0x44, 2126, 2375, 2250, 302}, + {0x45, 2174, 2428, 2300, 308}, + {0x46, 2221, 2480, 2350, 315}, + {0x47, 2269, 2500, 2400, 321}, + {0x48, 2316, 2500, 2450, 328}, + {0x49, 2364, 2500, 2500, 335}, }; static u32 get_hsfreq_by_mbps(u32 mbps) @@ -443,9 +443,11 @@ dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5); } +#define PHY_E (4) int ipu6_isys_dwc_phy_powerup_ack(struct ipu_isys *isys, u32 phy_id) { int rval; + u32 rescal_done; rval = dwc_dphy_pwr_up(isys, phy_id); if (rval != 0) { @@ -460,6 +462,18 @@ dev_dbg(&isys->adev->dev, "phy %u is ready!", phy_id); + if (phy_id != PHY_E || isys->phy_termcal_val) + return 0; + + usleep_range(100, 200); + rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1); + if (rescal_done) { + isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id, + 0x220, 2, 4); + dev_dbg(&isys->adev->dev, "termcal done with value = %u", + isys->phy_termcal_val); + } + return 0; } @@ -474,91 +488,3 @@ dwc_dphy_write(isys, phy_id, IPU_DWC_DPHY_TEST_IFC_REQ, TEST_IFC_REQ_RESET); } - -#define PHY_E (4) -int ipu6_isys_dwc_phy_termcal_rext(struct ipu_isys *isys, u32 mbps) -{ - u32 index; - u32 osc_freq_target; - u32 cfg_clk_freqrange; - u32 rescal_done; - struct ipu_bus_device *adev = to_ipu_bus_device(&isys->adev->dev); - struct ipu_device *isp = adev->isp; - int ret; - u32 phy_id = PHY_E; - - if (isys->phy_termcal_val) { - dev_dbg(&isys->adev->dev, "phy term cal already done, ignore."); - return 0; - } - - dev_dbg(&isys->adev->dev, "phy %u term calibration with %u mbps", - phy_id, mbps); - - ipu6_isys_dwc_phy_reset(isys, phy_id); - - index = get_hsfreq_by_mbps(mbps); - if (index == DPHY_FREQ_RANGE_INVALID_INDEX) { - dev_err(&isys->adev->dev, "link freq not found for mbps %u", - mbps); - return -EINVAL; - } - - dwc_dphy_write_mask(isys, phy_id, IPU_DWC_DPHY_HSFREQRANGE, - freqranges[index].hsfreq, 0, 7); - - /* - * Enable override to configure the DDL target oscillation - * frequency on bit 0 of register 0xe4 - */ - dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1); - /* - * configure registers 0xe2, 0xe3 with the - * appropriate DDL target oscillation frequency - * 0x1cc(460) - */ - osc_freq_target = freqranges[index].osc_freq_target; - dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2, - osc_freq_target & 0xff, 0, 8); - dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3, - (osc_freq_target >> 8) & 0xff, 0, 4); - - if (mbps < 1500) { - /* deskew_polarity_rw, for < 1.5Gbps */ - dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1); - } - - /* - * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4] - * (38.4 - 17) * 4 = 84 (0x54) - */ - cfg_clk_freqrange = (isp->buttress.ref_clk / 10 - 17) * 4; - dwc_dphy_write_mask(isys, phy_id, IPU_DWC_DPHY_CFGCLKFREQRANGE, - cfg_clk_freqrange, 0, 8); - - /* - * run without external reference resistor for 2Gbps - * dwc_dphy_ifc_write_mask(isys, phy_id, 0x4, 0x0, 4, 1); - */ - - dwc_dphy_write_mask(isys, phy_id, IPU_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1); - dwc_dphy_write_mask(isys, phy_id, IPU_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1); - - ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id); - if (ret) - return ret; - - usleep_range(100, 200); - rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1); - if (rescal_done) { - isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id, - 0x220, 2, 4); - dev_dbg(&isys->adev->dev, "termcal done with value = %u", - isys->phy_termcal_val); - } - - /* whatever reset the phy E after rext flow */ - ipu6_isys_dwc_phy_reset(isys, phy_id); - - return 0; -} diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu-platform.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu-platform.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu6/ipu-platform.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu6/ipu-platform.h 2023-10-18 15:30:24.000000000 +0800 @@ -11,6 +11,8 @@ #define IPU6EPES_FIRMWARE_NAME "intel/ipu6epes_fw.bin" #define IPU6_FIRMWARE_NAME "intel/ipu6_fw.bin" #define IPU6EPMTL_FIRMWARE_NAME "intel/ipu6epmtl_fw.bin" +#define IPU6EPMTLES_FIRMWARE_NAME "intel/ipu6epmtles_fw.bin" +#define IPU6EPADLN_FIRMWARE_NAME "intel/ipu6epadln_fw.bin" /* * The following definitions are encoded to the media_device's model field so diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-bus.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-bus.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-bus.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-bus.c 2023-10-18 15:30:24.000000000 +0800 @@ -101,9 +101,9 @@ rval = -ENODEV; goto out_err; } - rval = pm_runtime_get_sync(&adev->dev); + + rval = pm_runtime_resume_and_get(&adev->dev); if (rval < 0) { - pm_runtime_put(&adev->dev); dev_err(&adev->dev, "Failed to get runtime PM\n"); goto out_err; } @@ -154,6 +154,7 @@ { struct ipu_bus_device *adev = to_ipu_bus_device(dev); + kfree(adev->pdata); kfree(adev); } diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-buttress.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-buttress.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-buttress.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-buttress.h 2023-10-18 15:30:24.000000000 +0800 @@ -20,7 +20,7 @@ #define BUTTRESS_IS_FREQ_STEP 25U #define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8) -#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 16) +#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22) struct ipu_buttress_ctrl { u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu.c 2023-10-18 15:30:24.000000000 +0800 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,7 @@ } #endif - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); @@ -97,14 +98,16 @@ IPU_ISYS_NAME, nr); if (IS_ERR(isys)) { dev_err_probe(&pdev->dev, PTR_ERR(isys), - "ipu_bus_add_device(isys) failed\n"); - return ERR_CAST(isys); + "ipu_bus_initialize_device(isys) failed\n"); + kfree(pdata); + return isys; } isys->mmu = ipu_mmu_init(&pdev->dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(isys), + dev_err_probe(&pdev->dev, PTR_ERR(isys->mmu), "ipu_mmu_init(isys->mmu) failed\n"); + put_device(&isys->dev); return ERR_CAST(isys->mmu); } @@ -128,7 +131,7 @@ struct ipu_psys_pdata *pdata; int ret; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); @@ -139,15 +142,17 @@ IPU_PSYS_NAME, nr); if (IS_ERR(psys)) { dev_err_probe(&pdev->dev, PTR_ERR(psys), - "ipu_bus_add_device(psys) failed\n"); - return ERR_CAST(psys); + "ipu_bus_initialize_device(psys) failed\n"); + kfree(pdata); + return psys; } psys->mmu = ipu_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys), + dev_err_probe(&pdev->dev, PTR_ERR(psys->mmu), "ipu_mmu_init(psys->mmu) failed\n"); + put_device(&psys->dev); return ERR_CAST(psys->mmu); } @@ -299,7 +304,7 @@ struct dentry *file; struct dentry *dir; - dir = debugfs_create_dir(pci_name(isp->pdev), NULL); + dir = debugfs_create_dir(IPU_NAME, NULL); if (!dir) return -ENOMEM; @@ -351,6 +356,11 @@ pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, pci_command); + /* disable IPU6 PCI ATS on mtl ES2 */ + if (ipu_ver == IPU_VER_6EP_MTL && boot_cpu_data.x86_stepping == 0x2 && + pci_ats_supported(dev)) + pci_disable_ats(dev); + /* no msi pci capability for IPU6EP */ if (ipu_ver == IPU_VER_6EP || ipu_ver == IPU_VER_6EP_MTL) { /* likely do nothing as msi not enabled by default */ @@ -438,7 +448,6 @@ if (!isp) return -ENOMEM; - dev_set_name(&pdev->dev, "intel-ipu"); isp->pdev = pdev; INIT_LIST_HEAD(&isp->devices); @@ -486,14 +495,18 @@ isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME; break; case IPU6EP_ADL_P_PCI_ID: - case IPU6EP_ADL_N_PCI_ID: case IPU6EP_RPL_P_PCI_ID: ipu_ver = IPU_VER_6EP; isp->cpd_fw_name = is_es ? IPU6EPES_FIRMWARE_NAME : IPU6EP_FIRMWARE_NAME; break; + case IPU6EP_ADL_N_PCI_ID: + ipu_ver = IPU_VER_6EP; + isp->cpd_fw_name = IPU6EPADLN_FIRMWARE_NAME; + break; case IPU6EP_MTL_PCI_ID: ipu_ver = IPU_VER_6EP_MTL; - isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME; + isp->cpd_fw_name = is_es ? IPU6EPMTLES_FIRMWARE_NAME + : IPU6EPMTL_FIRMWARE_NAME; break; default: WARN(1, "Unsupported IPU device"); @@ -538,7 +551,7 @@ rval = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, &pdev->dev); if (rval) { dev_err(&isp->pdev->dev, "Requesting signed firmware failed\n"); - return rval; + goto buttress_exit; } rval = ipu_cpd_validate_cpd_file(isp, isp->cpd_fw->data, @@ -676,8 +689,9 @@ if (!IS_ERR_OR_NULL(isp->psys)) pm_runtime_put(&isp->psys->dev); ipu_bus_del_devices(pdev); - ipu_buttress_exit(isp); release_firmware(isp->cpd_fw); +buttress_exit: + ipu_buttress_exit(isp); return rval; } diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-cpd.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-cpd.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-cpd.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-cpd.c 2023-10-18 15:30:24.000000000 +0800 @@ -269,7 +269,7 @@ if (ret) { dev_err(&isp->pdev->dev, "Unable to parse module data section!\n"); - dma_free_attrs(&isp->psys->dev, *pkg_dir_size, pkg_dir, + dma_free_attrs(&adev->dev, *pkg_dir_size, pkg_dir, *dma_addr, #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) NULL); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu.h 2023-10-18 15:30:24.000000000 +0800 @@ -113,9 +113,9 @@ int cio2_bridge_init(struct pci_dev *cio2); #endif -/* Helpers for building against various kernel versions */ #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) #include +/* Helpers for building against various kernel versions */ static inline struct media_pipeline *media_entity_pipeline(struct media_entity *entity) { return entity->pipe; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys.c 2023-10-18 15:30:24.000000000 +0800 @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2013 - 2022 Intel Corporation +// Copyright (C) 2013 - 2023 Intel Corporation #include #include @@ -46,9 +46,11 @@ #define GDA_MEMOPEN_THRESHOLD_INDEX 3 #define DEFAULT_DID_RATIO 90 -#define DEFAULT_LTR_VALUE 1023 +#define IPU6EP_LTR_VALUE 200 +#define IPU6EP_MTL_LTR_VALUE 1023 #define DEFAULT_IWAKE_THRESHOLD 0x42 -#define MINIMUM_MEM_OPEN_THRESHOLD 0xc +#define IPU6EP_MIN_MEMOPEN_TH 0x4 +#define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc #define DEFAULT_MEM_OPEN_TIME 10 #define ONE_THOUSAND_MICROSECOND 1000 /* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */ @@ -371,7 +373,7 @@ for (i = 0; i < NR_OF_CSI2_BE_SOC_DEV; i++) ipu_isys_csi2_be_soc_cleanup(&isys->csi2_be_soc[i]); - for (i = 0; i < csi2->nports; i++) + for (i = 0; i < csi2->nports && isys->csi2; i++) ipu_isys_csi2_cleanup(&isys->csi2[i]); } @@ -562,7 +564,7 @@ struct ltr_did ltrdid; u16 calc_fill_time_us = 0, ltr = 0, did = 0; enum ltr_did_type ltr_did_type; - u32 iwake_threshold, iwake_critical_threshold, page_num; + u32 iwake_threshold, iwake_critical_threshold, page_num, mem_threshold; u32 mem_open_threshold = 0; u64 threshold_bytes; u64 isys_pb_datarate_mbs = 0; @@ -610,8 +612,9 @@ enable_iwake(isys, true); calc_fill_time_us = (u16)(max_sram_size / isys_pb_datarate_mbs); - if (ipu_ver == IPU_VER_6EP_MTL) { - ltr = DEFAULT_LTR_VALUE; + if (ipu_ver == IPU_VER_6EP_MTL || ipu_ver == IPU_VER_6EP) { + ltr = (ipu_ver == IPU_VER_6EP_MTL) ? + IPU6EP_MTL_LTR_VALUE : IPU6EP_LTR_VALUE; did = calc_fill_time_us * DEFAULT_DID_RATIO / 100; ltr_did_type = LTR_ENHANNCE_IWAKE; } else { @@ -640,21 +643,23 @@ set_iwake_ltrdid(isys, ltr, did, ltr_did_type); mutex_lock(&iwake_watermark->mutex); - if (ipu_ver == IPU_VER_6EP_MTL) + if (ipu_ver == IPU_VER_6EP_MTL || ipu_ver == IPU_VER_6EP) set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, DEFAULT_IWAKE_THRESHOLD); else set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, iwake_threshold); - if (ipu_ver == IPU_VER_6EP_MTL) { + if (ipu_ver == IPU_VER_6EP_MTL || ipu_ver == IPU_VER_6EP) { /* Calculate number of pages that will be filled in 10 usec */ page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) / ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE; page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) % ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0; - mem_open_threshold = max_t(u32, MINIMUM_MEM_OPEN_THRESHOLD, - page_num); + + mem_threshold = (ipu_ver == IPU_VER_6EP_MTL) ? + IPU6EP_MTL_MIN_MEMOPEN_TH : IPU6EP_MIN_MEMOPEN_TH; + mem_open_threshold = max_t(u32, mem_threshold, page_num); dev_dbg(&isys->adev->dev, "%s mem_open_threshold: %u\n", __func__, mem_open_threshold); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c 2023-10-18 15:30:24.000000000 +0800 @@ -25,6 +25,7 @@ MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_VYUY8_1X16, MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-csi2.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-csi2.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-csi2.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-csi2.c 2023-10-18 15:30:24.000000000 +0800 @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2013 - 2022 Intel Corporation +// Copyright (C) 2013 - 2023 Intel Corporation #include #include @@ -19,12 +19,16 @@ #include "ipu-platform-regs.h" static const u32 csi2_supported_codes_pad_sink[] = { + 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, +#ifdef V4L2_PIX_FMT_Y210 MEDIA_BUS_FMT_YUYV10_1X20, +#endif MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, @@ -45,12 +49,16 @@ }; static const u32 csi2_supported_codes_pad_source[] = { + 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, +#ifdef V4L2_PIX_FMT_Y210 MEDIA_BUS_FMT_YUYV10_1X20, +#endif MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, @@ -73,7 +81,7 @@ .close = ipu_isys_subdev_close, }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 255) int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, s64 *link_freq) { struct ipu_isys_pipeline *pipe = @@ -107,9 +115,9 @@ #else int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, __s64 *link_freq) { - struct ipu_isys_pipeline *pipe = container_of(csi2->asd.sd.entity.pipe, - struct ipu_isys_pipeline, - pipe); + struct ipu_isys_pipeline *pipe = + container_of(media_entity_pipeline(&csi2->asd.sd.entity), + struct ipu_isys_pipeline, pipe); struct v4l2_subdev *ext_sd = media_entity_to_v4l2_subdev(pipe->external->entity); struct v4l2_ext_control c = {.id = V4L2_CID_LINK_FREQ, }; @@ -258,8 +266,7 @@ { struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); + to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); struct ipu_isys_csi2_config *cfg; struct v4l2_subdev *ext_sd; struct ipu_isys_csi2_timing timing = {0}; @@ -322,17 +329,16 @@ if (!link->sink->entity || !link->source->entity) return -EINVAL; - csi2 = - to_ipu_isys_csi2(media_entity_to_v4l2_subdev(link->sink->entity)); media_pipe = media_entity_pipeline(link->sink->entity); if (!media_pipe) return -EINVAL; + csi2 = + to_ipu_isys_csi2(media_entity_to_v4l2_subdev(link->sink->entity)); ip = to_ipu_isys_pipeline(media_pipe); csi2->receiver_errors = 0; ip->csi2 = csi2; ipu_isys_video_add_capture_done(ip, csi2_capture_done); - rval = v4l2_subdev_link_validate(link); if (rval) return rval; @@ -399,8 +405,7 @@ struct v4l2_subdev_format *sink_fmt) { struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); + to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); if (source_fmt->format.field == V4L2_FIELD_ALTERNATE) ip->interlaced = true; @@ -469,31 +474,6 @@ WARN_ON(1); } -static const struct ipu_isys_pixelformat * -csi2_try_fmt(struct ipu_isys_video *av, - struct v4l2_pix_format_mplane *mpix) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(av->vdev.entity.links[0].source-> - entity); -#else - struct media_link *link = list_first_entry(&av->vdev.entity.links, - struct media_link, list); - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(link->source->entity); -#endif - struct ipu_isys_csi2 *csi2; - - if (!sd) - return NULL; - - csi2 = to_ipu_isys_csi2(sd); - - return ipu_isys_video_try_fmt_vid_mplane(av, mpix, - v4l2_ctrl_g_ctrl(csi2->store_csi2_header)); -} - void ipu_isys_csi2_cleanup(struct ipu_isys_csi2 *csi2) { if (!csi2->isys) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys.h 2023-10-18 15:30:24.000000000 +0800 @@ -56,7 +56,11 @@ #define IPU_ISYS_MAX_WIDTH 16384U #define IPU_ISYS_MAX_HEIGHT 16384U +#ifdef CONFIG_IPU_SINGLE_BE_SOC_DEVICE #define NR_OF_CSI2_BE_SOC_DEV 1 +#else +#define NR_OF_CSI2_BE_SOC_DEV 8 +#endif /* the threshold granularity is 2KB on IPU6 */ #define IPU6_SRAM_GRANULRITY_SHIFT 11 diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-queue.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-queue.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-queue.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-queue.c 2023-10-18 15:30:24.000000000 +0800 @@ -527,7 +527,7 @@ return rval; } -static void __buf_queue(struct vb2_buffer *vb, bool force) +static void buf_queue(struct vb2_buffer *vb) { struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); @@ -566,8 +566,9 @@ if (ib->req) return; - if (!pipe_av || !media_pipe || !vb->vb2_queue->streaming) { - dev_info(&av->isys->adev->dev, + if (!pipe_av || !media_pipe || + !vb->vb2_queue->start_streaming_called) { + dev_dbg(&av->isys->adev->dev, "no pipe or streaming, adding to incoming\n"); return; } @@ -575,8 +576,8 @@ mutex_unlock(&av->mutex); mutex_lock(&pipe_av->mutex); - if (!force && ip->nr_streaming != ip->nr_queues) { - dev_info(&av->isys->adev->dev, + if (ip->nr_streaming != ip->nr_queues) { + dev_dbg(&av->isys->adev->dev, "not streaming yet, adding to incoming\n"); goto out; } @@ -593,7 +594,7 @@ "error: buffer list get failed\n"); WARN_ON(1); } else { - dev_info(&av->isys->adev->dev, + dev_dbg(&av->isys->adev->dev, "not enough buffers available\n"); } goto out; @@ -602,8 +603,6 @@ msg = ipu_get_fw_msg_buf(ip); if (!msg) { rval = -ENOMEM; - dev_err(&av->isys->adev->dev, - "failed to get fw msg buf\n"); goto out; } buf = to_frame_msg_buf(msg); @@ -614,7 +613,7 @@ ip->nr_output_pins); if (!ip->streaming) { - dev_info(&av->isys->adev->dev, + dev_dbg(&av->isys->adev->dev, "got a buffer to start streaming!\n"); rval = ipu_isys_stream_start(ip, &bl, true); if (rval) @@ -644,11 +643,6 @@ mutex_lock(&av->mutex); } -static void buf_queue(struct vb2_buffer *vb) -{ - __buf_queue(vb, false); -} - int ipu_isys_link_fmt_validate(struct ipu_isys_queue *aq) { struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-subdev.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-subdev.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-subdev.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-subdev.c 2023-10-18 15:30:24.000000000 +0800 @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2013 - 2020 Intel Corporation +// Copyright (C) 2013 - 2023 Intel Corporation #include #include @@ -17,12 +17,15 @@ switch (code) { case MEDIA_BUS_FMT_RGB888_1X24: return 24; +#ifdef V4L2_PIX_FMT_Y210 case MEDIA_BUS_FMT_YUYV10_1X20: return 20; +#endif case MEDIA_BUS_FMT_Y10_1X10: case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: return 16; case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: @@ -34,6 +37,7 @@ case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: return 10; + case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: @@ -56,10 +60,13 @@ return IPU_ISYS_MIPI_CSI2_TYPE_RGB565; case MEDIA_BUS_FMT_RGB888_1X24: return IPU_ISYS_MIPI_CSI2_TYPE_RGB888; +#ifdef V4L2_PIX_FMT_Y210 case MEDIA_BUS_FMT_YUYV10_1X20: return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_10; +#endif case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_8; case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: @@ -72,6 +79,7 @@ case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: return IPU_ISYS_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: @@ -694,8 +702,7 @@ struct v4l2_subdev *source_sd = media_entity_to_v4l2_subdev(link->source->entity); struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); + to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); if (!source_sd) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-tpg.h ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-tpg.h --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-tpg.h 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-tpg.h 2023-10-18 15:30:24.000000000 +0800 @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2013 - 2020 Intel Corporation */ +/* Copyright (C) 2013 - 2023 Intel Corporation */ #ifndef IPU_ISYS_TPG_H #define IPU_ISYS_TPG_H @@ -74,7 +74,6 @@ void __iomem *base; void __iomem *sel; unsigned int index; - int streaming; struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-video.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-video.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-isys-video.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-isys-video.c 2023-10-18 15:30:24.000000000 +0800 @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2013 - 2022 Intel Corporation +// Copyright (C) 2013 - 2023 Intel Corporation #include #include @@ -35,13 +35,25 @@ #include "ipu-fw-isys.h" #include "ipu-fw-com.h" +#define IPU_IS_DEFAULT_FREQ \ + (IPU_IS_FREQ_RATIO_BASE * IPU_IS_FREQ_CTL_DEFAULT_RATIO) +#define IPU6SE_IS_DEFAULT_FREQ \ + (IPU_IS_FREQ_RATIO_BASE * IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO) + /* use max resolution pixel rate by default */ #define DEFAULT_PIXEL_RATE (360000000ULL * 2 * 4 / 10) +#define MAX_VIDEO_DEVICES 8 + +static int video_nr[MAX_VIDEO_DEVICES] = { [0 ...(MAX_VIDEO_DEVICES - 1)] = -1 }; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "video device numbers (-1=auto, 0=/dev/video0, etc.)"); + const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[] = { {V4L2_PIX_FMT_Y10, 16, 10, 0, MEDIA_BUS_FMT_Y10_1X10, IPU_FW_ISYS_FRAME_FORMAT_RAW16}, - {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, + {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_VYUY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_Z16, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, @@ -53,6 +65,8 @@ IPU_FW_ISYS_FRAME_FORMAT_NV16}, {V4L2_PIX_FMT_XRGB32, 32, 32, 0, MEDIA_BUS_FMT_RGB565_1X16, IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, + {V4L2_PIX_FMT_Y12I, 24, 24, 0, MEDIA_BUS_FMT_RGB888_1X24, + IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, {V4L2_PIX_FMT_XBGR32, 32, 32, 0, MEDIA_BUS_FMT_RGB888_1X24, IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, /* Raw bayer formats. */ @@ -82,6 +96,10 @@ IPU_FW_ISYS_FRAME_FORMAT_RAW8}, {V4L2_PIX_FMT_GREY, 8, 8, 0, MEDIA_BUS_FMT_Y8_1X8, IPU_FW_ISYS_FRAME_FORMAT_RAW8}, +#ifdef V4L2_PIX_FMT_Y210 + {V4L2_PIX_FMT_Y210, 20, 20, 0, MEDIA_BUS_FMT_YUYV10_1X20, + IPU_FW_ISYS_FRAME_FORMAT_YUYV}, +#endif {} }; @@ -92,7 +110,7 @@ {V4L2_PIX_FMT_Y210, 20, 20, 0, MEDIA_BUS_FMT_YUYV10_1X20, IPU_FW_ISYS_FRAME_FORMAT_YUYV}, #endif - {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, + {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_VYUY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_Z16, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, @@ -635,6 +653,21 @@ return 0; } +static void set_buttress_isys_freq(struct ipu_isys_video *av, bool max) +{ + struct ipu_device *isp = av->isys->adev->isp; + + if (max) { + ipu_buttress_isys_freq_set(isp, BUTTRESS_MAX_FORCE_IS_FREQ); + } else { + if (ipu_ver == IPU_VER_6SE) + ipu_buttress_isys_freq_set(isp, IPU6SE_IS_DEFAULT_FREQ); + else + ipu_buttress_isys_freq_set(isp, IPU_IS_DEFAULT_FREQ); + } + +} + static void get_stream_opened(struct ipu_isys_video *av) { unsigned long flags; @@ -643,6 +676,8 @@ av->isys->stream_opened++; spin_unlock_irqrestore(&av->isys->lock, flags); + if (av->isys->stream_opened > 1) + set_buttress_isys_freq(av, true); } static void put_stream_opened(struct ipu_isys_video *av) @@ -653,6 +688,8 @@ av->isys->stream_opened--; spin_unlock_irqrestore(&av->isys->lock, flags); + if (av->isys->stream_opened <= 1) + set_buttress_isys_freq(av, false); } static int get_stream_handle(struct ipu_isys_video *av) @@ -1069,7 +1106,6 @@ struct media_pad *source_pad = media_pad_remote_pad_first(&av->pad); #endif struct ipu_fw_isys_cropping_abi *crop; - enum ipu_fw_isys_send_type send_type; int rval, rvalout, tout; rval = get_external_facing_format(ip, &source_fmt); @@ -1158,8 +1194,6 @@ IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) csi_short_packet_prepare_fw_cfg(ip, stream_cfg); - ipu_fw_isys_dump_stream_cfg(dev, stream_cfg); - ip->nr_output_pins = stream_cfg->nof_output_pins; rval = get_stream_handle(av); @@ -1172,6 +1206,8 @@ ipu_fw_isys_set_params(stream_cfg); + ipu_fw_isys_dump_stream_cfg(dev, stream_cfg); + rval = ipu_fw_isys_complex_cmd(av->isys, ip->stream_handle, stream_cfg, @@ -1220,22 +1256,8 @@ reinit_completion(&ip->stream_start_completion); - if (bl) { - send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; - ipu_fw_isys_dump_frame_buff_set(dev, buf, - stream_cfg->nof_output_pins); - rval = ipu_fw_isys_complex_cmd(av->isys, - ip->stream_handle, - buf, to_dma_addr(msg), - sizeof(*buf), - send_type); - } else { - send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_START; - rval = ipu_fw_isys_simple_cmd(av->isys, - ip->stream_handle, - send_type); - } - + rval = ipu_fw_isys_simple_cmd(av->isys, ip->stream_handle, + IPU_FW_ISYS_SEND_TYPE_STREAM_START); if (rval < 0) { dev_err(dev, "can't start streaming (%d)\n", rval); goto out_stream_close; @@ -1253,7 +1275,18 @@ rval = -EIO; goto out_stream_close; } - dev_dbg(dev, "start stream: complete\n"); + + if (!bl) + return 0; + + ipu_fw_isys_dump_frame_buff_set(dev, buf, stream_cfg->nof_output_pins); + rval = ipu_fw_isys_complex_cmd(av->isys, ip->stream_handle, buf, + to_dma_addr(msg), sizeof(*buf), + IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); + if (rval < 0) { + dev_err(dev, "can't queue buffers (%d)\n", rval); + goto out_stream_close; + } return 0; @@ -1338,7 +1371,6 @@ dev_err(dev, "stream close error: %d\n", ip->error); else dev_dbg(dev, "close stream: complete\n"); - put_stream_opened(av); put_stream_handle(av); } @@ -1395,10 +1427,10 @@ if (ip->interlaced && isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) short_packet_queue_destroy(ip); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) - media_pipeline_stop(av->vdev.entity.pads); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) media_pipeline_stop(&av->vdev.entity); +#else + media_pipeline_stop(av->vdev.entity.pads); #endif media_entity_enum_cleanup(&ip->entity_enum); return 0; @@ -1425,18 +1457,16 @@ ip->interlaced = false; rval = media_entity_enum_init(&ip->entity_enum, mdev); - if (rval) { - dev_err(dev, "entity enum init failed\n"); + if (rval) return rval; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) - rval = media_pipeline_start(av->vdev.entity.pads, &ip->pipe); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) rval = media_pipeline_start(&av->vdev.entity, &ip->pipe); +#else + rval = media_pipeline_start(av->vdev.entity.pads, &ip->pipe); #endif if (rval < 0) { - dev_err(dev, "pipeline start failed\n"); + dev_dbg(dev, "pipeline start failed\n"); goto out_enum_cleanup; } @@ -1447,10 +1477,8 @@ } rval = media_graph_walk_init(&graph, mdev); - if (rval) { - dev_err(dev, "graph walk init failed\n"); + if (rval) goto out_pipeline_stop; - } /* Gather all entities in the graph. */ mutex_lock(&mdev->graph_mutex); @@ -1477,10 +1505,10 @@ return 0; out_pipeline_stop: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) - media_pipeline_stop(av->vdev.entity.pads); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) media_pipeline_stop(&av->vdev.entity); +#else + media_pipeline_stop(av->vdev.entity.pads); #endif out_enum_cleanup: @@ -1823,8 +1851,9 @@ unsigned int pad, unsigned long pad_flags, unsigned int flags) { + static atomic_t video_dev_count = ATOMIC_INIT(0); const struct v4l2_ioctl_ops *ioctl_ops = NULL; - int rval; + int rval, video_dev_nr; mutex_init(&av->mutex); init_completion(&av->ip.stream_open_completion); @@ -1874,12 +1903,18 @@ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); video_set_drvdata(&av->vdev, av); + video_dev_nr = atomic_inc_return(&video_dev_count) - 1; + if (video_dev_nr < MAX_VIDEO_DEVICES) + video_dev_nr = video_nr[video_dev_nr]; + else + video_dev_nr = -1; + mutex_lock(&av->mutex); #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) - rval = video_register_device(&av->vdev, VFL_TYPE_GRABBER, -1); + rval = video_register_device(&av->vdev, VFL_TYPE_GRABBER, video_dev_nr); #else - rval = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); + rval = video_register_device(&av->vdev, VFL_TYPE_VIDEO, video_dev_nr); #endif if (rval) goto out_media_entity_cleanup; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-psys.c ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-psys.c --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/ipu-psys.c 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/ipu-psys.c 2023-10-18 15:30:24.000000000 +0800 @@ -208,17 +208,18 @@ } } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) - nr = get_user_pages(current, current->mm, - start & PAGE_MASK, npages, -#else + nr = get_user_pages(current, current->mm, start & PAGE_MASK, + npages, 1, 0, pages, NULL); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) nr = get_user_pages(start & PAGE_MASK, npages, -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) - 1, 0, + 1, 0, pages, NULL); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) + nr = get_user_pages(start & PAGE_MASK, npages, + FOLL_WRITE, pages, NULL); #else - FOLL_WRITE, + nr = get_user_pages(start & PAGE_MASK, npages, + FOLL_WRITE, pages); #endif - pages, NULL); if (nr < npages) goto error_up_read; } @@ -640,17 +641,17 @@ if (kbuf->kaddr) dma_buf_vunmap(kbuf->dbuf, kbuf->kaddr); #endif - if (kbuf->sgt) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + if (kbuf->sgt) dma_buf_unmap_attachment_unlocked(kbuf->db_attach, kbuf->sgt, DMA_BIDIRECTIONAL); #else + if (kbuf->sgt) dma_buf_unmap_attachment(kbuf->db_attach, kbuf->sgt, DMA_BIDIRECTIONAL); #endif - } if (kbuf->db_attach) dma_buf_detach(kbuf->dbuf, kbuf->db_attach); dma_buf_put(kbuf->dbuf); @@ -776,7 +777,9 @@ struct dma_buf *dbuf; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 255) \ || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) - struct iosys_map dmap; + struct iosys_map dmap = { + .is_iomem = false, + }; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && LINUX_VERSION_CODE != KERNEL_VERSION(5, 10, 46) struct dma_buf_map dmap; #endif @@ -1842,6 +1845,6 @@ MODULE_AUTHOR("Yunliang Ding "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel ipu processing system driver"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) || IS_ENABLED(CONFIG_DRM_I915_HAS_SRIOV) MODULE_IMPORT_NS(DMA_BUF); #endif diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/Kconfig ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/Kconfig --- ipu6-drivers-0~git202302081010.7fdfb5eb/drivers/media/pci/intel/Kconfig 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/drivers/media/pci/intel/Kconfig 2023-10-18 15:30:24.000000000 +0800 @@ -39,4 +39,13 @@ If you want supported sensors can be registered as IPU subdevices, select y here. +config IPU_SINGLE_BE_SOC_DEVICE + bool "Intel IPU uses only one BE SOC device" + default n + depends on VIDEO_INTEL_IPU6 + help + If selected, IPU exports only one BE SOC device. + + If you don't need multiple camera streaming, select y here. + source "drivers/media/pci/intel/ipu3/Kconfig" diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/.github/workflows/test.yml ipu6-drivers-0~git202310180730.3f813580/.github/workflows/test.yml --- ipu6-drivers-0~git202302081010.7fdfb5eb/.github/workflows/test.yml 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/.github/workflows/test.yml 2023-10-18 15:30:24.000000000 +0800 @@ -6,7 +6,6 @@ - cron: '30 1 * * *' #every day at 1:30am jobs: Ubuntu-2204-dkms: - # ubuntu-lastest is currently still 20.04 runs-on: ubuntu-latest container: ubuntu:22.04 steps: @@ -37,7 +36,7 @@ # Add unstable kernel ppa add-apt-repository ppa:canonical-kernel-team/unstable apt-get update --quiet; - # latest and wip generic kernel headers + # latest generic kernel headers apt-get install --yes linux-headers-generic linux-headers-generic-hwe-22.04-edge # latest oem kernel apt-get install --yes linux-headers-oem-20.04 linux-headers-oem-22.04 linux-headers-oem-22.04b linux-headers-oem-22.04c @@ -80,7 +79,6 @@ echo "#### Successful builds for kernels: ${succeeded}"; Ubuntu-rolling-dkms: - # ubuntu-lastest is currently still 20.04 runs-on: ubuntu-latest container: ubuntu:rolling steps: @@ -112,9 +110,9 @@ add-apt-repository ppa:canonical-kernel-team/unstable apt-get update --quiet; # latest and wip generic kernel headers - apt-get install --yes linux-headers-generic + apt-get install --yes linux-headers-generic linux-headers-generic-wip # latest oem kernel - apt-get install --yes linux-headers-oem-22.04 linux-headers-oem-22.04a + apt-get install --yes linux-headers-oem-22.04 - name: Register with dkms shell: bash @@ -152,4 +150,71 @@ exit 1 fi echo "#### Successful builds for kernels: ${succeeded}"; + Debian-sid-dkms: + runs-on: ubuntu-latest + container: debian:sid + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Checkout ivsc-driver repo + uses: actions/checkout@v3 + with: + repository: intel/ivsc-driver + path: ivsc-driver + + - name: Merge with ivsc-driver + shell: bash + run: | + cp -r ivsc-driver/backport-include ivsc-driver/drivers ivsc-driver/include . + rm -rf ivsc-driver + + - name: Prepare environment + shell: bash + run: | + apt-get update --quiet; + apt-get install --yes --no-install-recommends dkms gpg-agent kmod software-properties-common sudo + + - name: Download header files + shell: bash + run: | + apt-get update --quiet; + # latest kernel + apt-get install --yes linux-headers-amd64 + + - name: Register with dkms + shell: bash + run: | + dkms add . + - name: Compile driver + shell: bash + run: | + # run dkms build and all available kernel headers + failed="" + succeeded="" + for kver in /lib/modules/*/build; do + # skip the kernel headers from the azure kernel. These kernel headers + # are preinstalled and of no interest + if [[ "$kver" == *"azure"* ]]; then + echo "Skipping $kver - This is the kernel of the github runner."; + continue; + fi; + test -d $kver || continue + kver=${kver%/build} + kver=${kver##*/} + echo "=== Testing ${kver} ==="; + echo "running: dkms build -m ipu6-drivers/0.0.0 -k ${kver}"; + ret=$(sudo dkms build -m ipu6-drivers/0.0.0 -k ${kver} >&2; echo $?); + if [ ${ret} -eq 0 ]; then + succeeded="${succeeded} ${kver}" + else + echo "#### Skipped unexpected failure ${kver}"; + failed="${failed} ${kver}"; + fi; + done + if [ "x${failed}" != "x" ]; then + echo "#### Failed kernels: ${failed}"; + exit 1 + fi + echo "#### Successful builds for kernels: ${succeeded}"; diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/Makefile ipu6-drivers-0~git202310180730.3f813580/Makefile --- ipu6-drivers-0~git202302081010.7fdfb5eb/Makefile 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/Makefile 2023-10-18 15:30:24.000000000 +0800 @@ -36,15 +36,19 @@ export CONFIG_VIDEO_INTEL_IPU6 = m export CONFIG_IPU_ISYS_BRIDGE = y +export CONFIG_IPU_SINGLE_BE_SOC_DEVICE = n export CONFIG_INTEL_SKL_INT3472 = m obj-y += drivers/media/pci/intel/ export CONFIG_VIDEO_HM11B1 = m +export CONFIG_VIDEO_GC5035 = m export CONFIG_VIDEO_OV01A1S = m export CONFIG_VIDEO_OV01A10 = m export CONFIG_VIDEO_OV02C10 = m +export CONFIG_VIDEO_OV02E10 = m export CONFIG_VIDEO_OV2740 = m export CONFIG_VIDEO_HM2170 = m +export CONFIG_VIDEO_HM2172 = m export CONFIG_VIDEO_HI556 = m # export CONFIG_POWER_CTRL_LOGIC = m obj-y += drivers/media/i2c/ @@ -55,12 +59,15 @@ ccflags-y += -I$(src)/backport-include/drivers/misc/mei/ -subdir-ccflags-y += -I$(src)/include/ +subdir-ccflags-y += -I$(src)/include/ \ + -DCONFIG_VIDEO_V4L2_SUBDEV_API subdir-ccflags-$(CONFIG_INTEL_VSC) += \ -DCONFIG_INTEL_VSC subdir-ccflags-$(CONFIG_IPU_ISYS_BRIDGE) += \ -DCONFIG_IPU_ISYS_BRIDGE +subdir-ccflags-$(CONFIG_IPU_SINGLE_BE_SOC_DEVICE) += \ + -DCONFIG_IPU_SINGLE_BE_SOC_DEVICE subdir-ccflags-$(CONFIG_INTEL_SKL_INT3472) += \ -DCONFIG_INTEL_SKL_INT3472 subdir-ccflags-$(CONFIG_POWER_CTRL_LOGIC) += \ diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/0002-iommu-Add-passthrough-for-MTL-IPU.patch ipu6-drivers-0~git202310180730.3f813580/patch/0002-iommu-Add-passthrough-for-MTL-IPU.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/0002-iommu-Add-passthrough-for-MTL-IPU.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/0002-iommu-Add-passthrough-for-MTL-IPU.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,77 @@ +diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c +index 52afcdaf7c7f..cc7df6c4e3ee 100644 +--- a/drivers/iommu/intel/iommu.c ++++ b/drivers/iommu/intel/iommu.c +@@ -37,6 +37,8 @@ + #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) + #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) + #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) ++#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ ++ ((pdev)->device == 0x7d19)) + #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) + + #define IOAPIC_RANGE_START (0xfee00000) +@@ -287,12 +289,14 @@ int intel_iommu_enabled = 0; + EXPORT_SYMBOL_GPL(intel_iommu_enabled); + + static int dmar_map_gfx = 1; ++static int dmar_map_ipu = 1; + static int intel_iommu_superpage = 1; + static int iommu_identity_mapping; + static int iommu_skip_te_disable; + + #define IDENTMAP_GFX 2 + #define IDENTMAP_AZALIA 4 ++#define IDENTMAP_IPU 8 + + const struct iommu_ops intel_iommu_ops; + +@@ -2584,6 +2588,9 @@ static int device_def_domain_type(struct device *dev) + + if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev)) + return IOMMU_DOMAIN_IDENTITY; ++ ++ if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) ++ return IOMMU_DOMAIN_IDENTITY; + } + + return 0; +@@ -2973,6 +2980,9 @@ static int __init init_dmars(void) + if (!dmar_map_gfx) + iommu_identity_mapping |= IDENTMAP_GFX; + ++ if (!dmar_map_ipu) ++ iommu_identity_mapping |= IDENTMAP_IPU; ++ + check_tylersburg_isoch(); + + ret = si_domain_init(hw_pass_through); +@@ -4813,6 +4823,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev) + dmar_map_gfx = 0; + } + ++static void quirk_iommu_ipu(struct pci_dev *dev) ++{ ++ if (!IS_INTEL_IPU(dev)) ++ return; ++ ++ if (risky_device(dev)) ++ return; ++ ++ pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n"); ++ dmar_map_ipu = 0; ++} ++ + /* G4x/GM45 integrated gfx dmar support is totally busted. */ + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx); +@@ -4848,6 +4870,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); + ++/* disable IPU dmar support */ ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu); ++ + static void quirk_iommu_rwbf(struct pci_dev *dev) + { + if (risky_device(dev)) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/gc5035-on-adlm/0001-Add-the-camera-sensor-gc5035-to-support-ADL-M.patch ipu6-drivers-0~git202310180730.3f813580/patch/gc5035-on-adlm/0001-Add-the-camera-sensor-gc5035-to-support-ADL-M.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/gc5035-on-adlm/0001-Add-the-camera-sensor-gc5035-to-support-ADL-M.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/gc5035-on-adlm/0001-Add-the-camera-sensor-gc5035-to-support-ADL-M.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,2399 @@ +From 0f5a529e1843d937e26c3303686418b3252f8c80 Mon Sep 17 00:00:00 2001 +From: "liang.wang" +Date: Tue, 18 Apr 2023 11:56:16 +0800 +Subject: [PATCH] Add the camera sensor gc5035 to support ADL-M. + +The sensor driver was updated to support several features: +1. Add acpi to support x86 platform. +2. Add some resolutions/control and more regs specifically for ADL-M. +3. Add gpio pins supported by int3272 and power on the sensor. +4. v4l2 support. +5. Adapt to intel IPU6-drivers, like 3A. +The sensor driver gc5035 is modified based on the following link: +https://patchwork.kernel.org/project/linux-media/patch/20200902224813.14283-4-tfiga@chromium.org + +Regarding to kernel update, don't forget to add the new sensor +support in .config + +Signed-off-by: liang.wang +--- + drivers/media/i2c/Kconfig | 13 + + drivers/media/i2c/Makefile | 1 + + drivers/media/i2c/gc5035.c | 2201 +++++++++++++++++ + drivers/platform/x86/intel/int3472/common.h | 3 +- + drivers/platform/x86/intel/int3472/discrete.c | 56 +- + 5 files changed, 2268 insertions(+), 6 deletions(-) + create mode 100644 drivers/media/i2c/gc5035.c + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 7806d4b81716..9f7ca594d32c 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -47,6 +47,19 @@ config VIDEO_AR0521 + To compile this driver as a module, choose M here: the + module will be called ar0521. + ++config VIDEO_GC5035 ++ tristate "Galaxycore GC5035 sensor support" ++ depends on I2C && VIDEO_DEV ++ select MEDIA_CONTROLLER ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a Video4Linux2 sensor driver for the Galaxycore ++ GC5035 imaging sensor.. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called gc5035. ++ + config VIDEO_HI556 + tristate "Hynix Hi-556 sensor support" + depends on I2C && VIDEO_DEV +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 0a2933103dd9..22f37eb2eaf9 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -102,6 +102,7 @@ obj-$(CONFIG_VIDEO_OV9734) += ov9734.o + obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o + obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o + obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o ++obj-$(CONFIG_VIDEO_GC5035) += gc5035.o + obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ + obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o + obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o +diff --git a/drivers/media/i2c/gc5035.c b/drivers/media/i2c/gc5035.c +new file mode 100644 +index 000000000000..b46fd5474b2a +--- /dev/null ++++ b/drivers/media/i2c/gc5035.c +@@ -0,0 +1,2201 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2020 Bitland Inc. ++// Copyright 2020 Google LLC.. ++// Copyright (c) 2022 Intel Corporation. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* External clock frequency supported by the driver */ ++#define GC5035_MCLK_RATE 24000000UL ++/* Number of lanes supported by this driver */ ++#define GC5035_DATA_LANES 2 ++/* Bits per sample of sensor output */ ++#define GC5035_BITS_PER_SAMPLE 10 ++ ++#define MIPI_FREQ 438000000LL ++ ++/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ ++#define GC5035_PIXEL_RATE (MIPI_FREQ * 2LL * 2LL / 10) ++ ++/* System registers (accessible regardless of the page. */ ++ ++/* Chip ID */ ++#define GC5035_REG_CHIP_ID_H 0xf0 ++#define GC5035_REG_CHIP_ID_L 0xf1 ++#define GC5035_CHIP_ID 0x5035 ++#define GC5035_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) ++ ++/* Register page selection register */ ++#define GC5035_PAGE_REG 0xfe ++ ++/* Page 0 registers */ ++ ++/* Exposure control */ ++#define GC5035_REG_EXPOSURE_H 0x03 ++#define GC5035_REG_EXPOSURE_L 0x04 ++#define GC5035_EXPOSURE_H_MASK 0x3f ++#define GC5035_EXPOSURE_MIN 4 ++#define GC5035_EXPOSURE_STEP 1 ++ ++/* Analog gain control */ ++#define GC5035_REG_ANALOG_GAIN 0xb6 ++#define GC5035_ANALOG_GAIN_MIN 256 ++#define GC5035_ANALOG_GAIN_MAX (16 * GC5035_ANALOG_GAIN_MIN) ++#define GC5035_ANALOG_GAIN_STEP 1 ++#define GC5035_ANALOG_GAIN_DEFAULT GC5035_ANALOG_GAIN_MIN ++ ++/* Digital gain control */ ++#define GC5035_REG_DIGI_GAIN_H 0xb1 ++#define GC5035_REG_DIGI_GAIN_L 0xb2 ++#define GC5035_DGAIN_H_MASK 0x0f ++#define GC5035_DGAIN_L_MASK 0xfc ++#define GC5035_DGAIN_L_SHIFT 2 ++#define GC5035_DIGI_GAIN_MIN 0 ++#define GC5035_DIGI_GAIN_MAX 1023 ++#define GC5035_DIGI_GAIN_STEP 1 ++#define GC5035_DIGI_GAIN_DEFAULT GC5035_DIGI_GAIN_MAX ++ ++/* Vblank control */ ++#define GC5035_REG_VTS_H 0x41 ++#define GC5035_REG_VTS_L 0x42 ++#define GC5035_VTS_H_MASK 0x3f ++#define GC5035_VTS_MAX 16383 ++#define GC5035_EXPOSURE_MARGIN 16 ++ ++#define GC5035_REG_CTRL_MODE 0x3e ++#define GC5035_MODE_SW_STANDBY 0x01 ++#define GC5035_MODE_STREAMING 0x91 ++ ++/* Page 1 registers */ ++ ++/* Test pattern control */ ++#define GC5035_REG_TEST_PATTERN 0x8c ++#define GC5035_TEST_PATTERN_ENABLE 0x11 ++#define GC5035_TEST_PATTERN_DISABLE 0x10 ++ ++/* Page 2 registers */ ++ ++/* OTP access registers */ ++#define GC5035_REG_OTP_MODE 0xf3 ++#define GC5035_OTP_PRE_READ 0x20 ++#define GC5035_OTP_READ_MODE 0x12 ++#define GC5035_OTP_READ_DONE 0x00 ++#define GC5035_REG_OTP_DATA 0x6c ++#define GC5035_REG_OTP_ACCESS_ADDR_H 0x69 ++#define GC5035_REG_OTP_ACCESS_ADDR_L 0x6a ++#define GC5035_OTP_ACCESS_ADDR_H_MASK 0x1f ++#define GC5035_OTP_ADDR_MASK 0x1fff ++#define GC5035_OTP_ADDR_SHIFT 3 ++#define GC5035_REG_DD_TOTALNUM_H 0x01 ++#define GC5035_REG_DD_TOTALNUM_L 0x02 ++#define GC5035_DD_TOTALNUM_H_MASK 0x07 ++#define GC5035_REG_DD_LOAD_STATUS 0x06 ++#define GC5035_OTP_BIT_LOAD BIT(0) ++ ++/* OTP-related definitions */ ++ ++#define GC5035_OTP_ID_SIZE 9 ++#define GC5035_OTP_ID_DATA_OFFSET 0x0020 ++#define GC5035_OTP_DATA_LENGTH 1024 ++ ++/* OTP DPC parameters */ ++#define GC5035_OTP_DPC_FLAG_OFFSET 0x0068 ++#define GC5035_OTP_DPC_FLAG_MASK 0x03 ++#define GC5035_OTP_FLAG_EMPTY 0x00 ++#define GC5035_OTP_FLAG_VALID 0x01 ++#define GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET 0x0070 ++#define GC5035_OTP_DPC_ERROR_NUMBER_OFFSET 0x0078 ++ ++/* OTP register parameters */ ++#define GC5035_OTP_REG_FLAG_OFFSET 0x0880 ++#define GC5035_OTP_REG_DATA_OFFSET 0x0888 ++#define GC5035_OTP_REG_ADDR_OFFSET 1 ++#define GC5035_OTP_REG_VAL_OFFSET 2 ++#define GC5035_OTP_PAGE_FLAG_OFFSET 3 ++#define GC5035_OTP_PER_PAGE_SIZE 4 ++#define GC5035_OTP_REG_PAGE_MASK 0x07 ++#define GC5035_OTP_REG_MAX_GROUP 5 ++#define GC5035_OTP_REG_BYTE_PER_GROUP 5 ++#define GC5035_OTP_REG_PER_GROUP 2 ++#define GC5035_OTP_REG_BYTE_PER_REG 2 ++#define GC5035_OTP_REG_DATA_SIZE 25 ++#define GC5035_OTP_REG_SIZE 10 ++ ++#define GC5035_DD_DELAY_US (10 * 1000) ++#define GC5035_DD_TIMEOUT_US (100 * 1000) ++ ++/* The clock source index in INT3472 CLDB */ ++#define INT3472_CLDB_CLKSRC_INDEX 14 ++ ++/* ++ * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 ++ * This _DSM GUID calls CLKC and CLKF. ++ */ ++static const guid_t clock_ctrl_guid = ++ GUID_INIT(0x82c0d13a, 0x78c5, 0x4244, ++ 0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11); ++ ++static const char * const gc5035_supplies[] = { ++ /* ++ * Requested separately due to power sequencing needs: ++ * "iovdd", * Power supply for I/O circuits * ++ */ ++ "dvdd12", /* Digital core power */ ++ "avdd21", /* Analog power */ ++}; ++ ++struct gc5035_regval { ++ u8 addr; ++ u8 val; ++}; ++ ++struct gc5035_reg { ++ u8 page; ++ struct gc5035_regval regval; ++}; ++ ++struct gc5035_otp_regs { ++ unsigned int num_regs; ++ struct gc5035_reg regs[GC5035_OTP_REG_SIZE]; ++}; ++ ++struct gc5035_dpc { ++ bool valid; ++ unsigned int total_num; ++}; ++ ++struct gc5035_mode { ++ u32 width; ++ u32 height; ++ u32 max_fps; ++ u32 hts_def; ++ u32 vts_def; ++ u32 exp_def; ++ const struct gc5035_regval *reg_list; ++ size_t num_regs; ++}; ++ ++ ++struct gc5035_power_ctrl { ++ /* Control Logic ACPI device */ ++ struct acpi_device *ctrl_logic; ++ /* GPIO for reset */ ++ struct gpio_desc *reset_gpio; ++ /* GPIO for power enable */ ++ struct gpio_desc *pwren_gpio; ++ /* GPIO for privacy LED */ ++ struct gpio_desc *pled_gpio; ++ ++ int status; ++ /* Clock source index */ ++ u8 clk_source_index; ++}; ++ ++struct gc5035 { ++ struct i2c_client *client; ++ struct clk *mclk; ++ unsigned long mclk_rate; ++ //struct gpio_desc *resetb_gpio; ++ ///struct gpio_desc *pwdn_gpio; ++ struct regulator *iovdd_supply; ++ struct regulator_bulk_data supplies[ARRAY_SIZE(gc5035_supplies)]; ++ ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vblank; ++ ++ u32 Dgain_ratio; ++ bool otp_read; ++ u8 otp_id[GC5035_OTP_ID_SIZE]; ++ struct gc5035_dpc dpc; ++ struct gc5035_otp_regs otp_regs; ++ ++ /* ++ * Serialize control access, get/set format, get selection ++ * and start streaming. ++ */ ++ struct mutex mutex; ++ struct gc5035_power_ctrl power; ++ bool streaming; ++ const struct gc5035_mode *cur_mode; ++}; ++ ++static inline struct gc5035 *to_gc5035(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct gc5035, subdev); ++} ++ ++static const struct gc5035_regval gc5035_otp_init_regs[] = { ++ {0xfc, 0x01}, ++ {0xf4, 0x40}, ++ {0xf5, 0xe9}, ++ {0xf6, 0x14}, ++ {0xf8, 0x49}, ++ {0xf9, 0x82}, ++ {0xfa, 0x00}, ++ {0xfc, 0x81}, ++ {0xfe, 0x00}, ++ {0x36, 0x01}, ++ {0xd3, 0x87}, ++ {0x36, 0x00}, ++ {0x33, 0x00}, ++ {0xf7, 0x01}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xee, 0x30}, ++ {0xfa, 0x10}, ++ {0xf5, 0xe9}, ++ {0xfe, 0x02}, ++ {0x67, 0xc0}, ++ {0x59, 0x3f}, ++ {0x55, 0x84}, ++ {0x65, 0x80}, ++ {0x66, 0x03}, ++ {0xfe, 0x00}, ++}; ++ ++static const struct gc5035_regval gc5035_otp_exit_regs[] = { ++ {0xfe, 0x02}, ++ {0x67, 0x00}, ++ {0xfe, 0x00}, ++ {0xfa, 0x00}, ++}; ++ ++static const struct gc5035_regval gc5035_dd_auto_load_regs[] = { ++ {0xfe, 0x02}, ++ {0xbe, 0x00}, ++ {0xa9, 0x01}, ++ {0x09, 0x33}, ++}; ++ ++static const struct gc5035_regval gc5035_otp_dd_regs[] = { ++ {0x03, 0x00}, ++ {0x04, 0x80}, ++ {0x95, 0x0a}, ++ {0x96, 0x30}, ++ {0x97, 0x0a}, ++ {0x98, 0x32}, ++ {0x99, 0x07}, ++ {0x9a, 0xa9}, ++ {0xf3, 0x80}, ++}; ++ ++static const struct gc5035_regval gc5035_otp_dd_enable_regs[] = { ++ {0xbe, 0x01}, ++ {0x09, 0x00}, ++ {0xfe, 0x01}, ++ {0x80, 0x02}, ++ {0xfe, 0x00}, ++}; ++ ++/* ++ * Xclk 24Mhz ++ * Pclk 87.6Mhz ++ * grabwindow_width 2592 ++ * grabwindow_height 1944 ++ * max_framerate 30fps ++ * mipi_datarate per lane 876Mbps ++ */ ++static const struct gc5035_regval gc5035_global_regs[] = { ++ /*init*/ ++ {0xfc, 0x01}, ++ {0xf4, 0x40}, ++ {0xf5, 0xe9}, ++ {0xf6, 0x14}, ++ {0xf8, 0x49}, ++ {0xf9, 0x82}, ++ {0xfa, 0x00}, ++ {0xfc, 0x81}, ++ {0xfe, 0x00}, ++ {0x36, 0x01}, ++ {0xd3, 0x87}, ++ {0x36, 0x00}, ++ {0x33, 0x00}, ++ {0xfe, 0x03}, ++ {0x01, 0xe7}, ++ {0xf7, 0x01}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xee, 0x30}, ++ {0x87, 0x18}, ++ {0xfe, 0x01}, ++ {0x8c, 0x90}, ++ {0xfe, 0x00}, ++ ++ /* Analog & CISCTL */ ++ {0xfe, 0x00}, ++ {0x05, 0x02}, ++ {0x06, 0xda}, ++ {0x9d, 0x0c}, ++ {0x09, 0x00}, ++ {0x0a, 0x04}, ++ {0x0b, 0x00}, ++ {0x0c, 0x03}, ++ {0x0d, 0x07}, ++ {0x0e, 0xa8}, ++ {0x0f, 0x0a}, ++ {0x10, 0x30}, ++ {0x11, 0x02}, ++ {0x17, 0x80}, ++ {0x19, 0x05}, ++ {0xfe, 0x02}, ++ {0x30, 0x03}, ++ {0x31, 0x03}, ++ {0xfe, 0x00}, ++ {0xd9, 0xc0}, ++ {0x1b, 0x20}, ++ {0x21, 0x48}, ++ {0x28, 0x22}, ++ {0x29, 0x58}, ++ {0x44, 0x20}, ++ {0x4b, 0x10}, ++ {0x4e, 0x1a}, ++ {0x50, 0x11}, ++ {0x52, 0x33}, ++ {0x53, 0x44}, ++ {0x55, 0x10}, ++ {0x5b, 0x11}, ++ {0xc5, 0x02}, ++ {0x8c, 0x1a}, ++ {0xfe, 0x02}, ++ {0x33, 0x05}, ++ {0x32, 0x38}, ++ {0xfe, 0x00}, ++ {0x91, 0x80}, ++ {0x92, 0x28}, ++ {0x93, 0x20}, ++ {0x95, 0xa0}, ++ {0x96, 0xe0}, ++ {0xd5, 0xfc}, ++ {0x97, 0x28}, ++ {0x16, 0x0c}, ++ {0x1a, 0x1a}, ++ {0x1f, 0x11}, ++ {0x20, 0x10}, ++ {0x46, 0x83}, ++ {0x4a, 0x04}, ++ {0x54, 0x02}, ++ {0x62, 0x00}, ++ {0x72, 0x8f}, ++ {0x73, 0x89}, ++ {0x7a, 0x05}, ++ {0x7d, 0xcc}, ++ {0x90, 0x00}, ++ {0xce, 0x18}, ++ {0xd0, 0xb2}, ++ {0xd2, 0x40}, ++ {0xe6, 0xe0}, ++ {0xfe, 0x02}, ++ {0x12, 0x01}, ++ {0x13, 0x01}, ++ {0x14, 0x01}, ++ {0x15, 0x02}, ++ {0x22, 0x7c}, ++ {0x91, 0x00}, ++ {0x92, 0x00}, ++ {0x93, 0x00}, ++ {0x94, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ ++ /* Gain */ ++ {0xfe, 0x00}, ++ {0xb0, 0x6e}, ++ {0xb1, 0x01}, ++ {0xb2, 0x00}, ++ {0xb3, 0x00}, ++ {0xb4, 0x00}, ++ {0xb6, 0x00}, ++ ++ /* ISP */ ++ {0xfe, 0x01}, ++ {0x53, 0x00}, ++ {0x89, 0x03}, ++ {0x60, 0x40}, ++ ++ /* BLK */ ++ {0xfe, 0x01}, ++ {0x42, 0x21}, ++ {0x49, 0x03}, ++ {0x4a, 0xff}, ++ {0x4b, 0xc0}, ++ {0x55, 0x00}, ++ ++ /* Anti_blooming */ ++ {0xfe, 0x01}, ++ {0x41, 0x28}, ++ {0x4c, 0x00}, ++ {0x4d, 0x00}, ++ {0x4e, 0x3c}, ++ {0x44, 0x08}, ++ {0x48, 0x02}, ++ ++ /* Crop */ ++ {0xfe, 0x01}, ++ {0x91, 0x00}, ++ {0x92, 0x08}, ++ {0x93, 0x00}, ++ {0x94, 0x07}, ++ {0x95, 0x07}, ++ {0x96, 0x98}, ++ {0x97, 0x0a}, ++ {0x98, 0x20}, ++ {0x99, 0x00}, ++ ++ /* MIPI */ ++ {0xfe, 0x03}, ++ {0x02, 0x57}, ++ {0x03, 0xb7}, ++ {0x15, 0x14}, ++ {0x18, 0x0f}, ++ {0x21, 0x22}, ++ {0x22, 0x06}, ++ {0x23, 0x48}, ++ {0x24, 0x12}, ++ {0x25, 0x28}, ++ {0x26, 0x08}, ++ {0x29, 0x06}, ++ {0x2a, 0x58}, ++ {0x2b, 0x08}, ++ {0xfe, 0x01}, ++ {0x8c, 0x10}, ++ ++ {0xfe, 0x00}, ++ {0x3e, 0x01}, ++}; ++ ++/* ++ * Xclk 24Mhz ++ * Pclk 87.6Mhz ++ * grabwindow_width 2592 ++ * grabwindow_height 1944 ++ * max_framerate 30fps ++ * mipi_datarate per lane 876Mbps ++ */ ++static const struct gc5035_regval gc5035_2592x1944_regs[] = { ++ /* System */ ++ {0xfe, 0x00}, ++ {0x3e, 0x01}, ++ {0xfc, 0x01}, ++ {0xf4, 0x40}, ++ {0xf5, 0xe9}, ++ {0xf6, 0x14}, ++ {0xf8, 0x58}, ++ {0xf9, 0x82}, ++ {0xfa, 0x00}, ++ {0xfc, 0x81}, ++ {0xfe, 0x00}, ++ {0x36, 0x01}, ++ {0xd3, 0x87}, ++ {0x36, 0x00}, ++ {0x33, 0x00}, ++ {0xfe, 0x03}, ++ {0x01, 0xe7}, ++ {0xf7, 0x01}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xee, 0x30}, ++ {0x87, 0x18}, ++ {0xfe, 0x01}, ++ {0x8c, 0x90}, ++ {0xfe, 0x00}, ++ /*Analog & CISCTL*/ ++ {0x03, 0x03},//0x06 ++ {0x04, 0xd8}, ++ {0x41, 0x07},//08 ++ {0x42, 0xd8},//58 ++ {0x05, 0x02}, ++ {0x06, 0xda}, ++ {0x9d, 0x18}, ++ {0x09, 0x00}, ++ {0x0a, 0x04}, ++ {0x0b, 0x00}, ++ {0x0c, 0x03}, ++ {0x0d, 0x07}, ++ {0x0e, 0xa8}, ++ {0x0f, 0x0a}, ++ {0x10, 0x30}, ++ {0x11, 0x02}, ++ {0x17, 0x80}, ++ {0x19, 0x05}, ++ {0xfe, 0x02}, ++ {0x30, 0x03}, ++ {0x31, 0x03}, ++ {0xfe, 0x00}, ++ {0xd9, 0xc0}, ++ {0x1b, 0x20}, ++ {0x21, 0x40}, ++ {0x28, 0x22}, ++ {0x29, 0x56}, ++ {0x44, 0x20}, ++ {0x4b, 0x10}, ++ {0x4e, 0x1a}, ++ {0x50, 0x11}, ++ {0x52, 0x33}, ++ {0x53, 0x44}, ++ {0x55, 0x10}, ++ {0x5b, 0x11}, ++ {0xc5, 0x02}, ++ {0x8c, 0x1a}, ++ {0xfe, 0x02}, ++ {0x33, 0x05}, ++ {0x32, 0x38}, ++ {0xfe, 0x00}, ++ {0x91, 0x80}, ++ {0x92, 0x28}, ++ {0x93, 0x20}, ++ {0x95, 0xa0}, ++ {0x96, 0xe0}, ++ {0xd5, 0xfc}, ++ {0x97, 0x28}, ++ {0x16, 0x0c}, ++ {0x1a, 0x1a}, ++ {0x1f, 0x11}, ++ {0x20, 0x10}, ++ {0x46, 0x83}, ++ {0x4a, 0x04}, ++ {0x54, 0x02}, ++ {0x62, 0x00}, ++ {0x72, 0x8f}, ++ {0x73, 0x89}, ++ {0x7a, 0x05}, ++ {0x7d, 0xcc}, ++ {0x90, 0x00}, ++ {0xce, 0x18}, ++ {0xd0, 0xb2}, ++ {0xd2, 0x40}, ++ {0xe6, 0xe0}, ++ {0xfe, 0x02}, ++ {0x12, 0x01}, ++ {0x13, 0x01}, ++ {0x14, 0x01}, ++ {0x15, 0x02}, ++ {0x22, 0x7c}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ /*GAIN*/ ++ {0xfe, 0x00}, ++ {0xb0, 0x6e}, ++ {0xb1, 0x01}, ++ {0xb2, 0x00}, ++ {0xb3, 0x00}, ++ {0xb4, 0x00}, ++ {0xb6, 0x00}, ++ /*ISP*/ ++ {0xfe, 0x01}, ++ {0x53, 0x00}, ++ {0x89, 0x03}, ++ {0x60, 0x40}, ++ /*BLK*/ ++ {0xfe, 0x01}, ++ {0x42, 0x21}, ++ {0x49, 0x03}, ++ {0x4a, 0xff}, ++ {0x4b, 0xc0}, ++ {0x55, 0x00}, ++ /*anti_blooming*/ ++ {0xfe, 0x01}, ++ {0x41, 0x28}, ++ {0x4c, 0x00}, ++ {0x4d, 0x00}, ++ {0x4e, 0x3c}, ++ {0x44, 0x08}, ++ {0x48, 0x02}, ++ /*CROP*/ ++ {0xfe, 0x01}, ++ {0x91, 0x00}, ++ {0x92, 0x08}, ++ {0x93, 0x00}, ++ {0x94, 0x08}, ++ {0x95, 0x07}, ++ {0x96, 0x98}, ++ {0x97, 0x0a}, ++ {0x98, 0x20}, ++ {0x99, 0x00}, ++ /*MIPI*/ ++ {0xfe, 0x03}, ++ {0x02, 0x57}, ++ {0x03, 0xb7}, ++ {0x15, 0x14}, ++ {0x18, 0x0f}, ++ {0x21, 0x22}, ++ {0x22, 0x06}, ++ {0x23, 0x48}, ++ {0x24, 0x12}, ++ {0x25, 0x28}, ++ {0x26, 0x08}, ++ {0x29, 0x06}, ++ {0x2a, 0x58}, ++ {0x2b, 0x08}, ++ {0xfe, 0x01}, ++ {0x8c, 0x10}, ++ {0xfe, 0x00}, ++ {0x3e, 0x01}, ++}; ++ ++/* ++ * Xclk 24Mhz ++ * Pclk 87.6Mhz ++ * grabwindow_width 1296 ++ * grabwindow_height 972 ++ * mipi_datarate per lane 876Mbps ++ */ ++static const struct gc5035_regval gc5035_1296x972_regs[] = { ++ /*NULL*/ ++ {0xfe, 0x00}, ++ {0x3e, 0x01}, ++ {0xfc, 0x01}, ++ {0xf4, 0x40}, ++ {0xf5, 0xe4}, ++ {0xf6, 0x14}, ++ {0xf8, 0x49}, ++ {0xf9, 0x12}, ++ {0xfa, 0x01}, ++ {0xfc, 0x81}, ++ {0xfe, 0x00}, ++ {0x36, 0x01}, ++ {0xd3, 0x87}, ++ {0x36, 0x00}, ++ {0x33, 0x20}, ++ {0xfe, 0x03}, ++ {0x01, 0x87}, ++ {0xf7, 0x11}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xee, 0x30}, ++ {0x87, 0x18}, ++ {0xfe, 0x01}, ++ {0x8c, 0x90}, ++ {0xfe, 0x00}, ++ ++ /* Analog & CISCTL */ ++ {0xfe, 0x00}, ++ {0x05, 0x02}, ++ {0x06, 0xda}, ++ {0x9d, 0x0c}, ++ {0x09, 0x00}, ++ {0x0a, 0x04}, ++ {0x0b, 0x00}, ++ {0x0c, 0x03}, ++ {0x0d, 0x07}, ++ {0x0e, 0xa8}, ++ {0x0f, 0x0a}, ++ {0x10, 0x30}, ++ {0x21, 0x60}, ++ {0x29, 0x30}, ++ {0x44, 0x18}, ++ {0x4e, 0x20}, ++ {0x8c, 0x20}, ++ {0x91, 0x15}, ++ {0x92, 0x3a}, ++ {0x93, 0x20}, ++ {0x95, 0x45}, ++ {0x96, 0x35}, ++ {0xd5, 0xf0}, ++ {0x97, 0x20}, ++ {0x1f, 0x19}, ++ {0xce, 0x18}, ++ {0xd0, 0xb3}, ++ {0xfe, 0x02}, ++ {0x14, 0x02}, ++ {0x15, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ ++ /* BLK */ ++ {0xfe, 0x01}, ++ {0x49, 0x00}, ++ {0x4a, 0x01}, ++ {0x4b, 0xf8}, ++ ++ /* Anti_blooming */ ++ {0xfe, 0x01}, ++ {0x4e, 0x06}, ++ {0x44, 0x02}, ++ ++ /* Crop */ ++ {0xfe, 0x01}, ++ {0x91, 0x00}, ++ {0x92, 0x04}, ++ {0x93, 0x00}, ++ {0x94, 0x03}, ++ {0x95, 0x03}, ++ {0x96, 0xcc}, ++ {0x97, 0x05}, ++ {0x98, 0x10}, ++ {0x99, 0x00}, ++ ++ /* MIPI */ ++ {0xfe, 0x03}, ++ {0x02, 0x58}, ++ {0x22, 0x03}, ++ {0x26, 0x06}, ++ {0x29, 0x03}, ++ {0x2b, 0x06}, ++ {0xfe, 0x01}, ++ {0x8c, 0x10}, ++}; ++ ++/* ++ * Xclk 24Mhz ++ * Pclk 87.6Mhz ++ * linelength 672{0x2a0) ++ * framelength 2232{0x8b8) ++ * grabwindow_width 1280 ++ * grabwindow_height 720 ++ * max_framerate 30fps ++ * mipi_datarate per lane 876Mbps ++ */ ++static const struct gc5035_regval gc5035_1280x720_regs[] = { ++ /* System */ ++ {0xfe, 0x00}, ++ {0x3e, 0x01}, ++ {0xfc, 0x01}, ++ {0xf4, 0x40}, ++ {0xf5, 0xe4}, ++ {0xf6, 0x14}, ++ {0xf8, 0x49}, ++ {0xf9, 0x12}, ++ {0xfa, 0x01}, ++ {0xfc, 0x81}, ++ {0xfe, 0x00}, ++ {0x36, 0x01}, ++ {0xd3, 0x87}, ++ {0x36, 0x00}, ++ {0x33, 0x20}, ++ {0xfe, 0x03}, ++ {0x01, 0x87}, ++ {0xf7, 0x11}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8f}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xee, 0x30}, ++ {0x87, 0x18}, ++ {0xfe, 0x01}, ++ {0x8c, 0x90}, ++ {0xfe, 0x00}, ++ ++ /* Analog & CISCTL */ ++ {0xfe, 0x00}, ++ {0x05, 0x02}, ++ {0x06, 0xda}, ++ {0x9d, 0x0c}, ++ {0x09, 0x00}, ++ {0x0a, 0x04}, ++ {0x0b, 0x00}, ++ {0x0c, 0x03}, ++ {0x0d, 0x07}, ++ {0x0e, 0xa8}, ++ {0x0f, 0x0a}, ++ {0x10, 0x30}, ++ {0x21, 0x60}, ++ {0x29, 0x30}, ++ {0x44, 0x18}, ++ {0x4e, 0x20}, ++ {0x8c, 0x20}, ++ {0x91, 0x15}, ++ {0x92, 0x3a}, ++ {0x93, 0x20}, ++ {0x95, 0x45}, ++ {0x96, 0x35}, ++ {0xd5, 0xf0}, ++ {0x97, 0x20}, ++ {0x1f, 0x19}, ++ {0xce, 0x18}, ++ {0xd0, 0xb3}, ++ {0xfe, 0x02}, ++ {0x14, 0x02}, ++ {0x15, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfe, 0x00}, ++ {0xfc, 0x88}, ++ {0xfe, 0x10}, ++ {0xfe, 0x00}, ++ {0xfc, 0x8e}, ++ ++ /* BLK */ ++ {0xfe, 0x01}, ++ {0x49, 0x00}, ++ {0x4a, 0x01}, ++ {0x4b, 0xf8}, ++ ++ /* Anti_blooming */ ++ {0xfe, 0x01}, ++ {0x4e, 0x06}, ++ {0x44, 0x02}, ++ ++ /* Crop */ ++ {0xfe, 0x01}, ++ {0x91, 0x00}, ++ {0x92, 0x0a}, ++ {0x93, 0x00}, ++ {0x94, 0x0b}, ++ {0x95, 0x02}, ++ {0x96, 0xd0}, ++ {0x97, 0x05}, ++ {0x98, 0x00}, ++ {0x99, 0x00}, ++ ++ /* MIPI */ ++ {0xfe, 0x03}, ++ {0x02, 0x58}, ++ {0x22, 0x03}, ++ {0x26, 0x06}, ++ {0x29, 0x03}, ++ {0x2b, 0x06}, ++ {0xfe, 0x01}, ++ {0x8c, 0x10}, ++ {0xfe, 0x00}, ++ {0x3e, 0x91}, ++}; ++ ++static const struct gc5035_mode gc5035_modes[] = { ++ { ++ .width = 2592, ++ .height = 1944, ++ .max_fps = 30, ++ .exp_def = 0x258, ++ .hts_def = 2920, ++ .vts_def = 2008, ++ .reg_list = gc5035_2592x1944_regs, ++ .num_regs = ARRAY_SIZE(gc5035_2592x1944_regs), ++ }, ++ { ++ .width = 1296, ++ .height = 972, ++ .max_fps = 30, ++ .exp_def = 0x258, ++ .hts_def = 1460, ++ .vts_def = 2008, ++ .reg_list = gc5035_1296x972_regs, ++ .num_regs = ARRAY_SIZE(gc5035_1296x972_regs), ++ }, ++ { ++ .width = 1280, ++ .height = 720, ++ .max_fps = 60, ++ .exp_def = 0x258, ++ .hts_def = 1896, ++ .vts_def = 1536, ++ .reg_list = gc5035_1280x720_regs, ++ .num_regs = ARRAY_SIZE(gc5035_1280x720_regs), ++ }, ++}; ++ ++static const char * const gc5035_test_pattern_menu[] = { ++ "Disabled", ++ "Color Bar", ++}; ++ ++static const s64 gc5035_link_freqs[] = { ++ 438000000, ++}; ++ ++static u64 gc5035_link_to_pixel_rate(u32 f_index) ++{ ++ u64 pixel_rate = gc5035_link_freqs[f_index] * 2 * GC5035_DATA_LANES; ++ ++ do_div(pixel_rate, GC5035_BITS_PER_SAMPLE); ++ ++ return pixel_rate; ++} ++ ++static struct gpio_desc* gc5035_get_gpio(struct gc5035 *gc5035, ++ const char* name) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct gpio_desc* gpio; ++ int ret; ++ ++ gpio = devm_gpiod_get(dev, name, GPIOD_OUT_HIGH); ++ ret = PTR_ERR_OR_ZERO(gpio); ++ if (ret < 0) { ++ gpio = NULL; ++ dev_warn(dev, "failed to get %s gpio: %d\n", name, ret); ++ } ++ ++ return gpio; ++} ++ ++static void gc5035_init_power_ctrl(struct gc5035 *gc5035) ++{ ++ struct gc5035_power_ctrl* power = &gc5035->power; ++ acpi_handle handle = ACPI_HANDLE(&gc5035->client->dev); ++ struct acpi_handle_list dep_devices; ++ acpi_status status; ++ int i = 0; ++ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; ++ union acpi_object *obj; ++ ++ power->ctrl_logic = NULL; ++ if (!acpi_has_method(handle, "_DEP")) ++ return; ++ ++ /* Call _DEP method of OV13B */ ++ status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); ++ if (ACPI_FAILURE(status)) { ++ acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); ++ return; ++ } ++ ++ /* Find INT3472 in _DEP */ ++ for (i = 0; i < dep_devices.count; i++) { ++ struct acpi_device *dep_device = NULL; ++ const char* dep_hid = NULL; ++ ++ if (dep_devices.handles[i]) { ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) ++ acpi_bus_get_device(dep_devices.handles[i], &dep_device); ++ #else ++ dep_device = acpi_fetch_acpi_dev(dep_devices.handles[i]); ++ #endif ++ } ++ if (dep_device) ++ dep_hid = acpi_device_hid(dep_device); ++ if (dep_hid && strcmp("INT3472", dep_hid) == 0) { ++ power->ctrl_logic = dep_device; ++ break; ++ } ++ } ++ ++ /* If we got INT3472 acpi device, read which clock source we'll use */ ++ if (power->ctrl_logic == NULL) ++ return; ++ status = acpi_evaluate_object(power->ctrl_logic->handle, ++ "CLDB", NULL, &buffer); ++ if (ACPI_FAILURE(status)) { ++ dev_warn(&gc5035->client->dev, "Read INT3472 CLDB failed"); ++ return; ++ } ++ ++ obj = buffer.pointer; ++ if (!obj) ++ dev_warn(&gc5035->client->dev, "INT3472 CLDB return NULL"); ++ if (obj->type != ACPI_TYPE_BUFFER) { ++ acpi_handle_err(power->ctrl_logic->handle, ++ "CLDB object is not an ACPI buffer\n"); ++ kfree(obj); ++ return; ++ } ++ if (obj->buffer.length < INT3472_CLDB_CLKSRC_INDEX + 1) { ++ acpi_handle_err(power->ctrl_logic->handle, ++ "The CLDB buffer size is wrong\n"); ++ kfree(obj); ++ return; ++ } ++ ++ /* Get the clock source index */ ++ gc5035->power.clk_source_index = ++ obj->buffer.pointer[INT3472_CLDB_CLKSRC_INDEX]; ++ kfree(obj); ++ ++ /* Get gpios mapped by INT3472 driver */ ++ power->reset_gpio = gc5035_get_gpio(gc5035, "reset"); ++ power->pwren_gpio = gc5035_get_gpio(gc5035, "pwren"); ++ power->pled_gpio = gc5035_get_gpio(gc5035, "pled"); ++ power->status = 0; ++} ++ ++static void gc5035_set_power(struct gc5035 *gc5035, int on) ++{ ++ struct gc5035_power_ctrl* power = &gc5035->power; ++ ++ on = (on ? 1 : 0); ++ if (on == power->status) ++ return; ++ ++ /* First, set reset pin as low */ ++ if (power->reset_gpio) { ++ gpiod_set_value_cansleep(power->reset_gpio, 0); ++ msleep(5); ++ } ++ ++ /* Use _DSM of INT3472 to enable clock */ ++ if (power->ctrl_logic) { ++ u8 clock_args[] = { power->clk_source_index, on, 0x01,}; ++ union acpi_object clock_ctrl_args = { ++ .buffer = { ++ .type = ACPI_TYPE_BUFFER, ++ .length = 3, ++ .pointer = clock_args, ++ }, ++ }; ++ acpi_evaluate_dsm(power->ctrl_logic->handle, ++ &clock_ctrl_guid, 0x00, 0x01, ++ &clock_ctrl_args); ++ } ++ ++ /* Set power pin and privacy LED pin */ ++ if (power->pwren_gpio) ++ gpiod_set_value_cansleep(power->pwren_gpio, on); ++ if (power->pled_gpio) ++ gpiod_set_value_cansleep(power->pled_gpio, on); ++ ++ /* If we need to power on, set reset pin to high at last */ ++ if (on && power->reset_gpio) { ++ gpiod_set_value_cansleep(power->reset_gpio, 1); ++ msleep(5); ++ } ++ power->status = on; ++} ++ ++static int gc5035_write_reg(struct gc5035 *gc5035, u8 reg, u8 val) ++{ ++ return i2c_smbus_write_byte_data(gc5035->client, reg, val); ++} ++ ++static int gc5035_write_array(struct gc5035 *gc5035, ++ const struct gc5035_regval *regs, ++ size_t num_regs) ++{ ++ unsigned int i; ++ int ret; ++ ++ for (i = 0; i < num_regs; i++) { ++ ret = gc5035_write_reg(gc5035, regs[i].addr, regs[i].val); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gc5035_read_reg(struct gc5035 *gc5035, u8 reg, u8 *val) ++{ ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(gc5035->client, reg); ++ if (ret < 0) ++ return ret; ++ ++ *val = (unsigned char)ret; ++ ++ return 0; ++} ++ ++static int gc5035_otp_read_data(struct gc5035 *gc5035, u16 bit_addr, u8 *data, ++ size_t length) ++{ ++ size_t i; ++ int ret; ++ ++ if (WARN_ON(bit_addr % 8)) ++ return -EINVAL; ++ ++ if (WARN_ON(bit_addr / 8 + length > GC5035_OTP_DATA_LENGTH)) ++ return -EINVAL; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 2); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_H, ++ (bit_addr >> 8) & ++ GC5035_OTP_ACCESS_ADDR_H_MASK); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_L, ++ bit_addr & 0xff); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, ++ GC5035_OTP_PRE_READ); ++ if (ret) ++ goto out_read_done; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, ++ GC5035_OTP_READ_MODE); ++ if (ret) ++ goto out_read_done; ++ ++ for (i = 0; i < length; i++) { ++ ret = gc5035_read_reg(gc5035, GC5035_REG_OTP_DATA, &data[i]); ++ if (ret) ++ goto out_read_done; ++ } ++ ++out_read_done: ++ gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, GC5035_OTP_READ_DONE); ++ ++ return ret; ++} ++ ++static int gc5035_read_otp_regs(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; ++ u8 regs[GC5035_OTP_REG_DATA_SIZE] = {0}; ++ unsigned int i, j; ++ u8 flag; ++ int ret; ++ ++ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_FLAG_OFFSET, ++ &flag, 1); ++ if (ret) { ++ dev_err(dev, "failed to read otp reg flag\n"); ++ return ret; ++ } ++ ++ dev_dbg(dev, "register update flag = 0x%x\n", flag); ++ ++ gc5035->otp_regs.num_regs = 0; ++ if (flag != GC5035_OTP_FLAG_VALID) ++ return 0; ++ ++ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_DATA_OFFSET, ++ regs, sizeof(regs)); ++ if (ret) { ++ dev_err(dev, "failed to read otp reg data\n"); ++ return ret; ++ } ++ ++ for (i = 0; i < GC5035_OTP_REG_MAX_GROUP; i++) { ++ unsigned int base_group = i * GC5035_OTP_REG_BYTE_PER_GROUP; ++ ++ for (j = 0; j < GC5035_OTP_REG_PER_GROUP; j++) { ++ struct gc5035_reg *reg; ++ ++ if (!(regs[base_group] & ++ BIT((GC5035_OTP_PER_PAGE_SIZE * j + ++ GC5035_OTP_PAGE_FLAG_OFFSET)))) ++ continue; ++ ++ reg = &otp_regs->regs[otp_regs->num_regs++]; ++ reg->page = (regs[base_group] >> ++ (GC5035_OTP_PER_PAGE_SIZE * j)) & ++ GC5035_OTP_REG_PAGE_MASK; ++ reg->regval.addr = regs[base_group + j * ++ GC5035_OTP_REG_BYTE_PER_REG + ++ GC5035_OTP_REG_ADDR_OFFSET]; ++ reg->regval.val = regs[base_group + j * ++ GC5035_OTP_REG_BYTE_PER_REG + ++ GC5035_OTP_REG_VAL_OFFSET]; ++ } ++ } ++ ++ return 0; ++} ++ ++static int gc5035_read_dpc(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct gc5035_dpc *dpc = &gc5035->dpc; ++ u8 dpc_flag = 0; ++ u8 error_number = 0; ++ u8 total_number = 0; ++ int ret; ++ ++ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_DPC_FLAG_OFFSET, ++ &dpc_flag, 1); ++ if (ret) { ++ dev_err(dev, "failed to read dpc flag\n"); ++ return ret; ++ } ++ ++ dev_dbg(dev, "dpc flag = 0x%x\n", dpc_flag); ++ ++ dpc->valid = false; ++ ++ switch (dpc_flag & GC5035_OTP_DPC_FLAG_MASK) { ++ case GC5035_OTP_FLAG_EMPTY: ++ dev_dbg(dev, "dpc info is empty!!\n"); ++ break; ++ ++ case GC5035_OTP_FLAG_VALID: ++ dev_dbg(dev, "dpc info is valid!\n"); ++ ret = gc5035_otp_read_data(gc5035, ++ GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET, ++ &total_number, 1); ++ if (ret) { ++ dev_err(dev, "failed to read dpc total number\n"); ++ return ret; ++ } ++ ++ ret = gc5035_otp_read_data(gc5035, ++ GC5035_OTP_DPC_ERROR_NUMBER_OFFSET, ++ &error_number, 1); ++ if (ret) { ++ dev_err(dev, "failed to read dpc error number\n"); ++ return ret; ++ } ++ ++ dpc->total_num = total_number + error_number; ++ dpc->valid = true; ++ dev_dbg(dev, "total_num = %d\n", dpc->total_num); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int gc5035_otp_read_sensor_info(struct gc5035 *gc5035) ++{ ++ int ret; ++ ++ ret = gc5035_read_dpc(gc5035); ++ if (ret) ++ return ret; ++ ++ return gc5035_read_otp_regs(gc5035); ++} ++ ++static int gc5035_check_dd_load_status(struct gc5035 *gc5035) ++{ ++ u8 status; ++ int ret; ++ ++ ret = gc5035_read_reg(gc5035, GC5035_REG_DD_LOAD_STATUS, &status); ++ if (ret) ++ return ret; ++ ++ if (status & GC5035_OTP_BIT_LOAD) ++ return status; ++ else ++ return 0; ++} ++ ++static int gc5035_otp_update_dd(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct gc5035_dpc *dpc = &gc5035->dpc; ++ int val, ret; ++ ++ if (!dpc->valid) { ++ dev_dbg(dev, "DPC table invalid, not updating DD.\n"); ++ return 0; ++ } ++ ++ dev_dbg(dev, "DD auto load start\n"); ++ ++ ret = gc5035_write_array(gc5035, gc5035_dd_auto_load_regs, ++ ARRAY_SIZE(gc5035_dd_auto_load_regs)); ++ if (ret) { ++ dev_err(dev, "failed to write dd auto load reg\n"); ++ return ret; ++ } ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_H, ++ (dpc->total_num >> 8) & ++ GC5035_DD_TOTALNUM_H_MASK); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_L, ++ dpc->total_num & 0xff); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_array(gc5035, gc5035_otp_dd_regs, ++ ARRAY_SIZE(gc5035_otp_dd_regs)); ++ if (ret) ++ return ret; ++ ++ /* Wait for DD to finish loading automatically */ ++ ret = readx_poll_timeout(gc5035_check_dd_load_status, gc5035, ++ val, val <= 0, GC5035_DD_DELAY_US, ++ GC5035_DD_TIMEOUT_US); ++ if (ret < 0) { ++ dev_err(dev, "DD load timeout\n"); ++ return -EFAULT; ++ } ++ if (val < 0) { ++ dev_err(dev, "DD load failure\n"); ++ return val; ++ } ++ ++ ret = gc5035_write_array(gc5035, gc5035_otp_dd_enable_regs, ++ ARRAY_SIZE(gc5035_otp_dd_enable_regs)); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int gc5035_otp_update_regs(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; ++ unsigned int i; ++ int ret; ++ ++ dev_dbg(dev, "reg count = %d\n", otp_regs->num_regs); ++ ++ for (i = 0; i < otp_regs->num_regs; i++) { ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, ++ otp_regs->regs[i].page); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, ++ otp_regs->regs[i].regval.addr, ++ otp_regs->regs[i].regval.val); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gc5035_otp_update(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ int ret; ++ ++ ret = gc5035_otp_update_dd(gc5035); ++ if (ret) { ++ dev_err(dev, "failed to update otp dd\n"); ++ return ret; ++ } ++ ++ ret = gc5035_otp_update_regs(gc5035); ++ if (ret) { ++ dev_err(dev, "failed to update otp regs\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int gc5035_set_otp_config(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ u8 otp_id[GC5035_OTP_ID_SIZE]; ++ int ret; ++ ++ ret = gc5035_write_array(gc5035, gc5035_otp_init_regs, ++ ARRAY_SIZE(gc5035_otp_init_regs)); ++ if (ret) { ++ dev_err(dev, "failed to write otp init reg\n"); ++ return ret; ++ } ++ ++ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_ID_DATA_OFFSET, ++ &otp_id[0], GC5035_OTP_ID_SIZE); ++ if (ret) { ++ dev_err(dev, "failed to read otp id\n"); ++ goto out_otp_exit; ++ } ++ ++ if (!gc5035->otp_read || memcmp(gc5035->otp_id, otp_id, sizeof(otp_id))) { ++ dev_dbg(dev, "reading OTP configuration\n"); ++ ++ memset(&gc5035->otp_regs, 0, sizeof(gc5035->otp_regs)); ++ memset(&gc5035->dpc, 0, sizeof(gc5035->dpc)); ++ ++ memcpy(gc5035->otp_id, otp_id, sizeof(gc5035->otp_id)); ++ ++ ret = gc5035_otp_read_sensor_info(gc5035); ++ if (ret < 0) { ++ dev_err(dev, "failed to read otp info\n"); ++ goto out_otp_exit; ++ } ++ ++ gc5035->otp_read = true; ++ } ++ ++ ret = gc5035_otp_update(gc5035); ++ if (ret < 0) ++ return ret; ++ ++out_otp_exit: ++ ret = gc5035_write_array(gc5035, gc5035_otp_exit_regs, ++ ARRAY_SIZE(gc5035_otp_exit_regs)); ++ if (ret) { ++ dev_err(dev, "failed to write otp exit reg\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int gc5035_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ const struct gc5035_mode *mode; ++ s64 h_blank, vblank_def; ++ ++ mode = v4l2_find_nearest_size(gc5035_modes, ++ ARRAY_SIZE(gc5035_modes), width, ++ height, fmt->format.width, ++ fmt->format.height); ++ ++ ++ //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ fmt->format.field = V4L2_FIELD_NONE; ++ ++ mutex_lock(&gc5035->mutex); ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; ++ } else { ++ gc5035->cur_mode = mode; ++ h_blank = mode->hts_def - mode->width; ++ __v4l2_ctrl_modify_range(gc5035->hblank, h_blank, ++ h_blank, 1, h_blank); ++ vblank_def = round_up(mode->vts_def, 4) - mode->height; ++ __v4l2_ctrl_modify_range(gc5035->vblank, vblank_def, ++ GC5035_VTS_MAX - mode->height, ++ 1, vblank_def); ++ } ++ mutex_unlock(&gc5035->mutex); ++ ++ return 0; ++} ++ ++static int gc5035_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ const struct gc5035_mode *mode = gc5035->cur_mode; ++ ++ mutex_lock(&gc5035->mutex); ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); ++ } else { ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; ++ fmt->format.field = V4L2_FIELD_NONE; ++ } ++ mutex_unlock(&gc5035->mutex); ++ ++ return 0; ++} ++ ++static int gc5035_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index != 0) ++ return -EINVAL; ++ ++ //code->code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ code->code = MEDIA_BUS_FMT_SGRBG10_1X10; ++ ++ return 0; ++} ++ ++static int gc5035_enum_frame_sizes(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index >= ARRAY_SIZE(gc5035_modes)) ++ return -EINVAL; ++ ++ //if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) ++ if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) ++ return -EINVAL; ++ ++ fse->min_width = gc5035_modes[fse->index].width; ++ fse->max_width = gc5035_modes[fse->index].width; ++ fse->max_height = gc5035_modes[fse->index].height; ++ fse->min_height = gc5035_modes[fse->index].height; ++ ++ return 0; ++} ++ ++static int __gc5035_start_stream(struct gc5035 *gc5035) ++{ ++ int ret; ++ ++ gc5035_set_power(gc5035, 1); ++ ++ ret = gc5035_write_array(gc5035, gc5035_global_regs, ++ ARRAY_SIZE(gc5035_global_regs)); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_set_otp_config(gc5035); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_array(gc5035, gc5035->cur_mode->reg_list, ++ gc5035->cur_mode->num_regs); ++ if (ret) ++ return ret; ++ ++ /* In case these controls are set before streaming */ ++ ret = __v4l2_ctrl_handler_setup(&gc5035->ctrl_handler); ++ if (ret) ++ return ret; ++ ++ gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++ if (ret) ++ return ret; ++ ++ return gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, ++ GC5035_MODE_STREAMING); ++} ++ ++static void __gc5035_stop_stream(struct gc5035 *gc5035) ++{ ++ int ret; ++ struct i2c_client *client = gc5035->client; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++ if (ret) ++ dev_err(&client->dev, "failed to stop streaming!"); ++ ++ if(gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, ++ GC5035_MODE_SW_STANDBY)) ++ dev_err(&client->dev, "failed to stop streaming"); ++ gc5035_set_power(gc5035, 0); ++} ++ ++static int gc5035_s_stream(struct v4l2_subdev *sd, int on) ++{ ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ struct i2c_client *client = gc5035->client; ++ int ret = 0; ++ ++ mutex_lock(&gc5035->mutex); ++ on = !!on; ++ if (on == gc5035->streaming) ++ goto unlock_and_return; ++ ++ if (on) { ++ ret = pm_runtime_get_sync(&client->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(&client->dev); ++ goto unlock_and_return; ++ } ++ ++ ret = __gc5035_start_stream(gc5035); ++ if (ret) { ++ dev_err(&client->dev, "start stream failed\n"); ++ pm_runtime_put(&client->dev); ++ goto unlock_and_return; ++ } ++ } else { ++ __gc5035_stop_stream(gc5035); ++ pm_runtime_put(&client->dev); ++ } ++ ++ gc5035->streaming = on; ++ ++unlock_and_return: ++ mutex_unlock(&gc5035->mutex); ++ ++ return ret; ++} ++ ++static int gc5035_runtime_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ int ret; ++ ++ if (gc5035->streaming) { ++ ret = __gc5035_start_stream(gc5035); ++ if (ret) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ __gc5035_stop_stream(gc5035); ++ gc5035->streaming = false; ++ ++ return ret; ++} ++ ++static int gc5035_runtime_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ ++ if (gc5035->streaming) ++ __gc5035_stop_stream(gc5035); ++ ++ return 0; ++} ++ ++static int gc5035_entity_init_cfg(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_state *sd_state) ++{ ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_TRY, ++ .format = { ++ .width = 2592, ++ .height = 1944, ++ } ++ }; ++ ++ gc5035_set_fmt(subdev, sd_state, &fmt); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops gc5035_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, ++ pm_runtime_force_resume) ++ SET_RUNTIME_PM_OPS(gc5035_runtime_suspend, ++ gc5035_runtime_resume, NULL) ++}; ++ ++static const struct v4l2_subdev_video_ops gc5035_video_ops = { ++ .s_stream = gc5035_s_stream, ++}; ++ ++static const struct v4l2_subdev_pad_ops gc5035_pad_ops = { ++ .init_cfg = gc5035_entity_init_cfg, ++ .enum_mbus_code = gc5035_enum_mbus_code, ++ .enum_frame_size = gc5035_enum_frame_sizes, ++ .get_fmt = gc5035_get_fmt, ++ .set_fmt = gc5035_set_fmt, ++}; ++ ++static const struct v4l2_subdev_ops gc5035_subdev_ops = { ++ .video = &gc5035_video_ops, ++ .pad = &gc5035_pad_ops, ++}; ++ ++static const struct media_entity_operations gc5035_subdev_entity_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++static int gc5035_set_exposure(struct gc5035 *gc5035, u32 val) ++{ ++ u32 caltime = 0; ++ int ret = 0; ++ ++ caltime = val / 2; ++ caltime = caltime * 2; ++ gc5035->Dgain_ratio = 256 * val / caltime; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_H, ++ (val >> 8) & GC5035_EXPOSURE_H_MASK); ++ if (ret) ++ return ret; ++ ++ return gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_L, val & 0xff); ++} ++ ++static u32 GC5035_AGC_Param[17][2] = { ++ { 256, 0 }, ++ { 302, 1 }, ++ { 358, 2 }, ++ { 425, 3 }, ++ { 502, 8 }, ++ { 599, 9 }, ++ { 717, 10 }, ++ { 845, 11 }, ++ { 998, 12 }, ++ { 1203, 13 }, ++ { 1434, 14 }, ++ { 1710, 15 }, ++ { 1997, 16 }, ++ { 2355, 17 }, ++ { 2816, 18 }, ++ { 3318, 19 }, ++ { 3994, 20 }, ++}; ++ ++static int gc5035_set_analogue_gain(struct gc5035 *gc5035, u32 a_gain) ++{ ++ struct device *dev = &gc5035->client->dev; ++ int ret = 0, i = 0; ++ u32 temp_gain = 0; ++ ++ if (a_gain < 0x100) ++ a_gain = 0x100; ++ else if (a_gain > GC5035_ANALOG_GAIN_MAX) ++ a_gain = GC5035_ANALOG_GAIN_MAX; ++ for (i = 16; i >= 0; i--) { ++ if (a_gain >= GC5035_AGC_Param[i][0]) ++ break; ++ } ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++ if (ret) ++ return ret; ++ ret |= gc5035_write_reg(gc5035, ++ GC5035_REG_ANALOG_GAIN, GC5035_AGC_Param[i][1]); ++ temp_gain = a_gain; ++ temp_gain = temp_gain * gc5035->Dgain_ratio / GC5035_AGC_Param[i][0]; ++ ++ ret |= gc5035_write_reg(gc5035, ++ GC5035_REG_DIGI_GAIN_H, ++ (temp_gain >> 8) & 0x0f); ++ ret |= gc5035_write_reg(gc5035, ++ GC5035_REG_DIGI_GAIN_L, ++ temp_gain & 0xfc); ++ return ret; ++} ++ ++static int gc5035_set_vblank(struct gc5035 *gc5035, u32 val) ++{ ++ int frame_length = 0; ++ int ret; ++ ++ frame_length = val + gc5035->cur_mode->height; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_VTS_H, ++ (frame_length >> 8) & GC5035_VTS_H_MASK); ++ if (ret) ++ return ret; ++ ++ return gc5035_write_reg(gc5035, GC5035_REG_VTS_L, frame_length & 0xff); ++} ++ ++static int gc5035_enable_test_pattern(struct gc5035 *gc5035, u32 pattern) ++{ ++ int ret; ++ ++ if (pattern) ++ pattern = GC5035_TEST_PATTERN_ENABLE; ++ else ++ pattern = GC5035_TEST_PATTERN_DISABLE; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 1); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_write_reg(gc5035, GC5035_REG_TEST_PATTERN, pattern); ++ if (ret) ++ return ret; ++ ++ return gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); ++} ++ ++static int gc5035_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct gc5035 *gc5035 = container_of(ctrl->handler, ++ struct gc5035, ctrl_handler); ++ struct i2c_client *client = gc5035->client; ++ s64 max; ++ int ret = 0; ++ ++ /* Propagate change of current control to all related controls */ ++ switch (ctrl->id) { ++ case V4L2_CID_VBLANK: ++ /* Update max exposure while meeting expected vblanking */ ++ max = gc5035->cur_mode->height + ctrl->val ++ - GC5035_EXPOSURE_MARGIN; ++ __v4l2_ctrl_modify_range(gc5035->exposure, ++ gc5035->exposure->minimum, max, ++ gc5035->exposure->step, ++ gc5035->exposure->default_value); ++ break; ++ } ++ ++ if (!pm_runtime_get_if_in_use(&client->dev)) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE: ++ ret = gc5035_set_exposure(gc5035, ctrl->val); ++ break; ++ case V4L2_CID_ANALOGUE_GAIN: ++ ret = gc5035_set_analogue_gain(gc5035, ctrl->val); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ break; ++ case V4L2_CID_VBLANK: ++ ret = gc5035_set_vblank(gc5035, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ ret = gc5035_enable_test_pattern(gc5035, ctrl->val); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ }; ++ ++ pm_runtime_put(&client->dev); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops gc5035_ctrl_ops = { ++ .s_ctrl = gc5035_set_ctrl, ++}; ++ ++static int gc5035_initialize_controls(struct gc5035 *gc5035) ++{ ++ const struct gc5035_mode *mode; ++ struct v4l2_ctrl_handler *handler; ++ struct v4l2_ctrl *ctrl; ++ u64 exposure_max; ++ u32 h_blank, vblank_def; ++ int ret; ++ ++ handler = &gc5035->ctrl_handler; ++ mode = gc5035->cur_mode; ++ ret = v4l2_ctrl_handler_init(handler, 8); ++ if (ret) ++ return ret; ++ ++ handler->lock = &gc5035->mutex; ++ ++ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, ++ 0, 0, gc5035_link_freqs); ++ if (ctrl) ++ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ++ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, ++ 0, GC5035_PIXEL_RATE, 1, GC5035_PIXEL_RATE); ++ ++ h_blank = mode->hts_def - mode->width; ++ gc5035->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, ++ h_blank, h_blank, 1, h_blank); ++ if (gc5035->hblank) ++ gc5035->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ++ vblank_def = round_up(mode->vts_def, 4) - mode->height; ++ gc5035->vblank = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, ++ V4L2_CID_VBLANK, vblank_def, ++ GC5035_VTS_MAX - mode->height, ++ 4, vblank_def); ++ ++ exposure_max = mode->vts_def - GC5035_EXPOSURE_MARGIN; ++ gc5035->exposure = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ GC5035_EXPOSURE_MIN, exposure_max, ++ GC5035_EXPOSURE_STEP, ++ mode->exp_def); ++ ++ v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, ++ GC5035_ANALOG_GAIN_MIN, GC5035_ANALOG_GAIN_MAX, ++ GC5035_ANALOG_GAIN_STEP, GC5035_ANALOG_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_DIGITAL_GAIN, ++ GC5035_DIGI_GAIN_MIN, GC5035_DIGI_GAIN_MAX, ++ GC5035_DIGI_GAIN_STEP, GC5035_DIGI_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std_menu_items(handler, &gc5035_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(gc5035_test_pattern_menu) - 1, ++ 0, 0, gc5035_test_pattern_menu); ++ ++ if (handler->error) { ++ ret = handler->error; ++ dev_err(&gc5035->client->dev, ++ "Failed to init controls(%d)\n", ret); ++ goto err_free_handler; ++ } ++ ++ gc5035->subdev.ctrl_handler = handler; ++ ++ return 0; ++ ++err_free_handler: ++ v4l2_ctrl_handler_free(handler); ++ ++ return ret; ++} ++ ++static int gc5035_check_sensor_id(struct gc5035 *gc5035, ++ struct i2c_client *client) ++{ ++ struct device *dev = &gc5035->client->dev; ++ u16 id; ++ u8 pid = 0; ++ u8 ver = 0; ++ int ret; ++ ++ ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_H, &pid); ++ if (ret) ++ return ret; ++ ++ ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_L, &ver); ++ if (ret) ++ return ret; ++ ++ id = GC5035_ID(pid, ver); ++ if (id != GC5035_CHIP_ID) { ++ dev_err(dev, "Unexpected sensor id(%04x)\n", id); ++ return -EINVAL; ++ } ++ ++ dev_dbg(dev, "Detected GC%04x sensor\n", id); ++ ++ return 0; ++} ++ ++static int gc5035_get_hwcfg(struct gc5035 *gc5035) ++{ ++ struct device *dev = &gc5035->client->dev; ++ struct v4l2_fwnode_endpoint bus_cfg = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ struct fwnode_handle *ep; ++ struct fwnode_handle *fwnode = dev_fwnode(dev); ++ unsigned long link_freq_mask = 0; ++ unsigned int i, j; ++ int ret; ++ ++ if (!fwnode) ++ return -ENODEV; ++ ++ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); ++ if (!ep) ++ return -ENODEV; ++ ++ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); ++ if (ret) ++ goto out; ++ ++ dev_dbg(dev, "num of link freqs: %d", bus_cfg.nr_of_link_frequencies); ++ if (!bus_cfg.nr_of_link_frequencies) { ++ dev_warn(dev, "no link frequencies defined"); ++ goto out; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(gc5035_link_freqs); ++i) { ++ for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { ++ if (bus_cfg.link_frequencies[j] ++ == gc5035_link_freqs[i]) { ++ link_freq_mask |= BIT(i); ++ dev_dbg(dev, "Link frequency %lld supported\n", ++ gc5035_link_freqs[i]); ++ break; ++ } ++ } ++ } ++ if (!link_freq_mask) { ++ dev_err(dev, "No supported link frequencies found\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++out: ++ v4l2_fwnode_endpoint_free(&bus_cfg); ++ fwnode_handle_put(ep); ++ return ret; ++} ++ ++static int gc5035_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct gc5035 *gc5035; ++ struct v4l2_subdev *sd; ++ int ret, i; ++ u32 freq = 192000000UL; ++ ++ gc5035 = devm_kzalloc(dev, sizeof(*gc5035), GFP_KERNEL); ++ if (!gc5035) ++ return -ENOMEM; ++ ++ gc5035->client = client; ++ gc5035_init_power_ctrl(gc5035); ++ gc5035_set_power(gc5035, 1); ++ ++ gc5035->cur_mode = &gc5035_modes[0]; ++ ++ ret = clk_set_rate(gc5035->mclk, freq); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Failed to set mclk rate\n"); ++ gc5035->mclk_rate = clk_get_rate(gc5035->mclk); ++ if (gc5035->mclk_rate != freq) ++ dev_warn(dev, "mclk rate set to %lu instead of requested %u\n", ++ gc5035->mclk_rate, freq); ++ gc5035->iovdd_supply = devm_regulator_get(dev, "iovdd"); ++ if (IS_ERR(gc5035->iovdd_supply)) ++ return dev_err_probe(dev, PTR_ERR(gc5035->iovdd_supply), ++ "Failed to get iovdd regulator\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(gc5035_supplies); i++) ++ gc5035->supplies[i].supply = gc5035_supplies[i]; ++ ret = devm_regulator_bulk_get(&gc5035->client->dev, ++ ARRAY_SIZE(gc5035_supplies), ++ gc5035->supplies); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get regulators\n"); ++ ++ mutex_init(&gc5035->mutex); ++ sd = &gc5035->subdev; ++ v4l2_i2c_subdev_init(sd, client, &gc5035_subdev_ops); ++ ret = gc5035_initialize_controls(gc5035); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to initialize controls\n"); ++ goto err_destroy_mutex; ++ } ++ ret = gc5035_runtime_resume(dev); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to power on\n"); ++ goto err_free_handler; ++ } ++ ret = gc5035_check_sensor_id(gc5035, client); ++ if (ret) { ++ dev_err_probe(dev, ret, "Sensor ID check failed\n"); ++ goto err_power_off; ++ } ++ ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sd->entity.ops = &gc5035_subdev_entity_ops; ++ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ gc5035->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_pads_init(&sd->entity, 1, &gc5035->pad); ++ if (ret < 0) { ++ dev_err_probe(dev, ret, "Failed to initialize pads\n"); ++ goto err_power_off; ++ } ++ ret = v4l2_async_register_subdev_sensor(sd); ++ if (ret) { ++ dev_err_probe(dev, ret, "v4l2 async register subdev failed\n"); ++ goto err_clean_entity; ++ } ++ ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ pm_runtime_idle(dev); ++ gc5035_set_power(gc5035, 0); ++ ++ return 0; ++ ++err_clean_entity: ++ media_entity_cleanup(&sd->entity); ++err_power_off: ++ gc5035_runtime_suspend(dev); ++err_free_handler: ++ v4l2_ctrl_handler_free(&gc5035->ctrl_handler); ++ gc5035_set_power(gc5035, 0); ++err_destroy_mutex: ++ mutex_destroy(&gc5035->mutex); ++ ++ return ret; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) ++static int gc5035_remove(struct i2c_client *client) ++#else ++static void gc5035_remove(struct i2c_client *client) ++#endif ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct gc5035 *gc5035 = to_gc5035(sd); ++ ++ v4l2_async_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(&gc5035->ctrl_handler); ++ mutex_destroy(&gc5035->mutex); ++ pm_runtime_disable(&client->dev); ++ if (!pm_runtime_status_suspended(&client->dev)) ++ gc5035_runtime_suspend(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) ++ return 0; ++ #endif ++} ++ ++#ifdef CONFIG_ACPI ++static const struct acpi_device_id gc5035_acpi_ids[] = { ++ {"GCTI5035"}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(acpi, gc5035_acpi_ids); ++#endif ++ ++static const struct of_device_id gc5035_of_match[] = { ++ { .compatible = "galaxycore,gc5035" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, gc5035_of_match); ++ ++static struct i2c_driver gc5035_i2c_driver = { ++ .driver = { ++ .name = "gc5035", ++ .pm = &gc5035_pm_ops, ++ .acpi_match_table = ACPI_PTR(gc5035_acpi_ids), ++ .of_match_table = gc5035_of_match, ++ }, ++ .probe_new = gc5035_probe, ++ .remove = gc5035_remove, ++}; ++module_i2c_driver(gc5035_i2c_driver); ++ ++MODULE_AUTHOR("Liang Wang "); ++MODULE_AUTHOR("Hao He "); ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_AUTHOR("Tomasz Figa "); ++MODULE_DESCRIPTION("GalaxyCore gc5035 sensor driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 53270d19c73a..7350a3840438 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -23,7 +23,7 @@ + #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d + + #define INT3472_PDEV_MAX_NAME_LEN 23 +-#define INT3472_MAX_SENSOR_GPIOS 3 ++#define INT3472_MAX_SENSOR_GPIOS 4 + + #define GPIO_REGULATOR_NAME_LENGTH 21 + #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 +@@ -73,6 +73,7 @@ struct int3472_sensor_config { + const char *sensor_module_name; + struct regulator_consumer_supply supply_map; + const struct int3472_gpio_function_remap *function_maps; ++ const bool use_independent_gpio; + }; + + struct int3472_discrete_device { +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index c42c3faa2c32..427552c14c76 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -57,11 +57,25 @@ static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = + + static const struct int3472_sensor_config int3472_sensor_configs[] = { + /* Lenovo Miix 510-12ISK - OV2680, Front */ +- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, ++ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps, false }, + /* Lenovo Miix 510-12ISK - OV5648, Rear */ +- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, + /* Surface Go 1&2 - OV5693, Front */ +- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, ++ /* Dell Latitude 9420 - OV01A1S, Front */ ++ { "0BF111N3", { 0 }, NULL, true }, ++ /* Dell Latitude 9420 - HM11B1, Front */ ++ { "9BF123N3", { 0 }, NULL, true }, ++ /* Lenovo X1 Yoga - OV2740, Front */ ++ { "CJFLE23", { 0 }, NULL, true }, ++ /* OV13B10 */ ++ { "09B13", { 0 }, NULL, true }, ++ /* HIMX1092 */ ++ { "KPFB297", { 0 }, NULL, true }, ++ /* GC5035 */ ++ { "CJAK519", { 0 }, NULL, true }, ++ /* S5K3L6 */ ++ { "KBAG296", { 0 }, NULL, true }, + }; + + static const struct int3472_sensor_config * +@@ -229,6 +243,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + const char *err_msg; + int ret; + u8 type; ++ u8 active_value; ++ u32 polarity = GPIO_LOOKUP_FLAGS_DEFAULT; + + if (!acpi_gpio_get_io_resource(ares, &agpio)) + return 1; +@@ -249,30 +265,60 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + } + + type = obj->integer.value & 0xff; ++ active_value = obj->integer.value >> 24; ++ if (!active_value) ++ polarity = GPIO_ACTIVE_LOW; + + switch (type) { + case INT3472_GPIO_TYPE_RESET: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", +- GPIO_ACTIVE_LOW); ++ polarity); + if (ret) + err_msg = "Failed to map reset pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_POWERDOWN: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", +- GPIO_ACTIVE_LOW); ++ polarity); + if (ret) + err_msg = "Failed to map powerdown pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_CLK_ENABLE: ++ if (!IS_ERR(int3472->sensor_config) && ++ int3472->sensor_config->use_independent_gpio) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "clken", polarity); ++ if (ret) ++ err_msg = "Failed to map clken pin to sensor\n"; ++ ++ break; ++ } + case INT3472_GPIO_TYPE_PRIVACY_LED: ++ if (!IS_ERR(int3472->sensor_config) && ++ int3472->sensor_config->use_independent_gpio) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "pled", polarity); ++ if (ret) ++ err_msg = "Failed to map pled pin to sensor\n"; ++ ++ break; ++ } + ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); + if (ret) + err_msg = "Failed to map GPIO to clock\n"; + + break; + case INT3472_GPIO_TYPE_POWER_ENABLE: ++ if (!IS_ERR(int3472->sensor_config) && ++ int3472->sensor_config->use_independent_gpio) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "pwren", polarity); ++ if (ret) ++ err_msg = "Failed to map clken pin to sensor\n"; ++ ++ break; ++ } + ret = skl_int3472_register_regulator(int3472, agpio); + if (ret) + err_msg = "Failed to map regulator to sensor\n"; +-- +2.17.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-support-independent-clock-and-LED-gpios-5.17+.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-support-independent-clock-and-LED-gpios-5.17+.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-support-independent-clock-and-LED-gpios-5.17+.patch 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-support-independent-clock-and-LED-gpios-5.17+.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,107 +0,0 @@ -diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h -index 53270d19c73a..6c5b52ac6c2c 100644 ---- a/drivers/platform/x86/intel/int3472/common.h -+++ b/drivers/platform/x86/intel/int3472/common.h -@@ -23,7 +23,7 @@ - #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d - - #define INT3472_PDEV_MAX_NAME_LEN 23 --#define INT3472_MAX_SENSOR_GPIOS 3 -+#define INT3472_MAX_SENSOR_GPIOS 4 - - #define GPIO_REGULATOR_NAME_LENGTH 21 - #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 -@@ -73,6 +73,7 @@ struct int3472_sensor_config { - const char *sensor_module_name; - struct regulator_consumer_supply supply_map; - const struct int3472_gpio_function_remap *function_maps; -+ const bool independent_clk_gpios; - }; - - struct int3472_discrete_device { -diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -index ed4c9d760757..f5857ec334fa 100644 ---- a/drivers/platform/x86/intel/int3472/discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -57,11 +57,17 @@ static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = - - static const struct int3472_sensor_config int3472_sensor_configs[] = { - /* Lenovo Miix 510-12ISK - OV2680, Front */ -- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, -+ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps, false }, - /* Lenovo Miix 510-12ISK - OV5648, Rear */ -- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, -+ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, - /* Surface Go 1&2 - OV5693, Front */ -- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, -+ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, -+ /* Dell Latitude 9420 - OV01A1S, Front */ -+ { "0BF111N3", { 0 }, NULL, true }, -+ /* Dell Latitude 9420 - HM11B1, Front */ -+ { "9BF123N3", { 0 }, NULL, true }, -+ /* Lenovo X1 Yoga - OV2740, Front */ -+ { "CJFLE23", { 0 }, NULL, true }, - }; - - static const struct int3472_sensor_config * -@@ -225,6 +231,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, - const char *err_msg; - int ret; - u8 type; -+ u8 active_value; -+ u32 polarity = GPIO_LOOKUP_FLAGS_DEFAULT; - - if (!acpi_gpio_get_io_resource(ares, &agpio)) - return 1; -@@ -245,28 +253,45 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, - } - - type = obj->integer.value & 0xff; -+ active_value = obj->integer.value >> 24; -+ if (!active_value) -+ polarity = GPIO_ACTIVE_LOW; - - switch (type) { - case INT3472_GPIO_TYPE_RESET: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", -- GPIO_ACTIVE_LOW); -+ polarity); - if (ret) - err_msg = "Failed to map reset pin to sensor\n"; - - break; - case INT3472_GPIO_TYPE_POWERDOWN: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", -- GPIO_ACTIVE_LOW); -+ polarity); - if (ret) - err_msg = "Failed to map powerdown pin to sensor\n"; - - break; - case INT3472_GPIO_TYPE_CLK_ENABLE: - case INT3472_GPIO_TYPE_PRIVACY_LED: -- ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); -- if (ret) -- err_msg = "Failed to map GPIO to clock\n"; -- -+ if (!IS_ERR(int3472->sensor_config) && -+ int3472->sensor_config->independent_clk_gpios) { -+ if (type == INT3472_GPIO_TYPE_CLK_ENABLE) { -+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, -+ "clken", polarity); -+ if (ret) -+ err_msg = "Failed to map clken pin to sensor\n"; -+ } else if (type == INT3472_GPIO_TYPE_PRIVACY_LED) { -+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, -+ "pled", polarity); -+ if (ret) -+ err_msg = "Failed to map pled pin to sensor\n"; -+ } -+ } else { -+ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); -+ if (ret) -+ err_msg = "Failed to map GPIO to clock\n"; -+ } - break; - case INT3472_GPIO_TYPE_POWER_ENABLE: - ret = skl_int3472_register_regulator(int3472, agpio); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-support-independent-clock-and-LED-gpios.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-support-independent-clock-and-LED-gpios.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-support-independent-clock-and-LED-gpios.patch 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-support-independent-clock-and-LED-gpios.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,110 +0,0 @@ -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h -index 714fde73b524..10d93ed4ea98 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h -+++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h -@@ -23,7 +23,7 @@ - #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d - - #define INT3472_PDEV_MAX_NAME_LEN 23 --#define INT3472_MAX_SENSOR_GPIOS 3 -+#define INT3472_MAX_SENSOR_GPIOS 4 - - #define GPIO_REGULATOR_NAME_LENGTH 21 - #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 -@@ -73,6 +73,7 @@ struct int3472_sensor_config { - const char *sensor_module_name; - struct regulator_consumer_supply supply_map; - const struct int3472_gpio_function_remap *function_maps; -+ const bool independent_clk_gpios; - }; - - struct int3472_discrete_device { -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c -index e59d79c7e82f..5cf6dd63d43f 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c -+++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c -@@ -57,11 +57,17 @@ static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = - - static const struct int3472_sensor_config int3472_sensor_configs[] = { - /* Lenovo Miix 510-12ISK - OV2680, Front */ -- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, -+ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps, false }, - /* Lenovo Miix 510-12ISK - OV5648, Rear */ -- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, -+ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, - /* Surface Go 1&2 - OV5693, Front */ -- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, -+ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, -+ /* Dell Latitude 9420 - OV01A1S, Front */ -+ { "0BF111N3", { 0 }, NULL, true }, -+ /* Dell Latitude 9420 - HM11B1, Front */ -+ { "9BF123N3", { 0 }, NULL, true }, -+ /* Lenovo X1 Yoga - OV2740, Front */ -+ { "CJFLE23", { 0 }, NULL, true }, - }; - - static const struct int3472_sensor_config * -@@ -226,6 +232,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, - const char *err_msg; - int ret; - u8 type; -+ u8 active_value; -+ u32 polarity = GPIO_LOOKUP_FLAGS_DEFAULT; - - if (!acpi_gpio_get_io_resource(ares, &agpio)) - return 1; -@@ -246,28 +254,45 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, - } - - type = obj->integer.value & 0xff; -+ active_value = obj->integer.value >> 24; -+ if (!active_value) -+ polarity = GPIO_ACTIVE_LOW; - - switch (type) { - case INT3472_GPIO_TYPE_RESET: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", -- GPIO_ACTIVE_LOW); -+ polarity); - if (ret) - err_msg = "Failed to map reset pin to sensor\n"; - - break; - case INT3472_GPIO_TYPE_POWERDOWN: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", -- GPIO_ACTIVE_LOW); -+ polarity); - if (ret) - err_msg = "Failed to map powerdown pin to sensor\n"; - - break; - case INT3472_GPIO_TYPE_CLK_ENABLE: - case INT3472_GPIO_TYPE_PRIVACY_LED: -- ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); -- if (ret) -- err_msg = "Failed to map GPIO to clock\n"; -- -+ if (!IS_ERR(int3472->sensor_config) && -+ int3472->sensor_config->independent_clk_gpios) { -+ if (type == INT3472_GPIO_TYPE_CLK_ENABLE) { -+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, -+ "clken", polarity); -+ if (ret) -+ err_msg = "Failed to map clken pin to sensor\n"; -+ } else if (type == INT3472_GPIO_TYPE_PRIVACY_LED) { -+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, -+ "pled", polarity); -+ if (ret) -+ err_msg = "Failed to map pled pin to sensor\n"; -+ } -+ } else { -+ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); -+ if (ret) -+ err_msg = "Failed to map GPIO to clock\n"; -+ } - break; - case INT3472_GPIO_TYPE_POWER_ENABLE: - ret = skl_int3472_register_regulator(int3472, agpio); --- -2.34.1 - diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v5.15/int3472-support-independent-clock-and-LED-gpios.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v5.15/int3472-support-independent-clock-and-LED-gpios.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v5.15/int3472-support-independent-clock-and-LED-gpios.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v5.15/int3472-support-independent-clock-and-LED-gpios.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,110 @@ +diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h +index 714fde73b524..10d93ed4ea98 100644 +--- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h ++++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h +@@ -23,7 +23,7 @@ + #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d + + #define INT3472_PDEV_MAX_NAME_LEN 23 +-#define INT3472_MAX_SENSOR_GPIOS 3 ++#define INT3472_MAX_SENSOR_GPIOS 4 + + #define GPIO_REGULATOR_NAME_LENGTH 21 + #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 +@@ -73,6 +73,7 @@ struct int3472_sensor_config { + const char *sensor_module_name; + struct regulator_consumer_supply supply_map; + const struct int3472_gpio_function_remap *function_maps; ++ const bool independent_clk_gpios; + }; + + struct int3472_discrete_device { +diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c +index e59d79c7e82f..5cf6dd63d43f 100644 +--- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c ++++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c +@@ -57,11 +57,17 @@ static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = + + static const struct int3472_sensor_config int3472_sensor_configs[] = { + /* Lenovo Miix 510-12ISK - OV2680, Front */ +- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, ++ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps, false }, + /* Lenovo Miix 510-12ISK - OV5648, Rear */ +- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, + /* Surface Go 1&2 - OV5693, Front */ +- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, ++ /* Dell Latitude 9420 - OV01A1S, Front */ ++ { "0BF111N3", { 0 }, NULL, true }, ++ /* Dell Latitude 9420 - HM11B1, Front */ ++ { "9BF123N3", { 0 }, NULL, true }, ++ /* Lenovo X1 Yoga - OV2740, Front */ ++ { "CJFLE23", { 0 }, NULL, true }, + }; + + static const struct int3472_sensor_config * +@@ -226,6 +232,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + const char *err_msg; + int ret; + u8 type; ++ u8 active_value; ++ u32 polarity = GPIO_LOOKUP_FLAGS_DEFAULT; + + if (!acpi_gpio_get_io_resource(ares, &agpio)) + return 1; +@@ -246,28 +254,45 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + } + + type = obj->integer.value & 0xff; ++ active_value = obj->integer.value >> 24; ++ if (!active_value) ++ polarity = GPIO_ACTIVE_LOW; + + switch (type) { + case INT3472_GPIO_TYPE_RESET: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", +- GPIO_ACTIVE_LOW); ++ polarity ^ GPIO_ACTIVE_LOW); + if (ret) + err_msg = "Failed to map reset pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_POWERDOWN: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", +- GPIO_ACTIVE_LOW); ++ polarity ^ GPIO_ACTIVE_LOW); + if (ret) + err_msg = "Failed to map powerdown pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_CLK_ENABLE: + case INT3472_GPIO_TYPE_PRIVACY_LED: +- ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); +- if (ret) +- err_msg = "Failed to map GPIO to clock\n"; +- ++ if (!IS_ERR(int3472->sensor_config) && ++ int3472->sensor_config->independent_clk_gpios) { ++ if (type == INT3472_GPIO_TYPE_CLK_ENABLE) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "clken", polarity); ++ if (ret) ++ err_msg = "Failed to map clken pin to sensor\n"; ++ } else if (type == INT3472_GPIO_TYPE_PRIVACY_LED) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "pled", polarity); ++ if (ret) ++ err_msg = "Failed to map pled pin to sensor\n"; ++ } ++ } else { ++ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); ++ if (ret) ++ err_msg = "Failed to map GPIO to clock\n"; ++ } + break; + case INT3472_GPIO_TYPE_POWER_ENABLE: + ret = skl_int3472_register_regulator(int3472, agpio); +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v5.17/int3472-support-independent-clock-and-LED-gpios.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v5.17/int3472-support-independent-clock-and-LED-gpios.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v5.17/int3472-support-independent-clock-and-LED-gpios.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v5.17/int3472-support-independent-clock-and-LED-gpios.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,107 @@ +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 53270d19c73a..6c5b52ac6c2c 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -23,7 +23,7 @@ + #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d + + #define INT3472_PDEV_MAX_NAME_LEN 23 +-#define INT3472_MAX_SENSOR_GPIOS 3 ++#define INT3472_MAX_SENSOR_GPIOS 4 + + #define GPIO_REGULATOR_NAME_LENGTH 21 + #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 +@@ -73,6 +73,7 @@ struct int3472_sensor_config { + const char *sensor_module_name; + struct regulator_consumer_supply supply_map; + const struct int3472_gpio_function_remap *function_maps; ++ const bool independent_clk_gpios; + }; + + struct int3472_discrete_device { +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index ed4c9d760757..f5857ec334fa 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -57,11 +57,17 @@ static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = + + static const struct int3472_sensor_config int3472_sensor_configs[] = { + /* Lenovo Miix 510-12ISK - OV2680, Front */ +- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, ++ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps, false }, + /* Lenovo Miix 510-12ISK - OV5648, Rear */ +- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, + /* Surface Go 1&2 - OV5693, Front */ +- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL, false }, ++ /* Dell Latitude 9420 - OV01A1S, Front */ ++ { "0BF111N3", { 0 }, NULL, true }, ++ /* Dell Latitude 9420 - HM11B1, Front */ ++ { "9BF123N3", { 0 }, NULL, true }, ++ /* Lenovo X1 Yoga - OV2740, Front */ ++ { "CJFLE23", { 0 }, NULL, true }, + }; + + static const struct int3472_sensor_config * +@@ -225,6 +231,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + const char *err_msg; + int ret; + u8 type; ++ u8 active_value; ++ u32 polarity = GPIO_LOOKUP_FLAGS_DEFAULT; + + if (!acpi_gpio_get_io_resource(ares, &agpio)) + return 1; +@@ -245,28 +253,45 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + } + + type = obj->integer.value & 0xff; ++ active_value = obj->integer.value >> 24; ++ if (!active_value) ++ polarity = GPIO_ACTIVE_LOW; + + switch (type) { + case INT3472_GPIO_TYPE_RESET: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", +- GPIO_ACTIVE_LOW); ++ polarity ^ GPIO_ACTIVE_LOW); + if (ret) + err_msg = "Failed to map reset pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_POWERDOWN: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", +- GPIO_ACTIVE_LOW); ++ polarity ^ GPIO_ACTIVE_LOW); + if (ret) + err_msg = "Failed to map powerdown pin to sensor\n"; + + break; + case INT3472_GPIO_TYPE_CLK_ENABLE: + case INT3472_GPIO_TYPE_PRIVACY_LED: +- ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); +- if (ret) +- err_msg = "Failed to map GPIO to clock\n"; +- ++ if (!IS_ERR(int3472->sensor_config) && ++ int3472->sensor_config->independent_clk_gpios) { ++ if (type == INT3472_GPIO_TYPE_CLK_ENABLE) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "clken", polarity); ++ if (ret) ++ err_msg = "Failed to map clken pin to sensor\n"; ++ } else if (type == INT3472_GPIO_TYPE_PRIVACY_LED) { ++ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, ++ "pled", polarity); ++ if (ret) ++ err_msg = "Failed to map pled pin to sensor\n"; ++ } ++ } else { ++ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); ++ if (ret) ++ err_msg = "Failed to map GPIO to clock\n"; ++ } + break; + case INT3472_GPIO_TYPE_POWER_ENABLE: + ret = skl_int3472_register_regulator(int3472, agpio); diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0001-MAINT_GIT-platform-x86-int3472-Evaluate-device-s-_DS.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0001-MAINT_GIT-platform-x86-int3472-Evaluate-device-s-_DS.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0001-MAINT_GIT-platform-x86-int3472-Evaluate-device-s-_DS.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0001-MAINT_GIT-platform-x86-int3472-Evaluate-device-s-_DS.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,238 @@ +From 1c07fff6e0ce61bc48dcd36494d5e057b46c2733 Mon Sep 17 00:00:00 2001 +From: Bingbu Cao +Date: Wed, 31 May 2023 15:44:29 +0200 +Subject: [PATCH 1/8] MAINT_GIT: platform/x86: int3472: Evaluate device's _DSM + method to control imaging clock + +On some platforms, the imaging clock should be controlled by evaluating +specific clock device's _DSM method instead of setting gpio, so this +change register clock if no gpio based clock and then use the _DSM method +to enable and disable clock. + +Signed-off-by: Bingbu Cao +Signed-off-by: Hao Yao +Link: https://lore.kernel.org/r/20230524035135.90315-2-bingbu.cao@intel.com +Signed-off-by: Hans de Goede +Link: https://lore.kernel.org/r/20230531134429.171337-1-hdegoede@redhat.com +--- + .../x86/intel/int3472/clk_and_regulator.c | 92 +++++++++++++++++-- + drivers/platform/x86/intel/int3472/common.h | 14 ++- + drivers/platform/x86/intel/int3472/discrete.c | 8 +- + 3 files changed, 99 insertions(+), 15 deletions(-) + +diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c +index 399f0623ca1b..410073ca371c 100644 +--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c ++++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c +@@ -11,6 +11,41 @@ + + #include "common.h" + ++/* ++ * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 ++ * This _DSM GUID allows controlling the sensor clk when it is not controlled ++ * through a GPIO. ++ */ ++static const guid_t img_clk_guid = ++ GUID_INIT(0x82c0d13a, 0x78c5, 0x4244, ++ 0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11); ++ ++static void skl_int3472_enable_clk(struct int3472_clock *clk, int enable) ++{ ++ struct int3472_discrete_device *int3472 = to_int3472_device(clk); ++ union acpi_object args[3]; ++ union acpi_object argv4; ++ ++ if (clk->ena_gpio) { ++ gpiod_set_value_cansleep(clk->ena_gpio, enable); ++ return; ++ } ++ ++ args[0].integer.type = ACPI_TYPE_INTEGER; ++ args[0].integer.value = clk->imgclk_index; ++ args[1].integer.type = ACPI_TYPE_INTEGER; ++ args[1].integer.value = enable; ++ args[2].integer.type = ACPI_TYPE_INTEGER; ++ args[2].integer.value = 1; ++ ++ argv4.type = ACPI_TYPE_PACKAGE; ++ argv4.package.count = 3; ++ argv4.package.elements = args; ++ ++ acpi_evaluate_dsm(acpi_device_handle(int3472->adev), &img_clk_guid, ++ 0, 1, &argv4); ++} ++ + /* + * The regulators have to have .ops to be valid, but the only ops we actually + * support are .enable and .disable which are handled via .ena_gpiod. Pass an +@@ -20,17 +55,13 @@ static const struct regulator_ops int3472_gpio_regulator_ops; + + static int skl_int3472_clk_prepare(struct clk_hw *hw) + { +- struct int3472_gpio_clock *clk = to_int3472_clk(hw); +- +- gpiod_set_value_cansleep(clk->ena_gpio, 1); ++ skl_int3472_enable_clk(to_int3472_clk(hw), 1); + return 0; + } + + static void skl_int3472_clk_unprepare(struct clk_hw *hw) + { +- struct int3472_gpio_clock *clk = to_int3472_clk(hw); +- +- gpiod_set_value_cansleep(clk->ena_gpio, 0); ++ skl_int3472_enable_clk(to_int3472_clk(hw), 0); + } + + static int skl_int3472_clk_enable(struct clk_hw *hw) +@@ -73,7 +104,7 @@ static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device + static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) + { +- struct int3472_gpio_clock *clk = to_int3472_clk(hw); ++ struct int3472_clock *clk = to_int3472_clk(hw); + + return clk->frequency; + } +@@ -86,8 +117,51 @@ static const struct clk_ops skl_int3472_clock_ops = { + .recalc_rate = skl_int3472_clk_recalc_rate, + }; + +-int skl_int3472_register_clock(struct int3472_discrete_device *int3472, +- struct acpi_resource_gpio *agpio, u32 polarity) ++int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472) ++{ ++ struct acpi_device *adev = int3472->adev; ++ struct clk_init_data init = { ++ .ops = &skl_int3472_clock_ops, ++ .flags = CLK_GET_RATE_NOCACHE, ++ }; ++ int ret; ++ ++ if (int3472->clock.cl) ++ return 0; /* A GPIO controlled clk has already been registered */ ++ ++ if (!acpi_check_dsm(adev->handle, &img_clk_guid, 0, BIT(1))) ++ return 0; /* DSM clock control is not available */ ++ ++ init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(adev)); ++ if (!init.name) ++ return -ENOMEM; ++ ++ int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472); ++ int3472->clock.clk_hw.init = &init; ++ int3472->clock.clk = clk_register(&adev->dev, &int3472->clock.clk_hw); ++ if (IS_ERR(int3472->clock.clk)) { ++ ret = PTR_ERR(int3472->clock.clk); ++ goto out_free_init_name; ++ } ++ ++ int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL, int3472->sensor_name); ++ if (!int3472->clock.cl) { ++ ret = -ENOMEM; ++ goto err_unregister_clk; ++ } ++ ++ kfree(init.name); ++ return 0; ++ ++err_unregister_clk: ++ clk_unregister(int3472->clock.clk); ++out_free_init_name: ++ kfree(init.name); ++ return ret; ++} ++ ++int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472, ++ struct acpi_resource_gpio *agpio, u32 polarity) + { + char *path = agpio->resource_source.string_ptr; + struct clk_init_data init = { +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 61688e450ce5..0c9c899e017b 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -43,7 +43,7 @@ + } + + #define to_int3472_clk(hw) \ +- container_of(hw, struct int3472_gpio_clock, clk_hw) ++ container_of(hw, struct int3472_clock, clk_hw) + + #define to_int3472_device(clk) \ + container_of(clk, struct int3472_discrete_device, clock) +@@ -64,7 +64,9 @@ struct int3472_cldb { + u8 control_logic_type; + u8 control_logic_id; + u8 sensor_card_sku; +- u8 reserved[28]; ++ u8 reserved[10]; ++ u8 clock_source; ++ u8 reserved2[17]; + }; + + struct int3472_gpio_function_remap { +@@ -94,12 +96,13 @@ struct int3472_discrete_device { + struct regulator_desc rdesc; + } regulator; + +- struct int3472_gpio_clock { ++ struct int3472_clock { + struct clk *clk; + struct clk_hw clk_hw; + struct clk_lookup *cl; + struct gpio_desc *ena_gpio; + u32 frequency; ++ u8 imgclk_index; + } clock; + + struct int3472_pled { +@@ -121,8 +124,9 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, + struct acpi_device **sensor_adev_ret, + const char **name_ret); + +-int skl_int3472_register_clock(struct int3472_discrete_device *int3472, +- struct acpi_resource_gpio *agpio, u32 polarity); ++int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472, ++ struct acpi_resource_gpio *agpio, u32 polarity); ++int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472); + void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); + + int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index ef020e23e596..8111579a59d4 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -258,7 +258,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + + break; + case INT3472_GPIO_TYPE_CLK_ENABLE: +- ret = skl_int3472_register_clock(int3472, agpio, polarity); ++ ret = skl_int3472_register_gpio_clock(int3472, agpio, polarity); + if (ret) + err_msg = "Failed to register clock\n"; + +@@ -311,6 +311,11 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) + + acpi_dev_free_resource_list(&resource_list); + ++ /* Register _DSM based clock (no-op if a GPIO clock was already registered) */ ++ ret = skl_int3472_register_dsm_clock(int3472); ++ if (ret < 0) ++ return ret; ++ + int3472->gpios.dev_id = int3472->sensor_name; + gpiod_add_lookup_table(&int3472->gpios); + +@@ -356,6 +361,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) + int3472->adev = adev; + int3472->dev = &pdev->dev; + platform_set_drvdata(pdev, int3472); ++ int3472->clock.imgclk_index = cldb.clock_source; + + ret = skl_int3472_get_sensor_adev_and_name(&pdev->dev, &int3472->sensor, + &int3472->sensor_name); +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0002-MAINT_GIT-platform-x86-int3472-discrete-Drop-GPIO-re.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0002-MAINT_GIT-platform-x86-int3472-discrete-Drop-GPIO-re.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0002-MAINT_GIT-platform-x86-int3472-discrete-Drop-GPIO-re.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0002-MAINT_GIT-platform-x86-int3472-discrete-Drop-GPIO-re.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,113 @@ +From 950c605c54c8b7f863dfc56950aa710a9a4286f3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 16 Jun 2023 19:21:27 +0200 +Subject: [PATCH 2/8] MAINT_GIT: platform/x86: int3472: discrete: Drop GPIO + remapping support + +The only sensor driver which needs GPIO remapping support is the ov2680 +driver and ACPI enumeration support + other necessary changes to +the ov2680 driver were never upstreamed. + +A new series updating the ov2680 driver is pending upstream now and +in this series the ov2680 driver is patched to look for "powerdown" +as con-id, instead of relying on GPIO remapping in the int3472 code, +so the GPIO remapping is no longer necessary. + +Tested-by: Hao Yao +Reviewed-by: Daniel Scally +Signed-off-by: Hans de Goede +Link: https://lore.kernel.org/r/20230616172132.37859-2-hdegoede@redhat.com +--- + drivers/platform/x86/intel/int3472/common.h | 6 --- + drivers/platform/x86/intel/int3472/discrete.c | 37 ++----------------- + 2 files changed, 3 insertions(+), 40 deletions(-) + +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 0c9c899e017b..735567f374a6 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -69,15 +69,9 @@ struct int3472_cldb { + u8 reserved2[17]; + }; + +-struct int3472_gpio_function_remap { +- const char *documented; +- const char *actual; +-}; +- + struct int3472_sensor_config { + const char *sensor_module_name; + struct regulator_consumer_supply supply_map; +- const struct int3472_gpio_function_remap *function_maps; + }; + + struct int3472_discrete_device { +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index 8111579a59d4..2ab3c7466986 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -39,27 +39,13 @@ static const guid_t cio2_sensor_module_guid = + * the functions mapping resources to the sensors. Where the sensors have + * a power enable pin defined in DSDT we need to provide a supply name so + * the sensor drivers can find the regulator. The device name will be derived +- * from the sensor's ACPI device within the code. Optionally, we can provide a +- * NULL terminated array of function name mappings to deal with any platform +- * specific deviations from the documented behaviour of GPIOs. +- * +- * Map a GPIO function name to NULL to prevent the driver from mapping that +- * GPIO at all. ++ * from the sensor's ACPI device within the code. + */ +- +-static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = { +- { "reset", NULL }, +- { "powerdown", "reset" }, +- { } +-}; +- + static const struct int3472_sensor_config int3472_sensor_configs[] = { +- /* Lenovo Miix 510-12ISK - OV2680, Front */ +- { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, + /* Lenovo Miix 510-12ISK - OV5648, Rear */ +- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL) }, + /* Surface Go 1&2 - OV5693, Front */ +- { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, ++ { "YHCU", REGULATOR_SUPPLY("avdd", NULL) }, + }; + + static const struct int3472_sensor_config * +@@ -96,7 +82,6 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 + struct acpi_resource_gpio *agpio, + const char *func, u32 polarity) + { +- const struct int3472_sensor_config *sensor_config; + char *path = agpio->resource_source.string_ptr; + struct gpiod_lookup *table_entry; + struct acpi_device *adev; +@@ -108,22 +93,6 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 + return -EINVAL; + } + +- sensor_config = int3472->sensor_config; +- if (!IS_ERR(sensor_config) && sensor_config->function_maps) { +- const struct int3472_gpio_function_remap *remap; +- +- for (remap = sensor_config->function_maps; remap->documented; remap++) { +- if (!strcmp(func, remap->documented)) { +- func = remap->actual; +- break; +- } +- } +- } +- +- /* Functions mapped to NULL should not be mapped to the sensor */ +- if (!func) +- return 0; +- + status = acpi_get_handle(NULL, path, &handle); + if (ACPI_FAILURE(status)) + return -EINVAL; +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0003-MAINT_GIT-platform-x86-int3472-discrete-Remove-senso.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0003-MAINT_GIT-platform-x86-int3472-discrete-Remove-senso.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0003-MAINT_GIT-platform-x86-int3472-discrete-Remove-senso.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0003-MAINT_GIT-platform-x86-int3472-discrete-Remove-senso.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,200 @@ +From 0302c0d48718d491f37c690a22258fa320101a8a Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 16 Jun 2023 19:21:28 +0200 +Subject: [PATCH 3/8] MAINT_GIT: platform/x86: int3472: discrete: Remove + sensor_config-s + +Currently the only 2 sensor_config-s both specify "avdd" as supply-id. + +The INT3472 device is going to be the only supplier of a regulator for +the sensor device. + +So there is no chance of collisions with other regulator suppliers +and it is undesirable to need to manually add new entries to +int3472_sensor_configs[] for each new sensor module which uses +a GPIO regulator. + +Instead just always use "avdd" as supply-id when registering +the GPIO regulator. + +If necessary for specific sensor drivers then other supply-ids can +be added as aliases in the future, adding aliases will be safe +since INT3472 will be the only regulator supplier for the sensor. + +Cc: Bingbu Cao +Tested-by: Hao Yao +Signed-off-by: Hans de Goede +Reviewed-by: Daniel Scally +Link: https://lore.kernel.org/r/20230616172132.37859-3-hdegoede@redhat.com +--- + .../x86/intel/int3472/clk_and_regulator.c | 40 ++++++++++------- + drivers/platform/x86/intel/int3472/common.h | 7 +-- + drivers/platform/x86/intel/int3472/discrete.c | 45 +++---------------- + 3 files changed, 31 insertions(+), 61 deletions(-) + +diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c +index 410073ca371c..5487f3ab66ad 100644 +--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c ++++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c +@@ -234,32 +234,40 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) + gpiod_put(int3472->clock.ena_gpio); + } + ++/* ++ * The INT3472 device is going to be the only supplier of a regulator for ++ * the sensor device. But unlike the clk framework the regulator framework ++ * does not allow matching by consumer-device-name only. ++ * ++ * Ideally all sensor drivers would use "avdd" as supply-id. But for drivers ++ * where this cannot be changed because another supply-id is already used in ++ * e.g. DeviceTree files an alias for the other supply-id can be added here. ++ * ++ * Do not forget to update GPIO_REGULATOR_SUPPLY_MAP_COUNT when changing this. ++ */ ++static const char * const skl_int3472_regulator_map_supplies[] = { ++ "avdd", ++}; ++ ++static_assert(ARRAY_SIZE(skl_int3472_regulator_map_supplies) == ++ GPIO_REGULATOR_SUPPLY_MAP_COUNT); ++ + int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, + struct acpi_resource_gpio *agpio) + { +- const struct int3472_sensor_config *sensor_config; + char *path = agpio->resource_source.string_ptr; +- struct regulator_consumer_supply supply_map; + struct regulator_init_data init_data = { }; + struct regulator_config cfg = { }; +- int ret; +- +- sensor_config = int3472->sensor_config; +- if (IS_ERR(sensor_config)) { +- dev_err(int3472->dev, "No sensor module config\n"); +- return PTR_ERR(sensor_config); +- } ++ int i, ret; + +- if (!sensor_config->supply_map.supply) { +- dev_err(int3472->dev, "No supply name defined\n"); +- return -ENODEV; ++ for (i = 0; i < ARRAY_SIZE(skl_int3472_regulator_map_supplies); i++) { ++ int3472->regulator.supply_map[i].supply = skl_int3472_regulator_map_supplies[i]; ++ int3472->regulator.supply_map[i].dev_name = int3472->sensor_name; + } + + init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; +- init_data.num_consumer_supplies = 1; +- supply_map = sensor_config->supply_map; +- supply_map.dev_name = int3472->sensor_name; +- init_data.consumer_supplies = &supply_map; ++ init_data.consumer_supplies = int3472->regulator.supply_map; ++ init_data.num_consumer_supplies = GPIO_REGULATOR_SUPPLY_MAP_COUNT; + + snprintf(int3472->regulator.regulator_name, + sizeof(int3472->regulator.regulator_name), "%s-regulator", +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 735567f374a6..225b067c854d 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -28,6 +28,7 @@ + + #define GPIO_REGULATOR_NAME_LENGTH 21 + #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 ++#define GPIO_REGULATOR_SUPPLY_MAP_COUNT 1 + + #define INT3472_LED_MAX_NAME_LEN 32 + +@@ -69,11 +70,6 @@ struct int3472_cldb { + u8 reserved2[17]; + }; + +-struct int3472_sensor_config { +- const char *sensor_module_name; +- struct regulator_consumer_supply supply_map; +-}; +- + struct int3472_discrete_device { + struct acpi_device *adev; + struct device *dev; +@@ -83,6 +79,7 @@ struct int3472_discrete_device { + const struct int3472_sensor_config *sensor_config; + + struct int3472_gpio_regulator { ++ struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT]; + char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; + char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH]; + struct gpio_desc *gpio; +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index 2ab3c7466986..3b410428cec2 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -34,48 +34,17 @@ static const guid_t cio2_sensor_module_guid = + GUID_INIT(0x822ace8f, 0x2814, 0x4174, + 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); + +-/* +- * Here follows platform specific mapping information that we can pass to +- * the functions mapping resources to the sensors. Where the sensors have +- * a power enable pin defined in DSDT we need to provide a supply name so +- * the sensor drivers can find the regulator. The device name will be derived +- * from the sensor's ACPI device within the code. +- */ +-static const struct int3472_sensor_config int3472_sensor_configs[] = { +- /* Lenovo Miix 510-12ISK - OV5648, Rear */ +- { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL) }, +- /* Surface Go 1&2 - OV5693, Front */ +- { "YHCU", REGULATOR_SUPPLY("avdd", NULL) }, +-}; +- +-static const struct int3472_sensor_config * +-skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472) ++static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *int3472) + { + union acpi_object *obj; +- unsigned int i; + + obj = acpi_evaluate_dsm_typed(int3472->sensor->handle, + &cio2_sensor_module_guid, 0x00, + 0x01, NULL, ACPI_TYPE_STRING); +- +- if (!obj) { +- dev_err(int3472->dev, +- "Failed to get sensor module string from _DSM\n"); +- return ERR_PTR(-ENODEV); +- } +- +- for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) { +- if (!strcmp(int3472_sensor_configs[i].sensor_module_name, +- obj->string.pointer)) +- break; ++ if (obj) { ++ dev_dbg(int3472->dev, "Sensor module id: '%s'\n", obj->string.pointer); ++ ACPI_FREE(obj); + } +- +- ACPI_FREE(obj); +- +- if (i >= ARRAY_SIZE(int3472_sensor_configs)) +- return ERR_PTR(-EINVAL); +- +- return &int3472_sensor_configs[i]; + } + + static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472, +@@ -266,11 +235,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) + LIST_HEAD(resource_list); + int ret; + +- /* +- * No error check, because not having a sensor config is not necessarily +- * a failure mode. +- */ +- int3472->sensor_config = skl_int3472_get_sensor_module_config(int3472); ++ skl_int3472_log_sensor_module_name(int3472); + + ret = acpi_dev_get_resources(int3472->adev, &resource_list, + skl_int3472_handle_gpio_resources, +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0004-MAINT_GIT-platform-x86-int3472-discrete-Use-FIELD_GE.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0004-MAINT_GIT-platform-x86-int3472-discrete-Use-FIELD_GE.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0004-MAINT_GIT-platform-x86-int3472-discrete-Use-FIELD_GE.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0004-MAINT_GIT-platform-x86-int3472-discrete-Use-FIELD_GE.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,61 @@ +From 6df3af9f6574234639af2477a1beb020bf6eeb0b Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 16 Jun 2023 19:21:31 +0200 +Subject: [PATCH 4/8] MAINT_GIT: platform/x86: int3472: discrete: Use + FIELD_GET() on the GPIO _DSM return value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add defines for the various fields encoded in the GPIO _DSM integer +return value and then use FIELD_GET() to get field values. + +Suggested-by: Ilpo Järvinen +Reviewed-by: Ilpo Järvinen +Signed-off-by: Hans de Goede +Link: https://lore.kernel.org/r/20230616172132.37859-6-hdegoede@redhat.com +--- + drivers/platform/x86/intel/int3472/discrete.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index 3b410428cec2..557517f43ede 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -2,6 +2,7 @@ + /* Author: Dan Scally */ + + #include ++#include + #include + #include + #include +@@ -25,6 +26,10 @@ static const guid_t int3472_gpio_guid = + GUID_INIT(0x79234640, 0x9e10, 0x4fea, + 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f); + ++#define INT3472_GPIO_DSM_TYPE GENMASK(7, 0) ++#define INT3472_GPIO_DSM_PIN GENMASK(15, 8) ++#define INT3472_GPIO_DSM_SENSOR_ON_VAL GENMASK(31, 24) ++ + /* + * 822ace8f-2814-4174-a56b-5f029fe079ee + * This _DSM GUID returns a string from the sensor device, which acts as a +@@ -174,12 +179,11 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + return 1; + } + +- type = obj->integer.value & 0xff; ++ type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value); + + int3472_get_func_and_polarity(type, &func, &polarity); + +- /* If bits 31-24 of the _DSM entry are all 0 then the signal is inverted */ +- active_value = obj->integer.value >> 24; ++ active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value); + if (!active_value) + polarity ^= GPIO_ACTIVE_LOW; + +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0005-MAINT_GIT-platform-x86-int3472-discrete-Log-a-warnin.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0005-MAINT_GIT-platform-x86-int3472-discrete-Log-a-warnin.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.1.7/0005-MAINT_GIT-platform-x86-int3472-discrete-Log-a-warnin.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.1.7/0005-MAINT_GIT-platform-x86-int3472-discrete-Log-a-warnin.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,54 @@ +From f317447e42b2f6c664d4799725beb0d32bbca39b Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 16 Jun 2023 19:21:32 +0200 +Subject: [PATCH 5/8] MAINT_GIT: platform/x86: int3472: discrete: Log a warning + if the pin-numbers don't match +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The INT3472 discrete code assumes that the ACPI GPIO resources are +in the same order as the pin-info _DSM entries. + +The returned pin-info includes the pin-number in bits 15-8. Add a check +that this matches with the ACPI GPIO resource pin-number in case +the assumption is not true with some ACPI tables. + +Reviewed-by: Daniel Scally +Reviewed-by: Ilpo Järvinen +Signed-off-by: Hans de Goede +Link: https://lore.kernel.org/r/20230616172132.37859-7-hdegoede@redhat.com +--- + drivers/platform/x86/intel/int3472/discrete.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index 557517f43ede..e33c2d75975c 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -154,8 +154,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + { + struct int3472_discrete_device *int3472 = data; + struct acpi_resource_gpio *agpio; ++ u8 active_value, pin, type; + union acpi_object *obj; +- u8 active_value, type; + const char *err_msg; + const char *func; + u32 polarity; +@@ -183,6 +183,12 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + + int3472_get_func_and_polarity(type, &func, &polarity); + ++ pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value); ++ if (pin != agpio->pin_table[0]) ++ dev_warn(int3472->dev, "%s %s pin number mismatch _DSM %d resource %d\n", ++ func, agpio->resource_source.string_ptr, pin, ++ agpio->pin_table[0]); ++ + active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value); + if (!active_value) + polarity ^= GPIO_ACTIVE_LOW; +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.5/0001-platform-x86-int3472-Add-handshake-GPIO-function.patch ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.5/0001-platform-x86-int3472-Add-handshake-GPIO-function.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/int3472-v6.5/0001-platform-x86-int3472-Add-handshake-GPIO-function.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/int3472-v6.5/0001-platform-x86-int3472-Add-handshake-GPIO-function.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,53 @@ +From 8b3c29ef041a410e303d553ca019b42c2e2765e6 Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Thu, 28 Sep 2023 14:17:25 +0800 +Subject: [PATCH] platform/x86: int3472: Add handshake GPIO function + +Handshake pin is used for Lattice MIPI aggregator to enable the +camera sensor. After pulled up, recommend to wail ~250ms to get +everything ready. + +Signed-off-by: Hao Yao +--- + drivers/platform/x86/intel/int3472/common.h | 1 + + drivers/platform/x86/intel/int3472/discrete.c | 5 +++++ + 2 files changed, 6 insertions(+) + +diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h +index 655ae3ec0593..3ad4c72afb45 100644 +--- a/drivers/platform/x86/intel/int3472/common.h ++++ b/drivers/platform/x86/intel/int3472/common.h +@@ -23,6 +23,7 @@ + #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b + #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c + #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d ++#define INT3472_GPIO_TYPE_HANDSHAKE 0x12 + + #define INT3472_PDEV_MAX_NAME_LEN 23 + #define INT3472_MAX_SENSOR_GPIOS 3 +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index b644ce65c990..4753161b4080 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -111,6 +111,10 @@ static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polar + *func = "power-enable"; + *polarity = GPIO_ACTIVE_HIGH; + break; ++ case INT3472_GPIO_TYPE_HANDSHAKE: ++ *func = "handshake"; ++ *polarity = GPIO_ACTIVE_HIGH; ++ break; + default: + *func = "unknown"; + *polarity = GPIO_ACTIVE_HIGH; +@@ -201,6 +205,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, + switch (type) { + case INT3472_GPIO_TYPE_RESET: + case INT3472_GPIO_TYPE_POWERDOWN: ++ case INT3472_GPIO_TYPE_HANDSHAKE: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity); + if (ret) + err_msg = "Failed to map GPIO pin to sensor\n"; +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/IOMMU-passthrough-for-intel-ipu.diff ipu6-drivers-0~git202310180730.3f813580/patch/IOMMU-passthrough-for-intel-ipu.diff --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/IOMMU-passthrough-for-intel-ipu.diff 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/IOMMU-passthrough-for-intel-ipu.diff 1970-01-01 08:00:00.000000000 +0800 @@ -1,82 +0,0 @@ -diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c -index 0ea47e17b379..4b69566cb34f 100644 ---- a/drivers/iommu/intel/iommu.c -+++ b/drivers/iommu/intel/iommu.c -@@ -37,6 +37,13 @@ - #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) - #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) - #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) -+#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ -+ ((pdev)->device == 0xa75d || \ -+ (pdev)->device == 0x9a19 || \ -+ (pdev)->device == 0x9a39 || \ -+ (pdev)->device == 0x4e19 || \ -+ (pdev)->device == 0x465d || \ -+ (pdev)->device == 0x1919)) - #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) - - #define IOAPIC_RANGE_START (0xfee00000) -@@ -307,12 +314,14 @@ int intel_iommu_enabled = 0; - EXPORT_SYMBOL_GPL(intel_iommu_enabled); - - static int dmar_map_gfx = 1; -+static int dmar_map_ipu = 1; - static int intel_iommu_superpage = 1; - static int iommu_identity_mapping; - static int iommu_skip_te_disable; - - #define IDENTMAP_GFX 2 - #define IDENTMAP_AZALIA 4 -+#define IDENTMAP_IPU 8 - - int intel_iommu_gfx_mapped; - EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); -@@ -2724,6 +2733,9 @@ static int device_def_domain_type(struct device *dev) - - if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev)) - return IOMMU_DOMAIN_IDENTITY; -+ -+ if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) -+ return IOMMU_DOMAIN_IDENTITY; - } - - return 0; -@@ -3160,6 +3172,9 @@ static int __init init_dmars(void) - if (!dmar_map_gfx) - iommu_identity_mapping |= IDENTMAP_GFX; - -+ if (!dmar_map_ipu) -+ iommu_identity_mapping |= IDENTMAP_IPU; -+ - check_tylersburg_isoch(); - - ret = si_domain_init(hw_pass_through); -@@ -4931,6 +4946,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev) - dmar_map_gfx = 0; - } - -+static void quirk_iommu_ipu(struct pci_dev *dev) -+{ -+ if (!IS_INTEL_IPU(dev)) -+ return; -+ -+ if (risky_device(dev)) -+ return; -+ -+ pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n"); -+ dmar_map_ipu = 0; -+} -+ - /* G4x/GM45 integrated gfx dmar support is totally busted. */ - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx); -@@ -4966,6 +4993,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); - -+/* disable IPU dmar support */ -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu); -+ - static void quirk_iommu_rwbf(struct pci_dev *dev) - { - if (risky_device(dev)) diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0002-media-ov13b10-Add-1364x768-register-settings.patch ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0002-media-ov13b10-Add-1364x768-register-settings.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0002-media-ov13b10-Add-1364x768-register-settings.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0002-media-ov13b10-Add-1364x768-register-settings.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,96 @@ +From 52bf7eabd410f9990f2e132bbc6aa88932d4dc9a Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Thu, 15 Jun 2023 10:58:26 +0800 +Subject: [PATCH 2/2] media: ov13b10: Add 1364x768 register settings + +Signed-off-by: Hao Yao +--- + drivers/media/i2c/ov13b10.c | 58 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 57 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c +index d7317e1e7f3f..f1f17765236c 100644 +--- a/drivers/media/i2c/ov13b10.c ++++ b/drivers/media/i2c/ov13b10.c +@@ -31,6 +31,7 @@ + #define OV13B10_REG_VTS 0x380e + #define OV13B10_VTS_30FPS 0x0c7c + #define OV13B10_VTS_60FPS 0x063e ++#define OV13B10_VTS_120FPS 0x0320 + #define OV13B10_VTS_MAX 0x7fff + + /* HBLANK control - read only */ +@@ -461,6 +462,50 @@ static const struct ov13b10_reg mode_2080x1170_regs[] = { + {0x5001, 0x0d}, + }; + ++static const struct ov13b10_reg mode_1364x768_120fps_regs[] = { ++ {0x0305, 0xaf}, ++ {0x3011, 0x7c}, ++ {0x3501, 0x03}, ++ {0x3502, 0x00}, ++ {0x3662, 0x88}, ++ {0x3714, 0x28}, ++ {0x3739, 0x10}, ++ {0x37c2, 0x14}, ++ {0x37d9, 0x06}, ++ {0x37e2, 0x0c}, ++ {0x37e4, 0x00}, ++ {0x3800, 0x02}, /* X start 740 */ ++ {0x3801, 0xe4}, ++ {0x3802, 0x03}, /* Y start 840 */ ++ {0x3803, 0x48}, ++ {0x3804, 0x0d}, /* X end 3499 */ ++ {0x3805, 0xab}, ++ {0x3806, 0x09}, /* Y end 2400 */ ++ {0x3807, 0x60}, ++ {0x3808, 0x05}, /* X out size 1364 */ ++ {0x3809, 0x54}, ++ {0x380a, 0x03}, /* Y out size 768 */ ++ {0x380b, 0x00}, ++ {0x380c, 0x04}, ++ {0x380d, 0x8e}, ++ {0x380e, 0x03}, ++ {0x380f, 0x20}, ++ {0x3811, 0x07}, /* isp x offset 7 */ ++ {0x3813, 0x07}, /* isp y offset 7 */ ++ {0x3814, 0x03}, ++ {0x3816, 0x03}, ++ {0x3820, 0x8b}, ++ {0x3c8c, 0x18}, ++ {0x4008, 0x00}, ++ {0x4009, 0x05}, ++ {0x4050, 0x00}, ++ {0x4051, 0x05}, ++ {0x4501, 0x08}, ++ {0x4505, 0x04}, ++ {0x5000, 0xfd}, ++ {0x5001, 0x0d}, ++}; ++ + static const char * const ov13b10_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", +@@ -561,7 +606,18 @@ static const struct ov13b10_mode supported_modes[] = { + .regs = mode_2080x1170_regs, + }, + .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, +- } ++ }, ++ { ++ .width = 1364, ++ .height = 768, ++ .vts_def = OV13B10_VTS_120FPS, ++ .vts_min = OV13B10_VTS_120FPS, ++ .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_1364x768_120fps_regs), ++ .regs = mode_1364x768_120fps_regs, ++ }, ++ }, + }; + + struct ov13b10 { +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0006-MLIST-media-ov13b10-support-new-ACPI-HID-OVTI13B1.patch ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0006-MLIST-media-ov13b10-support-new-ACPI-HID-OVTI13B1.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0006-MLIST-media-ov13b10-support-new-ACPI-HID-OVTI13B1.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0006-MLIST-media-ov13b10-support-new-ACPI-HID-OVTI13B1.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,29 @@ +From 3c307ed62acf60b55611e7a69eb4c7ec72bf19ed Mon Sep 17 00:00:00 2001 +From: Bingbu Cao +Date: Fri, 26 May 2023 18:07:23 +0800 +Subject: [PATCH 6/8] MLIST: media: ov13b10: support new ACPI HID 'OVTI13B1' + +On ACPI systems, the HID of ov13b10 is 'OVTI13B1', add this new +HID in acpi IDs table to make driver support it. + +Signed-off-by: Hao Yao +Signed-off-by: Bingbu Cao +--- + drivers/media/i2c/ov13b10.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c +index c1430044fb1e..ede33899248c 100644 +--- a/drivers/media/i2c/ov13b10.c ++++ b/drivers/media/i2c/ov13b10.c +@@ -1484,6 +1484,7 @@ static const struct dev_pm_ops ov13b10_pm_ops = { + #ifdef CONFIG_ACPI + static const struct acpi_device_id ov13b10_acpi_ids[] = { + {"OVTIDB10"}, ++ {"OVTI13B1"}, + { /* sentinel */ } + }; + +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0007-MLIST-media-ov13b10-Defer-probe-if-no-endpoint-found.patch ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0007-MLIST-media-ov13b10-Defer-probe-if-no-endpoint-found.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0007-MLIST-media-ov13b10-Defer-probe-if-no-endpoint-found.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0007-MLIST-media-ov13b10-Defer-probe-if-no-endpoint-found.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,45 @@ +From 4118cc61955e7c8d4f88d05d38fe1ea9fa690b8b Mon Sep 17 00:00:00 2001 +From: Bingbu Cao +Date: Fri, 26 May 2023 18:07:24 +0800 +Subject: [PATCH 7/8] MLIST: media: ov13b10: Defer probe if no endpoint found + +The ov13b10 need be connected to a CIO2 or IPU device by bridge, sometimes +the bridge driver was not probed before ov13b10 driver, then the absence +of the fwnode endpoint for this device is expected, so driver return +-EPROBE_DEFER in this case to let the probe occur after bridge driver. + +Signed-off-by: Hao Yao +Signed-off-by: Bingbu Cao +--- + drivers/media/i2c/ov13b10.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c +index ede33899248c..2d48c94659a4 100644 +--- a/drivers/media/i2c/ov13b10.c ++++ b/drivers/media/i2c/ov13b10.c +@@ -1331,6 +1331,10 @@ static int ov13b10_check_hwcfg(struct device *dev) + if (!fwnode) + return -ENXIO; + ++ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); ++ if (!ep) ++ return -EPROBE_DEFER; ++ + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &ext_clk); + if (ret) { +@@ -1344,10 +1348,6 @@ static int ov13b10_check_hwcfg(struct device *dev) + return -EINVAL; + } + +- ep = fwnode_graph_get_next_endpoint(fwnode, NULL); +- if (!ep) +- return -ENXIO; +- + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0008-MLIST-media-ov13b10-add-PM-control-support-based-on-.patch ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0008-MLIST-media-ov13b10-add-PM-control-support-based-on-.patch --- ipu6-drivers-0~git202302081010.7fdfb5eb/patch/ov13b10-v6.3/0008-MLIST-media-ov13b10-add-PM-control-support-based-on-.patch 1970-01-01 08:00:00.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/patch/ov13b10-v6.3/0008-MLIST-media-ov13b10-add-PM-control-support-based-on-.patch 2023-10-18 15:30:24.000000000 +0800 @@ -0,0 +1,246 @@ +From 7682c98857e3486cc5a3f20759c072ad425859a7 Mon Sep 17 00:00:00 2001 +From: Bingbu Cao +Date: Thu, 15 Jun 2023 13:54:16 +0800 +Subject: [PATCH 8/8] MLIST: media: ov13b10: add PM control support based on + power resources + +On ACPI based platforms, the ov13b10 camera sensor need to be powered +up by acquire the PM resource from INT3472. INT3472 will register one +regulator 'avdd', one reset gpio and clock source for ov13b10. +Add 2 power interfaces that are registered as runtime PM callbacks. + +Signed-off-by: Bingbu Cao +Signed-off-by: Hao Yao +Suggested-by: Hans de Goede +Reviewed-by: Hans de Goede +--- + drivers/media/i2c/ov13b10.c | 120 +++++++++++++++++++++++++++++++++--- + 1 file changed, 110 insertions(+), 10 deletions(-) + +diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c +index 2d48c94659a4..a6ac53e4fd89 100644 +--- a/drivers/media/i2c/ov13b10.c ++++ b/drivers/media/i2c/ov13b10.c +@@ -2,6 +2,9 @@ + // Copyright (c) 2021 Intel Corporation. + + #include ++#include ++#include ++#include + #include + #include + #include +@@ -573,6 +576,11 @@ struct ov13b10 { + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; ++ ++ struct clk *img_clk; ++ struct regulator *avdd; ++ struct gpio_desc *reset; ++ + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; +@@ -1051,6 +1059,49 @@ static int ov13b10_identify_module(struct ov13b10 *ov13b) + return 0; + } + ++static int ov13b10_power_off(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct ov13b10 *ov13b10 = to_ov13b10(sd); ++ ++ gpiod_set_value_cansleep(ov13b10->reset, 1); ++ ++ if (ov13b10->avdd) ++ regulator_disable(ov13b10->avdd); ++ ++ clk_disable_unprepare(ov13b10->img_clk); ++ ++ return 0; ++} ++ ++static int ov13b10_power_on(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct ov13b10 *ov13b10 = to_ov13b10(sd); ++ int ret; ++ ++ ret = clk_prepare_enable(ov13b10->img_clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable imaging clock: %d", ret); ++ return ret; ++ } ++ ++ if (ov13b10->avdd) { ++ ret = regulator_enable(ov13b10->avdd); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable avdd: %d", ret); ++ clk_disable_unprepare(ov13b10->img_clk); ++ return ret; ++ } ++ } ++ ++ gpiod_set_value_cansleep(ov13b10->reset, 0); ++ /* 5ms to wait ready after XSHUTDN assert */ ++ usleep_range(5000, 5500); ++ ++ return 0; ++} ++ + static int ov13b10_start_streaming(struct ov13b10 *ov13b) + { + struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); +@@ -1145,7 +1196,7 @@ static int ov13b10_set_stream(struct v4l2_subdev *sd, int enable) + return ret; + } + +-static int __maybe_unused ov13b10_suspend(struct device *dev) ++static int ov13b10_suspend(struct device *dev) + { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b = to_ov13b10(sd); +@@ -1153,26 +1204,35 @@ static int __maybe_unused ov13b10_suspend(struct device *dev) + if (ov13b->streaming) + ov13b10_stop_streaming(ov13b); + ++ ov13b10_power_off(dev); ++ + return 0; + } + +-static int __maybe_unused ov13b10_resume(struct device *dev) ++static int ov13b10_resume(struct device *dev) + { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b = to_ov13b10(sd); + int ret; + ++ ret = ov13b10_power_on(dev); ++ if (ret) ++ goto pm_fail; ++ + if (ov13b->streaming) { + ret = ov13b10_start_streaming(ov13b); + if (ret) +- goto error; ++ goto stop_streaming; + } + + return 0; + +-error: ++stop_streaming: + ov13b10_stop_streaming(ov13b); ++ ov13b10_power_off(dev); ++pm_fail: + ov13b->streaming = false; ++ + return ret; + } + +@@ -1317,6 +1377,34 @@ static void ov13b10_free_controls(struct ov13b10 *ov13b) + mutex_destroy(&ov13b->mutex); + } + ++static int ov13b10_get_pm_resources(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct ov13b10 *ov13b = to_ov13b10(sd); ++ int ret; ++ ++ ov13b->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(ov13b->reset)) ++ return dev_err_probe(dev, PTR_ERR(ov13b->reset), ++ "failed to get reset gpio\n"); ++ ++ ov13b->img_clk = devm_clk_get_optional(dev, NULL); ++ if (IS_ERR(ov13b->img_clk)) ++ return dev_err_probe(dev, PTR_ERR(ov13b->img_clk), ++ "failed to get imaging clock\n"); ++ ++ ov13b->avdd = devm_regulator_get_optional(dev, "avdd"); ++ if (IS_ERR(ov13b->avdd)) { ++ ret = PTR_ERR(ov13b->avdd); ++ ov13b->avdd = NULL; ++ if (ret != -ENODEV) ++ return dev_err_probe(dev, ret, ++ "failed to get avdd regulator\n"); ++ } ++ ++ return 0; ++} ++ + static int ov13b10_check_hwcfg(struct device *dev) + { + struct v4l2_fwnode_endpoint bus_cfg = { +@@ -1407,13 +1495,23 @@ static int ov13b10_probe(struct i2c_client *client) + /* Initialize subdev */ + v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); + ++ ret = ov13b10_get_pm_resources(&client->dev); ++ if (ret) ++ return ret; ++ + full_power = acpi_dev_state_d0(&client->dev); + if (full_power) { ++ ov13b10_power_on(&client->dev); ++ if (ret) { ++ dev_err(&client->dev, "failed to power on\n"); ++ return ret; ++ } ++ + /* Check module identity */ + ret = ov13b10_identify_module(ov13b); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); +- return ret; ++ goto error_power_off; + } + } + +@@ -1422,7 +1520,7 @@ static int ov13b10_probe(struct i2c_client *client) + + ret = ov13b10_init_controls(ov13b); + if (ret) +- return ret; ++ goto error_power_off; + + /* Initialize subdev */ + ov13b->sd.internal_ops = &ov13b10_internal_ops; +@@ -1462,6 +1560,9 @@ static int ov13b10_probe(struct i2c_client *client) + ov13b10_free_controls(ov13b); + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + ++error_power_off: ++ ov13b10_power_off(&client->dev); ++ + return ret; + } + +@@ -1477,9 +1578,8 @@ static void ov13b10_remove(struct i2c_client *client) + pm_runtime_disable(&client->dev); + } + +-static const struct dev_pm_ops ov13b10_pm_ops = { +- SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume) +-}; ++static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend, ++ ov13b10_resume, NULL); + + #ifdef CONFIG_ACPI + static const struct acpi_device_id ov13b10_acpi_ids[] = { +@@ -1494,7 +1594,7 @@ MODULE_DEVICE_TABLE(acpi, ov13b10_acpi_ids); + static struct i2c_driver ov13b10_i2c_driver = { + .driver = { + .name = "ov13b10", +- .pm = &ov13b10_pm_ops, ++ .pm = pm_ptr(&ov13b10_pm_ops), + .acpi_match_table = ACPI_PTR(ov13b10_acpi_ids), + }, + .probe_new = ov13b10_probe, +-- +2.34.1 + diff -Nru ipu6-drivers-0~git202302081010.7fdfb5eb/README.md ipu6-drivers-0~git202310180730.3f813580/README.md --- ipu6-drivers-0~git202302081010.7fdfb5eb/README.md 2023-02-08 18:10:56.000000000 +0800 +++ ipu6-drivers-0~git202310180730.3f813580/README.md 2023-10-18 15:30:24.000000000 +0800 @@ -1,7 +1,7 @@ # ipu6-drivers -This repository supports MIPI cameras through the IPU6 on Intel Tiger Lake and -Alder Lake platforms. There are 4 repositories that provide the complete setup: +This repository supports MIPI cameras through the IPU6 on Intel Tiger Lake, Alder Lake, Raptor Lake and Meteor Lake platforms. +There are 4 repositories that provide the complete setup: - https://github.com/intel/ipu6-drivers - kernel drivers for the IPU and sensors - https://github.com/intel/ipu6-camera-bins - IPU firmware and proprietary image processing libraries @@ -11,7 +11,8 @@ ## Content of this repository: - IPU6 kernel driver -- Drivers for HM11B1, OV01A1S, OV01A10, OV02C10, OV2740, HM2170 and HI556 sensors +- Kernel patches needed +- Drivers for HM11B1, OV01A1S, OV01A10, OV02C10, OV02E10, OV2740, HM2170, HM2172 and HI556 sensors ## Build instructions: Three ways are available: @@ -20,18 +21,24 @@ 3. and build with dkms ### 1. Build with kernel source tree -- Tested with kernel 6.0 +- Tested with kernel v6.4 - Check out kernel - Apply patches: ```sh - # For IPU6 - patch/IOMMU-passthrough-for-intel-ipu.diff + # For Meteor Lake B stepping only + patch/0002-iommu-Add-passthrough-for-MTL-IPU.patch - # For 5.15 <= kernel version < 5.17 - patch/int3472-support-independent-clock-and-LED-gpios.patch + # For v5.15 <= kernel version < v5.17 and using INT3472 + patch/int3472-v5.15/*.patch - # For kernel version >= 5.17 - patch/int3472-support-independent-clock-and-LED-gpios-5.17+.patch + # For v5.17 <= kernel version < v6.1.7 and using INT3472 + patch/int3472-v5.17/*.patch + + # For kernel version >= 6.1.7 and using INT3472 + patch/int3472-v6.1.7/*.patch + + # For kernel version >= 6.3 and using ov13b10 + patch/ov13b10-v6.3/*.patch ``` - Copy repo content to kernel source **(except Makefile and drivers/media/i2c/{Kconfig,Makefile}, will change manually next)** - Modify related Kconfig and Makefile @@ -104,6 +111,20 @@ To compile this driver as a module, choose M here: the module will be called ov02c10. + config VIDEO_OV02E10 + tristate "OmniVision OV02E10 sensor support" + depends on VIDEO_DEV && I2C + depends on ACPI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02E10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02e10. + config VIDEO_HM2170 tristate "Himax HM2170 sensor support" depends on VIDEO_DEV && I2C @@ -117,6 +138,19 @@ To compile this driver as a module, choose M here: the module will be called hm2170. + config VIDEO_HM2172 + tristate "Himax HM2172 sensor support" + depends on VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Himax + HM2170 camera. + + To compile this driver as a module, choose M here: the + module will be called hm2172. + ``` - Add to drivers/media/i2c/Makefile @@ -126,7 +160,9 @@ obj-$(CONFIG_VIDEO_HM11B1) += hm11b1.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o + obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o obj-$(CONFIG_VIDEO_HM2170) += hm2170.o + obj-$(CONFIG_VIDEO_HM2170) += hm2172.o ``` - Modify drivers/media/pci/Kconfig @@ -145,8 +181,12 @@ CONFIG_VIDEO_OV01A10=m CONFIG_VIDEO_HM11B1=m CONFIG_VIDEO_OV02C10=m + CONFIG_VIDEO_OV02E10=m CONFIG_VIDEO_HM2170=m - # If your kernel < 5.15 or not set CONFIG_INTEL_SKL_INT3472, please add the line below: + CONFIG_VIDEO_HM2172=m + # Set this only if you use only 1 camera and don't want too many device nodes in media-ctl + # CONFIG_IPU_SINGLE_BE_SOC_DEVICE=y + # If your kernel < 5.15 or not set CONFIG_INTEL_SKL_INT3472, please set this # CONFIG_POWER_CTRL_LOGIC=m ``` - LJCA and CVF part as below, please check details at https://github.com/intel/ivsc-driver/blob/main/README.md