diff --git a/README.md b/README.md index 29157d31..55314bbf 100644 --- a/README.md +++ b/README.md @@ -33,35 +33,36 @@ This repository contains reference drivers and configurations for Intel MIPI CSI | GMSL Sensor | User Guide | Vendor | IPU6EP | IPU6EPMTL | IPU75XA | IPU8 | |---------------------------------------------|---------------------------------------------|-----------------|:------:|:---------:|:-------:|:----:| -| [AR0233+GW5300](doc/ar0233/kernelspace.md) | [User Guide](doc/ar0233/userspace-gmsl.md) | Sensing |❌ |✅|✅|❌| -| [AR0234](doc/ar0234/kernelspace.md) | [User Guide](doc/ar0234/userspace-gmsl.md) | D3 Embedded |❌ |✅|✅|✅| -| [AR0820+GW5300](doc/ar0820/kernelspace.md) | [User Guide](doc/ar0820/userspace-gmsl.md) | Sensing |❌ |✅|✅|❌| -| D457 | [User Guide](doc/d4xx/userspace-gmsl.md) | RealSense |❌ |✅|✅|✅| -| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | D3 Embedded |✅*|✅|✅|✅| -| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | Leopard Imaging |✅*|✅|✅|✅| -| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | Sensing |✅*|✅|✅|✅| +| [AR0233+GW5300](doc/ar0233/kernelspace.md) | [User Guide](doc/ar0233/userspace-gmsl.md) | Sensing |❌ |✅B|✅B|❌ | +| [AR0234](doc/ar0234/kernelspace.md) | [User Guide](doc/ar0234/userspace-gmsl.md) | D3 Embedded |❌ |✅ |✅A|✅A| +| [AR0820+GW5300](doc/ar0820/kernelspace.md) | [User Guide](doc/ar0820/userspace-gmsl.md) | Sensing |❌ |✅B|✅B|❌ | +| D457 | [User Guide](doc/d4xx/userspace-gmsl.md) | RealSense |❌ |✅ |✅ |✅ | +| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | D3 Embedded |✅B|✅ |✅ |✅ | +| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | Leopard Imaging |✅B|✅ |✅ |✅ | +| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-gmsl.md) | Sensing |✅B|✅ |✅ |✅ | | MIPI Sensor | User Guide | Vendor | IPU6EP | IPU6EPMTL | IPU75XA | IPU8 | |---------------------------------------------|---------------------------------------------|-----------------|:------:|:---------:|:-------:|:----:| -| [AR0234](doc/ar0234/kernelspace.md) | [User Guide](doc/ar0234/userspace-mipi.md) | D3 Embedded |❌ |✅ |✅ |✅| -| [AR0830+AP1302](doc/ar0830/kernelspace.md) | [User Guide](doc/ar0830/userspace-mipi.md) | Leopard Imaging |❌ |✅*|✅*|❌| -| IMX415 | [User Guide](doc/imx415/userspace-mipi.md) | Leopard Imaging |❌ |✅*|❌ |❌| -| [IMX586](doc/imx586/kernelspace.md) | [User Guide](doc/imx586/userspace-mipi.md) | Leopard Imaging |❌ |✅*|❌ |❌| -| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-mipi.md) | D3 Embedded |✅*|✅ |✅ |✅| -| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-mipi.md) | Sensing |✅*|✅*|✅*|❌| -| OV13B10 | [User Guide](doc/ov13b10/userspace-mipi.md) | Leopard Imaging |❌ |❌ |✅ |✅| +| [AR0234](doc/ar0234/kernelspace.md) | [User Guide](doc/ar0234/userspace-mipi.md) | D3 Embedded |❌ |✅B|✅B|✅B| +| [AR0830+AP1302](doc/ar0830/kernelspace.md) | [User Guide](doc/ar0830/userspace-mipi.md) | Leopard Imaging |❌ |✅B|✅B|❌ | +| IMX415 | [User Guide](doc/imx415/userspace-mipi.md) | Leopard Imaging |❌ |✅B|❌ |❌ | +| [IMX586](doc/imx586/kernelspace.md) | [User Guide](doc/imx586/userspace-mipi.md) | Leopard Imaging |❌ |✅B|❌ |❌ | +| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-mipi.md) | D3 Embedded |✅B|✅ |✅ |✅ | +| [ISX031](doc/isx031/kernelspace.md) | [User Guide](doc/isx031/userspace-mipi.md) | Sensing |✅B|✅B|✅B|❌ | +| OV13B10 | [User Guide](doc/ov13b10/userspace-mipi.md) | Leopard Imaging |❌ |❌ |✅ |✅ | > **Note:** \ > Items marked with ✅ are enabled by BIOS and ASL method. \ -> Items marked with ✅* are enabled by BIOS method ONLY.\ +> Items marked with ✅B are enabled by BIOS method ONLY. \ +> Items marked with ✅A are enabled by ASL method ONLY. \ > Items marked with ❌ are not enabled by BIOS or ASL method. > **Note:** \ -IPU6EP represents TWL platforms; \ -IPU6EPMTL represents MTL and ARL platforms; \ -IPU75XA represents PTL platforms; \ -IPU8 represents NVL platforms. +> IPU6EP represents TWL platforms. \ +> IPU6EPMTL represents MTL and ARL platforms. \ +> IPU75XA represents PTL platforms. \ +> IPU8 represents NVL platforms. --- ## Supported Ubuntu and Kernel Version @@ -81,8 +82,8 @@ IPU8 represents NVL platforms. | | 26.04 | 7.0 | |✅|✅| > **Note:** \ -> ✅* indicates that ASL support is **NOT AVAILABLE** for 6.18 (BKC) **by default**. Please rebuild the 6.18 Kernel Overlay with ASL support enabled. For more details, please refer to [doc/acpi/kernelspace.md](doc/acpi/kernelspace.md).\ -> To use Intel BKC, please refer [here](#intel-bkc-using-getting-started-guide-gsg) for more details. +> ✅* indicates that ASL support is **NOT AVAILABLE by default** for 6.18 (BKC). Please rebuild the 6.18 Kernel Overlay with ASL support enabled. For more details, please refer to [Kernel Dependencies](doc/acpi/kernelspace.md#kernel-dependencies-for-ssdt-asl-method).\ +> To use Intel BKC, please refer to [Intel BKC Using Getting Started Guide (GSG)](#intel-bkc-using-getting-started-guide-gsg) for more details. --- ## Directory Structure diff --git a/acpi/ipu7/_ser_common_max9295.asl b/acpi/ipu7/_ser_common_max9295.asl index f5a22143..461cc2ba 100644 --- a/acpi/ipu7/_ser_common_max9295.asl +++ b/acpi/ipu7/_ser_common_max9295.asl @@ -96,6 +96,9 @@ Name(_CRS, ResourceTemplate () // _CRS: Current Resource Settings #endif #ifdef DESCH_SER_FSIN_GPIO DESCH_SER_FSIN_GPIO, // Extra pin (e.g. MFP7 on MAX9295) +#endif +#ifdef DESCH_SER_FSIN_GPIO_2 + DESCH_SER_FSIN_GPIO_2, // Extra pin (e.g. MFP7 on MAX9295) #endif } }) @@ -157,6 +160,9 @@ Name (MFP, Package() #ifdef DESCH_SER_FSIN_GPIO Package () { "gmsl-frame-sync-gpio-pin", DESCH_SER_FSIN_GPIO }, #endif + #ifdef DESCH_SER_FSIN_GPIO_2 + Package () { "gmsl-frame-sync-gpio-pin-2", DESCH_SER_FSIN_GPIO_2 }, + #endif }, }) #endif diff --git a/acpi/ipu7/robinson_bay/3_d3_isx031_1_d457_frame_sync.asl b/acpi/ipu7/robinson_bay/3_d3_isx031_1_d457_frame_sync.asl index feb74589..54752247 100644 --- a/acpi/ipu7/robinson_bay/3_d3_isx031_1_d457_frame_sync.asl +++ b/acpi/ipu7/robinson_bay/3_d3_isx031_1_d457_frame_sync.asl @@ -71,7 +71,7 @@ */ -DefinitionBlock ("", "SSDT", 2, "", "IMG_ROB", 0x20260827) +DefinitionBlock ("", "SSDT", 2, "", "IMG_ROB", 0x20260909) { External (_SB.PC00, DeviceObj) @@ -256,6 +256,7 @@ DefinitionBlock ("", "SSDT", 2, "", "IMG_ROB", 0x20260827) #define DESCH_SER_Z_VC Package () { 2 } #define DESCH_SER_U_VC Package () { 3 } #define DESCH_SER_FSIN_GPIO 0 + #define DESCH_SER_FSIN_GPIO_2 1 #define DESCH_SER_FSYNC_RX_ID 7 #define CAM_ALIAS 0x55 #define CAM_LANES 2 @@ -278,6 +279,9 @@ DefinitionBlock ("", "SSDT", 2, "", "IMG_ROB", 0x20260827) #ifdef DESCH_SER_FSIN_GPIO #undef DESCH_SER_FSIN_GPIO #endif +#ifdef DESCH_SER_FSIN_GPIO_2 + #undef DESCH_SER_FSIN_GPIO_2 +#endif #ifdef DESCH_SER_FSYNC_RX_ID #undef DESCH_SER_FSYNC_RX_ID #endif diff --git a/doc/acpi/kernelspace.md b/doc/acpi/kernelspace.md index 597aca29..044e66d5 100644 --- a/doc/acpi/kernelspace.md +++ b/doc/acpi/kernelspace.md @@ -15,6 +15,7 @@ This document contains information of imaging specific ACPI SSDT ASL sources com
  • What are ACPI ASL Source Files?
  • Reference ASL Source Files
  • ASL Source Files for Different Use Cases
  • +
  • Kernel Dependencies for SSDT ASL Method
  • Compile and Load ACPI ASL Source Files
  • @@ -285,6 +286,32 @@ Make sure the below ASL source files are at least a **subset** of your current h

    (Go to Compile and Load)

    +## Kernel Dependencies for SSDT ASL method + +For GMSL setups using SSDT ASL method, the base kernel needs the following kernel configs to be enabled. If they are not enabled, rebuild your kernel with below kernel configs. + + CONFIG_COMPILE_TEST=y + CONFIG_I2C_ATR=m + +>**Note:** `CONFIG_COMPILE_TEST` is a dependency config for `CONFIG_I2C_ATR`. + +To rebuild [Intel Linux-Kernel-Overlay](https://github.com/intel/linux-kernel-overlay.git), follow the instructions in [Intel BKC using Getting Started Guide (GSG)](../../README.md#intel-bkc-using-getting-started-guide-gsg). +Before running `build.sh`, add above configs to `kernel-config/features/ipu.cfg`. + +If you are rebuilding kernel from other source, make sure to add the configs into `.config` and run `make olddefconfig` before building the kernel. + +After installing the kernel and reboot, verify `i2c_atr` module is present and used by `max_serdes`. + +If `CONFIG_I2C_ATR=m`, verify the `i2c_atr` module is loaded: + + lsmod | grep i2c_atr + +You should see output similar to below: + + i2c_atr 24576 1 max_serdes + +If `CONFIG_I2C_ATR=y`, there will be no `lsmod` output for `i2c_atr`. + ## Compile and Load This section contains steps to compile your ASL source files into AML binary tables, and load them into the kernel during boot. The steps are as follows: diff --git a/doc/d4xx/userspace-gmsl.md b/doc/d4xx/userspace-gmsl.md index 65dd0446..df7374ff 100644 --- a/doc/d4xx/userspace-gmsl.md +++ b/doc/d4xx/userspace-gmsl.md @@ -522,6 +522,7 @@ MMAP Command device-name | AIC Link | Stream | device-name | +| ---------------- | ------ | ------------ | | A or DES0 Link 0 | Depth | d4xx-1-depth | | A or DES0 Link 0 | RGB | d4xx-1-rgb | | A or DES0 Link 0 | IR | d4xx-1-ir | @@ -553,7 +554,7 @@ For more details, please refer to icamerasrc device-name property for more detai num-vc | use case | num-vc | -| --- | --- | --- | +| --- | --- | | 1x stream | 1 | | 2x stream | 2 | | 3x stream | 3 | diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c index 8235c4ce..bc72d274 100644 --- a/drivers/media/i2c/isx031.c +++ b/drivers/media/i2c/isx031.c @@ -41,6 +41,15 @@ #define ISX031_REG_MODE_SELECT 0x8A00 #define ISX031_MODE_4LANES_60FPS 0x01 + +#define ISX031_REG_AEMODE 0xABC0 +#define ISX031_AEMODE_AUTO 0x00 +#define ISX031_AEMODE_FULL_ME 0x03 +#define ISX031_REG_FME_SHTVAL 0xABEC +#define ISX031_REG_FME_SHTVAL_UNIT 0xABF0 +#define ISX031_REG_FME_SHTVAL_S1 0xABF4 +#define ISX031_REG_FME_SHTVAL_S1_UNIT 0xABF8 +#define ISX031_FME_SHTVAL_UNIT_USEC 0x03 #define ISX031_MODE_4LANES_30FPS 0x17 #define ISX031_MODE_2LANES_30FPS 0x18 @@ -52,6 +61,12 @@ #define ISX031_REG_SLEEP_50MS 50 /* 50ms */ #define ISX031_REG_SLEEP_200MS 200 /* 200ms */ +/* Exposure is expressed directly in microseconds. */ +#define ISX031_EXPOSURE_MIN 1200 +#define ISX031_EXPOSURE_MAX 266547 +#define ISX031_EXPOSURE_STEP 1 +#define ISX031_EXPOSURE_DEF ISX031_EXPOSURE_MIN + /* To serialize asynchronous callbacks */ static DEFINE_MUTEX(isx031_mutex); @@ -101,6 +116,8 @@ struct isx031_mode { struct isx031 { struct v4l2_subdev sd; struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *ctrl_exposure_auto; + struct v4l2_ctrl *ctrl_exposure; isx031_platform_data *platform_data; struct i2c_client *client; @@ -316,13 +333,14 @@ static int isx031_read_reg(struct i2c_client *client, u16 reg, u16 len, u32 *val msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; + /* Sensor is little-endian for multi-byte fields (see D3 regmap). */ + msgs[1].buf = data_buf; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) return -EIO; - *val = get_unaligned_be32(data_buf); + *val = get_unaligned_le32(data_buf); return 0; } @@ -371,7 +389,8 @@ static int isx031_write_reg(struct i2c_client *client, u16 reg, u16 len, u32 val return -EINVAL; put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + /* Sensor is little-endian for multi-byte fields (see D3 regmap). */ + put_unaligned_le32(val, buf + 2); ret = i2c_master_send(client, buf, len + 2); if (ret != len + 2) @@ -671,15 +690,27 @@ static int isx031_start_streaming(struct isx031 *isx031) } } - ret = __v4l2_ctrl_handler_setup(&isx031->ctrls); + ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); if (ret) { - dev_err(&client->dev, "Failed to setup controls\n"); + dev_err(&client->dev, "Failed to start streaming\n"); return ret; } - ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); + /* + * Mark streaming before handler_setup so s_ctrl sees the sensor as + * streaming (e.g. rejects AEMODE changes with -EBUSY). + */ + isx031->streaming = true; + + /* + * Apply cached V4L2 controls after the STREAMING transition; the + * MODE_SELECT/MODE_SET_F=STREAMING sequence resets AE registers + * (e.g. FME_SHTVAL) to defaults, wiping any pre-stream writes. + */ + ret = __v4l2_ctrl_handler_setup(&isx031->ctrls); if (ret) { - dev_err(&client->dev, "Failed to start streaming\n"); + dev_err(&client->dev, "Failed to setup controls\n"); + isx031->streaming = false; return ret; } @@ -719,7 +750,7 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) goto unlock; } - isx031->streaming = true; + /* isx031->streaming set by isx031_start_streaming() */ } else { isx031_stop_streaming(isx031); @@ -1213,35 +1244,230 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { .open = isx031_open, }; +static const struct v4l2_ctrl_ops isx031_ctrl_ops; + +static int isx031_set_exposure_value(struct i2c_client *client, u32 val) +{ + int ret; + + ret = isx031_write_reg_retry(client, ISX031_REG_FME_SHTVAL_UNIT, + ISX031_REG_LEN_08BIT, + ISX031_FME_SHTVAL_UNIT_USEC); + if (ret) + return ret; + + ret = isx031_write_reg_retry(client, ISX031_REG_FME_SHTVAL, + 4, val); + if (ret) + return ret; + + ret = isx031_write_reg_retry(client, ISX031_REG_FME_SHTVAL_S1_UNIT, + ISX031_REG_LEN_08BIT, + ISX031_FME_SHTVAL_UNIT_USEC); + if (ret) + return ret; + + return isx031_write_reg_retry(client, ISX031_REG_FME_SHTVAL_S1, + 4, val); +} + static int isx031_set_ctrl(struct v4l2_ctrl *ctrl) { - return 0; -}; + struct isx031 *isx031 = container_of(ctrl->handler, + struct isx031, ctrls); + struct i2c_client *client = isx031->client; + u32 val; + int ret = 0; + + /* No HW access needed if exposure is inactive (AE-auto). */ + if (ctrl->id == V4L2_CID_EXPOSURE && + (!isx031->ctrl_exposure_auto || + isx031->ctrl_exposure_auto->val != V4L2_EXPOSURE_MANUAL)) + return 0; + + /* AEMODE must not be changed while streaming. */ + if (ctrl->id == V4L2_CID_EXPOSURE_AUTO && + isx031->streaming && ctrl->cur.val != ctrl->val) + return -EBUSY; + + if (ctrl->id == V4L2_CID_EXPOSURE_AUTO) { + if (ctrl->val != V4L2_EXPOSURE_AUTO && + ctrl->val != V4L2_EXPOSURE_MANUAL) { + dev_err(&client->dev, + "Invalid exposure_auto value: %d\n", ctrl->val); + return -EINVAL; + } + + if (isx031->ctrl_exposure) + v4l2_ctrl_activate(isx031->ctrl_exposure, + ctrl->val == V4L2_EXPOSURE_MANUAL); + } + + /* + * If not powered up, cache the value in v4l2 core; s_ctrl will be + * re-invoked by __v4l2_ctrl_handler_setup() at stream start. + */ + ret = pm_runtime_get_if_in_use(&client->dev); + if (!ret) + return 0; + if (ret < 0) + return ret; + ret = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + val = ctrl->val == V4L2_EXPOSURE_MANUAL ? + ISX031_AEMODE_FULL_ME : ISX031_AEMODE_AUTO; + + ret = isx031_write_reg_retry(client, ISX031_REG_AEMODE, + ISX031_REG_LEN_08BIT, val); + if (ret) { + dev_err(&client->dev, + "Failed to write AEMODE=0x%02x: %d\n", + val, ret); + break; + } + + if (ctrl->val == V4L2_EXPOSURE_MANUAL && isx031->ctrl_exposure) { + ret = isx031_set_exposure_value(client, + isx031->ctrl_exposure->val); + if (ret) + break; + } else { + /* + * AE-auto: force UNIT to us so sensor AE writes and + * status readbacks match the ctrl's uSec range. + */ + ret = isx031_write_reg_retry(client, + ISX031_REG_FME_SHTVAL_UNIT, + ISX031_REG_LEN_08BIT, + ISX031_FME_SHTVAL_UNIT_USEC); + if (ret) { + dev_err(&client->dev, + "Failed to set SHTVAL unit: %d\n", ret); + break; + } + ret = isx031_write_reg_retry(client, + ISX031_REG_FME_SHTVAL_S1_UNIT, + ISX031_REG_LEN_08BIT, + ISX031_FME_SHTVAL_UNIT_USEC); + if (ret) { + dev_err(&client->dev, + "Failed to set SHTVAL_S1 unit: %d\n", ret); + break; + } + } + + dev_dbg(&client->dev, "AEMODE set to 0x%02x (v4l2=%d)\n", + val, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = isx031_set_exposure_value(client, ctrl->val); + if (ret) { + dev_err(&client->dev, + "Failed to set exposure=%d: %d\n", + ctrl->val, ret); + break; + } + dev_dbg(&client->dev, + "Exposure set to %d us (SP1+SP2)\n", ctrl->val); + break; + + default: + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static int isx031_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isx031 *isx031 = container_of(ctrl->handler, + struct isx031, ctrls); + struct i2c_client *client = isx031->client; + u32 reg; + int ret = 0; + + /* If not powered up, leave ctrl->val untouched (framework uses cur.val). */ + ret = pm_runtime_get_if_in_use(&client->dev); + if (!ret) + return 0; + if (ret < 0) + return ret; + ret = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = isx031_read_reg(client, ISX031_REG_FME_SHTVAL, 4, ®); + if (ret) { + dev_err(&client->dev, + "Failed to read SHTVAL: %d\n", ret); + break; + } + + ctrl->val = reg; + dev_dbg(&client->dev, "SHTVAL read %u us\n", reg); + break; + default: + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} static const struct v4l2_ctrl_ops isx031_ctrl_ops = { .s_ctrl = isx031_set_ctrl, + .g_volatile_ctrl = isx031_get_ctrl, }; static int isx031_ctrls_init(struct isx031 *sensor) { struct v4l2_ctrl *ctrl; + struct v4l2_ctrl *link_freq; struct v4l2_ctrl_handler *hdl = &sensor->ctrls; - v4l2_ctrl_handler_init(hdl, 10); + v4l2_ctrl_handler_init(hdl, 16); /* There's a need to set the link frequency because IPU6 dictates it. */ - ctrl = v4l2_ctrl_new_int_menu(hdl, &isx031_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(isx031_link_frequencies) - 1, 0, - isx031_link_frequencies); + link_freq = v4l2_ctrl_new_int_menu(hdl, &isx031_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(isx031_link_frequencies) - 1, 0, + isx031_link_frequencies); + + ctrl = v4l2_ctrl_new_std_menu(hdl, &isx031_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, + 0, + V4L2_EXPOSURE_AUTO); + if (ctrl) { + sensor->ctrl_exposure_auto = ctrl; + ctrl->flags |= V4L2_CTRL_FLAG_UPDATE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + } + + ctrl = v4l2_ctrl_new_std(hdl, &isx031_ctrl_ops, + V4L2_CID_EXPOSURE, + ISX031_EXPOSURE_MIN, + ISX031_EXPOSURE_MAX, + ISX031_EXPOSURE_STEP, + ISX031_EXPOSURE_DEF); + if (ctrl) { + sensor->ctrl_exposure = ctrl; + v4l2_ctrl_activate(ctrl, false); + ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + } if (hdl->error) { v4l2_ctrl_handler_free(hdl); return hdl->error; } - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = hdl; diff --git a/drivers/media/i2c/maxim-serdes/max96717.c b/drivers/media/i2c/maxim-serdes/max96717.c index 2dc24a61..0cfeeec8 100644 --- a/drivers/media/i2c/maxim-serdes/max96717.c +++ b/drivers/media/i2c/maxim-serdes/max96717.c @@ -1445,9 +1445,8 @@ static int max96717_init_tpg(struct max_ser *ser) return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); } -static int max96717_configure_frame_sync(struct max96717_priv *priv) +static int max96717_configure_frame_sync(struct max96717_priv *priv, unsigned int pin) { - unsigned int pin = priv->ser.frame_sync_gpio_pin; int ret; ret = regmap_update_bits(priv->regmap, MAX96717_GPIO_C(pin), @@ -1517,11 +1516,29 @@ static int max96717_init(struct max_ser *ser) "Invalid gmsl-frame-sync-gpio-pin %u\n", ser->frame_sync_gpio_pin); return -EINVAL; + } else { + dev_info(priv->dev, "Enabling external GMSL frame_sync\n"); + ret = max96717_configure_frame_sync(priv, ser->frame_sync_gpio_pin); + + if (ret) + return ret; + } + + if (ser->frame_sync_gpio_pin_2 >= 0) { + if (ser->frame_sync_gpio_pin_2 >= MAX96717_GPIO_NUM) { + dev_err(priv->dev, + "Invalid gmsl-frame-sync-gpio-pin-2 %d\n", + ser->frame_sync_gpio_pin_2); + return -EINVAL; + } + dev_info(priv->dev, + "Enabling external GMSL frame_sync on GPIO pin 2\n"); + ret = max96717_configure_frame_sync(priv, + (unsigned int) ser->frame_sync_gpio_pin_2); + + if (ret) + return ret; } - dev_info(priv->dev, "Enabling external GMSL frame_sync\n"); - ret = max96717_configure_frame_sync(priv); - if (ret) - return ret; } return 0; diff --git a/drivers/media/i2c/maxim-serdes/max_ser.c b/drivers/media/i2c/maxim-serdes/max_ser.c index bfa82fde..d5e199a2 100644 --- a/drivers/media/i2c/maxim-serdes/max_ser.c +++ b/drivers/media/i2c/maxim-serdes/max_ser.c @@ -2137,7 +2137,7 @@ static int max_ser_parse_frame_sync(struct max_ser_priv *priv, struct fwnode_han { struct max_ser *ser = priv->ser; struct fwnode_handle *fsync; - u32 val; + u32 val, pin2; int ret; /* @@ -2166,6 +2166,22 @@ static int max_ser_parse_frame_sync(struct max_ser_priv *priv, struct fwnode_han fwnode_handle_put(fsync); return ret; } + ret = fwnode_property_read_u32(fsync, "gmsl-frame-sync-gpio-pin-2", + &pin2); + if (ret) { + dev_dbg(priv->dev, "Second FSIN GPIO pin not defined\n"); + ser->frame_sync_gpio_pin_2 = -1; + } else { + ser->frame_sync_gpio_pin_2 = (int) pin2; + + // cast pin2 > INT_MAX will result in negative + if (ser->frame_sync_gpio_pin_2 < 0) { + dev_err(priv->dev, + "Invalid gmsl-frame-sync-gpio-pin-2 %u\n", pin2); + fwnode_handle_put(fsync); + return -ERANGE; + } + } ret = fwnode_property_read_u32(fsync, "maxim,rx-id", &ser->frame_sync_rx_id); if (ret) diff --git a/drivers/media/i2c/maxim-serdes/max_ser.h b/drivers/media/i2c/maxim-serdes/max_ser.h index 1ebada3a..c2552551 100644 --- a/drivers/media/i2c/maxim-serdes/max_ser.h +++ b/drivers/media/i2c/maxim-serdes/max_ser.h @@ -141,6 +141,7 @@ struct max_ser { /* Parsed from the ACPI "fsync" child node by max_ser_parse_dt(). */ bool frame_sync_enable; unsigned int frame_sync_gpio_pin; + int frame_sync_gpio_pin_2; unsigned int frame_sync_rx_id; };