* [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support
@ 2026-08-26 13:22 Maurizio Casciano
2026-08-26 13:22 ` [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
` (8 more replies)
0 siblings, 9 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Lenovo Yoga Book YB1-X91L exposes an OV2740 front sensor and an
OV8858 rear sensor through the Cherry Trail AtomISP. Its firmware does
not provide enough information for the existing sensor and IPU bridge
drivers to construct the camera links.
Add the ACPI IDs and bridge data, describe the two AtomISP camera links,
support the Yoga Book sensor clocks and modes, allow explicit raw Bayer
capture, expose per-channel white-balance controls, and add the WV517S
rear-camera lens actuator.
Raw output remains opt-in and disabled by default, so existing AtomISP
users retain the current format-selection behavior.
Tested on a Lenovo Yoga Book YB1-X91L with continuous OV2740 raw capture,
OV8858 test bars and real 10-bit Bayer frames, and live white-balance
gain changes.
Maurizio Casciano (8):
media: ov8858: support 19.2 MHz clock and CHT gain setup
media: i2c: Add Yoga Book camera ACPI IDs
media: intel: ipu-bridge: Add Yoga Book camera sensors
media: atomisp: Add Yoga Book camera configuration
media: atomisp: support the Yoga Book OV2740 link
media: ov2740: add manual white balance controls
media: atomisp: allow opt-in raw Bayer capture
media: i2c: Add WV517S lens actuator driver
MAINTAINERS | 1 +
drivers/media/i2c/Kconfig | 8 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov2740.c | 170 ++++++++++++---
drivers/media/i2c/ov8858.c | 134 ++++++++++--
drivers/media/i2c/wv517s.c | 199 ++++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 19 +-
.../staging/media/atomisp/pci/atomisp_cmd.c | 24 ++-
.../staging/media/atomisp/pci/atomisp_csi2.c | 17 +-
.../staging/media/atomisp/pci/atomisp_csi2.h | 2 +
.../media/atomisp/pci/atomisp_csi2_bridge.c | 81 +++++++
.../media/atomisp/pci/atomisp_internal.h | 6 +
.../staging/media/atomisp/pci/atomisp_ioctl.c | 13 +-
.../staging/media/atomisp/pci/atomisp_v4l2.c | 15 ++
include/media/ipu-bridge.h | 2 +
15 files changed, 625 insertions(+), 67 deletions(-)
create mode 100644 drivers/media/i2c/wv517s.c
base-commit: 4900cad020c0580dfb1be27776ff10a4ef110cfa
--
2.53.0
^ permalink raw reply [flat|nested] 53+ messages in thread
* [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 9:40 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs Maurizio Casciano
` (7 subsequent siblings)
8 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book drives its OV8858 from a 19.2 MHz platform clock, while
the existing mode tables program the sensor PLL for 24 MHz. Reusing
those settings produces incorrect internal and CSI-2 clocks.
Accept both input rates and use the actual rate for the reset delay. For
19.2 MHz, apply the Cherry Trail MRD PLL and black-level settings after
the generic mode table.
The 19.2 MHz platform uses the per-channel manual white-balance
registers for digital gain. Program registers 0x5032, 0x5034 and 0x5036
and expose their 1x-to-4x range, while retaining the existing
long-exposure gain block for 24 MHz systems.
The manual white-balance register definitions and programming follow
the GPL-2.0 Intel OV5670 driver, so retain its 2017 Intel copyright
notice in this file. No proprietary source or tuning binary is included.
Tested on the Lenovo Yoga Book YB1-X91L OV8858 with full-range test bars
and real 10-bit Bayer frames.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov8858.c | 126 ++++++++++++++++++++++++++++++-------
1 file changed, 103 insertions(+), 23 deletions(-)
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index 3f45f7fab833..f2bed3e88382 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -3,10 +3,9 @@
* Copyright (C) 2023 Jacopo Mondi <jacopo.mondi@ideasonboard.com>
* Copyright (C) 2022 Nicholas Roth <nicholas@rothemail.net>
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2017 Intel Corporation.
*/
-#include <linux/unaligned.h>
-
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -18,6 +17,7 @@
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
@@ -29,7 +29,8 @@
#include <media/v4l2-subdev.h>
#define OV8858_LINK_FREQ 360000000U
-#define OV8858_XVCLK_FREQ 24000000
+#define OV8858_XVCLK_FREQ_19_2MHZ 19200000
+#define OV8858_XVCLK_FREQ_24MHZ 24000000
#define OV8858_REG_SIZE_SHIFT 16
#define OV8858_REG_ADDR_MASK 0xffff
@@ -59,15 +60,23 @@
#define OV8858_LONG_GAIN_STEP 1
#define OV8858_LONG_GAIN_DEFAULT 0x80
+#define OV8858_REG_MWB_RED_GAIN OV8858_REG_16BIT(0x5032)
+#define OV8858_REG_MWB_GREEN_GAIN OV8858_REG_16BIT(0x5034)
+#define OV8858_REG_MWB_BLUE_GAIN OV8858_REG_16BIT(0x5036)
+#define OV8858_MWB_GAIN_MIN 0x400
+#define OV8858_MWB_GAIN_MAX 0xfff
+#define OV8858_MWB_GAIN_DEFAULT 0x400
+
#define OV8858_REG_LONG_DIGIGAIN OV8858_REG_16BIT(0x350a)
#define OV8858_LONG_DIGIGAIN_H_MASK 0x3fc0
#define OV8858_LONG_DIGIGAIN_L_MASK 0x3f
#define OV8858_LONG_DIGIGAIN_H_SHIFT 2
#define OV8858_LONG_DIGIGAIN_MIN 0x0
#define OV8858_LONG_DIGIGAIN_MAX 0x3fff
-#define OV8858_LONG_DIGIGAIN_STEP 1
#define OV8858_LONG_DIGIGAIN_DEFAULT 0x200
+#define OV8858_DIGITAL_GAIN_STEP 1
+
#define OV8858_REG_VTS OV8858_REG_16BIT(0x380e)
#define OV8858_VTS_MAX 0x7fff
@@ -104,6 +113,7 @@ struct ov8858_mode {
struct ov8858 {
struct clk *xvclk;
+ unsigned long xvclk_rate;
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
@@ -121,6 +131,41 @@ struct ov8858 {
unsigned int num_lanes;
};
+/*
+ * Cherry Trail MRD production settings for a 19.2 MHz input and 360 MHz
+ * CSI-2 link. Apply these after the otherwise reusable 24 MHz mode table.
+ *
+ * Besides the corrected sensor/MIPI PLL divisors, keep the final common
+ * black-level settings here. The per-mode tables retain their resolution
+ * dependent black-column anchors and window sizes.
+ */
+static const struct regval ov8858_cht_mrd_19_2mhz[] = {
+ {0x0300, 0x00},
+ {0x0302, 0x27},
+ {0x0303, 0x00},
+ {0x0304, 0x03},
+ {0x030b, 0x00},
+ {0x030d, 0x27},
+ {0x030e, 0x00},
+ {0x030f, 0x04},
+ {0x0312, 0x01},
+ {0x031e, 0x0c},
+ {0x3f08, 0x08},
+ {0x400a, 0x01},
+ {0x400d, 0x10},
+ {0x4011, 0x20},
+ {0x403e, 0x08},
+ {0x4040, 0x07},
+ {0x4041, 0xc6},
+ {0x4202, 0x00},
+ {0x4500, 0x58},
+ {0x470b, 0x28},
+ {0x4837, 0x15},
+ {0x58f4, 0x32},
+ {0x58f8, 0x3d},
+ {REG_NULL, 0x00},
+};
+
static inline struct ov8858 *sd_to_ov8858(struct v4l2_subdev *sd)
{
return container_of(sd, struct ov8858, subdev);
@@ -1345,6 +1390,14 @@ static int ov8858_start_stream(struct ov8858 *ov8858,
if (ret)
return ret;
+ /* The mode tables contain PLL settings for a 24 MHz input clock. */
+ if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
+ ret = ov8858_write_array(ov8858,
+ ov8858_cht_mrd_19_2mhz);
+ if (ret)
+ return ret;
+ }
+
/* 200 usec max to let PLL stabilize. */
fsleep(200);
@@ -1523,6 +1576,31 @@ static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern)
return ov8858_write(ov8858, OV8858_REG_TEST_PATTERN, val, NULL);
}
+static int ov8858_set_digital_gain(struct ov8858 *ov8858, u32 gain)
+{
+ u16 long_gain;
+ int ret;
+
+ if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ) {
+ long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
+ ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
+ OV8858_LONG_DIGIGAIN_H_SHIFT);
+
+ return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
+ long_gain, NULL);
+ }
+
+ ret = ov8858_write(ov8858, OV8858_REG_MWB_RED_GAIN, gain, NULL);
+ if (ret)
+ return ret;
+
+ ret = ov8858_write(ov8858, OV8858_REG_MWB_GREEN_GAIN, gain, NULL);
+ if (ret)
+ return ret;
+
+ return ov8858_write(ov8858, OV8858_REG_MWB_BLUE_GAIN, gain, NULL);
+}
+
static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov8858 *ov8858 = container_of(ctrl->handler,
@@ -1531,7 +1609,6 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
- u16 digi_gain;
s64 max_exp;
int ret;
@@ -1570,17 +1647,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
ctrl->val, NULL);
break;
case V4L2_CID_DIGITAL_GAIN:
- /*
- * Digital gain is assembled as:
- * 0x350a[7:0] = dgain[13:6]
- * 0x350b[5:0] = dgain[5:0]
- * Reassemble the control value to write it in one go.
- */
- digi_gain = (ctrl->val & OV8858_LONG_DIGIGAIN_L_MASK)
- | ((ctrl->val & OV8858_LONG_DIGIGAIN_H_MASK) <<
- OV8858_LONG_DIGIGAIN_H_SHIFT);
- ret = ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
- digi_gain, NULL);
+ ret = ov8858_set_digital_gain(ov8858, ctrl->val);
break;
case V4L2_CID_VBLANK:
ret = ov8858_write(ov8858, OV8858_REG_VTS,
@@ -1616,9 +1683,6 @@ static int ov8858_power_on(struct ov8858 *ov8858)
unsigned long delay_us;
int ret;
- if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ)
- dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
-
ret = clk_prepare_enable(ov8858->xvclk);
if (ret < 0) {
dev_err(dev, "Failed to enable xvclk\n");
@@ -1637,7 +1701,7 @@ static int ov8858_power_on(struct ov8858 *ov8858)
* transaction, but a double sleep between the release of gpios
* helps with sporadic failures observed at probe time.
*/
- delay_us = DIV_ROUND_UP(8192, OV8858_XVCLK_FREQ / 1000 / 1000);
+ delay_us = DIV_ROUND_UP(8192, ov8858->xvclk_rate / 1000 / 1000);
gpiod_set_value_cansleep(ov8858->reset_gpio, 0);
fsleep(delay_us);
@@ -1697,6 +1761,9 @@ static int ov8858_init_ctrls(struct ov8858 *ov8858)
struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
const struct ov8858_mode *mode = &ov8858_modes[0];
struct v4l2_fwnode_device_properties props;
+ u32 digital_gain_default = OV8858_LONG_DIGIGAIN_DEFAULT;
+ u32 digital_gain_max = OV8858_LONG_DIGIGAIN_MAX;
+ u32 digital_gain_min = OV8858_LONG_DIGIGAIN_MIN;
s64 exposure_max, vblank_def;
unsigned int pixel_rate;
struct v4l2_ctrl *ctrl;
@@ -1740,10 +1807,16 @@ static int ov8858_init_ctrls(struct ov8858 *ov8858)
OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
+ if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
+ digital_gain_min = OV8858_MWB_GAIN_MIN;
+ digital_gain_max = OV8858_MWB_GAIN_MAX;
+ digital_gain_default = OV8858_MWB_GAIN_DEFAULT;
+ }
+
v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
- OV8858_LONG_DIGIGAIN_MIN, OV8858_LONG_DIGIGAIN_MAX,
- OV8858_LONG_DIGIGAIN_STEP,
- OV8858_LONG_DIGIGAIN_DEFAULT);
+ digital_gain_min, digital_gain_max,
+ OV8858_DIGITAL_GAIN_STEP,
+ digital_gain_default);
v4l2_ctrl_new_std_menu_items(handler, &ov8858_ctrl_ops,
V4L2_CID_TEST_PATTERN,
@@ -1881,6 +1954,13 @@ static int ov8858_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(ov8858->xvclk),
"Failed to get xvclk\n");
+ ov8858->xvclk_rate = clk_get_rate(ov8858->xvclk);
+ if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ &&
+ ov8858->xvclk_rate != OV8858_XVCLK_FREQ_24MHZ)
+ return dev_err_probe(dev, -EINVAL,
+ "Unsupported xvclk rate %lu Hz\n",
+ ov8858->xvclk_rate);
+
ov8858->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(ov8858->reset_gpio))
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-26 13:22 ` [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 9:58 ` Andy Shevchenko
2026-08-27 12:13 ` Sakari Ailus
2026-08-26 13:22 ` [PATCH 3/8] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
` (6 subsequent siblings)
8 siblings, 2 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Lenovo Yoga Book YB1-X91 firmware enumerates the front OV2740 as
OVTI2740 and the rear OV8858 as INT3477. Neither ID currently matches
its sensor driver, so both I2C devices remain unbound.
Add the IDs to the corresponding ACPI match tables. Sensor
configuration and CSI lane quirks are kept in later patches.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov2740.c | 1 +
drivers/media/i2c/ov8858.c | 8 ++++++++
2 files changed, 9 insertions(+)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 39003c1632ad..d85d83bdd203 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -1462,6 +1462,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume,
static const struct acpi_device_id ov2740_acpi_ids[] = {
{"INT3474"},
+ {"OVTI2740"},
{}
};
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index f2bed3e88382..5a97f21ab89d 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -6,6 +6,7 @@
* Copyright (c) 2017 Intel Corporation.
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -2061,10 +2062,17 @@ static const struct of_device_id ov8858_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ov8858_of_match);
+static const struct acpi_device_id ov8858_acpi_match[] = {
+ { "INT3477" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(acpi, ov8858_acpi_match);
+
static struct i2c_driver ov8858_i2c_driver = {
.driver = {
.name = "ov8858",
.pm = &ov8858_pm_ops,
+ .acpi_match_table = ACPI_PTR(ov8858_acpi_match),
.of_match_table = ov8858_of_match,
},
.probe = ov8858_probe,
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 3/8] media: intel: ipu-bridge: Add Yoga Book camera sensors
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-26 13:22 ` [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
2026-08-26 13:22 ` [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-26 13:22 ` [PATCH 4/8] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
` (5 subsequent siblings)
8 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book YB1-X91 uses INT3477 for its OV8858 rear sensor and
OVTI2740 for its OV2740 front sensor. Add link frequencies consumed by
the sensor drivers when the IPU bridge constructs software endpoints.
This supplies endpoint data only; it does not establish that either
sensor can stream on the Yoga Book hardware.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/pci/intel/ipu-bridge.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 1bb3a3e98d6b..47317c423fad 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -65,6 +65,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("INT33F0", 1, 384000000),
/* Omnivision OV2740 */
IPU_SENSOR_CONFIG("INT3474", 1, 180000000),
+ /* Omnivision OV8858 */
+ IPU_SENSOR_CONFIG("INT3477", 1, 360000000),
/* Omnivision OV5670 */
IPU_SENSOR_CONFIG("INT3479", 1, 422400000),
/* Omnivision OV8865 */
@@ -93,6 +95,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
/* Omnivision OV2680 */
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
+ /* Omnivision OV2740 */
+ IPU_SENSOR_CONFIG("OVTI2740", 1, 360000000),
/* Omnivision OV5675 */
IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
/* Omnivision OV8856 */
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 4/8] media: atomisp: Add Yoga Book camera configuration
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (2 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 3/8] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-26 13:22 ` [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
` (4 subsequent siblings)
8 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book YB1-X91 firmware lacks usable AtomISP configuration for
both cameras. Rear INT3477 OV8858 uses four CSI-2 lanes and a WV517S
actuator; front OVTI2740 uses two lanes and no actuator.
Add both sensor configurations and a DMI-scoped two-lane override for
OVTI2740:00. Keep the quirk limited to Lenovo YB1-X91L systems.
Binding and media nodes do not prove streaming; both sensors and the
focus actuator still require physical tests.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
.../media/atomisp/pci/atomisp_csi2_bridge.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index cca91c6d71a5..207ab69385b5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -74,6 +74,12 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
{}
};
+static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
+ /* The vendor driver and sensor modes use two CSI data lanes. */
+ { "OVTI2740:00", "CsiLanes", "2" },
+ {}
+};
+
static struct gmin_cfg_var xiaomi_mipad2_vars[] = {
/* _DSM contains the wrong CsiPort for the front facing OV5693 sensor */
{ "INT33BE:00", "CsiPort", "0" },
@@ -83,6 +89,14 @@ static struct gmin_cfg_var xiaomi_mipad2_vars[] = {
};
static const struct dmi_system_id gmin_cfg_dmi_overrides[] = {
+ {
+ /* Lenovo Yoga Book X91L */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91L"),
+ },
+ .driver_data = lenovo_yogabook_x91_vars,
+ },
{
/* Lenovo Ideapad Miix 310 */
.matches = {
@@ -360,6 +374,8 @@ static const struct acpi_device_id atomisp_sensor_configs[] = {
* an i2c-client for the VCM, so it is disabled for now.
*/
ATOMISP_SENSOR_CONFIG("INT33BE", 2, false), /* OV5693 */
+ ATOMISP_SENSOR_CONFIG("INT3477", 4, true), /* OV8858 */
+ ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false), /* OV2740 */
{}
};
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (3 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 4/8] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 14:26 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 6/8] media: ov2740: add manual white balance controls Maurizio Casciano
` (3 subsequent siblings)
8 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The YB1-X91L firmware does not describe the complete camera link. Its
front OV2740 uses two CSI-2 lanes at a 288 MHz link frequency and sends
a 1932x1092 BGGR transport frame with 12 pixels of horizontal and
vertical padding around the 1920x1080 image.
Allow the AtomISP bridge to provide per-sensor link frequencies and
padding, add the matching OV2740 mode, and derive ISP2401 D-PHY timing
from the sensor link-frequency control.
The register values are hardware configuration facts checked against
the Lenovo YB1-X91L configuration and physical captures; no proprietary
driver code or tuning binary is included.
Tested on a Lenovo Yoga Book YB1-X91L with continuous front-camera raw
capture.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov2740.c | 98 ++++++++++++++++++-
drivers/media/pci/intel/ipu-bridge.c | 15 ++-
.../staging/media/atomisp/pci/atomisp_csi2.c | 17 +++-
.../staging/media/atomisp/pci/atomisp_csi2.h | 2 +
.../media/atomisp/pci/atomisp_csi2_bridge.c | 65 ++++++++++++
include/media/ipu-bridge.h | 2 +
6 files changed, 186 insertions(+), 13 deletions(-)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index d85d83bdd203..b760d4dc0e68 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -17,6 +17,7 @@
#include <media/v4l2-fwnode.h>
#define OV2740_LINK_FREQ_360MHZ 360000000ULL
+#define OV2740_LINK_FREQ_288MHZ 288000000ULL
#define OV2740_LINK_FREQ_180MHZ 180000000ULL
#define OV2740_SCLK 72000000LL
#define OV2740_MCLK 19200000
@@ -91,6 +92,7 @@ struct nvm_data {
enum {
OV2740_LINK_FREQ_360MHZ_INDEX,
+ OV2740_LINK_FREQ_288MHZ_INDEX,
OV2740_LINK_FREQ_180MHZ_INDEX,
};
@@ -130,8 +132,14 @@ struct ov2740_mode {
/* Link frequency needed for this resolution */
u32 link_freq_index;
+ /* Optional common settings applied before the mode-specific settings */
+ const struct ov2740_reg_list init_reg_list;
+
/* Sensor register settings for this resolution */
const struct ov2740_reg_list reg_list;
+
+ /* Bayer order produced by this mode */
+ u32 code;
};
static const struct ov2740_reg mipi_data_rate_720mbps[] = {
@@ -142,6 +150,14 @@ static const struct ov2740_reg mipi_data_rate_720mbps[] = {
{0x0312, 0x11},
};
+static const struct ov2740_reg mipi_data_rate_576mbps[] = {
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x02},
+ {0x0312, 0x01},
+};
+
static const struct ov2740_reg mipi_data_rate_360mbps[] = {
{0x0302, 0x4b},
{0x0303, 0x01},
@@ -458,6 +474,36 @@ static const struct ov2740_reg mode_1932x1092_regs_180mhz[] = {
{0x4003, 0x40}, /* set Black level to 0x40 */
};
+/*
+ * Lenovo's Yoga Book vendor driver uses the generic initialization settings
+ * above followed by these mode overrides. Unlike the generic 720 Mbps mode,
+ * this is a 576 Mbps two-lane mode with BGGR output from the optical array.
+ */
+static const struct ov2740_reg mode_1932x1092_regs_288mhz[] = {
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x02},
+ {0x0312, 0x01},
+ {0x3808, 0x07},
+ {0x3809, 0x8c},
+ {0x380a, 0x04},
+ {0x380b, 0x44},
+ {0x380c, 0x04},
+ {0x380d, 0x38},
+ {0x380e, 0x06},
+ {0x380f, 0xf0},
+ {0x3810, 0x00},
+ {0x3811, 0x02},
+ {0x3812, 0x00},
+ {0x3813, 0x02},
+ {0x481f, 0x29},
+ {0x4820, 0x01},
+ {0x4837, 0x1b},
+ {0x5000, 0x7f},
+ {0x58f4, 0x32},
+};
+
static const char * const ov2740_test_pattern_menu[] = {
"Disabled",
"Color Bar",
@@ -468,6 +514,7 @@ static const char * const ov2740_test_pattern_menu[] = {
static const s64 link_freq_menu_items[] = {
OV2740_LINK_FREQ_360MHZ,
+ OV2740_LINK_FREQ_288MHZ,
OV2740_LINK_FREQ_180MHZ,
};
@@ -478,6 +525,12 @@ static const struct ov2740_link_freq_config link_freq_configs[] = {
.regs = mipi_data_rate_720mbps,
}
},
+ [OV2740_LINK_FREQ_288MHZ_INDEX] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_576mbps),
+ .regs = mipi_data_rate_576mbps,
+ }
+ },
[OV2740_LINK_FREQ_180MHZ_INDEX] = {
.reg_list = {
.num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps),
@@ -499,6 +552,28 @@ static const struct ov2740_mode supported_modes_360mhz[] = {
.regs = mode_1932x1092_regs_360mhz,
},
.link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ },
+};
+
+static const struct ov2740_mode supported_modes_288mhz[] = {
+ {
+ .width = 1932,
+ .height = 1092,
+ .hts = 2160,
+ .vts_min = 1776,
+ .vts_def = 1776,
+ .vts_max = 32767,
+ .init_reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_360mhz),
+ .regs = mode_1932x1092_regs_360mhz,
+ },
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_288mhz),
+ .regs = mode_1932x1092_regs_288mhz,
+ },
+ .link_freq_index = OV2740_LINK_FREQ_288MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
},
};
@@ -515,6 +590,7 @@ static const struct ov2740_mode supported_modes_180mhz[] = {
.regs = mode_1932x1092_regs_180mhz,
},
.link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
},
};
@@ -842,7 +918,7 @@ static void ov2740_update_pad_format(const struct ov2740_mode *mode,
{
fmt->width = mode->width;
fmt->height = mode->height;
- fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->code = mode->code;
fmt->field = V4L2_FIELD_NONE;
}
@@ -966,6 +1042,15 @@ static int ov2740_start_streaming(struct ov2740 *ov2740)
return ret;
}
+ if (ov2740->cur_mode->init_reg_list.num_of_regs) {
+ reg_list = &ov2740->cur_mode->init_reg_list;
+ ret = ov2740_write_reg_list(ov2740, reg_list);
+ if (ret) {
+ dev_err(ov2740->dev, "failed to set common mode registers\n");
+ return ret;
+ }
+ }
+
reg_list = &ov2740->cur_mode->reg_list;
ret = ov2740_write_reg_list(ov2740, reg_list);
if (ret) {
@@ -1062,10 +1147,12 @@ static int ov2740_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
+ struct ov2740 *ov2740 = to_ov2740(sd);
+
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ code->code = ov2740->supported_modes[0].code;
return 0;
}
@@ -1080,7 +1167,7 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd,
if (fse->index >= ov2740->supported_modes_count)
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ if (fse->code != supported_modes[0].code)
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
@@ -1178,6 +1265,11 @@ static int ov2740_check_hwcfg(struct ov2740 *ov2740)
ov2740->supported_modes_count =
ARRAY_SIZE(supported_modes_360mhz);
break;
+ case OV2740_LINK_FREQ_288MHZ_INDEX:
+ ov2740->supported_modes = supported_modes_288mhz;
+ ov2740->supported_modes_count =
+ ARRAY_SIZE(supported_modes_288mhz);
+ break;
case OV2740_LINK_FREQ_180MHZ_INDEX:
ov2740->supported_modes = supported_modes_180mhz;
ov2740->supported_modes_count =
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 47317c423fad..5730a95767a2 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -520,11 +520,16 @@ static void ipu_bridge_create_fwnode_properties(
sensor->prop_names.remote_endpoint,
sensor->local_ref);
- if (cfg->nr_link_freqs > 0)
- sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
- sensor->prop_names.link_frequencies,
- cfg->link_freqs,
- cfg->nr_link_freqs);
+ if (sensor->nr_link_freqs > 0)
+ sensor->ep_properties[3] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
+ sensor->link_freqs,
+ sensor->nr_link_freqs);
+ else if (cfg->nr_link_freqs > 0)
+ sensor->ep_properties[3] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
+ cfg->link_freqs,
+ cfg->nr_link_freqs);
sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
sensor->prop_names.data_lanes,
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index 95b9113d75e9..035f1addeaae 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -5,6 +5,7 @@
* Copyright (c) 2010 Intel Corporation. All Rights Reserved.
*/
+#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mediabus.h>
#include "atomisp_cmd.h"
@@ -288,18 +289,19 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
int dat_termen;
int dat_settle;
- struct v4l2_control ctrl;
struct atomisp_device *isp = asd->isp;
+ struct v4l2_subdev *sensor;
+ s64 link_freq;
int mipi_freq = 0;
enum atomisp_camera_port port;
int n;
port = isp->inputs[asd->input_curr].port;
- ctrl.id = V4L2_CID_LINK_FREQ;
- if (v4l2_g_ctrl
- (isp->inputs[asd->input_curr].sensor->ctrl_handler, &ctrl) == 0)
- mipi_freq = ctrl.value;
+ sensor = isp->inputs[asd->input_curr].sensor;
+ link_freq = v4l2_get_link_freq(&sensor->entity.pads[0], 0, 0);
+ if (link_freq > 0 && link_freq <= S32_MAX)
+ mipi_freq = link_freq;
clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen, mipi_freq,
TERMEN_DEFAULT);
@@ -310,6 +312,11 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle, mipi_freq,
SETTLE_DEFAULT);
+ dev_dbg(isp->dev,
+ "CSI port %u link frequency %d Hz, clk timing %d/%d, data timing %d/%d\n",
+ port, mipi_freq, clk_termen, clk_settle,
+ dat_termen, dat_settle);
+
for (n = 0; n < csi2_port_lanes[port] + 1; n++) {
hrt_address base = csi2_port_base[port] + csi2_lane_base[n];
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
index ec762f8fb922..dd36659e7984 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
@@ -45,6 +45,8 @@ int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2,
struct v4l2_device *vdev);
int atomisp_csi2_bridge_init(struct atomisp_device *isp);
int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp);
+bool atomisp_csi2_get_sensor_padding(struct device *dev, u32 *padding_w,
+ u32 *padding_h);
void atomisp_csi2_configure(struct atomisp_sub_device *asd);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index 207ab69385b5..0b9577ffb98c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -77,6 +77,11 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
/* The vendor driver and sensor modes use two CSI data lanes. */
{ "OVTI2740:00", "CsiLanes", "2" },
+ /* The vendor 1932x1092 mode uses a 576 Mbps two-lane link. */
+ { "OVTI2740:00", "CsiLinkFreq", "288000000" },
+ /* Crop the vendor mode's 1932x1092 transport frame to 1920x1080. */
+ { "OVTI2740:00", "CsiPaddingWidth", "12" },
+ { "OVTI2740:00", "CsiPaddingHeight", "12" },
{}
};
@@ -207,6 +212,49 @@ static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int defau
return default_val;
}
+bool atomisp_csi2_get_sensor_padding(struct device *dev, u32 *padding_w,
+ u32 *padding_h)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ bool override = false;
+ char *str_val;
+ unsigned int val;
+
+ *padding_w = pad_w;
+ *padding_h = pad_h;
+
+ if (!adev)
+ return false;
+
+ str_val = gmin_cfg_get(adev, "CsiPaddingWidth");
+ if (str_val) {
+ if (!kstrtouint(str_val, 0, &val) && val <= 64 && !(val & 1)) {
+ *padding_w = val;
+ override = true;
+ } else {
+ acpi_handle_warn(adev->handle,
+ "%s: Invalid CSI padding width %s\n",
+ dev_name(dev), str_val);
+ }
+ kfree(str_val);
+ }
+
+ str_val = gmin_cfg_get(adev, "CsiPaddingHeight");
+ if (str_val) {
+ if (!kstrtouint(str_val, 0, &val) && val <= 64 && !(val & 1)) {
+ *padding_h = val;
+ override = true;
+ } else {
+ acpi_handle_warn(adev->handle,
+ "%s: Invalid CSI padding height %s\n",
+ dev_name(dev), str_val);
+ }
+ kfree(str_val);
+ }
+
+ return override;
+}
+
static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev)
{
/* ACPI_PATH_SEGMENT_LENGTH is guaranteed to be big enough for name + 0 term. */
@@ -383,6 +431,8 @@ static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev,
struct ipu_sensor *sensor)
{
const struct acpi_device_id *id;
+ char *link_freq_str;
+ unsigned long long link_freq;
int ret, clock_num;
bool vcm = false;
int lanes = 1;
@@ -422,6 +472,21 @@ static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev,
return -EINVAL;
}
+ link_freq_str = gmin_cfg_get(adev, "CsiLinkFreq");
+ if (link_freq_str) {
+ ret = kstrtoull(link_freq_str, 0, &link_freq);
+ kfree(link_freq_str);
+ if (ret || !link_freq) {
+ acpi_handle_err(adev->handle,
+ "%s: Invalid CSI link frequency\n",
+ dev_name(&adev->dev));
+ return ret ?: -EINVAL;
+ }
+
+ sensor->link_freqs[0] = link_freq;
+ sensor->nr_link_freqs = 1;
+ }
+
ret = atomisp_csi2_add_gpio_mappings(adev);
if (ret)
return ret;
diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
index 16fac765456e..633ef0c4cf59 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -135,6 +135,8 @@ struct ipu_sensor {
u8 link;
u8 lanes;
+ u8 nr_link_freqs;
+ u64 link_freqs[MAX_NUM_LINK_FREQS];
u32 mclkspeed;
u32 rotation;
enum v4l2_fwnode_orientation orientation;
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 6/8] media: ov2740: add manual white balance controls
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (4 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 3:13 ` Cao, Bingbu
2026-08-27 14:32 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture Maurizio Casciano
` (2 subsequent siblings)
8 siblings, 2 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The sensor has separate red, green and blue manual white-balance gain
registers, but the driver currently writes the same digital-gain value
to all three channels. This prevents userspace from correcting the
strong color cast of raw Bayer capture.
Expose red- and blue-balance controls relative to the digital gain,
update all three channels under group hold, and always release and
launch the group even when a channel write fails.
Tested on the Yoga Book OV2740 with live gain changes and continuous raw
capture.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov2740.c | 71 +++++++++++++++++++++++++++-----------
1 file changed, 50 insertions(+), 21 deletions(-)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index b760d4dc0e68..7651443b28c1 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -607,6 +607,9 @@ struct ov2740 {
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *digital_gain;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
/* GPIOs, clocks, regulators */
struct gpio_desc *reset_gpio;
@@ -736,35 +739,46 @@ static int ov2740_identify_module(struct ov2740 *ov2740)
return 0;
}
-static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain)
+static int ov2740_update_mwb_gains(struct ov2740 *ov2740)
{
- int ret;
+ u32 green_gain = ov2740->digital_gain->val;
+ u32 red_gain, blue_gain;
+ int end_ret, launch_ret, ret;
+
+ /* Balance controls use 1024 as unity relative to the digital gain. */
+ red_gain = min_t(u64,
+ DIV_ROUND_CLOSEST_ULL((u64)green_gain *
+ ov2740->red_balance->val,
+ OV2740_DGTL_GAIN_DEFAULT),
+ OV2740_DGTL_GAIN_MAX);
+ blue_gain = min_t(u64,
+ DIV_ROUND_CLOSEST_ULL((u64)green_gain *
+ ov2740->blue_balance->val,
+ OV2740_DGTL_GAIN_DEFAULT),
+ OV2740_DGTL_GAIN_MAX);
ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
OV2740_GROUP_HOLD_START);
if (ret)
return ret;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain);
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, red_gain);
if (ret)
- return ret;
+ goto release_group;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, d_gain);
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, green_gain);
if (ret)
- return ret;
+ goto release_group;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
- if (ret)
- return ret;
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, blue_gain);
- ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
- OV2740_GROUP_HOLD_END);
- if (ret)
- return ret;
+release_group:
+ end_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_END);
+ launch_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_LAUNCH);
- ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
- OV2740_GROUP_HOLD_LAUNCH);
- return ret;
+ return ret ?: end_ret ?: launch_ret;
}
static int ov2740_test_pattern(struct ov2740 *ov2740, u32 pattern)
@@ -805,7 +819,9 @@ static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_DIGITAL_GAIN:
- ret = ov2740_update_digital_gain(ov2740, ctrl->val);
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ ret = ov2740_update_mwb_gains(ov2740);
break;
case V4L2_CID_EXPOSURE:
@@ -846,7 +862,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
int ret;
ctrl_hdlr = &ov2740->ctrl_handler;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
if (ret)
return ret;
@@ -881,9 +897,22 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OV2740_ANAL_GAIN_MIN, OV2740_ANAL_GAIN_MAX,
OV2740_ANAL_GAIN_STEP, OV2740_ANAL_GAIN_MIN);
- v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
- OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
- OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->digital_gain =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN,
+ OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
+ OV2740_DGTL_GAIN_STEP,
+ OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->red_balance =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_RED_BALANCE,
+ 1, OV2740_DGTL_GAIN_MAX, 1,
+ OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->blue_balance =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE,
+ 1, OV2740_DGTL_GAIN_MAX, 1,
+ OV2740_DGTL_GAIN_DEFAULT);
exposure_max = ov2740->cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_EXPOSURE,
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (5 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 6/8] media: ov2740: add manual white balance controls Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 14:43 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 8/8] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
8 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
AtomISP currently rejects all raw formats and silently substitutes
YUV420. This prevents userspace camera processing stacks from obtaining
unprocessed sensor frames.
Add an allow_raw_output module parameter, disabled by default. When it is
enabled, enumerate only the raw format matching the sensor media-bus code
and reconcile raw format requests with the code selected by the sensor.
Also apply the per-sensor padding discovered by the CSI-2 bridge when
enumerating and negotiating frame sizes. Existing systems retain the
global padding and raw-output defaults.
Tested on a Lenovo Yoga Book YB1-X91L with raw capture from its OV2740
and OV8858 sensors.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
.../staging/media/atomisp/pci/atomisp_cmd.c | 24 ++++++++++++++-----
.../media/atomisp/pci/atomisp_internal.h | 6 +++++
.../staging/media/atomisp/pci/atomisp_ioctl.c | 13 ++++++----
.../staging/media/atomisp/pci/atomisp_v4l2.c | 15 ++++++++++++
4 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 6cd500d9fd26..9e6387b662bc 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -3572,10 +3572,14 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
u32 min_pad_w = ISP2400_MIN_PAD_W;
u32 min_pad_h = ISP2400_MIN_PAD_H;
struct v4l2_mbus_framefmt *sink;
+ u32 input_padding_w = input->padding_override ?
+ input->padding_w : pad_w;
+ u32 input_padding_h = input->padding_override ?
+ input->padding_h : pad_h;
if (!input->crop_support) {
- *padding_w = pad_w;
- *padding_h = pad_h;
+ *padding_w = input_padding_w;
+ *padding_h = input_padding_h;
return;
}
@@ -3588,8 +3592,10 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
native_rect.height /= 2;
}
- *padding_w = min_t(u32, (native_rect.width - width) & ~1, pad_w);
- *padding_h = min_t(u32, (native_rect.height - height) & ~1, pad_h);
+ *padding_w = min_t(u32, (native_rect.width - width) & ~1,
+ input_padding_w);
+ *padding_h = min_t(u32, (native_rect.height - height) & ~1,
+ input_padding_h);
/* The below minimum padding requirements are for BYT / ISP2400 only */
if (IS_ISP2401)
@@ -3804,8 +3810,9 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
int ret;
fmt = atomisp_get_format_bridge(f->pixelformat);
- /* Currently, raw formats are broken!!! */
- if (!fmt || fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ /* Raw output remains opt-in while the legacy CSS path is validated. */
+ if (!fmt || (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ !atomisp_allow_raw_output)) {
f->pixelformat = V4L2_PIX_FMT_YUV420;
fmt = atomisp_get_format_bridge(f->pixelformat);
@@ -3846,6 +3853,11 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
ffmt.code);
return -EINVAL;
}
+ if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ fmt->mbus_code != snr_fmt->mbus_code) {
+ fmt = snr_fmt;
+ f->pixelformat = fmt->pixelformat;
+ }
f->width = ffmt.width - padding_w;
f->height = ffmt.height - padding_h;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h
index 5a69580b8251..99a55297033e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
@@ -98,6 +98,9 @@
#define ATOMISP_CSS_SUPPORT_YUVPP 1
#define ATOMISP_CSS_OUTPUT_SECOND_INDEX 1
+
+extern bool atomisp_allow_raw_output;
+
#define ATOMISP_CSS_OUTPUT_DEFAULT_INDEX 0
/* ISP2401 */
@@ -116,6 +119,9 @@
struct atomisp_input_subdev {
enum atomisp_camera_port port;
u32 code; /* MEDIA_BUS_FMT_* */
+ u32 padding_w;
+ u32 padding_h;
+ bool padding_override;
bool binning_support;
bool crop_support;
bool sensor_on;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index 50366bf10f32..e63b3e99e5fd 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -554,8 +554,10 @@ static int atomisp_enum_framesizes(struct file *file, void *priv,
return ret;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.max_width - pad_w;
- fsize->discrete.height = fse.max_height - pad_h;
+ fsize->discrete.width = fse.max_width -
+ (input->padding_override ? input->padding_w : pad_w);
+ fsize->discrete.height = fse.max_height -
+ (input->padding_override ? input->padding_h : pad_h);
return 0;
}
@@ -629,8 +631,11 @@ static int atomisp_enum_fmt_cap(struct file *file, void *fh,
*
* FIXME: fix the pipeline to allow sensor format too.
*/
- if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW)
- continue;
+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ if (!atomisp_allow_raw_output ||
+ format->mbus_code != code.code)
+ continue;
+ }
/* Found a match. Now let's pick f->index'th one. */
if (fi < f->index) {
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 812230397409..d30f69f5a794 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -25,6 +25,7 @@
#include "atomisp_cmd.h"
#include "atomisp_common.h"
+#include "atomisp_csi2.h"
#include "atomisp_fops.h"
#include "atomisp_ioctl.h"
#include "atomisp_internal.h"
@@ -72,6 +73,11 @@ int pad_h = 16;
module_param(pad_h, int, 0644);
MODULE_PARM_DESC(pad_h, "extra data for ISP processing");
+bool atomisp_allow_raw_output;
+module_param_named(allow_raw_output, atomisp_allow_raw_output, bool, 0644);
+MODULE_PARM_DESC(allow_raw_output,
+ "allow experimental raw Bayer output (default:false)");
+
/*
* FIXME: this is a hack to make easier to support ISP2401 variant.
* As a given system will either be ISP2401 or not, we can just use
@@ -889,6 +895,15 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
struct v4l2_subdev_state *try_sd_state, *act_sd_state;
int i, err;
+ input->padding_override =
+ atomisp_csi2_get_sensor_padding(input->sensor->dev,
+ &input->padding_w,
+ &input->padding_h);
+ if (input->padding_override)
+ dev_info(input->sensor->dev,
+ "using ISP input padding %ux%u\n",
+ input->padding_w, input->padding_h);
+
/*
* FIXME: Drivers are not supposed to use __v4l2_subdev_state_alloc()
* but atomisp needs this for try_fmt on its /dev/video# node since
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH 8/8] media: i2c: Add WV517S lens actuator driver
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (6 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture Maurizio Casciano
@ 2026-08-26 13:22 ` Maurizio Casciano
2026-08-27 12:30 ` Sakari Ailus
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
8 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-26 13:22 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
The Lenovo Yoga Book YB1-X91 rear camera contains a WV517S
voice-coil actuator. Add a V4L2 lens subdevice exposing the standard
10-bit FOCUS_ABSOLUTE control and the device ringing-control mode.
Tie register access to runtime PM so the IPU bridge sensor link keeps
shared power resources active. Propagate PM acquisition failures and
restore the drive mode and requested focus position after resume.
The register addresses and drive-mode value are derived from Intel's
GPL-2.0 WV517 driver. Retain its copyright notice.
Link: https://github.com/jekhor/yogabook-linux-android-kernel/blob/574bae692716f1b14093497bfab8a007fe8e460b/drivers/external_drivers/camera/drivers/media/i2c/wv517.c
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
MAINTAINERS | 1 +
drivers/media/i2c/Kconfig | 8 ++
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/wv517s.c | 199 +++++++++++++++++++++++++++++++++++++
4 files changed, 209 insertions(+)
create mode 100644 drivers/media/i2c/wv517s.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3785b8c1de0a..205a42646a7d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28268,6 +28268,7 @@ S: Maintained
F: drivers/media/i2c/ak*
F: drivers/media/i2c/dw*
F: drivers/media/i2c/lm*
+F: drivers/media/i2c/wv517s.c
V4L2 CAMERA SENSOR DRIVERS
M: Sakari Ailus <sakari.ailus@linux.intel.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5c52007f9cbe..c488452c1b38 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -949,6 +949,14 @@ config VIDEO_DW9807_VCM
capability. This is designed for linear control of
voice coil motors, controlled via I2C serial interface.
+config VIDEO_WV517S
+ tristate "WV517S lens voice coil support"
+ help
+ This is a driver for the WV517S camera lens voice coil. It supports
+ the 10-bit focus control used by the Lenovo Yoga Book rear camera.
+ The driver exposes the actuator through the standard V4L2 lens
+ sub-device interface.
+
endif
menu "Flash devices"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d04bd5724552..e480932a9540 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -173,4 +173,5 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_WV517S) += wv517s.o
obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/
diff --git a/drivers/media/i2c/wv517s.c b/drivers/media/i2c/wv517s.c
new file mode 100644
index 000000000000..43d15aadea30
--- /dev/null
+++ b/drivers/media/i2c/wv517s.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * WV517S voice-coil motor driver
+ *
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * On the Lenovo Yoga Book the IPU bridge instantiates this actuator as a
+ * secondary I2C client of the rear camera. The bridge holds the sensor's
+ * shared power resources on while probing the actuator and adds a runtime-PM
+ * device link for subsequent accesses.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define WV517S_MAX_FOCUS_POSITION 1023
+#define WV517S_DEFAULT_FOCUS_POSITION 300
+
+#define WV517S_REG_FOCUS 0x41
+#define WV517S_REG_DRIVE_MODE 0x43
+#define WV517S_DRIVE_MODE_12_6_MS 0x0211
+
+struct wv517s_device {
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl *focus;
+};
+
+static inline struct wv517s_device *to_wv517s(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wv517s_device, sd);
+}
+
+static int wv517s_write(struct i2c_client *client, u8 reg, u16 value)
+{
+ u8 buf[] = { reg, value >> 8, value };
+ int ret;
+
+ ret = i2c_master_send(client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ return ret == sizeof(buf) ? 0 : -EIO;
+}
+
+static int wv517s_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct wv517s_device *wv517s = container_of(ctrl->handler,
+ struct wv517s_device,
+ ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&wv517s->sd);
+ int ret;
+
+ ret = pm_runtime_get_if_in_use(&client->dev);
+ if (ret <= 0)
+ return ret;
+
+ if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
+ ret = wv517s_write(client, WV517S_REG_FOCUS, ctrl->val);
+ else
+ ret = -EINVAL;
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops wv517s_ctrl_ops = {
+ .s_ctrl = wv517s_set_ctrl,
+};
+
+static int wv517s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return pm_runtime_resume_and_get(sd->dev);
+}
+
+static int wv517s_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ pm_runtime_put(sd->dev);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops wv517s_internal_ops = {
+ .open = wv517s_open,
+ .close = wv517s_close,
+};
+
+static const struct v4l2_subdev_ops wv517s_subdev_ops = { };
+
+static int wv517s_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct wv517s_device *wv517s = to_wv517s(sd);
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ /* Restore the vendor-recommended 12.6 ms ringing-control mode. */
+ ret = wv517s_write(client, WV517S_REG_DRIVE_MODE,
+ WV517S_DRIVE_MODE_12_6_MS);
+ if (ret)
+ return ret;
+
+ return wv517s_write(client, WV517S_REG_FOCUS, wv517s->focus->val);
+}
+
+static int wv517s_probe(struct i2c_client *client)
+{
+ struct wv517s_device *wv517s;
+ int ret;
+
+ wv517s = devm_kzalloc(&client->dev, sizeof(*wv517s), GFP_KERNEL);
+ if (!wv517s)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&wv517s->sd, client, &wv517s_subdev_ops);
+ wv517s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ wv517s->sd.internal_ops = &wv517s_internal_ops;
+ wv517s->sd.entity.function = MEDIA_ENT_F_LENS;
+
+ v4l2_ctrl_handler_init(&wv517s->ctrl_handler, 1);
+ wv517s->focus = v4l2_ctrl_new_std(&wv517s->ctrl_handler,
+ &wv517s_ctrl_ops,
+ V4L2_CID_FOCUS_ABSOLUTE, 0,
+ WV517S_MAX_FOCUS_POSITION, 1,
+ WV517S_DEFAULT_FOCUS_POSITION);
+ if (wv517s->ctrl_handler.error) {
+ ret = wv517s->ctrl_handler.error;
+ goto err_free_ctrl_handler;
+ }
+ wv517s->sd.ctrl_handler = &wv517s->ctrl_handler;
+
+ ret = media_entity_pads_init(&wv517s->sd.entity, 0, NULL);
+ if (ret)
+ goto err_free_ctrl_handler;
+
+ ret = wv517s_resume(&client->dev);
+ if (ret)
+ goto err_cleanup_entity;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ ret = v4l2_async_register_subdev(&wv517s->sd);
+ if (ret)
+ goto err_disable_pm;
+
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+err_disable_pm:
+ pm_runtime_disable(&client->dev);
+err_cleanup_entity:
+ media_entity_cleanup(&wv517s->sd.entity);
+err_free_ctrl_handler:
+ v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
+
+ return ret;
+}
+
+static void wv517s_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wv517s_device *wv517s = to_wv517s(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ pm_runtime_disable(&client->dev);
+ v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+}
+
+static const struct i2c_device_id wv517s_id_table[] = {
+ { "wv517s" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wv517s_id_table);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(wv517s_pm_ops, NULL, wv517s_resume, NULL);
+
+static struct i2c_driver wv517s_i2c_driver = {
+ .driver = {
+ .name = "wv517s",
+ .pm = pm_ptr(&wv517s_pm_ops),
+ },
+ .probe = wv517s_probe,
+ .remove = wv517s_remove,
+ .id_table = wv517s_id_table,
+};
+module_i2c_driver(wv517s_i2c_driver);
+
+MODULE_AUTHOR("Maurizio Casciano");
+MODULE_DESCRIPTION("WV517S VCM driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* RE: [PATCH 6/8] media: ov2740: add manual white balance controls
2026-08-26 13:22 ` [PATCH 6/8] media: ov2740: add manual white balance controls Maurizio Casciano
@ 2026-08-27 3:13 ` Cao, Bingbu
2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 14:32 ` Andy Shevchenko
1 sibling, 1 reply; 53+ messages in thread
From: Cao, Bingbu @ 2026-08-27 3:13 UTC (permalink / raw)
To: Maurizio Casciano, linux-media@vger.kernel.org
Cc: Mauro Carvalho Chehab, Sakari Ailus, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging@lists.linux.dev,
linux-kernel@vger.kernel.org
AMD General
Hi, Maurizio,
Thanks for the patch.
> -----Original Message-----
> From: Maurizio Casciano <mauriziocasciano7@gmail.com>
> Sent: Wednesday, August 26, 2026 9:23 PM
> To: linux-media@vger.kernel.org
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>; Sakari Ailus
> <sakari.ailus@linux.intel.com>; Cao, Bingbu <Bingbu.Cao@amd.com>; Jacopo
> Mondi <jacopo.mondi@ideasonboard.com>; Nicholas Roth
> <nicholas@rothemail.net>; Andy Shevchenko <andy@kernel.org>; Hans de Goede
> <hansg@kernel.org>; Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jose
> Maria Martin <jmmartinf@hotmail.com>; linux-staging@lists.linux.dev; linux-
> kernel@vger.kernel.org; Maurizio Casciano <mauriziocasciano7@gmail.com>
> Subject: [PATCH 6/8] media: ov2740: add manual white balance controls
>
> [You don't often get email from mauriziocasciano7@gmail.com. Learn why this
> is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> The sensor has separate red, green and blue manual white-balance gain
> registers, but the driver currently writes the same digital-gain value
> to all three channels. This prevents userspace from correcting the
> strong color cast of raw Bayer capture.
>
> Expose red- and blue-balance controls relative to the digital gain,
> update all three channels under group hold, and always release and
> launch the group even when a channel write fails.
>
> Tested on the Yoga Book OV2740 with live gain changes and continuous raw
> capture.
>
> Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
> Assisted-by: Codex:gpt-5.6-sol sparse
> ---
> drivers/media/i2c/ov2740.c | 71 +++++++++++++++++++++++++++-----------
> 1 file changed, 50 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
> index b760d4dc0e68..7651443b28c1 100644
> --- a/drivers/media/i2c/ov2740.c
> +++ b/drivers/media/i2c/ov2740.c
--snip--
> @@ -881,9 +897,22 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
> v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> V4L2_CID_ANALOGUE_GAIN,
> OV2740_ANAL_GAIN_MIN, OV2740_ANAL_GAIN_MAX,
> OV2740_ANAL_GAIN_STEP, OV2740_ANAL_GAIN_MIN);
> - v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> V4L2_CID_DIGITAL_GAIN,
> - OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
> - OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT);
> + ov2740->digital_gain =
> + v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> + V4L2_CID_DIGITAL_GAIN,
> + OV2740_DGTL_GAIN_MIN,
> OV2740_DGTL_GAIN_MAX,
> + OV2740_DGTL_GAIN_STEP,
> + OV2740_DGTL_GAIN_DEFAULT);
> + ov2740->red_balance =
> + v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> + V4L2_CID_RED_BALANCE,
> + 1, OV2740_DGTL_GAIN_MAX, 1,
> + OV2740_DGTL_GAIN_DEFAULT);
> + ov2740->blue_balance =
> + v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> + V4L2_CID_BLUE_BALANCE,
> + 1, OV2740_DGTL_GAIN_MAX, 1,
> + OV2740_DGTL_GAIN_DEFAULT);
Do we need a v4l2_ctrl_cluster() here?
> exposure_max = ov2740->cur_mode->vts_def -
> OV2740_EXPOSURE_MAX_MARGIN;
> ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
> V4L2_CID_EXPOSURE,
> --
> 2.53.0
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-26 13:22 ` [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
@ 2026-08-27 9:40 ` Andy Shevchenko
2026-08-27 11:59 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
0 siblings, 2 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 9:40 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Wed, Aug 26, 2026 at 03:22:49PM +0200, Maurizio Casciano wrote:
> The Yoga Book drives its OV8858 from a 19.2 MHz platform clock, while
> the existing mode tables program the sensor PLL for 24 MHz. Reusing
> those settings produces incorrect internal and CSI-2 clocks.
>
> Accept both input rates and use the actual rate for the reset delay. For
> 19.2 MHz, apply the Cherry Trail MRD PLL and black-level settings after
> the generic mode table.
>
> The 19.2 MHz platform uses the per-channel manual white-balance
> registers for digital gain. Program registers 0x5032, 0x5034 and 0x5036
> and expose their 1x-to-4x range, while retaining the existing
> long-exposure gain block for 24 MHz systems.
>
> The manual white-balance register definitions and programming follow
> the GPL-2.0 Intel OV5670 driver, so retain its 2017 Intel copyright
> notice in this file. No proprietary source or tuning binary is included.
>
> Tested on the Lenovo Yoga Book YB1-X91L OV8858 with full-range test bars
> and real 10-bit Bayer frames.
...
> #define OV8858_LINK_FREQ 360000000U
> -#define OV8858_XVCLK_FREQ 24000000
> +#define OV8858_XVCLK_FREQ_19_2MHZ 19200000
> +#define OV8858_XVCLK_FREQ_24MHZ 24000000
While at it, use HZ_PER_MHZ multiplier from units.h.
...
> #define OV8858_REG_LONG_DIGIGAIN OV8858_REG_16BIT(0x350a)
> #define OV8858_LONG_DIGIGAIN_H_MASK 0x3fc0
> #define OV8858_LONG_DIGIGAIN_L_MASK 0x3f
> #define OV8858_LONG_DIGIGAIN_H_SHIFT 2
> #define OV8858_LONG_DIGIGAIN_MIN 0x0
> #define OV8858_LONG_DIGIGAIN_MAX 0x3fff
> -#define OV8858_LONG_DIGIGAIN_STEP 1
> #define OV8858_LONG_DIGIGAIN_DEFAULT 0x200
>
> +#define OV8858_DIGITAL_GAIN_STEP 1
> +
What has been changed here? Why?
...
> struct ov8858 {
> struct clk *xvclk;
> + unsigned long xvclk_rate;
> struct gpio_desc *reset_gpio;
> struct gpio_desc *pwdn_gpio;
> struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
I understand the logic of location of a new field, but can you confirm with
`pahole` that this is optimal as well from alignment perspective?
> unsigned int num_lanes;
> };
...
> + /* The mode tables contain PLL settings for a 24 MHz input clock. */
> + if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
> + ret = ov8858_write_array(ov8858,
> + ov8858_cht_mrd_19_2mhz);
It's perfectly a single line.
> + if (ret)
> + return ret;
> + }
...
> +static int ov8858_set_digital_gain(struct ov8858 *ov8858, u32 gain)
> +{
> + u16 long_gain;
> + int ret;
> +
> + if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ) {
> + long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
> + ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
> + OV8858_LONG_DIGIGAIN_H_SHIFT);
I don't see the usefulness of these MASKs and SHIFTs in this case.
Can't we simply
#define OV8858_LONG_DIGIGAIN_MASK
(OV8858_LONG_DIGIGAIN_L_MASK | OV8858_LONG_DIGIGAIN_H_MASK)
and then
long_gain = gain & OV8858_LONG_DIGIGAIN_MASK;
?
> + return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
> + long_gain, NULL);
> + }
> +
> + ret = ov8858_write(ov8858, OV8858_REG_MWB_RED_GAIN, gain, NULL);
> + if (ret)
> + return ret;
> +
> + ret = ov8858_write(ov8858, OV8858_REG_MWB_GREEN_GAIN, gain, NULL);
> + if (ret)
> + return ret;
> +
> + return ov8858_write(ov8858, OV8858_REG_MWB_BLUE_GAIN, gain, NULL);
> +}
...
> struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
> struct v4l2_mbus_framefmt *format;
> struct v4l2_subdev_state *state;
> - u16 digi_gain;
> s64 max_exp;
> int ret;
>
> @@ -1570,17 +1647,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
> ctrl->val, NULL);
> break;
> case V4L2_CID_DIGITAL_GAIN:
> - /*
> - * Digital gain is assembled as:
> - * 0x350a[7:0] = dgain[13:6]
> - * 0x350b[5:0] = dgain[5:0]
> - * Reassemble the control value to write it in one go.
> - */
> - digi_gain = (ctrl->val & OV8858_LONG_DIGIGAIN_L_MASK)
> - | ((ctrl->val & OV8858_LONG_DIGIGAIN_H_MASK) <<
> - OV8858_LONG_DIGIGAIN_H_SHIFT);
> - ret = ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
> - digi_gain, NULL);
> + ret = ov8858_set_digital_gain(ov8858, ctrl->val);
> break;
So this part with the above can be split to a preparatory patch.
...
> - delay_us = DIV_ROUND_UP(8192, OV8858_XVCLK_FREQ / 1000 / 1000);
> + delay_us = DIV_ROUND_UP(8192, ov8858->xvclk_rate / 1000 / 1000);
Also HZ_PER_MHZ
...
> static int ov8858_init_ctrls(struct ov8858 *ov8858)
> struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
> const struct ov8858_mode *mode = &ov8858_modes[0];
> struct v4l2_fwnode_device_properties props;
> + u32 digital_gain_default = OV8858_LONG_DIGIGAIN_DEFAULT;
> + u32 digital_gain_max = OV8858_LONG_DIGIGAIN_MAX;
> + u32 digital_gain_min = OV8858_LONG_DIGIGAIN_MIN;
> s64 exposure_max, vblank_def;
> unsigned int pixel_rate;
> struct v4l2_ctrl *ctrl;
> OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
> OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
>
> + if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
> + digital_gain_min = OV8858_MWB_GAIN_MIN;
> + digital_gain_max = OV8858_MWB_GAIN_MAX;
> + digital_gain_default = OV8858_MWB_GAIN_DEFAULT;
> + }
Instead, use 'else' branch so all assignments are close to each other.
Also possible to avoid adding local variables.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-26 13:22 ` [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs Maurizio Casciano
@ 2026-08-27 9:58 ` Andy Shevchenko
2026-08-27 12:14 ` Sakari Ailus
` (2 more replies)
2026-08-27 12:13 ` Sakari Ailus
1 sibling, 3 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 9:58 UTC (permalink / raw)
To: Maurizio Casciano, Uwe Kleine-König
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Wed, Aug 26, 2026 at 03:22:50PM +0200, Maurizio Casciano wrote:
> The Lenovo Yoga Book YB1-X91 firmware enumerates the front OV2740 as
> OVTI2740 and the rear OV8858 as INT3477. Neither ID currently matches
> its sensor driver, so both I2C devices remain unbound.
>
> Add the IDs to the corresponding ACPI match tables. Sensor
> configuration and CSI lane quirks are kept in later patches.
For the record can you provide an DSDT excerpt of this device object (no need
to put into commit message, just reply here)?
...
> static const struct acpi_device_id ov2740_acpi_ids[] = {
> {"INT3474"},
> + {"OVTI2740"},
> {}
> };
Add a separate patch that converts these to use C99 initialisers and follows
the proper style of the formatting (added spaces, removed unneeded blank line).
static const struct acpi_device_id ov2740_acpi_ids[] = {
{ .id = "INT3474" },
{ }
};
MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids);
...
> +#include <linux/acpi.h>
Why?! We only use a single data structure definition which is defined in a
separate header. If this is what Uwe's rework required, I'm pretty much against.
We should not include the all-in-one acpi.h for the cases when we only use
the ID data structure.
I expect to see here linux/device-id/acpi.h.
> #include <linux/clk.h>
> #include <linux/delay.h>
> #include <linux/device.h>
...
> +static const struct acpi_device_id ov8858_acpi_match[] = {
> + { "INT3477" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(acpi, ov8858_acpi_match);
See above about the style. Also This patch should be split on per-driver basis.
...
> static struct i2c_driver ov8858_i2c_driver = {
> .driver = {
> .name = "ov8858",
> .pm = &ov8858_pm_ops,
> + .acpi_match_table = ACPI_PTR(ov8858_acpi_match),
This is wrong to have here (it will induce compiler warning). And in general —
no ACPI_PTR() in a new code.
> .of_match_table = ov8858_of_match,
> },
> .probe = ov8858_probe,
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 9:40 ` Andy Shevchenko
@ 2026-08-27 11:59 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
1 sibling, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2026-08-27 11:59 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Thu, Aug 27, 2026 at 12:40:40PM +0300, Andy Shevchenko wrote:
> > static int ov8858_init_ctrls(struct ov8858 *ov8858)
>
> > struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
> > const struct ov8858_mode *mode = &ov8858_modes[0];
> > struct v4l2_fwnode_device_properties props;
> > + u32 digital_gain_default = OV8858_LONG_DIGIGAIN_DEFAULT;
> > + u32 digital_gain_max = OV8858_LONG_DIGIGAIN_MAX;
> > + u32 digital_gain_min = OV8858_LONG_DIGIGAIN_MIN;
> > s64 exposure_max, vblank_def;
> > unsigned int pixel_rate;
> > struct v4l2_ctrl *ctrl;
>
> > OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
> > OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
> >
> > + if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
> > + digital_gain_min = OV8858_MWB_GAIN_MIN;
> > + digital_gain_max = OV8858_MWB_GAIN_MAX;
> > + digital_gain_default = OV8858_MWB_GAIN_DEFAULT;
> > + }
>
> Instead, use 'else' branch so all assignments are close to each other.
> Also possible to avoid adding local variables.
Or better yet, put them into an array and pick and index based on the
frequency of the clock.
--
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-26 13:22 ` [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs Maurizio Casciano
2026-08-27 9:58 ` Andy Shevchenko
@ 2026-08-27 12:13 ` Sakari Ailus
1 sibling, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2026-08-27 12:13 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
Hi Maurizio,
Thank you for the set.
On Wed, Aug 26, 2026 at 03:22:50PM +0200, Maurizio Casciano wrote:
> The Lenovo Yoga Book YB1-X91 firmware enumerates the front OV2740 as
> OVTI2740 and the rear OV8858 as INT3477. Neither ID currently matches
> its sensor driver, so both I2C devices remain unbound.
>
> Add the IDs to the corresponding ACPI match tables. Sensor
> configuration and CSI lane quirks are kept in later patches.
>
> Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
> Assisted-by: Codex:gpt-5.6-sol sparse
Please split this into two, a patch per driver.
What is relevant here that ACPI IDs are added, not so much where these IDs
are being used (that goes to the cover letter).
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-27 9:58 ` Andy Shevchenko
@ 2026-08-27 12:14 ` Sakari Ailus
2026-08-27 12:46 ` Andy Shevchenko
2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 23:18 ` Maurizio Casciano
2 siblings, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2026-08-27 12:14 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, Uwe Kleine-König, linux-media,
Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 12:58:31PM +0300, Andy Shevchenko wrote:
> On Wed, Aug 26, 2026 at 03:22:50PM +0200, Maurizio Casciano wrote:
> > The Lenovo Yoga Book YB1-X91 firmware enumerates the front OV2740 as
> > OVTI2740 and the rear OV8858 as INT3477. Neither ID currently matches
> > its sensor driver, so both I2C devices remain unbound.
> >
> > Add the IDs to the corresponding ACPI match tables. Sensor
> > configuration and CSI lane quirks are kept in later patches.
>
> For the record can you provide an DSDT excerpt of this device object (no need
> to put into commit message, just reply here)?
Typically the information comes from BIOS (EFI variables?) so the DSDT
nor SSDTs have this information as such.
--
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 8/8] media: i2c: Add WV517S lens actuator driver
2026-08-26 13:22 ` [PATCH 8/8] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
@ 2026-08-27 12:30 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
0 siblings, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2026-08-27 12:30 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
Hi Maurizio,
On Wed, Aug 26, 2026 at 03:22:56PM +0200, Maurizio Casciano wrote:
> The Lenovo Yoga Book YB1-X91 rear camera contains a WV517S
> voice-coil actuator. Add a V4L2 lens subdevice exposing the standard
> 10-bit FOCUS_ABSOLUTE control and the device ringing-control mode.
>
> Tie register access to runtime PM so the IPU bridge sensor link keeps
> shared power resources active. Propagate PM acquisition failures and
> restore the drive mode and requested focus position after resume.
>
> The register addresses and drive-mode value are derived from Intel's
> GPL-2.0 WV517 driver. Retain its copyright notice.
>
> Link: https://github.com/jekhor/yogabook-linux-android-kernel/blob/574bae692716f1b14093497bfab8a007fe8e460b/drivers/external_drivers/camera/drivers/media/i2c/wv517.c
>
> Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
> Assisted-by: Codex:gpt-5.6-sol sparse
> ---
> MAINTAINERS | 1 +
> drivers/media/i2c/Kconfig | 8 ++
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/wv517s.c | 199 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 209 insertions(+)
> create mode 100644 drivers/media/i2c/wv517s.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3785b8c1de0a..205a42646a7d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -28268,6 +28268,7 @@ S: Maintained
> F: drivers/media/i2c/ak*
> F: drivers/media/i2c/dw*
> F: drivers/media/i2c/lm*
> +F: drivers/media/i2c/wv517s.c
>
> V4L2 CAMERA SENSOR DRIVERS
> M: Sakari Ailus <sakari.ailus@linux.intel.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 5c52007f9cbe..c488452c1b38 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -949,6 +949,14 @@ config VIDEO_DW9807_VCM
> capability. This is designed for linear control of
> voice coil motors, controlled via I2C serial interface.
>
> +config VIDEO_WV517S
> + tristate "WV517S lens voice coil support"
> + help
> + This is a driver for the WV517S camera lens voice coil. It supports
> + the 10-bit focus control used by the Lenovo Yoga Book rear camera.
> + The driver exposes the actuator through the standard V4L2 lens
> + sub-device interface.
The Lenovo model where the VCM exists belongs to the cover letter, not
here.
> +
> endif
>
> menu "Flash devices"
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index d04bd5724552..e480932a9540 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -173,4 +173,5 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
> obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
> obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
> obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
> +obj-$(CONFIG_VIDEO_WV517S) += wv517s.o
> obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/
> diff --git a/drivers/media/i2c/wv517s.c b/drivers/media/i2c/wv517s.c
> new file mode 100644
> index 000000000000..43d15aadea30
> --- /dev/null
> +++ b/drivers/media/i2c/wv517s.c
> @@ -0,0 +1,199 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + * WV517S voice-coil motor driver
> + *
> + * Copyright (c) 2014 Intel Corporation.
Are there no other copyrights since 2014?
> + *
> + * On the Lenovo Yoga Book the IPU bridge instantiates this actuator as a
> + * secondary I2C client of the rear camera. The bridge holds the sensor's
> + * shared power resources on while probing the actuator and adds a runtime-PM
> + * device link for subsequent accesses.
Same for this bit.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +
> +#define WV517S_MAX_FOCUS_POSITION 1023
> +#define WV517S_DEFAULT_FOCUS_POSITION 300
> +
> +#define WV517S_REG_FOCUS 0x41
> +#define WV517S_REG_DRIVE_MODE 0x43
> +#define WV517S_DRIVE_MODE_12_6_MS 0x0211
> +
> +struct wv517s_device {
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct v4l2_subdev sd;
> + struct v4l2_ctrl *focus;
> +};
> +
> +static inline struct wv517s_device *to_wv517s(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct wv517s_device, sd);
> +}
> +
> +static int wv517s_write(struct i2c_client *client, u8 reg, u16 value)
> +{
> + u8 buf[] = { reg, value >> 8, value };
> + int ret;
> +
> + ret = i2c_master_send(client, buf, sizeof(buf));
Could you use regmap?
> + if (ret < 0)
> + return ret;
> +
> + return ret == sizeof(buf) ? 0 : -EIO;
> +}
> +
> +static int wv517s_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct wv517s_device *wv517s = container_of(ctrl->handler,
> + struct wv517s_device,
> + ctrl_handler);
> + struct i2c_client *client = v4l2_get_subdevdata(&wv517s->sd);
> + int ret;
> +
> + ret = pm_runtime_get_if_in_use(&client->dev);
pm_runtime_get_if_active().
> + if (ret <= 0)
Test for !ret. If Runtime PM is disabled, ret is an error as well.
> + return ret;
> +
> + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
> + ret = wv517s_write(client, WV517S_REG_FOCUS, ctrl->val);
> + else
> + ret = -EINVAL;
> +
> + pm_runtime_put(&client->dev);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops wv517s_ctrl_ops = {
> + .s_ctrl = wv517s_set_ctrl,
> +};
> +
> +static int wv517s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> + return pm_runtime_resume_and_get(sd->dev);
> +}
> +
> +static int wv517s_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> + pm_runtime_put(sd->dev);
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops wv517s_internal_ops = {
> + .open = wv517s_open,
> + .close = wv517s_close,
> +};
> +
> +static const struct v4l2_subdev_ops wv517s_subdev_ops = { };
> +
> +static int wv517s_resume(struct device *dev)
> +{
> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
> + struct wv517s_device *wv517s = to_wv517s(sd);
> + struct i2c_client *client = to_i2c_client(dev);
> + int ret;
> +
> + /* Restore the vendor-recommended 12.6 ms ringing-control mode. */
> + ret = wv517s_write(client, WV517S_REG_DRIVE_MODE,
> + WV517S_DRIVE_MODE_12_6_MS);
> + if (ret)
> + return ret;
> +
> + return wv517s_write(client, WV517S_REG_FOCUS, wv517s->focus->val);
You could use v4l2_ctrl_handler_setup() for this and drop the focus field.
> +}
> +
> +static int wv517s_probe(struct i2c_client *client)
> +{
> + struct wv517s_device *wv517s;
> + int ret;
> +
> + wv517s = devm_kzalloc(&client->dev, sizeof(*wv517s), GFP_KERNEL);
> + if (!wv517s)
> + return -ENOMEM;
> +
> + v4l2_i2c_subdev_init(&wv517s->sd, client, &wv517s_subdev_ops);
> + wv517s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + wv517s->sd.internal_ops = &wv517s_internal_ops;
> + wv517s->sd.entity.function = MEDIA_ENT_F_LENS;
> +
> + v4l2_ctrl_handler_init(&wv517s->ctrl_handler, 1);
> + wv517s->focus = v4l2_ctrl_new_std(&wv517s->ctrl_handler,
> + &wv517s_ctrl_ops,
> + V4L2_CID_FOCUS_ABSOLUTE, 0,
> + WV517S_MAX_FOCUS_POSITION, 1,
> + WV517S_DEFAULT_FOCUS_POSITION);
> + if (wv517s->ctrl_handler.error) {
> + ret = wv517s->ctrl_handler.error;
> + goto err_free_ctrl_handler;
> + }
> + wv517s->sd.ctrl_handler = &wv517s->ctrl_handler;
> +
> + ret = media_entity_pads_init(&wv517s->sd.entity, 0, NULL);
> + if (ret)
> + goto err_free_ctrl_handler;
> +
> + ret = wv517s_resume(&client->dev);
> + if (ret)
> + goto err_cleanup_entity;
> +
> + pm_runtime_set_active(&client->dev);
> + pm_runtime_enable(&client->dev);
> +
> + ret = v4l2_async_register_subdev(&wv517s->sd);
> + if (ret)
> + goto err_disable_pm;
> +
> + pm_runtime_idle(&client->dev);
> +
> + return 0;
> +
> +err_disable_pm:
> + pm_runtime_disable(&client->dev);
> +err_cleanup_entity:
> + media_entity_cleanup(&wv517s->sd.entity);
> +err_free_ctrl_handler:
> + v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
> +
> + return ret;
> +}
> +
> +static void wv517s_remove(struct i2c_client *client)
> +{
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct wv517s_device *wv517s = to_wv517s(sd);
> +
> + v4l2_async_unregister_subdev(sd);
> + pm_runtime_disable(&client->dev);
> + v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
> + media_entity_cleanup(&sd->entity);
> +}
> +
> +static const struct i2c_device_id wv517s_id_table[] = {
> + { "wv517s" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, wv517s_id_table);
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(wv517s_pm_ops, NULL, wv517s_resume, NULL);
> +
> +static struct i2c_driver wv517s_i2c_driver = {
> + .driver = {
> + .name = "wv517s",
> + .pm = pm_ptr(&wv517s_pm_ops),
> + },
> + .probe = wv517s_probe,
> + .remove = wv517s_remove,
> + .id_table = wv517s_id_table,
> +};
> +module_i2c_driver(wv517s_i2c_driver);
> +
> +MODULE_AUTHOR("Maurizio Casciano");
> +MODULE_DESCRIPTION("WV517S VCM driver");
> +MODULE_LICENSE("GPL");
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-27 12:14 ` Sakari Ailus
@ 2026-08-27 12:46 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 12:46 UTC (permalink / raw)
To: Sakari Ailus
Cc: Maurizio Casciano, Uwe Kleine-König, linux-media,
Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 03:14:34PM +0300, Sakari Ailus wrote:
> On Thu, Aug 27, 2026 at 12:58:31PM +0300, Andy Shevchenko wrote:
> > On Wed, Aug 26, 2026 at 03:22:50PM +0200, Maurizio Casciano wrote:
> > > The Lenovo Yoga Book YB1-X91 firmware enumerates the front OV2740 as
> > > OVTI2740 and the rear OV8858 as INT3477. Neither ID currently matches
> > > its sensor driver, so both I2C devices remain unbound.
> > >
> > > Add the IDs to the corresponding ACPI match tables. Sensor
> > > configuration and CSI lane quirks are kept in later patches.
> >
> > For the record can you provide an DSDT excerpt of this device object (no need
> > to put into commit message, just reply here)?
>
> Typically the information comes from BIOS (EFI variables?) so the DSDT
> nor SSDTs have this information as such.
I am asking to see it in general, then to see that the IDs there.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link
2026-08-26 13:22 ` [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
@ 2026-08-27 14:26 ` Andy Shevchenko
2026-08-27 23:18 ` Maurizio Casciano
0 siblings, 1 reply; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 14:26 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Wed, Aug 26, 2026 at 03:22:53PM +0200, Maurizio Casciano wrote:
> The YB1-X91L firmware does not describe the complete camera link. Its
> front OV2740 uses two CSI-2 lanes at a 288 MHz link frequency and sends
> a 1932x1092 BGGR transport frame with 12 pixels of horizontal and
> vertical padding around the 1920x1080 image.
>
> Allow the AtomISP bridge to provide per-sensor link frequencies and
> padding, add the matching OV2740 mode, and derive ISP2401 D-PHY timing
> from the sensor link-frequency control.
>
> The register values are hardware configuration facts checked against
> the Lenovo YB1-X91L configuration and physical captures; no proprietary
> driver code or tuning binary is included.
>
> Tested on a Lenovo Yoga Book YB1-X91L with continuous front-camera raw
> capture.
...
> #define OV2740_LINK_FREQ_360MHZ 360000000ULL
> +#define OV2740_LINK_FREQ_288MHZ 288000000ULL
> #define OV2740_LINK_FREQ_180MHZ 180000000ULL
At some point would be good to use HZ_PER_MHZ multiplier from units.h.
...
> struct ov2740_mode {
> /* Link frequency needed for this resolution */
> u32 link_freq_index;
>
> + /* Optional common settings applied before the mode-specific settings */
> + const struct ov2740_reg_list init_reg_list;
> +
> /* Sensor register settings for this resolution */
> const struct ov2740_reg_list reg_list;
> +
> + /* Bayer order produced by this mode */
> + u32 code;
Check with `pahole`.
> };
...
> +++ b/drivers/media/pci/intel/ipu-bridge.c
> @@ -520,11 +520,16 @@ static void ipu_bridge_create_fwnode_properties(
> sensor->prop_names.remote_endpoint,
> sensor->local_ref);
>
> - if (cfg->nr_link_freqs > 0)
> - sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
> - sensor->prop_names.link_frequencies,
> - cfg->link_freqs,
> - cfg->nr_link_freqs);
> + if (sensor->nr_link_freqs > 0)
> + sensor->ep_properties[3] =
> + PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
> + sensor->link_freqs,
> + sensor->nr_link_freqs);
> + else if (cfg->nr_link_freqs > 0)
> + sensor->ep_properties[3] =
> + PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
> + cfg->link_freqs,
> + cfg->nr_link_freqs);
Why do we need this? The sensors are listed in the supported ones in the IPU
bridge code. Also, if required, some data can be altered by using driver_data
field in ACPI ID table.
...
> +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
> int atomisp_csi2_bridge_init(struct atomisp_device *isp);
> int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp);
> +bool atomisp_csi2_get_sensor_padding(struct device *dev, u32 *padding_w,
> + u32 *padding_h);
Better to split logically.
bool atomisp_csi2_get_sensor_padding(struct device *dev,
u32 *padding_w, u32 *padding_h);
> void atomisp_csi2_configure(struct atomisp_sub_device *asd);
...
> +bool atomisp_csi2_get_sensor_padding(struct device *dev, u32 *padding_w,
> + u32 *padding_h)
> +{
Ditto.
> + struct acpi_device *adev = ACPI_COMPANION(dev);
Split the assignment. The current way is prone to subtle mistakes in the
future.
> + bool override = false;
> + char *str_val;
> + unsigned int val;
Try to use reversed xmas tree order.
> + *padding_w = pad_w;
> + *padding_h = pad_h;
adev = ACPI_COMPANION(dev);
> + if (!adev)
> + return false;
> +
> + str_val = gmin_cfg_get(adev, "CsiPaddingWidth");
> + if (str_val) {
> + if (!kstrtouint(str_val, 0, &val) && val <= 64 && !(val & 1)) {
> + *padding_w = val;
> + override = true;
> + } else {
> + acpi_handle_warn(adev->handle,
> + "%s: Invalid CSI padding width %s\n",
> + dev_name(dev), str_val);
> + }
> + kfree(str_val);
> + }
> +
> + str_val = gmin_cfg_get(adev, "CsiPaddingHeight");
> + if (str_val) {
> + if (!kstrtouint(str_val, 0, &val) && val <= 64 && !(val & 1)) {
> + *padding_h = val;
> + override = true;
> + } else {
> + acpi_handle_warn(adev->handle,
> + "%s: Invalid CSI padding height %s\n",
> + dev_name(dev), str_val);
> + }
> + kfree(str_val);
> + }
This is dup of the code, please create a helper to deduplicate these.
Also, what's wrong with gmin_cfg_get_int()?
> + return override;
> +}
...
> {
> const struct acpi_device_id *id;
> + char *link_freq_str;
> + unsigned long long link_freq;
Reversed xmas tree order.
> int ret, clock_num;
> bool vcm = false;
> int lanes = 1;
...
> + link_freq_str = gmin_cfg_get(adev, "CsiLinkFreq");
> + if (link_freq_str) {
> + ret = kstrtoull(link_freq_str, 0, &link_freq);
> + kfree(link_freq_str);
> + if (ret || !link_freq) {
> + acpi_handle_err(adev->handle,
> + "%s: Invalid CSI link frequency\n",
> + dev_name(&adev->dev));
> + return ret ?: -EINVAL;
> + }
gmin_cfg_get_int()
> + sensor->link_freqs[0] = link_freq;
> + sensor->nr_link_freqs = 1;
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 6/8] media: ov2740: add manual white balance controls
2026-08-26 13:22 ` [PATCH 6/8] media: ov2740: add manual white balance controls Maurizio Casciano
2026-08-27 3:13 ` Cao, Bingbu
@ 2026-08-27 14:32 ` Andy Shevchenko
1 sibling, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 14:32 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Wed, Aug 26, 2026 at 03:22:54PM +0200, Maurizio Casciano wrote:
> The sensor has separate red, green and blue manual white-balance gain
> registers, but the driver currently writes the same digital-gain value
> to all three channels. This prevents userspace from correcting the
> strong color cast of raw Bayer capture.
>
> Expose red- and blue-balance controls relative to the digital gain,
> update all three channels under group hold, and always release and
> launch the group even when a channel write fails.
>
> Tested on the Yoga Book OV2740 with live gain changes and continuous raw
> capture.
...
> -static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain)
> +static int ov2740_update_mwb_gains(struct ov2740 *ov2740)
> {
> - int ret;
> + u32 green_gain = ov2740->digital_gain->val;
> + u32 red_gain, blue_gain;
Split assignment and combine all three on a single line.
Also, why not u64 from the start?
> + int end_ret, launch_ret, ret;
> +
> + /* Balance controls use 1024 as unity relative to the digital gain. */
> + red_gain = min_t(u64,
No min_t() in the new code. This macro is very exceptional.
> + DIV_ROUND_CLOSEST_ULL((u64)green_gain *
> + ov2740->red_balance->val,
> + OV2740_DGTL_GAIN_DEFAULT),
Is _ULL variant really required? What are the ranges of the _gain and ->val?
> + OV2740_DGTL_GAIN_MAX);
> + blue_gain = min_t(u64,
> + DIV_ROUND_CLOSEST_ULL((u64)green_gain *
> + ov2740->blue_balance->val,
> + OV2740_DGTL_GAIN_DEFAULT),
> + OV2740_DGTL_GAIN_MAX);
>
> ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> OV2740_GROUP_HOLD_START);
> if (ret)
> return ret;
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain);
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, red_gain);
> if (ret)
> - return ret;
> + goto release_group;
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, d_gain);
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, green_gain);
> if (ret)
> - return ret;
> + goto release_group;
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
> - if (ret)
> - return ret;
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, blue_gain);
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> - OV2740_GROUP_HOLD_END);
> - if (ret)
> - return ret;
> +release_group:
> + end_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> + OV2740_GROUP_HOLD_END);
> + launch_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> + OV2740_GROUP_HOLD_LAUNCH);
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> - OV2740_GROUP_HOLD_LAUNCH);
> - return ret;
> + return ret ?: end_ret ?: launch_ret;
To make this looking better it might be good to convert the driver to use CCI
accessors [1]. Maybe Hans knows more about this as he worked a lot on this drivers
and sensors in the past.
> }
[1]: include/media/v4l2-cci.h
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture
2026-08-26 13:22 ` [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture Maurizio Casciano
@ 2026-08-27 14:43 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 14:43 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel
On Wed, Aug 26, 2026 at 03:22:55PM +0200, Maurizio Casciano wrote:
> AtomISP currently rejects all raw formats and silently substitutes
> YUV420. This prevents userspace camera processing stacks from obtaining
> unprocessed sensor frames.
>
> Add an allow_raw_output module parameter, disabled by default. When it is
> enabled, enumerate only the raw format matching the sensor media-bus code
> and reconcile raw format requests with the code selected by the sensor.
>
> Also apply the per-sensor padding discovered by the CSI-2 bridge when
> enumerating and negotiating frame sizes. Existing systems retain the
> global padding and raw-output defaults.
>
> Tested on a Lenovo Yoga Book YB1-X91L with raw capture from its OV2740
> and OV8858 sensors.
...
> u32 min_pad_w = ISP2400_MIN_PAD_W;
> u32 min_pad_h = ISP2400_MIN_PAD_H;
> struct v4l2_mbus_framefmt *sink;
> + u32 input_padding_w = input->padding_override ?
> + input->padding_w : pad_w;
> + u32 input_padding_h = input->padding_override ?
> + input->padding_h : pad_h;
It's the same condition, split to if-else.
if (input->padding_override) {
input_padding_w = input->padding_w;
input_padding_h = input->padding_h;
} else {
input_padding_w = pad_w;
input_padding_h = pad_h;
}
> if (!input->crop_support) {
> - *padding_w = pad_w;
> - *padding_h = pad_h;
> + *padding_w = input_padding_w;
> + *padding_h = input_padding_h;
> return;
> }
Or maybe you can update pad_w and pad_h instead and leave this and might be
other code alone?
...
> +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
> #define ATOMISP_CSS_SUPPORT_YUVPP 1
>
> #define ATOMISP_CSS_OUTPUT_SECOND_INDEX 1
> +
> +extern bool atomisp_allow_raw_output;
> +
Location of this doesn't look right. Find a better one.
> #define ATOMISP_CSS_OUTPUT_DEFAULT_INDEX 0
...
> struct atomisp_input_subdev {
> enum atomisp_camera_port port;
> u32 code; /* MEDIA_BUS_FMT_* */
> + u32 padding_w;
> + u32 padding_h;
Why not struct v4l2_area?
> + bool padding_override;
> bool binning_support;
> bool crop_support;
> bool sensor_on;
...
> - if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW)
> - continue;
> + if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
> + if (!atomisp_allow_raw_output ||
> + format->mbus_code != code.code)
> + continue;
> + }
if (a) { if (b) { ... }} is equivalent to if (a && b) { ... }.
...
> +bool atomisp_allow_raw_output;
> +module_param_named(allow_raw_output, atomisp_allow_raw_output, bool, 0644);
> +MODULE_PARM_DESC(allow_raw_output,
> + "allow experimental raw Bayer output (default:false)");
Why do we need this? Can it be enabled a run-time via IOCTL or other means
of ABI?
...
> + input->padding_override =
> + atomisp_csi2_get_sensor_padding(input->sensor->dev,
> + &input->padding_w,
> + &input->padding_h);
Make a pointer to a struct v4l2_area to be filled by this function.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (7 preceding siblings ...)
2026-08-26 13:22 ` [PATCH 8/8] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 01/11] media: ov8858: Extract digital gain programming Maurizio Casciano
` (11 more replies)
8 siblings, 12 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The Lenovo Yoga Book YB1-X91L exposes an OV2740 front sensor and an
OV8858 rear sensor through the Cherry Trail AtomISP. Its firmware does
not provide enough information for the existing sensor and IPU bridge
drivers to construct the complete camera links.
Add the ACPI IDs and bridge data, describe both AtomISP camera links,
support the Yoga Book sensor clocks and modes, expose raw Bayer capture,
add per-channel white-balance controls, and add the WV517S rear-camera
lens actuator.
Raw capture is selected through the normal V4L2 raw pixel formats. There
is no module parameter or separate private ABI. Processed formats retain
the existing behavior, while raw formats expose the complete CSI-2
transport frames required by the ISP2401 copy pipeline.
Changes since v1:
- split the sensor ACPI IDs into one patch per driver, add a preparatory
C99-initializer cleanup, avoid ACPI_PTR, and retain the existing acpi.h
include because ov2740 already calls acpi_dev_state_d0();
- split the existing OV8858 digital-gain programming into a preparatory
patch and simplify the 19.2 MHz clock/gain changes;
- use standard frequency units, cleaner declaration ordering and bridge
quirk data instead of duplicated firmware-string parsing;
- remove the allow_raw_output module parameter and use normal VIDIOC_S_FMT
raw-format selection;
- represent sensor padding with struct v4l2_area, simplify the raw-format
conditions, and expose full raw transport dimensions;
- simplify the OV2740 white-balance arithmetic, cluster the three gain
controls, and apply them together during runtime resume; and
- convert WV517S register access to regmap, correct runtime-PM handling,
restore controls on resume, and clarify copyright and board comments.
The series is based on mainline at 73e3f0710014. All patches pass strict
checkpatch with no errors, warnings or checks. Pahole reports a 64-byte
ov2740_mode with no holes and a 792-byte ov8858 with the new clock-rate
field naturally aligned. A clean 20-job bindeb-pkg build containing the
same final source state completed successfully.
Runtime testing used a Lenovo Yoga Book YB1-X91L. Both sensors, AtomISP
and WV517S bound. Focus position 300 was accepted. Captures passed with:
- front raw BG10: 1932x1092, 4472832 bytes;
- rear raw BG10: 3264x2448, 16293888 bytes;
- front processed YU12: 1920x1080; and
- rear processed JPEG: 3248x2432, 1530647 bytes.
The raw format was selected and streamed in the same v4l2-ctl invocation,
as reopening the video node restores its default format. Both raw files
were non-empty and distinct, and the rear full-resolution JPEG completed
without AtomISP, sensor or runtime-PM errors.
Maurizio Casciano (11):
media: ov8858: Extract digital gain programming
media: ov8858: support 19.2 MHz clock and CHT gain setup
media: ov2740: Use C99 initializers for ACPI IDs
media: ov2740: Add OVTI2740 ACPI ID
media: ov8858: Add INT3477 ACPI ID
media: intel: ipu-bridge: Add Yoga Book camera sensors
media: atomisp: Add Yoga Book camera configuration
media: atomisp: support the Yoga Book OV2740 link
media: ov2740: add manual white balance controls
media: atomisp: allow raw Bayer capture
media: i2c: Add WV517S lens actuator driver
MAINTAINERS | 1 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov2740.c | 177 ++++++++++++---
drivers/media/i2c/ov8858.c | 156 ++++++++++++--
drivers/media/i2c/wv517s.c | 204 ++++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 19 +-
.../staging/media/atomisp/pci/atomisp_cmd.c | 33 ++-
.../staging/media/atomisp/pci/atomisp_csi2.c | 17 +-
.../staging/media/atomisp/pci/atomisp_csi2.h | 2 +
.../media/atomisp/pci/atomisp_csi2_bridge.c | 65 +++++-
.../media/atomisp/pci/atomisp_internal.h | 3 +
.../staging/media/atomisp/pci/atomisp_ioctl.c | 22 +-
.../staging/media/atomisp/pci/atomisp_v4l2.c | 9 +
include/media/ipu-bridge.h | 2 +
15 files changed, 648 insertions(+), 74 deletions(-)
create mode 100644 drivers/media/i2c/wv517s.c
base-commit: 73e3f0710014fe6d4ed98cfc02292f6121db7558
--
2.53.0
^ permalink raw reply [flat|nested] 53+ messages in thread
* [PATCH v2 01/11] media: ov8858: Extract digital gain programming
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
` (10 subsequent siblings)
11 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
Move the existing packed digital-gain register programming into a helper so
clock-specific gain handling can be added separately.
Assisted-by: Codex:gpt-5.6-sol sparse
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
---
drivers/media/i2c/ov8858.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index 3f45f7fab833..f0b3b4a5d3bd 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -1523,6 +1523,23 @@ static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern)
return ov8858_write(ov8858, OV8858_REG_TEST_PATTERN, val, NULL);
}
+static int ov8858_set_digital_gain(struct ov8858 *ov8858, u32 gain)
+{
+ u16 long_gain;
+
+ /*
+ * Digital gain is assembled as:
+ * 0x350a[7:0] = dgain[13:6]
+ * 0x350b[5:0] = dgain[5:0]
+ * Reassemble the control value to write it in one go.
+ */
+ long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
+ ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
+ OV8858_LONG_DIGIGAIN_H_SHIFT);
+
+ return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN, long_gain, NULL);
+}
+
static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov8858 *ov8858 = container_of(ctrl->handler,
@@ -1531,7 +1548,6 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
- u16 digi_gain;
s64 max_exp;
int ret;
@@ -1570,17 +1586,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
ctrl->val, NULL);
break;
case V4L2_CID_DIGITAL_GAIN:
- /*
- * Digital gain is assembled as:
- * 0x350a[7:0] = dgain[13:6]
- * 0x350b[5:0] = dgain[5:0]
- * Reassemble the control value to write it in one go.
- */
- digi_gain = (ctrl->val & OV8858_LONG_DIGIGAIN_L_MASK)
- | ((ctrl->val & OV8858_LONG_DIGIGAIN_H_MASK) <<
- OV8858_LONG_DIGIGAIN_H_SHIFT);
- ret = ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
- digi_gain, NULL);
+ ret = ov8858_set_digital_gain(ov8858, ctrl->val);
break;
case V4L2_CID_VBLANK:
ret = ov8858_write(ov8858, OV8858_REG_VTS,
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 01/11] media: ov8858: Extract digital gain programming Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 19:46 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs Maurizio Casciano
` (9 subsequent siblings)
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book drives its OV8858 from a 19.2 MHz platform clock, while
the existing mode tables program the sensor PLL for 24 MHz. Reusing
those settings produces incorrect internal and CSI-2 clocks.
Accept both input rates and use the actual rate for the reset delay. For
19.2 MHz, apply the Cherry Trail MRD PLL and black-level settings after
the generic mode table.
The 19.2 MHz platform uses the per-channel manual white-balance
registers for digital gain. Program registers 0x5032, 0x5034 and 0x5036
and expose their 1x-to-4x range, while retaining the existing
long-exposure gain block for 24 MHz systems.
The manual white-balance register definitions and programming follow
the GPL-2.0 Intel OV5670 driver, so retain its 2017 Intel copyright
notice in this file. No proprietary source or tuning binary is included.
Tested on the Lenovo Yoga Book YB1-X91L OV8858 with full-range test bars
and real 10-bit Bayer frames.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov8858.c | 138 +++++++++++++++++++++++++++++++------
1 file changed, 117 insertions(+), 21 deletions(-)
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index f0b3b4a5d3bd..b8c2f0d25505 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -3,10 +3,9 @@
* Copyright (C) 2023 Jacopo Mondi <jacopo.mondi@ideasonboard.com>
* Copyright (C) 2022 Nicholas Roth <nicholas@rothemail.net>
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2017 Intel Corporation.
*/
-#include <linux/unaligned.h>
-
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -18,6 +17,8 @@
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
@@ -28,8 +29,9 @@
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
-#define OV8858_LINK_FREQ 360000000U
-#define OV8858_XVCLK_FREQ 24000000
+#define OV8858_LINK_FREQ (360 * HZ_PER_MHZ)
+#define OV8858_XVCLK_FREQ_19_2MHZ (192 * HZ_PER_MHZ / 10)
+#define OV8858_XVCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
#define OV8858_REG_SIZE_SHIFT 16
#define OV8858_REG_ADDR_MASK 0xffff
@@ -59,6 +61,14 @@
#define OV8858_LONG_GAIN_STEP 1
#define OV8858_LONG_GAIN_DEFAULT 0x80
+#define OV8858_REG_MWB_RED_GAIN OV8858_REG_16BIT(0x5032)
+#define OV8858_REG_MWB_GREEN_GAIN OV8858_REG_16BIT(0x5034)
+#define OV8858_REG_MWB_BLUE_GAIN OV8858_REG_16BIT(0x5036)
+#define OV8858_MWB_GAIN_MIN 0x400
+#define OV8858_MWB_GAIN_MAX 0xfff
+#define OV8858_MWB_GAIN_STEP 1
+#define OV8858_MWB_GAIN_DEFAULT 0x400
+
#define OV8858_REG_LONG_DIGIGAIN OV8858_REG_16BIT(0x350a)
#define OV8858_LONG_DIGIGAIN_H_MASK 0x3fc0
#define OV8858_LONG_DIGIGAIN_L_MASK 0x3f
@@ -93,6 +103,33 @@ struct regval_modes {
const struct regval *mode_4lanes;
};
+struct ov8858_gain_range {
+ u32 min;
+ u32 max;
+ u32 step;
+ u32 def;
+};
+
+enum ov8858_xvclk_index {
+ OV8858_XVCLK_24MHZ,
+ OV8858_XVCLK_19_2MHZ,
+};
+
+static const struct ov8858_gain_range ov8858_digital_gain_ranges[] = {
+ [OV8858_XVCLK_24MHZ] = {
+ .min = OV8858_LONG_DIGIGAIN_MIN,
+ .max = OV8858_LONG_DIGIGAIN_MAX,
+ .step = OV8858_LONG_DIGIGAIN_STEP,
+ .def = OV8858_LONG_DIGIGAIN_DEFAULT,
+ },
+ [OV8858_XVCLK_19_2MHZ] = {
+ .min = OV8858_MWB_GAIN_MIN,
+ .max = OV8858_MWB_GAIN_MAX,
+ .step = OV8858_MWB_GAIN_STEP,
+ .def = OV8858_MWB_GAIN_DEFAULT,
+ },
+};
+
struct ov8858_mode {
u32 width;
u32 height;
@@ -104,6 +141,7 @@ struct ov8858_mode {
struct ov8858 {
struct clk *xvclk;
+ unsigned long xvclk_rate;
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
@@ -121,6 +159,41 @@ struct ov8858 {
unsigned int num_lanes;
};
+/*
+ * Cherry Trail MRD production settings for a 19.2 MHz input and 360 MHz
+ * CSI-2 link. Apply these after the otherwise reusable 24 MHz mode table.
+ *
+ * Besides the corrected sensor/MIPI PLL divisors, keep the final common
+ * black-level settings here. The per-mode tables retain their resolution
+ * dependent black-column anchors and window sizes.
+ */
+static const struct regval ov8858_cht_mrd_19_2mhz[] = {
+ {0x0300, 0x00},
+ {0x0302, 0x27},
+ {0x0303, 0x00},
+ {0x0304, 0x03},
+ {0x030b, 0x00},
+ {0x030d, 0x27},
+ {0x030e, 0x00},
+ {0x030f, 0x04},
+ {0x0312, 0x01},
+ {0x031e, 0x0c},
+ {0x3f08, 0x08},
+ {0x400a, 0x01},
+ {0x400d, 0x10},
+ {0x4011, 0x20},
+ {0x403e, 0x08},
+ {0x4040, 0x07},
+ {0x4041, 0xc6},
+ {0x4202, 0x00},
+ {0x4500, 0x58},
+ {0x470b, 0x28},
+ {0x4837, 0x15},
+ {0x58f4, 0x32},
+ {0x58f8, 0x3d},
+ {REG_NULL, 0x00},
+};
+
static inline struct ov8858 *sd_to_ov8858(struct v4l2_subdev *sd)
{
return container_of(sd, struct ov8858, subdev);
@@ -1345,6 +1418,13 @@ static int ov8858_start_stream(struct ov8858 *ov8858,
if (ret)
return ret;
+ /* The mode tables contain PLL settings for a 24 MHz input clock. */
+ if (ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ) {
+ ret = ov8858_write_array(ov8858, ov8858_cht_mrd_19_2mhz);
+ if (ret)
+ return ret;
+ }
+
/* 200 usec max to let PLL stabilize. */
fsleep(200);
@@ -1526,18 +1606,26 @@ static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern)
static int ov8858_set_digital_gain(struct ov8858 *ov8858, u32 gain)
{
u16 long_gain;
+ int ret;
- /*
- * Digital gain is assembled as:
- * 0x350a[7:0] = dgain[13:6]
- * 0x350b[5:0] = dgain[5:0]
- * Reassemble the control value to write it in one go.
- */
- long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
- ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
- OV8858_LONG_DIGIGAIN_H_SHIFT);
+ if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ) {
+ long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
+ ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
+ OV8858_LONG_DIGIGAIN_H_SHIFT);
- return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN, long_gain, NULL);
+ return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
+ long_gain, NULL);
+ }
+
+ ret = ov8858_write(ov8858, OV8858_REG_MWB_RED_GAIN, gain, NULL);
+ if (ret)
+ return ret;
+
+ ret = ov8858_write(ov8858, OV8858_REG_MWB_GREEN_GAIN, gain, NULL);
+ if (ret)
+ return ret;
+
+ return ov8858_write(ov8858, OV8858_REG_MWB_BLUE_GAIN, gain, NULL);
}
static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -1622,9 +1710,6 @@ static int ov8858_power_on(struct ov8858 *ov8858)
unsigned long delay_us;
int ret;
- if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ)
- dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
-
ret = clk_prepare_enable(ov8858->xvclk);
if (ret < 0) {
dev_err(dev, "Failed to enable xvclk\n");
@@ -1643,7 +1728,7 @@ static int ov8858_power_on(struct ov8858 *ov8858)
* transaction, but a double sleep between the release of gpios
* helps with sporadic failures observed at probe time.
*/
- delay_us = DIV_ROUND_UP(8192, OV8858_XVCLK_FREQ / 1000 / 1000);
+ delay_us = DIV_ROUND_UP(8192, ov8858->xvclk_rate / HZ_PER_MHZ);
gpiod_set_value_cansleep(ov8858->reset_gpio, 0);
fsleep(delay_us);
@@ -1701,9 +1786,11 @@ static int ov8858_init_ctrls(struct ov8858 *ov8858)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
+ const struct ov8858_gain_range *digital_gain_range;
const struct ov8858_mode *mode = &ov8858_modes[0];
struct v4l2_fwnode_device_properties props;
s64 exposure_max, vblank_def;
+ unsigned int xvclk_index;
unsigned int pixel_rate;
struct v4l2_ctrl *ctrl;
u32 h_blank;
@@ -1746,10 +1833,12 @@ static int ov8858_init_ctrls(struct ov8858 *ov8858)
OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
+ xvclk_index = ov8858->xvclk_rate == OV8858_XVCLK_FREQ_19_2MHZ ?
+ OV8858_XVCLK_19_2MHZ : OV8858_XVCLK_24MHZ;
+ digital_gain_range = &ov8858_digital_gain_ranges[xvclk_index];
v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
- OV8858_LONG_DIGIGAIN_MIN, OV8858_LONG_DIGIGAIN_MAX,
- OV8858_LONG_DIGIGAIN_STEP,
- OV8858_LONG_DIGIGAIN_DEFAULT);
+ digital_gain_range->min, digital_gain_range->max,
+ digital_gain_range->step, digital_gain_range->def);
v4l2_ctrl_new_std_menu_items(handler, &ov8858_ctrl_ops,
V4L2_CID_TEST_PATTERN,
@@ -1887,6 +1976,13 @@ static int ov8858_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(ov8858->xvclk),
"Failed to get xvclk\n");
+ ov8858->xvclk_rate = clk_get_rate(ov8858->xvclk);
+ if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ &&
+ ov8858->xvclk_rate != OV8858_XVCLK_FREQ_24MHZ)
+ return dev_err_probe(dev, -EINVAL,
+ "Unsupported xvclk rate %lu Hz\n",
+ ov8858->xvclk_rate);
+
ov8858->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(ov8858->reset_gpio))
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 01/11] media: ov8858: Extract digital gain programming Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 19:47 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 04/11] media: ov2740: Add OVTI2740 ACPI ID Maurizio Casciano
` (8 subsequent siblings)
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
Use designated initializers and the standard empty sentinel style
before adding firmware IDs.
Assisted-by: Codex:gpt-5.6-sol sparse
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
---
drivers/media/i2c/ov2740.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 39003c1632ad..8f0ad5dbb245 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -1461,10 +1461,9 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume,
NULL);
static const struct acpi_device_id ov2740_acpi_ids[] = {
- {"INT3474"},
- {}
+ { .id = "INT3474" },
+ { }
};
-
MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids);
static struct i2c_driver ov2740_i2c_driver = {
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 04/11] media: ov2740: Add OVTI2740 ACPI ID
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (2 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 05/11] media: ov8858: Add INT3477 " Maurizio Casciano
` (7 subsequent siblings)
11 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
Firmware may enumerate OV2740 image sensors using the OVTI2740 ACPI ID.
Add it to the existing ACPI match table.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
---
drivers/media/i2c/ov2740.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 8f0ad5dbb245..18bb3ac9701f 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -1462,6 +1462,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume,
static const struct acpi_device_id ov2740_acpi_ids[] = {
{ .id = "INT3474" },
+ { .id = "OVTI2740" },
{ }
};
MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids);
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 05/11] media: ov8858: Add INT3477 ACPI ID
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (3 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 04/11] media: ov2740: Add OVTI2740 ACPI ID Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-28 11:21 ` Sakari Ailus
2026-08-27 18:17 ` [PATCH v2 06/11] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
` (6 subsequent siblings)
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
Firmware may use INT3477 as the ACPI hardware ID for an OV8858 image
sensor. Add an ACPI match table for that ID.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
---
drivers/media/i2c/ov8858.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index b8c2f0d25505..b0ffc366a4e4 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/device-id/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -2077,6 +2078,12 @@ static void ov8858_remove(struct i2c_client *client)
pm_runtime_set_suspended(&client->dev);
}
+static const struct acpi_device_id ov8858_acpi_match[] = {
+ { .id = "INT3477" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ov8858_acpi_match);
+
static const struct of_device_id ov8858_of_match[] = {
{ .compatible = "ovti,ov8858" },
{ /* sentinel */ },
@@ -2087,6 +2094,7 @@ static struct i2c_driver ov8858_i2c_driver = {
.driver = {
.name = "ov8858",
.pm = &ov8858_pm_ops,
+ .acpi_match_table = ov8858_acpi_match,
.of_match_table = ov8858_of_match,
},
.probe = ov8858_probe,
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 06/11] media: intel: ipu-bridge: Add Yoga Book camera sensors
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (4 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 05/11] media: ov8858: Add INT3477 " Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 07/11] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
` (5 subsequent siblings)
11 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book YB1-X91 uses INT3477 for its OV8858 rear sensor and
OVTI2740 for its OV2740 front sensor. Add link frequencies consumed by
the sensor drivers when the IPU bridge constructs software endpoints.
This supplies endpoint data only; it does not establish that either
sensor can stream on the Yoga Book hardware.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/pci/intel/ipu-bridge.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 1bb3a3e98d6b..47317c423fad 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -65,6 +65,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("INT33F0", 1, 384000000),
/* Omnivision OV2740 */
IPU_SENSOR_CONFIG("INT3474", 1, 180000000),
+ /* Omnivision OV8858 */
+ IPU_SENSOR_CONFIG("INT3477", 1, 360000000),
/* Omnivision OV5670 */
IPU_SENSOR_CONFIG("INT3479", 1, 422400000),
/* Omnivision OV8865 */
@@ -93,6 +95,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
/* Omnivision OV2680 */
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
+ /* Omnivision OV2740 */
+ IPU_SENSOR_CONFIG("OVTI2740", 1, 360000000),
/* Omnivision OV5675 */
IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
/* Omnivision OV8856 */
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 07/11] media: atomisp: Add Yoga Book camera configuration
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (5 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 06/11] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
` (4 subsequent siblings)
11 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The Yoga Book YB1-X91 firmware lacks usable AtomISP configuration for
both cameras. Rear INT3477 OV8858 uses four CSI-2 lanes and a WV517S
actuator; front OVTI2740 uses two lanes and no actuator.
Add both sensor configurations and a DMI-scoped two-lane override for
OVTI2740:00. Keep the quirk limited to Lenovo YB1-X91L systems.
Binding and media nodes do not prove streaming; both sensors and the
focus actuator still require physical tests.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
.../media/atomisp/pci/atomisp_csi2_bridge.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index cca91c6d71a5..207ab69385b5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -74,6 +74,12 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
{}
};
+static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
+ /* The vendor driver and sensor modes use two CSI data lanes. */
+ { "OVTI2740:00", "CsiLanes", "2" },
+ {}
+};
+
static struct gmin_cfg_var xiaomi_mipad2_vars[] = {
/* _DSM contains the wrong CsiPort for the front facing OV5693 sensor */
{ "INT33BE:00", "CsiPort", "0" },
@@ -83,6 +89,14 @@ static struct gmin_cfg_var xiaomi_mipad2_vars[] = {
};
static const struct dmi_system_id gmin_cfg_dmi_overrides[] = {
+ {
+ /* Lenovo Yoga Book X91L */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91L"),
+ },
+ .driver_data = lenovo_yogabook_x91_vars,
+ },
{
/* Lenovo Ideapad Miix 310 */
.matches = {
@@ -360,6 +374,8 @@ static const struct acpi_device_id atomisp_sensor_configs[] = {
* an i2c-client for the VCM, so it is disabled for now.
*/
ATOMISP_SENSOR_CONFIG("INT33BE", 2, false), /* OV5693 */
+ ATOMISP_SENSOR_CONFIG("INT3477", 4, true), /* OV8858 */
+ ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false), /* OV2740 */
{}
};
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (6 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 07/11] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 19:57 ` Andy Shevchenko
` (2 more replies)
2026-08-27 18:17 ` [PATCH v2 09/11] media: ov2740: add manual white balance controls Maurizio Casciano
` (3 subsequent siblings)
11 siblings, 3 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The YB1-X91L firmware does not describe the complete camera link. Its
front OV2740 uses two CSI-2 lanes at a 288 MHz link frequency and sends
a 1932x1092 BGGR transport frame with 12 pixels of horizontal and
vertical padding around the 1920x1080 image.
Allow the AtomISP bridge to provide per-sensor link frequencies and
padding, add the matching OV2740 mode, and derive ISP2401 D-PHY timing
from the sensor link-frequency control.
The register values are hardware configuration facts checked against
the Lenovo YB1-X91L configuration and physical captures; no proprietary
driver code or tuning binary is included.
Tested on a Lenovo Yoga Book YB1-X91L with continuous front-camera raw
capture.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov2740.c | 103 +++++++++++++++++-
drivers/media/pci/intel/ipu-bridge.c | 15 ++-
.../staging/media/atomisp/pci/atomisp_csi2.c | 17 ++-
.../staging/media/atomisp/pci/atomisp_csi2.h | 2 +
.../media/atomisp/pci/atomisp_csi2_bridge.c | 53 ++++++++-
include/media/ipu-bridge.h | 2 +
6 files changed, 173 insertions(+), 19 deletions(-)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 18bb3ac9701f..6083dd28e4ad 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -12,12 +12,14 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
+#include <linux/units.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
-#define OV2740_LINK_FREQ_360MHZ 360000000ULL
-#define OV2740_LINK_FREQ_180MHZ 180000000ULL
+#define OV2740_LINK_FREQ_360MHZ (360ULL * HZ_PER_MHZ)
+#define OV2740_LINK_FREQ_288MHZ (288ULL * HZ_PER_MHZ)
+#define OV2740_LINK_FREQ_180MHZ (180ULL * HZ_PER_MHZ)
#define OV2740_SCLK 72000000LL
#define OV2740_MCLK 19200000
#define OV2740_DATA_LANES 2
@@ -91,6 +93,7 @@ struct nvm_data {
enum {
OV2740_LINK_FREQ_360MHZ_INDEX,
+ OV2740_LINK_FREQ_288MHZ_INDEX,
OV2740_LINK_FREQ_180MHZ_INDEX,
};
@@ -130,6 +133,12 @@ struct ov2740_mode {
/* Link frequency needed for this resolution */
u32 link_freq_index;
+ /* Bayer order produced by this mode */
+ u32 code;
+
+ /* Optional common settings applied before the mode-specific settings */
+ const struct ov2740_reg_list init_reg_list;
+
/* Sensor register settings for this resolution */
const struct ov2740_reg_list reg_list;
};
@@ -142,6 +151,14 @@ static const struct ov2740_reg mipi_data_rate_720mbps[] = {
{0x0312, 0x11},
};
+static const struct ov2740_reg mipi_data_rate_576mbps[] = {
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x02},
+ {0x0312, 0x01},
+};
+
static const struct ov2740_reg mipi_data_rate_360mbps[] = {
{0x0302, 0x4b},
{0x0303, 0x01},
@@ -458,6 +475,36 @@ static const struct ov2740_reg mode_1932x1092_regs_180mhz[] = {
{0x4003, 0x40}, /* set Black level to 0x40 */
};
+/*
+ * Lenovo's Yoga Book vendor driver uses the generic initialization settings
+ * above followed by these mode overrides. Unlike the generic 720 Mbps mode,
+ * this is a 576 Mbps two-lane mode with BGGR output from the optical array.
+ */
+static const struct ov2740_reg mode_1932x1092_regs_288mhz[] = {
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x02},
+ {0x0312, 0x01},
+ {0x3808, 0x07},
+ {0x3809, 0x8c},
+ {0x380a, 0x04},
+ {0x380b, 0x44},
+ {0x380c, 0x04},
+ {0x380d, 0x38},
+ {0x380e, 0x06},
+ {0x380f, 0xf0},
+ {0x3810, 0x00},
+ {0x3811, 0x02},
+ {0x3812, 0x00},
+ {0x3813, 0x02},
+ {0x481f, 0x29},
+ {0x4820, 0x01},
+ {0x4837, 0x1b},
+ {0x5000, 0x7f},
+ {0x58f4, 0x32},
+};
+
static const char * const ov2740_test_pattern_menu[] = {
"Disabled",
"Color Bar",
@@ -468,6 +515,7 @@ static const char * const ov2740_test_pattern_menu[] = {
static const s64 link_freq_menu_items[] = {
OV2740_LINK_FREQ_360MHZ,
+ OV2740_LINK_FREQ_288MHZ,
OV2740_LINK_FREQ_180MHZ,
};
@@ -478,6 +526,12 @@ static const struct ov2740_link_freq_config link_freq_configs[] = {
.regs = mipi_data_rate_720mbps,
}
},
+ [OV2740_LINK_FREQ_288MHZ_INDEX] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_576mbps),
+ .regs = mipi_data_rate_576mbps,
+ }
+ },
[OV2740_LINK_FREQ_180MHZ_INDEX] = {
.reg_list = {
.num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps),
@@ -499,6 +553,28 @@ static const struct ov2740_mode supported_modes_360mhz[] = {
.regs = mode_1932x1092_regs_360mhz,
},
.link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ },
+};
+
+static const struct ov2740_mode supported_modes_288mhz[] = {
+ {
+ .width = 1932,
+ .height = 1092,
+ .hts = 2160,
+ .vts_min = 1776,
+ .vts_def = 1776,
+ .vts_max = 32767,
+ .init_reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_360mhz),
+ .regs = mode_1932x1092_regs_360mhz,
+ },
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_288mhz),
+ .regs = mode_1932x1092_regs_288mhz,
+ },
+ .link_freq_index = OV2740_LINK_FREQ_288MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
},
};
@@ -515,6 +591,7 @@ static const struct ov2740_mode supported_modes_180mhz[] = {
.regs = mode_1932x1092_regs_180mhz,
},
.link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
},
};
@@ -842,7 +919,7 @@ static void ov2740_update_pad_format(const struct ov2740_mode *mode,
{
fmt->width = mode->width;
fmt->height = mode->height;
- fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->code = mode->code;
fmt->field = V4L2_FIELD_NONE;
}
@@ -966,6 +1043,15 @@ static int ov2740_start_streaming(struct ov2740 *ov2740)
return ret;
}
+ if (ov2740->cur_mode->init_reg_list.num_of_regs) {
+ reg_list = &ov2740->cur_mode->init_reg_list;
+ ret = ov2740_write_reg_list(ov2740, reg_list);
+ if (ret) {
+ dev_err(ov2740->dev, "failed to set common mode registers\n");
+ return ret;
+ }
+ }
+
reg_list = &ov2740->cur_mode->reg_list;
ret = ov2740_write_reg_list(ov2740, reg_list);
if (ret) {
@@ -1062,10 +1148,12 @@ static int ov2740_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
+ struct ov2740 *ov2740 = to_ov2740(sd);
+
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ code->code = ov2740->supported_modes[0].code;
return 0;
}
@@ -1080,7 +1168,7 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd,
if (fse->index >= ov2740->supported_modes_count)
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ if (fse->code != supported_modes[0].code)
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
@@ -1178,6 +1266,11 @@ static int ov2740_check_hwcfg(struct ov2740 *ov2740)
ov2740->supported_modes_count =
ARRAY_SIZE(supported_modes_360mhz);
break;
+ case OV2740_LINK_FREQ_288MHZ_INDEX:
+ ov2740->supported_modes = supported_modes_288mhz;
+ ov2740->supported_modes_count =
+ ARRAY_SIZE(supported_modes_288mhz);
+ break;
case OV2740_LINK_FREQ_180MHZ_INDEX:
ov2740->supported_modes = supported_modes_180mhz;
ov2740->supported_modes_count =
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 47317c423fad..5730a95767a2 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -520,11 +520,16 @@ static void ipu_bridge_create_fwnode_properties(
sensor->prop_names.remote_endpoint,
sensor->local_ref);
- if (cfg->nr_link_freqs > 0)
- sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
- sensor->prop_names.link_frequencies,
- cfg->link_freqs,
- cfg->nr_link_freqs);
+ if (sensor->nr_link_freqs > 0)
+ sensor->ep_properties[3] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
+ sensor->link_freqs,
+ sensor->nr_link_freqs);
+ else if (cfg->nr_link_freqs > 0)
+ sensor->ep_properties[3] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
+ cfg->link_freqs,
+ cfg->nr_link_freqs);
sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
sensor->prop_names.data_lanes,
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index 95b9113d75e9..035f1addeaae 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -5,6 +5,7 @@
* Copyright (c) 2010 Intel Corporation. All Rights Reserved.
*/
+#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mediabus.h>
#include "atomisp_cmd.h"
@@ -288,18 +289,19 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
int dat_termen;
int dat_settle;
- struct v4l2_control ctrl;
struct atomisp_device *isp = asd->isp;
+ struct v4l2_subdev *sensor;
+ s64 link_freq;
int mipi_freq = 0;
enum atomisp_camera_port port;
int n;
port = isp->inputs[asd->input_curr].port;
- ctrl.id = V4L2_CID_LINK_FREQ;
- if (v4l2_g_ctrl
- (isp->inputs[asd->input_curr].sensor->ctrl_handler, &ctrl) == 0)
- mipi_freq = ctrl.value;
+ sensor = isp->inputs[asd->input_curr].sensor;
+ link_freq = v4l2_get_link_freq(&sensor->entity.pads[0], 0, 0);
+ if (link_freq > 0 && link_freq <= S32_MAX)
+ mipi_freq = link_freq;
clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen, mipi_freq,
TERMEN_DEFAULT);
@@ -310,6 +312,11 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle, mipi_freq,
SETTLE_DEFAULT);
+ dev_dbg(isp->dev,
+ "CSI port %u link frequency %d Hz, clk timing %d/%d, data timing %d/%d\n",
+ port, mipi_freq, clk_termen, clk_settle,
+ dat_termen, dat_settle);
+
for (n = 0; n < csi2_port_lanes[port] + 1; n++) {
hrt_address base = csi2_port_base[port] + csi2_lane_base[n];
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
index ec762f8fb922..9298c31bf11e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
@@ -45,6 +45,8 @@ int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2,
struct v4l2_device *vdev);
int atomisp_csi2_bridge_init(struct atomisp_device *isp);
int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp);
+bool atomisp_csi2_get_sensor_padding(struct device *dev,
+ struct v4l2_area *padding);
void atomisp_csi2_configure(struct atomisp_sub_device *asd);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index 207ab69385b5..502c951cb687 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -15,6 +15,7 @@
#include <linux/dmi.h>
#include <linux/platform_data/x86/int3472.h>
#include <linux/property.h>
+#include <linux/units.h>
#include <media/ipu-bridge.h>
#include <media/v4l2-fwnode.h>
@@ -43,14 +44,16 @@ static const guid_t vcm_dsm_guid =
0x9f, 0x48, 0xa9, 0xc3, 0xb5, 0xda, 0x78, 0x9f);
struct atomisp_sensor_config {
+ u64 link_freq;
int lanes;
bool vcm;
};
-#define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM) \
+#define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM, _LINK_FREQ) \
{ \
.id = _HID, \
.driver_data = (long)&((const struct atomisp_sensor_config) { \
+ .link_freq = _LINK_FREQ, \
.lanes = _LANES, \
.vcm = _VCM, \
}) \
@@ -77,6 +80,9 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
/* The vendor driver and sensor modes use two CSI data lanes. */
{ "OVTI2740:00", "CsiLanes", "2" },
+ /* Crop the vendor mode's 1932x1092 transport frame to 1920x1080. */
+ { "OVTI2740:00", "CsiPaddingWidth", "12" },
+ { "OVTI2740:00", "CsiPaddingHeight", "12" },
{}
};
@@ -207,6 +213,40 @@ static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int defau
return default_val;
}
+static u32 atomisp_csi2_get_padding_value(struct acpi_device *adev,
+ const char *key, u32 default_val)
+{
+ int val;
+
+ val = gmin_cfg_get_int(adev, key, default_val);
+ if (val >= 0 && val <= 64 && !(val & 1))
+ return val;
+
+ acpi_handle_warn(adev->handle, "%s: Invalid %s=%d\n",
+ dev_name(&adev->dev), key, val);
+ return default_val;
+}
+
+bool atomisp_csi2_get_sensor_padding(struct device *dev,
+ struct v4l2_area *padding)
+{
+ struct acpi_device *adev;
+
+ padding->width = pad_w;
+ padding->height = pad_h;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return false;
+
+ padding->width = atomisp_csi2_get_padding_value(adev,
+ "CsiPaddingWidth", pad_w);
+ padding->height = atomisp_csi2_get_padding_value(adev,
+ "CsiPaddingHeight", pad_h);
+
+ return padding->width != pad_w || padding->height != pad_h;
+}
+
static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev)
{
/* ACPI_PATH_SEGMENT_LENGTH is guaranteed to be big enough for name + 0 term. */
@@ -373,9 +413,10 @@ static const struct acpi_device_id atomisp_sensor_configs[] = {
* the sensor fails to start streaming when instantiating
* an i2c-client for the VCM, so it is disabled for now.
*/
- ATOMISP_SENSOR_CONFIG("INT33BE", 2, false), /* OV5693 */
- ATOMISP_SENSOR_CONFIG("INT3477", 4, true), /* OV8858 */
- ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false), /* OV2740 */
+ ATOMISP_SENSOR_CONFIG("INT33BE", 2, false, 0), /* OV5693 */
+ ATOMISP_SENSOR_CONFIG("INT3477", 4, true, 0), /* OV8858 */
+ /* OV2740 */
+ ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false, 288 * HZ_PER_MHZ),
{}
};
@@ -394,6 +435,10 @@ static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev,
lanes = cfg->lanes;
vcm = cfg->vcm;
+ if (cfg->link_freq) {
+ sensor->link_freqs[0] = cfg->link_freq;
+ sensor->nr_link_freqs = 1;
+ }
}
/*
diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
index 16fac765456e..633ef0c4cf59 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -135,6 +135,8 @@ struct ipu_sensor {
u8 link;
u8 lanes;
+ u8 nr_link_freqs;
+ u64 link_freqs[MAX_NUM_LINK_FREQS];
u32 mclkspeed;
u32 rotation;
enum v4l2_fwnode_orientation orientation;
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 09/11] media: ov2740: add manual white balance controls
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (7 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 20:03 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 10/11] media: atomisp: allow raw Bayer capture Maurizio Casciano
` (2 subsequent siblings)
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The sensor has separate red, green and blue manual white-balance gain
registers, but the driver currently writes the same digital-gain value
to all three channels. This prevents userspace from correcting the
strong color cast of raw Bayer capture.
Expose red- and blue-balance controls relative to the digital gain,
update all three channels under group hold, and always release and
launch the group even when a channel write fails.
Tested on the Yoga Book OV2740 with live gain changes and continuous raw
capture.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
drivers/media/i2c/ov2740.c | 68 ++++++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 21 deletions(-)
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 6083dd28e4ad..2d46d8685971 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -608,6 +608,9 @@ struct ov2740 {
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *digital_gain;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
/* GPIOs, clocks, regulators */
struct gpio_desc *reset_gpio;
@@ -737,35 +740,42 @@ static int ov2740_identify_module(struct ov2740 *ov2740)
return 0;
}
-static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain)
+static int ov2740_update_mwb_gains(struct ov2740 *ov2740)
{
- int ret;
+ u32 blue_gain, green_gain, red_gain;
+ int end_ret, launch_ret, ret;
+
+ /* Balance controls use 1024 as unity relative to the digital gain. */
+ green_gain = ov2740->digital_gain->val;
+ red_gain = DIV_ROUND_CLOSEST(green_gain * ov2740->red_balance->val,
+ OV2740_DGTL_GAIN_DEFAULT);
+ red_gain = min(red_gain, OV2740_DGTL_GAIN_MAX);
+ blue_gain = DIV_ROUND_CLOSEST(green_gain * ov2740->blue_balance->val,
+ OV2740_DGTL_GAIN_DEFAULT);
+ blue_gain = min(blue_gain, OV2740_DGTL_GAIN_MAX);
ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
OV2740_GROUP_HOLD_START);
if (ret)
return ret;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain);
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, red_gain);
if (ret)
- return ret;
+ goto release_group;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, d_gain);
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, green_gain);
if (ret)
- return ret;
+ goto release_group;
- ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
- if (ret)
- return ret;
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, blue_gain);
- ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
- OV2740_GROUP_HOLD_END);
- if (ret)
- return ret;
+release_group:
+ end_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_END);
+ launch_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_LAUNCH);
- ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
- OV2740_GROUP_HOLD_LAUNCH);
- return ret;
+ return ret ?: end_ret ?: launch_ret;
}
static int ov2740_test_pattern(struct ov2740 *ov2740, u32 pattern)
@@ -806,7 +816,9 @@ static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_DIGITAL_GAIN:
- ret = ov2740_update_digital_gain(ov2740, ctrl->val);
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ ret = ov2740_update_mwb_gains(ov2740);
break;
case V4L2_CID_EXPOSURE:
@@ -847,7 +859,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
int ret;
ctrl_hdlr = &ov2740->ctrl_handler;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
if (ret)
return ret;
@@ -882,9 +894,23 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OV2740_ANAL_GAIN_MIN, OV2740_ANAL_GAIN_MAX,
OV2740_ANAL_GAIN_STEP, OV2740_ANAL_GAIN_MIN);
- v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
- OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
- OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->digital_gain =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN,
+ OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
+ OV2740_DGTL_GAIN_STEP,
+ OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->red_balance =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_RED_BALANCE,
+ 1, OV2740_DGTL_GAIN_MAX, 1,
+ OV2740_DGTL_GAIN_DEFAULT);
+ ov2740->blue_balance =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE,
+ 1, OV2740_DGTL_GAIN_MAX, 1,
+ OV2740_DGTL_GAIN_DEFAULT);
+ v4l2_ctrl_cluster(3, &ov2740->digital_gain);
exposure_max = ov2740->cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_EXPOSURE,
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 10/11] media: atomisp: allow raw Bayer capture
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (8 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 09/11] media: ov2740: add manual white balance controls Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 20:24 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
2026-08-27 18:58 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Andy Shevchenko
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
AtomISP currently rejects all raw formats and silently substitutes
YUV420. This prevents userspace camera processing stacks from obtaining
unprocessed sensor frames.
Enumerate only the raw format matching the sensor media-bus code and
reconcile raw format requests with the code selected by the sensor.
Userspace explicitly opts into raw capture by selecting that raw V4L2
pixel format with VIDIOC_S_FMT; no separate module parameter is needed.
Also apply the per-sensor padding discovered by the CSI-2 bridge when
enumerating and negotiating processed frame sizes. Raw formats expose
the full sensor transport frame so ISP2401 can use its copy pipeline.
Existing systems retain the global padding defaults.
Tested on a Lenovo Yoga Book YB1-X91L with raw capture from its OV2740
and OV8858 sensors.
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
Assisted-by: Codex:gpt-5.6-sol sparse
---
.../staging/media/atomisp/pci/atomisp_cmd.c | 33 +++++++++++++++----
.../media/atomisp/pci/atomisp_internal.h | 3 ++
.../staging/media/atomisp/pci/atomisp_ioctl.c | 22 +++++++++++--
.../staging/media/atomisp/pci/atomisp_v4l2.c | 9 +++++
4 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 6cd500d9fd26..70ff9b55b52e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -3572,10 +3572,17 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
u32 min_pad_w = ISP2400_MIN_PAD_W;
u32 min_pad_h = ISP2400_MIN_PAD_H;
struct v4l2_mbus_framefmt *sink;
+ u32 input_padding_w = pad_w;
+ u32 input_padding_h = pad_h;
+
+ if (input->padding_override) {
+ input_padding_w = input->padding.width;
+ input_padding_h = input->padding.height;
+ }
if (!input->crop_support) {
- *padding_w = pad_w;
- *padding_h = pad_h;
+ *padding_w = input_padding_w;
+ *padding_h = input_padding_h;
return;
}
@@ -3588,8 +3595,10 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
native_rect.height /= 2;
}
- *padding_w = min_t(u32, (native_rect.width - width) & ~1, pad_w);
- *padding_h = min_t(u32, (native_rect.height - height) & ~1, pad_h);
+ *padding_w = min_t(u32, (native_rect.width - width) & ~1,
+ input_padding_w);
+ *padding_h = min_t(u32, (native_rect.height - height) & ~1,
+ input_padding_h);
/* The below minimum padding requirements are for BYT / ISP2400 only */
if (IS_ISP2401)
@@ -3804,8 +3813,7 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
int ret;
fmt = atomisp_get_format_bridge(f->pixelformat);
- /* Currently, raw formats are broken!!! */
- if (!fmt || fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ if (!fmt) {
f->pixelformat = V4L2_PIX_FMT_YUV420;
fmt = atomisp_get_format_bridge(f->pixelformat);
@@ -3827,7 +3835,13 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
* resolution + padding. Add padding here and remove it again after
* the set_fmt call, like atomisp_set_fmt_to_snr() does.
*/
- atomisp_get_padding(isp, f->width, f->height, &padding_w, &padding_h);
+ if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ padding_w = 0;
+ padding_h = 0;
+ } else {
+ atomisp_get_padding(isp, f->width, f->height,
+ &padding_w, &padding_h);
+ }
v4l2_fill_mbus_format(&ffmt, f, fmt->mbus_code);
ffmt.width += padding_w;
ffmt.height += padding_h;
@@ -3846,6 +3860,11 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
ffmt.code);
return -EINVAL;
}
+ if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ fmt->mbus_code != snr_fmt->mbus_code) {
+ fmt = snr_fmt;
+ f->pixelformat = fmt->pixelformat;
+ }
f->width = ffmt.width - padding_w;
f->height = ffmt.height - padding_h;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h
index 5a69580b8251..66b12461bd40 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
@@ -98,6 +98,7 @@
#define ATOMISP_CSS_SUPPORT_YUVPP 1
#define ATOMISP_CSS_OUTPUT_SECOND_INDEX 1
+
#define ATOMISP_CSS_OUTPUT_DEFAULT_INDEX 0
/* ISP2401 */
@@ -116,6 +117,8 @@
struct atomisp_input_subdev {
enum atomisp_camera_port port;
u32 code; /* MEDIA_BUS_FMT_* */
+ struct v4l2_area padding;
+ bool padding_override;
bool binning_support;
bool crop_support;
bool sensor_on;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index 50366bf10f32..cf3e68983e0d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -536,11 +536,19 @@ static int atomisp_enum_framesizes(struct file *file, void *priv,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.code = input->code,
};
+ const struct atomisp_format_bridge *format;
struct v4l2_subdev_state *act_sd_state;
+ struct v4l2_area padding = {
+ .width = pad_w,
+ .height = pad_h,
+ };
int ret;
if (!input->sensor)
return -EINVAL;
+ format = atomisp_get_format_bridge(fsize->pixel_format);
+ if (!format)
+ return -EINVAL;
if (input->crop_support)
return atomisp_enum_framesizes_crop(isp, fsize);
@@ -553,9 +561,16 @@ static int atomisp_enum_framesizes(struct file *file, void *priv,
if (ret)
return ret;
+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ padding.width = 0;
+ padding.height = 0;
+ } else if (input->padding_override) {
+ padding = input->padding;
+ }
+
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.max_width - pad_w;
- fsize->discrete.height = fse.max_height - pad_h;
+ fsize->discrete.width = fse.max_width - padding.width;
+ fsize->discrete.height = fse.max_height - padding.height;
return 0;
}
@@ -629,7 +644,8 @@ static int atomisp_enum_fmt_cap(struct file *file, void *fh,
*
* FIXME: fix the pipeline to allow sensor format too.
*/
- if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW)
+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ format->mbus_code != code.code)
continue;
/* Found a match. Now let's pick f->index'th one. */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 812230397409..7b4c1c6c1295 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -25,6 +25,7 @@
#include "atomisp_cmd.h"
#include "atomisp_common.h"
+#include "atomisp_csi2.h"
#include "atomisp_fops.h"
#include "atomisp_ioctl.h"
#include "atomisp_internal.h"
@@ -889,6 +890,14 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
struct v4l2_subdev_state *try_sd_state, *act_sd_state;
int i, err;
+ input->padding_override =
+ atomisp_csi2_get_sensor_padding(input->sensor->dev,
+ &input->padding);
+ if (input->padding_override)
+ dev_info(input->sensor->dev,
+ "using ISP input padding %ux%u\n",
+ input->padding.width, input->padding.height);
+
/*
* FIXME: Drivers are not supposed to use __v4l2_subdev_state_alloc()
* but atomisp needs this for try_fmt on its /dev/video# node since
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (9 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 10/11] media: atomisp: allow raw Bayer capture Maurizio Casciano
@ 2026-08-27 18:17 ` Maurizio Casciano
2026-08-27 20:31 ` Andy Shevchenko
2026-08-27 18:58 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Andy Shevchenko
11 siblings, 1 reply; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:17 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel, Maurizio Casciano
The Lenovo Yoga Book YB1-X91 rear camera contains a WV517S voice-coil
actuator. Add a V4L2 lens subdevice exposing the standard 10-bit
FOCUS_ABSOLUTE control and the device ringing-control mode.
Use regmap for register access and tie control updates to runtime PM so
the IPU bridge sensor link keeps shared power resources active. Propagate
PM acquisition failures and restore the drive mode and controls after
resume.
The register addresses and drive-mode value are derived from Intel's
GPL-2.0 WV517 driver. Retain the Intel copyright notice and document the
2026 copyright for this V4L2/regmap implementation.
Link: https://github.com/jekhor/yogabook-linux-android-kernel/blob/574bae692716f1b14093497bfab8a007fe8e460b/drivers/external_drivers/camera/drivers/media/i2c/wv517.c
Assisted-by: Codex:gpt-5.6-sol sparse
Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
---
MAINTAINERS | 1 +
drivers/media/i2c/Kconfig | 11 ++
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/wv517s.c | 204 +++++++++++++++++++++++++++++++++++++
4 files changed, 217 insertions(+)
create mode 100644 drivers/media/i2c/wv517s.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 55b3c2d7a78a..a4380be897e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28555,6 +28555,7 @@ S: Maintained
F: drivers/media/i2c/ak*
F: drivers/media/i2c/dw*
F: drivers/media/i2c/lm*
+F: drivers/media/i2c/wv517s.c
V4L2 CAMERA SENSOR DRIVERS
M: Sakari Ailus <sakari.ailus@linux.intel.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5c52007f9cbe..c5636e0cbecf 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -949,6 +949,17 @@ config VIDEO_DW9807_VCM
capability. This is designed for linear control of
voice coil motors, controlled via I2C serial interface.
+config VIDEO_WV517S
+ tristate "WV517S lens voice coil support"
+ select REGMAP_I2C
+ help
+ This is a driver for the WV517S camera lens voice coil. The driver
+ supports 10-bit focus control and exposes the actuator through the
+ standard V4L2 lens sub-device interface.
+
+ To compile this driver as a module, choose M here: the module will be
+ called wv517s.
+
endif
menu "Flash devices"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d04bd5724552..e480932a9540 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -173,4 +173,5 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_WV517S) += wv517s.o
obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/
diff --git a/drivers/media/i2c/wv517s.c b/drivers/media/i2c/wv517s.c
new file mode 100644
index 000000000000..2b45a09ce879
--- /dev/null
+++ b/drivers/media/i2c/wv517s.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * WV517S voice-coil motor driver
+ *
+ * Copyright (c) 2014 Intel Corporation.
+ * Copyright (C) 2026 Maurizio Casciano <mauriziocasciano7@gmail.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define WV517S_MAX_FOCUS_POSITION 1023
+#define WV517S_DEFAULT_FOCUS_POSITION 300
+
+#define WV517S_REG_FOCUS 0x41
+#define WV517S_REG_DRIVE_MODE 0x43
+#define WV517S_DRIVE_MODE_12_6_MS 0x0211
+
+struct wv517s_device {
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_subdev sd;
+ struct regmap *regmap;
+ bool resuming;
+};
+
+static inline struct wv517s_device *to_wv517s(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wv517s_device, sd);
+}
+
+static const struct regmap_config wv517s_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WV517S_REG_DRIVE_MODE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int wv517s_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct wv517s_device *wv517s = container_of(ctrl->handler,
+ struct wv517s_device,
+ ctrl_handler);
+ struct device *dev = wv517s->sd.dev;
+ int pm_ret;
+ int ret;
+
+ /* Runtime resume restores controls while the PM state is RPM_RESUMING. */
+ pm_ret = pm_runtime_get_if_active(dev);
+ if (!pm_ret && !wv517s->resuming)
+ return 0;
+ if (pm_ret < 0)
+ return pm_ret;
+
+ if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
+ ret = regmap_write(wv517s->regmap, WV517S_REG_FOCUS,
+ ctrl->val);
+ else
+ ret = -EINVAL;
+
+ if (pm_ret > 0)
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops wv517s_ctrl_ops = {
+ .s_ctrl = wv517s_set_ctrl,
+};
+
+static int wv517s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return pm_runtime_resume_and_get(sd->dev);
+}
+
+static int wv517s_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ pm_runtime_put(sd->dev);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops wv517s_internal_ops = {
+ .open = wv517s_open,
+ .close = wv517s_close,
+};
+
+static const struct v4l2_subdev_ops wv517s_subdev_ops = { };
+
+static int wv517s_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct wv517s_device *wv517s = to_wv517s(sd);
+ int ret;
+
+ /* Restore the vendor-recommended 12.6 ms ringing-control mode. */
+ ret = regmap_write(wv517s->regmap, WV517S_REG_DRIVE_MODE,
+ WV517S_DRIVE_MODE_12_6_MS);
+ if (ret)
+ return ret;
+
+ wv517s->resuming = true;
+ ret = v4l2_ctrl_handler_setup(&wv517s->ctrl_handler);
+ wv517s->resuming = false;
+
+ return ret;
+}
+
+static int wv517s_probe(struct i2c_client *client)
+{
+ struct wv517s_device *wv517s;
+ int ret;
+
+ wv517s = devm_kzalloc(&client->dev, sizeof(*wv517s), GFP_KERNEL);
+ if (!wv517s)
+ return -ENOMEM;
+
+ wv517s->regmap = devm_regmap_init_i2c(client, &wv517s_regmap_config);
+ if (IS_ERR(wv517s->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(wv517s->regmap),
+ "failed to initialize regmap\n");
+
+ v4l2_i2c_subdev_init(&wv517s->sd, client, &wv517s_subdev_ops);
+ wv517s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ wv517s->sd.internal_ops = &wv517s_internal_ops;
+ wv517s->sd.entity.function = MEDIA_ENT_F_LENS;
+
+ v4l2_ctrl_handler_init(&wv517s->ctrl_handler, 1);
+ v4l2_ctrl_new_std(&wv517s->ctrl_handler, &wv517s_ctrl_ops,
+ V4L2_CID_FOCUS_ABSOLUTE, 0,
+ WV517S_MAX_FOCUS_POSITION, 1,
+ WV517S_DEFAULT_FOCUS_POSITION);
+ if (wv517s->ctrl_handler.error) {
+ ret = wv517s->ctrl_handler.error;
+ goto err_free_ctrl_handler;
+ }
+ wv517s->sd.ctrl_handler = &wv517s->ctrl_handler;
+
+ ret = media_entity_pads_init(&wv517s->sd.entity, 0, NULL);
+ if (ret)
+ goto err_free_ctrl_handler;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ ret = wv517s_resume(&client->dev);
+ if (ret)
+ goto err_disable_pm;
+
+ ret = v4l2_async_register_subdev(&wv517s->sd);
+ if (ret)
+ goto err_disable_pm;
+
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+err_disable_pm:
+ pm_runtime_disable(&client->dev);
+ media_entity_cleanup(&wv517s->sd.entity);
+err_free_ctrl_handler:
+ v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
+
+ return ret;
+}
+
+static void wv517s_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wv517s_device *wv517s = to_wv517s(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ pm_runtime_disable(&client->dev);
+ v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+}
+
+static const struct i2c_device_id wv517s_id_table[] = {
+ { "wv517s" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wv517s_id_table);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(wv517s_pm_ops, NULL, wv517s_resume, NULL);
+
+static struct i2c_driver wv517s_i2c_driver = {
+ .driver = {
+ .name = "wv517s",
+ .pm = pm_ptr(&wv517s_pm_ops),
+ },
+ .probe = wv517s_probe,
+ .remove = wv517s_remove,
+ .id_table = wv517s_id_table,
+};
+module_i2c_driver(wv517s_i2c_driver);
+
+MODULE_AUTHOR("Maurizio Casciano <mauriziocasciano7@gmail.com>");
+MODULE_DESCRIPTION("WV517S VCM driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-27 9:58 ` Andy Shevchenko
2026-08-27 12:14 ` Sakari Ailus
@ 2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 23:18 ` Maurizio Casciano
2 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:19 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Uwe Kleine-König, linux-media, Mauro Carvalho Chehab,
Sakari Ailus, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
Here is the requested excerpt from the YB1-X91L DSDT. Both devices are under
\\_SB.PCI0.I2C3:
Device (CA11)
{
Name (_HID, "INT3477")
Name (_CID, "OVTI8858")
Name (_DDN, "OV8858")
}
Device (CA01)
{
Name (_HID, "OVTI2740")
Name (_CID, "OVTI2740")
Name (_DDN, "OV2740")
}
The corresponding runtime ACPI paths are \\_SB.PCI0.I2C3.CA11 and
\\_SB.PCI0.I2C3.CA01. In v2 the ID additions are split into one patch per
driver and preceded by the requested C99-initializer cleanup.
Maurizio
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 6/8] media: ov2740: add manual white balance controls
2026-08-27 3:13 ` Cao, Bingbu
@ 2026-08-27 18:19 ` Maurizio Casciano
0 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:19 UTC (permalink / raw)
To: Bingbu Cao
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
Yes, the three controls need to be updated together. v2 clusters digital
gain, red balance and blue balance with:
v4l2_ctrl_cluster(3, &ov2740->digital_gain);
The runtime-resume path also applies the clustered controls after the sensor
is powered and initialized.
Thank you.
Maurizio
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 9:40 ` Andy Shevchenko
2026-08-27 11:59 ` Sakari Ailus
@ 2026-08-27 18:19 ` Maurizio Casciano
1 sibling, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:19 UTC (permalink / raw)
To: Andy Shevchenko
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, linux-staging,
linux-kernel, Maurizio Casciano
Thank you. v2 splits the existing 24 MHz digital-gain packing into a
preparatory patch before adding the clock-specific path.
I retained the two masks and shift for that existing path because the 14-bit
control value is not laid out contiguously in the 16-bit register write:
0x350a receives gain[13:6], while 0x350b[5:0] receives gain[5:0]. A single
combined mask would not perform that repacking.
The remaining review points are addressed in v2, including HZ_PER_MHZ,
single-line writes, a table-selected gain range, and declaration/layout
cleanup. Pahole on the final built object reports struct ov8858 at 792 bytes,
with xvclk_rate naturally aligned at offset 8 and no hole introduced by it.
Maurizio
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 8/8] media: i2c: Add WV517S lens actuator driver
2026-08-27 12:30 ` Sakari Ailus
@ 2026-08-27 18:19 ` Maurizio Casciano
0 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 18:19 UTC (permalink / raw)
To: Sakari Ailus
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel, Maurizio Casciano
Thank you. I addressed these points in v2.
The driver now records both Intel's 2014 copyright and my 2026 copyright for
the new V4L2/regmap implementation. Board-specific text was removed from the
Kconfig help and driver comment. Register access uses regmap,
pm_runtime_get_if_active() errors are propagated, and controls are restored
through the control handler after resume.
Maurizio
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
` (10 preceding siblings ...)
2026-08-27 18:17 ` [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
@ 2026-08-27 18:58 ` Andy Shevchenko
11 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 18:58 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:45PM +0200, Maurizio Casciano wrote:
> The Lenovo Yoga Book YB1-X91L exposes an OV2740 front sensor and an
> OV8858 rear sensor through the Cherry Trail AtomISP. Its firmware does
> not provide enough information for the existing sensor and IPU bridge
> drivers to construct the complete camera links.
>
> Add the ACPI IDs and bridge data, describe both AtomISP camera links,
> support the Yoga Book sensor clocks and modes, expose raw Bayer capture,
> add per-channel white-balance controls, and add the WV517S rear-camera
> lens actuator.
>
> Raw capture is selected through the normal V4L2 raw pixel formats. There
> is no module parameter or separate private ABI. Processed formats retain
> the existing behavior, while raw formats expose the complete CSI-2
> transport frames required by the ISP2401 copy pipeline.
Do not start new version in the email thread of the previous one!
> The series is based on mainline at 73e3f0710014.
Unneeded statement, the --base puts data (and it did) in the cover letter.
Tools know about it.
> All patches pass strict
> checkpatch with no errors, warnings or checks. Pahole reports a 64-byte
> ov2740_mode with no holes and a 792-byte ov8858 with the new clock-rate
> field naturally aligned. A clean 20-job bindeb-pkg build containing the
> same final source state completed successfully.
>
> Runtime testing used a Lenovo Yoga Book YB1-X91L. Both sensors, AtomISP
> and WV517S bound. Focus position 300 was accepted. Captures passed with:
>
> - front raw BG10: 1932x1092, 4472832 bytes;
> - rear raw BG10: 3264x2448, 16293888 bytes;
> - front processed YU12: 1920x1080; and
> - rear processed JPEG: 3248x2432, 1530647 bytes.
>
> The raw format was selected and streamed in the same v4l2-ctl invocation,
> as reopening the video node restores its default format. Both raw files
> were non-empty and distinct, and the rear full-resolution JPEG completed
> without AtomISP, sensor or runtime-PM errors.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 18:17 ` [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
@ 2026-08-27 19:46 ` Andy Shevchenko
2026-08-27 23:18 ` Maurizio Casciano
2026-08-28 7:28 ` Sakari Ailus
0 siblings, 2 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 19:46 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:47PM +0200, Maurizio Casciano wrote:
> The Yoga Book drives its OV8858 from a 19.2 MHz platform clock, while
> the existing mode tables program the sensor PLL for 24 MHz. Reusing
> those settings produces incorrect internal and CSI-2 clocks.
>
> Accept both input rates and use the actual rate for the reset delay. For
> 19.2 MHz, apply the Cherry Trail MRD PLL and black-level settings after
> the generic mode table.
>
> The 19.2 MHz platform uses the per-channel manual white-balance
> registers for digital gain. Program registers 0x5032, 0x5034 and 0x5036
> and expose their 1x-to-4x range, while retaining the existing
> long-exposure gain block for 24 MHz systems.
>
> The manual white-balance register definitions and programming follow
> the GPL-2.0 Intel OV5670 driver, so retain its 2017 Intel copyright
> notice in this file. No proprietary source or tuning binary is included.
>
> Tested on the Lenovo Yoga Book YB1-X91L OV8858 with full-range test bars
> and real 10-bit Bayer frames.
...
> -#define OV8858_LINK_FREQ 360000000U
> -#define OV8858_XVCLK_FREQ 24000000
> +#define OV8858_LINK_FREQ (360 * HZ_PER_MHZ)
> +#define OV8858_XVCLK_FREQ_19_2MHZ (192 * HZ_PER_MHZ / 10)
> +#define OV8858_XVCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
Ah, I completely missed that 19.2 has a fractional part in MHz units.
Then this conversion makes a little sense. Sorry for misleading review
comment.
...
> struct ov8858 {
> struct clk *xvclk;
> + unsigned long xvclk_rate;
> struct gpio_desc *reset_gpio;
> struct gpio_desc *pwdn_gpio;
> struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
> unsigned int num_lanes;
> };
Have you checked the layouts with `pahole`? Can you provide the summary?
...
> - /*
> - * Digital gain is assembled as:
> - * 0x350a[7:0] = dgain[13:6]
> - * 0x350b[5:0] = dgain[5:0]
> - * Reassemble the control value to write it in one go.
> - */
Why is this comment got removed?
> - long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
> - ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
> - OV8858_LONG_DIGIGAIN_H_SHIFT);
> + if (ov8858->xvclk_rate != OV8858_XVCLK_FREQ_19_2MHZ) {
> + long_gain = (gain & OV8858_LONG_DIGIGAIN_L_MASK) |
> + ((gain & OV8858_LONG_DIGIGAIN_H_MASK) <<
> + OV8858_LONG_DIGIGAIN_H_SHIFT);
>
> + return ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
> + long_gain, NULL);
> + }
Instead it's better to keep the current function as a helper. You might need to
rename it first.
> + ret = ov8858_write(ov8858, OV8858_REG_MWB_RED_GAIN, gain, NULL);
> + if (ret)
> + return ret;
> +
> + ret = ov8858_write(ov8858, OV8858_REG_MWB_GREEN_GAIN, gain, NULL);
> + if (ret)
> + return ret;
> +
> + return ov8858_write(ov8858, OV8858_REG_MWB_BLUE_GAIN, gain, NULL);
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs
2026-08-27 18:17 ` [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs Maurizio Casciano
@ 2026-08-27 19:47 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 19:47 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:48PM +0200, Maurizio Casciano wrote:
> Use designated initializers and the standard empty sentinel style
> before adding firmware IDs.
Suggested-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
@ 2026-08-27 19:57 ` Andy Shevchenko
2026-08-28 11:37 ` Sakari Ailus
2026-08-28 11:42 ` Sakari Ailus
2 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 19:57 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:53PM +0200, Maurizio Casciano wrote:
> The YB1-X91L firmware does not describe the complete camera link. Its
> front OV2740 uses two CSI-2 lanes at a 288 MHz link frequency and sends
> a 1932x1092 BGGR transport frame with 12 pixels of horizontal and
> vertical padding around the 1920x1080 image.
>
> Allow the AtomISP bridge to provide per-sensor link frequencies and
> padding, add the matching OV2740 mode, and derive ISP2401 D-PHY timing
> from the sensor link-frequency control.
>
> The register values are hardware configuration facts checked against
> the Lenovo YB1-X91L configuration and physical captures; no proprietary
> driver code or tuning binary is included.
>
> Tested on a Lenovo Yoga Book YB1-X91L with continuous front-camera raw
> capture.
...
> +bool atomisp_csi2_get_sensor_padding(struct device *dev,
> + struct v4l2_area *padding)
> +{
> + struct acpi_device *adev;
> +
> + padding->width = pad_w;
> + padding->height = pad_h;
> +
> + adev = ACPI_COMPANION(dev);
> + if (!adev)
> + return false;
> + padding->width = atomisp_csi2_get_padding_value(adev,
> + "CsiPaddingWidth", pad_w);
padding->width =
atomisp_csi2_get_padding_value(adev, "CsiPaddingWidth", pad_w);
> + padding->height = atomisp_csi2_get_padding_value(adev,
> + "CsiPaddingHeight", pad_h);
In the similar way.
> + return padding->width != pad_w || padding->height != pad_h;
> +}
...
> - ATOMISP_SENSOR_CONFIG("INT33BE", 2, false), /* OV5693 */
> - ATOMISP_SENSOR_CONFIG("INT3477", 4, true), /* OV8858 */
> - ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false), /* OV2740 */
> + ATOMISP_SENSOR_CONFIG("INT33BE", 2, false, 0), /* OV5693 */
> + ATOMISP_SENSOR_CONFIG("INT3477", 4, true, 0), /* OV8858 */
> + /* OV2740 */
> + ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false, 288 * HZ_PER_MHZ),
It's better to keep style
/* OV5693 */
ATOMISP_SENSOR_CONFIG("INT33BE", 2, false, 0),
/* OV8858 */
ATOMISP_SENSOR_CONFIG("INT3477", 4, true, 0),
/* OV2740 */
ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false, 288 * HZ_PER_MHZ),
Check if that can be done in the previous patch(es) already.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 09/11] media: ov2740: add manual white balance controls
2026-08-27 18:17 ` [PATCH v2 09/11] media: ov2740: add manual white balance controls Maurizio Casciano
@ 2026-08-27 20:03 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 20:03 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:54PM +0200, Maurizio Casciano wrote:
> The sensor has separate red, green and blue manual white-balance gain
> registers, but the driver currently writes the same digital-gain value
> to all three channels. This prevents userspace from correcting the
> strong color cast of raw Bayer capture.
>
> Expose red- and blue-balance controls relative to the digital gain,
> update all three channels under group hold, and always release and
> launch the group even when a channel write fails.
>
> Tested on the Yoga Book OV2740 with live gain changes and continuous raw
> capture.
...
> -static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain)
> +static int ov2740_update_mwb_gains(struct ov2740 *ov2740)
> {
> - int ret;
> + u32 blue_gain, green_gain, red_gain;
> + int end_ret, launch_ret, ret;
> +
> + /* Balance controls use 1024 as unity relative to the digital gain. */
> + green_gain = ov2740->digital_gain->val;
> + red_gain = DIV_ROUND_CLOSEST(green_gain * ov2740->red_balance->val,
> + OV2740_DGTL_GAIN_DEFAULT);
> + red_gain = min(red_gain, OV2740_DGTL_GAIN_MAX);
> + blue_gain = DIV_ROUND_CLOSEST(green_gain * ov2740->blue_balance->val,
> + OV2740_DGTL_GAIN_DEFAULT);
> + blue_gain = min(blue_gain, OV2740_DGTL_GAIN_MAX);
Move each of them closer to their first user(s) respectively.
> ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> OV2740_GROUP_HOLD_START);
> if (ret)
> return ret;
red is here
red_gain = DIV_ROUND_CLOSEST(d_gain * ov2740->red_balance->val,
OV2740_DGTL_GAIN_DEFAULT);
red_gain = min(red_gain, OV2740_DGTL_GAIN_MAX);
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain);
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, red_gain);
> if (ret)
> - return ret;
> + goto release_group;
green is here
Perhaps leave d_gain as local variable and assign it with
ov2740->digital_gain->val.
/* Balance controls use 1024 as unity relative to the digital gain. */
u32 d_gain = ov2740->digital_gain->val;
u32 blue_gain, green_gain, red_gain;
int end_ret, launch_ret, ret;
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, d_gain);
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, green_gain);
> if (ret)
> - return ret;
> + goto release_group;
blue is here
> - ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
> - if (ret)
> - return ret;
> + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, blue_gain);
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> - OV2740_GROUP_HOLD_END);
> - if (ret)
> - return ret;
> +release_group:
> + end_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> + OV2740_GROUP_HOLD_END);
> + launch_ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> + OV2740_GROUP_HOLD_LAUNCH);
>
> - ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
> - OV2740_GROUP_HOLD_LAUNCH);
> - return ret;
> + return ret ?: end_ret ?: launch_ret;
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 10/11] media: atomisp: allow raw Bayer capture
2026-08-27 18:17 ` [PATCH v2 10/11] media: atomisp: allow raw Bayer capture Maurizio Casciano
@ 2026-08-27 20:24 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 20:24 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:55PM +0200, Maurizio Casciano wrote:
> AtomISP currently rejects all raw formats and silently substitutes
> YUV420. This prevents userspace camera processing stacks from obtaining
> unprocessed sensor frames.
>
> Enumerate only the raw format matching the sensor media-bus code and
> reconcile raw format requests with the code selected by the sensor.
> Userspace explicitly opts into raw capture by selecting that raw V4L2
> pixel format with VIDIOC_S_FMT; no separate module parameter is needed.
>
> Also apply the per-sensor padding discovered by the CSI-2 bridge when
> enumerating and negotiating processed frame sizes. Raw formats expose
> the full sensor transport frame so ISP2401 can use its copy pipeline.
> Existing systems retain the global padding defaults.
>
> Tested on a Lenovo Yoga Book YB1-X91L with raw capture from its OV2740
> and OV8858 sensors.
...
> void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
> u32 min_pad_w = ISP2400_MIN_PAD_W;
> u32 min_pad_h = ISP2400_MIN_PAD_H;
> struct v4l2_mbus_framefmt *sink;
> + u32 input_padding_w = pad_w;
> + u32 input_padding_h = pad_h;
There are only three callers of this function. Can we have a preparatory patch
that converts this function from
void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
u32 *padding_w, u32 *padding_h);
to
void atomisp_get_padding(struct atomisp_device *isp, struct v4l2_area size,
struct v4l2_area *pad);
(with the respective changes in the implementation of the function)?
...
> - atomisp_get_padding(isp, f->width, f->height, &padding_w, &padding_h);
> + if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
> + padding_w = 0;
> + padding_h = 0;
Define it as
struct v4l2_area padding;
> + } else {
> + atomisp_get_padding(isp, f->width, f->height,
> + &padding_w, &padding_h);
> + }
...
> if (!input->sensor)
> return -EINVAL;
+ blank line.
> + format = atomisp_get_format_bridge(fsize->pixel_format);
> + if (!format)
> + return -EINVAL;
>
> if (input->crop_support)
> return atomisp_enum_framesizes_crop(isp, fsize);
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver
2026-08-27 18:17 ` [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
@ 2026-08-27 20:31 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-27 20:31 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Thu, Aug 27, 2026 at 08:17:56PM +0200, Maurizio Casciano wrote:
> The Lenovo Yoga Book YB1-X91 rear camera contains a WV517S voice-coil
> actuator. Add a V4L2 lens subdevice exposing the standard 10-bit
> FOCUS_ABSOLUTE control and the device ringing-control mode.
>
> Use regmap for register access and tie control updates to runtime PM so
> the IPU bridge sensor link keeps shared power resources active. Propagate
> PM acquisition failures and restore the drive mode and controls after
> resume.
>
> The register addresses and drive-mode value are derived from Intel's
> GPL-2.0 WV517 driver. Retain the Intel copyright notice and document the
> 2026 copyright for this V4L2/regmap implementation.
...
+ container_of.h
+ device.h // dev_get_drvdata()
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
+ types.h // true, false
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
...
> +static int wv517s_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct wv517s_device *wv517s = container_of(ctrl->handler,
> + struct wv517s_device,
> + ctrl_handler);
struct wv517s_device *wv517s =
container_of(ctrl->handler, struct wv517s_device, ctrl_handler);
> + struct device *dev = wv517s->sd.dev;
> + int pm_ret;
> + int ret;
> +
> + /* Runtime resume restores controls while the PM state is RPM_RESUMING. */
> + pm_ret = pm_runtime_get_if_active(dev);
> + if (!pm_ret && !wv517s->resuming)
> + return 0;
> + if (pm_ret < 0)
> + return pm_ret;
> + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
> + ret = regmap_write(wv517s->regmap, WV517S_REG_FOCUS,
> + ctrl->val);
> + else
> + ret = -EINVAL;
The else branch can be done before even trying runtime PM.
> + if (pm_ret > 0)
> + pm_runtime_put(dev);
> +
> + return ret;
> +}
...
> +static int wv517s_probe(struct i2c_client *client)
> +{
struct device *dev = &client->dev;
> + struct wv517s_device *wv517s;
> + int ret;
> +
> + wv517s = devm_kzalloc(&client->dev, sizeof(*wv517s), GFP_KERNEL);
wv517s = devm_kzalloc(dev, sizeof(*wv517s), GFP_KERNEL);
and so on...
> + if (!wv517s)
> + return -ENOMEM;
> +
> + wv517s->regmap = devm_regmap_init_i2c(client, &wv517s_regmap_config);
> + if (IS_ERR(wv517s->regmap))
> + return dev_err_probe(&client->dev, PTR_ERR(wv517s->regmap),
> + "failed to initialize regmap\n");
> +
> + v4l2_i2c_subdev_init(&wv517s->sd, client, &wv517s_subdev_ops);
> + wv517s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + wv517s->sd.internal_ops = &wv517s_internal_ops;
> + wv517s->sd.entity.function = MEDIA_ENT_F_LENS;
> +
> + v4l2_ctrl_handler_init(&wv517s->ctrl_handler, 1);
> + v4l2_ctrl_new_std(&wv517s->ctrl_handler, &wv517s_ctrl_ops,
> + V4L2_CID_FOCUS_ABSOLUTE, 0,
> + WV517S_MAX_FOCUS_POSITION, 1,
> + WV517S_DEFAULT_FOCUS_POSITION);
> + if (wv517s->ctrl_handler.error) {
> + ret = wv517s->ctrl_handler.error;
> + goto err_free_ctrl_handler;
> + }
> + wv517s->sd.ctrl_handler = &wv517s->ctrl_handler;
> +
> + ret = media_entity_pads_init(&wv517s->sd.entity, 0, NULL);
> + if (ret)
> + goto err_free_ctrl_handler;
> +
> + pm_runtime_set_active(&client->dev);
> + pm_runtime_enable(&client->dev);
> +
> + ret = wv517s_resume(&client->dev);
> + if (ret)
> + goto err_disable_pm;
> +
> + ret = v4l2_async_register_subdev(&wv517s->sd);
> + if (ret)
> + goto err_disable_pm;
> +
> + pm_runtime_idle(&client->dev);
> +
> + return 0;
> +
> +err_disable_pm:
> + pm_runtime_disable(&client->dev);
> + media_entity_cleanup(&wv517s->sd.entity);
> +err_free_ctrl_handler:
> + v4l2_ctrl_handler_free(&wv517s->ctrl_handler);
> +
> + return ret;
> +}
...
> +static const struct i2c_device_id wv517s_id_table[] = {
> + { "wv517s" },
Use C99 initialisers.
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, wv517s_id_table);
> +static DEFINE_RUNTIME_DEV_PM_OPS(wv517s_pm_ops, NULL, wv517s_resume, NULL);
Move it up, closer to the implementation of the resume callback.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs
2026-08-27 9:58 ` Andy Shevchenko
2026-08-27 12:14 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
@ 2026-08-27 23:18 ` Maurizio Casciano
2 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 23:18 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, Uwe Kleine-Koenig, linux-media,
Mauro Carvalho Chehab, Sakari Ailus, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
Hi Andy,
As requested, the relevant device objects from the tablet DSDT are:
Device (CA11)
{
Name (_ADR, Zero)
Name (_HID, "INT3477")
Name (_CID, "OVTI8858")
Name (_SUB, "INTL0000")
Name (_DDN, "OV8858")
Name (_UID, One)
}
Device (CA01)
{
Name (_ADR, Zero)
Name (_HID, "OVTI2740")
Name (_CID, "OVTI2740")
Name (_SUB, "INTL0000")
Name (_DDN, "OV2740")
Name (_UID, One)
}
The per-driver ACPI ID patches are included in the v3 series sent as a
fresh thread.
With Best Regards,
Maurizio Casciano
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link
2026-08-27 14:26 ` Andy Shevchenko
@ 2026-08-27 23:18 ` Maurizio Casciano
0 siblings, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 23:18 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab,
Sakari Ailus, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
Hi Andy,
I checked the final v3 object with pahole after moving code before the two
register-list members. The resulting layout is:
struct ov2740_mode {
u32 width; /* 0 4 */
u32 height; /* 4 4 */
u32 hts; /* 8 4 */
u32 vts_def; /* 12 4 */
u32 vts_min; /* 16 4 */
u32 vts_max; /* 20 4 */
u32 link_freq_index; /* 24 4 */
u32 code; /* 28 4 */
const struct ov2740_reg_list init_reg_list; /* 32 16 */
const struct ov2740_reg_list reg_list; /* 48 16 */
/* size: 64, cachelines: 1, members: 10 */
};
There are no holes or padding. This layout is included in the v3 series
sent as a fresh thread.
With Best Regards,
Maurizio Casciano
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 19:46 ` Andy Shevchenko
@ 2026-08-27 23:18 ` Maurizio Casciano
2026-08-28 7:28 ` Sakari Ailus
1 sibling, 0 replies; 53+ messages in thread
From: Maurizio Casciano @ 2026-08-27 23:18 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab,
Sakari Ailus, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, linux-staging, linux-kernel
Hi Andy,
I checked the final v3 object with pahole. Struct ov8858 is 792 bytes,
aligned to 8 bytes, with 4 bytes of tail padding. The xvclk_rate field is
at offset 8 and does not introduce a hole.
The original digital-gain explanation is restored and the existing
long-gain programming remains a separate helper. These changes are in the
v3 series sent as a fresh thread.
With Best Regards,
Maurizio Casciano
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-27 19:46 ` Andy Shevchenko
2026-08-27 23:18 ` Maurizio Casciano
@ 2026-08-28 7:28 ` Sakari Ailus
2026-08-28 7:48 ` Andy Shevchenko
1 sibling, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2026-08-28 7:28 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
Hi Andy, Maurizio,
On Thu, Aug 27, 2026 at 10:46:48PM +0300, Andy Shevchenko wrote:
> > +#define OV8858_XVCLK_FREQ_19_2MHZ (192 * HZ_PER_MHZ / 10)
You can do:
#define OV8858_XVCLK_FREQ_19_2MHZ (19.2 * HZ_PER_MHZ)
i.e. no need for a division, the compiler can handle it.
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup
2026-08-28 7:28 ` Sakari Ailus
@ 2026-08-28 7:48 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-28 7:48 UTC (permalink / raw)
To: Sakari Ailus
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
On Fri, Aug 28, 2026 at 10:28:39AM +0300, Sakari Ailus wrote:
> On Thu, Aug 27, 2026 at 10:46:48PM +0300, Andy Shevchenko wrote:
...
> > > +#define OV8858_XVCLK_FREQ_19_2MHZ (192 * HZ_PER_MHZ / 10)
>
> You can do:
>
> #define OV8858_XVCLK_FREQ_19_2MHZ (19.2 * HZ_PER_MHZ)
>
> i.e. no need for a division, the compiler can handle it.
Yes, but that feels not right (converting float to integer at compile-time).
As I said, I think my suggestion _in this case_ was wrong, let leave them
as they are now defined.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 05/11] media: ov8858: Add INT3477 ACPI ID
2026-08-27 18:17 ` [PATCH v2 05/11] media: ov8858: Add INT3477 " Maurizio Casciano
@ 2026-08-28 11:21 ` Sakari Ailus
2026-08-28 13:24 ` Andy Shevchenko
0 siblings, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2026-08-28 11:21 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
Hi Maurizio,
On Thu, Aug 27, 2026 at 08:17:50PM +0200, Maurizio Casciano wrote:
> Firmware may use INT3477 as the ACPI hardware ID for an OV8858 image
> sensor. Add an ACPI match table for that ID.
>
> Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
> ---
> drivers/media/i2c/ov8858.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
> index b8c2f0d25505..b0ffc366a4e4 100644
> --- a/drivers/media/i2c/ov8858.c
> +++ b/drivers/media/i2c/ov8858.c
> @@ -9,6 +9,7 @@
> #include <linux/clk.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> +#include <linux/device-id/acpi.h>
> #include <linux/gpio/consumer.h>
> #include <linux/i2c.h>
> #include <linux/module.h>
> @@ -2077,6 +2078,12 @@ static void ov8858_remove(struct i2c_client *client)
> pm_runtime_set_suspended(&client->dev);
> }
>
> +static const struct acpi_device_id ov8858_acpi_match[] = {
> + { .id = "INT3477" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(acpi, ov8858_acpi_match);
> +
> static const struct of_device_id ov8858_of_match[] = {
> { .compatible = "ovti,ov8858" },
> { /* sentinel */ },
> @@ -2087,6 +2094,7 @@ static struct i2c_driver ov8858_i2c_driver = {
> .driver = {
> .name = "ov8858",
> .pm = &ov8858_pm_ops,
> + .acpi_match_table = ov8858_acpi_match,
Please use ACPI_PTR() for this one. (Also see
<URL:https://lore.kernel.org/linux-media/20260828084328.257703-1-sakari.ailus@linux.intel.com/T/#t>;
this won't produce a compiler warning then.)
> .of_match_table = ov8858_of_match,
> },
> .probe = ov8858_probe,
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
2026-08-27 19:57 ` Andy Shevchenko
@ 2026-08-28 11:37 ` Sakari Ailus
2026-08-28 11:42 ` Sakari Ailus
2 siblings, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2026-08-28 11:37 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
Hi Maurizio,
On Thu, Aug 27, 2026 at 08:17:53PM +0200, Maurizio Casciano wrote:
> The YB1-X91L firmware does not describe the complete camera link. Its
> front OV2740 uses two CSI-2 lanes at a 288 MHz link frequency and sends
> a 1932x1092 BGGR transport frame with 12 pixels of horizontal and
> vertical padding around the 1920x1080 image.
>
> Allow the AtomISP bridge to provide per-sensor link frequencies and
> padding, add the matching OV2740 mode, and derive ISP2401 D-PHY timing
> from the sensor link-frequency control.
>
> The register values are hardware configuration facts checked against
> the Lenovo YB1-X91L configuration and physical captures; no proprietary
> driver code or tuning binary is included.
>
> Tested on a Lenovo Yoga Book YB1-X91L with continuous front-camera raw
> capture.
>
> Signed-off-by: Maurizio Casciano <mauriziocasciano7@gmail.com>
> Assisted-by: Codex:gpt-5.6-sol sparse
> ---
> drivers/media/i2c/ov2740.c | 103 +++++++++++++++++-
> drivers/media/pci/intel/ipu-bridge.c | 15 ++-
> .../staging/media/atomisp/pci/atomisp_csi2.c | 17 ++-
> .../staging/media/atomisp/pci/atomisp_csi2.h | 2 +
> .../media/atomisp/pci/atomisp_csi2_bridge.c | 53 ++++++++-
> include/media/ipu-bridge.h | 2 +
> 6 files changed, 173 insertions(+), 19 deletions(-)
Please split this into three (at least):
- ov2740
- IPU bridge
- atomisp
- atomisp bridge
>
> diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
> index 18bb3ac9701f..6083dd28e4ad 100644
> --- a/drivers/media/i2c/ov2740.c
> +++ b/drivers/media/i2c/ov2740.c
> @@ -12,12 +12,14 @@
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/unaligned.h>
> +#include <linux/units.h>
> #include <media/v4l2-ctrls.h>
> #include <media/v4l2-device.h>
> #include <media/v4l2-fwnode.h>
>
> -#define OV2740_LINK_FREQ_360MHZ 360000000ULL
> -#define OV2740_LINK_FREQ_180MHZ 180000000ULL
> +#define OV2740_LINK_FREQ_360MHZ (360ULL * HZ_PER_MHZ)
> +#define OV2740_LINK_FREQ_288MHZ (288ULL * HZ_PER_MHZ)
> +#define OV2740_LINK_FREQ_180MHZ (180ULL * HZ_PER_MHZ)
> #define OV2740_SCLK 72000000LL
> #define OV2740_MCLK 19200000
(19.2 * HZ_PER_MHZ)
?
> #define OV2740_DATA_LANES 2
> @@ -91,6 +93,7 @@ struct nvm_data {
>
> enum {
> OV2740_LINK_FREQ_360MHZ_INDEX,
> + OV2740_LINK_FREQ_288MHZ_INDEX,
> OV2740_LINK_FREQ_180MHZ_INDEX,
> };
>
> @@ -130,6 +133,12 @@ struct ov2740_mode {
> /* Link frequency needed for this resolution */
> u32 link_freq_index;
>
> + /* Bayer order produced by this mode */
> + u32 code;
> +
> + /* Optional common settings applied before the mode-specific settings */
> + const struct ov2740_reg_list init_reg_list;
> +
> /* Sensor register settings for this resolution */
> const struct ov2740_reg_list reg_list;
> };
> @@ -142,6 +151,14 @@ static const struct ov2740_reg mipi_data_rate_720mbps[] = {
> {0x0312, 0x11},
> };
>
> +static const struct ov2740_reg mipi_data_rate_576mbps[] = {
> + {0x0302, 0x1e},
> + {0x0303, 0x00},
> + {0x030d, 0x1e},
> + {0x030e, 0x02},
> + {0x0312, 0x01},
> +};
> +
> static const struct ov2740_reg mipi_data_rate_360mbps[] = {
> {0x0302, 0x4b},
> {0x0303, 0x01},
> @@ -458,6 +475,36 @@ static const struct ov2740_reg mode_1932x1092_regs_180mhz[] = {
> {0x4003, 0x40}, /* set Black level to 0x40 */
> };
>
> +/*
> + * Lenovo's Yoga Book vendor driver uses the generic initialization settings
> + * above followed by these mode overrides. Unlike the generic 720 Mbps mode,
> + * this is a 576 Mbps two-lane mode with BGGR output from the optical array.
> + */
> +static const struct ov2740_reg mode_1932x1092_regs_288mhz[] = {
> + {0x0302, 0x1e},
> + {0x0303, 0x00},
> + {0x030d, 0x1e},
> + {0x030e, 0x02},
> + {0x0312, 0x01},
> + {0x3808, 0x07},
> + {0x3809, 0x8c},
> + {0x380a, 0x04},
> + {0x380b, 0x44},
> + {0x380c, 0x04},
> + {0x380d, 0x38},
> + {0x380e, 0x06},
> + {0x380f, 0xf0},
> + {0x3810, 0x00},
> + {0x3811, 0x02},
> + {0x3812, 0x00},
> + {0x3813, 0x02},
> + {0x481f, 0x29},
> + {0x4820, 0x01},
> + {0x4837, 0x1b},
> + {0x5000, 0x7f},
> + {0x58f4, 0x32},
> +};
> +
> static const char * const ov2740_test_pattern_menu[] = {
> "Disabled",
> "Color Bar",
> @@ -468,6 +515,7 @@ static const char * const ov2740_test_pattern_menu[] = {
>
> static const s64 link_freq_menu_items[] = {
> OV2740_LINK_FREQ_360MHZ,
> + OV2740_LINK_FREQ_288MHZ,
> OV2740_LINK_FREQ_180MHZ,
> };
>
> @@ -478,6 +526,12 @@ static const struct ov2740_link_freq_config link_freq_configs[] = {
> .regs = mipi_data_rate_720mbps,
> }
> },
> + [OV2740_LINK_FREQ_288MHZ_INDEX] = {
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mipi_data_rate_576mbps),
> + .regs = mipi_data_rate_576mbps,
> + }
> + },
> [OV2740_LINK_FREQ_180MHZ_INDEX] = {
> .reg_list = {
> .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps),
> @@ -499,6 +553,28 @@ static const struct ov2740_mode supported_modes_360mhz[] = {
> .regs = mode_1932x1092_regs_360mhz,
> },
> .link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX,
> + .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> + },
> +};
> +
> +static const struct ov2740_mode supported_modes_288mhz[] = {
> + {
> + .width = 1932,
> + .height = 1092,
> + .hts = 2160,
> + .vts_min = 1776,
> + .vts_def = 1776,
> + .vts_max = 32767,
> + .init_reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_360mhz),
> + .regs = mode_1932x1092_regs_360mhz,
> + },
> + .reg_list = {
> + .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_288mhz),
> + .regs = mode_1932x1092_regs_288mhz,
> + },
> + .link_freq_index = OV2740_LINK_FREQ_288MHZ_INDEX,
> + .code = MEDIA_BUS_FMT_SBGGR10_1X10,
Why is the mbus code different for this mode? Even the size is the same!
Could you use the same cropping and just a different PLL configuration?
> },
> };
>
> @@ -515,6 +591,7 @@ static const struct ov2740_mode supported_modes_180mhz[] = {
> .regs = mode_1932x1092_regs_180mhz,
> },
> .link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX,
> + .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> },
> };
>
> @@ -842,7 +919,7 @@ static void ov2740_update_pad_format(const struct ov2740_mode *mode,
> {
> fmt->width = mode->width;
> fmt->height = mode->height;
> - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
> + fmt->code = mode->code;
> fmt->field = V4L2_FIELD_NONE;
> }
>
> @@ -966,6 +1043,15 @@ static int ov2740_start_streaming(struct ov2740 *ov2740)
> return ret;
> }
>
> + if (ov2740->cur_mode->init_reg_list.num_of_regs) {
> + reg_list = &ov2740->cur_mode->init_reg_list;
> + ret = ov2740_write_reg_list(ov2740, reg_list);
No need to do this conditionally, and also the reg_list variable is
redundant.
> + if (ret) {
> + dev_err(ov2740->dev, "failed to set common mode registers\n");
> + return ret;
> + }
> + }
> +
> reg_list = &ov2740->cur_mode->reg_list;
> ret = ov2740_write_reg_list(ov2740, reg_list);
> if (ret) {
> @@ -1062,10 +1148,12 @@ static int ov2740_enum_mbus_code(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *sd_state,
> struct v4l2_subdev_mbus_code_enum *code)
> {
> + struct ov2740 *ov2740 = to_ov2740(sd);
> +
> if (code->index > 0)
> return -EINVAL;
>
> - code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
> + code->code = ov2740->supported_modes[0].code;
>
> return 0;
> }
> @@ -1080,7 +1168,7 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd,
> if (fse->index >= ov2740->supported_modes_count)
> return -EINVAL;
>
> - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
> + if (fse->code != supported_modes[0].code)
> return -EINVAL;
>
> fse->min_width = supported_modes[fse->index].width;
> @@ -1178,6 +1266,11 @@ static int ov2740_check_hwcfg(struct ov2740 *ov2740)
> ov2740->supported_modes_count =
> ARRAY_SIZE(supported_modes_360mhz);
> break;
> + case OV2740_LINK_FREQ_288MHZ_INDEX:
> + ov2740->supported_modes = supported_modes_288mhz;
> + ov2740->supported_modes_count =
> + ARRAY_SIZE(supported_modes_288mhz);
> + break;
> case OV2740_LINK_FREQ_180MHZ_INDEX:
> ov2740->supported_modes = supported_modes_180mhz;
> ov2740->supported_modes_count =
> diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
> index 47317c423fad..5730a95767a2 100644
> --- a/drivers/media/pci/intel/ipu-bridge.c
> +++ b/drivers/media/pci/intel/ipu-bridge.c
> @@ -520,11 +520,16 @@ static void ipu_bridge_create_fwnode_properties(
> sensor->prop_names.remote_endpoint,
> sensor->local_ref);
>
> - if (cfg->nr_link_freqs > 0)
> - sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
> - sensor->prop_names.link_frequencies,
> - cfg->link_freqs,
> - cfg->nr_link_freqs);
> + if (sensor->nr_link_freqs > 0)
> + sensor->ep_properties[3] =
> + PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
> + sensor->link_freqs,
> + sensor->nr_link_freqs);
> + else if (cfg->nr_link_freqs > 0)
> + sensor->ep_properties[3] =
> + PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
> + cfg->link_freqs,
> + cfg->nr_link_freqs);
>
> sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
> sensor->prop_names.data_lanes,
> diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
> index 95b9113d75e9..035f1addeaae 100644
> --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
> +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
> @@ -5,6 +5,7 @@
> * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
> */
>
> +#include <media/v4l2-common.h>
> #include <media/v4l2-event.h>
> #include <media/v4l2-mediabus.h>
> #include "atomisp_cmd.h"
> @@ -288,18 +289,19 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
> int dat_termen;
> int dat_settle;
>
> - struct v4l2_control ctrl;
> struct atomisp_device *isp = asd->isp;
> + struct v4l2_subdev *sensor;
> + s64 link_freq;
> int mipi_freq = 0;
> enum atomisp_camera_port port;
> int n;
>
> port = isp->inputs[asd->input_curr].port;
>
> - ctrl.id = V4L2_CID_LINK_FREQ;
> - if (v4l2_g_ctrl
> - (isp->inputs[asd->input_curr].sensor->ctrl_handler, &ctrl) == 0)
> - mipi_freq = ctrl.value;
> + sensor = isp->inputs[asd->input_curr].sensor;
> + link_freq = v4l2_get_link_freq(&sensor->entity.pads[0], 0, 0);
> + if (link_freq > 0 && link_freq <= S32_MAX)
Why S32_MAX? Presumably more is too much, but that shouldn't happen anyway.
> + mipi_freq = link_freq;
>
> clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen, mipi_freq,
> TERMEN_DEFAULT);
> @@ -310,6 +312,11 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
> dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle, mipi_freq,
> SETTLE_DEFAULT);
>
> + dev_dbg(isp->dev,
> + "CSI port %u link frequency %d Hz, clk timing %d/%d, data timing %d/%d\n",
> + port, mipi_freq, clk_termen, clk_settle,
> + dat_termen, dat_settle);
> +
> for (n = 0; n < csi2_port_lanes[port] + 1; n++) {
> hrt_address base = csi2_port_base[port] + csi2_lane_base[n];
>
> diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
> index ec762f8fb922..9298c31bf11e 100644
> --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h
> +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
> @@ -45,6 +45,8 @@ int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2,
> struct v4l2_device *vdev);
> int atomisp_csi2_bridge_init(struct atomisp_device *isp);
> int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp);
> +bool atomisp_csi2_get_sensor_padding(struct device *dev,
> + struct v4l2_area *padding);
>
> void atomisp_csi2_configure(struct atomisp_sub_device *asd);
>
> diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
> index 207ab69385b5..502c951cb687 100644
> --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
> +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
> @@ -15,6 +15,7 @@
> #include <linux/dmi.h>
> #include <linux/platform_data/x86/int3472.h>
> #include <linux/property.h>
> +#include <linux/units.h>
>
> #include <media/ipu-bridge.h>
> #include <media/v4l2-fwnode.h>
> @@ -43,14 +44,16 @@ static const guid_t vcm_dsm_guid =
> 0x9f, 0x48, 0xa9, 0xc3, 0xb5, 0xda, 0x78, 0x9f);
>
> struct atomisp_sensor_config {
> + u64 link_freq;
> int lanes;
> bool vcm;
> };
>
> -#define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM) \
> +#define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM, _LINK_FREQ) \
> { \
> .id = _HID, \
> .driver_data = (long)&((const struct atomisp_sensor_config) { \
> + .link_freq = _LINK_FREQ, \
> .lanes = _LANES, \
> .vcm = _VCM, \
> }) \
> @@ -77,6 +80,9 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
> static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
> /* The vendor driver and sensor modes use two CSI data lanes. */
> { "OVTI2740:00", "CsiLanes", "2" },
> + /* Crop the vendor mode's 1932x1092 transport frame to 1920x1080. */
> + { "OVTI2740:00", "CsiPaddingWidth", "12" },
> + { "OVTI2740:00", "CsiPaddingHeight", "12" },
> {}
> };
>
> @@ -207,6 +213,40 @@ static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int defau
> return default_val;
> }
>
> +static u32 atomisp_csi2_get_padding_value(struct acpi_device *adev,
> + const char *key, u32 default_val)
> +{
> + int val;
> +
> + val = gmin_cfg_get_int(adev, key, default_val);
> + if (val >= 0 && val <= 64 && !(val & 1))
> + return val;
> +
> + acpi_handle_warn(adev->handle, "%s: Invalid %s=%d\n",
> + dev_name(&adev->dev), key, val);
> + return default_val;
> +}
> +
> +bool atomisp_csi2_get_sensor_padding(struct device *dev,
> + struct v4l2_area *padding)
> +{
> + struct acpi_device *adev;
> +
> + padding->width = pad_w;
> + padding->height = pad_h;
> +
> + adev = ACPI_COMPANION(dev);
> + if (!adev)
> + return false;
> +
> + padding->width = atomisp_csi2_get_padding_value(adev,
> + "CsiPaddingWidth", pad_w);
> + padding->height = atomisp_csi2_get_padding_value(adev,
> + "CsiPaddingHeight", pad_h);
> +
> + return padding->width != pad_w || padding->height != pad_h;
> +}
> +
> static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev)
> {
> /* ACPI_PATH_SEGMENT_LENGTH is guaranteed to be big enough for name + 0 term. */
> @@ -373,9 +413,10 @@ static const struct acpi_device_id atomisp_sensor_configs[] = {
> * the sensor fails to start streaming when instantiating
> * an i2c-client for the VCM, so it is disabled for now.
> */
> - ATOMISP_SENSOR_CONFIG("INT33BE", 2, false), /* OV5693 */
> - ATOMISP_SENSOR_CONFIG("INT3477", 4, true), /* OV8858 */
> - ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false), /* OV2740 */
Please only add the OVTI2740 line once the frequency can be assigned.
> + ATOMISP_SENSOR_CONFIG("INT33BE", 2, false, 0), /* OV5693 */
> + ATOMISP_SENSOR_CONFIG("INT3477", 4, true, 0), /* OV8858 */
> + /* OV2740 */
> + ATOMISP_SENSOR_CONFIG("OVTI2740", 2, false, 288 * HZ_PER_MHZ),
> {}
> };
>
> @@ -394,6 +435,10 @@ static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev,
>
> lanes = cfg->lanes;
> vcm = cfg->vcm;
> + if (cfg->link_freq) {
> + sensor->link_freqs[0] = cfg->link_freq;
> + sensor->nr_link_freqs = 1;
> + }
> }
>
> /*
> diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
> index 16fac765456e..633ef0c4cf59 100644
> --- a/include/media/ipu-bridge.h
> +++ b/include/media/ipu-bridge.h
> @@ -135,6 +135,8 @@ struct ipu_sensor {
>
> u8 link;
> u8 lanes;
> + u8 nr_link_freqs;
> + u64 link_freqs[MAX_NUM_LINK_FREQS];
> u32 mclkspeed;
> u32 rotation;
> enum v4l2_fwnode_orientation orientation;
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
2026-08-27 19:57 ` Andy Shevchenko
2026-08-28 11:37 ` Sakari Ailus
@ 2026-08-28 11:42 ` Sakari Ailus
2 siblings, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2026-08-28 11:42 UTC (permalink / raw)
To: Maurizio Casciano
Cc: linux-media, Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi,
Nicholas Roth, Andy Shevchenko, Andy Shevchenko, Hans de Goede,
Greg Kroah-Hartman, Jose Maria Martin, Uwe Kleine-König,
linux-staging, linux-kernel
Hi Maurizio,
On Thu, Aug 27, 2026 at 08:17:53PM +0200, Maurizio Casciano wrote:
> @@ -77,6 +80,9 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
> static struct gmin_cfg_var lenovo_yogabook_x91_vars[] = {
> /* The vendor driver and sensor modes use two CSI data lanes. */
> { "OVTI2740:00", "CsiLanes", "2" },
> + /* Crop the vendor mode's 1932x1092 transport frame to 1920x1080. */
> + { "OVTI2740:00", "CsiPaddingWidth", "12" },
> + { "OVTI2740:00", "CsiPaddingHeight", "12" },
The cropping should be configured from the user space on the CSI-2 receiver
using VIDIOC_SUBDEV_S_SELECTION instead.
--
Regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 05/11] media: ov8858: Add INT3477 ACPI ID
2026-08-28 11:21 ` Sakari Ailus
@ 2026-08-28 13:24 ` Andy Shevchenko
2026-08-28 13:31 ` Andy Shevchenko
0 siblings, 1 reply; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-28 13:24 UTC (permalink / raw)
To: Sakari Ailus
Cc: Maurizio Casciano, linux-media, Mauro Carvalho Chehab, Bingbu Cao,
Jacopo Mondi, Nicholas Roth, Andy Shevchenko, Andy Shevchenko,
Hans de Goede, Greg Kroah-Hartman, Jose Maria Martin,
Uwe Kleine-König, linux-staging, linux-kernel
On Fri, Aug 28, 2026 at 2:21 PM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
> On Thu, Aug 27, 2026 at 08:17:50PM +0200, Maurizio Casciano wrote:
> > + .acpi_match_table = ov8858_acpi_match,
>
> Please use ACPI_PTR() for this one. (Also see
> <URL:https://lore.kernel.org/linux-media/20260828084328.257703-1-sakari.ailus@linux.intel.com/T/#t>;
> this won't produce a compiler warning then.)
Why?! What's the point in having this?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v2 05/11] media: ov8858: Add INT3477 ACPI ID
2026-08-28 13:24 ` Andy Shevchenko
@ 2026-08-28 13:31 ` Andy Shevchenko
0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2026-08-28 13:31 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Sakari Ailus, Maurizio Casciano, linux-media,
Mauro Carvalho Chehab, Bingbu Cao, Jacopo Mondi, Nicholas Roth,
Andy Shevchenko, Hans de Goede, Greg Kroah-Hartman,
Jose Maria Martin, Uwe Kleine-König, linux-staging,
linux-kernel
On Fri, Aug 28, 2026 at 04:24:38PM +0300, Andy Shevchenko wrote:
> On Fri, Aug 28, 2026 at 2:21 PM Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
> > On Thu, Aug 27, 2026 at 08:17:50PM +0200, Maurizio Casciano wrote:
...
> > > + .acpi_match_table = ov8858_acpi_match,
> >
> > Please use ACPI_PTR() for this one. (Also see
> > <URL:https://lore.kernel.org/linux-media/20260828084328.257703-1-sakari.ailus@linux.intel.com/T/#t>;
> > this won't produce a compiler warning then.)
>
> Why?! What's the point in having this?
For the record, while the first patches are okay there, I definitely NAKed
the last one. It's quite controversial.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 53+ messages in thread
end of thread, other threads:[~2026-08-28 13:31 UTC | newest]
Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 13:22 [PATCH 0/8] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-26 13:22 ` [PATCH 1/8] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
2026-08-27 9:40 ` Andy Shevchenko
2026-08-27 11:59 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
2026-08-26 13:22 ` [PATCH 2/8] media: i2c: Add Yoga Book camera ACPI IDs Maurizio Casciano
2026-08-27 9:58 ` Andy Shevchenko
2026-08-27 12:14 ` Sakari Ailus
2026-08-27 12:46 ` Andy Shevchenko
2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 23:18 ` Maurizio Casciano
2026-08-27 12:13 ` Sakari Ailus
2026-08-26 13:22 ` [PATCH 3/8] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
2026-08-26 13:22 ` [PATCH 4/8] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
2026-08-26 13:22 ` [PATCH 5/8] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
2026-08-27 14:26 ` Andy Shevchenko
2026-08-27 23:18 ` Maurizio Casciano
2026-08-26 13:22 ` [PATCH 6/8] media: ov2740: add manual white balance controls Maurizio Casciano
2026-08-27 3:13 ` Cao, Bingbu
2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 14:32 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 7/8] media: atomisp: allow opt-in raw Bayer capture Maurizio Casciano
2026-08-27 14:43 ` Andy Shevchenko
2026-08-26 13:22 ` [PATCH 8/8] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
2026-08-27 12:30 ` Sakari Ailus
2026-08-27 18:19 ` Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 01/11] media: ov8858: Extract digital gain programming Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 02/11] media: ov8858: support 19.2 MHz clock and CHT gain setup Maurizio Casciano
2026-08-27 19:46 ` Andy Shevchenko
2026-08-27 23:18 ` Maurizio Casciano
2026-08-28 7:28 ` Sakari Ailus
2026-08-28 7:48 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 03/11] media: ov2740: Use C99 initializers for ACPI IDs Maurizio Casciano
2026-08-27 19:47 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 04/11] media: ov2740: Add OVTI2740 ACPI ID Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 05/11] media: ov8858: Add INT3477 " Maurizio Casciano
2026-08-28 11:21 ` Sakari Ailus
2026-08-28 13:24 ` Andy Shevchenko
2026-08-28 13:31 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 06/11] media: intel: ipu-bridge: Add Yoga Book camera sensors Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 07/11] media: atomisp: Add Yoga Book camera configuration Maurizio Casciano
2026-08-27 18:17 ` [PATCH v2 08/11] media: atomisp: support the Yoga Book OV2740 link Maurizio Casciano
2026-08-27 19:57 ` Andy Shevchenko
2026-08-28 11:37 ` Sakari Ailus
2026-08-28 11:42 ` Sakari Ailus
2026-08-27 18:17 ` [PATCH v2 09/11] media: ov2740: add manual white balance controls Maurizio Casciano
2026-08-27 20:03 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 10/11] media: atomisp: allow raw Bayer capture Maurizio Casciano
2026-08-27 20:24 ` Andy Shevchenko
2026-08-27 18:17 ` [PATCH v2 11/11] media: i2c: Add WV517S lens actuator driver Maurizio Casciano
2026-08-27 20:31 ` Andy Shevchenko
2026-08-27 18:58 ` [PATCH v2 00/11] media: Add Lenovo Yoga Book YB1-X91 camera support Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).