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;
};