Apply the IPU7 camera's ISP tuning, fix its gain, range and exposure, harden PSYS pinning, and support Lunar Lake (#723)
* Apply the Intel IPU7 camera's ISP tuning and fix its gain, range and exposure The XPS 14 / 16 webcam looked soft and grainy because almost none of the image pipeline was tuned: - 0011: the graph asks for ISP tuning mode 4, which the OV08X40 tuning does not carry, so the generic AIC found no tunings and noise reduction, TNR and sharpening ran on library defaults. Fall back to the tuning's default ISP container. - 0010: AIQ emits the raw analog gain register code, which the in-tree ov08x40 driver halves, so the sensor ran at twice the gain AE and the ISP noise model assumed. - 0008: the HAL ignored the requested YUV range and always produced full-range frames that consumers decode as limited range. - 0009 + relay config: icamerasrc pinned auto exposure to 1/30 s; a new fps-range property lets AE lengthen frames in dim light, with gain capped at 27 dB. * Guard fps-range against NULL and correct two descriptions Setting icamerasrc's new fps-range property to NULL, its own default, handed NULL to gst_camerasrc_parse_range, which crashed in strlen(). NULL now leaves the last range in effect, because the HAL has no way to drop a range once set. The value the relay sets goes through unchanged. The relay comment said AE raises gain past 27 dB once frames reach 15 fps, but gain-range becomes a hard ISO ceiling (manual_iso_max), so gain never goes past it. The 0008 message credited the sensor JSON's yuvColorRangeMode, which only the mock HAL reads. Co-Authored-By: Codex XHigh <noreply@openai.com> * Free fps-range when icamerasrc is destroyed finalize never freed the string the fps-range setter allocates, so every icamerasrc element that had the property set leaked it. The other string properties leak the same way upstream and are left to an upstream fix for all of them. Co-Authored-By: Codex XHigh <noreply@openai.com> * Harden IPU7 PSYS userptr pinning (cherry picked from commit 5f1d0013b0e37d9229dfb906806dee0e98199035) * Avoid unverified IPU7 permission assurances (cherry picked from commit 9ce97d9788f2be508743fe785f8434ccfec7842e) * Check IPU7 against Omarchy headers without a runtime dependency Use linux-omarchy-headers only for check(), avoiding Arch headers on installed Omarchy systems and matching the supported kernel build configuration. Signed-off-by: Afonso Oliveira <afonso.oliveira707@gmail.com> (cherry picked from commit d0d382c07ea99eca5bb5c52240841db779d24b14) * Test IPU7 build-only headers and kernel-tree selection Signed-off-by: Afonso Oliveira <afonso.oliveira707@gmail.com> (cherry picked from commit eb37cc828fac201a360c35aa1e87a1daf2d5fed1) * Restrict the IPU7 PSYS node to root Intel's PSYS driver has two buffer-lifecycle bugs that this package does not fix: a GETBUF that is never mapped leaves the buffer owned by both the PSYS handle and the exported dma-buf, so closing the two is a use-after-free and a double free, and UNMAPBUF drops its mapping reference before clearing the attachment, racing a concurrent dma-buf release. Any account that can open /dev/ipu7-psys0 can reach both. Nothing but v4l2-relayd@ipu7, which runs as root, opens the node; applications use the v4l2loopback device. With the node at 0600 root:root the camera streams the same, so the video group and the seat user lose nothing and the bugs need root. GROUP and MODE are explicit so the upgrade's change event also tightens nodes on running machines. Co-Authored-By: Codex XHigh <noreply@openai.com> * Drop the DKMS Intel CVS driver now that linux-omarchy ships it linux-omarchy and linux-omarchy-bore build drivers/media/i2c/cvs in-tree (CONFIG_VIDEO_INTEL_CVS=m) and carry the same wake-IRQ fix as our 0007 (0542), plus the Nova Lake ACPI ID (0541) that our copy lacks. The DKMS module has the same name and installs under updates/, so on every 7.2+ kernel it displaced the kernel's signed module with an unsigned, older copy: on Nova Lake that copy cannot bind the CVS device and the camera fails, and any later fix in the kernel's driver would be masked. Krzysztof Wilczyński reported the conflict on #723. Skipping the build only on kernels that carry the driver (BUILD_EXCLUSIVE_CONFIG) would have kept it for stock Arch kernels, but the pacman dkms hook reports every such skip as "exited 77", so every Omarchy kernel update would print a warning. A stock Arch kernel left installed as the fallback boot entry now has no camera. On upgrade the dkms hook removes intel-cvs and restores the kernel's original module. vision-drivers still covers kernels before 7.2. * intel-ipu7-camera: build the Lunar Lake HAL plugin and stop rotating its frames Two Panther Lake assumptions in this package break Lunar Lake boards, where it is installed by the same hardware detection. Build both HAL platforms. libcamhal picks its plugin by platform, and a Lunar Lake machine asks for one that was never built: CamHAL[ERR] HalAdaptor: load_camera_hal_library, failed to open library: /usr/lib/libcamhal/plugins/ipu7x.so: No such file or directory CamHAL[WAR] CameraParserInvoker: parseSensors: No sensors available so the camera cannot work at all. The proprietary side of it is already shipped -- libia_aic-ipu7x.so and the rest of that set come from ipu7-camera-bins today; only the plugin the HAL loads was missing. Upstream builds both platforms from one configure run and `make install` lays down /etc/camera/ipu7x/ alongside ipu75xa, including the tuning this hardware wants (OV08X40_BBG802N3_LNL.aiqb, gcss/OV08X40_BBG802N3_LNL.IPU7X.bin), so the change is the IPU_VERSIONS list plus 0008, the ipu7x twin of 0006: the "Intel CVS" pad formats that 1.0.6 added to the ipu75xa sensor config are needed in the ipu7x one for the same reason, or link validation fails at stream-on behind the Linux 7.2 bridge entity. Pick the relay pipeline per board. v4l2-relayd-ipu7.conf rotates every frame 180 degrees, which is right where the sensor is mounted inverted and wrong here: the sensor reports camera_sensor_rotation = 0 and camera_orientation = Front, and the picture arrives upside down in every application. camera-init now writes VIDEOSRC to /run based on the bridge ACPI id and the relay drop-in reads it. Only INTC10DE takes the new path; INTC10E1, INTC10CF, INTC10E0 and anything unrecognised keep the packaged pipeline byte for byte, and a boot where camera-init did not run falls back to it as well. Verified on a Dell Pro 14 Premium PA14250 (Lunar Lake, INTC10DE, OV08X40 + HM1092) against intel-ipu7-camera 1.0.6-2 on linux-omarchy 7.2.5-3: with the package's own intel-cvs DKMS the sensor joins the graph behind "Intel CVS", the ipu7x plugin built from the pinned commit with 0005 and 0008 resolves ov08x40-uf on CSI port 0, v4l2-relayd streams 30 fps to /dev/video50 and the image is upright in a browser. Note for reviewers: necessary but not sufficient on Lunar Lake. Three more things this board needs are not in this PR: the sensor probe races the bridge's runtime suspend (camera-init's `sleep 2` lands while the I2C bus is still owned by the bridge firmware and ov08x40 reads its chip id as -110), the in-tree cvs driver's quirk for the Synaptics SVP7500 (06cb:0701) hands the privacy LED to the host so it never lights, and ipu-bridge before 7.3 does not know the HM1092 IR sensor. Details in #366. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 9be51b47104da9d115a7d79e04bc2ae5445809fc) * Derive the Lunar Lake relay pipeline from the packaged one camera-videosrc-init wrote VIDEOSRC for every board, with a copy of the pipeline the relay config carried when it was written, and its file is read after /etc/v4l2-relayd.d/ipu7.conf. On Panther Lake that replaced this branch's fps-range, gain-range and color-range settings with the old sharpness=80 ev=-1 saturation=10 pipeline. It now writes nothing unless the board is Lunar Lake, and there it takes the packaged pipeline and drops only the rotation, so a later change to the relay config reaches both platforms. It also removes a file left by an earlier run, which camera-init's restart on resume would otherwise keep. The ipu7x CVS format patch becomes 0012: 0008 is already the YUV range patch. * Regenerate the Lunar Lake pipeline on every relay start camera-videosrc-init ran from camera-init.service and sourced /etc/v4l2-relayd.d/ipu7.conf as bash. A config that is valid for systemd but not for bash, such as an unquoted VIDEOSRC, made it fail and Lunar Lake fell back to the rotated pipeline; and because camera-init stays active, editing the config and restarting only the relay kept the override cached since boot. It now runs as the relay's ExecStartPre, where systemd hands it VIDEOSRC already parsed from the relay's own environment files, and writes the override quoted for systemd's parser. ExecStart re-reads the drop-in's EnvironmentFile and ExecStopPost removes it, so each start sees the current config. Co-Authored-By: Codex XHigh <noreply@openai.com> * Scale the Lunar Lake ov08x40's analog gain codes too The Lunar Lake tuning (OV08X40_BBG802N3_LNL.aiqb) carries the same CMC gain table as the Panther Lake one, 1x = code 256, and both platforms use the same in-tree ov08x40 driver, which takes 1x = 128. Without the shift a 4x request ran the Lunar Lake sensor at 8x, the mismatch 0010 fixes on Panther Lake. 0010 now sets analogGainCodeShift in the ipu7x sensor config as well. Co-Authored-By: Codex XHigh <noreply@openai.com> * Remove the Lunar Lake override before the generator runs systemd passes ExecStartPre the unit's environment files as they stand when that command starts, so the generator saw a /run override left behind by a crash or SIGKILL (anything that skipped ExecStopPost) and took its VIDEOSRC for the configured one: an edited /etc/v4l2-relayd.d/ipu7.conf would lose to the stale file. Removing the file in its own ExecStartPre first means the generator's environment is re-read without it. Co-Authored-By: Codex XHigh <noreply@openai.com> * Author the HAL and icamerasrc patches from the Omarchy address --------- Signed-off-by: Afonso Oliveira <afonso.oliveira707@gmail.com> Co-authored-by: Codex XHigh <noreply@openai.com> Co-authored-by: Afonso Oliveira <afonso.oliveira707@gmail.com> Co-authored-by: Kolbas <pkolbas@pm.me> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
23 files changed
+979
-180
No files matched your search
@@ -50,6 +50,8 @@ jobs:
|
||||
pacman -Syu --noconfirm git jq python libarchive neovim tmux
|
||||
python tests/oma-service-removal.py
|
||||
python tests/upstream-watch.py
|
||||
bash tests/ipu7-install.sh
|
||||
bash tests/ipu7-headers.sh
|
||||
./bin/sync-upstream self-test
|
||||
./bin/sync-rebuilds --self-test
|
||||
./bin/omarchy-pkgs self-test
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Diff context has a marker space before upstream tabs and blank lines.
|
||||
*.patch whitespace=-blank-at-eol,-space-before-tab
|
||||
@@ -0,0 +1,153 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Afonso Oliveira <afonso.oliveira707@gmail.com>
|
||||
Date: Tue, 1 Sep 2026 21:50:00 +0100
|
||||
Subject: [PATCH] media: intel/ipu7: harden PSYS userptr pinning
|
||||
|
||||
Reject invalid and oversized userptr ranges before allocating their page
|
||||
arrays. Keep the page count in size_t, use overflow-checked allocation,
|
||||
remove FOLL_FORCE, and require the entire range to be pinned before
|
||||
publishing attachment state. Clear the attachment state after releasing
|
||||
it. Clear failed buffer attachments before the exported fd can later
|
||||
release the surviving buffer.
|
||||
|
||||
Upstream-Status: Not submitted
|
||||
---
|
||||
drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 71 lines changed
|
||||
1 file changed, 40 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
|
||||
index fee9ee82465b..03fe0b1d054b 100644
|
||||
--- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
|
||||
+++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/overflow.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/poll.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
@@ -49,63 +50,63 @@
|
||||
|
||||
static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach)
|
||||
{
|
||||
- struct vm_area_struct *vma;
|
||||
- unsigned long start, end;
|
||||
- int npages, array_size;
|
||||
+ unsigned long start, last;
|
||||
+ size_t npages;
|
||||
struct page **pages;
|
||||
struct sg_table *sgt;
|
||||
int ret = -ENOMEM;
|
||||
- int nr = 0;
|
||||
+ long nr;
|
||||
u32 flags;
|
||||
|
||||
+ if (WARN_ON_ONCE(attach->pages || attach->npages || attach->sgt))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!attach->userptr || !attach->len || attach->len > MAX_RW_COUNT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
start = (unsigned long)attach->userptr;
|
||||
- end = PAGE_ALIGN(start + attach->len);
|
||||
- npages = PHYS_PFN(end - (start & PAGE_MASK));
|
||||
- array_size = npages * sizeof(struct page *);
|
||||
+ if (check_add_overflow(start, (unsigned long)attach->len - 1, &last))
|
||||
+ return -EOVERFLOW;
|
||||
+
|
||||
+ npages = (((last & PAGE_MASK) - (start & PAGE_MASK)) >> PAGE_SHIFT) + 1;
|
||||
+ if (!npages || npages > INT_MAX)
|
||||
+ return -E2BIG;
|
||||
|
||||
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt)
|
||||
return -ENOMEM;
|
||||
|
||||
- WARN_ON_ONCE(attach->npages);
|
||||
-
|
||||
- pages = kvzalloc(array_size, GFP_KERNEL);
|
||||
+ pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
|
||||
if (!pages)
|
||||
goto free_sgt;
|
||||
|
||||
- mmap_read_lock(current->mm);
|
||||
- vma = vma_lookup(current->mm, start);
|
||||
- if (unlikely(!vma)) {
|
||||
- ret = -EFAULT;
|
||||
- goto error_up_read;
|
||||
- }
|
||||
- mmap_read_unlock(current->mm);
|
||||
-
|
||||
- flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM;
|
||||
+ flags = FOLL_WRITE | FOLL_LONGTERM;
|
||||
nr = pin_user_pages_fast(start & PAGE_MASK, npages,
|
||||
flags, pages);
|
||||
- if (nr < npages)
|
||||
- goto error;
|
||||
-
|
||||
- attach->pages = pages;
|
||||
- attach->npages = npages;
|
||||
+ if (nr < 0) {
|
||||
+ ret = nr;
|
||||
+ goto unpin_pages;
|
||||
+ }
|
||||
+ if ((size_t)nr != npages) {
|
||||
+ ret = -EFAULT;
|
||||
+ goto unpin_pages;
|
||||
+ }
|
||||
|
||||
ret = sg_alloc_table_from_pages(sgt, pages, npages,
|
||||
start & ~PAGE_MASK, attach->len,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
- goto error;
|
||||
-
|
||||
+ goto unpin_pages;
|
||||
+
|
||||
+ attach->pages = pages;
|
||||
+ attach->npages = npages;
|
||||
attach->sgt = sgt;
|
||||
|
||||
return 0;
|
||||
|
||||
-error_up_read:
|
||||
- mmap_read_unlock(current->mm);
|
||||
-error:
|
||||
- if (nr)
|
||||
+unpin_pages:
|
||||
+ if (nr > 0)
|
||||
unpin_user_pages(pages, nr);
|
||||
-
|
||||
kvfree(pages);
|
||||
free_sgt:
|
||||
kfree(sgt);
|
||||
@@ -126,6 +127,8 @@
|
||||
|
||||
sg_free_table(attach->sgt);
|
||||
kfree(attach->sgt);
|
||||
+ attach->pages = NULL;
|
||||
+ attach->npages = 0;
|
||||
attach->sgt = NULL;
|
||||
}
|
||||
|
||||
@@ -580,6 +583,11 @@
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ if (!buf->len || buf->len > MAX_RW_COUNT) {
|
||||
+ dev_err(dev, "Invalid userptr buffer length\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
@@ -782,6 +790,7 @@
|
||||
dma_buf_detach(kbuf->dbuf, kbuf->db_attach);
|
||||
|
||||
attach_fail:
|
||||
+ kbuf->db_attach = NULL;
|
||||
list_del(&kbuf->list);
|
||||
if (!kbuf->userptr)
|
||||
kfree(kbuf);
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Junjie Cao <junjie.cao@intel.com>
|
||||
Date: Tue, 08 Sep 2026 18:57:17 +0800
|
||||
Subject: [PATCH] media: i2c: cvs: Get the wake IRQ without claiming the GPIO
|
||||
Message-ID: <20260908105717.496232-1-junjie.cao@intel.com>
|
||||
Link: https://patchwork.linuxtv.org/project/linux-media/patch/20260908105717.496232-1-junjie.cao@intel.com/
|
||||
|
||||
The wake line is only used as an IRQ source, yet the driver requests
|
||||
it with devm_gpiod_get() before the I2C handshake. Where ipu-bridge
|
||||
does not expose the CSI endpoints, CSI init returns -EPROBE_DEFER and
|
||||
every retry claims the line again for the length of the handshake.
|
||||
|
||||
On the Dell XPS 14 DA14260 (Panther Lake) the four CS35L57 amplifiers
|
||||
read their speaker ID from one GpioIo (DSDT decoded in the second
|
||||
link):
|
||||
|
||||
GpioIo (Shared, PullNone, 0, 0, IoRestrictionInputOnly,
|
||||
"\_SB.GPI1", 0, ResourceConsumer,,) {20}
|
||||
|
||||
A request that lands while another consumer holds the line fails, and
|
||||
cs35l56 does not retry:
|
||||
|
||||
cs35l56 sdw:0:2:01fa:3557:01:2: error -EBUSY: Failed to get spk-id-gpios
|
||||
|
||||
All four fail on Fedora 7.1.13, the first Fedora 7.1 kernel with the
|
||||
driver enabled; the same board on 7.1.12 without it creates the card.
|
||||
The second link shows the same failure on openSUSE 7.2.2, whose
|
||||
config also enables the driver.
|
||||
|
||||
The INTC10E1 _CRS of this machine has not been decoded. The vendor
|
||||
driver in intel/vision-drivers requests req, resp and rst the same
|
||||
way but maps wake to an IRQ with acpi_dev_gpio_irq_get_by() without
|
||||
requesting it, and on another DA14260 (board 0VRKYR, BIOS 1.8.2) a
|
||||
build of it is bound while the amplifiers probe. The wake entry is
|
||||
the line that differs.
|
||||
|
||||
Take the IRQ from the GpioInt entry the same way, as the I2C core
|
||||
does for client->irq; this also applies the trigger type from _CRS.
|
||||
The driver binds as a platform device too, hence the explicit lookup.
|
||||
|
||||
Fixes: 8e2b43d2c10b ("media: i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS)")
|
||||
Cc: stable@vger.kernel.org
|
||||
Link: https://bugzilla.redhat.com/show_bug.cgi?id=2529031
|
||||
Link: https://github.com/thesofproject/sof/issues/11152
|
||||
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
|
||||
---
|
||||
drivers/media/i2c/cvs/core.c | 16 ++++++----------
|
||||
1 file changed, 6 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c
|
||||
index d4a3b9c3bab1e..8d857bbd8ab51 100644
|
||||
--- a/drivers/media/i2c/cvs/core.c
|
||||
+++ b/drivers/media/i2c/cvs/core.c
|
||||
@@ -725,8 +725,6 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
if (ctx->res == ICVS_FULLCAP) {
|
||||
- struct gpio_desc *wake;
|
||||
-
|
||||
ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ctx->rst)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(ctx->rst),
|
||||
@@ -734,14 +732,12 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
|
||||
goto err_put_ipu;
|
||||
}
|
||||
|
||||
- wake = devm_gpiod_get(dev, "wake", GPIOD_IN);
|
||||
- if (IS_ERR(wake)) {
|
||||
- ret = dev_err_probe(dev, PTR_ERR(wake),
|
||||
- "failed to get wake GPIO\n");
|
||||
- goto err_put_ipu;
|
||||
- }
|
||||
-
|
||||
- ctx->irq = gpiod_to_irq(wake);
|
||||
+ /*
|
||||
+ * Do not request the line: another device's _CRS may list
|
||||
+ * the same pin, and its driver would then fail with -EBUSY.
|
||||
+ */
|
||||
+ ctx->irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev),
|
||||
+ "wake", 0);
|
||||
if (ctx->irq < 0) {
|
||||
ret = dev_err_probe(dev, ctx->irq,
|
||||
"failed to get wake IRQ\n");
|
||||
@@ -0,0 +1,35 @@
|
||||
From d358e3b1bcf9f26638ae326e979661d0f2c740e2 Mon Sep 17 00:00:00 2001
|
||||
From: Spencer Bull <spencer@omarchy.org>
|
||||
Date: Tue, 29 Sep 2026 00:19:50 -0500
|
||||
Subject: [PATCH] IpuPacAdaptor: honor the reduced YUV color range
|
||||
|
||||
applyMediaFormat() always hands the PAL media_format_legacy, so the ISP
|
||||
emits full-range (0-255) YUV no matter what the application asks for
|
||||
through setYuvColorRangeMode() (icamerasrc color-range=reduced).
|
||||
Consumers of an NV12 webcam assume limited range (16-235), so full-range
|
||||
frames are shown with crushed shadows and clipped highlights.
|
||||
|
||||
Select media_format_legacy_narrow when the reduced range is requested.
|
||||
The full-range default is unchanged.
|
||||
---
|
||||
src/core/IpuPacAdaptor.cpp | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/core/IpuPacAdaptor.cpp b/src/core/IpuPacAdaptor.cpp
|
||||
index a8aad3f..cbcd8eb 100644
|
||||
--- a/src/core/IpuPacAdaptor.cpp
|
||||
+++ b/src/core/IpuPacAdaptor.cpp
|
||||
@@ -285,7 +285,12 @@ void IpuPacAdaptor::applyMediaFormat(const AiqResult* aiqResult,
|
||||
auto cameraContext = CameraContext::getInstance(mCameraId);
|
||||
auto dataContext = cameraContext->getDataContextBySeq(sequence);
|
||||
|
||||
- *mediaFormat = media_format_legacy;
|
||||
+ // Honor the requested YUV range: reduced range needs the narrow variant,
|
||||
+ // otherwise the ISP always emits full-range data.
|
||||
+ *mediaFormat =
|
||||
+ (dataContext->mAiqParams.yuvColorRangeMode == CAMERA_REDUCED_MODE_YUV_COLOR_RANGE)
|
||||
+ ? media_format_legacy_narrow
|
||||
+ : media_format_legacy;
|
||||
if (dataContext->mAiqParams.tonemapMode == TONEMAP_MODE_GAMMA_VALUE) {
|
||||
const float gamma = dataContext->mAiqParams.tonemapGamma;
|
||||
if (std::fabs(gamma - 1.0F) < std::numeric_limits<float>::epsilon()) {
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
From 2d2f5f56e4f4033df3bd1e51fdc519c59ddb6f09 Mon Sep 17 00:00:00 2001
|
||||
From: Spencer Bull <spencer@omarchy.org>
|
||||
Date: Tue, 29 Sep 2026 00:19:50 -0500
|
||||
Subject: [PATCH] Add an fps-range property for auto exposure
|
||||
|
||||
icamerasrc passes the caps frame rate to the HAL with setFrameRate(), and
|
||||
the HAL turns that into an AE frame-time range of exactly [fps, fps]. At
|
||||
30 fps auto exposure can never expose longer than 33 ms, so in dim rooms
|
||||
all extra brightness comes from analog plus digital gain, which is where
|
||||
the grain comes from.
|
||||
|
||||
The HAL already supports an AE frame rate range (setFpsRange); expose it
|
||||
as "fps-range" (e.g. "15~30") so AE may stretch the frame time in low
|
||||
light and return to the full rate when there is enough light.
|
||||
---
|
||||
src/gstcamerasrc.cpp | 25 +++++++++++++++++++++++++
|
||||
src/gstcamerasrc.h | 2 ++
|
||||
2 files changed, 27 insertions(+)
|
||||
|
||||
diff --git a/src/gstcamerasrc.cpp b/src/gstcamerasrc.cpp
|
||||
index 61683e5..f392fc1 100644
|
||||
--- a/src/gstcamerasrc.cpp
|
||||
+++ b/src/gstcamerasrc.cpp
|
||||
@@ -120,6 +120,7 @@ enum
|
||||
PROP_AF_REGION,
|
||||
PROP_EXPOSURE_TIME_RANGE,
|
||||
PROP_GAIN_RANGE,
|
||||
+ PROP_FPS_RANGE,
|
||||
PROP_CONVERGE_SPEED,
|
||||
PROP_CONVERGE_SPEED_MODE,
|
||||
/* Backlight Settings*/
|
||||
@@ -902,6 +903,9 @@ gst_camerasrc_finalize (Gstcamerasrc *camerasrc)
|
||||
delete camerasrc->isp_control_tags;
|
||||
camerasrc->isp_control_tags = NULL;
|
||||
|
||||
+ g_free(camerasrc->man_ctl.fps_range);
|
||||
+ camerasrc->man_ctl.fps_range = NULL;
|
||||
+
|
||||
for (int i = 0; i < GST_CAMERASRC_MAX_STREAM_NUM; i++) {
|
||||
camerasrc->streams[i].activated = FALSE;
|
||||
}
|
||||
@@ -1137,6 +1141,11 @@ gst_camerasrc_class_init (GstcamerasrcClass * klass)
|
||||
g_param_spec_string("gain-range","AE gain range","AE gain range",
|
||||
DEFAULT_PROP_GAIN_RANGE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
+ g_object_class_install_property(gobject_class,PROP_FPS_RANGE,
|
||||
+ g_param_spec_string("fps-range","AE frame rate range",
|
||||
+ "Frame rate range AE may use, e.g. 15~30: lets auto exposure lengthen frames in low light",
|
||||
+ DEFAULT_PROP_FPS_RANGE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
+
|
||||
g_object_class_install_property (gobject_class, PROP_CUSTOM_AIC_PARAMETER,
|
||||
g_param_spec_string("custom-aic-param","Custom Aic Parameter","Custom Aic Parameter",
|
||||
DEFAULT_PROP_CUSTOM_AIC_PARAMETER, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
@@ -1884,6 +1893,7 @@ gst_camerasrc_set_property (GObject * object, guint prop_id,
|
||||
camera_range_t cct_range;
|
||||
camera_range_t exposure_time_range;
|
||||
camera_range_t gain_range;
|
||||
+ camera_range_t fps_range;
|
||||
camera_coordinate_t white_point;
|
||||
unsigned int custom_aic_param_len = 0;
|
||||
camera_crop_region_t crop_region;
|
||||
@@ -2150,6 +2160,18 @@ gst_camerasrc_set_property (GObject * object, guint prop_id,
|
||||
if (ret == 0)
|
||||
src->param->setSensitivityGainRange(gain_range);
|
||||
break;
|
||||
+ case PROP_FPS_RANGE:
|
||||
+ g_free(src->man_ctl.fps_range);
|
||||
+ src->man_ctl.fps_range = g_strdup(g_value_get_string(value));
|
||||
+ // NULL is the default; the HAL has no way to drop a range once set.
|
||||
+ if (src->man_ctl.fps_range == NULL)
|
||||
+ break;
|
||||
+ ret = gst_camerasrc_parse_range(src->man_ctl.fps_range,
|
||||
+ fps_range.min,
|
||||
+ fps_range.max);
|
||||
+ if (ret == 0)
|
||||
+ src->param->setFpsRange(fps_range);
|
||||
+ break;
|
||||
case PROP_AWB_COLOR_TRANSFORM:
|
||||
ret = gst_camerasrc_parse_string_to_matrix(g_value_get_string (value),
|
||||
(float**)(transform.color_transform), 3, 3);
|
||||
@@ -2380,6 +2402,9 @@ gst_camerasrc_get_property (GObject * object, guint prop_id,
|
||||
case PROP_GAIN_RANGE:
|
||||
g_value_set_string (value, src->man_ctl.gain_range);
|
||||
break;
|
||||
+ case PROP_FPS_RANGE:
|
||||
+ g_value_set_string (value, src->man_ctl.fps_range);
|
||||
+ break;
|
||||
case PROP_AWB_COLOR_TRANSFORM:
|
||||
g_value_set_string(value, src->man_ctl.color_transform);
|
||||
break;
|
||||
diff --git a/src/gstcamerasrc.h b/src/gstcamerasrc.h
|
||||
index 508a405..ec8a64f 100644
|
||||
--- a/src/gstcamerasrc.h
|
||||
+++ b/src/gstcamerasrc.h
|
||||
@@ -143,6 +143,7 @@ using namespace std;
|
||||
#define DEFAULT_PROP_AF_REGION NULL
|
||||
#define DEFAULT_PROP_EXPOSURE_TIME_RANGE NULL
|
||||
#define DEFAULT_PROP_GAIN_RANGE NULL
|
||||
+#define DEFAULT_PROP_FPS_RANGE NULL
|
||||
#define DEFAULT_PROP_CCT_RANGE NULL
|
||||
#define DEFAULT_PROP_COLOR_TRANSFORM NULL
|
||||
#define DEFAULT_PROP_CUSTOM_AIC_PARAMETER NULL
|
||||
@@ -407,6 +408,7 @@ struct _Gst3AManualControl
|
||||
int converge_speed_mode;
|
||||
gchar *exp_time_range;
|
||||
gchar *gain_range;
|
||||
+ gchar *fps_range;
|
||||
/* Backlight Settings*/
|
||||
int blc_area_mode;
|
||||
guint wdr_level;
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
From ee34eedf620ff0b697d66eb70e06b91062ab7e15 Mon Sep 17 00:00:00 2001
|
||||
From: Spencer Bull <spencer@omarchy.org>
|
||||
Date: Tue, 29 Sep 2026 00:24:27 -0500
|
||||
Subject: [PATCH] Scale AIQ analog gain codes to the ov08x40 driver's units
|
||||
|
||||
The OV08X40 tuning (OV08X40_KAFE799_PTL.aiqb) makes AIQ produce the raw
|
||||
0x3508 register value, where 1x = 256. The in-tree ov08x40 driver takes
|
||||
V4L2_CID_ANALOGUE_GAIN in units of half that (1x = 128, range 128..1984,
|
||||
written to the register as val << 1). The HAL passed the code through, so
|
||||
every analog gain below 7.75x ran at twice what AE and the ISP noise model
|
||||
believed, and every request above collapsed to the 15.5x clamp. Measured
|
||||
on a Dell XPS 16 DA16260: a 7.75x request (code 1984) reads back as
|
||||
analogue_gain = 1984, i.e. 15.5x on the sensor.
|
||||
|
||||
Add an "analogGainCodeShift" sensor option that right-shifts the code
|
||||
before V4L2_CID_ANALOGUE_GAIN, and set it to 1 for the ov08x40 on both
|
||||
IPU7.5 and IPU7: the Lunar Lake tuning (OV08X40_BBG802N3_LNL.aiqb)
|
||||
carries the same gain table. Sensors without the option are unchanged.
|
||||
---
|
||||
config/linux/ipu75xa/sensors/ov08x40-uf.json | 3 +++
|
||||
config/linux/ipu7x/sensors/ov08x40-uf.json | 3 +++
|
||||
src/core/SensorHwCtrl.cpp | 9 ++++++---
|
||||
src/platformdata/CameraSensorsParser.cpp | 3 +++
|
||||
src/platformdata/PlatformData.cpp | 4 ++++
|
||||
src/platformdata/PlatformData.h | 11 +++++++++++
|
||||
6 files changed, 30 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/config/linux/ipu75xa/sensors/ov08x40-uf.json b/config/linux/ipu75xa/sensors/ov08x40-uf.json
|
||||
index 483bf1d..dbe2363 100644
|
||||
--- a/config/linux/ipu75xa/sensors/ov08x40-uf.json
|
||||
+++ b/config/linux/ipu75xa/sensors/ov08x40-uf.json
|
||||
@@ -194,6 +194,9 @@
|
||||
"initialSkipFrame": 0,
|
||||
"exposureLag": 2,
|
||||
"gainLag": 2,
|
||||
+ // AIQ codes are the 0x3508 register value (1x = 256); the in-tree
|
||||
+ // ov08x40 driver takes V4L2_CID_ANALOGUE_GAIN as register / 2 (1x = 128).
|
||||
+ "analogGainCodeShift": 1,
|
||||
"digitalGainLag": 0,
|
||||
"ltmGainLag": 1,
|
||||
// There are 2 yuv color range mode, like full, reduced.
|
||||
diff --git a/config/linux/ipu7x/sensors/ov08x40-uf.json b/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
index 472b0da..00f78d7 100644
|
||||
--- a/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
+++ b/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
@@ -194,6 +194,9 @@
|
||||
"initialSkipFrame": 0,
|
||||
"exposureLag": 2,
|
||||
"gainLag": 2,
|
||||
+ // AIQ codes are the 0x3508 register value (1x = 256); the in-tree
|
||||
+ // ov08x40 driver takes V4L2_CID_ANALOGUE_GAIN as register / 2 (1x = 128).
|
||||
+ "analogGainCodeShift": 1,
|
||||
"digitalGainLag": 0,
|
||||
"ltmGainLag": 1,
|
||||
// There are 2 yuv color range mode, like full, reduced.
|
||||
diff --git a/src/core/SensorHwCtrl.cpp b/src/core/SensorHwCtrl.cpp
|
||||
index 799e02e..a7832a2 100644
|
||||
--- a/src/core/SensorHwCtrl.cpp
|
||||
+++ b/src/core/SensorHwCtrl.cpp
|
||||
@@ -136,9 +136,12 @@ int SensorHwCtrl::setAnalogGains(const vector<int>& analogGains) {
|
||||
CheckAndLogError(mPixelArraySubdev == nullptr, NO_INIT, "pixel array sub device is not set");
|
||||
CheckAndLogError(analogGains.empty(), BAD_VALUE, "No analog gain data!");
|
||||
|
||||
- LOG2("%s analogGain=%d", __func__, analogGains[0]);
|
||||
- int status = mPixelArraySubdev->SetControl(V4L2_CID_ANALOGUE_GAIN, analogGains[0]);
|
||||
- CheckAndLogError((status != 0), status, "failed to set analog gain %d.", analogGains[0]);
|
||||
+ // AIQ produces the sensor's register code; some drivers expose the gain in
|
||||
+ // coarser units (e.g. ov08x40 takes code / 2), so scale it down to match.
|
||||
+ const int analogGain = analogGains[0] >> PlatformData::getAnalogGainCodeShift(mCameraId);
|
||||
+ LOG2("%s analogGain=%d", __func__, analogGain);
|
||||
+ int status = mPixelArraySubdev->SetControl(V4L2_CID_ANALOGUE_GAIN, analogGain);
|
||||
+ CheckAndLogError((status != 0), status, "failed to set analog gain %d.", analogGain);
|
||||
|
||||
return OK;
|
||||
}
|
||||
diff --git a/src/platformdata/CameraSensorsParser.cpp b/src/platformdata/CameraSensorsParser.cpp
|
||||
index c5a4633..04ac4ef 100644
|
||||
--- a/src/platformdata/CameraSensorsParser.cpp
|
||||
+++ b/src/platformdata/CameraSensorsParser.cpp
|
||||
@@ -804,6 +804,9 @@ void CameraSensorsParser::parseSensorSection(const Json::Value& node) {
|
||||
if (node.isMember("gainLag")) {
|
||||
mCurCam->mAnalogGainLag = node["gainLag"].asInt();
|
||||
}
|
||||
+ if (node.isMember("analogGainCodeShift")) {
|
||||
+ mCurCam->mAnalogGainCodeShift = node["analogGainCodeShift"].asInt();
|
||||
+ }
|
||||
if (node.isMember("digitalGainLag")) {
|
||||
mCurCam->mDigitalGainLag = node["digitalGainLag"].asInt();
|
||||
}
|
||||
diff --git a/src/platformdata/PlatformData.cpp b/src/platformdata/PlatformData.cpp
|
||||
index 7e25e61..d4b7cd3 100644
|
||||
--- a/src/platformdata/PlatformData.cpp
|
||||
+++ b/src/platformdata/PlatformData.cpp
|
||||
@@ -379,6 +379,10 @@ int PlatformData::getDigitalGainLag(int cameraId) {
|
||||
return getInstance()->mStaticCfg.mCameras[cameraId].mDigitalGainLag;
|
||||
}
|
||||
|
||||
+int PlatformData::getAnalogGainCodeShift(int cameraId) {
|
||||
+ return getInstance()->mStaticCfg.mCameras[cameraId].mAnalogGainCodeShift;
|
||||
+}
|
||||
+
|
||||
int PlatformData::getExposureLag(int cameraId) {
|
||||
return getInstance()->mStaticCfg.mCameras[cameraId].mExposureLag;
|
||||
}
|
||||
diff --git a/src/platformdata/PlatformData.h b/src/platformdata/PlatformData.h
|
||||
index 8f1f84d..bfc8e6e 100644
|
||||
--- a/src/platformdata/PlatformData.h
|
||||
+++ b/src/platformdata/PlatformData.h
|
||||
@@ -211,6 +211,7 @@ class PlatformData {
|
||||
mDigitalGainLag(-1),
|
||||
mExposureLag(MAX_BUFFER_COUNT),
|
||||
mAnalogGainLag(0),
|
||||
+ mAnalogGainCodeShift(0),
|
||||
mMaxSensorDigitalGain(0),
|
||||
mSensorDgType(SensorDgType::SENSOR_DG_TYPE_NONE),
|
||||
mISysFourcc(V4L2_PIX_FMT_SGRBG8),
|
||||
@@ -296,6 +297,7 @@ class PlatformData {
|
||||
int mDigitalGainLag;
|
||||
int mExposureLag;
|
||||
int mAnalogGainLag;
|
||||
+ int mAnalogGainCodeShift;
|
||||
int mMaxSensorDigitalGain;
|
||||
SensorDgType mSensorDgType;
|
||||
std::string mCustomAicLibraryName;
|
||||
@@ -843,6 +845,15 @@ class PlatformData {
|
||||
* \return the value of digital gain lag
|
||||
*/
|
||||
static int getDigitalGainLag(int cameraId);
|
||||
+
|
||||
+ /**
|
||||
+ * Get the right shift from AIQ analog gain codes to the sensor driver's
|
||||
+ * V4L2_CID_ANALOGUE_GAIN units
|
||||
+ *
|
||||
+ * \param cameraId: [0, MAX_CAMERA_NUMBER - 1]
|
||||
+ * \return the shift, 0 when the driver takes AIQ codes as they are
|
||||
+ */
|
||||
+ static int getAnalogGainCodeShift(int cameraId);
|
||||
/**
|
||||
* Get sensor's exposure lag
|
||||
*
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
From 5ba99a9f2cda77035dd99cacf8d98d3a0293768f Mon Sep 17 00:00:00 2001
|
||||
From: Spencer Bull <spencer@omarchy.org>
|
||||
Date: Tue, 29 Sep 2026 08:27:30 -0500
|
||||
Subject: [PATCH] PipeLine: fall back to the default ISP tuning when the mode
|
||||
is missing
|
||||
|
||||
The static graph tags its nodes with an ISP tuning mode, and PipeLine
|
||||
asks CCA to load the matching ISP container from the tuning file. When
|
||||
the file has no container for that mode, updateTuning() fails and the
|
||||
HAL carries on with the graph's mode anyway. The generic AIC then looks
|
||||
up tunings for a mode it does not have, finds none, and every ISP kernel
|
||||
it tunes (noise reduction, TNR, sharpening, ...) runs on library
|
||||
defaults.
|
||||
|
||||
The OV08X40 PTL tuning (OV08X40_KAFE799_PTL.aiqb) ships DFLT, LMOD 1 and
|
||||
LMOD 2, while its graph (OV08X40_KAFE799.IPU75XA.bin) tags the 1080p
|
||||
video pipe with mode 4: gaic_find_all_tunings(60001, 4) returns nothing
|
||||
and temporal noise reduction never engages. Measured on a Dell XPS 16
|
||||
DA16260 at ISO 402: frame-to-frame noise on a flat wall drops from 9.2
|
||||
to 1.9 once the default tuning is applied.
|
||||
|
||||
On failure, load the default (DFLT) ISP tuning and configure the CB
|
||||
stages with mode 0, which is how the generic AIC indexes it. Tunings
|
||||
that carry the requested mode are unchanged.
|
||||
---
|
||||
src/core/processingUnit/PipeLine.cpp | 23 +++++++++++++++++++++--
|
||||
src/core/processingUnit/PipeLine.h | 3 +++
|
||||
2 files changed, 24 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/core/processingUnit/PipeLine.cpp b/src/core/processingUnit/PipeLine.cpp
|
||||
index afd82f7..eebff11 100644
|
||||
--- a/src/core/processingUnit/PipeLine.cpp
|
||||
+++ b/src/core/processingUnit/PipeLine.cpp
|
||||
@@ -53,6 +53,7 @@ void PipeLine::deinit() {
|
||||
}
|
||||
|
||||
void PipeLine::updateIspTuningMode(TuningMode tuningMode) {
|
||||
+ mUseDefaultIspTuning = false;
|
||||
if (!PlatformData::supportUpdateTuning(mCameraId)) {
|
||||
return;
|
||||
}
|
||||
@@ -74,8 +75,22 @@ void PipeLine::updateIspTuningMode(TuningMode tuningMode) {
|
||||
CheckAndLogError(!intelCca, VOID_VALUE, "Failed to get IntelCca");
|
||||
|
||||
ia_err iaErr = intelCca->updateTuning(cca::CCA_LARD_ISP, lardParam, tmpNvm, mStreamId);
|
||||
- CheckAndLogError(iaErr != ia_err_none, VOID_VALUE, "Failed to update tuning %u",
|
||||
- ispTuningMode);
|
||||
+ if (iaErr != ia_err_none) {
|
||||
+ // The graph asks for an ISP tuning mode the tuning file does not carry (the
|
||||
+ // OV08X40 PTL tuning ships DFLT, LMOD 1 and LMOD 2 while its graph asks for 4).
|
||||
+ // Without a fallback the generic AIC finds no tunings for the mode, and every
|
||||
+ // ISP kernel it tunes (noise reduction, TNR, sharpening, ...) runs on library
|
||||
+ // defaults. Load the default ISP tuning and look it up as mode 0 instead.
|
||||
+ LOGW("No ISP tuning for mode %u, falling back to the default tuning", ispTuningMode);
|
||||
+ lardParam.isp_mode_index = IA_MKN_CHTOUL('D', 'F', 'L', 'T');
|
||||
+ iaErr = intelCca->updateTuning(cca::CCA_LARD_ISP, lardParam, tmpNvm, mStreamId);
|
||||
+ if (iaErr != ia_err_none) {
|
||||
+ // The default ISP tuning loaded at init is still in place.
|
||||
+ LOGW("Failed to reload the default ISP tuning (%d)", iaErr);
|
||||
+ }
|
||||
+ mUseDefaultIspTuning = true;
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
LOG1("Update isp tuning mode %u, streamId %d", ispTuningMode, mStreamId);
|
||||
}
|
||||
@@ -292,6 +307,10 @@ status_t PipeLine::configurePipeStages() {
|
||||
CheckAndLogError(!pNode, UNKNOWN_ERROR, "No PSys node for psys contextId %d",
|
||||
unit.psysContextId);
|
||||
|
||||
+ if (mUseDefaultIspTuning) {
|
||||
+ unit.node->nodeKernels.operationMode = 0;
|
||||
+ }
|
||||
+
|
||||
std::unordered_map<uint8_t, TerminalConfig> terminalConfig;
|
||||
ret = unit.ipuStage->configure(unit.node->nodeKernels,
|
||||
const_cast<const GraphLink**>(links), numLinks,
|
||||
diff --git a/src/core/processingUnit/PipeLine.h b/src/core/processingUnit/PipeLine.h
|
||||
index 0f881f0..a72756d 100644
|
||||
--- a/src/core/processingUnit/PipeLine.h
|
||||
+++ b/src/core/processingUnit/PipeLine.h
|
||||
@@ -134,6 +134,9 @@ class PipeLine {
|
||||
std::map<uuid, TerminalInfo> mTerminalsDesc;
|
||||
|
||||
TuningMode mTuningMode;
|
||||
+ // The tuning has no ISP container for the graph's tuning mode, so the default
|
||||
+ // (DFLT) ISP tuning is loaded and looked up as mode 0.
|
||||
+ bool mUseDefaultIspTuning = false;
|
||||
|
||||
PSysDevice* mPSysDevice;
|
||||
PSysGraph mPSysGraph;
|
||||
@@ -0,0 +1,28 @@
|
||||
From: Kolbas <pkolbas@pm.me>
|
||||
Subject: [PATCH] ipu7x: set Intel CVS pad formats for ov08x40
|
||||
|
||||
Lunar Lake analog of Omarchy's 0006 (ipu75xa) and of the ipu8 config part of
|
||||
upstream intel/ipu7-camera-hal f167239b3ecf. On Linux 7.2 the "Intel CVS"
|
||||
bridge entity sits between ov08x40 and the IPU7 CSI2 receiver; its pad formats
|
||||
must be set or link validation fails at stream-on. Links stay sensor -> CSI2:
|
||||
MediaControl (0005) rewrites them through the bridge at runtime.
|
||||
---
|
||||
diff --git i/config/linux/ipu7x/sensors/ov08x40-uf.json w/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
index 7123d45..472b0da 100644
|
||||
--- i/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
+++ w/config/linux/ipu7x/sensors/ov08x40-uf.json
|
||||
@@ -31,6 +31,14 @@
|
||||
"name": "ov08x40 $I2CBUS", "pad": 0, "width": 3856, "height": 2176,
|
||||
"format": "V4L2_MBUS_FMT_SGRBG10_1X10"
|
||||
},
|
||||
+ {
|
||||
+ "name": "Intel CVS", "pad": 0, "width": 3856, "height": 2176,
|
||||
+ "format": "V4L2_MBUS_FMT_SGRBG10_1X10"
|
||||
+ },
|
||||
+ {
|
||||
+ "name": "Intel CVS", "pad": 1, "width": 3856, "height": 2176,
|
||||
+ "format": "V4L2_MBUS_FMT_SGRBG10_1X10"
|
||||
+ },
|
||||
{
|
||||
"name": "Intel IPU7 CSI2 $CSI_PORT", "pad": 0, "width": 3856, "height": 2176,
|
||||
"format": "V4L2_MBUS_FMT_SGRBG10_1X10"
|
||||
@@ -0,0 +1 @@
|
||||
KERNEL=="ipu7-psys0", GROUP="root", MODE="0600", SYMLINK+="ipu-psys0"
|
||||
@@ -1 +0,0 @@
|
||||
KERNEL=="ipu7-psys0", MODE="0666", SYMLINK+="ipu-psys0"
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=intel-ipu7-camera
|
||||
pkgver=1.0.6
|
||||
pkgrel=2
|
||||
pkgrel=3
|
||||
pkgdesc="Intel IPU7 MIPI camera stack for Hurrican/Performance (OV08X40 + hardware ISP)"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/TsaiGaggery/hurrican_omarchy_enabling"
|
||||
@@ -27,6 +27,7 @@ makedepends=(
|
||||
'libtool'
|
||||
'pkgconf'
|
||||
)
|
||||
checkdepends=('linux-omarchy-headers')
|
||||
install=intel-ipu7-camera.install
|
||||
options=('!strip')
|
||||
|
||||
@@ -37,22 +38,23 @@ _ipu7_camera_hal_commit="b1f6ebef12111fb5da0133b144d69dd9b001836c"
|
||||
_icamerasrc_commit="4fb31db76b618aae72184c59314b839dedb42689"
|
||||
|
||||
# Linux 7.2 moved the Intel CVS (Computer Vision Sensing) controller into the
|
||||
# IPU media graph: ipu-bridge now places it between the sensor and the IPU
|
||||
# CSI2 receiver as an "Intel CVS" V4L2 bridge sub-device, provided by the
|
||||
# in-tree drivers/media/i2c/cvs driver. The legacy vision-drivers misc driver
|
||||
# has no such sub-device, so on 7.2 the sensor never joins the graph and the
|
||||
# HAL finds no camera. We ship the in-tree driver as a DKMS module
|
||||
# for 7.2+ kernels (Arch-derived kernel configs cannot enable it: the option
|
||||
# is hidden behind MEDIA_HIDE_ANCILLARY_SUBDRV) and keep vision-drivers for
|
||||
# older kernels. Both dkms.conf files carry matching BUILD_EXCLUSIVE_KERNEL.
|
||||
# IPU media graph, so the sensor only appears once drivers/media/i2c/cvs is
|
||||
# loaded. linux-omarchy builds that driver in-tree, signed and carrying the
|
||||
# XPS 14 speaker-ID wake-IRQ fix, and camera-init loads it. Kernels before 7.2
|
||||
# use the legacy misc driver from vision-drivers instead.
|
||||
#
|
||||
# 0007 is the upstream fix for the audio regression on the Dell XPS 14
|
||||
# DA14260: the driver requested its "wake" GPIO, which shares a pin with the
|
||||
# CS35L57 amplifiers' speaker-ID GpioIo, so every cs35l56 probe failed with
|
||||
# -EBUSY and the sound card never appeared. Drop it once the stable tag
|
||||
# fetched below contains the fix.
|
||||
_intel_cvs_kernel_tag="v7.2.5"
|
||||
_intel_cvs_url="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/media/i2c/cvs"
|
||||
# 0008-0011 are image quality fixes for the XPS 14 / 16 camera:
|
||||
# - 0011: the graph asks for ISP tuning mode 4, which the OV08X40 tuning does
|
||||
# not carry, so no ISP tuning at all (noise reduction, TNR, sharpening, ...)
|
||||
# reached the hardware. Fall back to the tuning's default ISP container.
|
||||
# - 0010: the tuning makes AIQ emit the raw analog gain register code, but
|
||||
# the in-tree ov08x40 driver takes half of it, so the sensor ran at twice
|
||||
# the gain AE and the ISP noise model assumed.
|
||||
# - 0008: the HAL ignored the requested YUV range and always produced
|
||||
# full-range frames that consumers decode as limited range.
|
||||
# - 0009: icamerasrc pinned auto exposure to exactly 1/30 s; its new
|
||||
# fps-range property lets AE lengthen frames in dim light instead of
|
||||
# adding gain (see v4l2-relayd-ipu7.conf).
|
||||
|
||||
source=(
|
||||
"ipu7-drivers::git+https://github.com/intel/ipu7-drivers.git#commit=${_ipu7_drivers_commit}"
|
||||
@@ -60,18 +62,19 @@ source=(
|
||||
"ipu7-camera-bins::git+https://github.com/intel/ipu7-camera-bins.git#commit=${_ipu7_camera_bins_commit}"
|
||||
"ipu7-camera-hal::git+https://github.com/intel/ipu7-camera-hal.git#commit=${_ipu7_camera_hal_commit}"
|
||||
"icamerasrc::git+https://github.com/intel/icamerasrc.git#commit=${_icamerasrc_commit}"
|
||||
"intel-cvs-core.c::${_intel_cvs_url}/core.c?h=${_intel_cvs_kernel_tag}"
|
||||
"intel-cvs-v4l2.c::${_intel_cvs_url}/v4l2.c?h=${_intel_cvs_kernel_tag}"
|
||||
"intel-cvs-icvs.h::${_intel_cvs_url}/icvs.h?h=${_intel_cvs_kernel_tag}"
|
||||
"0004-ipu7-psys-register-device-bus.patch"
|
||||
"0005-ipu7-psys-harden-userptr-pinning.patch"
|
||||
"check-userptr-range.c"
|
||||
"0003-icvs-set-rgbcamera_pwrup_host-0-for-Panther-Lake.patch"
|
||||
"0005-camhal-MediaControl-route-through-Intel-CVS-bridge.patch"
|
||||
"0006-camhal-ipu75xa-ov08x40-Intel-CVS-formats.patch"
|
||||
"0007-media-i2c-cvs-get-the-wake-irq-without-claiming-the-gpio.patch"
|
||||
"0012-camhal-ipu7x-ov08x40-Intel-CVS-formats.patch"
|
||||
"0008-camhal-honor-the-reduced-YUV-color-range.patch"
|
||||
"0009-icamerasrc-add-an-fps-range-property-for-auto-exposure.patch"
|
||||
"0010-camhal-scale-analog-gain-codes-to-the-ov08x40-driver-units.patch"
|
||||
"0011-camhal-fall-back-to-the-default-ISP-tuning-when-the-mode-is-missing.patch"
|
||||
"dkms-ipu7-drivers.conf"
|
||||
"dkms-vision-drivers.conf"
|
||||
"dkms-intel-cvs.conf"
|
||||
"intel-cvs-Makefile"
|
||||
"camera-deps.conf"
|
||||
"v4l2loopback-modprobe.conf"
|
||||
"camera-init.service"
|
||||
@@ -79,27 +82,28 @@ source=(
|
||||
"hide-ipu7-v4l2.conf"
|
||||
"disable-libcamera.conf"
|
||||
"71-ipu7-hide-isys.rules"
|
||||
"90-ipu7-psys.rules"
|
||||
"70-ipu7-psys.rules"
|
||||
"camera-tmpfiles.conf"
|
||||
"ov08x40.yaml"
|
||||
"camera-sleep-hook"
|
||||
"v4l2-relayd-ipu7.conf"
|
||||
"v4l2-relayd-ipu7-override.conf"
|
||||
"camera-videosrc-init"
|
||||
)
|
||||
sha256sums=(
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'b4a90ad1815c02e4cadc1b5ad6c576ae0b150cb365c68d2c934cf964a995d91f'
|
||||
'e988014f54b1b5183e9ef43b602d73bbc2c991f19a8d503560a8cdba112b76c6'
|
||||
'6dcd5201fb78766a440038c8eba2cc8c3892064ba829065e2269c30dc1905983'
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'SKIP'
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
)
|
||||
|
||||
prepare() {
|
||||
cd "${srcdir}/ipu7-drivers"
|
||||
patch -Np1 -i "${srcdir}/0004-ipu7-psys-register-device-bus.patch"
|
||||
# --fuzz=0: a security patch that lands somewhere other than its anchor still
|
||||
# compiles, and the build is the only place that can refuse it.
|
||||
patch -Np1 --fuzz=0 -i "${srcdir}/0004-ipu7-psys-register-device-bus.patch"
|
||||
patch -Np1 --fuzz=0 -i "${srcdir}/0005-ipu7-psys-harden-userptr-pinning.patch"
|
||||
|
||||
cd "${srcdir}/vision-drivers"
|
||||
git config user.email "build@localhost"
|
||||
@@ -112,15 +116,13 @@ prepare() {
|
||||
cd "${srcdir}/ipu7-camera-hal"
|
||||
patch -Np1 -i "${srcdir}/0005-camhal-MediaControl-route-through-Intel-CVS-bridge.patch"
|
||||
patch -Np1 -i "${srcdir}/0006-camhal-ipu75xa-ov08x40-Intel-CVS-formats.patch"
|
||||
patch -Np1 -i "${srcdir}/0012-camhal-ipu7x-ov08x40-Intel-CVS-formats.patch"
|
||||
patch -Np1 -i "${srcdir}/0008-camhal-honor-the-reduced-YUV-color-range.patch"
|
||||
patch -Np1 -i "${srcdir}/0010-camhal-scale-analog-gain-codes-to-the-ov08x40-driver-units.patch"
|
||||
patch -Np1 -i "${srcdir}/0011-camhal-fall-back-to-the-default-ISP-tuning-when-the-mode-is-missing.patch"
|
||||
|
||||
# Intel CVS: take the wake IRQ without claiming the GPIO (shared with the
|
||||
# speaker-ID line on the XPS 14). makepkg symlinks plain downloads into
|
||||
# srcdir and patch refuses to touch a symlink, so patch a real copy. The
|
||||
# patch is against a/drivers/media/i2c/cvs/core.c, hence -p5.
|
||||
cp --remove-destination "$(readlink -f "${srcdir}/intel-cvs-core.c")" \
|
||||
"${srcdir}/intel-cvs-core.c"
|
||||
patch -p5 -F0 --no-backup-if-mismatch "${srcdir}/intel-cvs-core.c" \
|
||||
< "${srcdir}/0007-media-i2c-cvs-get-the-wake-irq-without-claiming-the-gpio.patch"
|
||||
cd "${srcdir}/icamerasrc"
|
||||
patch -Np1 -i "${srcdir}/0009-icamerasrc-add-an-fps-range-property-for-auto-exposure.patch"
|
||||
|
||||
# Stage proprietary libs/headers for build-time use
|
||||
local staging="${srcdir}/staging"
|
||||
@@ -157,7 +159,7 @@ build() {
|
||||
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||
-DBUILD_CAMHAL_ADAPTOR=ON \
|
||||
-DBUILD_CAMHAL_PLUGIN=ON \
|
||||
-DIPU_VERSIONS="ipu75xa" \
|
||||
-DIPU_VERSIONS="ipu7x;ipu75xa" \
|
||||
-DUSE_STATIC_GRAPH=ON \
|
||||
-DUSE_STATIC_GRAPH_AUTOGEN=ON \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
@@ -179,6 +181,54 @@ build() {
|
||||
make -j"$(nproc)"
|
||||
}
|
||||
|
||||
check() {
|
||||
local driver_tree="${srcdir}/ipu7-drivers"
|
||||
local psys_source="${srcdir}/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c"
|
||||
local check_tree="${srcdir}/ipu7-drivers-check"
|
||||
local kernel_build kernel_files grep_result result=0 attach_failure
|
||||
|
||||
[[ -f $psys_source && -r $psys_source ]] || return 1
|
||||
grep -q 'kvmalloc_array(npages, sizeof(\*pages)' "${psys_source}" || return 1
|
||||
grep -q 'attach->len > MAX_RW_COUNT' "${psys_source}" || return 1
|
||||
if grep 'FOLL_FORCE' "${psys_source}" >/dev/null; then
|
||||
return 1
|
||||
else
|
||||
grep_result=$?
|
||||
(( grep_result == 1 )) || return 1
|
||||
fi
|
||||
attach_failure=$(sed -n '/^attach_fail:$/,+1p' "$psys_source") || return 1
|
||||
[[ $attach_failure == $'attach_fail:\n\tkbuf->db_attach = NULL;' ]] || return 1
|
||||
|
||||
cc -std=c11 -O2 -Wall -Wextra -Werror \
|
||||
"${srcdir}/check-userptr-range.c" -o "${srcdir}/check-userptr-range" || return 1
|
||||
"${srcdir}/check-userptr-range" || return 1
|
||||
|
||||
kernel_files=$(pacman -Qql linux-omarchy-headers) || return 1
|
||||
kernel_build="$(sed -n \
|
||||
'\#/build/Makefile$# { s#/Makefile$##; s#^#/#; p; q; }' <<<"$kernel_files")" || return 1
|
||||
if [[ ! -f "${kernel_build}/Makefile" ]]; then
|
||||
printf 'Unable to locate the linux-omarchy-headers build tree\n' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
(
|
||||
cd "${srcdir}"
|
||||
"${kernel_build}/scripts/checkpatch.pl" --no-tree --strict --no-signoff \
|
||||
0005-ipu7-psys-harden-userptr-pinning.patch
|
||||
) || return 1
|
||||
|
||||
rm -rf "${check_tree}" || return 1
|
||||
cp -a "${driver_tree}" "${check_tree}" || return 1
|
||||
if ! make -C "${check_tree}" BUILD_INTEL_IPU_ACPI=1 \
|
||||
KERNEL_SRC="${kernel_build}"; then
|
||||
rm -rf "${check_tree}"
|
||||
return 1
|
||||
fi
|
||||
[[ -s ${check_tree}/drivers/media/pci/intel/ipu7/psys/intel-ipu7-psys.ko ]] || result=1
|
||||
rm -rf "${check_tree}" || return 1
|
||||
return "$result"
|
||||
}
|
||||
|
||||
package() {
|
||||
# DKMS: IPU7 drivers (ISYS + PSYS + ACPI)
|
||||
local ipu7_dkms_dir="${pkgdir}/usr/src/ipu7-drivers-${pkgver}"
|
||||
@@ -195,15 +245,6 @@ package() {
|
||||
install -Dm644 "${srcdir}/dkms-vision-drivers.conf" "${cvs_dkms_dir}/dkms.conf"
|
||||
sed -i "s/@PKGVER@/${pkgver}/" "${cvs_dkms_dir}/dkms.conf"
|
||||
|
||||
# DKMS: in-tree Linux 7.2 Intel CVS V4L2 bridge driver, kernels 7.2+
|
||||
local icvs_dkms_dir="${pkgdir}/usr/src/intel-cvs-${pkgver}"
|
||||
install -Dm644 "${srcdir}/intel-cvs-core.c" "${icvs_dkms_dir}/core.c"
|
||||
install -Dm644 "${srcdir}/intel-cvs-v4l2.c" "${icvs_dkms_dir}/v4l2.c"
|
||||
install -Dm644 "${srcdir}/intel-cvs-icvs.h" "${icvs_dkms_dir}/icvs.h"
|
||||
install -Dm644 "${srcdir}/intel-cvs-Makefile" "${icvs_dkms_dir}/Makefile"
|
||||
install -Dm644 "${srcdir}/dkms-intel-cvs.conf" "${icvs_dkms_dir}/dkms.conf"
|
||||
sed -i "s/@PKGVER@/${pkgver}/" "${icvs_dkms_dir}/dkms.conf"
|
||||
|
||||
# Firmware
|
||||
install -dm755 "${pkgdir}/usr/lib/firmware/intel/ipu"
|
||||
install -Dm644 "${srcdir}/ipu7-camera-bins/lib/firmware/intel/ipu/"*.bin \
|
||||
@@ -267,8 +308,10 @@ package() {
|
||||
# udev rules
|
||||
install -Dm644 "${srcdir}/71-ipu7-hide-isys.rules" \
|
||||
"${pkgdir}/usr/lib/udev/rules.d/71-ipu7-hide-isys.rules"
|
||||
install -Dm644 "${srcdir}/90-ipu7-psys.rules" \
|
||||
"${pkgdir}/usr/lib/udev/rules.d/90-ipu7-psys.rules"
|
||||
# Root-only: the relay is the PSYS node's only user, and the driver's buffer
|
||||
# lifecycle has use-after-free bugs that anyone who can open it can reach.
|
||||
install -Dm644 "${srcdir}/70-ipu7-psys.rules" \
|
||||
"${pkgdir}/usr/lib/udev/rules.d/70-ipu7-psys.rules"
|
||||
|
||||
# v4l2-relayd config for IPU7 camera
|
||||
install -Dm644 "${srcdir}/v4l2-relayd-ipu7.conf" \
|
||||
@@ -283,4 +326,8 @@ package() {
|
||||
# Sleep hook — staged here, installed to system-sleep by the .install scriptlet
|
||||
install -Dm755 "${srcdir}/camera-sleep-hook" \
|
||||
"${pkgdir}/usr/share/intel-ipu7-camera/camera-sleep-hook"
|
||||
|
||||
# Picks the relay pipeline for this board; run before each relay start
|
||||
install -Dm755 "${srcdir}/camera-videosrc-init" \
|
||||
"${pkgdir}/usr/share/intel-ipu7-camera/camera-videosrc-init"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
d /run/camera 0777 root root -
|
||||
d /run/camera 0770 root video -
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Drop the relay's 180-degree rotation on boards whose sensor is mounted upright.
|
||||
#
|
||||
# The packaged pipeline rotates because the sensor is mounted inverted on the
|
||||
# Panther Lake boards this package was first written for. On Lunar Lake it is
|
||||
# not: the sensor reports camera_sensor_rotation = 0 and camera_orientation =
|
||||
# Front, so the rotation turns an already-correct frame upside down. Keyed on
|
||||
# the bridge ACPI id; anything not positively identified as Lunar Lake keeps
|
||||
# the configured pipeline untouched:
|
||||
#
|
||||
# INTC10DE Lunar Lake no rotation
|
||||
# INTC10E1 Panther Lake rotate-180, as packaged
|
||||
# INTC10CF Meteor Lake rotate-180, as packaged
|
||||
# INTC10E0 Arrow Lake rotate-180, as packaged
|
||||
#
|
||||
# Runs as v4l2-relayd@ipu7's ExecStartPre, after one that removes the previous
|
||||
# override, so VIDEOSRC arrives parsed by systemd from the configured files.
|
||||
# ExecStart re-reads the drop-in's optional EnvironmentFile, so an edited
|
||||
# /etc/v4l2-relayd.d/ipu7.conf takes effect on the next start.
|
||||
|
||||
set -eu
|
||||
|
||||
out=/run/v4l2-relayd-ipu7-videosrc.env
|
||||
rm -f "${out}"
|
||||
|
||||
shopt -s nullglob
|
||||
lunar_lake=(/sys/bus/acpi/devices/INTC10DE:*)
|
||||
shopt -u nullglob
|
||||
((${#lunar_lake[@]} > 0)) || exit 0
|
||||
|
||||
src=${VIDEOSRC:-}
|
||||
[[ -n ${src} ]] || exit 0
|
||||
src=${src/ ! videoflip method=rotate-180/}
|
||||
# Quote for systemd's EnvironmentFile parser, which unescapes these in "...".
|
||||
src=${src//\\/\\\\}
|
||||
src=${src//\"/\\\"}
|
||||
src=${src//\$/\\\$}
|
||||
src=${src//\`/\\\`}
|
||||
printf 'VIDEOSRC="%s"\n' "${src}" >"${out}"
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define PAGE_SHIFT 12
|
||||
#define PAGE_SIZE (1UL << PAGE_SHIFT)
|
||||
#define MAX_RW_COUNT (INT_MAX & ~(PAGE_SIZE - 1))
|
||||
|
||||
static int page_array_size(uintptr_t start, uint64_t len,
|
||||
size_t *npages, size_t *bytes)
|
||||
{
|
||||
uintptr_t last;
|
||||
|
||||
if (!len || len > MAX_RW_COUNT)
|
||||
return -EINVAL;
|
||||
if (__builtin_add_overflow(start, len - 1, &last))
|
||||
return -EOVERFLOW;
|
||||
|
||||
*npages = (((last & ~(PAGE_SIZE - 1)) -
|
||||
(start & ~(PAGE_SIZE - 1))) >> PAGE_SHIFT) + 1;
|
||||
if (!*npages || *npages > INT_MAX)
|
||||
return -E2BIG;
|
||||
if (__builtin_mul_overflow(*npages, sizeof(void *), bytes))
|
||||
return -EOVERFLOW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const uintptr_t start = UINT64_C(0x100000000);
|
||||
const uint64_t exploit_pages = UINT64_C(0x20000001);
|
||||
const uint64_t ordinary_size = UINT64_C(64) * 1024 * 1024;
|
||||
size_t npages;
|
||||
size_t bytes;
|
||||
|
||||
assert(page_array_size(start, ordinary_size, &npages, &bytes) == 0);
|
||||
assert(npages == ordinary_size / PAGE_SIZE);
|
||||
assert(bytes == (ordinary_size / PAGE_SIZE) * sizeof(void *));
|
||||
|
||||
assert(page_array_size(start, exploit_pages * PAGE_SIZE,
|
||||
&npages, &bytes) == -EINVAL);
|
||||
assert(page_array_size(UINTPTR_MAX - 100, 200,
|
||||
&npages, &bytes) == -EOVERFLOW);
|
||||
assert(page_array_size(start, 0, &npages, &bytes) == -EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
# Intel CVS (Computer Vision Sensing) CSI-2 bridge driver.
|
||||
#
|
||||
# Copy of the in-tree Linux 7.2 driver (drivers/media/i2c/cvs) plus the
|
||||
# upstream wake-IRQ fix carried by the PKGBUILD, built out of tree so it
|
||||
# exists on every 7.2+ kernel regardless of the kernel's Kconfig.
|
||||
# Arch-derived configs cannot enable VIDEO_INTEL_CVS at all: it lives under
|
||||
# the "Miscellaneous helper chips" menu, which is hidden by
|
||||
# MEDIA_HIDE_ANCILLARY_SUBDRV unless CONFIG_EXPERT is set.
|
||||
#
|
||||
# 7.2's ipu-bridge inserts the CVS device between the sensor and the IPU
|
||||
# CSI2 receiver, so the IPU only sees the sensor once a driver registers
|
||||
# this V4L2 bridge sub-device. Kernels before 7.2 keep using the legacy
|
||||
# misc driver from vision-drivers (see dkms-vision-drivers.conf).
|
||||
PACKAGE_NAME="intel-cvs"
|
||||
PACKAGE_VERSION="@PKGVER@"
|
||||
AUTOINSTALL="yes"
|
||||
|
||||
# 7.2 and later only (regex, so the pacman dkms hook also honours it)
|
||||
BUILD_EXCLUSIVE_KERNEL="^(7\.([2-9]|[1-9][0-9]+)\.|([8-9]|[1-9][0-9]+)\.)"
|
||||
|
||||
BUILT_MODULE_NAME[0]="intel_cvs"
|
||||
BUILT_MODULE_LOCATION[0]=""
|
||||
DEST_MODULE_LOCATION[0]="/updates"
|
||||
|
||||
MAKE[0]="make KERNEL_SRC=${kernel_source_dir}"
|
||||
CLEAN="make KERNEL_SRC=${kernel_source_dir} clean"
|
||||
@@ -1,8 +1,7 @@
|
||||
# Legacy Intel CVS misc driver (no V4L2 bridge). Only for kernels before
|
||||
# 7.2: from 7.2 on, ipu-bridge routes the sensor through a CVS V4L2 bridge
|
||||
# sub-device, which this driver does not provide, and it would displace the
|
||||
# in-tree intel_cvs module of the same name. 7.2+ kernels use intel-cvs
|
||||
# (see dkms-intel-cvs.conf) instead.
|
||||
# in-tree intel_cvs module of the same name, which 7.2+ kernels use instead.
|
||||
PACKAGE_NAME="vision-drivers"
|
||||
PACKAGE_VERSION="@PKGVER@"
|
||||
AUTOINSTALL="yes"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Out-of-tree build of the in-tree Linux 7.2 Intel CVS driver
|
||||
# (drivers/media/i2c/cvs). Sources are kernel files plus the upstream
|
||||
# wake-IRQ fix carried by the PKGBUILD.
|
||||
KERNELRELEASE ?= $(shell uname -r)
|
||||
KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build
|
||||
|
||||
obj-m := intel_cvs.o
|
||||
intel_cvs-y := core.o v4l2.o
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(CURDIR) modules
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(CURDIR) clean
|
||||
@@ -19,12 +19,31 @@ _install_sleep_hook() {
|
||||
chmod 755 /usr/lib/systemd/system-sleep/camera-sleep-hook
|
||||
}
|
||||
|
||||
_apply_runtime_permissions() {
|
||||
if ! /usr/bin/systemd-tmpfiles --create /usr/lib/tmpfiles.d/camera.conf; then
|
||||
echo ":: WARNING: failed to restrict /run/camera; reboot before using the camera." >&2
|
||||
fi
|
||||
|
||||
if [ -S /run/udev/control ]; then
|
||||
if ! /usr/bin/udevadm control --reload-rules ||
|
||||
! /usr/bin/udevadm trigger --action=change --sysname-match=ipu7-psys0; then
|
||||
echo ":: WARNING: failed to restrict the IPU7 device; reboot before using the camera." >&2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_warn_if_driver_is_loaded() {
|
||||
if [ -d /sys/module/intel_ipu7_psys ]; then
|
||||
echo ":: SECURITY: reboot required to activate the updated IPU7 kernel driver." >&2
|
||||
fi
|
||||
}
|
||||
|
||||
post_install() {
|
||||
_install_sleep_hook
|
||||
_apply_runtime_permissions
|
||||
systemctl daemon-reload
|
||||
systemctl disable camera-init.service v4l2-relayd@ipu7.service 2>/dev/null || true
|
||||
systemctl enable intel-ipu7-camera.service
|
||||
udevadm control --reload-rules 2>/dev/null || true
|
||||
|
||||
if [ -n "$SUDO_USER" ]; then
|
||||
usermod -aG video "$SUDO_USER" 2>/dev/null || true
|
||||
@@ -37,12 +56,13 @@ post_install() {
|
||||
|
||||
post_upgrade() {
|
||||
_install_sleep_hook
|
||||
_apply_runtime_permissions
|
||||
_warn_if_driver_is_loaded
|
||||
systemctl daemon-reload
|
||||
systemctl disable camera-init.service v4l2-relayd@ipu7.service 2>/dev/null || true
|
||||
systemctl enable intel-ipu7-camera.service
|
||||
# Disable old camera-feed-watch (replaced by v4l2-relayd)
|
||||
systemctl --global disable camera-feed-watch.service 2>/dev/null || true
|
||||
udevadm control --reload-rules 2>/dev/null || true
|
||||
|
||||
if [ -n "$SUDO_USER" ]; then
|
||||
_add_pipewire_camera "/home/$SUDO_USER/.config/chromium-flags.conf"
|
||||
|
||||
@@ -3,6 +3,14 @@ After=camera-init.service
|
||||
|
||||
[Service]
|
||||
Environment=GST_PLUGIN_PATH=/usr/lib/gstreamer-1.0
|
||||
# Board-specific VIDEOSRC, regenerated on every start from the configured one.
|
||||
# Optional on purpose: without it /etc/v4l2-relayd.d/ipu7.conf applies unchanged.
|
||||
EnvironmentFile=-/run/v4l2-relayd-ipu7-videosrc.env
|
||||
# Remove the last override first: each ExecStartPre re-reads the environment
|
||||
# files, so the generator then sees the configured VIDEOSRC, not a stale one.
|
||||
ExecStartPre=-/usr/bin/rm -f /run/v4l2-relayd-ipu7-videosrc.env
|
||||
ExecStartPre=-/usr/share/intel-ipu7-camera/camera-videosrc-init
|
||||
ExecStopPost=-/usr/bin/rm -f /run/v4l2-relayd-ipu7-videosrc.env
|
||||
# Relax sandboxing for icamerasrc hardware access
|
||||
PrivateNetwork=no
|
||||
InaccessibleDirectories=
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
VIDEOSRC="icamerasrc device-name=ov08x40-uf sharpness=80 ev=-1 saturation=10 ! videoflip method=rotate-180"
|
||||
# fps-range and gain-range: in dim light AE may lengthen frames down to 15 fps
|
||||
# and never raises gain past 27 dB, instead of holding 30 fps at ~48x gain.
|
||||
# color-range=reduced: consumers decode NV12 webcam frames as limited range.
|
||||
VIDEOSRC="icamerasrc device-name=ov08x40-uf fps-range=15~30 gain-range=0~27 color-range=reduced ! videoflip method=rotate-180"
|
||||
FORMAT=NV12
|
||||
WIDTH=1920
|
||||
HEIGHT=1080
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
PKGBUILD=${1:-"$REPO_ROOT/pkgbuilds/intel-ipu7-camera/PKGBUILD"}
|
||||
(( $# <= 1 )) && [[ -f $PKGBUILD ]] || { echo 'Usage: ipu7-headers.sh [PKGBUILD]' >&2; exit 1; }
|
||||
|
||||
fail() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
has_package() {
|
||||
local sought=$1 package
|
||||
shift
|
||||
for package in "$@"; do
|
||||
[[ $package == "$sought" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Sourcing reads the real metadata and defines the real check(), without
|
||||
# running prepare(), build(), package(), or any hardware operation.
|
||||
source "$PKGBUILD"
|
||||
for package in linux-headers linux-omarchy-headers; do
|
||||
if has_package "$package" "${depends[@]}"; then
|
||||
fail "$package must not be a runtime dependency"
|
||||
fi
|
||||
done
|
||||
declare -p checkdepends >/dev/null 2>&1 || fail 'checkdepends is missing'
|
||||
has_package linux-omarchy-headers "${checkdepends[@]}" || fail 'Omarchy headers are missing from checkdepends'
|
||||
if has_package linux-headers "${checkdepends[@]}"; then
|
||||
fail 'Arch headers must not be a check dependency'
|
||||
fi
|
||||
|
||||
TEST_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/ipu7-headers.XXXXXXXX")
|
||||
cleanup() {
|
||||
if [[ -n ${TEST_ROOT:-} && -d $TEST_ROOT && ${TEST_ROOT##*/} == ipu7-headers.* ]]; then
|
||||
rm -rf -- "$TEST_ROOT"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
srcdir="$TEST_ROOT/src"
|
||||
TEST_KERNEL_BUILD="$TEST_ROOT/kernel/build"
|
||||
TEST_CALLS="$TEST_ROOT/calls"
|
||||
export TEST_KERNEL_BUILD TEST_CALLS srcdir
|
||||
mkdir -p "$TEST_ROOT/bin" "$srcdir/ipu7-drivers/drivers/media/pci/intel/ipu7/psys" \
|
||||
"$TEST_KERNEL_BUILD/scripts"
|
||||
printf '%s\n' 'fixture kernel Makefile' >"$TEST_KERNEL_BUILD/Makefile"
|
||||
printf '%s\n' 'fixture patch' >"$srcdir/0005-ipu7-psys-harden-userptr-pinning.patch"
|
||||
printf '%s\n' 'fixture source' >"$srcdir/check-userptr-range.c"
|
||||
cat >"$srcdir/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c" <<'EOF'
|
||||
kvmalloc_array(npages, sizeof(*pages)
|
||||
attach->len > MAX_RW_COUNT
|
||||
attach_fail:
|
||||
kbuf->db_attach = NULL;
|
||||
EOF
|
||||
|
||||
# Each command accepts only the one call made by check(). All output stays in
|
||||
# the disposable fixture, including the stand-in compiled program and module.
|
||||
cat >"$TEST_ROOT/bin/stub" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
case ${0##*/} in
|
||||
stub) [[ ${1:-} == '--probe' ]] || exit 1 ;;
|
||||
pacman)
|
||||
printf 'pacman %s\n' "$*" >>"$TEST_CALLS"
|
||||
if (( $# != 2 )) || [[ $1 != '-Qql' || $2 != 'linux-omarchy-headers' ]]; then
|
||||
printf 'Unexpected pacman query: %s\n' "$*" >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ $TEST_SCENARIO != 'missing-package' ]] || exit 1
|
||||
if [[ $TEST_SCENARIO == 'missing-makefile' ]]; then
|
||||
printf '%s\n' "$TEST_KERNEL_BUILD/missing/build/Makefile"
|
||||
else
|
||||
printf '%s\n' "$TEST_KERNEL_BUILD/Makefile"
|
||||
fi
|
||||
;;
|
||||
cc)
|
||||
printf 'cc %s\n' "$*" >>"$TEST_CALLS"
|
||||
(( $# == 8 )) && [[ $6 == "$srcdir/check-userptr-range.c" && $7 == '-o' && $8 == "$srcdir/check-userptr-range" ]] || exit 1
|
||||
cat >"$8" <<'PROGRAM'
|
||||
#!/bin/bash
|
||||
printf '%s\n' 'userptr fixture ran' >>"$TEST_CALLS"
|
||||
PROGRAM
|
||||
chmod +x "$8"
|
||||
;;
|
||||
make)
|
||||
printf 'make %s\n' "$*" >>"$TEST_CALLS"
|
||||
(( $# == 4 )) && [[ $1 == '-C' && $2 == "$srcdir/ipu7-drivers-check" && $3 == 'BUILD_INTEL_IPU_ACPI=1' && $4 == "KERNEL_SRC=/$TEST_KERNEL_BUILD" ]] || exit 1
|
||||
mkdir -p "$2/drivers/media/pci/intel/ipu7/psys"
|
||||
printf '%s\n' 'fixture module' >"$2/drivers/media/pci/intel/ipu7/psys/intel-ipu7-psys.ko"
|
||||
;;
|
||||
checkpatch.pl)
|
||||
printf 'checkpatch %s\n' "$*" >>"$TEST_CALLS"
|
||||
(( $# == 4 )) && [[ $1 == '--no-tree' && $2 == '--strict' && $3 == '--no-signoff' && $4 == '0005-ipu7-psys-harden-userptr-pinning.patch' ]] || exit 1
|
||||
[[ -f $4 ]] || exit 1
|
||||
;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$TEST_ROOT/bin/stub"
|
||||
"$TEST_ROOT/bin/stub" --probe 2>/dev/null || fail 'Set TMPDIR to an executable temporary directory for the test fixtures'
|
||||
for command in pacman cc make; do ln -s stub "$TEST_ROOT/bin/$command"; done
|
||||
ln -s "$TEST_ROOT/bin/stub" "$TEST_KERNEL_BUILD/scripts/checkpatch.pl"
|
||||
export PATH="$TEST_ROOT/bin:$PATH"
|
||||
|
||||
TEST_SCENARIO=success
|
||||
export TEST_SCENARIO
|
||||
: >"$TEST_CALLS"
|
||||
check || fail 'check() rejected valid Omarchy headers fixture'
|
||||
grep -Fxq 'pacman -Qql linux-omarchy-headers' "$TEST_CALLS" || fail 'Omarchy headers were not queried'
|
||||
grep -Fxq 'userptr fixture ran' "$TEST_CALLS" || fail 'compiled fixture was not run'
|
||||
grep -Fxq 'checkpatch --no-tree --strict --no-signoff 0005-ipu7-psys-harden-userptr-pinning.patch' "$TEST_CALLS" || fail 'checkpatch was not reached'
|
||||
grep -Fxq "make -C $srcdir/ipu7-drivers-check BUILD_INTEL_IPU_ACPI=1 KERNEL_SRC=/$TEST_KERNEL_BUILD" "$TEST_CALLS" || fail 'module build did not use Omarchy headers'
|
||||
|
||||
for TEST_SCENARIO in missing-package missing-makefile; do
|
||||
: >"$TEST_CALLS"
|
||||
if check; then fail "check() accepted $TEST_SCENARIO"; fi
|
||||
grep -Fxq 'pacman -Qql linux-omarchy-headers' "$TEST_CALLS" || fail "headers query skipped for $TEST_SCENARIO"
|
||||
if grep -Eq '^(checkpatch|make) ' "$TEST_CALLS"; then fail "build continued after $TEST_SCENARIO"; fi
|
||||
done
|
||||
|
||||
echo 'PASS: IPU7 header metadata, Omarchy build path, and missing-header failures'
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
TEST_ROOT=$(mktemp -d)
|
||||
trap 'rm -rf -- "$TEST_ROOT"' EXIT
|
||||
CALLS="$TEST_ROOT/calls"
|
||||
|
||||
# Exercise the real scriptlet with only system operations and state probes
|
||||
# substituted. No host files, services, udev state or modules are changed.
|
||||
script=$(<"$REPO_ROOT/pkgbuilds/intel-ipu7-camera/intel-ipu7-camera.install")
|
||||
script=${script//\/usr\/bin\/systemd-tmpfiles/fixture_tmpfiles}
|
||||
script=${script//\/usr\/bin\/udevadm/fixture_udevadm}
|
||||
source /dev/stdin <<<"$script"
|
||||
|
||||
fixture_tmpfiles() {
|
||||
printf 'tmpfiles %s\n' "$*" >>"$CALLS"
|
||||
[[ $scenario != "tmpfiles-fail" ]]
|
||||
}
|
||||
fixture_udevadm() {
|
||||
printf 'udev %s\n' "$*" >>"$CALLS"
|
||||
case "$scenario:$1" in
|
||||
reload-fail:control|trigger-fail:trigger) return 1 ;;
|
||||
esac
|
||||
}
|
||||
function [() {
|
||||
case "$*" in
|
||||
'-S /run/udev/control ]') [[ $scenario != "no-udev" ]] ;;
|
||||
'-d /sys/module/intel_ipu7_psys ]') [[ $loaded == "yes" ]] ;;
|
||||
*) builtin [ "$@" ;;
|
||||
esac
|
||||
}
|
||||
_install_sleep_hook() { echo sleep-hook >>"$CALLS"; }
|
||||
_add_pipewire_camera() { printf 'browser %s\n' "$*" >>"$CALLS"; }
|
||||
systemctl() { printf 'systemctl %s\n' "$*" >>"$CALLS"; }
|
||||
usermod() { printf 'usermod %s\n' "$*" >>"$CALLS"; }
|
||||
SUDO_USER=fixture-user
|
||||
|
||||
fail() { printf 'FAIL: %s (%s, loaded=%s, %s)\n' "$*" "$hook" "$loaded" "$scenario" >&2; exit 1; }
|
||||
|
||||
for hook in post_install post_upgrade; do
|
||||
for loaded in yes no; do
|
||||
for scenario in reload-fail trigger-fail tmpfiles-fail no-udev success; do
|
||||
: >"$CALLS"
|
||||
# Pacman scriptlets do not use errexit; a refresh failure must not skip
|
||||
# the remaining lifecycle work, including the loaded-driver warning.
|
||||
output=$(set +e; "$hook" 2>&1) || fail "scriptlet failed"
|
||||
[[ $output != *"restricted device permissions are active"* ]] || fail "unverified permission assurance"
|
||||
if [[ $hook == "post_upgrade" && $loaded == "yes" ]]; then
|
||||
[[ $output == *"reboot required to activate the updated IPU7 kernel driver"* ]] || fail "missing reboot warning"
|
||||
else
|
||||
[[ $output != *"reboot required to activate"* ]] || fail "unexpected loaded-driver warning"
|
||||
fi
|
||||
case "$scenario" in
|
||||
tmpfiles-fail) [[ $output == *"failed to restrict /run/camera"* ]] || fail "missing directory warning" ;;
|
||||
reload-fail|trigger-fail) [[ $output == *"failed to restrict the IPU7 device"* ]] || fail "missing device warning" ;;
|
||||
*) [[ $output != *"WARNING:"* ]] || fail "unexpected refresh warning" ;;
|
||||
esac
|
||||
grep -qx 'sleep-hook' "$CALLS" || fail "sleep hook skipped"
|
||||
grep -qx 'tmpfiles --create /usr/lib/tmpfiles.d/camera.conf' "$CALLS" || fail "permission refresh skipped"
|
||||
grep -qx 'systemctl daemon-reload' "$CALLS" || fail "daemon reload skipped"
|
||||
grep -qx 'systemctl enable intel-ipu7-camera.service' "$CALLS" || fail "service enable skipped"
|
||||
[[ $(grep -c '^browser ' "$CALLS") == 3 ]] || fail "browser lifecycle skipped"
|
||||
if [[ $scenario == "no-udev" ]]; then
|
||||
! grep -q '^udev ' "$CALLS" || fail "contacted absent udev"
|
||||
else
|
||||
grep -qx 'udev control --reload-rules' "$CALLS" || fail "udev reload skipped"
|
||||
if [[ $scenario != "reload-fail" ]]; then
|
||||
grep -qx 'udev trigger --action=change --sysname-match=ipu7-psys0' "$CALLS" || fail "udev trigger skipped"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
echo 'PASS: 20 IPU7 install/upgrade warning and lifecycle cases'
|
||||
Reference in new issue
Block a user