From 7739b20d1b0fe1393f3f84bb450af949d965ead0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 23 Aug 2023 12:03:42 +0300 Subject: [PATCH 001/159] media: v4l2-mc: Make v4l2_pipeline_pm_{get,put} deprecated The v4l2_pipeline_pm_get() and v4l2_pipeline_pm_put() functions were needed to control sub-devices' power states before runtime PM. These functions should no longer be used, and instead sub-device drivers should use runtime PM. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 6d7ca3c9c1de48c627769602af9cf841459c1828) --- include/media/v4l2-mc.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index b39586dfba358a..ed0a44b6eadae8 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -143,6 +143,9 @@ int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd, * v4l2_pipeline_pm_get - Increase the use count of a pipeline * @entity: The root entity of a pipeline * + * THIS FUNCTION IS DEPRECATED. DO NOT USE IN NEW DRIVERS. USE RUNTIME PM + * ON SUB-DEVICE DRIVERS INSTEAD. + * * Update the use count of all entities in the pipeline and power entities on. * * This function is intended to be called in video node open. It uses @@ -157,6 +160,9 @@ int v4l2_pipeline_pm_get(struct media_entity *entity); * v4l2_pipeline_pm_put - Decrease the use count of a pipeline * @entity: The root entity of a pipeline * + * THIS FUNCTION IS DEPRECATED. DO NOT USE IN NEW DRIVERS. USE RUNTIME PM + * ON SUB-DEVICE DRIVERS INSTEAD. + * * Update the use count of all entities in the pipeline and power entities off. * * This function is intended to be called in video node release. It uses From 0fa0307265829730c0fe66ca77749ff7a220dd43 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:38 +0300 Subject: [PATCH 002/159] Documentation: media: camera-sensor: Fix typo and vocabulary selection Drop an unneeded double colon, and use 'shall' instead of 'must' for consistency with the rest of the file. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 5adf57f8a9ddf1540a27c132af47bb1349bfe170) Signed-off-by: Jacopo Mondi --- Documentation/driver-api/media/camera-sensor.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 93f4f2536c250d..96cfbc261b6e63 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -13,7 +13,7 @@ Handling clocks Camera sensors have an internal clock tree including a PLL and a number of divisors. The clock tree is generally configured by the driver based on a few -input parameters that are specific to the hardware:: the external clock frequency +input parameters that are specific to the hardware: the external clock frequency and the link frequency. The two parameters generally are obtained from system firmware. **No other frequencies should be used in any circumstances.** @@ -123,7 +123,7 @@ Power management Always use runtime PM to manage the power states of your device. Camera sensor drivers are in no way special in this respect: they are responsible for controlling the power state of the device they otherwise control as well. In -general, the device must be powered on at least when its registers are being +general, the device shall be powered on at least when its registers are being accessed and when it is streaming. Existing camera sensor drivers may rely on the old From 06dd26744dfacd79f381fb58ccd5f9842fc7884a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:39 +0300 Subject: [PATCH 003/159] Documentation: media: camera-sensor: Use link to upstream DT bindings The Documentation/devicetree/bindings/clock/clock-bindings.txt file is deprecated and points to clock-bindings.yaml, which is not hosted in the kernel source tree. Use an HTTPS link to refer to the YAML binding document. While at it, drop "currently" from the paragraph, as the whole file refers to the current recommended practices except where explicitly noted. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit a7acee9965d914202386ff438799feed74bd504c) Signed-off-by: Jacopo Mondi --- Documentation/driver-api/media/camera-sensor.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 96cfbc261b6e63..3510a57ecb9aea 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -32,10 +32,11 @@ can rely on this frequency being used. Devicetree ~~~~~~~~~~ -The currently preferred way to achieve this is using ``assigned-clocks``, -``assigned-clock-parents`` and ``assigned-clock-rates`` properties. See -``Documentation/devicetree/bindings/clock/clock-bindings.txt`` for more -information. The driver then gets the frequency using ``clk_get_rate()``. +The preferred way to achieve this is using ``assigned-clocks``, +``assigned-clock-parents`` and ``assigned-clock-rates`` properties. See the +`clock device tree bindings `_ +for more information. The driver then gets the frequency using +``clk_get_rate()``. This approach has the drawback that there's no guarantee that the frequency hasn't been modified directly or indirectly by another driver, or supported by From 62890b49f5c27437fcc0f2a54e3e3b381fe40275 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:40 +0300 Subject: [PATCH 004/159] Documentation: media: camera-sensor: Move power management section Move the power management section up, just after clocks, as it relates to internal system resources and not features exposed to applications. The text itself is otherwise unchanged. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 3a8b77f735ce0c17a57025426d79764d74c41047) Signed-off-by: Jacopo Mondi --- .../driver-api/media/camera-sensor.rst | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 3510a57ecb9aea..8ab166a2138d65 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -43,6 +43,41 @@ hasn't been modified directly or indirectly by another driver, or supported by the board's clock tree to begin with. Changes to the Common Clock Framework API are required to ensure reliability. +Power management +---------------- + +Always use runtime PM to manage the power states of your device. Camera sensor +drivers are in no way special in this respect: they are responsible for +controlling the power state of the device they otherwise control as well. In +general, the device shall be powered on at least when its registers are being +accessed and when it is streaming. + +Existing camera sensor drivers may rely on the old +struct v4l2_subdev_core_ops->s_power() callback for bridge or ISP drivers to +manage their power state. This is however **deprecated**. If you feel you need +to begin calling an s_power from an ISP or a bridge driver, instead please add +runtime PM support to the sensor driver you are using. Likewise, new drivers +should not use s_power. + +Please see examples in e.g. ``drivers/media/i2c/ov8856.c`` and +``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI +and DT based systems. + +Control framework +~~~~~~~~~~~~~~~~~ + +``v4l2_ctrl_handler_setup()`` function may not be used in the device's runtime +PM ``runtime_resume`` callback, as it has no way to figure out the power state +of the device. This is because the power state of the device is only changed +after the power state transition has taken place. The ``s_ctrl`` callback can be +used to obtain device's power state after the power state transition: + +.. c:function:: int pm_runtime_get_if_in_use(struct device *dev); + +The function returns a non-zero value if it succeeded getting the power count or +runtime PM was disabled, in either of which cases the driver may proceed to +access the device. + Frame size ---------- @@ -118,41 +153,6 @@ rate) on device level in firmware or hardware. This means lower level controls implemented by raw cameras may not be used on uAPI (or even kAPI) to control the frame interval on these devices. -Power management ----------------- - -Always use runtime PM to manage the power states of your device. Camera sensor -drivers are in no way special in this respect: they are responsible for -controlling the power state of the device they otherwise control as well. In -general, the device shall be powered on at least when its registers are being -accessed and when it is streaming. - -Existing camera sensor drivers may rely on the old -struct v4l2_subdev_core_ops->s_power() callback for bridge or ISP drivers to -manage their power state. This is however **deprecated**. If you feel you need -to begin calling an s_power from an ISP or a bridge driver, instead please add -runtime PM support to the sensor driver you are using. Likewise, new drivers -should not use s_power. - -Please see examples in e.g. ``drivers/media/i2c/ov8856.c`` and -``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI -and DT based systems. - -Control framework -~~~~~~~~~~~~~~~~~ - -``v4l2_ctrl_handler_setup()`` function may not be used in the device's runtime -PM ``runtime_resume`` callback, as it has no way to figure out the power state -of the device. This is because the power state of the device is only changed -after the power state transition has taken place. The ``s_ctrl`` callback can be -used to obtain device's power state after the power state transition: - -.. c:function:: int pm_runtime_get_if_in_use(struct device *dev); - -The function returns a non-zero value if it succeeded getting the power count or -runtime PM was disabled, in either of which cases the driver may proceed to -access the device. - Rotation, orientation and flipping ---------------------------------- From 8ae18d53d5baf5f6ddfdd16e131928239bda64ba Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:41 +0300 Subject: [PATCH 005/159] Documentation: media: camera-sensor: Improve power management documentation Camera sensor drivers are highly subject to cargo cult programming, with back practices being copied from old to new drivers. In particular, many drivers implement system and runtime PM incorrectly. As a first step towards fixing this situation, refactor and expand the power management documentation to detail correct usage of system and runtime PM. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 072278810ca27421fb75d5ad8ce5b310ad09a267) Signed-off-by: Jacopo Mondi --- .../driver-api/media/camera-sensor.rst | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 8ab166a2138d65..2acc08142a1a59 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -46,22 +46,48 @@ are required to ensure reliability. Power management ---------------- -Always use runtime PM to manage the power states of your device. Camera sensor -drivers are in no way special in this respect: they are responsible for -controlling the power state of the device they otherwise control as well. In -general, the device shall be powered on at least when its registers are being -accessed and when it is streaming. - -Existing camera sensor drivers may rely on the old -struct v4l2_subdev_core_ops->s_power() callback for bridge or ISP drivers to -manage their power state. This is however **deprecated**. If you feel you need -to begin calling an s_power from an ISP or a bridge driver, instead please add -runtime PM support to the sensor driver you are using. Likewise, new drivers -should not use s_power. - -Please see examples in e.g. ``drivers/media/i2c/ov8856.c`` and -``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI -and DT based systems. +Camera sensors are used in conjunction with other devices to form a camera +pipeline. They must obey the rules listed herein to ensure coherent power +management over the pipeline. + +Camera sensor drivers are responsible for controlling the power state of the +device they otherwise control as well. They shall use runtime PM to manage +power states. Runtime PM shall be enabled at probe time and disabled at remove +time. Drivers should enable runtime PM autosuspend. + +The runtime PM handlers shall handle clocks, regulators, GPIOs, and other +system resources required to power the sensor up and down. For drivers that +don't use any of those resources (such as drivers that support ACPI systems +only), the runtime PM handlers may be left unimplemented. + +In general, the device shall be powered on at least when its registers are +being accessed and when it is streaming. Drivers should use +``pm_runtime_resume_and_get()`` when starting streaming and +``pm_runtime_put()`` or ``pm_runtime_put_autosuspend()`` when stopping +streaming. They may power the device up at probe time (for example to read +identification registers), but should not keep it powered unconditionally after +probe. + +At system suspend time, the whole camera pipeline must stop streaming, and +restart when the system is resumed. This requires coordination between the +camera sensor and the rest of the camera pipeline. Bridge drivers are +responsible for this coordination, and instruct camera sensors to stop and +restart streaming by calling the appropriate subdev operations +(``.s_stream()``, ``.enable_streams()`` or ``.disable_streams()``). Camera +sensor drivers shall therefore **not** keep track of the streaming state to +stop streaming in the PM suspend handler and restart it in the resume handler. +Drivers should in general not implement the system PM handlers. + +Camera sensor drivers shall **not** implement the subdev ``.s_power()`` +operation, as it is deprecated. While this operation is implemented in some +existing drivers as they predate the deprecation, new drivers shall use runtime +PM instead. If you feel you need to begin calling ``.s_power()`` from an ISP or +a bridge driver, instead add runtime PM support to the sensor driver you are +using and drop its ``.s_power()`` handler. + +See examples of runtime PM handling in e.g. ``drivers/media/i2c/ov8856.c`` and +``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI and DT +based systems. Control framework ~~~~~~~~~~~~~~~~~ From a6f30c60c5af2e3c0407a6cea4713ec05c65904e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 12 Sep 2023 11:36:51 +0300 Subject: [PATCH 006/159] media: Add MIPI CSI-2 generic long packet type definition Add a definition for MIPI CSI-2 generic long packet types. The generic long packet types are numbered from 1 to 4. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 2d21fef54800bd6641f85a6821427260439c9539) --- include/media/mipi-csi2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/mipi-csi2.h b/include/media/mipi-csi2.h index c3d8f12234b1fb..40fc0264250d77 100644 --- a/include/media/mipi-csi2.h +++ b/include/media/mipi-csi2.h @@ -19,6 +19,7 @@ #define MIPI_CSI2_DT_NULL 0x10 #define MIPI_CSI2_DT_BLANKING 0x11 #define MIPI_CSI2_DT_EMBEDDED_8B 0x12 +#define MIPI_CSI2_DT_GENERIC_LONG(n) (0x13 + (n) - 1) /* 1..4 */ #define MIPI_CSI2_DT_YUV420_8B 0x18 #define MIPI_CSI2_DT_YUV420_10B 0x19 #define MIPI_CSI2_DT_YUV420_8B_LEGACY 0x1a From 1641823fb518b128be23156aaa8013296cd8d5cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:11 +0200 Subject: [PATCH 007/159] media: v4l2-subdev: Rename pad config 'try_*' fields The try_fmt, try_crop and try_compose fields of the v4l2_subdev_pad_config structure are misnamed (for historical reason) as they also store data for the subdev active configuration. Rename them to format, crop and compose respectively and update the accessor helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 36f2cd3bd496348a4ff8e1de9b23955bd641ce42) --- include/media/v4l2-subdev.h | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ab2a7ef61d420f..1180baa29991b1 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -688,21 +688,18 @@ struct v4l2_subdev_ir_ops { /** * struct v4l2_subdev_pad_config - Used for storing subdev pad information. * - * @try_fmt: &struct v4l2_mbus_framefmt - * @try_crop: &struct v4l2_rect to be used for crop - * @try_compose: &struct v4l2_rect to be used for compose + * @format: &struct v4l2_mbus_framefmt + * @crop: &struct v4l2_rect to be used for crop + * @compose: &struct v4l2_rect to be used for compose * * This structure only needs to be passed to the pad op if the 'which' field * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL. - * - * Note: This struct is also used in active state, and the 'try' prefix is - * historical and to be removed. */ struct v4l2_subdev_pad_config { - struct v4l2_mbus_framefmt try_fmt; - struct v4l2_rect try_crop; - struct v4l2_rect try_compose; + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_rect compose; }; /** @@ -1146,8 +1143,8 @@ struct v4l2_subdev_fh { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) /** - * v4l2_subdev_get_pad_format - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_fmt + * v4l2_subdev_get_pad_format - ancillary routine to get + * &struct v4l2_subdev_pad_config->format * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state @@ -1162,12 +1159,12 @@ v4l2_subdev_get_pad_format(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_fmt; + return &state->pads[pad].format; } /** - * v4l2_subdev_get_pad_crop - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_crop + * v4l2_subdev_get_pad_crop - ancillary routine to get + * &struct v4l2_subdev_pad_config->crop * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. @@ -1182,12 +1179,12 @@ v4l2_subdev_get_pad_crop(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_crop; + return &state->pads[pad].crop; } /** - * v4l2_subdev_get_pad_compose - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_compose + * v4l2_subdev_get_pad_compose - ancillary routine to get + * &struct v4l2_subdev_pad_config->compose * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. @@ -1202,7 +1199,7 @@ v4l2_subdev_get_pad_compose(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_compose; + return &state->pads[pad].compose; } /* From 02718b8dce81999bb092008ba51bec60d4a99844 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:10 +0200 Subject: [PATCH 008/159] media: i2c: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit fd17e3a9a7886ec949ce269a396b67875b51ff45) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/adv7183.c | 2 +- drivers/media/i2c/imx274.c | 12 ++++++------ drivers/media/i2c/mt9m001.c | 2 +- drivers/media/i2c/mt9m111.c | 2 +- drivers/media/i2c/mt9t112.c | 2 +- drivers/media/i2c/mt9v011.c | 2 +- drivers/media/i2c/mt9v111.c | 2 +- drivers/media/i2c/ov2640.c | 2 +- drivers/media/i2c/ov2680.c | 4 ++-- drivers/media/i2c/ov6650.c | 34 +++++++++++++++++++++------------- drivers/media/i2c/ov772x.c | 2 +- drivers/media/i2c/ov9640.c | 2 +- drivers/media/i2c/rj54n1cb0c.c | 2 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/tw9910.c | 2 +- 15 files changed, 41 insertions(+), 33 deletions(-) diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 3659feafac69c8..0754bfefa073ae 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -443,7 +443,7 @@ static int adv7183_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; else - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; return 0; } diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f33b692e6951ef..1886eaab1c2471 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1018,8 +1018,8 @@ static int __imx274_change_compose(struct stimx274 *imx274, int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { - cur_crop = &sd_state->pads->try_crop; - tgt_fmt = &sd_state->pads->try_fmt; + cur_crop = v4l2_subdev_get_pad_crop(&imx274->sd, sd_state, 0); + tgt_fmt = v4l2_subdev_get_pad_format(&imx274->sd, sd_state, 0); } else { cur_crop = &imx274->crop; tgt_fmt = &imx274->format; @@ -1112,7 +1112,7 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, */ fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; else imx274->format = *fmt; @@ -1143,8 +1143,8 @@ static int imx274_get_selection(struct v4l2_subdev *sd, } if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - src_crop = &sd_state->pads->try_crop; - src_fmt = &sd_state->pads->try_fmt; + src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, 0); } else { src_crop = &imx274->crop; src_fmt = &imx274->format; @@ -1215,7 +1215,7 @@ static int imx274_set_selection_crop(struct stimx274 *imx274, sel->r = new_crop; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - tgt_crop = &sd_state->pads->try_crop; + tgt_crop = v4l2_subdev_get_pad_crop(&imx274->sd, sd_state, 0); else tgt_crop = &imx274->crop; diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index ce9568e8391cdd..5790d5b5c2d35e 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -411,7 +411,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index df8d9c9e6a96cf..05f710ee4c140f 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -677,7 +677,7 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 93f34b767027af..e3b9ff37450071 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -982,7 +982,7 @@ static int mt9t112_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 774861ba7747d5..5ff7322b3c8787 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -358,7 +358,7 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd, set_res(sd); } else { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; } return 0; diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 1f7edc0f5b1abe..a5043195fe6f59 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -957,7 +957,7 @@ static int mt9v111_set_format(struct v4l2_subdev *subdev, static int mt9v111_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state) { - sd_state->pads->try_fmt = mt9v111_def_fmt; + *v4l2_subdev_get_pad_format(subdev, sd_state, 0) = mt9v111_def_fmt; return 0; } diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index bb6c9863a5460f..1c04281adc4143 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -994,7 +994,7 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, /* select format */ priv->cfmt_code = mf->code; } else { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; } out: mutex_unlock(&priv->lock); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 6436879f95c015..d4d40acc4c4b16 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -760,9 +760,9 @@ static int ov2680_init_cfg(struct v4l2_subdev *sd, { struct ov2680_dev *sensor = to_ov2680_dev(sd); - sd_state->pads[0].try_crop = ov2680_default_crop; + *v4l2_subdev_get_pad_crop(sd, sd_state, 0) = ov2680_default_crop; - ov2680_fill_format(sensor, &sd_state->pads[0].try_fmt, + ov2680_fill_format(sensor, v4l2_subdev_get_pad_format(sd, sd_state, 0), OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); return 0; } diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 1ad07935f0465a..38d21b40f5d815 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -476,7 +476,7 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { /* pre-select try crop rectangle */ - rect = &sd_state->pads->try_crop; + rect = v4l2_subdev_get_pad_crop(sd, sd_state, 0); } else { /* pre-select active crop rectangle */ @@ -531,8 +531,10 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, ov6650_bind_align_crop_rectangle(&sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_rect *crop = &sd_state->pads->try_crop; - struct v4l2_mbus_framefmt *mf = &sd_state->pads->try_fmt; + struct v4l2_rect *crop = + v4l2_subdev_get_pad_crop(sd, sd_state, 0); + struct v4l2_mbus_framefmt *mf = + v4l2_subdev_get_pad_format(sd, sd_state, 0); /* detect current pad config scaling factor */ bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); @@ -588,9 +590,12 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, /* update media bus format code and frame size */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_pad_format(sd, sd_state, 0); + + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { mf->width = priv->rect.width >> priv->half_scale; @@ -717,23 +722,26 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) - crop = &sd_state->pads->try_crop; + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); else crop = &priv->rect; half_scale = !is_unscaled_ok(mf->width, mf->height, crop); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_pad_format(sd, sd_state, 0); + /* store new mbus frame format code and size in pad config */ - sd_state->pads->try_fmt.width = crop->width >> half_scale; - sd_state->pads->try_fmt.height = crop->height >> half_scale; - sd_state->pads->try_fmt.code = mf->code; + try_fmt->width = crop->width >> half_scale; + try_fmt->height = crop->height >> half_scale; + try_fmt->code = mf->code; /* return default mbus frame format updated with pad config */ *mf = ov6650_def_fmt; - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { int ret = 0; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 386d69c8e07465..8319dba46ddf56 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1222,7 +1222,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index cbaea049531d24..a9adddde10064e 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -547,7 +547,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index b430046f9e2a9f..dbddb7a9f21120 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1009,7 +1009,7 @@ static int rj54n1_set_fmt(struct v4l2_subdev *sd, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index c106e7a7d1f4ed..82976aee5e860a 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -595,7 +595,7 @@ static int saa6752hs_set_fmt(struct v4l2_subdev *sd, f->colorspace = V4L2_COLORSPACE_SMPTE170M; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *f; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *f; return 0; } diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 477a64d8f8ab6f..5233c33b93df21 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -829,7 +829,7 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; return 0; } From 5efa20e8d982a667a6d20b28c7012100e9f173e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 24 Oct 2023 00:17:12 +0200 Subject: [PATCH 009/159] media: v4l2-subdev: Drop outdated comment for v4l2_subdev_pad_config The v4l2_subdev_pad_config structure is not passed to subdev operations anymore. Drop an outdated comment to refers to that old mechanism. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 6078b2b803dba5f3fa62f652d931120fc1f0137d) --- include/media/v4l2-subdev.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 1180baa29991b1..16321204834ed3 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -691,10 +691,6 @@ struct v4l2_subdev_ir_ops { * @format: &struct v4l2_mbus_framefmt * @crop: &struct v4l2_rect to be used for crop * @compose: &struct v4l2_rect to be used for compose - * - * This structure only needs to be passed to the pad op if the 'which' field - * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For - * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL. */ struct v4l2_subdev_pad_config { struct v4l2_mbus_framefmt format; From f64f2cdc6bbde306d2b10652de91d3ff5fa368c6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 27 Oct 2023 11:58:47 +0200 Subject: [PATCH 010/159] media: v4l2-subdev: Fix references to pad config V4L2 subdev operations have moved from operating on a v4l2_subdev_pad_config to a v4l2_subdev_state a long time ago. Fix the few remaining incorrect references to pad config in the documentation. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit c1ac5298867b955e8551bc4a93fcd739ea5db85a) --- Documentation/driver-api/media/v4l2-subdev.rst | 4 ++-- include/media/v4l2-subdev.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index e56b50b3f203ee..a62e4aff31be70 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -562,8 +562,8 @@ device configuration. This is often implemented as e.g. an array of struct v4l2_mbus_framefmt, one entry for each pad, and similarly for crop and compose rectangles. -In addition to the active configuration, each subdev file handle has an array of -struct v4l2_subdev_pad_config, managed by the V4L2 core, which contains the try +In addition to the active configuration, each subdev file handle has a struct +v4l2_subdev_state, managed by the V4L2 core, which contains the try configuration. To simplify the subdev drivers the V4L2 subdev API now optionally supports a diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 16321204834ed3..c23f4b3f6f2cdc 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -769,7 +769,7 @@ struct v4l2_subdev_state { /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * - * @init_cfg: initialize the pad config to default values + * @init_cfg: initialize the subdev state to default values * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler * code. * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE() ioctl handler From eae12f8c7d99a95ca4c59c07a1cea4671a268122 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 09:15:48 +0200 Subject: [PATCH 011/159] media: v4l: subdev: Store the sub-device in the sub-device state Store the sub-device in the sub-device state. This will be needed in e.g. validating pad number when retrieving information for non-stream-aware users. There are expected to be more needs for this in the future. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 52c2575db8faa1d93227a92a32deb2cd66b96614) --- drivers/media/v4l2-core/v4l2-subdev.c | 2 ++ include/media/v4l2-subdev.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index a32ef739eb4490..0e85bad996bfec 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1414,6 +1414,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, else state->lock = &state->_lock; + state->sd = sd; + /* Drivers that support streams do not need the legacy pad config */ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) { state->pads = kvcalloc(sd->entity.num_pads, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index c23f4b3f6f2cdc..f4f5b51cd76ef4 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -749,6 +749,7 @@ struct v4l2_subdev_krouting { * * @_lock: default for 'lock' * @lock: mutex for the state. May be replaced by the user. + * @sd: the sub-device which the state is related to * @pads: &struct v4l2_subdev_pad_config array * @routing: routing table for the subdev * @stream_configs: stream configurations (only for V4L2_SUBDEV_FL_STREAMS) @@ -761,6 +762,7 @@ struct v4l2_subdev_state { /* lock for the struct v4l2_subdev_state fields */ struct mutex _lock; struct mutex *lock; + struct v4l2_subdev *sd; struct v4l2_subdev_pad_config *pads; struct v4l2_subdev_krouting routing; struct v4l2_subdev_stream_configs stream_configs; From f26dfaf71236684ddde3b92c2cfd31902ac4f4ce Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 09:31:37 +0200 Subject: [PATCH 012/159] media: v4l: subdev: Also return pads array information on stream functions There are two sets of functions that return information from sub-device state, one for stream-unaware users and another for stream-aware users. Add support for stream-aware functions to return format, crop and compose information from pad-based array that are functionally equivalent to the old, stream-unaware ones. Also check state is non-NULL, in order to guard against old drivers potentially calling this with NULL state for active formats or selection rectangles. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 049fa16b81c276f25a09abaa4e8f743470c3cdc8) --- drivers/media/v4l2-core/v4l2-subdev.c | 63 +++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 9 ++-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 0e85bad996bfec..d435cf8be03bed 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1657,6 +1657,27 @@ v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].format; + } + lockdep_assert_held(state->lock); stream_configs = &state->stream_configs; @@ -1678,6 +1699,27 @@ v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].crop; + } + lockdep_assert_held(state->lock); stream_configs = &state->stream_configs; @@ -1699,6 +1741,27 @@ v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].compose; + } + lockdep_assert_held(state->lock); stream_configs = &state->stream_configs; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f4f5b51cd76ef4..9a4e464bc069e0 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1544,7 +1544,8 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + * stream in the subdev state. * - * If the state does not contain the given pad + stream, NULL is returned. + * For stream-unaware drivers the format for the corresponding pad is returned. + * If the pad does not exist, NULL is returned. */ struct v4l2_mbus_framefmt * v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, @@ -1559,7 +1560,8 @@ v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, * This returns a pointer to crop rectangle for the given pad + stream in the * subdev state. * - * If the state does not contain the given pad + stream, NULL is returned. + * For stream-unaware drivers the crop rectangle for the corresponding pad is + * returned. If the pad does not exist, NULL is returned. */ struct v4l2_rect * v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, @@ -1575,7 +1577,8 @@ v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, * This returns a pointer to compose rectangle for the given pad + stream in the * subdev state. * - * If the state does not contain the given pad + stream, NULL is returned. + * For stream-unaware drivers the compose rectangle for the corresponding pad is + * returned. If the pad does not exist, NULL is returned. */ struct v4l2_rect * v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, From edb51edbfffb85405d9158765fd7d3457d6d11ba Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 10:16:06 +0200 Subject: [PATCH 013/159] media: v4l: subdev: Rename sub-device state information access functions Rename the sub-devices state information access functions, removing "_stream" from them and replacing "format" by "ffmt". This makes them shorter and so more convenient to use. No other sets of functions will be needed to access this information. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit d0fde6aae2bacdc024fff43461ba0f325375fa97) --- drivers/media/i2c/ds90ub913.c | 3 +-- drivers/media/i2c/ds90ub953.c | 3 +-- drivers/media/i2c/ds90ub960.c | 12 ++++----- .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 10 +++---- drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++---------- include/media/v4l2-subdev.h | 19 +++++++------ 6 files changed, 34 insertions(+), 40 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 4bfa3b3cf619b3..ba768858f9cf9b 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -426,8 +426,7 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, } /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index dc394e22a42c43..c362d82ce46e70 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -560,8 +560,7 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return v4l2_subdev_get_fmt(sd, state, format); /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 8ba5750f5a2319..37aa2e98c15b6c 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2451,9 +2451,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, if (rx_data[nport].num_streams > 2) return -EPIPE; - fmt = v4l2_subdev_state_get_stream_format(state, - route->sink_pad, - route->sink_stream); + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); if (!fmt) return -EPIPE; @@ -2844,8 +2843,8 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, const struct ub960_format_info *ub960_fmt; struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_state_get_stream_format(state, pad, - route->source_stream); + fmt = v4l2_subdev_state_get_format(state, pad, + route->source_stream); if (!fmt) { ret = -EINVAL; @@ -2893,8 +2892,7 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, if (!ub960_find_format(format->format.code)) format->format.code = ub960_formats[0].code; - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index c9a4d091b57074..26caca55f9d666 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -58,7 +58,7 @@ static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, return -EINVAL; } - fmt = v4l2_subdev_state_get_stream_format(state, port, 0); + fmt = v4l2_subdev_state_get_format(state, port, 0); if (!fmt) return -EINVAL; @@ -280,8 +280,7 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, * Set the format on the sink stream and propagate it to the source * streams. */ - sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad, - fmt->stream); + sink_fmt = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream); if (!sink_fmt) return -EINVAL; @@ -295,8 +294,9 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, route->sink_stream != fmt->stream) continue; - source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad, - route->source_stream); + source_fmt = v4l2_subdev_state_get_format(state, + route->source_pad, + route->source_stream); if (!source_fmt) return -EINVAL; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d435cf8be03bed..51e94099fa414a 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -176,7 +176,7 @@ static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + if (!v4l2_subdev_state_get_format(state, pad, stream)) return -EINVAL; return 0; #else @@ -1554,8 +1554,8 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_mbus_framefmt *fmt; if (sd->flags & V4L2_SUBDEV_FL_STREAMS) - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); else if (format->pad < sd->entity.num_pads && format->stream == 0) fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); else @@ -1651,8 +1651,8 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) +v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1690,11 +1690,11 @@ v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format); +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_format); struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) +v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1732,11 +1732,11 @@ v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop); +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_crop); struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) +v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1774,7 +1774,7 @@ v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_compose); int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, @@ -1820,8 +1820,7 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, if (ret) return NULL; - return v4l2_subdev_state_get_stream_format(state, other_pad, - other_stream); + return v4l2_subdev_state_get_format(state, other_pad, other_stream); } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9a4e464bc069e0..61515b18f27643 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1536,7 +1536,7 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, const struct v4l2_mbus_framefmt *fmt); /** - * v4l2_subdev_state_get_stream_format() - Get pointer to a stream format + * v4l2_subdev_state_get_format() - Get pointer to a stream format * @state: subdevice state * @pad: pad id * @stream: stream id @@ -1548,11 +1548,11 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, * If the pad does not exist, NULL is returned. */ struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); +v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); /** - * v4l2_subdev_state_get_stream_crop() - Get pointer to a stream crop rectangle + * v4l2_subdev_state_get_crop() - Get pointer to a stream crop rectangle * @state: subdevice state * @pad: pad id * @stream: stream id @@ -1564,12 +1564,11 @@ v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, * returned. If the pad does not exist, NULL is returned. */ struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); +v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); /** - * v4l2_subdev_state_get_stream_compose() - Get pointer to a stream compose - * rectangle + * v4l2_subdev_state_get_compose() - Get pointer to a stream compose rectangle * @state: subdevice state * @pad: pad id * @stream: stream id @@ -1581,8 +1580,8 @@ v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, * returned. If the pad does not exist, NULL is returned. */ struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); +v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); /** * v4l2_subdev_routing_find_opposite_end() - Find the opposite stream From 51e375096393f7c83f0e99617d3b643487bc51b0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 10:54:24 +0200 Subject: [PATCH 014/159] media: v4l: subdev: v4l2_subdev_state_get_format always returns format now Now that v4l2_subdev_state_get_format() always returns format, don't call alternative v4l2_subdev_get_pad_format() anymore. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 8824170e95d72d7583bd8e897246685d3856455a) --- drivers/media/v4l2-core/v4l2-subdev.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 51e94099fa414a..c36bc541e62601 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1553,14 +1553,7 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { struct v4l2_mbus_framefmt *fmt; - if (sd->flags & V4L2_SUBDEV_FL_STREAMS) - fmt = v4l2_subdev_state_get_format(state, format->pad, - format->stream); - else if (format->pad < sd->entity.num_pads && format->stream == 0) - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - else - fmt = NULL; - + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; From 52681651d16b0b712f782761d88c43cca8f5a137 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Oct 2023 12:26:34 +0200 Subject: [PATCH 015/159] media: v4l: subdev: Make stream argument optional in state access functions The sub-device state access functions take three arguments: sub-device state, pad and stream. The stream is not relevant for the majority of drivers and having to specify 0 for the stream is considered a nuisance. Provide a two-argument macros for these state access functions to cover the needs of stream-unaware users. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 791765b426df4d2a1b1be0617c2c0bf2248bfaba) --- drivers/media/v4l2-core/v4l2-subdev.c | 18 +++++----- include/media/v4l2-subdev.h | 49 ++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index c36bc541e62601..ed4fc2bd0cd3ef 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1644,8 +1644,8 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream) +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1683,11 +1683,11 @@ v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_format); +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format); struct v4l2_rect * -v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream) +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1725,11 +1725,11 @@ v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_crop); +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop); struct v4l2_rect * -v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream) +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) { struct v4l2_subdev_stream_configs *stream_configs; unsigned int i; @@ -1767,7 +1767,7 @@ v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, return NULL; } -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_compose); +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose); int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 61515b18f27643..11cbb82b22259c 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1535,11 +1535,18 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *routing, const struct v4l2_mbus_framefmt *fmt); +/* + * A macro to generate the macro or function name for sub-devices state access + * wrapper macros below. + */ +#define __v4l2_subdev_state_gen_call(NAME, _1, ARG, ...) \ + __v4l2_subdev_state_get_ ## NAME ## ARG + /** * v4l2_subdev_state_get_format() - Get pointer to a stream format * @state: subdevice state * @pad: pad id - * @stream: stream id + * @...: stream id (optional argument) * * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + * stream in the subdev state. @@ -1547,15 +1554,29 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, * For stream-unaware drivers the format for the corresponding pad is returned. * If the pad does not exist, NULL is returned. */ +/* + * Wrap v4l2_subdev_state_get_format(), allowing the function to be called with + * two or three arguments. The purpose of the __v4l2_subdev_state_get_format() + * macro below is to come up with the name of the function or macro to call, + * using the last two arguments (_stream and _pad). The selected function or + * macro is then called using the arguments specified by the caller. A similar + * arrangement is used for v4l2_subdev_state_crop() and + * v4l2_subdev_state_compose() below. + */ +#define v4l2_subdev_state_get_format(state, pad, ...) \ + __v4l2_subdev_state_gen_call(format, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_format_pad(state, pad) \ + __v4l2_subdev_state_get_format(state, pad, 0) struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream); +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); /** * v4l2_subdev_state_get_crop() - Get pointer to a stream crop rectangle * @state: subdevice state * @pad: pad id - * @stream: stream id + * @...: stream id (optional argument) * * This returns a pointer to crop rectangle for the given pad + stream in the * subdev state. @@ -1563,15 +1584,20 @@ v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, unsigned int pad, * For stream-unaware drivers the crop rectangle for the corresponding pad is * returned. If the pad does not exist, NULL is returned. */ +#define v4l2_subdev_state_get_crop(state, pad, ...) \ + __v4l2_subdev_state_gen_call(crop, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_crop_pad(state, pad) \ + __v4l2_subdev_state_get_crop(state, pad, 0) struct v4l2_rect * -v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream); +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); /** * v4l2_subdev_state_get_compose() - Get pointer to a stream compose rectangle * @state: subdevice state * @pad: pad id - * @stream: stream id + * @...: stream id (optional argument) * * This returns a pointer to compose rectangle for the given pad + stream in the * subdev state. @@ -1579,9 +1605,14 @@ v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, * For stream-unaware drivers the compose rectangle for the corresponding pad is * returned. If the pad does not exist, NULL is returned. */ +#define v4l2_subdev_state_get_compose(state, pad, ...) \ + __v4l2_subdev_state_gen_call(compose, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_compose_pad(state, pad) \ + __v4l2_subdev_state_get_compose(state, pad, 0) struct v4l2_rect * -v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream); +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); /** * v4l2_subdev_routing_find_opposite_end() - Find the opposite stream From 00026abb3125721de3a0e74acb24f1be0402474d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 10 Nov 2023 08:10:26 +0100 Subject: [PATCH 016/159] media: v4l: subdev: Always compile sub-device state access functions Compile sub-device state information access functions v4l2_subdev_state_get_{format,crop,compose} unconditionally as they are now also used by plain V4L2 drivers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 981e0d4c724fae1fbc281b3b25b18b041b793f4d) --- drivers/media/v4l2-core/v4l2-subdev.c | 252 +++++++++++++------------- include/media/v4l2-subdev.h | 128 ++++++------- 2 files changed, 190 insertions(+), 190 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index ed4fc2bd0cd3ef..262cf88dee33c4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1497,6 +1497,132 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) } EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); +struct v4l2_mbus_framefmt * +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].format; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format); + +struct v4l2_rect * +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].crop; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].crop; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop); + +struct v4l2_rect * +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + /* + * Set the pad to 0 on error as this is aligned with the + * behaviour of the pad state information access functions. The + * purpose of setting pad to 0 here is to avoid accessing memory + * outside the pads array, but still issuing warning of the + * invalid access while making the caller's error handling + * easier. + */ + if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) + pad = 0; + + return &state->pads[pad].compose; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].compose; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose); + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int @@ -1643,132 +1769,6 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); -struct v4l2_mbus_framefmt * -__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - if (WARN_ON_ONCE(!state)) - return NULL; - - if (state->pads) { - if (stream) - return NULL; - - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; - - return &state->pads[pad].format; - } - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].fmt; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format); - -struct v4l2_rect * -__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, - u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - if (WARN_ON_ONCE(!state)) - return NULL; - - if (state->pads) { - if (stream) - return NULL; - - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; - - return &state->pads[pad].crop; - } - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].crop; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop); - -struct v4l2_rect * -__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - if (WARN_ON_ONCE(!state)) - return NULL; - - if (state->pads) { - if (stream) - return NULL; - - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; - - return &state->pads[pad].compose; - } - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].compose; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose); - int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, u32 *other_stream) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 11cbb82b22259c..f82227507c08a6 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1471,70 +1471,6 @@ v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd) return sd->active_state; } -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - -/** - * v4l2_subdev_get_fmt() - Fill format based on state - * @sd: subdevice - * @state: subdevice state - * @format: pointer to &struct v4l2_subdev_format - * - * Fill @format->format field based on the information in the @format struct. - * - * This function can be used by the subdev drivers which support active state to - * implement v4l2_subdev_pad_ops.get_fmt if the subdev driver does not need to - * do anything special in their get_fmt op. - * - * Returns 0 on success, error value otherwise. - */ -int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format); - -/** - * v4l2_subdev_set_routing() - Set given routing to subdev state - * @sd: The subdevice - * @state: The subdevice state - * @routing: Routing that will be copied to subdev state - * - * This will release old routing table (if any) from the state, allocate - * enough space for the given routing, and copy the routing. - * - * This can be used from the subdev driver's set_routing op, after validating - * the routing. - */ -int v4l2_subdev_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - const struct v4l2_subdev_krouting *routing); - -struct v4l2_subdev_route * -__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, - struct v4l2_subdev_route *route); - -/** - * for_each_active_route - iterate on all active routes of a routing table - * @routing: The routing table - * @route: The route iterator - */ -#define for_each_active_route(routing, route) \ - for ((route) = NULL; \ - ((route) = __v4l2_subdev_next_active_route((routing), (route)));) - -/** - * v4l2_subdev_set_routing_with_fmt() - Set given routing and format to subdev - * state - * @sd: The subdevice - * @state: The subdevice state - * @routing: Routing that will be copied to subdev state - * @fmt: Format used to initialize all the streams - * - * This is the same as v4l2_subdev_set_routing, but additionally initializes - * all the streams using the given format. - */ -int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - const struct v4l2_subdev_krouting *routing, - const struct v4l2_mbus_framefmt *fmt); - /* * A macro to generate the macro or function name for sub-devices state access * wrapper macros below. @@ -1614,6 +1550,70 @@ struct v4l2_rect * __v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, unsigned int pad, u32 stream); +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + +/** + * v4l2_subdev_get_fmt() - Fill format based on state + * @sd: subdevice + * @state: subdevice state + * @format: pointer to &struct v4l2_subdev_format + * + * Fill @format->format field based on the information in the @format struct. + * + * This function can be used by the subdev drivers which support active state to + * implement v4l2_subdev_pad_ops.get_fmt if the subdev driver does not need to + * do anything special in their get_fmt op. + * + * Returns 0 on success, error value otherwise. + */ +int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format); + +/** + * v4l2_subdev_set_routing() - Set given routing to subdev state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state + * + * This will release old routing table (if any) from the state, allocate + * enough space for the given routing, and copy the routing. + * + * This can be used from the subdev driver's set_routing op, after validating + * the routing. + */ +int v4l2_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + const struct v4l2_subdev_krouting *routing); + +struct v4l2_subdev_route * +__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, + struct v4l2_subdev_route *route); + +/** + * for_each_active_route - iterate on all active routes of a routing table + * @routing: The routing table + * @route: The route iterator + */ +#define for_each_active_route(routing, route) \ + for ((route) = NULL; \ + ((route) = __v4l2_subdev_next_active_route((routing), (route)));) + +/** + * v4l2_subdev_set_routing_with_fmt() - Set given routing and format to subdev + * state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state + * @fmt: Format used to initialize all the streams + * + * This is the same as v4l2_subdev_set_routing, but additionally initializes + * all the streams using the given format. + */ +int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + const struct v4l2_subdev_krouting *routing, + const struct v4l2_mbus_framefmt *fmt); + /** * v4l2_subdev_routing_find_opposite_end() - Find the opposite stream * @routing: routing used to find the opposite side From 89954ba4b21ff8a25501383ecd5d49f1129a6e47 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 10:09:21 +0200 Subject: [PATCH 017/159] media: v4l: subdev: Remove stream-unaware sub-device state access Remove stream-unaware sub-device state access functions and macros. These are no longer used. [Sakari Ailus: Resolve a minor conflict in removed code.] Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 34dfd1dd52660dc85d43c92c2cb8029efb2bb8f3) --- include/media/v4l2-subdev.h | 77 ------------------------------------- 1 file changed, 77 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f82227507c08a6..0a0aecdd176ee3 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1138,83 +1138,6 @@ struct v4l2_subdev_fh { #define to_v4l2_subdev_fh(fh) \ container_of(fh, struct v4l2_subdev_fh, vfh) -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - -/** - * v4l2_subdev_get_pad_format - ancillary routine to get - * &struct v4l2_subdev_pad_config->format - * - * @sd: pointer to &struct v4l2_subdev - * @state: pointer to &struct v4l2_subdev_state - * @pad: index of the pad in the &struct v4l2_subdev_state->pads array - */ -static inline struct v4l2_mbus_framefmt * -v4l2_subdev_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - unsigned int pad) -{ - if (WARN_ON(!state)) - return NULL; - if (WARN_ON(pad >= sd->entity.num_pads)) - pad = 0; - return &state->pads[pad].format; -} - -/** - * v4l2_subdev_get_pad_crop - ancillary routine to get - * &struct v4l2_subdev_pad_config->crop - * - * @sd: pointer to &struct v4l2_subdev - * @state: pointer to &struct v4l2_subdev_state. - * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. - */ -static inline struct v4l2_rect * -v4l2_subdev_get_pad_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - unsigned int pad) -{ - if (WARN_ON(!state)) - return NULL; - if (WARN_ON(pad >= sd->entity.num_pads)) - pad = 0; - return &state->pads[pad].crop; -} - -/** - * v4l2_subdev_get_pad_compose - ancillary routine to get - * &struct v4l2_subdev_pad_config->compose - * - * @sd: pointer to &struct v4l2_subdev - * @state: pointer to &struct v4l2_subdev_state. - * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. - */ -static inline struct v4l2_rect * -v4l2_subdev_get_pad_compose(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - unsigned int pad) -{ - if (WARN_ON(!state)) - return NULL; - if (WARN_ON(pad >= sd->entity.num_pads)) - pad = 0; - return &state->pads[pad].compose; -} - -/* - * Temprary helpers until uses of v4l2_subdev_get_try_* functions have been - * renamed - */ -#define v4l2_subdev_get_try_format(sd, state, pad) \ - v4l2_subdev_get_pad_format(sd, state, pad) - -#define v4l2_subdev_get_try_crop(sd, state, pad) \ - v4l2_subdev_get_pad_crop(sd, state, pad) - -#define v4l2_subdev_get_try_compose(sd, state, pad) \ - v4l2_subdev_get_pad_compose(sd, state, pad) - -#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ - extern const struct v4l2_file_operations v4l2_subdev_fops; /** From e46dcc901b770f0685e4130996823ada5916fd68 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 1 Oct 2024 16:27:35 +0300 Subject: [PATCH 018/159] Revert "media: v4l: subdev: Remove stream-unaware sub-device state access" This reverts commit 064468855c423bfea9299af66239e6fd96709502. --- include/media/v4l2-subdev.h | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 0a0aecdd176ee3..f82227507c08a6 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1138,6 +1138,83 @@ struct v4l2_subdev_fh { #define to_v4l2_subdev_fh(fh) \ container_of(fh, struct v4l2_subdev_fh, vfh) +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + +/** + * v4l2_subdev_get_pad_format - ancillary routine to get + * &struct v4l2_subdev_pad_config->format + * + * @sd: pointer to &struct v4l2_subdev + * @state: pointer to &struct v4l2_subdev_state + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array + */ +static inline struct v4l2_mbus_framefmt * +v4l2_subdev_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + if (WARN_ON(!state)) + return NULL; + if (WARN_ON(pad >= sd->entity.num_pads)) + pad = 0; + return &state->pads[pad].format; +} + +/** + * v4l2_subdev_get_pad_crop - ancillary routine to get + * &struct v4l2_subdev_pad_config->crop + * + * @sd: pointer to &struct v4l2_subdev + * @state: pointer to &struct v4l2_subdev_state. + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. + */ +static inline struct v4l2_rect * +v4l2_subdev_get_pad_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + if (WARN_ON(!state)) + return NULL; + if (WARN_ON(pad >= sd->entity.num_pads)) + pad = 0; + return &state->pads[pad].crop; +} + +/** + * v4l2_subdev_get_pad_compose - ancillary routine to get + * &struct v4l2_subdev_pad_config->compose + * + * @sd: pointer to &struct v4l2_subdev + * @state: pointer to &struct v4l2_subdev_state. + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. + */ +static inline struct v4l2_rect * +v4l2_subdev_get_pad_compose(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + unsigned int pad) +{ + if (WARN_ON(!state)) + return NULL; + if (WARN_ON(pad >= sd->entity.num_pads)) + pad = 0; + return &state->pads[pad].compose; +} + +/* + * Temprary helpers until uses of v4l2_subdev_get_try_* functions have been + * renamed + */ +#define v4l2_subdev_get_try_format(sd, state, pad) \ + v4l2_subdev_get_pad_format(sd, state, pad) + +#define v4l2_subdev_get_try_crop(sd, state, pad) \ + v4l2_subdev_get_pad_crop(sd, state, pad) + +#define v4l2_subdev_get_try_compose(sd, state, pad) \ + v4l2_subdev_get_pad_compose(sd, state, pad) + +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ + extern const struct v4l2_file_operations v4l2_subdev_fops; /** From f797e9665d9280af24f82b2480a38c9c003c07a6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 25 Oct 2023 12:22:56 +0200 Subject: [PATCH 019/159] media: v4l: subdev: Return NULL from pad access functions on error Return NULL from sub-device pad state access functions (v4l2_subdev_state_get_{format,crop,compose}) for non-existent pads. While this behaviour differs from older set of pad state information access functions, we've had a WARN_ON() there for a long time and callers also do validate the pad index nowadays. Therefore problems are not expected. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 3591c53ae9eccafbeffa9ae2d1899873a5076a87) --- drivers/media/v4l2-core/v4l2-subdev.c | 36 +++++---------------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 262cf88dee33c4..7dfde2acf601a3 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1511,16 +1511,8 @@ __v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, if (stream) return NULL; - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; + if (pad >= state->sd->entity.num_pads) + return NULL; return &state->pads[pad].format; } @@ -1553,16 +1545,8 @@ __v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, if (stream) return NULL; - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; + if (pad >= state->sd->entity.num_pads) + return NULL; return &state->pads[pad].crop; } @@ -1595,16 +1579,8 @@ __v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, if (stream) return NULL; - /* - * Set the pad to 0 on error as this is aligned with the - * behaviour of the pad state information access functions. The - * purpose of setting pad to 0 here is to avoid accessing memory - * outside the pads array, but still issuing warning of the - * invalid access while making the caller's error handling - * easier. - */ - if (WARN_ON_ONCE(pad >= state->sd->entity.num_pads)) - pad = 0; + if (pad >= state->sd->entity.num_pads) + return NULL; return &state->pads[pad].compose; } From b66fbb8398c1add214d0d56bc89fe57172275d56 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Dec 2023 12:00:02 +0200 Subject: [PATCH 020/159] media: v4l: Safely to call v4l2_subdev_cleanup on an uninitialised subdev Graciously handle an uninitialised (but still zeroed) sub-device in v4l2_subdev_cleanup(). The list_empty() check there is unnecessary, too, so replace that by cheking whether the lists's next field is NULL. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit ab8d7194acd1fea1702fff41a1ec93e458b6dcbf) --- drivers/media/v4l2-core/v4l2-subdev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 7dfde2acf601a3..519385a9ea8ce0 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1485,7 +1485,8 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) __v4l2_subdev_state_free(sd->active_state); sd->active_state = NULL; - if (list_empty(&sd->async_subdev_endpoint_list)) + /* Uninitialised sub-device, bail out here. */ + if (!sd->async_subdev_endpoint_list.next) return; list_for_each_entry_safe(ase, ase_tmp, &sd->async_subdev_endpoint_list, From f320860d82599ea7aa69d18f06ca83e0196fce9c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 13 Dec 2023 17:00:03 +0200 Subject: [PATCH 021/159] media: v4l: subdev: Move out subdev state lock macros outside CONFIG_MEDIA_CONTROLLER The subdev state locking macros and macros to get the active state are currently behind CONFIG_MEDIA_CONTROLLER. This makes sense, as there can be no subdev state without MC. However, we have code paths common to MC and non-MC cases which call subdev operations that have subdev state as a parameter. In the non-MC case the state parameter would always be NULL. Thus it makes sense to allow, e.g.: v4l2_subdev_call_state_active(sd, pad, get_fmt, fmt) which for non-MC case would call the subdev passing NULL as the state. This currently fails: https://lore.kernel.org/oe-kbuild-all/202312061101.PLrz5NnJ-lkp@intel.com/ Fix the issue by moving the related macros to be outside CONFIG_MEDIA_CONTROLLER. The v4l2_subdev_lock_state() and v4l2_subdev_unlock_state() macros will crash if given NULL as the state, but the other macros behave correctly even when there's no active state, and they will only call the lock/unlock macros if there is a state. An alternative fix would be to make another version of v4l2_subdev_call_state_try() with ifdefs, which would not use any state macros and would always pass NULL as the state. But having two version of a macro/function is always more confusing than having just one, so I went this way. So, this fixes the v4l2_subdev_call_state_active() macro. But we also have v4l2_subdev_call_state_try(). It would be possible to fix that macro by additionally creating "no-op" variants of the state alloc and free functions. However, v4l2_subdev_call_state_try() is only used by a single driver (stm32-dcmi), which selects MC, and the macro is supposed to be removed as soon as the users have been converted away from the macro. Thus I have not touched the state alloc/free functions, and I think it makes sense to keep alloc/free functions available only if there's actually something that can be allocated or freed. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Reviewed-by: Hans Verkuil Link: https://lore.kernel.org/r/20231208-v4l2-state-mc-fix-v1-1-a0c8162557c6@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 08e5c36410ca5cb14237e47e6dfe3fde02e0f275) --- include/media/v4l2-subdev.h | 166 ++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f82227507c08a6..f42c8c107e36f1 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1388,89 +1388,6 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, */ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); -/** - * v4l2_subdev_lock_state() - Locks the subdev state - * @state: The subdevice state - * - * Locks the given subdev state. - * - * The state must be unlocked with v4l2_subdev_unlock_state() after use. - */ -static inline void v4l2_subdev_lock_state(struct v4l2_subdev_state *state) -{ - mutex_lock(state->lock); -} - -/** - * v4l2_subdev_unlock_state() - Unlocks the subdev state - * @state: The subdevice state - * - * Unlocks the given subdev state. - */ -static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) -{ - mutex_unlock(state->lock); -} - -/** - * v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state - * is unlocked and returns it - * @sd: The subdevice - * - * Returns the active state for the subdevice, or NULL if the subdev does not - * support active state. If the state is not NULL, calls - * lockdep_assert_not_held() to issue a warning if the state is locked. - * - * This function is to be used e.g. when getting the active state for the sole - * purpose of passing it forward, without accessing the state fields. - */ -static inline struct v4l2_subdev_state * -v4l2_subdev_get_unlocked_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - lockdep_assert_not_held(sd->active_state->lock); - return sd->active_state; -} - -/** - * v4l2_subdev_get_locked_active_state() - Checks that the active subdev state - * is locked and returns it - * - * @sd: The subdevice - * - * Returns the active state for the subdevice, or NULL if the subdev does not - * support active state. If the state is not NULL, calls lockdep_assert_held() - * to issue a warning if the state is not locked. - * - * This function is to be used when the caller knows that the active state is - * already locked. - */ -static inline struct v4l2_subdev_state * -v4l2_subdev_get_locked_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - lockdep_assert_held(sd->active_state->lock); - return sd->active_state; -} - -/** - * v4l2_subdev_lock_and_get_active_state() - Locks and returns the active subdev - * state for the subdevice - * @sd: The subdevice - * - * Returns the locked active state for the subdevice, or NULL if the subdev - * does not support active state. - * - * The state must be unlocked with v4l2_subdev_unlock_state() after use. - */ -static inline struct v4l2_subdev_state * -v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - v4l2_subdev_lock_state(sd->active_state); - return sd->active_state; -} - /* * A macro to generate the macro or function name for sub-devices state access * wrapper macros below. @@ -1815,6 +1732,89 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable); #endif /* CONFIG_MEDIA_CONTROLLER */ +/** + * v4l2_subdev_lock_state() - Locks the subdev state + * @state: The subdevice state + * + * Locks the given subdev state. + * + * The state must be unlocked with v4l2_subdev_unlock_state() after use. + */ +static inline void v4l2_subdev_lock_state(struct v4l2_subdev_state *state) +{ + mutex_lock(state->lock); +} + +/** + * v4l2_subdev_unlock_state() - Unlocks the subdev state + * @state: The subdevice state + * + * Unlocks the given subdev state. + */ +static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) +{ + mutex_unlock(state->lock); +} + +/** + * v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state + * is unlocked and returns it + * @sd: The subdevice + * + * Returns the active state for the subdevice, or NULL if the subdev does not + * support active state. If the state is not NULL, calls + * lockdep_assert_not_held() to issue a warning if the state is locked. + * + * This function is to be used e.g. when getting the active state for the sole + * purpose of passing it forward, without accessing the state fields. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_get_unlocked_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + lockdep_assert_not_held(sd->active_state->lock); + return sd->active_state; +} + +/** + * v4l2_subdev_get_locked_active_state() - Checks that the active subdev state + * is locked and returns it + * + * @sd: The subdevice + * + * Returns the active state for the subdevice, or NULL if the subdev does not + * support active state. If the state is not NULL, calls lockdep_assert_held() + * to issue a warning if the state is not locked. + * + * This function is to be used when the caller knows that the active state is + * already locked. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_get_locked_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + lockdep_assert_held(sd->active_state->lock); + return sd->active_state; +} + +/** + * v4l2_subdev_lock_and_get_active_state() - Locks and returns the active subdev + * state for the subdevice + * @sd: The subdevice + * + * Returns the locked active state for the subdevice, or NULL if the subdev + * does not support active state. + * + * The state must be unlocked with v4l2_subdev_unlock_state() after use. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + v4l2_subdev_lock_state(sd->active_state); + return sd->active_state; +} + /** * v4l2_subdev_init - initializes the sub-device struct * From 8aec6db5eb45c7955a2b17dfc20426b0a2d9bfda Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 Aug 2023 15:11:50 +0300 Subject: [PATCH 022/159] media: Documentation: Align numbered list, make it a proper ReST Align lines for numbered list so that Sphinx produces an uniform output for all list entries. Also indent paragraphs of such list entries for consistency. Also use ReST numbered list syntax for the entries. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 580139fa0c3025e39d55f9472bafbd8f0f662d7b) --- .../userspace-api/media/v4l/dev-subdev.rst | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index a4f1df7093e8e7..43988516acddfa 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -579,20 +579,19 @@ is started. There are three steps in configuring the streams: -1) Set up links. Connect the pads between sub-devices using the :ref:`Media -Controller API ` +1. Set up links. Connect the pads between sub-devices using the + :ref:`Media Controller API ` -2) Streams. Streams are declared and their routing is configured by -setting the routing table for the sub-device using -:ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. Note that -setting the routing table will reset formats and selections in the -sub-device to default values. +2. Streams. Streams are declared and their routing is configured by setting the + routing table for the sub-device using :ref:`VIDIOC_SUBDEV_S_ROUTING + ` ioctl. Note that setting the routing table will + reset formats and selections in the sub-device to default values. -3) Configure formats and selections. Formats and selections of each stream -are configured separately as documented for plain sub-devices in -:ref:`format-propagation`. The stream ID is set to the same stream ID -associated with either sink or source pads of routes configured using the -:ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. +3. Configure formats and selections. Formats and selections of each stream are + configured separately as documented for plain sub-devices in + :ref:`format-propagation`. The stream ID is set to the same stream ID + associated with either sink or source pads of routes configured using the + :ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. Multiplexed streams setup example ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -618,11 +617,11 @@ modeled as V4L2 devices, exposed to userspace via /dev/videoX nodes. To configure this pipeline, the userspace must take the following steps: -1) Set up media links between entities: connect the sensors to the bridge, -bridge to the receiver, and the receiver to the DMA engines. This step does -not differ from normal non-multiplexed media controller setup. +1. Set up media links between entities: connect the sensors to the bridge, + bridge to the receiver, and the receiver to the DMA engines. This step does + not differ from normal non-multiplexed media controller setup. -2) Configure routing +2. Configure routing .. flat-table:: Bridge routing table :header-rows: 1 @@ -656,14 +655,14 @@ not differ from normal non-multiplexed media controller setup. - V4L2_SUBDEV_ROUTE_FL_ACTIVE - Pixel data stream from Sensor B -3) Configure formats and selections +3. Configure formats and selections -After configuring routing, the next step is configuring the formats and -selections for the streams. This is similar to performing this step without -streams, with just one exception: the ``stream`` field needs to be assigned -to the value of the stream ID. + After configuring routing, the next step is configuring the formats and + selections for the streams. This is similar to performing this step without + streams, with just one exception: the ``stream`` field needs to be assigned + to the value of the stream ID. -A common way to accomplish this is to start from the sensors and propagate the -configurations along the stream towards the receiver, -using :ref:`VIDIOC_SUBDEV_S_FMT ` ioctls to configure each -stream endpoint in each sub-device. + A common way to accomplish this is to start from the sensors and propagate + the configurations along the stream towards the receiver, using + :ref:`VIDIOC_SUBDEV_S_FMT ` ioctls to configure each + stream endpoint in each sub-device. From 5944c6bcaa0b6883df17dfbe6055f38e99e01583 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 15 Sep 2023 14:23:28 +0300 Subject: [PATCH 023/159] media: v4l: subdev: Clear frame descriptor before get_frame_desc Clear frame descriptor before calling transmitter's get_frame_desc() op. Also remove the corresponding memset() calls from drivers. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen (cherry picked from commit 92b224d588792669348777b304f2c88570b0898d) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ds90ub913.c | 2 -- drivers/media/i2c/ds90ub953.c | 2 -- drivers/media/i2c/ds90ub960.c | 2 -- drivers/media/platform/nxp/imx-mipi-csis.c | 2 -- drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++ 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index ba768858f9cf9b..8bb6be95678027 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -362,8 +362,6 @@ static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (ret) return ret; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; state = v4l2_subdev_lock_and_get_active_state(sd); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index c362d82ce46e70..4eb08e3a31c72b 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -499,8 +499,6 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (ret) return ret; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; state = v4l2_subdev_lock_and_get_active_state(sd); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 37aa2e98c15b6c..1d1476098c92f6 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2785,8 +2785,6 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (!ub960_pad_is_source(priv, pad)) return -EINVAL; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; state = v4l2_subdev_lock_and_get_active_state(&priv->sd); diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 142ac7b73e14a0..b08f6d2e7516d0 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1114,8 +1114,6 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; fd->num_entries = 1; - memset(entry, 0, sizeof(*entry)); - entry->flags = 0; entry->pixelcode = csis_fmt->code; entry->bus.csi2.vc = 0; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 519385a9ea8ce0..6c6a9614f21e99 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -306,6 +306,14 @@ static int call_set_selection(struct v4l2_subdev *sd, sd->ops->pad->set_selection(sd, state, sel); } +static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + memset(fd, 0, sizeof(*fd)); + + return sd->ops->pad->get_frame_desc(sd, pad, fd); +} + static inline int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) { @@ -448,6 +456,7 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .set_edid = call_set_edid, .dv_timings_cap = call_dv_timings_cap, .enum_dv_timings = call_enum_dv_timings, + .get_frame_desc = call_get_frame_desc, .get_mbus_config = call_get_mbus_config, }; From 5d436fe210bc0513a536d3ee2f53d890981bc9f3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 12 Sep 2023 14:55:24 +0300 Subject: [PATCH 024/159] media: v4l: subdev: Print debug information on frame descriptor Print debug level information on returned frame descriptors. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart (cherry picked from commit 703cfceaa5e253ecb5311ed19445753dbfc604bc) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6c6a9614f21e99..7d98d89d37a7a5 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -309,9 +310,37 @@ static int call_set_selection(struct v4l2_subdev *sd, static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { + unsigned int i; + int ret; + memset(fd, 0, sizeof(*fd)); - return sd->ops->pad->get_frame_desc(sd, pad, fd); + ret = sd->ops->pad->get_frame_desc(sd, pad, fd); + if (ret) + return ret; + + dev_dbg(sd->dev, "Frame descriptor on pad %u, type %s\n", pad, + fd->type == V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL ? "parallel" : + fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2 ? "CSI-2" : + "unknown"); + + for (i = 0; i < fd->num_entries; i++) { + struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[i]; + char buf[20] = ""; + + if (fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + WARN_ON(snprintf(buf, sizeof(buf), + ", vc %u, dt 0x%02x", + entry->bus.csi2.vc, + entry->bus.csi2.dt) >= sizeof(buf)); + + dev_dbg(sd->dev, + "\tstream %u, code 0x%04x, length %u, flags 0x%04x%s\n", + entry->stream, entry->pixelcode, entry->length, + entry->flags, buf); + } + + return 0; } static inline int check_edid(struct v4l2_subdev *sd, From 7e6791402c9feb9110773713c497ad82c6a085fd Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 27 Sep 2023 14:22:52 +0300 Subject: [PATCH 025/159] media: Documentation: Split camera sensor documentation Split camera sensor documentation into user and kernel portions. This should make it easier for the user space developers to find the relevant documentation. Also add a list of exemplary drivers and add imx219 driver to it, besides those that were already mentioned. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi (cherry picked from commit 0fa78064f6e3a354616fb24462864900c0db3191) Signed-off-by: Jacopo Mondi --- .../driver-api/media/camera-sensor.rst | 131 +++++------------- .../media/drivers/camera-sensor.rst | 104 ++++++++++++++ .../userspace-api/media/drivers/index.rst | 1 + .../userspace-api/media/v4l/control.rst | 4 + 4 files changed, 147 insertions(+), 93 deletions(-) create mode 100644 Documentation/userspace-api/media/drivers/camera-sensor.rst diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 2acc08142a1a59..6456145f96ed00 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -1,8 +1,14 @@ .. SPDX-License-Identifier: GPL-2.0 +.. _media_writing_camera_sensor_drivers: + Writing camera sensor drivers ============================= +This document covers the in-kernel APIs only. For the best practices on +userspace API implementation in camera sensor drivers, please see +:ref:`media_using_camera_sensor_drivers`. + CSI-2 and parallel (BT.601 and BT.656) busses --------------------------------------------- @@ -34,7 +40,8 @@ Devicetree The preferred way to achieve this is using ``assigned-clocks``, ``assigned-clock-parents`` and ``assigned-clock-rates`` properties. See the -`clock device tree bindings `_ +`clock device tree bindings +`_ for more information. The driver then gets the frequency using ``clk_get_rate()``. @@ -85,9 +92,7 @@ PM instead. If you feel you need to begin calling ``.s_power()`` from an ISP or a bridge driver, instead add runtime PM support to the sensor driver you are using and drop its ``.s_power()`` handler. -See examples of runtime PM handling in e.g. ``drivers/media/i2c/ov8856.c`` and -``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI and DT -based systems. +Please also see :ref:`examples `. Control framework ~~~~~~~~~~~~~~~~~ @@ -104,99 +109,39 @@ The function returns a non-zero value if it succeeded getting the power count or runtime PM was disabled, in either of which cases the driver may proceed to access the device. -Frame size ----------- - -There are two distinct ways to configure the frame size produced by camera -sensors. - -Freely configurable camera sensor drivers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Freely configurable camera sensor drivers expose the device's internal -processing pipeline as one or more sub-devices with different cropping and -scaling configurations. The output size of the device is the result of a series -of cropping and scaling operations from the device's pixel array's size. - -An example of such a driver is the CCS driver (see ``drivers/media/i2c/ccs``). - -Register list based drivers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Register list based drivers generally, instead of able to configure the device -they control based on user requests, are limited to a number of preset -configurations that combine a number of different parameters that on hardware -level are independent. How a driver picks such configuration is based on the -format set on a source pad at the end of the device's internal pipeline. - -Most sensor drivers are implemented this way, see e.g. -``drivers/media/i2c/imx319.c`` for an example. - -Frame interval configuration ----------------------------- - -There are two different methods for obtaining possibilities for different frame -intervals as well as configuring the frame interval. Which one to implement -depends on the type of the device. - -Raw camera sensors -~~~~~~~~~~~~~~~~~~ - -Instead of a high level parameter such as frame interval, the frame interval is -a result of the configuration of a number of camera sensor implementation -specific parameters. Luckily, these parameters tend to be the same for more or -less all modern raw camera sensors. - -The frame interval is calculated using the following equation:: - - frame interval = (analogue crop width + horizontal blanking) * - (analogue crop height + vertical blanking) / pixel rate - -The formula is bus independent and is applicable for raw timing parameters on -large variety of devices beyond camera sensors. Devices that have no analogue -crop, use the full source image size, i.e. pixel array size. - -Horizontal and vertical blanking are specified by ``V4L2_CID_HBLANK`` and -``V4L2_CID_VBLANK``, respectively. The unit of the ``V4L2_CID_HBLANK`` control -is pixels and the unit of the ``V4L2_CID_VBLANK`` is lines. The pixel rate in -the sensor's **pixel array** is specified by ``V4L2_CID_PIXEL_RATE`` in the same -sub-device. The unit of that control is pixels per second. - -Register list based drivers need to implement read-only sub-device nodes for the -purpose. Devices that are not register list based need these to configure the -device's internal processing pipeline. - -The first entity in the linear pipeline is the pixel array. The pixel array may -be followed by other entities that are there to allow configuring binning, -skipping, scaling or digital crop :ref:`v4l2-subdev-selections`. - -USB cameras etc. devices -~~~~~~~~~~~~~~~~~~~~~~~~ - -USB video class hardware, as well as many cameras offering a similar higher -level interface natively, generally use the concept of frame interval (or frame -rate) on device level in firmware or hardware. This means lower level controls -implemented by raw cameras may not be used on uAPI (or even kAPI) to control the -frame interval on these devices. - Rotation, orientation and flipping ---------------------------------- -Some systems have the camera sensor mounted upside down compared to its natural -mounting rotation. In such cases, drivers shall expose the information to -userspace with the :ref:`V4L2_CID_CAMERA_SENSOR_ROTATION -` control. - -Sensor drivers shall also report the sensor's mounting orientation with the -:ref:`V4L2_CID_CAMERA_SENSOR_ORIENTATION `. - Use ``v4l2_fwnode_device_parse()`` to obtain rotation and orientation information from system firmware and ``v4l2_ctrl_new_fwnode_properties()`` to register the appropriate controls. -Sensor drivers that have any vertical or horizontal flips embedded in the -register programming sequences shall initialize the V4L2_CID_HFLIP and -V4L2_CID_VFLIP controls with the values programmed by the register sequences. -The default values of these controls shall be 0 (disabled). Especially these -controls shall not be inverted, independently of the sensor's mounting -rotation. +.. _media-camera-sensor-examples: + +Example drivers +--------------- + +Features implemented by sensor drivers vary, and depending on the set of +supported features and other qualities, particular sensor drivers better serve +the purpose of an example. The following drivers are known to be good examples: + +.. flat-table:: Example sensor drivers + :header-rows: 0 + :widths: 1 1 1 2 + + * - Driver name + - File(s) + - Driver type + - Example topic + * - CCS + - ``drivers/media/i2c/ccs/`` + - Freely configurable + - Power management (ACPI and DT), UAPI + * - imx219 + - ``drivers/media/i2c/imx219.c`` + - Register list based + - Power management (DT), UAPI, mode selection + * - imx319 + - ``drivers/media/i2c/imx319.c`` + - Register list based + - Power management (ACPI and DT) diff --git a/Documentation/userspace-api/media/drivers/camera-sensor.rst b/Documentation/userspace-api/media/drivers/camera-sensor.rst new file mode 100644 index 00000000000000..919a50e8b9d9c5 --- /dev/null +++ b/Documentation/userspace-api/media/drivers/camera-sensor.rst @@ -0,0 +1,104 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _media_using_camera_sensor_drivers: + +Using camera sensor drivers +=========================== + +This section describes common practices for how the V4L2 sub-device interface is +used to control the camera sensor drivers. + +You may also find :ref:`media_writing_camera_sensor_drivers` useful. + +Frame size +---------- + +There are two distinct ways to configure the frame size produced by camera +sensors. + +Freely configurable camera sensor drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Freely configurable camera sensor drivers expose the device's internal +processing pipeline as one or more sub-devices with different cropping and +scaling configurations. The output size of the device is the result of a series +of cropping and scaling operations from the device's pixel array's size. + +An example of such a driver is the CCS driver. + +Register list based drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Register list based drivers generally, instead of able to configure the device +they control based on user requests, are limited to a number of preset +configurations that combine a number of different parameters that on hardware +level are independent. How a driver picks such configuration is based on the +format set on a source pad at the end of the device's internal pipeline. + +Most sensor drivers are implemented this way. + +Frame interval configuration +---------------------------- + +There are two different methods for obtaining possibilities for different frame +intervals as well as configuring the frame interval. Which one to implement +depends on the type of the device. + +Raw camera sensors +~~~~~~~~~~~~~~~~~~ + +Instead of a high level parameter such as frame interval, the frame interval is +a result of the configuration of a number of camera sensor implementation +specific parameters. Luckily, these parameters tend to be the same for more or +less all modern raw camera sensors. + +The frame interval is calculated using the following equation:: + + frame interval = (analogue crop width + horizontal blanking) * + (analogue crop height + vertical blanking) / pixel rate + +The formula is bus independent and is applicable for raw timing parameters on +large variety of devices beyond camera sensors. Devices that have no analogue +crop, use the full source image size, i.e. pixel array size. + +Horizontal and vertical blanking are specified by ``V4L2_CID_HBLANK`` and +``V4L2_CID_VBLANK``, respectively. The unit of the ``V4L2_CID_HBLANK`` control +is pixels and the unit of the ``V4L2_CID_VBLANK`` is lines. The pixel rate in +the sensor's **pixel array** is specified by ``V4L2_CID_PIXEL_RATE`` in the same +sub-device. The unit of that control is pixels per second. + +Register list based drivers need to implement read-only sub-device nodes for the +purpose. Devices that are not register list based need these to configure the +device's internal processing pipeline. + +The first entity in the linear pipeline is the pixel array. The pixel array may +be followed by other entities that are there to allow configuring binning, +skipping, scaling or digital crop, see :ref:`VIDIOC_SUBDEV_G_SELECTION +`. + +USB cameras etc. devices +~~~~~~~~~~~~~~~~~~~~~~~~ + +USB video class hardware, as well as many cameras offering a similar higher +level interface natively, generally use the concept of frame interval (or frame +rate) on device level in firmware or hardware. This means lower level controls +implemented by raw cameras may not be used on uAPI (or even kAPI) to control the +frame interval on these devices. + +Rotation, orientation and flipping +---------------------------------- + +Some systems have the camera sensor mounted upside down compared to its natural +mounting rotation. In such cases, drivers shall expose the information to +userspace with the :ref:`V4L2_CID_CAMERA_SENSOR_ROTATION +` control. + +Sensor drivers shall also report the sensor's mounting orientation with the +:ref:`V4L2_CID_CAMERA_SENSOR_ORIENTATION `. + +Sensor drivers that have any vertical or horizontal flips embedded in the +register programming sequences shall initialize the :ref:`V4L2_CID_HFLIP +` and :ref:`V4L2_CID_VFLIP ` controls with the +values programmed by the register sequences. The default values of these +controls shall be 0 (disabled). Especially these controls shall not be inverted, +independently of the sensor's mounting rotation. diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst index 65de8ab99c58cb..e911740daec8cf 100644 --- a/Documentation/userspace-api/media/drivers/index.rst +++ b/Documentation/userspace-api/media/drivers/index.rst @@ -32,6 +32,7 @@ For more details see the file COPYING in the source distribution of Linux. :numbered: aspeed-video + camera-sensor ccs cx2341x-uapi dw100 diff --git a/Documentation/userspace-api/media/v4l/control.rst b/Documentation/userspace-api/media/v4l/control.rst index 4463fce694b08d..57893814a1e5d9 100644 --- a/Documentation/userspace-api/media/v4l/control.rst +++ b/Documentation/userspace-api/media/v4l/control.rst @@ -143,9 +143,13 @@ Control IDs recognise the difference between digital and analogue gain use controls ``V4L2_CID_DIGITAL_GAIN`` and ``V4L2_CID_ANALOGUE_GAIN``. +.. _v4l2-cid-hflip: + ``V4L2_CID_HFLIP`` ``(boolean)`` Mirror the picture horizontally. +.. _v4l2-cid-vflip: + ``V4L2_CID_VFLIP`` ``(boolean)`` Mirror the picture vertically. From 1f604ee364278cde72c31c0831a40ff5956ac0e9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 25 Apr 2023 12:30:45 +0300 Subject: [PATCH 026/159] media: mc: Check pad flag validity Check the validity of pad flags on entity init. Exactly one of the flags must be set. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit deb866f9e3a45ae058b21765feeffae6aea6a193) --- drivers/media/mc/mc-entity.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 951b79ca125cdd..96dd0f6ccd0d0a 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -197,6 +197,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, struct media_device *mdev = entity->graph_obj.mdev; struct media_pad *iter; unsigned int i = 0; + int ret = 0; if (num_pads >= MEDIA_ENTITY_MAX_PADS) return -E2BIG; @@ -210,15 +211,27 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, media_entity_for_each_pad(entity, iter) { iter->entity = entity; iter->index = i++; + + if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_SOURCE)) != 1) { + ret = -EINVAL; + break; + } + if (mdev) media_gobj_create(mdev, MEDIA_GRAPH_PAD, &iter->graph_obj); } + if (ret && mdev) { + media_entity_for_each_pad(entity, iter) + media_gobj_destroy(&iter->graph_obj); + } + if (mdev) mutex_unlock(&mdev->graph_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(media_entity_pads_init); From 27a2ba818edb2298a12bd26887f85d004d30a09c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 25 Apr 2023 16:50:25 +0300 Subject: [PATCH 027/159] media: uapi: Add generic serial metadata mbus formats Add generic serial metadata mbus formats. These formats describe data width and packing but not the content itself. The reason for specifying such formats is that the formats as such are fairly device specific but they are still handled by CSI-2 receiver drivers that should not be aware of device specific formats. What makes generic metadata formats possible is that these formats are parsed by software only, after capturing the data to system memory. Also add a definition for "Data Unit" to cover what is essentially a pixel but is not image data. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit f28bdda2f87aa0c299ad3ddef3e1fa5134ae8e47) Signed-off-by: Jacopo Mondi --- .../userspace-api/media/glossary.rst | 7 + .../media/v4l/subdev-formats.rst | 253 ++++++++++++++++++ include/uapi/linux/media-bus-format.h | 9 + 3 files changed, 269 insertions(+) diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst index 96a360edbf3b1f..f4e95246fa127e 100644 --- a/Documentation/userspace-api/media/glossary.rst +++ b/Documentation/userspace-api/media/glossary.rst @@ -25,6 +25,13 @@ Glossary See :ref:`cec`. + Data Unit + + Unit of data transported by a bus. On parallel buses, the data unit + consists of one or more related samples while on serial buses the data + unit is logical. If the data unit is image data, it may also be called a + pixel. + Device Driver Part of the Linux Kernel that implements support for a hardware component. diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index d7250b6a169548..8750cd7d90b424 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -8377,3 +8377,256 @@ The following table lists the existing sensor ancillary metadata formats: CSI-2/SMIA embedded data format as described in the `CSI-2 specification. `_ +Generic Serial Metadata Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generic serial metadata formats are used on serial buses where the actual data +content is more or less device specific but the data is transmitted and received +by multiple devices that do not process the data in any way, simply writing +it to system memory for processing in software at the end of the pipeline. + +"b" in an array cell signifies a byte of data, followed by the number of the bit +and finally the bit number in subscript. "x" indicates a padding bit. + +.. _media-bus-format-generic-meta: + +.. cssclass: longtable + +.. flat-table:: Generic Serial Metadata Formats + :header-rows: 2 + :stub-columns: 0 + + * - Identifier + - Code + - + - :cspan:`23` Data organization within bus :term:`Data Unit` + * - + - + - Bit + - 23 + - 22 + - 21 + - 20 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-META-8: + + - MEDIA_BUS_FMT_META_8 + - 0x8001 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + * .. _MEDIA-BUS-FMT-META-10: + + - MEDIA_BUS_FMT_META_10 + - 0x8002 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + * .. _MEDIA-BUS-FMT-META-12: + + - MEDIA_BUS_FMT_META_12 + - 0x8003 + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-14: + + - MEDIA_BUS_FMT_META_14 + - 0x8004 + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-16: + + - MEDIA_BUS_FMT_META_16 + - 0x8005 + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-20: + + - MEDIA_BUS_FMT_META_20 + - 0x8006 + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-24: + + - MEDIA_BUS_FMT_META_24 + - 0x8007 + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 7fbeb5a95f4518..950d35865de5b3 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -176,4 +176,13 @@ /* Sensor ancillary metadata formats - next is 0x7002 */ #define MEDIA_BUS_FMT_SENSOR_DATA 0x7002 +/* Generic line based metadata formats for serial buses. Next is 0x8008. */ +#define MEDIA_BUS_FMT_META_8 0x8001 +#define MEDIA_BUS_FMT_META_10 0x8002 +#define MEDIA_BUS_FMT_META_12 0x8003 +#define MEDIA_BUS_FMT_META_14 0x8004 +#define MEDIA_BUS_FMT_META_16 0x8005 +#define MEDIA_BUS_FMT_META_20 0x8006 +#define MEDIA_BUS_FMT_META_24 0x8007 + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ From b3d32df5a0299d3d4e0beba7574937265af6771f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 1 Aug 2023 16:15:36 +0300 Subject: [PATCH 028/159] media: uapi: Document which mbus format fields are valid for metadata Now that metadata mbus formats have been added, it is necessary to define which fields in struct v4l2_mbus_format are applicable to them (not many). Signed-off-by: Sakari Ailus Reviewed-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit ac5214a47336b8a8f7e54f406ee4f7eed9223599) Signed-off-by: Jacopo Mondi --- .../userspace-api/media/v4l/subdev-formats.rst | 15 ++++++++------- include/uapi/linux/v4l2-mediabus.h | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index 8750cd7d90b424..80355baa4d8859 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -33,7 +33,7 @@ Media Bus Formats * - __u32 - ``field`` - Field order, from enum :c:type:`v4l2_field`. See - :ref:`field-order` for details. + :ref:`field-order` for details. Zero for metadata mbus codes. * - __u32 - ``colorspace`` - Image colorspace, from enum :c:type:`v4l2_colorspace`. @@ -45,7 +45,7 @@ Media Bus Formats conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags`. + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - union { - (anonymous) * - __u16 @@ -61,7 +61,7 @@ Media Bus Formats that ycbcr_enc conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags`. + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - __u16 - ``hsv_enc`` - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`. @@ -75,7 +75,7 @@ Media Bus Formats that hsv_enc conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags` + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - } - * - __u16 @@ -90,8 +90,8 @@ Media Bus Formats The driver indicates that quantization conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` - during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. - + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for + metadata mbus codes. * - __u16 - ``xfer_func`` - Transfer function, from enum :c:type:`v4l2_xfer_func`. @@ -104,7 +104,8 @@ Media Bus Formats The driver indicates that the transfer function conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` - during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for + metadata mbus codes. * - __u16 - ``flags`` - flags See: :ref:v4l2-mbus-framefmt-flags diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 6b07b73473b5ff..946520bc49f10c 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -19,12 +19,18 @@ * @width: image width * @height: image height * @code: data format code (from enum v4l2_mbus_pixelcode) - * @field: used interlacing type (from enum v4l2_field) - * @colorspace: colorspace of the data (from enum v4l2_colorspace) - * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding) - * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding) - * @quantization: quantization of the data (from enum v4l2_quantization) - * @xfer_func: transfer function of the data (from enum v4l2_xfer_func) + * @field: used interlacing type (from enum v4l2_field), zero for metadata + * mbus codes + * @colorspace: colorspace of the data (from enum v4l2_colorspace), zero on + * metadata mbus codes + * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero + * for metadata mbus codes + * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding), zero for + * metadata mbus codes + * @quantization: quantization of the data (from enum v4l2_quantization), zero + * for metadata mbus codes + * @xfer_func: transfer function of the data (from enum v4l2_xfer_func), zero + * for metadata mbus codes * @flags: flags (V4L2_MBUS_FRAMEFMT_*) * @reserved: reserved bytes that can be later used */ From b96cbe13e2fbf0ccfc2f11064032f4fd5f4929ef Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 26 Apr 2023 13:28:48 +0300 Subject: [PATCH 029/159] media: uapi: v4l: Add generic 8-bit metadata format definitions Generic 8-bit metadata formats define the in-memory data layout but not the format of the data itself. The reasoning for having such formats is to allow CSI-2 receiver drivers to receive and DMA drivers to write the data to memory without knowing a large number of device-specific formats. These formats may be used only in conjunction with a Media Controller pipeline where the internal pad of the source sub-device defines the specific format of the data (using an mbus code). Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 1d92152339587174363c3c9f7561de95646d5625) Signed-off-by: Jacopo Mondi --- .../userspace-api/media/v4l/dev-subdev.rst | 2 + .../userspace-api/media/v4l/meta-formats.rst | 3 +- .../media/v4l/metafmt-generic.rst | 340 ++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 9 +- include/uapi/linux/videodev2.h | 7 + 5 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 Documentation/userspace-api/media/v4l/metafmt-generic.rst diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index 43988516acddfa..f375b820ab68e7 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -506,6 +506,8 @@ source pads. subdev-formats +.. _subdev-routing: + Streams, multiplexed media pads and internal routing ---------------------------------------------------- diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 178653874e44b3..69e09857d55ea9 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -14,11 +14,12 @@ These formats are used for the :ref:`metadata` interface only. metafmt-bcm2835-isp-stats metafmt-d4xx + metafmt-generic metafmt-intel-ipu3 metafmt-pisp-be metafmt-rkisp1 metafmt-sensor-data metafmt-uvc + metafmt-vivid metafmt-vsp1-hgo metafmt-vsp1-hgt - metafmt-vivid diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst new file mode 100644 index 00000000000000..78ab56b21682d2 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst @@ -0,0 +1,340 @@ +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later + +******************************************************************************************************************************************************************************************************************************************************************************** +V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O') +******************************************************************************************************************************************************************************************************************************************************************************** + + +Generic line-based metadata formats + + +Description +=========== + +These generic line-based metadata formats define the memory layout of the data +without defining the format or meaning of the metadata itself. + +.. _v4l2-meta-fmt-generic-8: + +V4L2_META_FMT_GENERIC_8 +----------------------- + +The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format +is used on CSI-2 for 8 bits per :term:`Data Unit`. + +Additionally it is used for 16 bits per Data Unit when two bytes of metadata are +packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_16 `. + +**Byte Order Of V4L2_META_FMT_GENERIC_8.** +Each cell is one byte. "M" denotes a byte of metadata. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + * - start + 4: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + +.. _v4l2-meta-fmt-generic-csi2-10: + +V4L2_META_FMT_GENERIC_CSI2_10 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit +Data Units, with one padding byte after every four bytes of metadata. This +format is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is also used in conjunction with 20 bits per :term:`Data Unit` +formats that pack two bytes of metadata into one Data Unit. Otherwise the +20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20 +`. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + - x + * - start + 5: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-12: + +V4L2_META_FMT_GENERIC_CSI2_12 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit +Data Units, with one padding byte after every two bytes of metadata. This format +is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is also used in conjunction with 24 bits per :term:`Data Unit` +formats that pack two bytes of metadata into one Data Unit. Otherwise the +24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24 +`. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - x + - M\ :sub:`20` + - M\ :sub:`30` + - x + * - start + 6: + - M\ :sub:`01` + - M\ :sub:`11` + - x + - M\ :sub:`21` + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-14: + +V4L2_META_FMT_GENERIC_CSI2_14 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit +Data Units, with three padding bytes after every four bytes of metadata. This +format is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + - x + - x + - x + * - start + 7: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + - x + - x + - x + +.. _v4l2-meta-fmt-generic-csi2-16: + +V4L2_META_FMT_GENERIC_CSI2_16 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit +Data Units, with one padding byte after every byte of metadata. This format is +typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_8 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - M\ :sub:`10` + - x + - M\ :sub:`20` + - x + - M\ :sub:`30` + - x + * - start + 8: + - M\ :sub:`01` + - x + - M\ :sub:`11` + - x + - M\ :sub:`21` + - x + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-20: + +V4L2_META_FMT_GENERIC_CSI2_20 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit +Data Units, with alternating one or two padding bytes after every byte of +metadata. This format is typically used by CSI-2 receivers with a source that +transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data +to memory as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_10 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - M\ :sub:`10` + - x + - x + - M\ :sub:`20` + - x + - M\ :sub:`30` + - x + - x + * - start + 10: + - M\ :sub:`01` + - x + - M\ :sub:`11` + - x + - x + - M\ :sub:`21` + - x + - M\ :sub:`31` + - x + - x + +.. _v4l2-meta-fmt-generic-csi2-24: + +V4L2_META_FMT_GENERIC_CSI2_24 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit +Data Units, with two padding bytes after every byte of metadata. This format is +typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_12 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - x + - M\ :sub:`10` + - x + - x + - M\ :sub:`20` + - x + - x + - M\ :sub:`30` + - x + - x + * - start + 12: + - M\ :sub:`01` + - x + - x + - M\ :sub:`11` + - x + - x + - M\ :sub:`21` + - x + - x + - M\ :sub:`31` + - x + - x diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 0ba33c03bb21dd..2b1ed42305a2bc 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1463,6 +1463,13 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP BE Config format"; break; case V4L2_META_FMT_RPI_FE_CFG: descr = "PiSP FE Config format"; break; case V4L2_META_FMT_RPI_FE_STATS: descr = "PiSP FE Statistics format"; break; + case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; + case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_14: descr = "8-bit Generic Meta, 14b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_16: descr = "8-bit Generic Meta, 16b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_20: descr = "8-bit Generic Meta, 20b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_24: descr = "8-bit Generic Meta, 24b CSI-2"; break; default: /* Compressed formats */ @@ -1525,7 +1532,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_PISP_COMP1_RGGB: case V4L2_PIX_FMT_PISP_COMP1_GRBG: case V4L2_PIX_FMT_PISP_COMP1_GBRG: - case V4L2_PIX_FMT_PISP_COMP1_BGGR: + case V4L2_PIX_FMT_PISP_COMP1_BGGR: case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP Bayer Compressed Format"; break; case V4L2_PIX_FMT_PISP_COMP2_RGGB: case V4L2_PIX_FMT_PISP_COMP2_GRBG: diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index f8157c9c9da689..4728aae117cbd0 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -879,6 +879,13 @@ struct v4l2_pix_format { /* The metadata format identifier for FE configuration buffers. */ #define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') +#define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_14 v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */ /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe From 07a4959e30c4aaeb7c41df9919f028f75a73f066 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 21 Feb 2023 19:37:29 +0200 Subject: [PATCH 030/159] media: v4l: Support line-based metadata capture Many camera sensors, among other devices, transmit embedded data and image data for each CSI-2 frame. This embedded data typically contains register configuration of the sensor that has been used to capture the image data of the same frame. The embedded data is received by the CSI-2 receiver and has the same properties as the image data, including that it is line based: it has width, height and bytesperline (stride). Add these fields to struct v4l2_meta_format and document them. Also add V4L2_FMT_FLAG_META_LINE_BASED to tell a given format is line-based i.e. these fields of struct v4l2_meta_format are valid for it. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 89345c2a6ff9c48c5f1ea336e66e46dfc38a467a) Signed-off-by: Jacopo Mondi --- .../userspace-api/media/v4l/dev-meta.rst | 21 +++++++++++++++++++ .../media/v4l/vidioc-enum-fmt.rst | 7 +++++++ .../media/videodev2.h.rst.exceptions | 1 + drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- include/uapi/linux/videodev2.h | 10 +++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst index 0e7e1ee1471a44..5eee9ab60395e8 100644 --- a/Documentation/userspace-api/media/v4l/dev-meta.rst +++ b/Documentation/userspace-api/media/v4l/dev-meta.rst @@ -47,6 +47,12 @@ member of the ``fmt`` union as needed per the desired operation. Both drivers and applications must set the remainder of the :c:type:`v4l2_format` structure to 0. +Devices that capture metadata by line have the struct v4l2_fmtdesc +``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such +devices can typically also :ref:`capture image data `. This primarily +involves devices that receive the data from a different devices such as a camera +sensor. + .. c:type:: v4l2_meta_format .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}| @@ -65,3 +71,18 @@ to 0. - ``buffersize`` - Maximum buffer size in bytes required for data. The value is set by the driver. + * - __u32 + - ``width`` + - Width of a line of metadata in Data Units. Valid when + :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, + otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. + * - __u32 + - ``height`` + - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag + ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See + :c:func:`VIDIOC_ENUM_FMT`. + * - __u32 + - ``bytesperline`` + - Offset in bytes between the beginning of two consecutive lines. Valid + when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is + set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst index 000c154b0f989d..3adb3d205531b4 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst @@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: The application can ask to configure the quantization of the capture device when calling the :ref:`VIDIOC_S_FMT ` ioctl with :ref:`V4L2_PIX_FMT_FLAG_SET_CSC ` set. + * - ``V4L2_FMT_FLAG_META_LINE_BASED`` + - 0x0200 + - The metadata format is line-based. In this case the ``width``, + ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are + valid. The buffer consists of ``height`` lines, each having ``width`` + Data Units of data and the offset (in bytes) between the beginning of + each two consecutive lines is ``bytesperline``. Return Value ============ diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index 3e58aac4ef0b6e..bdc628e8c1d694 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags +replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags # V4L2 timecode types replace define V4L2_TC_TYPE_24FPS timecode-type diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2b1ed42305a2bc..841718e9ea6a5a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only) case V4L2_BUF_TYPE_META_OUTPUT: meta = &p->fmt.meta; pixelformat = meta->dataformat; - pr_cont(", dataformat=%p4cc, buffersize=%u\n", - &pixelformat, meta->buffersize); + pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n", + &pixelformat, meta->buffersize, meta->width, + meta->height, meta->bytesperline); break; } } diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 4728aae117cbd0..b32b4f5096368c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -917,6 +917,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_CSC_YCBCR_ENC 0x0080 #define V4L2_FMT_FLAG_CSC_HSV_ENC V4L2_FMT_FLAG_CSC_YCBCR_ENC #define V4L2_FMT_FLAG_CSC_QUANTIZATION 0x0100 +#define V4L2_FMT_FLAG_META_LINE_BASED 0x0200 /* Frame Size and frame rate enumeration */ /* @@ -2460,10 +2461,19 @@ struct v4l2_sdr_format { * struct v4l2_meta_format - metadata format definition * @dataformat: little endian four character code (fourcc) * @buffersize: maximum size in bytes required for data + * @width: number of data units of data per line (valid for line + * based formats only, see format documentation) + * @height: number of lines of data per buffer (valid for line based + * formats only) + * @bytesperline: offset between the beginnings of two adjacent lines in + * bytes (valid for line based formats only) */ struct v4l2_meta_format { __u32 dataformat; __u32 buffersize; + __u32 width; + __u32 height; + __u32 bytesperline; } __attribute__ ((packed)); /** From b9a61053ea2da94651ed66f17997fc99d295166f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 23 Apr 2024 10:39:47 +0300 Subject: [PATCH 031/159] media: v4l: Set line based metadata flag in V4L2 core Set (and unset) the V4L2_FMT_FLAG_META_LINE_BASED flag in struct v4l2_fmtdesc based on the format after returning the driver callback for enumerating formats. This way the drivers don't need to care about the flag. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 21828609f0a652d825f73dca621f783a2f225762) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-ioctl.c | 16 ++++++++++++++++ include/uapi/linux/videodev2.h | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 841718e9ea6a5a..cbb57d37e642e1 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1551,6 +1551,22 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) } } + if (fmt->type == V4L2_BUF_TYPE_META_CAPTURE) { + switch (fmt->pixelformat) { + case V4L2_META_FMT_GENERIC_8: + case V4L2_META_FMT_GENERIC_CSI2_10: + case V4L2_META_FMT_GENERIC_CSI2_12: + case V4L2_META_FMT_GENERIC_CSI2_14: + case V4L2_META_FMT_GENERIC_CSI2_16: + case V4L2_META_FMT_GENERIC_CSI2_20: + case V4L2_META_FMT_GENERIC_CSI2_24: + fmt->flags |= V4L2_FMT_FLAG_META_LINE_BASED; + break; + default: + fmt->flags &= ~V4L2_FMT_FLAG_META_LINE_BASED; + } + } + if (descr) WARN_ON(strscpy(fmt->description, descr, sz) < 0); fmt->flags |= flags; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b32b4f5096368c..af0231fbd4e8c6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -879,6 +879,11 @@ struct v4l2_pix_format { /* The metadata format identifier for FE configuration buffers. */ #define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') + +/* + * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when + * adding new ones! + */ #define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */ #define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */ #define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */ From f62e3a19c786b01056e7552c8570ac90303b7cbc Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Sep 2023 10:56:14 +0300 Subject: [PATCH 032/159] media: v4l: subdev: Add a function to lock two sub-device states, use it Add two new functions, v4l2_subdev_lock_states() and v4l2_subdev_unclock_states(), to acquire and release the state of two sub-devices. They differ from calling v4l2_subdev_{un,}lock_state() so that if the two states share the same lock, the lock is acquired only once. Also use the new functions in v4l2_subdev_link_validate(). Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 72364b91ce022ded4aa541c62f51c10a65fb3498) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 12 +++----- include/media/v4l2-subdev.h | 40 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 7d98d89d37a7a5..dd36947a6a2084 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1388,17 +1388,13 @@ int v4l2_subdev_link_validate(struct media_link *link) states_locked = sink_state && source_state; - if (states_locked) { - v4l2_subdev_lock_state(sink_state); - v4l2_subdev_lock_state(source_state); - } + if (states_locked) + v4l2_subdev_lock_states(sink_state, source_state); ret = v4l2_subdev_link_validate_locked(link, states_locked); - if (states_locked) { - v4l2_subdev_unlock_state(sink_state); - v4l2_subdev_unlock_state(source_state); - } + if (states_locked) + v4l2_subdev_unlock_states(sink_state, source_state); return ret; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f42c8c107e36f1..2e216505261ba5 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1756,6 +1756,46 @@ static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) mutex_unlock(state->lock); } +/** + * v4l2_subdev_lock_states - Lock two sub-device states + * @state1: One subdevice state + * @state2: The other subdevice state + * + * Locks the state of two sub-devices. + * + * The states must be unlocked with v4l2_subdev_unlock_states() after use. + * + * This differs from calling v4l2_subdev_lock_state() on both states so that if + * the states share the same lock, the lock is acquired only once (so no + * deadlock occurs). The caller is responsible for ensuring the locks will + * always be acquired in the same order. + */ +static inline void v4l2_subdev_lock_states(struct v4l2_subdev_state *state1, + struct v4l2_subdev_state *state2) +{ + mutex_lock(state1->lock); + if (state1->lock != state2->lock) + mutex_lock(state2->lock); +} + +/** + * v4l2_subdev_unlock_states() - Unlock two sub-device states + * @state1: One subdevice state + * @state2: The other subdevice state + * + * Unlocks the state of two sub-devices. + * + * This differs from calling v4l2_subdev_unlock_state() on both states so that + * if the states share the same lock, the lock is released only once. + */ +static inline void v4l2_subdev_unlock_states(struct v4l2_subdev_state *state1, + struct v4l2_subdev_state *state2) +{ + mutex_unlock(state1->lock); + if (state1->lock != state2->lock) + mutex_unlock(state2->lock); +} + /** * v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state * is unlocked and returns it From ffff6fefd0b6b6ef76d66050a5cf6e5fdbc4e704 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 31 Aug 2023 14:40:29 +0300 Subject: [PATCH 033/159] media: v4l: subdev: Copy argument back to user also for S_ROUTING As the user needs to know what went wrong for S_ROUTING, copy array arguments back to the user. Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 38c84932de9cdef7472ff6ecf3214533ba517176) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-ioctl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index cbb57d37e642e1..27f26cab224275 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -3452,11 +3452,14 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, * FIXME: subdev IOCTLS are partially handled here and partially in * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS * defined here as part of the 'v4l2_ioctls' array. As - * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even - * in case of failure, but it is not defined here as part of the + * VIDIOC_SUBDEV_[GS]_ROUTING needs to return results to applications + * even in case of failure, but it is not defined here as part of the * 'v4l2_ioctls' array, insert an ad-hoc check to address that. */ - if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) + if (cmd == VIDIOC_SUBDEV_G_ROUTING || cmd == VIDIOC_SUBDEV_S_ROUTING) + always_copy = true; + + if (err < 0 && !always_copy) goto out; if (has_array_args) { From 119fb31cdefb236e1aeb9e6f665d4b1461f31d82 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 31 Aug 2023 14:56:28 +0300 Subject: [PATCH 034/159] media: v4l: subdev: Add len_routes field to struct v4l2_subdev_routing The len_routes field is used to tell the size of the routes array in struct v4l2_subdev_routing. This way the number of routes returned from S_ROUTING IOCTL may be larger than the number of routes provided, in case there are more routes returned by the driver. Note that this uAPI is still disabled in the code, so this change can safely be done. Anyone who manually patched the code to enable this uAPI must update their code. The patch also increases the number of reserved fields in struct v4l2_subdev_routing. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 83a22a07cd9d51b7ffd18a8904e1857061eda28f) Signed-off-by: Jacopo Mondi --- .../media/v4l/vidioc-subdev-g-routing.rst | 46 +++++++++++++------ drivers/media/v4l2-core/v4l2-ioctl.c | 4 +- drivers/media/v4l2-core/v4l2-subdev.c | 12 ++--- include/media/v4l2-subdev.h | 2 + include/uapi/linux/v4l2-subdev.h | 10 ++-- 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 72677a280cd647..2a7f95382808f3 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -43,23 +43,38 @@ The routing configuration determines the flows of data inside an entity. Drivers report their current routing tables using the ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and -setting or clearing flags of the ``flags`` field of a -struct :c:type:`v4l2_subdev_route`. +setting or clearing flags of the ``flags`` field of a struct +:c:type:`v4l2_subdev_route`. -All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This -means that the userspace must reconfigure all streams after calling the ioctl -with e.g. ``VIDIOC_SUBDEV_S_FMT``. +All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. +This means that the userspace must reconfigure all stream formats and selections +after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``. Only subdevices which have both sink and source pads can support routing. -When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application -provided ``num_routes`` is not big enough to contain all the available routes -the subdevice exposes, drivers return the ENOSPC error code and adjust the -value of the ``num_routes`` field. Application should then reserve enough memory -for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again. - -On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the -``num_routes`` field to reflect the actual number of routes returned. +The ``len_routes`` field indicates the number of routes that can fit in the +``routes`` array allocated by userspace. It is set by applications for both +ioctls to indicate how many routes the kernel can return, and is never modified +by the kernel. + +The ``num_routes`` field indicates the number of routes in the routing +table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of +routes that the application stored in the ``routes`` array. For both ioctls, it +is returned by the kernel and indicates how many routes are stored in the +subdevice routing table. This may be smaller or larger than the value of +``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as +drivers may adjust the requested routing table. + +The kernel can return a ``num_routes`` value larger than ``len_routes`` from +both ioctls. This indicates thare are more routes in the routing table than fits +the ``routes`` array. In this case, the ``routes`` array is filled by the kernel +with the first ``len_routes`` entries of the subdevice routing table. This is +not considered to be an error, and the ioctl call succeeds. If the applications +wants to retrieve the missing routes, it can issue a new +``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array. + +``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in +``num_routes`` field due to e.g. hardware properties. .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| @@ -74,6 +89,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the - ``which`` - Format to modified, from enum :ref:`v4l2_subdev_format_whence `. + * - __u32 + - ``len_routes`` + - The length of the array (as in memory reserved for the array) * - struct :c:type:`v4l2_subdev_route` - ``routes[]`` - Array of struct :c:type:`v4l2_subdev_route` entries @@ -81,7 +99,7 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the - ``num_routes`` - Number of entries of the routes array * - __u32 - - ``reserved``\ [5] + - ``reserved``\ [11] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 27f26cab224275..4be767c129af84 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -3192,13 +3192,13 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, case VIDIOC_SUBDEV_S_ROUTING: { struct v4l2_subdev_routing *routing = parg; - if (routing->num_routes > 256) + if (routing->len_routes > 256) return -E2BIG; *user_ptr = u64_to_user_ptr(routing->routes); *kernel_ptr = (void **)&routing->routes; *array_size = sizeof(struct v4l2_subdev_route) - * routing->num_routes; + * routing->len_routes; ret = 1; break; } diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index dd36947a6a2084..1b62b56b1695dd 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -912,14 +912,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, krouting = &state->routing; - if (routing->num_routes < krouting->num_routes) { - routing->num_routes = krouting->num_routes; - return -ENOSPC; - } - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, krouting->routes, - krouting->num_routes * sizeof(*krouting->routes)); + min(krouting->num_routes, routing->len_routes) * + sizeof(*krouting->routes)); routing->num_routes = krouting->num_routes; return 0; @@ -941,6 +937,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; + if (routing->num_routes > routing->len_routes) + return -EINVAL; + memset(routing->reserved, 0, sizeof(routing->reserved)); for (i = 0; i < routing->num_routes; ++i) { @@ -967,6 +966,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, } krouting.num_routes = routing->num_routes; + krouting.len_routes = routing->len_routes; krouting.routes = routes; return v4l2_subdev_call(sd, pad, set_routing, state, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 2e216505261ba5..f913ea0b188f91 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -734,12 +734,14 @@ struct v4l2_subdev_stream_configs { /** * struct v4l2_subdev_krouting - subdev routing table * + * @len_routes: length of routes array, in routes * @num_routes: number of routes * @routes: &struct v4l2_subdev_route * * This structure contains the routing table for a subdev. */ struct v4l2_subdev_krouting { + unsigned int len_routes; unsigned int num_routes; struct v4l2_subdev_route *routes; }; diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index b383c2fe0cf354..6dd4372dfd3fee 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -222,15 +222,19 @@ struct v4l2_subdev_route { * struct v4l2_subdev_routing - Subdev routing information * * @which: configuration type (from enum v4l2_subdev_format_whence) - * @num_routes: the total number of routes in the routes array + * @len_routes: the length of the routes array, in routes; set by the user, not + * modified by the kernel * @routes: pointer to the routes array + * @num_routes: the total number of routes, possibly more than fits in the + * routes array * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_routing { __u32 which; - __u32 num_routes; + __u32 len_routes; __u64 routes; - __u32 reserved[6]; + __u32 num_routes; + __u32 reserved[11]; }; /* From 0d4b7c4f39c7e3e0b59649df9d03298e77795a37 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Sep 2023 13:51:04 +0300 Subject: [PATCH 035/159] media: v4l: subdev: Return routes set using S_ROUTING Return the routes set using S_ROUTING back to the user. Also reflect this in documentation. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 91e99e5a0bed1084ab5db2a69923b248039408ae) Signed-off-by: Jacopo Mondi --- .../media/v4l/vidioc-subdev-g-routing.rst | 7 ++----- drivers/media/v4l2-core/v4l2-subdev.c | 12 +++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 2a7f95382808f3..d386e3b0172495 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -44,7 +44,8 @@ Drivers report their current routing tables using the ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and setting or clearing flags of the ``flags`` field of a struct -:c:type:`v4l2_subdev_route`. +:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also +``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user. All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This means that the userspace must reconfigure all stream formats and selections @@ -153,10 +154,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set appropriately. The generic error codes are described at the :ref:`Generic Error Codes ` chapter. -ENOSPC - The application provided ``num_routes`` is not big enough to contain - all the available routes the subdevice exposes. - EINVAL The sink or source pad identifiers reference a non-existing pad, or reference pads of different types (ie. the sink_pad identifiers refers to a source pad). diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 1b62b56b1695dd..222a1ac7b44c3b 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -969,8 +969,18 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, krouting.len_routes = routing->len_routes; krouting.routes = routes; - return v4l2_subdev_call(sd, pad, set_routing, state, + rval = v4l2_subdev_call(sd, pad, set_routing, state, routing->which, &krouting); + if (rval < 0) + return rval; + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + state->routing.routes, + min(state->routing.num_routes, routing->len_routes) * + sizeof(*state->routing.routes)); + routing->num_routes = state->routing.num_routes; + + return 0; } case VIDIOC_SUBDEV_G_CLIENT_CAP: { From 0d486c4760e387cde5b4ffacda1e3f2311ec9397 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 31 Aug 2023 15:40:07 +0300 Subject: [PATCH 036/159] media: v4l: subdev: Add trivial set_routing support Add trivial S_ROUTING IOCTL support for drivers where routing is static. Essentially this means returning the same information G_ROUTING call would have done. Signed-off-by: Sakari Ailus Co-developed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Julien Massot Signed-off-by: Hans Verkuil (cherry picked from commit 1bfef49741fde3fa4a4d3276352e25a6a2a86499) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 222a1ac7b44c3b..19c30138329ee4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -965,6 +965,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, return -EINVAL; } + /* + * If the driver doesn't support setting routing, just return + * the routing table. + */ + if (!v4l2_subdev_has_op(sd, pad, set_routing)) { + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + state->routing.routes, + min(state->routing.num_routes, routing->len_routes) * + sizeof(*state->routing.routes)); + routing->num_routes = state->routing.num_routes; + + return 0; + } + krouting.num_routes = routing->num_routes; krouting.len_routes = routing->len_routes; krouting.routes = routes; From 580bc6ebb728cf94edf61e14c34ec649a4047d4f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 26 Apr 2024 18:33:19 +0300 Subject: [PATCH 037/159] media: uapi: v4l: Don't expose generic metadata formats to userspace The generic metadata pixel formats (V4L2_META_FMT_GENERIC_*) are meant to be used in conjunction with device-specific media bus codes. Those codes are work in progress and not available in the upstream kernel yet. To make sure the generic metadata pixel formats won't be used by userspace until we have the full infrastructure in place, keep their definition private to the kernel for now. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit d69c8429ea80af02e89e5b3eecb78e417ad049c8) Signed-off-by: Jacopo Mondi --- include/uapi/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index af0231fbd4e8c6..6e6165ea10efc3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -880,6 +880,7 @@ struct v4l2_pix_format { /* The metadata format identifier for FE configuration buffers. */ #define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') +#ifdef __KERNEL__ /* * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when * adding new ones! @@ -891,6 +892,7 @@ struct v4l2_pix_format { #define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */ #define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */ #define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */ +#endif /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe From ec7e1694a79b628e51708dd6091d7ccae53af9bf Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:04 +0300 Subject: [PATCH 038/159] media: subdev: Add privacy led helpers Add helper functions to enable and disable the privacy led. This moves the #if from the call site to the privacy led functions, and makes adding privacy led support to v4l2_subdev_enable/disable_streams() cleaner. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 4e628f95e743c8fd37b5daef87b05681d724137e) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 19c30138329ee4..12ffb99d8cc17f 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -148,6 +148,23 @@ static int subdev_close(struct file *file) } #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, + sd->privacy_led->max_brightness); +#endif +} + +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, 0); +#endif +} + static inline int check_which(u32 which) { if (which != V4L2_SUBDEV_FORMAT_TRY && @@ -418,15 +435,10 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) if (!ret) { sd->enabled_streams = enable ? BIT(0) : 0; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) - if (!IS_ERR_OR_NULL(sd->privacy_led)) { - if (enable) - led_set_brightness(sd->privacy_led, - sd->privacy_led->max_brightness); - else - led_set_brightness(sd->privacy_led, 0); - } -#endif + if (enable) + v4l2_subdev_enable_privacy_led(sd); + else + v4l2_subdev_disable_privacy_led(sd); } return ret; From 841c6e7cb006697d69cdbf28abdeda99621c01a2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:05 +0300 Subject: [PATCH 039/159] media: subdev: Use v4l2_subdev_has_op() in v4l2_subdev_enable/disable_streams() Use v4l2_subdev_has_op() in v4l2_subdev_enable/disable_streams() instead of open coding the same. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 68e36241b74683e36533b0089966881d0b7d866b) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 12ffb99d8cc17f..83656a6be5a244 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2085,7 +2085,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->enable_streams) + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) return v4l2_subdev_enable_streams_fallback(sd, pad, streams_mask); @@ -2202,7 +2202,7 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->disable_streams) + if (!v4l2_subdev_has_op(sd, pad, disable_streams)) return v4l2_subdev_disable_streams_fallback(sd, pad, streams_mask); From e2f88dffff1a62d0c4bd2654fdb221c991012a9e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:06 +0300 Subject: [PATCH 040/159] media: subdev: Add checks for subdev features Add some checks to verify that the subdev driver implements required features. A subdevice that supports streams (V4L2_SUBDEV_FL_STREAMS) must do one of the following: - Implement neither .enable/disable_streams() nor .s_stream(), if the subdev is part of a video driver that uses an internal method to enable the subdev. - Implement only .enable/disable_streams(), if support for legacy sink-side subdevices is not needed. - Implement .enable/disable_streams() and .s_stream(), if support for legacy sink-side subdevices is needed. At the moment the framework doesn't check this requirement. Add the check. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit e003fd9c1e3883a96451d24174dc5bad2c248341) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 83656a6be5a244..14bacd3998153c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1537,6 +1537,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, struct lock_class_key *key) { struct v4l2_subdev_state *state; + struct device *dev = sd->dev; + bool has_disable_streams; + bool has_enable_streams; + bool has_s_stream; + + /* Check that the subdevice implements the required features */ + + has_s_stream = v4l2_subdev_has_op(sd, video, s_stream); + has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams); + has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams); + + if (has_enable_streams != has_disable_streams) { + dev_err(dev, + "subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n", + sd->name); + return -EINVAL; + } + + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + if (has_s_stream && !has_enable_streams) { + dev_err(dev, + "subdev '%s' must implement .enable/disable_streams()\n", + sd->name); + + return -EINVAL; + } + } state = __v4l2_subdev_state_alloc(sd, name, key); if (IS_ERR(state)) From 63852d507f34c093a7bce05a7c3a781a312100bf Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:07 +0300 Subject: [PATCH 041/159] media: subdev: Fix use of sd->enabled_streams in call_s_stream() call_s_stream() uses sd->enabled_streams to track whether streaming has already been enabled. However, v4l2_subdev_enable/disable_streams_fallback(), which was the original user of this field, already uses it, and v4l2_subdev_enable/disable_streams_fallback() will call call_s_stream(). This leads to a conflict as both functions set the field. Afaics, both functions set the field to the same value, so it won't cause a runtime bug, but it's still wrong and if we, e.g., change how v4l2_subdev_enable/disable_streams_fallback() operates we might easily cause bugs. Fix this by adding a new field, 's_stream_enabled', for call_s_stream(). Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 1d7804281df3f09f0a109d00406e859a00bae7ae) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 8 ++------ include/media/v4l2-subdev.h | 3 +++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 14bacd3998153c..29b84364fb951d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -417,12 +417,8 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) * The .s_stream() operation must never be called to start or stop an * already started or stopped subdev. Catch offenders but don't return * an error yet to avoid regressions. - * - * As .s_stream() is mutually exclusive with the .enable_streams() and - * .disable_streams() operation, we can use the enabled_streams field - * to store the subdev streaming state. */ - if (WARN_ON(!!sd->enabled_streams == !!enable)) + if (WARN_ON(sd->s_stream_enabled == !!enable)) return 0; ret = sd->ops->video->s_stream(sd, enable); @@ -433,7 +429,7 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) } if (!ret) { - sd->enabled_streams = enable ? BIT(0) : 0; + sd->s_stream_enabled = enable; if (enable) v4l2_subdev_enable_privacy_led(sd); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f913ea0b188f91..2f6ffaf46b1df3 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1039,6 +1039,8 @@ struct v4l2_subdev_platform_data { * v4l2_subdev_enable_streams() and * v4l2_subdev_disable_streams() helper functions for fallback * cases. + * @s_stream_enabled: Tracks whether streaming has been enabled with s_stream. + * This is only for call_s_stream() internal use. * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. @@ -1087,6 +1089,7 @@ struct v4l2_subdev { */ struct v4l2_subdev_state *active_state; u64 enabled_streams; + bool s_stream_enabled; }; From a544c5147f110cf3e121a55430c4ac1b297b6349 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:08 +0300 Subject: [PATCH 042/159] media: subdev: Improve v4l2_subdev_enable/disable_streams_fallback v4l2_subdev_enable/disable_streams_fallback() supports falling back to .s_stream() for subdevs with a single source pad. It also tracks the enabled streams for that one pad in the sd->enabled_streams field. Tracking the enabled streams with sd->enabled_streams does not make sense, as with .s_stream() there can only be a single stream per pad. Thus, as the v4l2_subdev_enable/disable_streams_fallback() only supports a single source pad, all we really need is a boolean which tells whether streaming has been enabled on this pad or not. However, as we only need a true/false state for a pad (instead of tracking which streams have been enabled for a pad), we can easily extend the fallback mechanism to support multiple source pads as we only need to keep track of which pads have been enabled. Change the sd->enabled_streams field to sd->enabled_pads, which is a 64-bit bitmask tracking the enabled source pads. With this change we can remove the restriction that v4l2_subdev_enable/disable_streams_fallback() only supports a single source pad. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 61d6c8c896c1ccde350c281817847a32b0c6b83b) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 68 ++++++++++++++++----------- include/media/v4l2-subdev.h | 9 ++-- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 29b84364fb951d..c2e40a8d5aab6d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2056,37 +2056,43 @@ static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; int ret; /* * The subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. + * to the .s_stream() operation. */ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + /* + * .s_stream() means there is no streams support, so the only allowed + * stream is the implicit stream 0. + */ + if (streams_mask != BIT_ULL(0)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; - if (sd->enabled_streams & streams_mask) { - dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", - streams_mask, sd->entity.name, pad); + if (sd->enabled_pads & BIT_ULL(pad)) { + dev_dbg(dev, "pad %u already enabled on %s\n", + pad, sd->entity.name); return -EALREADY; } - /* Start streaming when the first streams are enabled. */ - if (!sd->enabled_streams) { + /* Start streaming when the first pad is enabled. */ + if (!sd->enabled_pads) { ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) return ret; } - sd->enabled_streams |= streams_mask; + sd->enabled_pads |= BIT_ULL(pad); return 0; } @@ -2173,37 +2179,43 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; int ret; /* - * If the subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. + * If the subdev doesn't implement pad-based stream enable, fall back + * to the .s_stream() operation. */ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + /* + * .s_stream() means there is no streams support, so the only allowed + * stream is the implicit stream 0. + */ + if (streams_mask != BIT_ULL(0)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; - if ((sd->enabled_streams & streams_mask) != streams_mask) { - dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", - streams_mask, sd->entity.name, pad); + if (!(sd->enabled_pads & BIT_ULL(pad))) { + dev_dbg(dev, "pad %u already disabled on %s\n", + pad, sd->entity.name); return -EALREADY; } /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_streams & ~streams_mask)) { + if (!(sd->enabled_pads & ~BIT_ULL(pad))) { ret = v4l2_subdev_call(sd, video, s_stream, 0); if (ret) return ret; } - sd->enabled_streams &= ~streams_mask; + sd->enabled_pads &= ~BIT_ULL(pad); return 0; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 2f6ffaf46b1df3..931b7ac574ea49 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1035,10 +1035,9 @@ struct v4l2_subdev_platform_data { * @active_state: Active state for the subdev (NULL for subdevs tracking the * state internally). Initialized by calling * v4l2_subdev_init_finalize(). - * @enabled_streams: Bitmask of enabled streams used by - * v4l2_subdev_enable_streams() and - * v4l2_subdev_disable_streams() helper functions for fallback - * cases. + * @enabled_pads: Bitmask of enabled pads used by v4l2_subdev_enable_streams() + * and v4l2_subdev_disable_streams() helper functions for + * fallback cases. * @s_stream_enabled: Tracks whether streaming has been enabled with s_stream. * This is only for call_s_stream() internal use. * @@ -1088,7 +1087,7 @@ struct v4l2_subdev { * doesn't support it. */ struct v4l2_subdev_state *active_state; - u64 enabled_streams; + u64 enabled_pads; bool s_stream_enabled; }; From 131b03dc67f5e7bc81d703405289a6dcd8e7c874 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:09 +0300 Subject: [PATCH 043/159] media: subdev: Add v4l2_subdev_is_streaming() Add a helper function which returns whether the subdevice is streaming, i.e. if .s_stream or .enable_streams has been called successfully. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 5f3ce14fae742d1d23061c3122d93edb879ebf53) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 25 +++++++++++++++++++++++++ include/media/v4l2-subdev.h | 13 +++++++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index c2e40a8d5aab6d..2b37ef30528c66 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2371,6 +2371,31 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd) +{ + struct v4l2_subdev_state *state; + + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) + return sd->s_stream_enabled; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return !!sd->enabled_pads; + + state = v4l2_subdev_get_locked_active_state(sd); + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg; + + cfg = &state->stream_configs.configs[i]; + + if (cfg->enabled) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming); + int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd) { #if IS_REACHABLE(CONFIG_LEDS_CLASS) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 931b7ac574ea49..db3bd7433eab3d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1988,4 +1988,17 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers; void v4l2_subdev_notify_event(struct v4l2_subdev *sd, const struct v4l2_event *ev); +/** + * v4l2_subdev_is_streaming() - Returns if the subdevice is streaming + * @sd: The subdevice + * + * v4l2_subdev_is_streaming() tells if the subdevice is currently streaming. + * "Streaming" here means whether .s_stream() or .enable_streams() has been + * successfully called, and the streaming has not yet been disabled. + * + * If the subdevice implements .enable_streams() this function must be called + * while holding the active state lock. + */ +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd); + #endif /* _V4L2_SUBDEV_H */ From 0b0d7f22f8575ab23dfb82d2fbcde9fd149e10b2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:10 +0300 Subject: [PATCH 044/159] media: subdev: Support privacy led in v4l2_subdev_enable/disable_streams() We support camera privacy leds with the .s_stream() operation, in call_s_stream(), but we don't have that support when the subdevice implements .enable/disable_streams() operations. Add the support by enabling the led when the first stream for a subdevice is enabled, and disabling the led then the last stream is disabled. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 585d8fd5ebb9a946dc5c4931e77f6f6cefbdd501) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2b37ef30528c66..b071118b5e458c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2102,6 +2102,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; + bool already_streaming; u64 found_streams = 0; unsigned int i; int ret; @@ -2150,6 +2151,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); + already_streaming = v4l2_subdev_is_streaming(sd); + /* Call the .enable_streams() operation. */ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, streams_mask); @@ -2168,6 +2171,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, cfg->enabled = true; } + if (!already_streaming) + v4l2_subdev_enable_privacy_led(sd); + done: v4l2_subdev_unlock_state(state); @@ -2292,6 +2298,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, } done: + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); + v4l2_subdev_unlock_state(state); return ret; From be0b8bbfa45e39d5a015aad514971d9b87ca4eb2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:11 +0300 Subject: [PATCH 045/159] media: subdev: Refactor v4l2_subdev_enable/disable_streams() Add two internal helper functions, v4l2_subdev_collect_streams() and v4l2_subdev_set_streams_enabled(), which allows us to refactor v4l2_subdev_enable/disable_streams() functions. This (I think) makes the code a bit easier to read, and lets us more easily add new functionality in the helper functions in the following patch. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 86862307606b9f07d36097f7c9f018ad04f36ce0) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 109 ++++++++++++++------------ 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b071118b5e458c..fd681f477a8bcc 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2052,6 +2052,42 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); +static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + u64 *found_streams, + u64 *enabled_streams) +{ + *found_streams = 0; + *enabled_streams = 0; + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; + + *found_streams |= BIT_ULL(cfg->stream); + if (cfg->enabled) + *enabled_streams |= BIT_ULL(cfg->stream); + } +} + +static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + bool enabled) +{ + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = enabled; + } +} + static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { @@ -2103,8 +2139,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; bool already_streaming; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; int ret; /* A few basic sanity checks first. */ @@ -2125,22 +2161,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, * Verify that the requested streams exist and that they are not * already enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (cfg->enabled) { - dev_dbg(dev, "stream %u already enabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2149,6 +2172,13 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams) { + dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n", + enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); already_streaming = v4l2_subdev_is_streaming(sd); @@ -2163,13 +2193,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, } /* Mark the streams as enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = true; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); if (!already_streaming) v4l2_subdev_enable_privacy_led(sd); @@ -2231,8 +2255,8 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; int ret; /* A few basic sanity checks first. */ @@ -2253,22 +2277,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, * Verify that the requested streams exist and that they are not * already disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (!cfg->enabled) { - dev_dbg(dev, "stream %u already disabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2277,6 +2288,13 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n", + streams_mask & ~enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); /* Call the .disable_streams() operation. */ @@ -2288,14 +2306,7 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } - /* Mark the streams as disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = false; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: if (!v4l2_subdev_is_streaming(sd)) From 370ae3c1aaa51f10242a62a243571654fd80d7af Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:12 +0300 Subject: [PATCH 046/159] media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams() At the moment the v4l2_subdev_enable/disable_streams() functions call fallback helpers to handle the case where the subdev only implements .s_stream(), and the main function handles the case where the subdev implements streams (V4L2_SUBDEV_FL_STREAMS, which implies .enable/disable_streams()). What is missing is support for subdevs which do not implement streams support, but do implement .enable/disable_streams(). Example cases of these subdevices are single-stream cameras, where using .enable/disable_streams() is not required but helps us remove the users of the legacy .s_stream(), and subdevices with multiple source pads (but single stream per pad), where .enable/disable_streams() allows the subdevice to control the enable/disable state per pad. The two single-streams cases (.s_stream() and .enable/disable_streams()) are very similar, and with small changes we can change the v4l2_subdev_enable/disable_streams() functions to support all three cases, without needing separate fallback functions. A few potentially problematic details, though: - For the single-streams cases we use sd->enabled_pads field, which limits the number of pads for the subdevice to 64. For simplicity I added the check for this limitation to the beginning of the function, and it also applies to the streams case. - The fallback functions only allowed the target pad to be a source pad. It is not very clear to me why this check was needed, but it was not needed in the streams case. However, I doubt the v4l2_subdev_enable/disable_streams() code has ever been tested with sink pads, so to be on the safe side, I added the same check to the v4l2_subdev_enable/disable_streams() functions. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit b62949ddaa52e7612d3bbd90095079f97946c821) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 194 ++++++++++++-------------- 1 file changed, 86 insertions(+), 108 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index fd681f477a8bcc..4872e74bbf014c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2058,6 +2058,13 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, u64 *found_streams, u64 *enabled_streams) { + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + *found_streams = BIT_ULL(0); + *enabled_streams = + (sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0; + return; + } + *found_streams = 0; *enabled_streams = 0; @@ -2079,6 +2086,14 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, u32 pad, u64 streams_mask, bool enabled) { + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + if (enabled) + sd->enabled_pads |= BIT_ULL(pad); + else + sd->enabled_pads &= ~BIT_ULL(pad); + return; + } + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { struct v4l2_subdev_stream_config *cfg = &state->stream_configs.configs[i]; @@ -2088,51 +2103,6 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, } } -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - int ret; - - /* - * The subdev doesn't implement pad-based stream enable, fall back - * to the .s_stream() operation. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; - - /* - * .s_stream() means there is no streams support, so the only allowed - * stream is the implicit stream 0. - */ - if (streams_mask != BIT_ULL(0)) - return -EOPNOTSUPP; - - /* - * We use a 64-bit bitmask for tracking enabled pads, so only subdevices - * with 64 pads or less can be supported. - */ - if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) - return -EOPNOTSUPP; - - if (sd->enabled_pads & BIT_ULL(pad)) { - dev_dbg(dev, "pad %u already enabled on %s\n", - pad, sd->entity.name); - return -EALREADY; - } - - /* Start streaming when the first pad is enabled. */ - if (!sd->enabled_pads) { - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret) - return ret; - } - - sd->enabled_pads |= BIT_ULL(pad); - - return 0; -} - int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { @@ -2141,21 +2111,33 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, bool already_streaming; u64 enabled_streams; u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!v4l2_subdev_has_op(sd, pad, enable_streams)) - return v4l2_subdev_enable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not @@ -2183,9 +2165,18 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, already_streaming = v4l2_subdev_is_streaming(sd); - /* Call the .enable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .enable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, + streams_mask); + } else { + /* Start streaming when the first pad is enabled. */ + if (!already_streaming) + ret = v4l2_subdev_call(sd, video, s_stream, 1); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2195,34 +2186,39 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, /* Mark the streams as enabled. */ v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); - if (!already_streaming) + /* + * TODO: When all the drivers have been changed to use + * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(), + * instead of calling .s_stream() operation directly, we can remove + * the privacy LED handling from call_s_stream() and do it here + * for all cases. + */ + if (!use_s_stream && !already_streaming) v4l2_subdev_enable_privacy_led(sd); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) + v4l2_subdev_unlock_state(state); return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) +int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; + struct v4l2_subdev_state *state; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; - /* - * If the subdev doesn't implement pad-based stream enable, fall back - * to the .s_stream() operation. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; + /* A few basic sanity checks first. */ + if (pad >= sd->entity.num_pads) + return -EINVAL; - /* - * .s_stream() means there is no streams support, so the only allowed - * stream is the implicit stream 0. - */ - if (streams_mask != BIT_ULL(0)) + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; /* @@ -2232,46 +2228,16 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) return -EOPNOTSUPP; - if (!(sd->enabled_pads & BIT_ULL(pad))) { - dev_dbg(dev, "pad %u already disabled on %s\n", - pad, sd->entity.name); - return -EALREADY; - } - - /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_pads & ~BIT_ULL(pad))) { - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret) - return ret; - } - - sd->enabled_pads &= ~BIT_ULL(pad); - - return 0; -} - -int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - struct v4l2_subdev_state *state; - u64 enabled_streams; - u64 found_streams; - int ret; - - /* A few basic sanity checks first. */ - if (pad >= sd->entity.num_pads) - return -EINVAL; - if (!streams_mask) return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!v4l2_subdev_has_op(sd, pad, disable_streams)) - return v4l2_subdev_disable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not @@ -2297,9 +2263,19 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); - /* Call the .disable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .disable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, + streams_mask); + } else { + /* Stop streaming when the last streams are disabled. */ + + if (!(sd->enabled_pads & ~BIT_ULL(pad))) + ret = v4l2_subdev_call(sd, video, s_stream, 0); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2309,10 +2285,12 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: - if (!v4l2_subdev_is_streaming(sd)) - v4l2_subdev_disable_privacy_led(sd); + if (!use_s_stream) { + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } return ret; } From dcc47b541335d420ce12f2345af108ea9574ffc2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:13 +0300 Subject: [PATCH 047/159] media: subdev: Support non-routing subdevs in v4l2_subdev_s_stream_helper() At the moment v4l2_subdev_s_stream_helper() only works for subdevices that support routing. As enable/disable_streams now also works for subdevices without routing, improve v4l2_subdev_s_stream_helper() to do the same. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 93c726f41afb8407d43e0bea66d716f6446c9e46) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 4872e74bbf014c..6e4d1681365d62 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2319,15 +2319,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) if (WARN_ON(pad_index == -1)) return -EINVAL; - /* - * As there's a single source pad, just collect all the source streams. - */ - state = v4l2_subdev_lock_and_get_active_state(sd); + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + /* + * As there's a single source pad, just collect all the source + * streams. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); - for_each_active_route(&state->routing, route) - source_mask |= BIT_ULL(route->source_stream); + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } else { + /* + * For non-streams subdevices, there's a single implicit stream + * per pad. + */ + source_mask = BIT_ULL(0); + } if (enable) return v4l2_subdev_enable_streams(sd, pad_index, source_mask); From f1fb219ffb84379f213e77ade1d29abbb0cd6704 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 13 Nov 2023 11:35:23 +0200 Subject: [PATCH 048/159] media: v4l2-subdev: Drop unreacheable warning The v4l2_subdev_link_validate_get_format() function warns if the pad given as argument belongs to a non-subdev entity. This can't happen, as the function is called from v4l2_subdev_link_validate() only (indirectly through v4l2_subdev_link_validate_locked()), and that function ensures that both ends of the link are subdevs. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen (cherry picked from commit 18a8f4c28884b7fb5fd3ba759c777cca693788ef) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6e4d1681365d62..e6941167f70663 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1220,14 +1220,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev *sd; int ret; - if (!is_media_entity_v4l2_subdev(pad->entity)) { - WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, - "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->function, pad->entity->name); - - return -EINVAL; - } - sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; From 5d70c5eb2dfe7728c009cdfdb0d7174c567c14dd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 02:56:01 +0300 Subject: [PATCH 049/159] media: v4l2-subdev: Refactor warnings in v4l2_subdev_link_validate() The v4l2_subdev_link_validate() function prints a one-time warning if it gets called on a link whose source or sink is not a subdev. As links get validated in the context of their sink, a call to the helper when the link's sink is not a subdev indicates that the driver has set its .link_validate() handler to v4l2_subdev_link_validate() on a non-subdev entity, which is a clear driver bug. On the other hand, the link's source not being a subdev indicates that the helper is used for a subdev connected to a video output device, which is a lesser issue, if only because this is currently common practice. There are no drivers left in the kernel that use v4l2_subdev_link_validate() in a context where it may get called on a non-subdev sink. Replace the pr_warn_once() with a WARN_ON_ONCE() in this case to make sure that new offenders won't be introduced. A subsequent change will improve the v4l2_subdev_link_validate() helper to properly support validating video device to subdev links. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus (cherry picked from commit d1307671e5221967809c4b626affbad29e371006) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index e6941167f70663..23ba705bc6b9a6 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1394,11 +1394,15 @@ int v4l2_subdev_link_validate(struct media_link *link) bool states_locked; int ret; - if (!is_media_entity_v4l2_subdev(link->sink->entity) || - !is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("%s of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - !is_media_entity_v4l2_subdev(link->sink->entity) ? - "sink" : "source", + /* + * Links are validated in the context of the sink entity. Usage of this + * helper on a sink that is not a subdev is a clear driver bug. + */ + if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity))) + return -EINVAL; + + if (!is_media_entity_v4l2_subdev(link->source->entity)) { + pr_warn_once("source of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); return 0; From 0280f8815f249f2306d1c7ca7de78563b50739e1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 03:02:42 +0300 Subject: [PATCH 050/159] media: v4l2-subdev: Support hybrid links in v4l2_subdev_link_validate() The v4l2_subdev_link_validate() helper function is meant to be used as a drop-in implementation of a V4L2 subdev entity .link_validate() handler. It supports subdev-to-subdev links only, and complains if one end of the link is not a subdev. This forces drivers that have video output devices connected to subdevs to implement a custom .link_validate() handler, calling v4l2_subdev_link_validate() for the subdev-to-subdev links, and performing manual link validation for the video-to-subdev links. Video devices embed a media entity, and therefore also have a .link_validate() operation. For video capture devices, the operation should be manually implemented by drivers for validate the subdev-to-video links. For video output devices, on the other hand, that operation is never called, as link validation is performed in the context of the sink entity. As a result, we end up forcing drivers to implement a custom .link_validate() handler for subdevs connected to video output devices, when the video devices provide an operation that could be used for that purpose. To improve that situation, make v4l2_subdev_link_validate() delegate link validation to the source's .link_validate() operation when the link source is a video device and the link sink is a subdev. This allows broader usage of v4l2_subdev_link_validate(), and simplifies drivers by making video device link validation easy to implement in the video device .link_validate(), regardless of whether the video device is an output device or a capture device. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus (cherry picked from commit 5fd3e2412ade67ea20d855f0aea821c650d27559) Signed-off-by: Jacopo Mondi --- drivers/media/v4l2-core/v4l2-subdev.c | 43 +++++++++++++++++++++++---- include/media/v4l2-subdev.h | 6 ++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 23ba705bc6b9a6..fea4b4d934511d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1401,13 +1401,46 @@ int v4l2_subdev_link_validate(struct media_link *link) if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity))) return -EINVAL; - if (!is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("source of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index); - return 0; + /* + * If the source is a video device, delegate link validation to it. This + * allows usage of this helper for subdev connected to a video output + * device, provided that the driver implement the video output device's + * .link_validate() operation. + */ + if (is_media_entity_v4l2_video_device(link->source->entity)) { + struct media_entity *source = link->source->entity; + + if (!source->ops || !source->ops->link_validate) { + /* + * Many existing drivers do not implement the required + * .link_validate() operation for their video devices. + * Print a warning to get the drivers fixed, and return + * 0 to avoid breaking userspace. This should + * eventually be turned into a WARN_ON() when all + * drivers will have been fixed. + */ + pr_warn_once("video device '%s' does not implement .link_validate(), driver bug!\n", + source->name); + return 0; + } + + /* + * Avoid infinite loops in case a video device incorrectly uses + * this helper function as its .link_validate() handler. + */ + if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate)) + return -EINVAL; + + return source->ops->link_validate(link); } + /* + * If the source is still not a subdev, usage of this helper is a clear + * driver bug. + */ + if (WARN_ON(!is_media_entity_v4l2_subdev(link->source->entity))) + return -EINVAL; + sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); source_sd = media_entity_to_v4l2_subdev(link->source->entity); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index db3bd7433eab3d..b568d1690b5d84 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1312,6 +1312,12 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, * calls v4l2_subdev_link_validate_default() to ensure that * width, height and the media bus pixel code are equal on both * source and sink of the link. + * + * The function can be used as a drop-in &media_entity_ops.link_validate + * implementation for v4l2_subdev instances. It supports all links between + * subdevs, as well as links between subdevs and video devices, provided that + * the video devices also implement their &media_entity_ops.link_validate + * operation. */ int v4l2_subdev_link_validate(struct media_link *link); From f3cbee2715f44c854d373caf15596a42a18939b9 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 11 Sep 2023 16:56:36 +0200 Subject: [PATCH 051/159] media: i2c: Drop ifdeffery from sensor drivers Since commit 7d3c7d2a2914 ("media: i2c: Add a camera sensor top level menu") the CONFIG_MEDIA_CONTROLLER and CONFIG_VIDEO_V4L2_SUBDEV_API are selected by the top-level VIDEO_CAMERA_SENSOR menu. Remove all ifdefferies from camera sensor drivers to simplify the code. Compile-tested only. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit b4e9a2d278c955715926f332773a9f66140fc41d) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/mt9m111.c | 13 +------------ drivers/media/i2c/mt9v011.c | 6 ------ drivers/media/i2c/mt9v111.c | 14 -------------- drivers/media/i2c/ov2640.c | 11 +---------- drivers/media/i2c/ov2659.c | 16 +--------------- drivers/media/i2c/ov2685.c | 12 ------------ drivers/media/i2c/ov5695.c | 19 ------------------- drivers/media/i2c/ov7670.c | 20 -------------------- drivers/media/i2c/ov772x.c | 4 ---- drivers/media/i2c/ov7740.c | 23 ++--------------------- 10 files changed, 5 insertions(+), 133 deletions(-) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 05f710ee4c140f..e17ff41ebba091 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -244,9 +244,7 @@ struct mt9m111 { bool is_streaming; /* user point of view - 0: falling 1: rising edge */ unsigned int pclk_sample:1; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif }; static const struct mt9m111_mode_info mt9m111_mode_data[MT9M111_NUM_MODES] = { @@ -527,13 +525,9 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mf = v4l2_subdev_get_try_format(sd, sd_state, format->pad); format->format = *mf; return 0; -#else - return -EINVAL; -#endif } mf->width = mt9m111->width; @@ -1120,7 +1114,6 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) static int mt9m111_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, sd_state, 0); @@ -1132,7 +1125,7 @@ static int mt9m111_init_cfg(struct v4l2_subdev *sd, format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; format->quantization = V4L2_QUANTIZATION_DEFAULT; format->xfer_func = V4L2_XFER_FUNC_DEFAULT; -#endif + return 0; } @@ -1315,13 +1308,11 @@ static int mt9m111_probe(struct i2c_client *client) return ret; } -#ifdef CONFIG_MEDIA_CONTROLLER mt9m111->pad.flags = MEDIA_PAD_FL_SOURCE; mt9m111->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&mt9m111->subdev.entity, 1, &mt9m111->pad); if (ret < 0) goto out_hdlfree; -#endif mt9m111->current_mode = &mt9m111_mode_data[MT9M111_MODE_SXGA_15FPS]; mt9m111->frame_interval.numerator = 1; @@ -1350,10 +1341,8 @@ static int mt9m111_probe(struct i2c_client *client) return 0; out_entityclean: -#ifdef CONFIG_MEDIA_CONTROLLER media_entity_cleanup(&mt9m111->subdev.entity); out_hdlfree: -#endif v4l2_ctrl_handler_free(&mt9m111->hdl); return ret; diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 5ff7322b3c8787..7f313d731839be 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -49,9 +49,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); struct mt9v011 { struct v4l2_subdev sd; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif struct v4l2_ctrl_handler ctrls; unsigned width, height; unsigned xtal; @@ -483,9 +481,7 @@ static int mt9v011_probe(struct i2c_client *c) u16 version; struct mt9v011 *core; struct v4l2_subdev *sd; -#ifdef CONFIG_MEDIA_CONTROLLER int ret; -#endif /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, @@ -499,14 +495,12 @@ static int mt9v011_probe(struct i2c_client *c) sd = &core->sd; v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); -#ifdef CONFIG_MEDIA_CONTROLLER core->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &core->pad); if (ret < 0) return ret; -#endif /* Check if the sensor is really a MT9V011 */ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index a5043195fe6f59..2c321e4884bea1 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -121,9 +121,7 @@ struct mt9v111_dev { u8 addr_space; struct v4l2_subdev sd; -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl *auto_awb; struct v4l2_ctrl *auto_exp; @@ -797,11 +795,7 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: -#if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) return v4l2_subdev_get_try_format(&mt9v111->sd, sd_state, pad); -#else - return &sd_state->pads->try_fmt; -#endif case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v111->fmt; default: @@ -987,11 +981,9 @@ static const struct v4l2_subdev_ops mt9v111_ops = { .pad = &mt9v111_pad_ops, }; -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) static const struct media_entity_operations mt9v111_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; -#endif /* --- V4L2 ctrl --- */ static int mt9v111_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1203,7 +1195,6 @@ static int mt9v111_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; mt9v111->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1212,7 +1203,6 @@ static int mt9v111_probe(struct i2c_client *client) ret = media_entity_pads_init(&mt9v111->sd.entity, 1, &mt9v111->pad); if (ret) goto error_free_entity; -#endif ret = mt9v111_chip_probe(mt9v111); if (ret) @@ -1225,9 +1215,7 @@ static int mt9v111_probe(struct i2c_client *client) return 0; error_free_entity: -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&mt9v111->sd.entity); -#endif error_free_ctrls: v4l2_ctrl_handler_free(&mt9v111->ctrls); @@ -1245,9 +1233,7 @@ static void mt9v111_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&mt9v111->ctrls); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 1c04281adc4143..e18e122426d432 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -293,9 +293,7 @@ struct ov2640_win_size { struct ov2640_priv { struct v4l2_subdev subdev; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl_handler hdl; u32 cfmt_code; struct clk *clk; @@ -922,13 +920,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mf = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mf; return 0; -#else - return -EINVAL; -#endif } mf->width = priv->win->width; @@ -1005,7 +999,6 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, static int ov2640_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); const struct ov2640_win_size *win = @@ -1019,7 +1012,7 @@ static int ov2640_init_cfg(struct v4l2_subdev *sd, try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -#endif + return 0; } @@ -1239,13 +1232,11 @@ static int ov2640_probe(struct i2c_client *client) ret = priv->hdl.error; goto err_hdl; } -#if defined(CONFIG_MEDIA_CONTROLLER) priv->pad.flags = MEDIA_PAD_FL_SOURCE; priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) goto err_hdl; -#endif ret = ov2640_video_probe(client); if (ret < 0) diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 5429bd2eb05318..2c3dbe164eb696 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1031,7 +1031,6 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, dev_dbg(&client->dev, "ov2659_get_fmt\n"); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mf; mf = v4l2_subdev_get_try_format(sd, sd_state, 0); @@ -1039,9 +1038,6 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, fmt->format = *mf; mutex_unlock(&ov2659->lock); return 0; -#else - return -EINVAL; -#endif } mutex_lock(&ov2659->lock); @@ -1113,10 +1109,8 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2659->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *mf = fmt->format; -#endif } else { s64 val; @@ -1306,7 +1300,6 @@ static int ov2659_power_on(struct device *dev) * V4L2 subdev internal operations */ -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1319,7 +1312,6 @@ static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static const struct v4l2_subdev_core_ops ov2659_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, @@ -1338,7 +1330,6 @@ static const struct v4l2_subdev_pad_ops ov2659_subdev_pad_ops = { .set_fmt = ov2659_set_fmt, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_ops ov2659_subdev_ops = { .core = &ov2659_subdev_core_ops, .video = &ov2659_subdev_video_ops, @@ -1348,7 +1339,6 @@ static const struct v4l2_subdev_ops ov2659_subdev_ops = { static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = { .open = ov2659_open, }; -#endif static int ov2659_detect(struct v4l2_subdev *sd) { @@ -1489,15 +1479,12 @@ static int ov2659_probe(struct i2c_client *client) sd = &ov2659->sd; client->flags |= I2C_CLIENT_SCCB; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); + v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); sd->internal_ops = &ov2659_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov2659->pad); @@ -1505,7 +1492,6 @@ static int ov2659_probe(struct i2c_client *client) v4l2_ctrl_handler_free(&ov2659->ctrls); return ret; } -#endif mutex_init(&ov2659->lock); diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 303793e1f97d64..de0d34e43969fc 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -547,7 +547,6 @@ static int ov2685_s_stream(struct v4l2_subdev *sd, int on) return ret; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov2685 *ov2685 = to_ov2685(sd); @@ -563,7 +562,6 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static int __maybe_unused ov2685_runtime_resume(struct device *dev) { @@ -660,11 +658,9 @@ static const struct v4l2_subdev_ops ov2685_subdev_ops = { .pad = &ov2685_pad_ops, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov2685_internal_ops = { .open = ov2685_open, }; -#endif static const struct v4l2_ctrl_ops ov2685_ctrl_ops = { .s_ctrl = ov2685_set_ctrl, @@ -833,17 +829,13 @@ static int ov2685_probe(struct i2c_client *client) if (ret) goto err_power_off; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ov2685->subdev.internal_ops = &ov2685_internal_ops; ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov2685->pad.flags = MEDIA_PAD_FL_SOURCE; ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad); if (ret < 0) goto err_power_off; -#endif ret = v4l2_async_register_subdev(&ov2685->subdev); if (ret) { @@ -858,9 +850,7 @@ static int ov2685_probe(struct i2c_client *client) return 0; err_clean_entity: -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&ov2685->subdev.entity); -#endif err_power_off: __ov2685_power_off(ov2685); err_free_handler: @@ -877,9 +867,7 @@ static void ov2685_remove(struct i2c_client *client) struct ov2685 *ov2685 = to_ov2685(sd); v4l2_async_unregister_subdev(sd); -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&ov2685->ctrl_handler); mutex_destroy(&ov2685->mutex); diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 3023b72541677d..8d1c3a673c23a2 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -821,9 +821,7 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; -#endif } else { ov5695->cur_mode = mode; h_blank = mode->hts_def - mode->width; @@ -849,13 +847,8 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov5695->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); -#else - mutex_unlock(&ov5695->mutex); - return -EINVAL; -#endif } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -1048,7 +1041,6 @@ static int __maybe_unused ov5695_runtime_suspend(struct device *dev) return 0; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov5695 *ov5695 = to_ov5695(sd); @@ -1068,18 +1060,15 @@ static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static const struct dev_pm_ops ov5695_pm_ops = { SET_RUNTIME_PM_OPS(ov5695_runtime_suspend, ov5695_runtime_resume, NULL) }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov5695_internal_ops = { .open = ov5695_open, }; -#endif static const struct v4l2_subdev_video_ops ov5695_video_ops = { .s_stream = ov5695_s_stream, @@ -1322,17 +1311,13 @@ static int ov5695_probe(struct i2c_client *client) if (ret) goto err_power_off; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov5695_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov5695->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad); if (ret < 0) goto err_power_off; -#endif ret = v4l2_async_register_subdev_sensor(sd); if (ret) { @@ -1347,9 +1332,7 @@ static int ov5695_probe(struct i2c_client *client) return 0; err_clean_entity: -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif err_power_off: __ov5695_power_off(ov5695); err_free_handler: @@ -1366,9 +1349,7 @@ static void ov5695_remove(struct i2c_client *client) struct ov5695 *ov5695 = to_ov5695(sd); v4l2_async_unregister_subdev(sd); -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&ov5695->ctrl_handler); mutex_destroy(&ov5695->mutex); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 2f55491ef571f0..85bf745c0badd7 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -217,9 +217,7 @@ struct ov7670_devtype { struct ov7670_format_struct; /* coming later */ struct ov7670_info { struct v4l2_subdev sd; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl_handler hdl; struct { /* gain cluster */ @@ -1108,9 +1106,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif int ret; if (format->pad) @@ -1120,11 +1116,9 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) return ret; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); *mbus_fmt = format->format; -#endif return 0; } @@ -1148,18 +1142,12 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mbus_fmt; return 0; -#else - return -EINVAL; -#endif } else { format->format = info->format; } @@ -1720,7 +1708,6 @@ static void ov7670_get_default_format(struct v4l2_subdev *sd, format->field = V4L2_FIELD_NONE; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = @@ -1730,7 +1717,6 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif /* ----------------------------------------------------------------------- */ @@ -1766,11 +1752,9 @@ static const struct v4l2_subdev_ops ov7670_ops = { .pad = &ov7670_pad_ops, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops = { .open = ov7670_open, }; -#endif /* ----------------------------------------------------------------------- */ @@ -1861,10 +1845,8 @@ static int ov7670_probe(struct i2c_client *client) sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &ov7670_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7670_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif info->clock_speed = 30; /* default: a guess */ @@ -1977,13 +1959,11 @@ static int ov7670_probe(struct i2c_client *client) V4L2_EXPOSURE_MANUAL, false); v4l2_ctrl_cluster(2, &info->saturation); -#if defined(CONFIG_MEDIA_CONTROLLER) info->pad.flags = MEDIA_PAD_FL_SOURCE; info->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&info->sd.entity, 1, &info->pad); if (ret < 0) goto hdl_free; -#endif v4l2_ctrl_handler_setup(&info->hdl); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 8319dba46ddf56..1fbf72152956ac 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -433,9 +433,7 @@ struct ov772x_priv { struct mutex lock; int power_count; int streaming; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif enum v4l2_mbus_type bus_type; }; @@ -1488,13 +1486,11 @@ static int ov772x_probe(struct i2c_client *client) if (ret < 0) goto error_gpio_put; -#ifdef CONFIG_MEDIA_CONTROLLER priv->pad.flags = MEDIA_PAD_FL_SOURCE; priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) goto error_gpio_put; -#endif priv->cfmt = &ov772x_cfmts[0]; priv->win = &ov772x_win_sizes[0]; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index dffdb475e43391..78d04ce68971ad 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -83,9 +83,7 @@ struct ov7740 { struct v4l2_subdev subdev; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_mbus_framefmt format; const struct ov7740_pixfmt *fmt; /* Current format */ const struct ov7740_framesize *frmsize; @@ -807,9 +805,7 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); const struct ov7740_pixfmt *ovfmt; const struct ov7740_framesize *fsize; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif int ret; mutex_lock(&ov7740->mutex); @@ -822,11 +818,10 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) goto error; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); *mbus_fmt = format->format; -#endif mutex_unlock(&ov7740->mutex); return 0; } @@ -851,26 +846,18 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif - int ret = 0; mutex_lock(&ov7740->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); format->format = *mbus_fmt; - ret = 0; -#else - ret = -EINVAL; -#endif } else { format->format = ov7740->format; } mutex_unlock(&ov7740->mutex); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = { @@ -899,7 +886,6 @@ static void ov7740_get_default_format(struct v4l2_subdev *sd, format->field = V4L2_FIELD_NONE; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); @@ -916,7 +902,6 @@ static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = { .open = ov7740_open, }; -#endif static int ov7740_probe_dt(struct i2c_client *client, struct ov7740 *ov7740) @@ -1094,18 +1079,14 @@ static int ov7740_probe(struct i2c_client *client) sd = &ov7740->subdev; v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7740_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov7740->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad); if (ret) return ret; -#endif ret = ov7740_set_power(ov7740, 1); if (ret) From 295202ca16f7c9a8d36455c50c6aa70b8d7bc15f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:07 +0200 Subject: [PATCH 052/159] media: atmel-isc: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit f4b7c07dc19f70ba8fb3f290f76f6199e8090795) Signed-off-by: Jacopo Mondi --- .../staging/media/deprecated/atmel/atmel-isc-base.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c index f5d963904201fa..24b5bb715611c4 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c @@ -824,6 +824,8 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { + struct v4l2_rect *try_crop = + v4l2_subdev_get_pad_crop(isc->current_subdev->sd, sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -845,11 +847,11 @@ static void isc_try_fse(struct isc_device *isc, * just use the maximum ISC can receive. */ if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; + try_crop->width = isc->max_width; + try_crop->height = isc->max_height; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } From cebb405f3b4539f4cc6769c9a0d734d7b1abb62b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:09 +0200 Subject: [PATCH 053/159] media: tegra-video: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Reviewed-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 0623979d8352efe18f83c4fad95a2e61df17b3e7) Signed-off-by: Jacopo Mondi --- drivers/staging/media/tegra-video/vi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 94171e62dee9e4..a2f21c70a5bc84 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -439,6 +439,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, .which = V4L2_SUBDEV_FORMAT_ACTIVE, .target = V4L2_SEL_TGT_CROP_BOUNDS, }; + struct v4l2_rect *try_crop; int ret; subdev = tegra_channel_get_remote_source_subdev(chan); @@ -473,24 +474,25 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, * Attempt to obtain the format size from subdev. * If not available, try to get crop boundary from subdev. */ + try_crop = v4l2_subdev_get_pad_crop(subdev, sd_state, 0); fse.code = fmtinfo->code; ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse); if (ret) { if (!v4l2_subdev_has_op(subdev, pad, get_selection)) { - sd_state->pads->try_crop.width = 0; - sd_state->pads->try_crop.height = 0; + try_crop->width = 0; + try_crop->height = 0; } else { ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); if (ret) return -EINVAL; - sd_state->pads->try_crop.width = sdsel.r.width; - sd_state->pads->try_crop.height = sdsel.r.height; + try_crop->width = sdsel.r.width; + try_crop->height = sdsel.r.height; } } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); From af4e175c927295cf753b5586cb8dcd9b7583592e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:05 +0200 Subject: [PATCH 054/159] media: atmel-isi: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit f9c12d6783580f22f283bfc733fdd6c41a0c690d) Signed-off-by: Jacopo Mondi --- drivers/media/platform/atmel/atmel-isi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 4046212d48b415..bb83117bab2e69 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -559,11 +559,13 @@ static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, struct v4l2_subdev_state *sd_state) { - int ret; + struct v4l2_rect *try_crop = + v4l2_subdev_get_pad_crop(isi->entity.subdev, sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .code = isi_fmt->mbus_code, .which = V4L2_SUBDEV_FORMAT_TRY, }; + int ret; ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, sd_state, &fse); @@ -572,11 +574,11 @@ static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, * just use the maximum ISI can receive. */ if (ret) { - sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; - sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; + try_crop->width = MAX_SUPPORT_WIDTH; + try_crop->height = MAX_SUPPORT_HEIGHT; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } From feec322af32bf3cbb6281fe46a5140201defe641 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:06 +0200 Subject: [PATCH 055/159] media: microchip-isc: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 098a1eed5daeb62e2c974f6c37d3b0d582c5586e) Signed-off-by: Jacopo Mondi --- drivers/media/platform/microchip/microchip-isc-base.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index 8dbf7bc1e863ba..acb7395c156612 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -858,6 +858,8 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { + struct v4l2_rect *try_crop = + v4l2_subdev_get_pad_crop(isc->current_subdev->sd, sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -879,11 +881,11 @@ static void isc_try_fse(struct isc_device *isc, * just use the maximum ISC can receive. */ if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; + try_crop->width = isc->max_width; + try_crop->height = isc->max_height; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } From b2842a898c15b4da0955d2c43d185b0202f27bb9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Oct 2023 23:40:08 +0200 Subject: [PATCH 056/159] media: atomisp: Use accessors for pad config 'try_*' fields The 'try_*' fields of the v4l2_subdev_pad_config structure are meant to be accessed through helper functions. Replace direct access with usage of the v4l2_subdev_get_pad_format(), v4l2_subdev_get_pad_crop() and v4l2_subdev_get_pad_compose() helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 9295e7e0cc38be1e99e330a0ccefa90569ffc91d) Signed-off-by: Jacopo Mondi --- drivers/staging/media/atomisp/i2c/atomisp-gc2235.c | 2 +- drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c | 2 +- drivers/staging/media/atomisp/i2c/atomisp-ov2722.c | 2 +- drivers/staging/media/atomisp/pci/atomisp_tpg.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 9fa390fbc5f31e..5e438c5fd4a92e 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -561,7 +561,7 @@ static int gc2235_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; mutex_unlock(&dev->input_lock); return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index 1c6643c442ef36..db76f52e1dc8e4 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -666,7 +666,7 @@ static int mt9m114_set_fmt(struct v4l2_subdev *sd, fmt->height = res->height; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c index 6a72691ed5b74d..ae70e04040dd69 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c @@ -671,7 +671,7 @@ static int ov2722_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c index 074826a5b70643..b2376ebf45a166 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c +++ b/drivers/staging/media/atomisp/pci/atomisp_tpg.c @@ -47,7 +47,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd, /* only raw8 grbg is supported by TPG */ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; return 0; } return 0; From eb6aadc45f5068d7ce566f0663e519f861578e29 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 23 Aug 2023 11:44:37 +0300 Subject: [PATCH 057/159] media: ccs: Correct error handling in ccs_register_subdev ccs_register_subdev() did not clean up the media entity in error case, do that now. Also switch to goto based error handling. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit f055e53ebcfc042e6a26acc27acac25ef9c0188e) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 6f8fbd82e21c8f..3fed071b65abbb 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2968,7 +2968,7 @@ static int ccs_register_subdev(struct ccs_sensor *sensor, rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, &ssd->sd); if (rval) { dev_err(&client->dev, "v4l2_device_register_subdev failed\n"); - return rval; + goto out_media_entity_cleanup; } rval = media_create_pad_link(&ssd->sd.entity, source_pad, @@ -2976,11 +2976,18 @@ static int ccs_register_subdev(struct ccs_sensor *sensor, link_flags); if (rval) { dev_err(&client->dev, "media_create_pad_link failed\n"); - v4l2_device_unregister_subdev(&ssd->sd); - return rval; + goto out_v4l2_device_unregister_subdev; } return 0; + +out_v4l2_device_unregister_subdev: + v4l2_device_unregister_subdev(&ssd->sd); + +out_media_entity_cleanup: + media_entity_cleanup(&ssd->sd.entity); + + return rval; } static void ccs_unregistered(struct v4l2_subdev *subdev) From 40931cdc20df82f00b25ef22aaf0c17dae9a2a82 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 9 Aug 2023 16:12:38 +0300 Subject: [PATCH 058/159] media: ccs: Switch to init_cfg Use init_cfg() instead of manually setting up defaults in file handle open. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 3935665586783dffaafb8308f561b12800245af5) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 3fed071b65abbb..a4f593866ad699 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2945,7 +2945,6 @@ static int ccs_identify_module(struct ccs_sensor *sensor) } static const struct v4l2_subdev_ops ccs_ops; -static const struct v4l2_subdev_internal_ops ccs_internal_ops; static const struct media_entity_operations ccs_entity_ops; static int ccs_register_subdev(struct ccs_sensor *sensor, @@ -3076,13 +3075,13 @@ static void ccs_create_subdev(struct ccs_sensor *sensor, if (ssd == sensor->src) return; - ssd->sd.internal_ops = &ccs_internal_ops; ssd->sd.owner = THIS_MODULE; ssd->sd.dev = &client->dev; v4l2_set_subdevdata(&ssd->sd, client); } -static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ccs_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ccs_subdev *ssd = to_ccs_subdev(sd); struct ccs_sensor *sensor = ssd->sensor; @@ -3092,9 +3091,9 @@ static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) for (i = 0; i < ssd->npads; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, i); + v4l2_subdev_get_try_format(sd, sd_state, i); struct v4l2_rect *try_crop = - v4l2_subdev_get_try_crop(sd, fh->state, i); + v4l2_subdev_get_try_crop(sd, sd_state, i); struct v4l2_rect *try_comp; ccs_get_native_size(ssd, try_crop); @@ -3107,7 +3106,7 @@ static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) if (ssd == sensor->pixel_array) continue; - try_comp = v4l2_subdev_get_try_compose(sd, fh->state, i); + try_comp = v4l2_subdev_get_try_compose(sd, sd_state, i); *try_comp = *try_crop; } @@ -3123,6 +3122,7 @@ static const struct v4l2_subdev_video_ops ccs_video_ops = { }; static const struct v4l2_subdev_pad_ops ccs_pad_ops = { + .init_cfg = ccs_init_cfg, .enum_mbus_code = ccs_enum_mbus_code, .get_fmt = ccs_get_format, .set_fmt = ccs_set_format, @@ -3148,11 +3148,6 @@ static const struct media_entity_operations ccs_entity_ops = { static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { .registered = ccs_registered, .unregistered = ccs_unregistered, - .open = ccs_open, -}; - -static const struct v4l2_subdev_internal_ops ccs_internal_ops = { - .open = ccs_open, }; /* ----------------------------------------------------------------------------- From 08156a0bbc10dfce94b687c578f7677fa5855b19 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 2 Oct 2023 10:19:15 +0300 Subject: [PATCH 059/159] media: ccs: Rename ccs_create_subdev as ccs_init_subdev The ccs_create_subdev() function initialises a sub-device in the CCS driver, including CCS specific needs. Rename it as ccs_init_subdev() as it better reflects what the function does. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit ac84b72242ce745ccf79df1504a53afd079e5b45) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index a4f593866ad699..293378cca5f931 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3038,9 +3038,9 @@ static void ccs_cleanup(struct ccs_sensor *sensor) ccs_free_controls(sensor); } -static void ccs_create_subdev(struct ccs_sensor *sensor, - struct ccs_subdev *ssd, const char *name, - unsigned short num_pads, u32 function) +static void ccs_init_subdev(struct ccs_sensor *sensor, + struct ccs_subdev *ssd, const char *name, + unsigned short num_pads, u32 function) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -3589,12 +3589,12 @@ static int ccs_probe(struct i2c_client *client) sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); - ccs_create_subdev(sensor, sensor->scaler, " scaler", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_create_subdev(sensor, sensor->binner, " binner", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1, - MEDIA_ENT_F_CAM_SENSOR); + ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + ccs_init_subdev(sensor, sensor->binner, " binner", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + ccs_init_subdev(sensor, sensor->pixel_array, " pixel_array", 1, + MEDIA_ENT_F_CAM_SENSOR); rval = ccs_init_controls(sensor); if (rval < 0) From 9f0b4586da7fff1fea73e44b05af59109d92e962 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 2 Oct 2023 10:25:37 +0300 Subject: [PATCH 060/159] media: ccs: Move media_entity_pads_init to init from register The media entity will soon need to be initialised before the sub-device init finalisation that allocates memory for sub-device state. Do that now. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 77ab9dc8d926bf5a0ce1f6dde14c0bb29c473426) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 75 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 293378cca5f931..c72995f9facee4 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2958,16 +2958,10 @@ static int ccs_register_subdev(struct ccs_sensor *sensor, if (!sink_ssd) return 0; - rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); - if (rval) { - dev_err(&client->dev, "media_entity_pads_init failed\n"); - return rval; - } - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, &ssd->sd); if (rval) { dev_err(&client->dev, "v4l2_device_register_subdev failed\n"); - goto out_media_entity_cleanup; + return rval; } rval = media_create_pad_link(&ssd->sd.entity, source_pad, @@ -2975,18 +2969,11 @@ static int ccs_register_subdev(struct ccs_sensor *sensor, link_flags); if (rval) { dev_err(&client->dev, "media_create_pad_link failed\n"); - goto out_v4l2_device_unregister_subdev; + v4l2_device_unregister_subdev(&ssd->sd); + return rval; } return 0; - -out_v4l2_device_unregister_subdev: - v4l2_device_unregister_subdev(&ssd->sd); - -out_media_entity_cleanup: - media_entity_cleanup(&ssd->sd.entity); - - return rval; } static void ccs_unregistered(struct v4l2_subdev *subdev) @@ -3031,6 +3018,10 @@ static int ccs_registered(struct v4l2_subdev *subdev) static void ccs_cleanup(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + media_entity_cleanup(&sensor->ssds[i].sd.entity); device_remove_file(&client->dev, &dev_attr_nvm); device_remove_file(&client->dev, &dev_attr_ident); @@ -3038,14 +3029,15 @@ static void ccs_cleanup(struct ccs_sensor *sensor) ccs_free_controls(sensor); } -static void ccs_init_subdev(struct ccs_sensor *sensor, - struct ccs_subdev *ssd, const char *name, - unsigned short num_pads, u32 function) +static int ccs_init_subdev(struct ccs_sensor *sensor, + struct ccs_subdev *ssd, const char *name, + unsigned short num_pads, u32 function) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; if (!ssd) - return; + return 0; if (ssd != sensor->src) v4l2_subdev_init(&ssd->sd, &ccs_ops); @@ -3072,12 +3064,20 @@ static void ccs_init_subdev(struct ccs_sensor *sensor, ssd->sd.entity.ops = &ccs_entity_ops; + rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); + if (rval) { + dev_err(&client->dev, "media_entity_pads_init failed\n"); + return rval; + } + if (ssd == sensor->src) - return; + return 0; ssd->sd.owner = THIS_MODULE; ssd->sd.dev = &client->dev; v4l2_set_subdevdata(&ssd->sd, client); + + return 0; } static int ccs_init_cfg(struct v4l2_subdev *sd, @@ -3589,12 +3589,18 @@ static int ccs_probe(struct i2c_client *client) sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); - ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_init_subdev(sensor, sensor->binner, " binner", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_init_subdev(sensor, sensor->pixel_array, " pixel_array", 1, - MEDIA_ENT_F_CAM_SENSOR); + rval = ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + if (rval) + goto out_cleanup; + rval = ccs_init_subdev(sensor, sensor->binner, " binner", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + if (rval) + goto out_cleanup; + rval = ccs_init_subdev(sensor, sensor->pixel_array, " pixel_array", 1, + MEDIA_ENT_F_CAM_SENSOR); + if (rval) + goto out_cleanup; rval = ccs_init_controls(sensor); if (rval < 0) @@ -3627,14 +3633,9 @@ static int ccs_probe(struct i2c_client *client) sensor->streaming = false; sensor->dev_init_done = true; - rval = media_entity_pads_init(&sensor->src->sd.entity, 2, - sensor->src->pads); - if (rval < 0) - goto out_media_entity_cleanup; - rval = ccs_write_msr_regs(sensor); if (rval) - goto out_media_entity_cleanup; + goto out_cleanup; pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); @@ -3654,9 +3655,6 @@ static int ccs_probe(struct i2c_client *client) pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); -out_media_entity_cleanup: - media_entity_cleanup(&sensor->src->sd.entity); - out_cleanup: ccs_cleanup(sensor); @@ -3689,10 +3687,9 @@ static void ccs_remove(struct i2c_client *client) ccs_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - for (i = 0; i < sensor->ssds_used; i++) { + for (i = 0; i < sensor->ssds_used; i++) v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - media_entity_cleanup(&sensor->ssds[i].sd.entity); - } + ccs_cleanup(sensor); mutex_destroy(&sensor->mutex); kfree(sensor->ccs_limits); From e1176f4ea26dfbeddd9920e7361cf6065744432a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 2 Oct 2023 13:29:22 +0300 Subject: [PATCH 061/159] media: ccs: Obtain media bus formats before initialising up sub-devices The available mbus codes will soon be needed earlier, at the time sub-devices are initialisaed. This is due to calling init_cfg() op via the v4l2_subdev_init_finalize(). Move ccs_get_mbus_formats() before ccs_init_subdev() calls. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit f00a1572b65a444082fb72488ae3b7ce73065696) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index c72995f9facee4..4bf959a5a9d8d6 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3589,6 +3589,12 @@ static int ccs_probe(struct i2c_client *client) sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); + rval = ccs_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + rval = ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, MEDIA_ENT_F_PROC_VIDEO_SCALER); if (rval) @@ -3610,12 +3616,6 @@ static int ccs_probe(struct i2c_client *client) if (rval) goto out_cleanup; - rval = ccs_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_cleanup; - } - rval = ccs_init_late_controls(sensor); if (rval) { rval = -ENODEV; From 55ca4a01f1f30ba5e3f35053ae038e6784e0eea9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Aug 2023 10:31:10 +0300 Subject: [PATCH 062/159] media: ccs: Use sub-device active state Make use of sub-device active state. In most cases the effect on need for acquiring the mutex is non-existent as access to the driver's core data structure still needs to be serialised. This still removes a lot of code as the code paths for active and try state are the same in many cases. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit d8bca3ed1d70652e68b07799171d471026b7b3e2) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 286 +++++++++++-------------------- drivers/media/i2c/ccs/ccs.h | 4 +- 2 files changed, 104 insertions(+), 186 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 4bf959a5a9d8d6..4d997b7d591d9a 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -508,9 +508,8 @@ static void __ccs_update_exposure_limits(struct ccs_sensor *sensor) struct v4l2_ctrl *ctrl = sensor->exposure; int max; - max = sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - + sensor->vblank->val - - CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); + max = sensor->pa_src.height + sensor->vblank->val - + CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); } @@ -728,15 +727,12 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: rval = ccs_write(sensor, FRAME_LENGTH_LINES, - sensor->pixel_array->crop[ - CCS_PA_PAD_SRC].height - + ctrl->val); + sensor->pa_src.height + ctrl->val); break; case V4L2_CID_HBLANK: rval = ccs_write(sensor, LINE_LENGTH_PCK, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - + ctrl->val); + sensor->pa_src.width + ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -1214,15 +1210,13 @@ static void ccs_update_blanking(struct ccs_sensor *sensor) min = max_t(int, CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES), - min_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height); - max = max_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height; + min_fll - sensor->pa_src.height); + max = max_fll - sensor->pa_src.height; __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); - min = max_t(int, - min_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width, - min_lbp); - max = max_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width; + min = max_t(int, min_llp - sensor->pa_src.width, min_lbp); + max = max_llp - sensor->pa_src.width; __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); @@ -1246,10 +1240,8 @@ static int ccs_pll_blanking_update(struct ccs_sensor *sensor) dev_dbg(&client->dev, "real timeperframe\t100/%d\n", sensor->pll.pixel_rate_pixel_array / - ((sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - + sensor->hblank->val) * - (sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - + sensor->vblank->val) / 100)); + ((sensor->pa_src.width + sensor->hblank->val) * + (sensor->pa_src.height + sensor->vblank->val) / 100)); return 0; } @@ -1756,28 +1748,22 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) goto out; /* Analog crop start coordinates */ - rval = ccs_write(sensor, X_ADDR_START, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].left); + rval = ccs_write(sensor, X_ADDR_START, sensor->pa_src.left); if (rval < 0) goto out; - rval = ccs_write(sensor, Y_ADDR_START, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].top); + rval = ccs_write(sensor, Y_ADDR_START, sensor->pa_src.top); if (rval < 0) goto out; /* Analog crop end coordinates */ - rval = ccs_write( - sensor, X_ADDR_END, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].left - + sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - 1); + rval = ccs_write(sensor, X_ADDR_END, + sensor->pa_src.left + sensor->pa_src.width - 1); if (rval < 0) goto out; - rval = ccs_write( - sensor, Y_ADDR_END, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].top - + sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - 1); + rval = ccs_write(sensor, Y_ADDR_END, + sensor->pa_src.top + sensor->pa_src.height - 1); if (rval < 0) goto out; @@ -1789,27 +1775,23 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) /* Digital crop */ if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - rval = ccs_write( - sensor, DIGITAL_CROP_X_OFFSET, - sensor->scaler->crop[CCS_PAD_SINK].left); + rval = ccs_write(sensor, DIGITAL_CROP_X_OFFSET, + sensor->scaler_sink.left); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_Y_OFFSET, - sensor->scaler->crop[CCS_PAD_SINK].top); + rval = ccs_write(sensor, DIGITAL_CROP_Y_OFFSET, + sensor->scaler_sink.top); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler->crop[CCS_PAD_SINK].width); + rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler_sink.width); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler->crop[CCS_PAD_SINK].height); + rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler_sink.height); if (rval < 0) goto out; } @@ -1827,12 +1809,10 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) } /* Output size from sensor */ - rval = ccs_write(sensor, X_OUTPUT_SIZE, - sensor->src->crop[CCS_PAD_SRC].width); + rval = ccs_write(sensor, X_OUTPUT_SIZE, sensor->src_src.width); if (rval < 0) goto out; - rval = ccs_write(sensor, Y_OUTPUT_SIZE, - sensor->src->crop[CCS_PAD_SRC].height); + rval = ccs_write(sensor, Y_OUTPUT_SIZE, sensor->src_src.height); if (rval < 0) goto out; @@ -2053,24 +2033,8 @@ static int __ccs_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct ccs_subdev *ssd = to_ccs_subdev(subdev); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); - } else { - struct v4l2_rect *r; - - if (fmt->pad == ssd->source_pad) - r = &ssd->crop[ssd->source_pad]; - else - r = &ssd->sink_fmt; - - fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); - fmt->format.width = r->width; - fmt->format.height = r->height; - fmt->format.field = V4L2_FIELD_NONE; - } + fmt->format = *v4l2_subdev_get_pad_format(subdev, sd_state, fmt->pad); + fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); return 0; } @@ -2092,28 +2056,18 @@ static int ccs_get_format(struct v4l2_subdev *subdev, static void ccs_get_crop_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_rect **crops, - struct v4l2_rect **comps, int which) + struct v4l2_rect **comps) { struct ccs_subdev *ssd = to_ccs_subdev(subdev); unsigned int i; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = &ssd->crop[i]; - if (comps) - *comps = &ssd->compose; - } else { - if (crops) { - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = v4l2_subdev_get_try_crop(subdev, - sd_state, - i); - } - if (comps) - *comps = v4l2_subdev_get_try_compose(subdev, sd_state, - CCS_PAD_SINK); - } + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = + v4l2_subdev_get_pad_crop(subdev, sd_state, i); + if (comps) + *comps = v4l2_subdev_get_pad_compose(subdev, sd_state, + ssd->sink_pad); } /* Changes require propagation only on sink pad. */ @@ -2125,7 +2079,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); switch (target) { case V4L2_SEL_TGT_CROP: @@ -2136,6 +2090,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN); sensor->scaling_mode = CCS_SCALING_MODE_NO_SCALING; + sensor->scaler_sink = *comp; } else if (ssd == sensor->binner) { sensor->binning_horizontal = 1; sensor->binning_vertical = 1; @@ -2144,6 +2099,8 @@ static void ccs_propagate(struct v4l2_subdev *subdev, fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[CCS_PAD_SRC] = *comp; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) + sensor->src_src = *crops[CCS_PAD_SRC]; break; default: WARN_ON_ONCE(1); @@ -2252,14 +2209,12 @@ static int ccs_set_format(struct v4l2_subdev *subdev, CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE), CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE)); - ccs_get_crop_compose(subdev, sd_state, crops, NULL, fmt->which); + ccs_get_crop_compose(subdev, sd_state, crops, NULL); crops[ssd->sink_pad]->left = 0; crops[ssd->sink_pad]->top = 0; crops[ssd->sink_pad]->width = fmt->format.width; crops[ssd->sink_pad]->height = fmt->format.height; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ssd->sink_fmt = *crops[ssd->sink_pad]; ccs_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -2482,7 +2437,7 @@ static int ccs_set_compose(struct v4l2_subdev *subdev, struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); sel->r.top = 0; sel->r.left = 0; @@ -2501,8 +2456,8 @@ static int ccs_set_compose(struct v4l2_subdev *subdev, return 0; } -static int __ccs_sel_supported(struct v4l2_subdev *subdev, - struct v4l2_subdev_selection *sel) +static int ccs_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); @@ -2545,33 +2500,18 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); - struct v4l2_rect *src_size, *crops[CCS_PADS]; - struct v4l2_rect _r; + struct v4l2_rect src_size = { 0 }, *crops[CCS_PADS], *comp; - ccs_get_crop_compose(subdev, sd_state, crops, NULL, sel->which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (sel->pad == ssd->sink_pad) - src_size = &ssd->sink_fmt; - else - src_size = &ssd->compose; + if (sel->pad == ssd->sink_pad) { + struct v4l2_mbus_framefmt *mfmt = + v4l2_subdev_get_pad_format(subdev, sd_state, sel->pad); + + src_size.width = mfmt->width; + src_size.height = mfmt->height; } else { - if (sel->pad == ssd->sink_pad) { - _r.left = 0; - _r.top = 0; - _r.width = v4l2_subdev_get_try_format(subdev, - sd_state, - sel->pad) - ->width; - _r.height = v4l2_subdev_get_try_format(subdev, - sd_state, - sel->pad) - ->height; - src_size = &_r; - } else { - src_size = v4l2_subdev_get_try_compose( - subdev, sd_state, ssd->sink_pad); - } + src_size = *comp; } if (ssd == sensor->src && sel->pad == CCS_PAD_SRC) { @@ -2579,16 +2519,19 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, sel->r.top = 0; } - sel->r.width = min(sel->r.width, src_size->width); - sel->r.height = min(sel->r.height, src_size->height); + sel->r.width = min(sel->r.width, src_size.width); + sel->r.height = min(sel->r.height, src_size.height); - sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); - sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); + sel->r.left = min_t(int, sel->r.left, src_size.width - sel->r.width); + sel->r.top = min_t(int, sel->r.top, src_size.height - sel->r.height); *crops[sel->pad] = sel->r; if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK) ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP); + else if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + ssd == sensor->pixel_array) + sensor->pa_src = sel->r; return 0; } @@ -2601,44 +2544,36 @@ static void ccs_get_native_size(struct ccs_subdev *ssd, struct v4l2_rect *r) r->height = CCS_LIM(ssd->sensor, Y_ADDR_MAX) + 1; } -static int __ccs_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) +static int ccs_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; - struct v4l2_rect sink_fmt; int ret; - ret = __ccs_sel_supported(subdev, sel); + ret = ccs_sel_supported(subdev, sel); if (ret) return ret; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = ssd->sink_fmt; - } else { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(subdev, sd_state, - ssd->sink_pad); - - sink_fmt.left = 0; - sink_fmt.top = 0; - sink_fmt.width = fmt->width; - sink_fmt.height = fmt->height; - } + ccs_get_crop_compose(subdev, sd_state, crops, &comp); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_NATIVE_SIZE: - if (ssd == sensor->pixel_array) + if (ssd == sensor->pixel_array) { ccs_get_native_size(ssd, &sel->r); - else if (sel->pad == ssd->sink_pad) - sel->r = sink_fmt; - else + } else if (sel->pad == ssd->sink_pad) { + struct v4l2_mbus_framefmt *sink_fmt = + v4l2_subdev_get_pad_format(subdev, sd_state, + ssd->sink_pad); + sel->r.top = sel->r.left = 0; + sel->r.width = sink_fmt->width; + sel->r.height = sink_fmt->height; + } else { sel->r = *comp; + } break; case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -2652,20 +2587,6 @@ static int __ccs_get_selection(struct v4l2_subdev *subdev, return 0; } -static int ccs_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct ccs_sensor *sensor = to_ccs_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __ccs_get_selection(subdev, sd_state, sel); - mutex_unlock(&sensor->mutex); - - return rval; -} - static int ccs_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -2673,7 +2594,7 @@ static int ccs_set_selection(struct v4l2_subdev *subdev, struct ccs_sensor *sensor = to_ccs_sensor(subdev); int ret; - ret = __ccs_sel_supported(subdev, sel); + ret = ccs_sel_supported(subdev, sel); if (ret) return ret; @@ -3020,8 +2941,10 @@ static void ccs_cleanup(struct ccs_sensor *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); unsigned int i; - for (i = 0; i < sensor->ssds_used; i++) + for (i = 0; i < sensor->ssds_used; i++) { + v4l2_subdev_cleanup(&sensor->ssds[2].sd); media_entity_cleanup(&sensor->ssds[i].sd.entity); + } device_remove_file(&client->dev, &dev_attr_nvm); device_remove_file(&client->dev, &dev_attr_ident); @@ -3051,31 +2974,29 @@ static int ccs_init_subdev(struct ccs_sensor *sensor, v4l2_i2c_subdev_set_name(&ssd->sd, client, sensor->minfo.name, name); - ccs_get_native_size(ssd, &ssd->sink_fmt); - - ssd->compose.width = ssd->sink_fmt.width; - ssd->compose.height = ssd->sink_fmt.height; - ssd->crop[ssd->source_pad] = ssd->compose; ssd->pads[ssd->source_pad].flags = MEDIA_PAD_FL_SOURCE; - if (ssd != sensor->pixel_array) { - ssd->crop[ssd->sink_pad] = ssd->compose; + if (ssd != sensor->pixel_array) ssd->pads[ssd->sink_pad].flags = MEDIA_PAD_FL_SINK; - } ssd->sd.entity.ops = &ccs_entity_ops; + if (ssd != sensor->src) { + ssd->sd.owner = THIS_MODULE; + ssd->sd.dev = &client->dev; + v4l2_set_subdevdata(&ssd->sd, client); + } + rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); if (rval) { dev_err(&client->dev, "media_entity_pads_init failed\n"); return rval; } - if (ssd == sensor->src) - return 0; - - ssd->sd.owner = THIS_MODULE; - ssd->sd.dev = &client->dev; - v4l2_set_subdevdata(&ssd->sd, client); + rval = v4l2_subdev_init_finalize(&ssd->sd); + if (rval) { + media_entity_cleanup(&ssd->sd.entity); + return rval; + } return 0; } @@ -3090,24 +3011,24 @@ static int ccs_init_cfg(struct v4l2_subdev *sd, mutex_lock(&sensor->mutex); for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, i); - struct v4l2_rect *try_crop = - v4l2_subdev_get_try_crop(sd, sd_state, i); - struct v4l2_rect *try_comp; + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_pad_format(sd, sd_state, i); + struct v4l2_rect *crop = + v4l2_subdev_get_pad_crop(sd, sd_state, i); + struct v4l2_rect *comp; - ccs_get_native_size(ssd, try_crop); + ccs_get_native_size(ssd, crop); - try_fmt->width = try_crop->width; - try_fmt->height = try_crop->height; - try_fmt->code = sensor->internal_csi_format->code; - try_fmt->field = V4L2_FIELD_NONE; + fmt->width = crop->width; + fmt->height = crop->height; + fmt->code = sensor->internal_csi_format->code; + fmt->field = V4L2_FIELD_NONE; if (ssd == sensor->pixel_array) continue; - try_comp = v4l2_subdev_get_try_compose(sd, sd_state, i); - *try_comp = *try_crop; + comp = v4l2_subdev_get_pad_compose(sd, sd_state, i); + *comp = *crop; } mutex_unlock(&sensor->mutex); @@ -3689,7 +3610,6 @@ static void ccs_remove(struct i2c_client *client) for (i = 0; i < sensor->ssds_used; i++) v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - ccs_cleanup(sensor); mutex_destroy(&sensor->mutex); kfree(sensor->ccs_limits); diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index a94c796cea4871..9c3587b2fbe731 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -182,9 +182,6 @@ struct ccs_binning_subtype { struct ccs_subdev { struct v4l2_subdev sd; struct media_pad pads[CCS_PADS]; - struct v4l2_rect sink_fmt; - struct v4l2_rect crop[CCS_PADS]; - struct v4l2_rect compose; /* compose on sink */ unsigned short sink_pad; unsigned short source_pad; int npads; @@ -220,6 +217,7 @@ struct ccs_sensor { u32 mbus_frame_fmts; const struct ccs_csi_data_format *csi_format; const struct ccs_csi_data_format *internal_csi_format; + struct v4l2_rect pa_src, scaler_sink, src_src; u32 default_mbus_frame_fmts; int default_pixel_order; struct ccs_data_container sdata, mdata; From 90c32251b25732ac33b991a082073cff1aba41ae Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 29 Sep 2023 11:32:46 +0300 Subject: [PATCH 063/159] media: ccs: Drop re-entrant s_stream support The s_stream is called to enable and to disable streaming on a sub-device. The caller may only call it to change the state, enabling streaming is not allowed when it is already disabled, and similarly for disabling streaming. Remove the check from the CCS driver. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 8a695a29d68e8f6e7334ae4fdcffd7e687a48d2f) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 4d997b7d591d9a..6bcfe612eb7868 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1903,9 +1903,6 @@ static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - if (sensor->streaming == enable) - return 0; - if (!enable) { ccs_stop_streaming(sensor); sensor->streaming = false; From dd9cfed9e2a140617968912b3cee27520514f763 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 9 Oct 2023 09:40:11 +0300 Subject: [PATCH 064/159] media: ccs: Rework initialising sub-device state Initialise sub-device state in init_cfg callback using ccs_propagate() to the extent it covers of the initialisation. This fixes a bug where the driver configuration was incorrectly initialised. Fixes: d8bca3ed1d70 ("media: ccs: Use sub-device active state") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 256b7767c97d94c8df46e1bf5bdb89a7f7dcac99) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 45 ++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 6bcfe612eb7868..72341da81a7ac1 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2075,6 +2075,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; + struct v4l2_mbus_framefmt *fmt; ccs_get_crop_compose(subdev, sd_state, crops, &comp); @@ -2096,6 +2097,9 @@ static void ccs_propagate(struct v4l2_subdev *subdev, fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[CCS_PAD_SRC] = *comp; + fmt = v4l2_subdev_get_pad_format(subdev, sd_state, CCS_PAD_SRC); + fmt->width = comp->width; + fmt->height = comp->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) sensor->src_src = *crops[CCS_PAD_SRC]; break; @@ -3003,31 +3007,38 @@ static int ccs_init_cfg(struct v4l2_subdev *sd, { struct ccs_subdev *ssd = to_ccs_subdev(sd); struct ccs_sensor *sensor = ssd->sensor; - unsigned int i; + unsigned int pad = ssd == sensor->pixel_array ? + CCS_PA_PAD_SRC : CCS_PAD_SINK; + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_pad_format(sd, sd_state, pad); + struct v4l2_rect *crop = + v4l2_subdev_get_pad_crop(sd, sd_state, pad); + bool is_active = !sd->active_state || sd->active_state == sd_state; mutex_lock(&sensor->mutex); - for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_pad_format(sd, sd_state, i); - struct v4l2_rect *crop = - v4l2_subdev_get_pad_crop(sd, sd_state, i); - struct v4l2_rect *comp; - - ccs_get_native_size(ssd, crop); + ccs_get_native_size(ssd, crop); - fmt->width = crop->width; - fmt->height = crop->height; - fmt->code = sensor->internal_csi_format->code; - fmt->field = V4L2_FIELD_NONE; + fmt->width = crop->width; + fmt->height = crop->height; + fmt->code = sensor->internal_csi_format->code; + fmt->field = V4L2_FIELD_NONE; - if (ssd == sensor->pixel_array) - continue; + if (ssd == sensor->pixel_array) { + if (is_active) + sensor->pa_src = *crop; - comp = v4l2_subdev_get_pad_compose(sd, sd_state, i); - *comp = *crop; + mutex_unlock(&sensor->mutex); + return 0; } + fmt = v4l2_subdev_get_pad_format(sd, sd_state, CCS_PAD_SRC); + fmt->code = ssd == sensor->src ? + sensor->csi_format->code : sensor->internal_csi_format->code; + fmt->field = V4L2_FIELD_NONE; + + ccs_propagate(sd, sd_state, is_active, V4L2_SEL_TGT_CROP); + mutex_unlock(&sensor->mutex); return 0; From 84819ce1ac4c895c4574fa18bf0be9bfada12d00 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 9 Oct 2023 14:54:57 +0300 Subject: [PATCH 065/159] media: ccs: Fix a (harmless) lockdep warning The v4l2_subdev_init_finalize() is a macro that creates an unique lockdep key and name. As the CCS driver initialises all three of its sub-devices using the same call site, this creates a lockdep warning. Address it. Fixes: d8bca3ed1d70 ("media: ccs: Use sub-device active state") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 827804d6d7608cb797e689eabe6e200fa9fb58e6) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 72341da81a7ac1..f7cc4d36f427c8 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2955,7 +2955,9 @@ static void ccs_cleanup(struct ccs_sensor *sensor) static int ccs_init_subdev(struct ccs_sensor *sensor, struct ccs_subdev *ssd, const char *name, - unsigned short num_pads, u32 function) + unsigned short num_pads, u32 function, + const char *lock_name, + struct lock_class_key *lock_key) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; @@ -2993,7 +2995,7 @@ static int ccs_init_subdev(struct ccs_sensor *sensor, return rval; } - rval = v4l2_subdev_init_finalize(&ssd->sd); + rval = __v4l2_subdev_init_finalize(&ssd->sd, lock_name, lock_key); if (rval) { media_entity_cleanup(&ssd->sd.entity); return rval; @@ -3242,6 +3244,8 @@ static int ccs_firmware_name(struct i2c_client *client, static int ccs_probe(struct i2c_client *client) { + static struct lock_class_key pixel_array_lock_key, binner_lock_key, + scaler_lock_key; const struct ccs_device *ccsdev = device_get_match_data(&client->dev); struct ccs_sensor *sensor; const struct firmware *fw; @@ -3525,15 +3529,18 @@ static int ccs_probe(struct i2c_client *client) } rval = ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); + MEDIA_ENT_F_PROC_VIDEO_SCALER, + "ccs scaler mutex", &scaler_lock_key); if (rval) goto out_cleanup; rval = ccs_init_subdev(sensor, sensor->binner, " binner", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); + MEDIA_ENT_F_PROC_VIDEO_SCALER, + "ccs binner mutex", &binner_lock_key); if (rval) goto out_cleanup; rval = ccs_init_subdev(sensor, sensor->pixel_array, " pixel_array", 1, - MEDIA_ENT_F_CAM_SENSOR); + MEDIA_ENT_F_CAM_SENSOR, "ccs pixel array mutex", + &pixel_array_lock_key); if (rval) goto out_cleanup; From 0fa1979d5b10058434f44ebc6f83c7ea24bada29 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 14 Oct 2023 20:56:00 +0300 Subject: [PATCH 066/159] media: ccs: Ensure control handlers have been set up after probe If the sensor remains powered on after probe when it is needed again and the runtime PM usage_count is incremented, the control handler setup for neither control handler is run. As this is, in most cases, a needless operation in probe, track the status of sensor power handling after probe and set up control handlers even if the device was already active right after probe. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit a274f4d1e5af404fcc7b5554521913590c9d7b08) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ccs/ccs-core.c | 5 ++++- drivers/media/i2c/ccs/ccs.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index f7cc4d36f427c8..af998c4c9d5ce2 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1878,9 +1878,11 @@ static int ccs_pm_get_init(struct ccs_sensor *sensor) goto error; /* Device was already active, so don't set controls */ - if (rval == 1) + if (rval == 1 && !sensor->handler_setup_needed) return 0; + sensor->handler_setup_needed = false; + /* Restore V4L2 controls to the previously suspended device */ rval = v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler); if (rval) @@ -3568,6 +3570,7 @@ static int ccs_probe(struct i2c_client *client) sensor->streaming = false; sensor->dev_init_done = true; + sensor->handler_setup_needed = true; rval = ccs_write_msr_regs(sensor); if (rval) diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 9c3587b2fbe731..2c013d96adcc2b 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -236,6 +236,7 @@ struct ccs_sensor { bool streaming; bool dev_init_done; + bool handler_setup_needed; u8 compressed_min_bpp; struct ccs_module_info minfo; From 04b30de9730ff6af2a07ebacd2271ba828416588 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 9 Oct 2023 18:39:30 +0530 Subject: [PATCH 067/159] media: cadence: csi2rx: Cleanup media entity properly Call media_entity_cleanup() in probe error path and remove to make sure the media entity is cleaned up properly. Suggested-by: Laurent Pinchart Signed-off-by: Pratyush Yadav Tested-by: Julien Massot Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Maxime Ripard Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit aee5b415c95aa913aa9222bbe13c817c7e67ec85) Signed-off-by: Jacopo Mondi --- drivers/media/platform/cadence/cdns-csi2rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 2d803cf31e9d1c..6e59ff486ab7b7 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -549,6 +549,7 @@ static int csi2rx_probe(struct platform_device *pdev) err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); + media_entity_cleanup(&csi2rx->subdev.entity); err_free_priv: kfree(csi2rx); return ret; @@ -561,6 +562,7 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); v4l2_async_unregister_subdev(&csi2rx->subdev); + media_entity_cleanup(&csi2rx->subdev.entity); kfree(csi2rx); } From 53c3aab753260e989bc9127520baf97376b05945 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 9 Oct 2023 18:39:31 +0530 Subject: [PATCH 068/159] media: cadence: csi2rx: Add get_fmt and set_fmt pad ops The format is needed to calculate the link speed for the external DPHY configuration. It is not right to query the format from the source subdev. Add get_fmt and set_fmt pad operations so that the format can be configured and correct bpp be selected. Initialize and use the v4l2 subdev active state to keep track of the active formats. Also propagate the new format from the sink pad to all the source pads. Signed-off-by: Pratyush Yadav Tested-by: Julien Massot Reviewed-by: Maxime Ripard Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit dbca7b3c412b86e2684542467f2565b52e2f0cb3) Signed-off-by: Jacopo Mondi --- drivers/media/platform/cadence/cdns-csi2rx.c | 96 +++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 6e59ff486ab7b7..939d0d2a4c5635 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -61,6 +61,11 @@ enum csi2rx_pads { CSI2RX_PAD_MAX, }; +struct csi2rx_fmt { + u32 code; + u8 bpp; +}; + struct csi2rx_priv { struct device *dev; unsigned int count; @@ -95,6 +100,32 @@ struct csi2rx_priv { int source_pad; }; +static const struct csi2rx_fmt formats[] = { + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, }, +}; + +static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) + if (formats[i].code == code) + return &formats[i]; + + return NULL; +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -305,12 +336,68 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) return ret; } +static int csi2rx_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + unsigned int i; + + /* No transcoding, source and sink formats must match. */ + if (format->pad != CSI2RX_PAD_SINK) + return v4l2_subdev_get_fmt(subdev, state, format); + + if (!csi2rx_get_fmt_by_code(format->format.code)) + format->format.code = formats[0].code; + + format->format.field = V4L2_FIELD_NONE; + + /* Set sink format */ + fmt = v4l2_subdev_get_pad_format(subdev, state, format->pad); + *fmt = format->format; + + /* Propagate to source formats */ + for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { + fmt = v4l2_subdev_get_pad_format(subdev, state, i); + *fmt = format->format; + } + + return 0; +} + +static int csi2rx_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format format = { + .pad = CSI2RX_PAD_SINK, + .format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }, + }; + + return csi2rx_set_fmt(subdev, state, &format); +} + +static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2rx_set_fmt, + .init_cfg = csi2rx_init_cfg, +}; + static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .video = &csi2rx_video_ops, + .pad = &csi2rx_pad_ops, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -534,9 +621,13 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + ret = v4l2_subdev_init_finalize(&csi2rx->subdev); + if (ret) + goto err_cleanup; + ret = v4l2_async_register_subdev(&csi2rx->subdev); if (ret < 0) - goto err_cleanup; + goto err_free_state; dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", @@ -546,6 +637,8 @@ static int csi2rx_probe(struct platform_device *pdev) return 0; +err_free_state: + v4l2_subdev_cleanup(&csi2rx->subdev); err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); @@ -562,6 +655,7 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); v4l2_async_unregister_subdev(&csi2rx->subdev); + v4l2_subdev_cleanup(&csi2rx->subdev); media_entity_cleanup(&csi2rx->subdev.entity); kfree(csi2rx); } From 84830ed019a4d8211f230e0282d6cd238077330a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 26 Jan 2023 03:10:01 +0200 Subject: [PATCH 069/159] media: rkisp1: csi: Use V4L2 subdev active state Use the V4L2 subdev active state API to store the active format and crop rectangle. This simplifies the driver not only by dropping the state stored in the rkisp1_csi structure, but also by replacing the ops_lock with the state lock. Signed-off-by: Laurent Pinchart Reviewed-by: Paul Elder Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit c7e26218820786d23d999ec9b33c812d07f98199) Signed-off-by: Jacopo Mondi --- .../platform/rockchip/rkisp1/rkisp1-common.h | 6 - .../platform/rockchip/rkisp1/rkisp1-csi.c | 107 ++++++------------ 2 files changed, 33 insertions(+), 80 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index e9bc6c155d2fcf..6965a998013b6a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -175,9 +175,6 @@ struct rkisp1_sensor_async { * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt) * @sd: v4l2_subdev variable * @pads: media pads - * @pad_cfg: configurations for the pads - * @sink_fmt: input format - * @lock: protects pad_cfg and sink_fmt * @source: source in-use, set when starting streaming */ struct rkisp1_csi { @@ -186,9 +183,6 @@ struct rkisp1_csi { bool is_dphy_errctrl_disabled; struct v4l2_subdev sd; struct media_pad pads[RKISP1_CSI_PAD_NUM]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_CSI_PAD_NUM]; - const struct rkisp1_mbus_info *sink_fmt; - struct mutex lock; struct v4l2_subdev *source; }; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 1537dccbd2e28f..7320c1c72e688e 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -30,23 +30,6 @@ static inline struct rkisp1_csi *to_rkisp1_csi(struct v4l2_subdev *sd) return container_of(sd, struct rkisp1_csi, sd); } -static struct v4l2_mbus_framefmt * -rkisp1_csi_get_pad_fmt(struct rkisp1_csi *csi, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = csi->pad_cfg - }; - - lockdep_assert_held(&csi->lock); - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&csi->sd, &state, pad); -} - int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, struct rkisp1_sensor_async *s_asd, unsigned int source_pad) @@ -76,7 +59,8 @@ int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, } static int rkisp1_csi_config(struct rkisp1_csi *csi, - const struct rkisp1_sensor_async *sensor) + const struct rkisp1_sensor_async *sensor, + const struct rkisp1_mbus_info *format) { struct rkisp1_device *rkisp1 = csi->rkisp1; unsigned int lanes = sensor->lanes; @@ -98,7 +82,7 @@ static int rkisp1_csi_config(struct rkisp1_csi *csi, /* Configure Data Type and Virtual Channel */ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL, - RKISP1_CIF_MIPI_DATA_SEL_DT(csi->sink_fmt->mipi_dt) | + RKISP1_CIF_MIPI_DATA_SEL_DT(format->mipi_dt) | RKISP1_CIF_MIPI_DATA_SEL_VC(0)); /* Clear MIPI interrupts */ @@ -163,7 +147,8 @@ static void rkisp1_csi_disable(struct rkisp1_csi *csi) } static int rkisp1_csi_start(struct rkisp1_csi *csi, - const struct rkisp1_sensor_async *sensor) + const struct rkisp1_sensor_async *sensor, + const struct rkisp1_mbus_info *format) { struct rkisp1_device *rkisp1 = csi->rkisp1; union phy_configure_opts opts; @@ -171,7 +156,7 @@ static int rkisp1_csi_start(struct rkisp1_csi *csi, s64 pixel_clock; int ret; - ret = rkisp1_csi_config(csi, sensor); + ret = rkisp1_csi_config(csi, sensor, format); if (ret) return ret; @@ -181,7 +166,7 @@ static int rkisp1_csi_start(struct rkisp1_csi *csi, return -EINVAL; } - phy_mipi_dphy_get_default_config(pixel_clock, csi->sink_fmt->bus_width, + phy_mipi_dphy_get_default_config(pixel_clock, format->bus_width, sensor->lanes, cfg); phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY); phy_configure(csi->dphy, &opts); @@ -263,7 +248,6 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - struct rkisp1_csi *csi = to_rkisp1_csi(sd); unsigned int i; int pos = 0; @@ -273,15 +257,10 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index) return -EINVAL; - mutex_lock(&csi->lock); - - sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, - RKISP1_CSI_PAD_SINK, - code->which); + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, + RKISP1_CSI_PAD_SINK); code->code = sink_fmt->code; - mutex_unlock(&csi->lock); - return 0; } @@ -311,9 +290,9 @@ static int rkisp1_csi_init_config(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; @@ -326,36 +305,18 @@ static int rkisp1_csi_init_config(struct v4l2_subdev *sd, return 0; } -static int rkisp1_csi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_csi *csi = to_rkisp1_csi(sd); - - mutex_lock(&csi->lock); - fmt->format = *rkisp1_csi_get_pad_fmt(csi, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&csi->lock); - - return 0; -} - static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct rkisp1_csi *csi = to_rkisp1_csi(sd); const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; /* The format on the source pad always matches the sink pad. */ if (fmt->pad == RKISP1_CSI_PAD_SRC) - return rkisp1_csi_get_fmt(sd, sd_state, fmt); + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&csi->lock); - - sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SINK, - fmt->which); + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); sink_fmt->code = fmt->format.code; @@ -374,16 +335,10 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - csi->sink_fmt = mbus_info; - /* Propagate the format to the source pad. */ - src_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SRC, - fmt->which); + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SRC); *src_fmt = *sink_fmt; - mutex_unlock(&csi->lock); - return 0; } @@ -395,8 +350,11 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_csi *csi = to_rkisp1_csi(sd); struct rkisp1_device *rkisp1 = csi->rkisp1; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct rkisp1_mbus_info *format; struct rkisp1_sensor_async *source_asd; struct v4l2_async_connection *asc; + struct v4l2_subdev_state *sd_state; struct media_pad *source_pad; struct v4l2_subdev *source; int ret; @@ -430,9 +388,12 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY) return -EINVAL; - mutex_lock(&csi->lock); - ret = rkisp1_csi_start(csi, source_asd); - mutex_unlock(&csi->lock); + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + format = rkisp1_mbus_info_get_by_code(sink_fmt->code); + v4l2_subdev_unlock_state(sd_state); + + ret = rkisp1_csi_start(csi, source_asd, format); if (ret) return ret; @@ -462,7 +423,7 @@ static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { .enum_mbus_code = rkisp1_csi_enum_mbus_code, .init_cfg = rkisp1_csi_init_config, - .get_fmt = rkisp1_csi_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_csi_set_fmt, }; @@ -474,13 +435,11 @@ static const struct v4l2_subdev_ops rkisp1_csi_ops = { int rkisp1_csi_register(struct rkisp1_device *rkisp1) { struct rkisp1_csi *csi = &rkisp1->csi; - struct v4l2_subdev_state state = {}; struct media_pad *pads; struct v4l2_subdev *sd; int ret; csi->rkisp1 = rkisp1; - mutex_init(&csi->lock); sd = &csi->sd; v4l2_subdev_init(sd, &rkisp1_csi_ops); @@ -496,26 +455,26 @@ int rkisp1_csi_register(struct rkisp1_device *rkisp1) pads[RKISP1_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; - csi->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_CSI_DEF_FMT); - ret = media_entity_pads_init(&sd->entity, RKISP1_CSI_PAD_NUM, pads); if (ret) - goto error; + goto err_entity_cleanup; - state.pads = csi->pad_cfg; - rkisp1_csi_init_config(sd, &state); + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&csi->rkisp1->v4l2_dev, sd); if (ret) { dev_err(sd->dev, "Failed to register csi receiver subdev\n"); - goto error; + goto err_subdev_cleanup; } return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&csi->lock); csi->rkisp1 = NULL; return ret; } @@ -528,8 +487,8 @@ void rkisp1_csi_unregister(struct rkisp1_device *rkisp1) return; v4l2_device_unregister_subdev(&csi->sd); + v4l2_subdev_cleanup(&csi->sd); media_entity_cleanup(&csi->sd.entity); - mutex_destroy(&csi->lock); } int rkisp1_csi_init(struct rkisp1_device *rkisp1) From 7485213e845078aa7cff2822b046126da16d635a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 26 Jan 2023 03:10:01 +0200 Subject: [PATCH 070/159] media: rkisp1: isp: Use V4L2 subdev active state Use the V4L2 subdev active state API to store the active format and crop rectangle. This simplifies the driver not only by dropping the state stored in the rkisp1_isp structure, but also by replacing the ops_lock with the state lock. The rkisp1_isp.sink_fmt field needs to be kept, as it is accessed from the stats interrupt handler. To simplify the rkisp1_isp_set_sink_fmt() implementation, the field is now set when starting the ISP, instead of when setting the format. Signed-off-by: Laurent Pinchart Reviewed-by: Paul Elder Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 2cce0a369dbdb59d806f9c0c3599ff74a864c0f4) Signed-off-by: Jacopo Mondi --- .../platform/rockchip/rkisp1/rkisp1-common.h | 6 - .../platform/rockchip/rkisp1/rkisp1-isp.c | 261 +++++++----------- 2 files changed, 102 insertions(+), 165 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 6965a998013b6a..1cc723d2e52ee8 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -192,20 +192,14 @@ struct rkisp1_csi { * @sd: v4l2_subdev variable * @rkisp1: pointer to rkisp1_device * @pads: media pads - * @pad_cfg: pads configurations * @sink_fmt: input format - * @src_fmt: output format - * @ops_lock: ops serialization * @frame_sequence: used to synchronize frame_id between video devices. */ struct rkisp1_isp { struct v4l2_subdev sd; struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_ISP_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_mbus_info *sink_fmt; - const struct rkisp1_mbus_info *src_fmt; - struct mutex ops_lock; /* serialize the subdevice ops */ __u32 frame_sequence; }; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 8fc9c1c116f1dc..b897e69a9f469c 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -53,40 +53,6 @@ * +---------------------------------------------------------+ */ -/* ---------------------------------------------------------------------------- - * Helpers - */ - -static struct v4l2_mbus_framefmt * -rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = isp->pad_cfg - }; - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&isp->sd, &state, pad); -} - -static struct v4l2_rect * -rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = isp->pad_cfg - }; - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad); - else - return v4l2_subdev_get_try_crop(&isp->sd, &state, pad); -} - /* ---------------------------------------------------------------------------- * Camera Interface registers configurations */ @@ -96,12 +62,12 @@ rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, * This should only be called when configuring CIF * or at the frame end interrupt */ -static void rkisp1_config_ism(struct rkisp1_isp *isp) +static void rkisp1_config_ism(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = - rkisp1_isp_get_pad_crop(isp, NULL, - RKISP1_ISP_PAD_SOURCE_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; @@ -125,21 +91,26 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp) * configure ISP blocks with input format, size...... */ static int rkisp1_config_isp(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { struct rkisp1_device *rkisp1 = isp->rkisp1; u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0; - const struct rkisp1_mbus_info *sink_fmt = isp->sink_fmt; - const struct rkisp1_mbus_info *src_fmt = isp->src_fmt; + const struct rkisp1_mbus_info *sink_fmt; + const struct rkisp1_mbus_info *src_fmt; + const struct v4l2_mbus_framefmt *src_frm; const struct v4l2_mbus_framefmt *sink_frm; const struct v4l2_rect *sink_crop; - sink_frm = rkisp1_isp_get_pad_fmt(isp, NULL, - RKISP1_ISP_PAD_SINK_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); - sink_crop = rkisp1_isp_get_pad_crop(isp, NULL, - RKISP1_ISP_PAD_SINK_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + sink_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + + sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code); + src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code); if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { acq_mult = 1; @@ -230,14 +201,15 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = rkisp1_isp_get_pad_fmt(isp, NULL, - RKISP1_ISP_PAD_SOURCE_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat, src_frm->quantization, src_frm->ycbcr_enc); } + isp->sink_fmt = sink_fmt; + return 0; } @@ -258,16 +230,17 @@ static void rkisp1_config_path(struct rkisp1_isp *isp, /* Hardware configure Entry */ static int rkisp1_config_cif(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { int ret; - ret = rkisp1_config_isp(isp, mbus_type, mbus_flags); + ret = rkisp1_config_isp(isp, sd_state, mbus_type, mbus_flags); if (ret) return ret; rkisp1_config_path(isp, mbus_type); - rkisp1_config_ism(isp); + rkisp1_config_ism(isp, sd_state); return 0; } @@ -342,9 +315,12 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp) } } -static void rkisp1_isp_start(struct rkisp1_isp *isp) +static void rkisp1_isp_start(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = isp->rkisp1; + const struct v4l2_mbus_framefmt *src_fmt; + const struct rkisp1_mbus_info *src_info; u32 val; rkisp1_config_clk(isp); @@ -356,7 +332,11 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp) RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); - if (isp->src_fmt->pixel_enc != V4L2_PIXEL_ENC_BAYER) + src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); + + if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) rkisp1_params_post_configure(&rkisp1->params); } @@ -450,7 +430,7 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, struct v4l2_rect *sink_crop, *src_crop; /* Video. */ - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -461,14 +441,14 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, + sink_crop = v4l2_subdev_get_pad_crop(sd, sd_state, RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; @@ -477,14 +457,14 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - src_crop = v4l2_subdev_get_try_crop(sd, sd_state, + src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; /* Parameters and statistics. */ - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = 0; sink_fmt->height = 0; @@ -497,8 +477,7 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *sink_info; const struct rkisp1_mbus_info *src_info; @@ -507,12 +486,12 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, const struct v4l2_rect *src_crop; bool set_csc; - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, which); - src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); + sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); /* * Media bus code. The ISP can operate in pass-through mode (Bayer in, @@ -595,26 +574,20 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, */ if (set_csc) format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC; - - /* Store the source format info when setting the active format. */ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - isp->src_fmt = src_info; } static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, unsigned int which) + struct v4l2_rect *r) { struct v4l2_mbus_framefmt *src_fmt; const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, - which); - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); src_crop->left = ALIGN(r->left, 2); src_crop->width = ALIGN(r->width, 2); @@ -625,24 +598,22 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, *r = *src_crop; /* Propagate to out format */ - src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt, which); + src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt); } static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, unsigned int which) + struct v4l2_rect *r) { struct v4l2_rect *sink_crop, *src_crop; const struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->left = ALIGN(r->left, 2); sink_crop->width = ALIGN(r->width, 2); @@ -653,32 +624,28 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, *r = *sink_crop; /* Propagate to out crop */ - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_crop(isp, sd_state, src_crop, which); + src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + rkisp1_isp_set_src_crop(isp, sd_state, src_crop); } static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->code = format->code; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - isp->sink_fmt = mbus_info; sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, @@ -720,23 +687,9 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); - rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop, which); -} - -static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_isp *isp = to_rkisp1_isp(sd); - - mutex_lock(&isp->ops_lock); - fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&isp->ops_lock); - return 0; + sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop); } static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, @@ -745,18 +698,13 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = to_rkisp1_isp(sd); - mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format, - fmt->which); + rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format, - fmt->which); + rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format); else - fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, - fmt->which); + fmt->format = *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad); - mutex_unlock(&isp->ops_lock); return 0; } @@ -764,39 +712,37 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_isp *isp = to_rkisp1_isp(sd); int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && sel->pad != RKISP1_ISP_PAD_SINK_VIDEO) return -EINVAL; - mutex_lock(&isp->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, sel->pad, - sel->which); + fmt = v4l2_subdev_get_pad_format(sd, sd_state, sel->pad); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - sel->which); + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); } break; + case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, sel->pad, - sel->which); + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, sel->pad); break; + default: ret = -EINVAL; + break; } - mutex_unlock(&isp->ops_lock); + return ret; } @@ -812,15 +758,14 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - mutex_lock(&isp->ops_lock); + if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r, sel->which); + rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_crop(isp, sd_state, &sel->r, sel->which); + rkisp1_isp_set_src_crop(isp, sd_state, &sel->r); else ret = -EINVAL; - mutex_unlock(&isp->ops_lock); return ret; } @@ -838,7 +783,7 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, .init_cfg = rkisp1_isp_init_config, - .get_fmt = rkisp1_isp_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_isp_set_fmt, .link_validate = v4l2_subdev_link_validate_default, }; @@ -851,6 +796,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_isp *isp = to_rkisp1_isp(sd); struct rkisp1_device *rkisp1 = isp->rkisp1; + struct v4l2_subdev_state *sd_state; struct media_pad *source_pad; struct media_pad *sink_pad; enum v4l2_mbus_type mbus_type; @@ -895,21 +841,23 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } isp->frame_sequence = -1; - mutex_lock(&isp->ops_lock); - ret = rkisp1_config_cif(isp, mbus_type, mbus_flags); + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + ret = rkisp1_config_cif(isp, sd_state, mbus_type, mbus_flags); if (ret) - goto mutex_unlock; + goto out_unlock; - rkisp1_isp_start(isp); + rkisp1_isp_start(isp, sd_state); ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true); if (ret) { rkisp1_isp_stop(isp); - goto mutex_unlock; + goto out_unlock; } -mutex_unlock: - mutex_unlock(&isp->ops_lock); +out_unlock: + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -947,9 +895,6 @@ static const struct v4l2_subdev_ops rkisp1_isp_ops = { int rkisp1_isp_register(struct rkisp1_device *rkisp1) { - struct v4l2_subdev_state state = { - .pads = rkisp1->isp.pad_cfg - }; struct rkisp1_isp *isp = &rkisp1->isp; struct media_pad *pads = isp->pads; struct v4l2_subdev *sd = &isp->sd; @@ -970,27 +915,26 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE; pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; - isp->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SINK_PAD_FMT); - isp->src_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SRC_PAD_FMT); - - mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) - goto error; + goto err_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_subdev_cleanup; ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd); if (ret) { dev_err(rkisp1->dev, "Failed to register isp subdev\n"); - goto error; + goto err_subdev_cleanup; } - rkisp1_isp_init_config(sd, &state); - return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&isp->ops_lock); isp->sd.v4l2_dev = NULL; return ret; } @@ -1004,7 +948,6 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1) v4l2_device_unregister_subdev(&isp->sd); media_entity_cleanup(&isp->sd.entity); - mutex_destroy(&isp->ops_lock); } /* ---------------------------------------------------------------------------- From 59401140bb87b1472ffcce02f6f8eb0892bb802f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 26 Jan 2023 03:10:01 +0200 Subject: [PATCH 071/159] media: rkisp1: resizer: Use V4L2 subdev active state Use the V4L2 subdev active state API to store the active format and crop rectangle. This simplifies the driver not only by dropping the state stored in the rkisp1_resizer structure, but also by replacing the ops_lock with the state lock. Signed-off-by: Laurent Pinchart Reviewed-by: Paul Elder Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 88f870f6ecc410b4de6d75c1e1b82c3ad38a3a39) Signed-off-by: Jacopo Mondi --- .../platform/rockchip/rkisp1/rkisp1-common.h | 6 - .../platform/rockchip/rkisp1/rkisp1-resizer.c | 184 +++++++----------- 2 files changed, 66 insertions(+), 124 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 1cc723d2e52ee8..a4e272adc1ad06 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -386,10 +386,7 @@ struct rkisp1_params { * @id: id of the resizer, one of RKISP1_SELFPATH, RKISP1_MAINPATH * @rkisp1: pointer to the rkisp1 device * @pads: media pads - * @pad_cfg: configurations for the pads * @config: the set of registers to configure the resizer - * @pixel_enc: pixel encoding of the resizer - * @ops_lock: a lock for the subdev ops */ struct rkisp1_resizer { struct v4l2_subdev sd; @@ -397,10 +394,7 @@ struct rkisp1_resizer { enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_RSZ_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX]; const struct rkisp1_rsz_config *config; - enum v4l2_pixel_encoding pixel_enc; - struct mutex ops_lock; /* serialize the subdevice ops */ }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index eb0aae56d2c7f8..abaa8757d9cfad 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -117,34 +117,6 @@ static inline void rkisp1_rsz_write(struct rkisp1_resizer *rsz, u32 offset, rkisp1_write(rsz->rkisp1, rsz->regs_base + offset, value); } -static struct v4l2_mbus_framefmt * -rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&rsz->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&rsz->sd, &state, pad); -} - -static struct v4l2_rect * -rkisp1_rsz_get_pad_crop(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&rsz->sd, sd_state, pad); - else - return v4l2_subdev_get_try_crop(&rsz->sd, &state, pad); -} - /* ---------------------------------------------------------------------------- * Dual crop hw configs */ @@ -165,17 +137,18 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, } /* configure dual-crop unit */ -static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz) +static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, + struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = rsz->rkisp1; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; u32 dc_ctrl; - sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); + sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); if (sink_crop->width == sink_fmt->width && sink_crop->height == sink_fmt->height && @@ -296,6 +269,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, + struct v4l2_subdev_state *sd_state, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; @@ -303,20 +277,21 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; struct v4l2_rect *sink_crop; - sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SRC, - V4L2_SUBDEV_FORMAT_ACTIVE); - src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); + sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SRC); + sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code); + src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); /* * The resizer only works on yuv formats, * so return if it is bayer format. */ - if (rsz->pixel_enc == V4L2_PIXEL_ENC_BAYER) { + if (!sink_yuv_info) { rkisp1_rsz_disable(rsz, when); return; } @@ -415,7 +390,7 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -433,7 +408,7 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; @@ -444,16 +419,16 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, - which); + sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SRC); + sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); /* for YUV formats, userspace can change the mbus code on the src pad if it is supported */ @@ -473,18 +448,16 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, - unsigned int which) + struct v4l2_rect *r) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - which); + sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); /* Not crop for MP bayer raw data */ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -511,21 +484,20 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, - which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - which); + sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SRC); + sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; else @@ -536,8 +508,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, sink_fmt->code = RKISP1_DEF_FMT; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - rsz->pixel_enc = mbus_info->pixel_enc; sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, @@ -586,21 +556,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, src_fmt->quantization = sink_fmt->quantization; /* Update sink crop */ - rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which); -} - -static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_resizer *rsz = - container_of(sd, struct rkisp1_resizer, sd); - - mutex_lock(&rsz->ops_lock); - fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&rsz->ops_lock); - return 0; + rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop); } static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, @@ -610,15 +566,11 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); - mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) - rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format, - fmt->which); + rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format); else - rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format, - fmt->which); + rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format); - mutex_unlock(&rsz->ops_lock); return 0; } @@ -626,35 +578,32 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_resizer *rsz = - container_of(sd, struct rkisp1_resizer, sd); struct v4l2_mbus_framefmt *mf_sink; int ret = 0; if (sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = rkisp1_rsz_get_pad_fmt(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - sel->which); + mf_sink = v4l2_subdev_get_pad_format(sd, sd_state, + RKISP1_RSZ_PAD_SINK); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; sel->r.left = 0; sel->r.top = 0; break; + case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - sel->which); + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, + RKISP1_RSZ_PAD_SINK); break; + default: ret = -EINVAL; + break; } - mutex_unlock(&rsz->ops_lock); return ret; } @@ -671,9 +620,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - mutex_lock(&rsz->ops_lock); - rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r, sel->which); - mutex_unlock(&rsz->ops_lock); + rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r); return 0; } @@ -687,7 +634,7 @@ static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = { .get_selection = rkisp1_rsz_get_selection, .set_selection = rkisp1_rsz_set_selection, .init_cfg = rkisp1_rsz_init_config, - .get_fmt = rkisp1_rsz_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_rsz_set_fmt, .link_validate = v4l2_subdev_link_validate_default, }; @@ -703,6 +650,7 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) struct rkisp1_device *rkisp1 = rsz->rkisp1; struct rkisp1_capture *other = &rkisp1->capture_devs[rsz->id ^ 1]; enum rkisp1_shadow_regs_when when = RKISP1_SHADOW_REGS_SYNC; + struct v4l2_subdev_state *sd_state; if (!enable) { rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_ASYNC); @@ -713,11 +661,13 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) if (other->is_streaming) when = RKISP1_SHADOW_REGS_ASYNC; - mutex_lock(&rsz->ops_lock); - rkisp1_rsz_config(rsz, when); - rkisp1_dcrop_config(rsz); + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + rkisp1_rsz_config(rsz, sd_state, when); + rkisp1_dcrop_config(rsz, sd_state); + + v4l2_subdev_unlock_state(sd_state); - mutex_unlock(&rsz->ops_lock); return 0; } @@ -736,15 +686,12 @@ static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) return; v4l2_device_unregister_subdev(&rsz->sd); + v4l2_subdev_cleanup(&rsz->sd); media_entity_cleanup(&rsz->sd.entity); - mutex_destroy(&rsz->ops_lock); } static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) { - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; static const char * const dev_names[] = { RKISP1_RSZ_MP_DEV_NAME, RKISP1_RSZ_SP_DEV_NAME @@ -773,25 +720,26 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) pads[RKISP1_RSZ_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; - rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC; - - mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) - goto error; + goto err_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&rsz->rkisp1->v4l2_dev, sd); if (ret) { dev_err(sd->dev, "Failed to register resizer subdev\n"); - goto error; + goto err_subdev_cleanup; } - rkisp1_rsz_init_config(sd, &state); return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&rsz->ops_lock); return ret; } From 4be8afc85f679be43948eee30ed3a5b5df22d91b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Sep 2023 22:49:06 +0300 Subject: [PATCH 072/159] media: rkisp1: resizer: Constify argument and local variables Pointers to v4l2_mbus_framefmt and v4l2_rect instances don't need to be modified when configuring the resizer. Make them const. Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 1a6ae627af1477e7dbc081c21102bb5d8889adb1) Signed-off-by: Jacopo Mondi --- .../media/platform/rockchip/rkisp1/rkisp1-resizer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index abaa8757d9cfad..8cbd67b55e326e 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -209,10 +209,10 @@ static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, - struct v4l2_rect *sink_y, - struct v4l2_rect *sink_c, - struct v4l2_rect *src_y, - struct v4l2_rect *src_c, + const struct v4l2_rect *sink_y, + const struct v4l2_rect *sink_c, + const struct v4l2_rect *src_y, + const struct v4l2_rect *src_c, enum rkisp1_shadow_regs_when when) { u32 ratio, rsz_ctrl = 0; @@ -273,9 +273,9 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; + const struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; + const struct v4l2_rect *sink_crop; struct v4l2_rect sink_y, sink_c, src_y, src_c; - struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - struct v4l2_rect *sink_crop; sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, RKISP1_RSZ_PAD_SINK); From 724ff50d49a470f536780746be415399f4e7417a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Sep 2023 22:49:06 +0300 Subject: [PATCH 073/159] media: rkisp1: resizer: Use v4l2_area instead of v4l2_rect to store size The rkisp1_rsz_config() and rkisp1_rsz_config_regs() functions use a v4l2_rect to pass frame sizes, leaving the top and left members unused and uninitialized. Use v4l2_area instead. Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 603fbdc1122565dd5bb3fc0b3f574afe5ac49cd9) Signed-off-by: Jacopo Mondi --- drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 8cbd67b55e326e..5111d645aa2695 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -211,8 +211,8 @@ static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz, static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, const struct v4l2_rect *sink_y, const struct v4l2_rect *sink_c, - const struct v4l2_rect *src_y, - const struct v4l2_rect *src_c, + const struct v4l2_area *src_y, + const struct v4l2_area *src_c, enum rkisp1_shadow_regs_when when) { u32 ratio, rsz_ctrl = 0; @@ -275,7 +275,8 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; const struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; const struct v4l2_rect *sink_crop; - struct v4l2_rect sink_y, sink_c, src_y, src_c; + struct v4l2_rect sink_y, sink_c; + struct v4l2_area src_y, src_c; sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, RKISP1_RSZ_PAD_SINK); From 3afbf4e6510faec7718d2e14611252ee2aa4b6b5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Sep 2023 22:49:06 +0300 Subject: [PATCH 074/159] media: rkisp1: resizer: Drop unneeded local variable The sink_y local variable in rkisp1_rsz_config() stores a copy of the sink_crop crop rectangle. Drop it, and rename sink_crop to sink_y. Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 1126d89f8f54a13e7c5cbee24c414603ee794f10) Signed-off-by: Jacopo Mondi --- .../platform/rockchip/rkisp1/rkisp1-resizer.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 5111d645aa2695..328741bb7da0fe 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -274,14 +274,12 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; const struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - const struct v4l2_rect *sink_crop; - struct v4l2_rect sink_y, sink_c; + const struct v4l2_rect *sink_y; struct v4l2_area src_y, src_c; + struct v4l2_rect sink_c; sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, RKISP1_RSZ_PAD_SINK); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, RKISP1_RSZ_PAD_SRC); @@ -297,14 +295,14 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, return; } - sink_y.width = sink_crop->width; - sink_y.height = sink_crop->height; + sink_y = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, + RKISP1_RSZ_PAD_SINK); + sink_c.width = sink_y->width / sink_yuv_info->hdiv; + sink_c.height = sink_y->height / sink_yuv_info->vdiv; + src_y.width = src_fmt->width; src_y.height = src_fmt->height; - sink_c.width = sink_y.width / sink_yuv_info->hdiv; - sink_c.height = sink_y.height / sink_yuv_info->vdiv; - /* * The resizer is used not only to change the dimensions of the frame * but also to change the scale for YUV formats, @@ -320,13 +318,13 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, } dev_dbg(rsz->rkisp1->dev, "stream %d rsz/scale: %dx%d -> %dx%d\n", - rsz->id, sink_crop->width, sink_crop->height, + rsz->id, sink_y->width, sink_y->height, src_fmt->width, src_fmt->height); dev_dbg(rsz->rkisp1->dev, "chroma scaling %dx%d -> %dx%d\n", sink_c.width, sink_c.height, src_c.width, src_c.height); /* set values in the hw */ - rkisp1_rsz_config_regs(rsz, &sink_y, &sink_c, &src_y, &src_c, when); + rkisp1_rsz_config_regs(rsz, sink_y, &sink_c, &src_y, &src_c, when); } /* ---------------------------------------------------------------------------- From 24247a3fd25e61db7a8ac9dbd32a6bf4da18c3f5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Oct 2023 09:37:10 +0200 Subject: [PATCH 075/159] media: v4l: subdev: Switch to stream-aware state functions Switch all drivers accessing sub-device state to use the stream-aware functions. We will soon remove the old ones. This patch has been generated using the following Coccinelle script: ---------8<------------ @@ expression E1, E2, E3; @@ - v4l2_subdev_get_pad_format(E1, E2, E3) + v4l2_subdev_state_get_format(E2, E3) @@ expression E1, E2, E3; @@ - v4l2_subdev_get_pad_crop(E1, E2, E3) + v4l2_subdev_state_get_crop(E2, E3) @@ expression E1, E2, E3; @@ - v4l2_subdev_get_pad_compose(E1, E2, E3) + v4l2_subdev_state_get_compose(E2, E3) @@ expression E1, E2, E3; @@ - v4l2_subdev_get_try_format(E1, E2, E3) + v4l2_subdev_state_get_format(E2, E3) @@ expression E1, E2, E3; @@ - v4l2_subdev_get_try_crop(E1, E2, E3) + v4l2_subdev_state_get_crop(E2, E3) @@ expression E1, E2, E3; @@ - v4l2_subdev_get_try_compose(E1, E2, E3) + v4l2_subdev_state_get_compose(E2, E3) ---------8<------------ Additionally drivers/media/i2c/s5k5baf.c and drivers/media/platform/samsung/s3c-camif/camif-capture.c have been manually changed as Coccinelle didn't. Further local variables have been removed as they became unused as a result of the other changes. Also Coccinelle introduced indentation by space in files drivers/media/i2c/st-mipid02.c and drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c. This has been also corrected. The diff from Coccinelle-generated changes are: > diff --git b/drivers/media/i2c/imx319.c a/drivers/media/i2c/imx319.c > index e549692ff478..420984382173 100644 > --- b/drivers/media/i2c/imx319.c > +++ a/drivers/media/i2c/imx319.c > @@ -2001,7 +2001,6 @@ static int imx319_do_get_pad_format(struct imx319 *imx319, > struct v4l2_subdev_format *fmt) > { > struct v4l2_mbus_framefmt *framefmt; > - struct v4l2_subdev *sd = &imx319->sd; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > diff --git b/drivers/media/i2c/imx355.c a/drivers/media/i2c/imx355.c > index 96bdde685d65..e1b1d2fc79dd 100644 > --- b/drivers/media/i2c/imx355.c > +++ a/drivers/media/i2c/imx355.c > @@ -1299,7 +1299,6 @@ static int imx355_do_get_pad_format(struct imx355 *imx355, > struct v4l2_subdev_format *fmt) > { > struct v4l2_mbus_framefmt *framefmt; > - struct v4l2_subdev *sd = &imx355->sd; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > diff --git b/drivers/media/i2c/ov08x40.c a/drivers/media/i2c/ov08x40.c > index ca799bbcfdb7..abbb0b774d43 100644 > --- b/drivers/media/i2c/ov08x40.c > +++ a/drivers/media/i2c/ov08x40.c > @@ -2774,7 +2774,6 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, > struct v4l2_subdev_format *fmt) > { > struct v4l2_mbus_framefmt *framefmt; > - struct v4l2_subdev *sd = &ov08x->sd; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > diff --git b/drivers/media/i2c/ov13858.c a/drivers/media/i2c/ov13858.c > index 7816d9787c61..09387e335d80 100644 > --- b/drivers/media/i2c/ov13858.c > +++ a/drivers/media/i2c/ov13858.c > @@ -1316,7 +1316,6 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858, > struct v4l2_subdev_format *fmt) > { > struct v4l2_mbus_framefmt *framefmt; > - struct v4l2_subdev *sd = &ov13858->sd; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > diff --git b/drivers/media/i2c/ov13b10.c a/drivers/media/i2c/ov13b10.c > index 268cd4b03f9c..c06411d5ee2b 100644 > --- b/drivers/media/i2c/ov13b10.c > +++ a/drivers/media/i2c/ov13b10.c > @@ -1001,7 +1001,6 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b, > struct v4l2_subdev_format *fmt) > { > struct v4l2_mbus_framefmt *framefmt; > - struct v4l2_subdev *sd = &ov13b->sd; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > diff --git b/drivers/media/i2c/s5c73m3/s5c73m3-core.c a/drivers/media/i2c/s5c73m3/s5c73m3-core.c > index 47605e36bc60..8f9b5713daf7 100644 > --- b/drivers/media/i2c/s5c73m3/s5c73m3-core.c > +++ a/drivers/media/i2c/s5c73m3/s5c73m3-core.c > @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, > struct v4l2_subdev_format *fmt, > const struct s5c73m3_frame_size **fs) > { > - struct v4l2_subdev *sd = &state->sensor_sd; > u32 code; > > switch (fmt->pad) { > diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c > index 67da2045f543..03ccfb0e1e11 100644 > --- a/drivers/media/i2c/s5k5baf.c > +++ b/drivers/media/i2c/s5k5baf.c > @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd, > > if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { > rects = (struct v4l2_rect * []) { > - &s5k5baf_cis_rect, > - v4l2_subdev_get_try_crop(sd, sd_state, > - PAD_CIS), > - v4l2_subdev_get_try_compose(sd, sd_state, > - PAD_CIS), > - v4l2_subdev_get_try_crop(sd, sd_state, > - PAD_OUT) > - }; > + &s5k5baf_cis_rect, > + v4l2_subdev_state_get_crop(sd_state, PAD_CIS), > + v4l2_subdev_state_get_compose(sd_state, PAD_CIS), > + v4l2_subdev_state_get_crop(sd_state, PAD_OUT) > + }; > s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); > return 0; > } > diff --git b/drivers/media/platform/samsung/s3c-camif/camif-capture.c a/drivers/media/platform/samsung/s3c-camif/camif-capture.c > index 295e083f38e8..be58260ea67e 100644 > --- b/drivers/media/platform/samsung/s3c-camif/camif-capture.c > +++ a/drivers/media/platform/samsung/s3c-camif/camif-capture.c > @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, > struct v4l2_mbus_framefmt *mf = &fmt->format; > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); > + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); > fmt->format = *mf; > return 0; > } > @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, > __camif_subdev_try_format(camif, mf, fmt->pad); > > if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); > + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); > *mf = fmt->format; > mutex_unlock(&camif->lock); > return 0; > diff --git b/drivers/media/platform/ti/cal/cal-camerarx.c a/drivers/media/platform/ti/cal/cal-camerarx.c > index cea454ed9c20..61433744c6c4 100644 > --- b/drivers/media/platform/ti/cal/cal-camerarx.c > +++ a/drivers/media/platform/ti/cal/cal-camerarx.c > @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, > struct v4l2_subdev_state *state, > struct v4l2_subdev_mbus_code_enum *code) > { > - struct cal_camerarx *phy = to_cal_camerarx(sd); > - > /* No transcoding, source and sink codes must match. */ > if (cal_rx_pad_is_source(code->pad)) { > struct v4l2_mbus_framefmt *fmt; > diff --git b/drivers/staging/media/imx/imx-ic-prp.c a/drivers/staging/media/imx/imx-ic-prp.c > index dd558fac6477..61d69f19657e 100644 > --- b/drivers/staging/media/imx/imx-ic-prp.c > +++ a/drivers/staging/media/imx/imx-ic-prp.c > @@ -82,8 +82,6 @@ static struct v4l2_mbus_framefmt * > __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, > unsigned int pad, enum v4l2_subdev_format_whence which) > { > - struct imx_ic_priv *ic_priv = priv->ic_priv; > - > if (which == V4L2_SUBDEV_FORMAT_TRY) > return v4l2_subdev_state_get_format(sd_state, pad); > else > diff --git b/drivers/staging/media/imx/imx-ic-prpencvf.c a/drivers/staging/media/imx/imx-ic-prpencvf.c > index 02db7dbb884b..ec73c901079e 100644 > --- b/drivers/staging/media/imx/imx-ic-prpencvf.c > +++ a/drivers/staging/media/imx/imx-ic-prpencvf.c > @@ -790,8 +790,6 @@ static struct v4l2_mbus_framefmt * > __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, > unsigned int pad, enum v4l2_subdev_format_whence which) > { > - struct imx_ic_priv *ic_priv = priv->ic_priv; > - > if (which == V4L2_SUBDEV_FORMAT_TRY) > return v4l2_subdev_state_get_format(sd_state, pad); > else > diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c > index 9c9361354c00..b08a249b5fdd 100644 > --- a/drivers/media/i2c/st-mipid02.c > +++ b/drivers/media/i2c/st-mipid02.c > @@ -751,7 +751,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, > format->format = bridge->fmt; > else > format->format = *v4l2_subdev_state_get_format(sd_state, > - MIPID02_SINK_0); > + MIPID02_SINK_0); > > /* but code may need to be converted */ > format->format.code = serial_to_parallel_code(format->format.code); > diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c > index 117912d3bfbd..96353648c032 100644 > --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c > +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c > @@ -319,7 +319,7 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp, > rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); > > src_fmt = v4l2_subdev_state_get_format(sd_state, > - RKISP1_ISP_PAD_SOURCE_VIDEO); > + RKISP1_ISP_PAD_SOURCE_VIDEO); > src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); > > if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) > @@ -475,9 +475,9 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, > sink_fmt = v4l2_subdev_state_get_format(sd_state, > RKISP1_ISP_PAD_SINK_VIDEO); > src_fmt = v4l2_subdev_state_get_format(sd_state, > - RKISP1_ISP_PAD_SOURCE_VIDEO); > + RKISP1_ISP_PAD_SOURCE_VIDEO); > src_crop = v4l2_subdev_state_get_crop(sd_state, > - RKISP1_ISP_PAD_SOURCE_VIDEO); > + RKISP1_ISP_PAD_SOURCE_VIDEO); > > /* > * Media bus code. The ISP can operate in pass-through mode (Bayer in, Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 5bae63ec71ef2c261c2bba906f2f7eb55e389969) Signed-off-by: Jacopo Mondi --- Conflicts: drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c Patch not applied on the above entries Signed-off-by: Jacopo Mondi --- drivers/media/i2c/adv7180.c | 4 +- drivers/media/i2c/adv7183.c | 2 +- drivers/media/i2c/adv748x/adv748x-afe.c | 6 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +- drivers/media/i2c/adv748x/adv748x-hdmi.c | 6 +- drivers/media/i2c/adv7511-v4l2.c | 4 +- drivers/media/i2c/adv7604.c | 4 +- drivers/media/i2c/adv7842.c | 4 +- drivers/media/i2c/ar0521.c | 5 +- drivers/media/i2c/arducam-pivariety.c | 4 +- drivers/media/i2c/arducam_64mp.c | 20 ++-- drivers/media/i2c/ccs/ccs-core.c | 22 ++-- drivers/media/i2c/et8ek8/et8ek8_driver.c | 3 +- drivers/media/i2c/hi556.c | 13 +-- drivers/media/i2c/hi846.c | 11 +- drivers/media/i2c/hi847.c | 9 +- drivers/media/i2c/imx208.c | 9 +- drivers/media/i2c/imx214.c | 4 +- drivers/media/i2c/imx258.c | 13 +-- drivers/media/i2c/imx274.c | 12 +- drivers/media/i2c/imx290.c | 8 +- drivers/media/i2c/imx296.c | 18 +-- drivers/media/i2c/imx319.c | 7 +- drivers/media/i2c/imx334.c | 4 +- drivers/media/i2c/imx335.c | 4 +- drivers/media/i2c/imx355.c | 7 +- drivers/media/i2c/imx412.c | 4 +- drivers/media/i2c/imx415.c | 8 +- drivers/media/i2c/imx477.c | 19 ++-- drivers/media/i2c/imx500.c | 14 +-- drivers/media/i2c/imx519.c | 19 ++-- drivers/media/i2c/imx708.c | 19 ++-- drivers/media/i2c/irs1125.c | 2 +- drivers/media/i2c/isl7998x.c | 6 +- drivers/media/i2c/max9286.c | 4 +- drivers/media/i2c/mt9m001.c | 6 +- drivers/media/i2c/mt9m111.c | 6 +- drivers/media/i2c/mt9p031.c | 6 +- drivers/media/i2c/mt9t112.c | 2 +- drivers/media/i2c/mt9v011.c | 2 +- drivers/media/i2c/mt9v032.c | 10 +- drivers/media/i2c/mt9v111.c | 4 +- drivers/media/i2c/og01a1b.c | 10 +- drivers/media/i2c/ov01a10.c | 2 +- drivers/media/i2c/ov02a10.c | 6 +- drivers/media/i2c/ov08d10.c | 9 +- drivers/media/i2c/ov08x40.c | 7 +- drivers/media/i2c/ov13858.c | 10 +- drivers/media/i2c/ov13b10.c | 10 +- drivers/media/i2c/ov2311.c | 8 +- drivers/media/i2c/ov2640.c | 6 +- drivers/media/i2c/ov2659.c | 6 +- drivers/media/i2c/ov2680.c | 10 +- drivers/media/i2c/ov2685.c | 4 +- drivers/media/i2c/ov4689.c | 2 +- drivers/media/i2c/ov5640.c | 9 +- drivers/media/i2c/ov5645.c | 4 +- drivers/media/i2c/ov5647.c | 12 +- drivers/media/i2c/ov5648.c | 6 +- drivers/media/i2c/ov5670.c | 13 +-- drivers/media/i2c/ov5675.c | 9 +- drivers/media/i2c/ov5693.c | 4 +- drivers/media/i2c/ov5695.c | 8 +- drivers/media/i2c/ov64a40.c | 10 +- drivers/media/i2c/ov6650.c | 12 +- drivers/media/i2c/ov7251.c | 4 +- drivers/media/i2c/ov7670.c | 7 +- drivers/media/i2c/ov772x.c | 2 +- drivers/media/i2c/ov7740.c | 7 +- drivers/media/i2c/ov8856.c | 9 +- drivers/media/i2c/ov8858.c | 6 +- drivers/media/i2c/ov8865.c | 8 +- drivers/media/i2c/ov9282.c | 6 +- drivers/media/i2c/ov9640.c | 2 +- drivers/media/i2c/ov9650.c | 7 +- drivers/media/i2c/ov9734.c | 9 +- drivers/media/i2c/rj54n1cb0c.c | 2 +- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 37 +++---- drivers/media/i2c/s5k5baf.c | 35 +++--- drivers/media/i2c/s5k6a3.c | 8 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/st-mipid02.c | 11 +- drivers/media/i2c/st-vgxy61.c | 5 +- drivers/media/i2c/tc358746.c | 12 +- drivers/media/i2c/tda1997x.c | 6 +- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/i2c/tw9910.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 10 +- drivers/media/pci/intel/ivsc/mei_csi.c | 4 +- drivers/media/platform/atmel/atmel-isi.c | 2 +- drivers/media/platform/cadence/cdns-csi2rx.c | 4 +- drivers/media/platform/cadence/cdns-csi2tx.c | 3 +- .../platform/microchip/microchip-csi2dc.c | 15 ++- .../platform/microchip/microchip-isc-base.c | 2 +- .../platform/microchip/microchip-isc-scaler.c | 16 +-- drivers/media/platform/nxp/imx-mipi-csis.c | 10 +- drivers/media/platform/nxp/imx7-media-csi.c | 16 +-- .../platform/nxp/imx8-isi/imx8-isi-pipe.c | 18 +-- .../platform/nxp/imx8-isi/imx8-isi-video.c | 2 +- drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 13 ++- .../media/platform/qcom/camss/camss-csid.c | 3 +- .../media/platform/qcom/camss/camss-csiphy.c | 3 +- .../media/platform/qcom/camss/camss-ispif.c | 3 +- drivers/media/platform/qcom/camss/camss-vfe.c | 10 +- drivers/media/platform/renesas/rcar-isp.c | 4 +- .../platform/renesas/rcar-vin/rcar-csi2.c | 4 +- .../platform/renesas/rzg2l-cru/rzg2l-csi2.c | 6 +- .../platform/renesas/rzg2l-cru/rzg2l-ip.c | 6 +- .../media/platform/renesas/vsp1/vsp1_brx.c | 2 +- .../media/platform/renesas/vsp1/vsp1_entity.c | 8 +- .../media/platform/renesas/vsp1/vsp1_rwpf.c | 3 +- .../platform/rockchip/rkisp1/rkisp1-csi.c | 16 ++- .../platform/rockchip/rkisp1/rkisp1-isp.c | 103 +++++++++--------- .../platform/rockchip/rkisp1/rkisp1-resizer.c | 53 ++++----- .../samsung/exynos4-is/fimc-capture.c | 12 +- .../platform/samsung/exynos4-is/fimc-isp.c | 24 ++-- .../platform/samsung/exynos4-is/fimc-lite.c | 16 ++- .../platform/samsung/exynos4-is/mipi-csis.c | 3 +- .../samsung/s3c-camif/camif-capture.c | 8 +- .../platform/sunxi/sun4i-csi/sun4i_v4l2.c | 8 +- .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 8 +- .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c | 8 +- .../sun8i_a83t_mipi_csi2.c | 8 +- drivers/media/platform/ti/cal/cal-camerarx.c | 18 ++- drivers/media/platform/ti/cal/cal-video.c | 2 +- drivers/media/platform/ti/omap3isp/ispccdc.c | 7 +- drivers/media/platform/ti/omap3isp/ispccp2.c | 3 +- drivers/media/platform/ti/omap3isp/ispcsi2.c | 3 +- .../media/platform/ti/omap3isp/isppreview.c | 6 +- .../media/platform/ti/omap3isp/ispresizer.c | 5 +- drivers/media/platform/video-mux.c | 12 +- .../media/platform/xilinx/xilinx-csi2rxss.c | 5 +- drivers/media/platform/xilinx/xilinx-tpg.c | 9 +- drivers/media/platform/xilinx/xilinx-vip.c | 4 +- .../media/test-drivers/vimc/vimc-debayer.c | 10 +- drivers/media/test-drivers/vimc/vimc-scaler.c | 9 +- drivers/media/test-drivers/vimc/vimc-sensor.c | 6 +- .../media/atomisp/i2c/atomisp-gc0310.c | 2 +- .../media/atomisp/i2c/atomisp-gc2235.c | 2 +- .../media/atomisp/i2c/atomisp-mt9m114.c | 2 +- .../media/atomisp/i2c/atomisp-ov2722.c | 2 +- .../staging/media/atomisp/pci/atomisp_csi2.c | 3 +- .../media/atomisp/pci/atomisp_subdev.c | 6 +- .../staging/media/atomisp/pci/atomisp_tpg.c | 2 +- .../media/deprecated/atmel/atmel-isc-base.c | 3 +- drivers/staging/media/imx/imx-ic-prp.c | 4 +- drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- drivers/staging/media/imx/imx-media-csi.c | 8 +- drivers/staging/media/imx/imx-media-utils.c | 2 +- drivers/staging/media/imx/imx-media-vdic.c | 2 +- drivers/staging/media/imx/imx6-mipi-csi2.c | 2 +- drivers/staging/media/ipu3/ipu3-v4l2.c | 14 +-- drivers/staging/media/omap4iss/iss_csi2.c | 3 +- drivers/staging/media/omap4iss/iss_ipipe.c | 3 +- drivers/staging/media/omap4iss/iss_ipipeif.c | 3 +- drivers/staging/media/omap4iss/iss_resizer.c | 3 +- .../media/sunxi/sun6i-isp/sun6i_isp_proc.c | 8 +- drivers/staging/media/tegra-video/vi.c | 2 +- 158 files changed, 608 insertions(+), 707 deletions(-) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 413021887f9615..66dd94773c4e9c 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -826,7 +826,7 @@ static int adv7180_get_pad_format(struct v4l2_subdev *sd, struct adv7180_state *state = to_state(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); } else { adv7180_mbus_fmt(sd, &format->format); format->format.field = state->field; @@ -867,7 +867,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, I2P_IDX : INTERLACED_IDX); } } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 0754bfefa073ae..bcb99ba9a27201 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -443,7 +443,7 @@ static int adv7183_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; else - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 00095c7762c24d..50d9fbadbe3822 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -354,8 +354,8 @@ static int adv748x_afe_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_afe_fill_format(afe, &sdformat->format); @@ -378,7 +378,7 @@ static int adv748x_afe_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_afe_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index a5a7cb228896b0..5b265b722394a2 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -147,7 +147,7 @@ adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &tx->format; } diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 400d71c2745cde..ec151dc69c239f 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -441,8 +441,8 @@ static int adv748x_hdmi_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_hdmi_fill_format(hdmi, &sdformat->format); @@ -464,7 +464,7 @@ static int adv748x_hdmi_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_hdmi_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index a9183d9282fd24..0f780eb6ef6335 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1238,7 +1238,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; @@ -1293,7 +1293,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d1609bd8f0485a..f12e758c4ea022 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1929,7 +1929,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1978,7 +1978,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index c1664a3620c8ed..2ad0f9f5503db8 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2087,7 +2087,7 @@ static int adv7842_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -2119,7 +2119,7 @@ static int adv7842_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 60ab433cafb114..a2ad22807d9dec 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -448,8 +448,7 @@ static int ar0521_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, 0 - /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); else fmt = &sensor->fmt; @@ -474,7 +473,7 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0 /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; mutex_unlock(&sensor->lock); diff --git a/drivers/media/i2c/arducam-pivariety.c b/drivers/media/i2c/arducam-pivariety.c index 6bb9e9c48e5c2c..f64d4d46ad6bcb 100644 --- a/drivers/media/i2c/arducam-pivariety.c +++ b/drivers/media/i2c/arducam-pivariety.c @@ -394,7 +394,7 @@ static int pivariety_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct pivariety *pivariety = to_pivariety(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); struct arducam_format *def_fmt = &pivariety->supported_formats[0]; /* Initialize try_fmt */ @@ -771,7 +771,7 @@ __pivariety_get_pad_crop(struct pivariety *pivariety, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&pivariety->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: ret = pivariety_read_sel(pivariety, &pivariety->crop); if (ret) diff --git a/drivers/media/i2c/arducam_64mp.c b/drivers/media/i2c/arducam_64mp.c index 0d6a2c463ee4ff..2a38df8bd35e00 100644 --- a/drivers/media/i2c/arducam_64mp.c +++ b/drivers/media/i2c/arducam_64mp.c @@ -1555,9 +1555,9 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, METADATA_PAD); struct v4l2_rect *try_crop; mutex_lock(&arducam_64mp->mutex); @@ -1575,7 +1575,7 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_meta->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); try_crop->left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT; try_crop->top = ARDUCAM_64MP_PIXEL_ARRAY_TOP; try_crop->width = ARDUCAM_64MP_PIXEL_ARRAY_WIDTH; @@ -1825,8 +1825,7 @@ static int arducam_64mp_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&arducam_64mp->sd, sd_state, - fmt->pad); + v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip: */ try_fmt->code = fmt->pad == IMAGE_PAD ? arducam_64mp_get_format_code(arducam_64mp) : @@ -1925,8 +1924,8 @@ static int arducam_64mp_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); arducam_64mp_update_image_pad_format(arducam_64mp, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { arducam_64mp->mode = mode; @@ -1935,8 +1934,8 @@ static int arducam_64mp_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -1957,8 +1956,7 @@ __arducam_64mp_get_pad_crop(struct arducam_64mp *arducam_64mp, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&arducam_64mp->sd, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &arducam_64mp->mode->crop; } diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index af998c4c9d5ce2..a2c3c572a0a44d 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2032,7 +2032,7 @@ static int __ccs_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - fmt->format = *v4l2_subdev_get_pad_format(subdev, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); return 0; @@ -2063,10 +2063,10 @@ static void ccs_get_crop_compose(struct v4l2_subdev *subdev, if (crops) for (i = 0; i < subdev->entity.num_pads; i++) crops[i] = - v4l2_subdev_get_pad_crop(subdev, sd_state, i); + v4l2_subdev_state_get_crop(sd_state, i); if (comps) - *comps = v4l2_subdev_get_pad_compose(subdev, sd_state, - ssd->sink_pad); + *comps = v4l2_subdev_state_get_compose(sd_state, + ssd->sink_pad); } /* Changes require propagation only on sink pad. */ @@ -2099,7 +2099,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[CCS_PAD_SRC] = *comp; - fmt = v4l2_subdev_get_pad_format(subdev, sd_state, CCS_PAD_SRC); + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); fmt->width = comp->width; fmt->height = comp->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) @@ -2509,7 +2509,7 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, if (sel->pad == ssd->sink_pad) { struct v4l2_mbus_framefmt *mfmt = - v4l2_subdev_get_pad_format(subdev, sd_state, sel->pad); + v4l2_subdev_state_get_format(sd_state, sel->pad); src_size.width = mfmt->width; src_size.height = mfmt->height; @@ -2569,8 +2569,8 @@ static int ccs_get_selection(struct v4l2_subdev *subdev, ccs_get_native_size(ssd, &sel->r); } else if (sel->pad == ssd->sink_pad) { struct v4l2_mbus_framefmt *sink_fmt = - v4l2_subdev_get_pad_format(subdev, sd_state, - ssd->sink_pad); + v4l2_subdev_state_get_format(sd_state, + ssd->sink_pad); sel->r.top = sel->r.left = 0; sel->r.width = sink_fmt->width; sel->r.height = sink_fmt->height; @@ -3014,9 +3014,9 @@ static int ccs_init_cfg(struct v4l2_subdev *sd, unsigned int pad = ssd == sensor->pixel_array ? CCS_PA_PAD_SRC : CCS_PAD_SINK; struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_pad_format(sd, sd_state, pad); + v4l2_subdev_state_get_format(sd_state, pad); struct v4l2_rect *crop = - v4l2_subdev_get_pad_crop(sd, sd_state, pad); + v4l2_subdev_state_get_crop(sd_state, pad); bool is_active = !sd->active_state || sd->active_state == sd_state; mutex_lock(&sensor->mutex); @@ -3036,7 +3036,7 @@ static int ccs_init_cfg(struct v4l2_subdev *sd, return 0; } - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CCS_PAD_SRC); + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); fmt->code = ssd == sensor->src ? sensor->csi_format->code : sensor->internal_csi_format->code; fmt->field = V4L2_FIELD_NONE; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 0d6f0f8506f76f..b13b3db7af5d51 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -995,8 +995,7 @@ __et8ek8_get_pad_format(struct et8ek8_sensor *sensor, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index fd56ba13873915..7b71e6def40c41 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -876,7 +876,7 @@ __hi556_get_pad_crop(struct hi556 *hi556, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&hi556->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &hi556->cur_mode->crop; } @@ -1058,7 +1058,7 @@ static int hi556_set_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); hi556_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi556->cur_mode = mode; __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); @@ -1092,9 +1092,8 @@ static int hi556_get_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi556_assign_pad_format(hi556->cur_mode, &fmt->format); @@ -1140,10 +1139,10 @@ static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi556->mutex); hi556_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->top = HI556_PIXEL_ARRAY_TOP; try_crop->left = HI556_PIXEL_ARRAY_LEFT; try_crop->width = HI556_PIXEL_ARRAY_WIDTH; diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index fa0038749a3b98..8d61349835b759 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1731,7 +1731,7 @@ static int hi846_set_format(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mf; return 0; } @@ -1809,9 +1809,8 @@ static int hi846_get_format(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(&hi846->sd, - sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); return 0; } @@ -1878,7 +1877,7 @@ static int hi846_get_selection(struct v4l2_subdev *sd, mutex_lock(&hi846->mutex); switch (sel->which) { case V4L2_SUBDEV_FORMAT_TRY: - v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: sel->r = hi846->cur_mode->crop; @@ -1904,7 +1903,7 @@ static int hi846_init_cfg(struct v4l2_subdev *sd, struct hi846 *hi846 = to_hi846(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&hi846->mutex); mf->code = HI846_MEDIA_BUS_FORMAT; diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 32547d7a2659fa..cda9fce0ee1b6b 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -2703,7 +2703,7 @@ static int hi847_set_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); hi847_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi847->cur_mode = mode; @@ -2738,9 +2738,8 @@ static int hi847_get_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi847->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi847_assign_pad_format(hi847->cur_mode, &fmt->format); @@ -2785,7 +2784,7 @@ static int hi847_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi847->mutex); hi847_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&hi847->mutex); return 0; diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index ee5a2867538844..865799880b0bad 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -398,7 +398,7 @@ static int imx208_write_regs(struct imx208 *imx208, static int imx208_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -551,9 +551,8 @@ static int __imx208_get_pad_format(struct imx208 *imx208, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx208->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx208_mode_to_pad_format(imx208, imx208->cur_mode, fmt); @@ -594,7 +593,7 @@ static int imx208_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx208_mode_to_pad_format(imx208, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { imx208->cur_mode = mode; __v4l2_ctrl_s_ctrl(imx208->link_freq, mode->link_freq_index); diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 2f9c8582f9401a..72797e003d901d 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -540,7 +540,7 @@ __imx214_get_pad_format(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->fmt; default: @@ -570,7 +570,7 @@ __imx214_get_pad_crop(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->crop; default: diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index e30e14d63823aa..fdb38b948a20f7 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1005,7 +1005,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx258 *imx258 = to_imx258(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; /* Initialize try_fmt */ @@ -1015,7 +1015,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->left = IMX258_PIXEL_ARRAY_LEFT; try_crop->top = IMX258_PIXEL_ARRAY_TOP; try_crop->width = IMX258_PIXEL_ARRAY_WIDTH; @@ -1220,9 +1220,8 @@ static int __imx258_get_pad_format(struct imx258 *imx258, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx258_update_pad_format(imx258, imx258->cur_mode, fmt); @@ -1267,7 +1266,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx258_update_pad_format(imx258, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx258->cur_mode = mode; @@ -1311,7 +1310,7 @@ __imx258_get_pad_crop(struct imx258 *imx258, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx258->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx258->cur_mode->crop; } diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 1886eaab1c2471..8dc11c9ec1ee1c 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1018,8 +1018,8 @@ static int __imx274_change_compose(struct stimx274 *imx274, int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { - cur_crop = v4l2_subdev_get_pad_crop(&imx274->sd, sd_state, 0); - tgt_fmt = v4l2_subdev_get_pad_format(&imx274->sd, sd_state, 0); + cur_crop = v4l2_subdev_state_get_crop(sd_state, 0); + tgt_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { cur_crop = &imx274->crop; tgt_fmt = &imx274->format; @@ -1112,7 +1112,7 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, */ fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; else imx274->format = *fmt; @@ -1143,8 +1143,8 @@ static int imx274_get_selection(struct v4l2_subdev *sd, } if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, 0); + src_crop = v4l2_subdev_state_get_crop(sd_state, 0); + src_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { src_crop = &imx274->crop; src_fmt = &imx274->format; @@ -1215,7 +1215,7 @@ static int imx274_set_selection_crop(struct stimx274 *imx274, sel->r = new_crop; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - tgt_crop = v4l2_subdev_get_pad_crop(&imx274->sd, sd_state, 0); + tgt_crop = v4l2_subdev_state_get_crop(sd_state, 0); else tgt_crop = &imx274->crop; diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 6dacd38ae947a1..517c9703d7cff1 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -758,7 +758,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&imx290->sd); - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: @@ -994,7 +994,7 @@ static int imx290_start_streaming(struct imx290 *imx290, } /* Apply the register values related to current frame format */ - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); ret = imx290_setup_format(imx290, format); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format - %d\n", ret); @@ -1132,7 +1132,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx290->current_mode = mode; @@ -1155,7 +1155,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: { - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); /* * The sensor moves the readout by 1 pixel based on flips to diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index e87cc1dc038e0d..4b3a9945b5a2a0 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -362,7 +362,7 @@ static int imx296_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -579,8 +579,8 @@ static int imx296_setup(struct imx296 *sensor, struct v4l2_subdev_state *state) unsigned int i; int ret = 0; - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); - crop = v4l2_subdev_get_pad_crop(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); for (i = 0; i < ARRAY_SIZE(imx296_init_table); ++i) imx296_write(sensor, imx296_init_table[i].reg, @@ -757,7 +757,7 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd, const struct v4l2_mbus_framefmt *format; struct imx296 *sensor = to_imx296(sd); - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); /* * Binning does not seem to work on either mono or colour sensor @@ -782,8 +782,8 @@ static int imx296_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad); - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); format->width = crop->width; format->height = crop->height; @@ -808,7 +808,7 @@ static int imx296_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; case V4L2_SEL_TGT_CROP_DEFAULT: @@ -856,14 +856,14 @@ static int imx296_set_selection(struct v4l2_subdev *sd, rect.height = min_t(unsigned int, rect.height, IMX296_PIXEL_ARRAY_HEIGHT - rect.top); - crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + crop = v4l2_subdev_state_get_crop(state, sel->pad); if (rect.width != crop->width || rect.height != crop->height) { /* * Reset the output image size if the crop rectangle size has * been modified. */ - format = v4l2_subdev_get_pad_format(sd, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); format->width = rect.width; format->height = rect.height; } diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index 52ebb096e10750..90018d5c1da280 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -1862,7 +1862,7 @@ static int imx319_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx319 *imx319 = to_imx319(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx319->mutex); @@ -2003,10 +2003,9 @@ static int imx319_do_get_pad_format(struct imx319 *imx319, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx319->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx319_update_pad_format(imx319, imx319->cur_mode, fmt); @@ -2057,7 +2056,7 @@ imx319_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx319_update_pad_format(imx319, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx319->cur_mode = mode; diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index d722c9b7cd31d0..b22746b773f83b 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -831,7 +831,7 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { fmt->format.code = imx334->cur_code; @@ -872,7 +872,7 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else if (imx334->cur_mode != mode || imx334->cur_code != fmt->format.code) { imx334->cur_code = fmt->format.code; diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index cb3f4fc66a1740..6b670ebc0bd440 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -580,7 +580,7 @@ static int imx335_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); @@ -615,7 +615,7 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx335_update_controls(imx335, mode); diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 059a41b7eefc49..5c68fd948ff65d 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1161,7 +1161,7 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx355->mutex); @@ -1302,10 +1302,9 @@ static int imx355_do_get_pad_format(struct imx355 *imx355, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx355->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx355_update_pad_format(imx355, imx355->cur_mode, fmt); @@ -1356,7 +1355,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx355->cur_mode = mode; diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 8597f98a8dcf80..75150e15730c28 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -722,7 +722,7 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx412_fill_pad_format(imx412, imx412->cur_mode, fmt); @@ -757,7 +757,7 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx412_update_controls(imx412, mode); diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3f00172df3cc38..29e4f8ba1ebeb5 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -547,7 +547,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -830,7 +830,7 @@ static int imx415_enum_frame_size(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); if (fse->index > 0 || fse->code != format->code) return -EINVAL; @@ -846,7 +846,7 @@ static int imx415_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { - fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); return 0; } @@ -857,7 +857,7 @@ static int imx415_set_format(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); format->width = fmt->format.width; format->height = fmt->format.height; diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 8e9d89e88de3c9..720192f484cbe8 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -1315,9 +1315,9 @@ static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx477 *imx477 = to_imx477(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, METADATA_PAD); struct v4l2_rect *try_crop; mutex_lock(&imx477->mutex); @@ -1336,7 +1336,7 @@ static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_meta->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); try_crop->left = IMX477_PIXEL_ARRAY_LEFT; try_crop->top = IMX477_PIXEL_ARRAY_TOP; try_crop->width = IMX477_PIXEL_ARRAY_WIDTH; @@ -1571,8 +1571,7 @@ static int imx477_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx477->sd, sd_state, - fmt->pad); + v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip: */ try_fmt->code = fmt->pad == IMAGE_PAD ? imx477_get_format_code(imx477, try_fmt->code) : @@ -1666,8 +1665,8 @@ static int imx477_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); imx477_update_image_pad_format(imx477, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else if (imx477->mode != mode) { imx477->mode = mode; @@ -1676,8 +1675,8 @@ static int imx477_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -1697,7 +1696,7 @@ __imx477_get_pad_crop(struct imx477 *imx477, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx477->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx477->mode->crop; } diff --git a/drivers/media/i2c/imx500.c b/drivers/media/i2c/imx500.c index 15e7d3f7dccdc4..c03e51ea90435f 100644 --- a/drivers/media/i2c/imx500.c +++ b/drivers/media/i2c/imx500.c @@ -2105,8 +2105,8 @@ static int imx500_get_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx500->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format( - &imx500->sd, sd_state, fmt->pad); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); /* update the code which could change due to vflip or hflip */ try_fmt->code = fmt->pad == IMAGE_PAD ? imx500_get_format_code(imx500) : @@ -2174,8 +2174,8 @@ static int imx500_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); imx500_update_image_pad_format(imx500, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else if (imx500->mode != mode) { imx500->mode = mode; @@ -2184,8 +2184,8 @@ static int imx500_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -2204,7 +2204,7 @@ __imx500_get_pad_crop(struct imx500 *imx500, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx500->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx500->mode->crop; } diff --git a/drivers/media/i2c/imx519.c b/drivers/media/i2c/imx519.c index 7cb63e0187641f..0f5661ff7e967a 100644 --- a/drivers/media/i2c/imx519.c +++ b/drivers/media/i2c/imx519.c @@ -1157,9 +1157,9 @@ static int imx519_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx519 *imx519 = to_imx519(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, METADATA_PAD); struct v4l2_rect *try_crop; mutex_lock(&imx519->mutex); @@ -1177,7 +1177,7 @@ static int imx519_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_meta->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); try_crop->left = IMX519_PIXEL_ARRAY_LEFT; try_crop->top = IMX519_PIXEL_ARRAY_TOP; try_crop->width = IMX519_PIXEL_ARRAY_WIDTH; @@ -1402,8 +1402,7 @@ static int imx519_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx519->sd, sd_state, - fmt->pad); + v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip: */ try_fmt->code = fmt->pad == IMAGE_PAD ? imx519_get_format_code(imx519) : @@ -1494,8 +1493,8 @@ static int imx519_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); imx519_update_image_pad_format(imx519, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { imx519->mode = mode; @@ -1504,8 +1503,8 @@ static int imx519_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -1524,7 +1523,7 @@ __imx519_get_pad_crop(struct imx519 *imx519, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx519->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx519->mode->crop; } diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c index 29b1d4479c508a..d2c822202cafc1 100644 --- a/drivers/media/i2c/imx708.c +++ b/drivers/media/i2c/imx708.c @@ -1007,9 +1007,9 @@ static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx708 *imx708 = to_imx708(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, METADATA_PAD); struct v4l2_rect *try_crop; mutex_lock(&imx708->mutex); @@ -1032,7 +1032,7 @@ static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_meta->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); try_crop->left = IMX708_PIXEL_ARRAY_LEFT; try_crop->top = IMX708_PIXEL_ARRAY_TOP; try_crop->width = IMX708_PIXEL_ARRAY_WIDTH; @@ -1349,8 +1349,7 @@ static int imx708_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx708->sd, sd_state, - fmt->pad); + v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip */ try_fmt->code = fmt->pad == IMAGE_PAD ? imx708_get_format_code(imx708) : @@ -1400,8 +1399,8 @@ static int imx708_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); imx708_update_image_pad_format(imx708, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { imx708->mode = mode; @@ -1409,8 +1408,8 @@ static int imx708_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -1429,7 +1428,7 @@ __imx708_get_pad_crop(struct imx708 *imx708, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx708->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx708->mode->crop; } diff --git a/drivers/media/i2c/irs1125.c b/drivers/media/i2c/irs1125.c index eac03e13aeb54e..f69b4c5a98279f 100644 --- a/drivers/media/i2c/irs1125.c +++ b/drivers/media/i2c/irs1125.c @@ -930,7 +930,7 @@ static int irs1125_detect(struct v4l2_subdev *sd) static int irs1125_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); format->code = MEDIA_BUS_FMT_Y12_1X12; format->width = IRS1125_WINDOW_WIDTH_DEF; diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 73460688c356d2..89e13ebbce0c21 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1007,8 +1007,8 @@ static int isl7998x_get_fmt(struct v4l2_subdev *sd, mutex_lock(&isl7998x->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); goto out; } @@ -1044,7 +1044,7 @@ static int isl7998x_set_fmt(struct v4l2_subdev *sd, mf->field = mode->field; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = format->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = format->format; mutex_unlock(&isl7998x->lock); diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index fc1cf196ef0151..ee11ae682d8de6 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -913,7 +913,7 @@ max9286_get_pad_format(struct max9286_priv *priv, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &priv->fmt[pad]; default: @@ -1020,7 +1020,7 @@ static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) unsigned int i; for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_get_try_format(subdev, fh->state, i); + format = v4l2_subdev_state_get_format(fh->state, i); max9286_init_format(format); } diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 5790d5b5c2d35e..be56e35de01180 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -331,7 +331,7 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; } @@ -411,7 +411,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -662,7 +662,7 @@ static int mt9m001_init_cfg(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); try_fmt->width = MT9M001_MAX_WIDTH; try_fmt->height = MT9M001_MAX_HEIGHT; diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index e17ff41ebba091..17ca92810b581f 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -525,7 +525,7 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + mf = v4l2_subdev_state_get_format(sd_state, format->pad); format->format = *mf; return 0; } @@ -671,7 +671,7 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -1115,7 +1115,7 @@ static int mt9m111_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); format->width = MT9M111_MAX_WIDTH; format->height = MT9M111_MAX_HEIGHT; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 348f1e1098fb87..89bcd48748b916 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -549,8 +549,7 @@ __mt9p031_get_pad_format(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -565,8 +564,7 @@ __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index e3b9ff37450071..2e2d9853c08958 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -982,7 +982,7 @@ static int mt9t112_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 7f313d731839be..d0924b4ac6fb05 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -356,7 +356,7 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd, set_res(sd); } else { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; } return 0; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 00e7bc6e3235c6..c5614f535d1a4b 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -355,8 +355,7 @@ __mt9v032_get_pad_format(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -371,8 +370,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -930,13 +928,13 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); + crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 2c321e4884bea1..6752582cb2c7a8 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -795,7 +795,7 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v111->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v111->fmt; default: @@ -951,7 +951,7 @@ static int mt9v111_set_format(struct v4l2_subdev *subdev, static int mt9v111_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state) { - *v4l2_subdev_get_pad_format(subdev, sd_state, 0) = mt9v111_def_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = mt9v111_def_fmt; return 0; } diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 365ce568458360..1051b59affa39e 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -815,8 +815,7 @@ static int og01a1b_set_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { og01a1b->cur_mode = mode; __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index); @@ -849,9 +848,8 @@ static int og01a1b_get_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&og01a1b->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format); @@ -896,7 +894,7 @@ static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&og01a1b->mutex); return 0; diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index 9afe9bf50334a9..e94f7c53e6f08b 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -771,7 +771,7 @@ static int ov01a10_set_format(struct v4l2_subdev *sd, h_blank); } - format = v4l2_subdev_get_pad_format(sd, sd_state, fmt->stream); + format = v4l2_subdev_state_get_format(sd_state, fmt->stream); *format = fmt->format; return 0; diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 741d977a76f32f..aa6ad482baa392 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -315,7 +315,7 @@ static int ov02a10_set_fmt(struct v4l2_subdev *sd, ov02a10_fill_fmt(ov02a10->cur_mode, mbus_fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - frame_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + frame_fmt = v4l2_subdev_state_get_format(sd_state, 0); else frame_fmt = &ov02a10->fmt; @@ -336,8 +336,8 @@ static int ov02a10_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov02a10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format = ov02a10->fmt; mbus_fmt->code = ov02a10->fmt.code; diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c index 7d55d4ca24de18..d790f7d2652656 100644 --- a/drivers/media/i2c/ov08d10.c +++ b/drivers/media/i2c/ov08d10.c @@ -1192,7 +1192,7 @@ static int ov08d10_set_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov08d10->cur_mode = mode; @@ -1231,9 +1231,8 @@ static int ov08d10_get_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov08d10->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov08d10_update_pad_format(ov08d10, ov08d10->cur_mode, &fmt->format); @@ -1289,7 +1288,7 @@ static int ov08d10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, &ov08d10->priv_lane->sp_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov08d10->mutex); return 0; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 637da4df69011d..ab63d06a68cb4f 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -2539,7 +2539,7 @@ static int ov08x40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) const struct ov08x40_mode *default_mode = &supported_modes[0]; struct ov08x40 *ov08x = to_ov08x40(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov08x->mutex); @@ -2777,10 +2777,9 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov08x->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov08x40_update_pad_format(ov08x->cur_mode, fmt); @@ -2829,7 +2828,7 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov08x40_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov08x->cur_mode = mode; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 35652b36234726..1ccd1f8c67afe8 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1150,9 +1150,8 @@ static int ov13858_write_reg_list(struct ov13858 *ov13858, static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov13858 *ov13858 = to_ov13858(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13858->mutex); @@ -1320,10 +1319,9 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13858->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13858_update_pad_format(ov13858->cur_mode, fmt); @@ -1372,7 +1370,7 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13858->cur_mode = mode; diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 124eaa4f9e2ca8..1ae6cc4e38abd0 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -702,9 +702,8 @@ static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { const struct ov13b10_mode *default_mode = &supported_modes[0]; struct ov13b10 *ov13b = to_ov13b10(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13b->mutex); @@ -949,10 +948,9 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13b->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13b10_update_pad_format(ov13b->cur_mode, fmt); @@ -1001,7 +999,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13b10_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13b->cur_mode = mode; diff --git a/drivers/media/i2c/ov2311.c b/drivers/media/i2c/ov2311.c index fa5bb797d3d67d..3680561ad779b7 100644 --- a/drivers/media/i2c/ov2311.c +++ b/drivers/media/i2c/ov2311.c @@ -484,7 +484,7 @@ static int ov2311_set_fmt(struct v4l2_subdev *sd, V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov2311->cur_mode = mode; @@ -519,7 +519,7 @@ static int ov2311_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2311->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -596,7 +596,7 @@ __ov2311_get_pad_crop(struct ov2311 *ov2311, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov2311->subdev, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov2311->cur_mode->crop; } @@ -802,7 +802,7 @@ static int ov2311_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov2311 *ov2311 = to_ov2311(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); const struct ov2311_mode *def_mode = &supported_modes[0]; mutex_lock(&ov2311->mutex); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index e18e122426d432..c7b4111e594349 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -920,7 +920,7 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; } @@ -988,7 +988,7 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, /* select format */ priv->cfmt_code = mf->code; } else { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; } out: mutex_unlock(&priv->lock); @@ -1000,7 +1000,7 @@ static int ov2640_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); const struct ov2640_win_size *win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 2c3dbe164eb696..1d0ef72a64036b 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1033,7 +1033,7 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&ov2659->lock); fmt->format = *mf; mutex_unlock(&ov2659->lock); @@ -1109,7 +1109,7 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2659->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } else { s64 val; @@ -1304,7 +1304,7 @@ static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); dev_dbg(&client->dev, "%s:\n", __func__); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index d4d40acc4c4b16..832da7512b2b0f 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -309,7 +309,7 @@ __ov2680_get_pad_format(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&sensor->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); return &sensor->mode.fmt; } @@ -321,7 +321,7 @@ __ov2680_get_pad_crop(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); return &sensor->mode.crop; } @@ -650,7 +650,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, ov2680_fill_format(sensor, &format->format, width, height); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + try_fmt = v4l2_subdev_state_get_format(sd_state, 0); *try_fmt = format->format; return 0; } @@ -760,9 +760,9 @@ static int ov2680_init_cfg(struct v4l2_subdev *sd, { struct ov2680_dev *sensor = to_ov2680_dev(sd); - *v4l2_subdev_get_pad_crop(sd, sd_state, 0) = ov2680_default_crop; + *v4l2_subdev_state_get_crop(sd_state, 0) = ov2680_default_crop; - ov2680_fill_format(sensor, v4l2_subdev_get_pad_format(sd, sd_state, 0), + ov2680_fill_format(sensor, v4l2_subdev_state_get_format(sd_state, 0), OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); return 0; } diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index de0d34e43969fc..8c935202ac9bfc 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -405,7 +405,7 @@ __ov2685_get_pad_crop(struct ov2685 *ov2685, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov2685->subdev, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } @@ -554,7 +554,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov2685->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov2685_fill_fmt(&supported_modes[0], try_fmt); diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c index fda217d2cb10af..b271ff3e254418 100644 --- a/drivers/media/i2c/ov4689.c +++ b/drivers/media/i2c/ov4689.c @@ -577,7 +577,7 @@ static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov4689->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 40532f7bcabea8..3f79a3b77044f2 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2797,8 +2797,7 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -2971,7 +2970,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, 0) = *mbus_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *mbus_fmt; goto out; } @@ -3750,8 +3749,8 @@ static int ov5640_init_cfg(struct v4l2_subdev *sd, { struct ov5640_dev *sensor = to_ov5640_dev(sd); struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); *fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt : ov5640_dvp_default_fmt; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index a70db7e601a495..695f4f6735363f 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -851,7 +851,7 @@ __ov5645_get_pad_format(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->fmt; default: @@ -878,7 +878,7 @@ __ov5645_get_pad_crop(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->crop; default: diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index f474d22dbded94..164f16da70239b 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -782,7 +782,7 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5647->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5647->mode->crop; } @@ -899,8 +899,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); switch (format->which) { case V4L2_SUBDEV_FORMAT_TRY: - sensor_format = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + sensor_format = v4l2_subdev_state_get_format(sd_state, + format->pad); break; default: sensor_format = &sensor->mode->format; @@ -930,7 +930,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, /* Update the sensor mode and apply at it at streamon time. */ mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = mode->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = mode->format; } else { int exposure_max, exposure_def; int hblank, vblank; @@ -1052,8 +1052,8 @@ static int ov5647_detect(struct v4l2_subdev *sd) static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = OV5647_PIXEL_ARRAY_LEFT; crop->top = OV5647_PIXEL_ARRAY_TOP; diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index aa10eb4e3991c5..13e6060d15d4d8 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -2232,8 +2232,8 @@ static int ov5648_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov5648_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2285,7 +2285,7 @@ static int ov5648_set_fmt(struct v4l2_subdev *subdev, ov5648_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 29e773a997dd4d..8fa925c4d98c77 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2202,9 +2202,9 @@ static int ov5670_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); const struct ov5670_mode *default_mode = &supported_modes[0]; - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); fmt->width = default_mode->width; fmt->height = default_mode->height; @@ -2265,9 +2265,8 @@ static int ov5670_do_get_pad_format(struct ov5670 *ov5670, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5670_update_pad_format(ov5670->cur_mode, fmt); @@ -2312,7 +2311,7 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov5670_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5670->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); @@ -2583,7 +2582,7 @@ __ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index c499e7e93c54b9..3590c8de7ef0cf 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1079,7 +1079,7 @@ static int ov5675_set_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); ov5675_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5675->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5675->link_freq, mode->link_freq_index); @@ -1112,9 +1112,8 @@ static int ov5675_get_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5675_update_pad_format(ov5675->cur_mode, &fmt->format); @@ -1184,7 +1183,7 @@ static int ov5675_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov5675->mutex); ov5675_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov5675->mutex); return 0; diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 488ee6d9d30101..3211f21614e84b 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -776,7 +776,7 @@ __ov5693_get_pad_format(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.format; default: @@ -791,7 +791,7 @@ __ov5693_get_pad_crop(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.crop; } diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 8d1c3a673c23a2..c2f0063206392a 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -821,7 +821,7 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5695->cur_mode = mode; h_blank = mode->hts_def - mode->width; @@ -847,8 +847,8 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov5695->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -1045,7 +1045,7 @@ static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov5695 *ov5695 = to_ov5695(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); const struct ov5695_mode *def_mode = &supported_modes[0]; mutex_lock(&ov5695->mutex); diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c index ee75bbf8d8bfd6..246a2fea8db114 100644 --- a/drivers/media/i2c/ov64a40.c +++ b/drivers/media/i2c/ov64a40.c @@ -3059,10 +3059,10 @@ static int ov64a40_init_cfg(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); ov64a40_update_pad_fmt(ov64a40, &ov64a40_modes[0], format); - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); crop->top = OV64A40_PIXEL_ARRAY_TOP; crop->left = OV64A40_PIXEL_ARRAY_LEFT; crop->width = OV64A40_PIXEL_ARRAY_WIDTH; @@ -3115,7 +3115,7 @@ static int ov64a40_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); + sel->r = *v4l2_subdev_state_get_crop(state, 0); return 0; @@ -3155,7 +3155,7 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, ov64a40_update_pad_fmt(ov64a40, mode, &fmt->format); - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); if (ov64a40->mode == mode && format->code == fmt->format.code) return 0; @@ -3166,7 +3166,7 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, int exp_max; ov64a40->mode = mode; - *v4l2_subdev_get_pad_crop(sd, state, 0) = mode->analogue_crop; + *v4l2_subdev_state_get_crop(state, 0) = mode->analogue_crop; /* Update control limits according to the new mode. */ timings = ov64a40_get_timings(ov64a40, diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 38d21b40f5d815..bf1e7617ee08b8 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -476,7 +476,7 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { /* pre-select try crop rectangle */ - rect = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + rect = v4l2_subdev_state_get_crop(sd_state, 0); } else { /* pre-select active crop rectangle */ @@ -532,9 +532,9 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_rect *crop = - v4l2_subdev_get_pad_crop(sd, sd_state, 0); + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_pad_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); /* detect current pad config scaling factor */ bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); @@ -591,7 +591,7 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, /* update media bus format code and frame size */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_pad_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); mf->width = try_fmt->width; mf->height = try_fmt->height; @@ -722,7 +722,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + crop = v4l2_subdev_state_get_crop(sd_state, 0); else crop = &priv->rect; @@ -730,7 +730,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_pad_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); /* store new mbus frame format code and size in pad config */ try_fmt->width = crop->width >> half_scale; diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index fc26f2b6a60492..b317a1ee6c86a3 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1151,7 +1151,7 @@ __ov7251_get_pad_format(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->fmt; default: @@ -1181,7 +1181,7 @@ __ov7251_get_pad_crop(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->crop; default: diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 85bf745c0badd7..1557ebbb7f9c0f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1116,8 +1116,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) return ret; - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; return 0; } @@ -1145,7 +1144,7 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mbus_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; return 0; } else { @@ -1711,7 +1710,7 @@ static void ov7670_get_default_format(struct v4l2_subdev *sd, static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov7670_get_default_format(sd, format); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 1fbf72152956ac..e397f7531e1dd0 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1220,7 +1220,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 78d04ce68971ad..6c642693ca307f 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -819,8 +819,7 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, if (ret) goto error; - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; mutex_unlock(&ov7740->mutex); return 0; @@ -850,7 +849,7 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov7740->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; } else { format->format = ov7740->format; @@ -890,7 +889,7 @@ static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov7740->mutex); ov7740_get_default_format(sd, format); diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index f053c3a7676a09..50a3c575bf78e6 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -2180,7 +2180,7 @@ static int ov8856_set_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov8856->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); @@ -2218,9 +2218,8 @@ static int ov8856_get_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov8856_update_pad_format(ov8856, ov8856->cur_mode, &fmt->format); @@ -2271,7 +2270,7 @@ static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, &ov8856->priv_lane->supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov8856->mutex); return 0; diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c index 4d9fd76e2f60f3..0e377613613695 100644 --- a/drivers/media/i2c/ov8858.c +++ b/drivers/media/i2c/ov8858.c @@ -1333,7 +1333,7 @@ static int ov8858_start_stream(struct ov8858 *ov8858, if (ret) return ret; - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes), width, height, format->width, format->height); @@ -1428,7 +1428,7 @@ static int ov8858_set_fmt(struct v4l2_subdev *sd, fmt->format.field = V4L2_FIELD_NONE; /* Store the format in the current subdev state. */ - *v4l2_subdev_get_pad_format(sd, state, 0) = fmt->format; + *v4l2_subdev_state_get_format(state, 0) = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) return 0; @@ -1547,7 +1547,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl) * - by the driver when s_ctrl is called in the s_stream(1) call path */ state = v4l2_subdev_get_locked_active_state(&ov8858->subdev); - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); /* Propagate change of current control to all related controls */ switch (ctrl->id) { diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index f2213c6158d3e4..fb19ab0c2a9d7b 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2710,8 +2710,8 @@ static int ov8865_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2765,7 +2765,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, ov8865_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) @@ -2818,7 +2818,7 @@ __ov8865_get_pad_crop(struct ov8865_sensor *sensor, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); + *r = *v4l2_subdev_state_get_crop(state, pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: r->height = mode->output_size_y; diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 0b092a425cfc8c..8ef09b52fa22ac 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -816,7 +816,7 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov9282_fill_pad_format(ov9282, ov9282->cur_mode, ov9282->code, @@ -862,7 +862,7 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = ov9282_update_controls(ov9282, mode, fmt); @@ -904,7 +904,7 @@ __ov9282_get_pad_crop(struct ov9282 *ov9282, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov9282->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov9282->cur_mode->crop; } diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index a9adddde10064e..b0c171fe75bc08 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -547,7 +547,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index da1ab5135eaa1c..753f6222102a3a 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1172,7 +1172,7 @@ static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); fmt->format = *mf; return 0; } @@ -1233,8 +1233,7 @@ static int ov965x_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { if (sd_state) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } } else { @@ -1363,7 +1362,7 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on) static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov965x_get_default_format(mf); return 0; diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c index b36fc0fedad481..a3ba0f06f017a9 100644 --- a/drivers/media/i2c/ov9734.c +++ b/drivers/media/i2c/ov9734.c @@ -742,7 +742,7 @@ static int ov9734_set_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); ov9734_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov9734->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov9734->link_freq, mode->link_freq_index); @@ -775,9 +775,8 @@ static int ov9734_get_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov9734->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov9734_update_pad_format(ov9734->cur_mode, &fmt->format); @@ -822,7 +821,7 @@ static int ov9734_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov9734->mutex); ov9734_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov9734->mutex); return 0; diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index dbddb7a9f21120..403185b18f061f 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1009,7 +1009,7 @@ static int rj54n1_set_fmt(struct v4l2_subdev *sd, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ed5b10731a14fd..8f9b5713daf7a4 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { - struct v4l2_subdev *sd = &state->sensor_sd; u32 code; switch (fmt->pad) { @@ -841,10 +840,8 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) *fs = state->oif_pix_size[RES_ISP]; else - *fs = s5c73m3_find_frame_size( - v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD), - RES_ISP); + *fs = s5c73m3_find_frame_size(v4l2_subdev_state_get_format(sd_state, OIF_ISP_PAD), + RES_ISP); break; } @@ -990,8 +987,8 @@ static int s5c73m3_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1025,8 +1022,8 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1069,7 +1066,7 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd, s5c73m3_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } else { switch (fmt->pad) { @@ -1108,11 +1105,11 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, s5c73m3_oif_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; if (fmt->pad == OIF_ISP_PAD) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_SOURCE_PAD); mf->width = fmt->format.width; mf->height = fmt->format.height; } @@ -1260,8 +1257,8 @@ static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, if (fse->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_ISP_PAD); w = mf->width; h = mf->height; @@ -1316,11 +1313,11 @@ static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); @@ -1331,15 +1328,15 @@ static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_SOURCE_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); return 0; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 67da2045f5431f..03ccfb0e1e11db 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1273,7 +1273,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1307,7 +1307,7 @@ static int s5k5baf_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = *mf; return 0; } @@ -1379,11 +1379,11 @@ static int s5k5baf_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { if (rtype == R_COMPOSE) - sel->r = *v4l2_subdev_get_try_compose(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_compose(sd_state, + sel->pad); else - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + sel->pad); return 0; } @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { rects = (struct v4l2_rect * []) { - &s5k5baf_cis_rect, - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_compose(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_OUT) - }; + &s5k5baf_cis_rect, + v4l2_subdev_state_get_crop(sd_state, PAD_CIS), + v4l2_subdev_state_get_compose(sd_state, PAD_CIS), + v4l2_subdev_state_get_crop(sd_state, PAD_OUT) + }; s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); return 0; } @@ -1696,22 +1693,22 @@ static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_CIS); + mf = v4l2_subdev_state_get_format(fh->state, PAD_CIS); s5k5baf_try_cis_format(mf); if (s5k5baf_is_cis_subdev(sd)) return 0; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_OUT); + mf = v4l2_subdev_state_get_format(fh->state, PAD_OUT); mf->colorspace = s5k5baf_formats[0].colorspace; mf->code = s5k5baf_formats[0].code; mf->width = s5k5baf_cis_rect.width; mf->height = s5k5baf_cis_rect.height; mf->field = V4L2_FIELD_NONE; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_compose(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_OUT) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_compose(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_OUT) = s5k5baf_cis_rect; return 0; } diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index b3560c8f8b4116..0c2674115b7bd3 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -127,8 +127,7 @@ static struct v4l2_mbus_framefmt *__s5k6a3_get_format( u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&sensor->subdev, - sd_state, pad) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL; return &sensor->format; } @@ -174,9 +173,8 @@ static const struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(fh->state, + 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 82976aee5e860a..51b62a97946a46 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -595,7 +595,7 @@ static int saa6752hs_set_fmt(struct v4l2_subdev *sd, f->colorspace = V4L2_COLORSPACE_SMPTE170M; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *f; + *v4l2_subdev_state_get_format(sd_state, 0) = *f; return 0; } diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index dab14787116b6d..914f915749a80f 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -724,8 +724,7 @@ static int mipid02_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&bridge->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &bridge->fmt; @@ -751,8 +750,8 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = bridge->fmt; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - MIPID02_SINK_0); + format->format = *v4l2_subdev_state_get_format(sd_state, + MIPID02_SINK_0); /* but code may need to be converted */ format->format.code = serial_to_parallel_code(format->format.code); @@ -761,7 +760,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, if (format->which != V4L2_SUBDEV_FORMAT_TRY) return; - *v4l2_subdev_get_try_format(sd, sd_state, MIPID02_SOURCE) = + *v4l2_subdev_state_get_format(sd_state, MIPID02_SOURCE) = format->format; } @@ -776,7 +775,7 @@ static void mipid02_set_fmt_sink(struct v4l2_subdev *sd, format->format.code = get_fmt_code(format->format.code); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &bridge->fmt; diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c index 30f82ca344c446..9e62a1dde0af50 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/st-vgxy61.c @@ -780,8 +780,7 @@ static int vgxy61_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -1294,7 +1293,7 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; } else if (sensor->current_mode != new_mode || sensor->fmt.code != format->format.code) { diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 566f5eaddd572e..5fcf75ef1925bf 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -427,7 +427,7 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); /* Self defined CSI user data type id's are not supported yet */ @@ -745,10 +745,10 @@ static int tc358746_init_cfg(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SINK); + fmt = v4l2_subdev_state_get_format(state, TC358746_SINK); *fmt = tc358746_def_fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SOURCE); + fmt = v4l2_subdev_state_get_format(state, TC358746_SOURCE); *fmt = tc358746_def_fmt; fmt->code = tc358746_src_mbus_code(tc358746_def_fmt.code); @@ -781,7 +781,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, if (format->pad == TC358746_SOURCE) return v4l2_subdev_get_fmt(sd, sd_state, format); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SINK); fmt = tc358746_get_format_by_code(format->pad, format->format.code); if (IS_ERR(fmt)) @@ -796,7 +796,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, *sink_fmt = format->format; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SOURCE); + src_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SOURCE); *src_fmt = *sink_fmt; src_fmt->code = tc358746_src_mbus_code(sink_fmt->code); @@ -901,7 +901,7 @@ tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, return err; sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); /* Check the FIFO settings */ fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 325e9912594164..63c12b77ff1ebb 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1740,7 +1740,7 @@ static int tda1997x_init_cfg(struct v4l2_subdev *sd, struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mf->code = state->mbus_codes[0]; return 0; @@ -1792,7 +1792,7 @@ static int tda1997x_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else format->format.code = state->mbus_code; @@ -1826,7 +1826,7 @@ static int tda1997x_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *fmt = format->format; } else { int ret = tda1997x_setup_format(state, format->format.code); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e543b3f7a4d89b..76a92bfb6fdee7 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1035,7 +1035,7 @@ tvp5150_get_pad_crop(struct tvp5150 *decoder, return &decoder->rect; case V4L2_SUBDEV_FORMAT_TRY: #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_crop(&decoder->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); #else return ERR_PTR(-EINVAL); #endif diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 5233c33b93df21..7c331a7f12d473 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -829,7 +829,7 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 423842d2a5b2b2..0bc508ad38dfc7 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1205,11 +1205,11 @@ static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) }; /* Initialize try_fmt */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SINK); *format = fmt_default; /* same as sink */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SOURCE); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SOURCE); *format = fmt_default; return 0; @@ -1231,8 +1231,8 @@ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, mutex_lock(&q->subdev_lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else fmt->format = q->subdev_fmt; @@ -1265,7 +1265,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, return cio2_subdev_get_fmt(sd, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mbus = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mbus = v4l2_subdev_state_get_format(sd_state, fmt->pad); else mbus = &q->subdev_fmt; diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 685b2ec96071a4..a8ee5650dfcc51 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -345,7 +345,7 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &csi->format_mbus[pad]; default: @@ -363,7 +363,7 @@ static int mei_csi_init_cfg(struct v4l2_subdev *sd, mutex_lock(&csi->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = mei_csi_format_mbus_default; } diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index bb83117bab2e69..da58f33b6b0af4 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -560,7 +560,7 @@ static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, struct v4l2_subdev_state *sd_state) { struct v4l2_rect *try_crop = - v4l2_subdev_get_pad_crop(isi->entity.subdev, sd_state, 0); + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .code = isi_fmt->mbus_code, .which = V4L2_SUBDEV_FORMAT_TRY, diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 939d0d2a4c5635..373321184fe609 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -353,12 +353,12 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_get_pad_format(subdev, state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad); *fmt = format->format; /* Propagate to source formats */ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_get_pad_format(subdev, state, i); + fmt = v4l2_subdev_state_get_format(state, i); *fmt = format->format; } diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index 1e0400b7803e93..1107e34cd8d380 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -176,8 +176,7 @@ __csi2tx_get_pad_format(struct v4l2_subdev *subdev, struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + return v4l2_subdev_state_get_format(sd_state, fmt->pad); return &csi2tx->pad_fmts[fmt->pad]; } diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index 988c1cc1d8b6b9..87a2d092aad2fa 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -232,8 +232,8 @@ static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -281,13 +281,12 @@ static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, req_fmt->format.field = V4L2_FIELD_NONE; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, - sd_state, - CSI2DC_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + CSI2DC_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; /* if we are just trying, we are done */ @@ -440,7 +439,7 @@ static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); v4l2_try_fmt->height = 480; v4l2_try_fmt->width = 640; diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index acb7395c156612..1d310814ae1eed 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -859,7 +859,7 @@ static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { struct v4l2_rect *try_crop = - v4l2_subdev_get_pad_crop(isc->current_subdev->sd, sd_state, 0); + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c index 0f29a32d15ce5d..58dffbc9fbcbbd 100644 --- a/drivers/media/platform/microchip/microchip-isc-scaler.c +++ b/drivers/media/platform/microchip/microchip-isc-scaler.c @@ -33,8 +33,8 @@ static int isc_scaler_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -74,12 +74,12 @@ static int isc_scaler_set_fmt(struct v4l2_subdev *sd, req_fmt->format.code = fmt->mbus_code; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - ISC_SCALER_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + ISC_SCALER_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; v4l_bound_align_image(&v4l2_try_fmt->width, @@ -149,13 +149,13 @@ static int isc_scaler_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); struct v4l2_rect *try_crop; struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE]; - try_crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); try_crop->top = 0; try_crop->left = 0; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index b08f6d2e7516d0..eb016ba98c011f 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -954,7 +954,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - format = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK); csis_fmt = find_csis_format(format->code); ret = mipi_csis_calculate_params(csis, csis_fmt); @@ -1002,7 +1002,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -1069,7 +1069,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, &sdformat->format.height, 1, CSIS_MAX_PIX_HEIGHT, 0, 0); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csis_fmt->code; fmt->width = sdformat->format.width; @@ -1083,7 +1083,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, CSIS_PAD_SOURCE); *fmt = sdformat->format; /* The format on the source pad might change due to unpacking. */ @@ -1104,7 +1104,7 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(state, CSIS_PAD_SOURCE); csis_fmt = find_csis_format(fmt->code); v4l2_subdev_unlock_state(state); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 15049c6aab3724..da29971acf26f6 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -542,8 +542,8 @@ static void imx7_csi_configure(struct imx7_csi *csi, } else { const struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&csi->sd, sd_state, - IMX7_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SINK); cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; @@ -1738,7 +1738,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd, for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_pad_format(sd, sd_state, i); + v4l2_subdev_state_get_format(sd_state, i); mf->code = IMX7_CSI_DEF_MBUS_CODE; mf->width = IMX7_CSI_DEF_PIX_WIDTH; @@ -1762,7 +1762,7 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; int ret = 0; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (code->pad) { case IMX7_CSI_PAD_SINK: @@ -1841,7 +1841,7 @@ static void imx7_csi_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; u32 code; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (sdformat->pad) { case IMX7_CSI_PAD_SRC: @@ -1891,7 +1891,7 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, imx7_csi_try_fmt(sd, sd_state, sdformat, &cc); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *fmt = sdformat->format; @@ -1902,8 +1902,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, format.format = sdformat->format; imx7_csi_try_fmt(sd, sd_state, &format, &outcc); - outfmt = v4l2_subdev_get_pad_format(sd, sd_state, - IMX7_CSI_PAD_SRC); + outfmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SRC); *outfmt = format.format; } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 65d20e9bae69db..14c6da392803e0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -263,10 +263,10 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) /* Configure the pipeline. */ state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); - compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); - crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); + compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK); + crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, MXC_ISI_PIPE_PAD_SINK); @@ -322,7 +322,7 @@ mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_format(&pipe->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); } static struct v4l2_rect * @@ -330,7 +330,7 @@ mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); } static struct v4l2_rect * @@ -338,7 +338,7 @@ mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); + return v4l2_subdev_state_get_compose(state, pad); } static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, @@ -832,8 +832,8 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, int ret; state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); v4l2_subdev_unlock_state(state); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 10840c9a0912b1..49bca2b01cc61d 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -713,7 +713,7 @@ static int mxc_isi_video_validate_format(struct mxc_isi_video *video) info = mxc_isi_format_by_fourcc(video->pix.pixelformat, MXC_ISI_VIDEO_CAP); - format = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); if (format->code != info->mbus_code || format->width != video->pix.width || diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index ed048f73c982b4..02a4c546a90aea 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -296,7 +296,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, /* Calculate the line rate from the pixel rate. */ - fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); csi2_fmt = find_csi2_format(fmt->code); link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, @@ -443,8 +443,9 @@ static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; - fmt_sink = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SINK); - fmt_source = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt_sink = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); + fmt_source = v4l2_subdev_state_get_format(sd_state, + MIPI_CSI2_PAD_SOURCE); fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; @@ -477,7 +478,7 @@ static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -514,7 +515,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, if (!csi2_fmt) csi2_fmt = &imx8mq_mipi_csi_formats[0]; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csi2_fmt->code; fmt->width = sdformat->format.width; @@ -523,7 +524,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SOURCE); *fmt = sdformat->format; return 0; diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 6360314f04a636..3afbadbd7f23f9 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -275,8 +275,7 @@ __csid_get_format(struct csid_device *csid, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csid->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 3f726a7237f566..340ccc0e827884 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -325,8 +325,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csiphy->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index b713f5b86aba69..79d91ec110236b 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -879,8 +879,7 @@ __ispif_get_format(struct ispif_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 965500b83d073b..94197355075ceb 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -772,8 +772,7 @@ __vfe_get_format(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -792,8 +791,8 @@ __vfe_get_compose(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, sd_state, - MSM_VFE_PAD_SINK); + return v4l2_subdev_state_get_compose(sd_state, + MSM_VFE_PAD_SINK); return &line->compose; } @@ -812,8 +811,7 @@ __vfe_get_crop(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, sd_state, - MSM_VFE_PAD_SRC); + return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC); return &line->crop; } diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 7360cf3863f2c8..aa974f7b5c0479 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -282,7 +282,7 @@ static int risp_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isp->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -302,7 +302,7 @@ static int risp_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = isp->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&isp->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 109cca91f733a0..eaf26f29be245c 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -1185,7 +1185,7 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { priv->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -1205,7 +1205,7 @@ static int rcsi2_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = priv->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&priv->lock); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 246eec259c5d7c..e81b5d34d0723c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -250,7 +250,7 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) } state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev); - fmt = v4l2_subdev_get_pad_format(&csi2->subdev, state, RZG2L_CSI2_SINK); + fmt = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); format = rzg2l_csi2_code_to_fmt(fmt->code); v4l2_subdev_unlock_state(state); @@ -500,13 +500,13 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SOURCE); if (fmt->pad == RZG2L_CSI2_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SINK); + sink_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); if (!rzg2l_csi2_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_csi2_formats[0].code; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 4dcd2faff5bbca..17a59dfc836310 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -39,7 +39,7 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) struct v4l2_mbus_framefmt *fmt; state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); - fmt = v4l2_subdev_get_pad_format(&cru->ip.subdev, state, 1); + fmt = v4l2_subdev_state_get_format(state, 1); v4l2_subdev_unlock_state(state); return fmt; @@ -108,13 +108,13 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CRU_IP_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); if (fmt->pad == RZG2L_CRU_IP_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + sink_format = v4l2_subdev_state_get_format(state, fmt->pad); if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_cru_ip_formats[0].code; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 89385b4cabe572..95bf7b4ac9b81b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -100,7 +100,7 @@ static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } static void brx_try_format(struct vsp1_brx *brx, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index c31f05a80bb567..d5a49e08a607d3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -142,7 +142,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_format(&entity->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } /** @@ -163,11 +163,9 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, { switch (target) { case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_compose(sd_state, pad); case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); default: return NULL; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index e0f87c8103ca56..6391a503ec97ce 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -19,8 +19,7 @@ struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_state *sd_state) { - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state, - RWPF_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 7320c1c72e688e..f0cef766fc0cb2 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -257,8 +257,8 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index) return -EINVAL; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_CSI_PAD_SINK); code->code = sink_fmt->code; return 0; @@ -290,10 +290,8 @@ static int rkisp1_csi_init_config(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -316,7 +314,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == RKISP1_CSI_PAD_SRC) return v4l2_subdev_get_fmt(sd, sd_state, fmt); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); sink_fmt->code = fmt->format.code; @@ -336,7 +334,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; /* Propagate the format to the source pad. */ - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SRC); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); *src_fmt = *sink_fmt; return 0; @@ -389,7 +387,7 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) return -EINVAL; sd_state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); format = rkisp1_mbus_info_get_by_code(sink_fmt->code); v4l2_subdev_unlock_state(sd_state); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index b897e69a9f469c..cd7d27240e0e10 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -66,8 +66,8 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = - v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; @@ -102,12 +102,12 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, const struct v4l2_mbus_framefmt *sink_frm; const struct v4l2_rect *sink_crop; - sink_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code); src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code); @@ -201,8 +201,8 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat, src_frm->quantization, src_frm->ycbcr_enc); @@ -332,8 +332,8 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp, RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) @@ -430,8 +430,8 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, struct v4l2_rect *sink_crop, *src_crop; /* Video. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -441,15 +441,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - sink_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; src_fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -457,15 +457,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; /* Parameters and statistics. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_STATS); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_PARAMS); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = 0; sink_fmt->height = 0; sink_fmt->field = V4L2_FIELD_NONE; @@ -486,12 +486,12 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, const struct v4l2_rect *src_crop; bool set_csc; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); /* * Media bus code. The ISP can operate in pass-through mode (Bayer in, @@ -584,10 +584,10 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); src_crop->left = ALIGN(r->left, 2); src_crop->width = ALIGN(r->width, 2); @@ -598,8 +598,8 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, *r = *src_crop; /* Propagate to out format */ - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt); } @@ -610,10 +610,10 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop, *src_crop; const struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->left = ALIGN(r->left, 2); sink_crop->width = ALIGN(r->width, 2); @@ -624,8 +624,8 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, *r = *sink_crop; /* Propagate to out crop */ - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_crop(isp, sd_state, src_crop); } @@ -638,8 +638,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->code = format->code; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { @@ -687,8 +687,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop); } @@ -703,7 +703,8 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format); else - fmt->format = *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -723,19 +724,19 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sel->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sel->pad); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); } break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; default: diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 328741bb7da0fe..991bc0639bc32f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -145,10 +145,8 @@ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; u32 dc_ctrl; - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); if (sink_crop->width == sink_fmt->width && sink_crop->height == sink_fmt->height && @@ -278,10 +276,8 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, struct v4l2_area src_y, src_c; struct v4l2_rect sink_c; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code); src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); @@ -295,8 +291,7 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, return; } - sink_y = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_y = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_c.width = sink_y->width / sink_yuv_info->hdiv; sink_c.height = sink_y->height / sink_yuv_info->vdiv; @@ -389,8 +384,7 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -400,15 +394,13 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; /* NOTE: there is no crop in the source pad, only in the sink */ @@ -423,10 +415,8 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -453,10 +443,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); /* Not crop for MP bayer raw data */ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -490,12 +478,9 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; @@ -585,8 +570,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + mf_sink = v4l2_subdev_state_get_format(sd_state, + RKISP1_RSZ_PAD_SINK); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; sel->r.left = 0; @@ -594,8 +579,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_RSZ_PAD_SINK); break; default: diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index a0d43bf892e677..05cafba1c7287a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -1479,7 +1479,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1534,7 +1534,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; return 0; } @@ -1604,10 +1604,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: @@ -1651,10 +1651,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c index b85986e50f4689..3c5d7bee2655b5 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c @@ -126,7 +126,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *mf = *v4l2_subdev_state_get_format(sd_state, fmt->pad); return 0; } @@ -172,9 +172,8 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(&isp->subdev, - sd_state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, + FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -207,7 +206,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __isp_subdev_try_format(isp, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -220,8 +219,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, format.pad = pad; __isp_subdev_try_format(isp, sd_state, &format); - mf = v4l2_subdev_get_try_format(sd, sd_state, - pad); + mf = v4l2_subdev_state_get_format(sd_state, + pad); *mf = format.format; } } @@ -374,18 +373,17 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, .field = V4L2_FIELD_NONE, }; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, FIMC_ISP_SD_PAD_SINK); *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 9396b10b5b1cc7..7898c9bebb0467 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -574,16 +574,14 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + rect = v4l2_subdev_state_get_crop(sd_state, + FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; mf->colorspace = sink->fmt->colorspace; @@ -1021,7 +1019,7 @@ static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, @@ -1129,7 +1127,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1166,7 +1164,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 686ca8753ba22a..aae8a8b2c0f4c0 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -569,8 +569,7 @@ static struct v4l2_mbus_framefmt *__s5pcsis_get_format( enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&state->sd, - sd_state, 0) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, 0) : NULL; return &state->format; } diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 0f5b3845d7b94f..be58260ea67e79 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1357,7 +1357,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1445,7 +1445,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 48702134ccc55e..c37bb1d76ef65d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -271,7 +271,7 @@ static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, CSI_SUBDEV_SINK); *fmt = sun4i_csi_pad_fmt_default; return 0; @@ -285,8 +285,7 @@ static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -303,8 +302,7 @@ static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index e573413123b955..d57481feee0533 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -507,7 +507,7 @@ static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi_dev->bridge.lock; mutex_lock(lock); @@ -547,8 +547,8 @@ static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi_dev->bridge.mbus_format; @@ -570,7 +570,7 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, sun6i_csi_bridge_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi_dev->bridge.mbus_format = *mbus_format; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index 08d86c17b284a5..d2c9f5d20496ae 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -311,7 +311,7 @@ static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -351,8 +351,8 @@ static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -374,7 +374,7 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun6i_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index 14a1844812c0e2..d6275954af984b 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -345,7 +345,7 @@ static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, v4l2_get_subdevdata(subdev); unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -387,8 +387,8 @@ static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -411,7 +411,7 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 1a4273bbe75255..61433744c6c49a 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -57,7 +57,7 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) state = v4l2_subdev_get_locked_active_state(&phy->subdev); - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; @@ -630,8 +628,8 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); code->code = fmt->code; } else { if (code->index >= cal_num_formats) @@ -656,8 +654,8 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); if (fse->code != fmt->code) return -EINVAL; @@ -713,11 +711,11 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_FIRST_SOURCE); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_FIRST_SOURCE); *fmt = format->format; return 0; diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index a8abcd0fee17e5..da4dcfc829b7cc 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -697,7 +697,7 @@ static int cal_video_check_format(struct cal_ctx *ctx) state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); - format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index); + format = v4l2_subdev_state_get_format(state, remote_pad->index); if (!format) { ret = -EINVAL; goto out; diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 2fe42aa9180049..7f46d0cdde6e77 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1948,8 +1948,7 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccdc->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccdc->formats[pad]; } @@ -1960,8 +1959,8 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&ccdc->subdev, sd_state, - CCDC_PAD_SOURCE_OF); + return v4l2_subdev_state_get_crop(sd_state, + CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index da5f0176ec7890..0517d410b22cfa 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -625,8 +625,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccp2->formats[pad]; } diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 0f9a54b11f983e..21270fc4e62365 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -834,8 +834,7 @@ __csi2_get_format(struct isp_csi2_device *csi2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index 53aedec7990da1..fe550021d930af 100644 --- a/drivers/media/platform/ti/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c @@ -1684,8 +1684,7 @@ __preview_get_format(struct isp_prev_device *prev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&prev->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &prev->formats[pad]; } @@ -1696,8 +1695,7 @@ __preview_get_crop(struct isp_prev_device *prev, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&prev->subdev, sd_state, - PREV_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, PREV_PAD_SINK); else return &prev->crop; } diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index ed2fb0c7a57e50..bbcb27508fab2a 100644 --- a/drivers/media/platform/ti/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c @@ -119,7 +119,7 @@ __resizer_get_format(struct isp_res_device *res, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&res->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &res->formats[pad]; } @@ -136,8 +136,7 @@ __resizer_get_crop(struct isp_res_device *res, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&res->subdev, sd_state, - RESZ_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RESZ_PAD_SINK); else return &res->crop.request; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index ea6d094e0844eb..9095c95c0a67a7 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -107,9 +107,9 @@ static int video_mux_link_setup(struct media_entity *entity, vmux->active = local->index; /* Propagate the active format to the source */ - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); - *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state, + *source_mbusformat = *v4l2_subdev_state_get_format(sd_state, vmux->active); source_sd = media_entity_to_v4l2_subdev(remote->entity); @@ -178,11 +178,11 @@ static int video_mux_set_format(struct v4l2_subdev *sd, struct media_pad *pad = &vmux->pads[sdformat->pad]; u16 source_pad = sd->entity.num_pads - 1; - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); if (!mbusformat) return -EINVAL; - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, source_pad); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); if (!source_mbusformat) return -EINVAL; @@ -292,7 +292,7 @@ static int video_mux_set_format(struct v4l2_subdev *sd, /* Source pad mirrors active sink pad, no limitations on sink pads */ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0) - sdformat->format = *v4l2_subdev_get_pad_format(sd, sd_state, + sdformat->format = *v4l2_subdev_state_get_format(sd_state, vmux->active); *mbusformat = sdformat->format; @@ -316,7 +316,7 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, mutex_lock(&vmux->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = video_mux_format_mbus_default; } diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 5b53745fe44eb8..3e94d4f0b62307 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -671,8 +671,7 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xcsi2rxss->format; default: @@ -699,7 +698,7 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, mutex_lock(&xcsi2rxss->lock); for (i = 0; i < XCSI_MEDIA_PADS; i++) { - format = v4l2_subdev_get_try_format(sd, sd_state, i); + format = v4l2_subdev_state_get_format(sd_state, i); *format = xcsi2rxss->default_format; } mutex_unlock(&xcsi2rxss->lock); diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 80353ca44402a5..e05e528ffc6f72 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -256,8 +256,7 @@ __xtpg_get_pad_format(struct xtpg_device *xtpg, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xtpg->formats[pad]; default: @@ -326,7 +325,7 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -354,11 +353,11 @@ static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); *format = xtpg->default_format; if (xtpg->npads == 2) { - format = v4l2_subdev_get_try_format(subdev, fh->state, 1); + format = v4l2_subdev_state_get_format(fh->state, 1); *format = xtpg->default_format; } diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index 5b214bf7f93a47..f1574edd2b43cd 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -260,7 +260,7 @@ int xvip_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); + format = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = format->code; @@ -295,7 +295,7 @@ int xvip_enum_frame_size(struct v4l2_subdev *subdev, if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index f671251fdf0e4a..a2fb32c97c8401 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -157,11 +157,11 @@ static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; unsigned int i; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = sink_fmt_default; mf->code = vdebayer->src_code; } @@ -221,7 +221,7 @@ static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, /* Get the current sink format */ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, 0) : + *v4l2_subdev_state_get_format(sd_state, 0) : vdebayer->sink_fmt; /* Set the right code for the source pad */ @@ -267,8 +267,8 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, sink_fmt = &vdebayer->sink_fmt; src_code = &vdebayer->src_code; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - src_code = &v4l2_subdev_get_try_format(sd, sd_state, 1)->code; + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; } /* diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index b671774e2784ab..f6592a789f1e7d 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -78,11 +78,11 @@ static int vimc_scaler_init_cfg(struct v4l2_subdev *sd, unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } - r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCALER_SINK); + r = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); *r = crop_rect_default; return 0; @@ -138,7 +138,7 @@ vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&vscaler->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &vscaler->fmt[pad]; } @@ -149,8 +149,7 @@ vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&vscaler->sd, sd_state, - VIMC_SCALER_SINK); + return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); else return &vscaler->crop_rect; } diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 41a3dce2d71471..676ab503f0ca11 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -49,7 +49,7 @@ static int vimc_sensor_init_cfg(struct v4l2_subdev *sd, for (i = 0; i < sd->entity.num_pads; i++) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } @@ -100,7 +100,7 @@ static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, container_of(sd, struct vimc_sensor_device, sd); fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : + *v4l2_subdev_state_get_format(sd_state, fmt->pad) : vsensor->mbus_format; return 0; @@ -159,7 +159,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, mf = &vsensor->mbus_format; } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); } /* Set the new format */ diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 9a11793f34f74b..de20ea26f838fa 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -362,7 +362,7 @@ gc0310_get_pad_format(struct gc0310_device *dev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&dev->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); return &dev->mode.fmt; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 5e438c5fd4a92e..9c20fe91523813 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -561,7 +561,7 @@ static int gc2235_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; mutex_unlock(&dev->input_lock); return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index db76f52e1dc8e4..8105365fbb2a50 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -666,7 +666,7 @@ static int mt9m114_set_fmt(struct v4l2_subdev *sd, fmt->height = res->height; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c index ae70e04040dd69..1de63c82cce15d 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c @@ -671,7 +671,7 @@ static int ov2722_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c index abf55a86f79578..89118438a3b679 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c @@ -29,8 +29,7 @@ v4l2_mbus_framefmt *__csi2_get_format(struct atomisp_mipi_csi2_device *csi2, unsigned int pad) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 45073e401bac48..7ad9e48424537f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -240,9 +240,9 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, if (which == V4L2_SUBDEV_FORMAT_TRY) { switch (target) { case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(sd, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } } @@ -264,7 +264,7 @@ struct v4l2_mbus_framefmt struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &isp_sd->fmt[pad].fmt; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c index b2376ebf45a166..92e61ee9099346 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c +++ b/drivers/staging/media/atomisp/pci/atomisp_tpg.c @@ -47,7 +47,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd, /* only raw8 grbg is supported by TPG */ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_pad_format(sd, sd_state, 0) = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } return 0; diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c index 24b5bb715611c4..df9e446b453088 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c @@ -824,8 +824,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { - struct v4l2_rect *try_crop = - v4l2_subdev_get_pad_crop(isc->current_subdev->sd, sd_state, 0); + struct v4l2_rect *try_crop = v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index ac5fb332088ea3..61d69f19657e39 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -82,10 +82,8 @@ static struct v4l2_mbus_framefmt * __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { - struct imx_ic_priv *ic_priv = priv->ic_priv; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus; } diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 9b81cfbcd77790..ec73c901079ed8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -790,10 +790,8 @@ static struct v4l2_mbus_framefmt * __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { - struct imx_ic_priv *ic_priv = priv->ic_priv; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index dda1ebc34692aa..33902f5daf80cf 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1148,7 +1148,7 @@ __csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } @@ -1158,8 +1158,7 @@ __csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&priv->sd, sd_state, - CSI_SINK_PAD); + return v4l2_subdev_state_get_crop(sd_state, CSI_SINK_PAD); else return &priv->crop; } @@ -1169,8 +1168,7 @@ __csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&priv->sd, sd_state, - CSI_SINK_PAD); + return v4l2_subdev_state_get_compose(sd_state, CSI_SINK_PAD); else return &priv->compose; } diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 064dc562bc96a0..ec4349ab48cda3 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -445,7 +445,7 @@ int imx_media_init_cfg(struct v4l2_subdev *sd, if (ret) continue; - mf_try = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf_try = v4l2_subdev_state_get_format(sd_state, pad); *mf_try = format.format; } diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 3c2093c520bab2..792bca1d2d2518 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -536,7 +536,7 @@ __vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index b2d8476d83a0af..6906220ee89ef0 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -501,7 +501,7 @@ __csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->format_mbus; } diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 55cc44a401bc43..9bb4fab2839636 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -36,7 +36,7 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ for (i = 0; i < IMGU_NODE_NUM; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, i); + v4l2_subdev_state_get_format(fh->state, i); try_fmt->width = try_crop.width; try_fmt->height = try_crop.height; @@ -44,8 +44,8 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->field = V4L2_FIELD_NONE; } - *v4l2_subdev_get_try_crop(sd, fh->state, IMGU_NODE_IN) = try_crop; - *v4l2_subdev_get_try_compose(sd, fh->state, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_state_get_crop(fh->state, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_state_get_compose(fh->state, IMGU_NODE_IN) = try_crop; return 0; } @@ -136,7 +136,7 @@ static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { fmt->format = imgu_pipe->nodes[pad].pad_fmt; } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf = v4l2_subdev_state_get_format(sd_state, pad); fmt->format = *mf; } @@ -161,7 +161,7 @@ static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, imgu_pipe = &imgu->imgu_pipe[pipe]; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mf = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf = v4l2_subdev_state_get_format(sd_state, pad); else mf = &imgu_pipe->nodes[pad].pad_fmt; @@ -194,7 +194,7 @@ imgu_subdev_get_crop(struct imgu_v4l2_subdev *sd, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sd->subdev, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); else return &sd->rect.eff; } @@ -205,7 +205,7 @@ imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&sd->subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); else return &sd->rect.bds; } diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 04ce0e7eb55784..c6a8381082c389 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -830,8 +830,7 @@ __csi2_get_format(struct iss_csi2_device *csi2, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csi2->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index 23f707cb336f53..ad013ed923e7be 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -180,8 +180,7 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipe->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &ipipe->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 5e7f25cd53acfd..5cafcd38438a48 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -361,8 +361,7 @@ __ipipeif_get_format(struct iss_ipipeif_device *ipipeif, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipeif->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &ipipeif->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index a5f8f9f1ab16d2..be26467ad65374 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -420,8 +420,7 @@ __resizer_get_format(struct iss_resizer_device *resizer, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&resizer->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &resizer->formats[pad]; } diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c index ccbb530aa2e236..b2ce0d4fae9572 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -262,7 +262,7 @@ static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &isp_dev->proc.lock; mutex_lock(lock); @@ -302,8 +302,8 @@ static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = isp_dev->proc.mbus_format; @@ -325,7 +325,7 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, sun6i_isp_proc_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else isp_dev->proc.mbus_format = *mbus_format; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index a2f21c70a5bc84..bc12a564e54f9c 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -474,7 +474,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, * Attempt to obtain the format size from subdev. * If not available, try to get crop boundary from subdev. */ - try_crop = v4l2_subdev_get_pad_crop(subdev, sd_state, 0); + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); fse.code = fmtinfo->code; ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse); if (ret) { From f5894bcfca50a920bd29b5bed774c829b12ea0f0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 13:35:33 +0200 Subject: [PATCH 076/159] media: saa6752hs: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit c692696fc51c5acee555b94d391d328510b557c8) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/saa6752hs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 51b62a97946a46..897eaa669b8622 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -594,10 +594,8 @@ static int saa6752hs_set_fmt(struct v4l2_subdev *sd, f->field = V4L2_FIELD_INTERLACED; f->colorspace = V4L2_COLORSPACE_SMPTE170M; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_state_get_format(sd_state, 0) = *f; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* FIXME: translate and round width/height into EMPRESS From b96b84f6db4c49694e2045a545160721efb7b650 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 19:20:19 +0200 Subject: [PATCH 077/159] media: adv7183: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit dff1eebf2be36278d2669504ad9249f1d7505bc0) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/adv7183.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index bcb99ba9a27201..2a2cace4a1538c 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -442,8 +442,6 @@ static int adv7183_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; - else - *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } From 18d9e87689693c0741ba762bd48e9bf2697530dc Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 19:22:03 +0200 Subject: [PATCH 078/159] media: mt9t112: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 72c8cb48a4cc05e8132725250b801326966c5c93) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/mt9t112.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 2e2d9853c08958..fb1588c57cc806 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -982,7 +982,6 @@ static int mt9t112_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } From 019abb1ed0986ef4f4232728b5a4c5f3caf8c11e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 19:22:43 +0200 Subject: [PATCH 079/159] media: rj54n1cb0c: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 09aee3995f9eea712b7b372421ce85b79d5c2adb) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/rj54n1cb0c.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index 403185b18f061f..a59db10153cdcb 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1008,10 +1008,8 @@ static int rj54n1_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_state_get_format(sd_state, 0) = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* * Verify if the sensor has just been powered on. TODO: replace this From 3df44db96c5c20e40918054c4c12a43e8d4940fa Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 19:24:11 +0200 Subject: [PATCH 080/159] media: tw9910: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 843750fb85fda6111c78e5c40e5ca5bc7dde6d10) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/tw9910.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 7c331a7f12d473..905af98c7d53cd 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -829,8 +829,6 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - *v4l2_subdev_state_get_format(sd_state, 0) = *mf; - return 0; } From beb33ef89de323ffb63c32610536b54b3e21ed86 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Dec 2023 19:25:07 +0200 Subject: [PATCH 081/159] media: ov9640: Don't set format in sub-device state For the purpose of setting old non-pad based sub-device try format as a basis for VIDIOC_TRY_FMT implementation, there is no need to set the format in the sub-device state. Drop the assignment to the state, which would result in a NULL pointer dereference. Fixes: fd17e3a9a788 ("media: i2c: Use accessors for pad config 'try_*' fields") Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit e55a9482888da73eeadde5f13ef8bafce68a38ed) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov9640.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index b0c171fe75bc08..e9a52a8a9dc0af 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -547,8 +547,6 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - *v4l2_subdev_state_get_format(sd_state, 0) = *mf; - return 0; } From 24f4874df9fee9e4f820637ada078a967e22e019 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:28 +0300 Subject: [PATCH 082/159] media: i2c: ov2740: Drop check for reentrant .s_stream() The subdev .s_stream() operation shall not be called to start streaming on an already started subdev, or stop streaming on a stopped subdev. Remove the check that guards against that condition. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 58e7ab2f381b06e9eb212fc9ba4d2f1cef4181b0) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov2740.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 41d4f85470fd21..59356dd9afd81b 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -794,9 +794,6 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; - if (ov2740->streaming == enable) - return 0; - mutex_lock(&ov2740->mutex); if (enable) { ret = pm_runtime_resume_and_get(&client->dev); From 11a40366bdbfbb497c07b7a1d70ee39fbaa7035a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:59 +0300 Subject: [PATCH 083/159] media: i2c: ov2740: Drop system suspend and resume handlers Stopping streaming on a camera pipeline at system suspend time, and restarting it at system resume time, requires coordinated action between the bridge driver and the camera sensor driver. This is handled by the bridge driver calling the sensor's .s_stream() handler at system suspend and resume time. There is thus no need for the sensor to independently implement system sleep PM operations. Drop them. The streaming field of the driver's private structure is now unused, drop it as well. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 55e941d06a761be4f769c1c2e1fff3664c78ab4a) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov2740.c | 42 -------------------------------------- 1 file changed, 42 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 59356dd9afd81b..3a9700fbbe8cb3 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -339,9 +339,6 @@ struct ov2740 { /* To serialize asynchronus callbacks */ struct mutex mutex; - /* Streaming on/off */ - bool streaming; - /* NVM data inforamtion */ struct nvm_data *nvm; @@ -813,47 +810,11 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(&client->dev); } - ov2740->streaming = enable; mutex_unlock(&ov2740->mutex); return ret; } -static int ov2740_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2740 *ov2740 = to_ov2740(sd); - - mutex_lock(&ov2740->mutex); - if (ov2740->streaming) - ov2740_stop_streaming(ov2740); - - mutex_unlock(&ov2740->mutex); - - return 0; -} - -static int ov2740_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2740 *ov2740 = to_ov2740(sd); - int ret = 0; - - mutex_lock(&ov2740->mutex); - if (!ov2740->streaming) - goto exit; - - ret = ov2740_start_streaming(ov2740); - if (ret) { - ov2740->streaming = false; - ov2740_stop_streaming(ov2740); - } - -exit: - mutex_unlock(&ov2740->mutex); - return ret; -} - static int ov2740_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1197,8 +1158,6 @@ static int ov2740_probe(struct i2c_client *client) return ret; } -static DEFINE_SIMPLE_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume); - static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} @@ -1209,7 +1168,6 @@ MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", - .pm = pm_sleep_ptr(&ov2740_pm_ops), .acpi_match_table = ov2740_acpi_ids, }, .probe = ov2740_probe, From 5c0ba164bede577b2fae7e248b8fefc38f4ee991 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 8 Sep 2023 11:52:12 +0300 Subject: [PATCH 084/159] media: ov2740: Enable runtime PM before registering the async subdev Enable runtime PM before registering the async subdev as the driver UAPI may become accessible immediately after the registration. Runtime PM needs to be enabled by that time. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 1cfe77a541a5044a061afd4f7935f504cc4d4c48) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov2740.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 3a9700fbbe8cb3..6d5fb2789dc6a0 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1130,6 +1130,12 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } + /* Set the device's state to active if it's in D0 state. */ + if (full_power) + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + ret = v4l2_async_register_subdev_sensor(&ov2740->sd); if (ret < 0) { dev_err_probe(dev, ret, "failed to register V4L2 subdev\n"); @@ -1140,16 +1146,12 @@ static int ov2740_probe(struct i2c_client *client) if (ret) dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret); - /* Set the device's state to active if it's in D0 state. */ - if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - return 0; probe_error_media_entity_cleanup: media_entity_cleanup(&ov2740->sd.entity); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); From fa22d7b3d2051e80dcdffdcf212a824bb99c7374 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Sep 2023 15:10:03 +0300 Subject: [PATCH 085/159] media: ov2740: Use sub-device active state Use sub-device active state. Rely on control handler lock to serialise access to the active state. Also clean up locking on s_stream handler. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 289c25923ecdde90050a0cb3904f9295ca68b425) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov2740.c | 109 ++++++++++++++----------------------- 1 file changed, 42 insertions(+), 67 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 6d5fb2789dc6a0..d83ac31efd9cb8 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -336,9 +336,6 @@ struct ov2740 { /* Current mode */ const struct ov2740_mode *cur_mode; - /* To serialize asynchronus callbacks */ - struct mutex mutex; - /* NVM data inforamtion */ struct nvm_data *nvm; @@ -579,7 +576,6 @@ static int ov2740_init_controls(struct ov2740 *ov2740) if (ret) return ret; - ctrl_hdlr->lock = &ov2740->mutex; cur_mode = ov2740->cur_mode; size = ARRAY_SIZE(link_freq_menu_items); @@ -789,15 +785,15 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) { struct ov2740 *ov2740 = to_ov2740(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *sd_state; int ret = 0; - mutex_lock(&ov2740->mutex); + sd_state = v4l2_subdev_lock_and_get_active_state(&ov2740->sd); + if (enable) { ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) { - mutex_unlock(&ov2740->mutex); - return ret; - } + if (ret < 0) + goto out_unlock; ret = ov2740_start_streaming(ov2740); if (ret) { @@ -810,7 +806,8 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(&client->dev); } - mutex_unlock(&ov2740->mutex); +out_unlock: + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -828,48 +825,26 @@ static int ov2740_set_format(struct v4l2_subdev *sd, height, fmt->format.width, fmt->format.height); - mutex_lock(&ov2740->mutex); ov2740_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; - } else { - ov2740->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(ov2740->pixel_rate, - to_pixel_rate(mode->link_freq_index)); - - /* Update limits and set FPS to default */ - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(ov2740->vblank, - mode->vts_min - mode->height, - OV2740_VTS_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); - h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - - mode->width; - __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, - h_blank); - } - mutex_unlock(&ov2740->mutex); + *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad) = fmt->format; - return 0; -} - -static int ov2740_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov2740 *ov2740 = to_ov2740(sd); - - mutex_lock(&ov2740->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov2740->sd, - sd_state, - fmt->pad); - else - ov2740_update_pad_format(ov2740->cur_mode, &fmt->format); + return 0; - mutex_unlock(&ov2740->mutex); + ov2740->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov2740->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov2740->vblank, + mode->vts_min - mode->height, + OV2740_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, h_blank); return 0; } @@ -904,14 +879,11 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int ov2740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ov2740_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { - struct ov2740 *ov2740 = to_ov2740(sd); - - mutex_lock(&ov2740->mutex); ov2740_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); - mutex_unlock(&ov2740->mutex); + v4l2_subdev_get_pad_format(sd, sd_state, 0)); return 0; } @@ -921,10 +893,11 @@ static const struct v4l2_subdev_video_ops ov2740_video_ops = { }; static const struct v4l2_subdev_pad_ops ov2740_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ov2740_set_format, - .get_fmt = ov2740_get_format, .enum_mbus_code = ov2740_enum_mbus_code, .enum_frame_size = ov2740_enum_frame_size, + .init_cfg = ov2740_init_cfg, }; static const struct v4l2_subdev_ops ov2740_subdev_ops = { @@ -936,10 +909,6 @@ static const struct media_entity_operations ov2740_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; -static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { - .open = ov2740_open, -}; - static int ov2740_check_hwcfg(struct device *dev) { struct fwnode_handle *ep; @@ -1005,13 +974,12 @@ static int ov2740_check_hwcfg(struct device *dev) static void ov2740_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2740 *ov2740 = to_ov2740(sd); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); + v4l2_subdev_cleanup(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - mutex_destroy(&ov2740->mutex); } static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, @@ -1020,9 +988,11 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, struct nvm_data *nvm = priv; struct device *dev = regmap_get_device(nvm->regmap); struct ov2740 *ov2740 = to_ov2740(dev_get_drvdata(dev)); + struct v4l2_subdev_state *sd_state; int ret = 0; - mutex_lock(&ov2740->mutex); + /* Serialise sensor access */ + sd_state = v4l2_subdev_lock_and_get_active_state(&ov2740->sd); if (nvm->nvm_buffer) { memcpy(val, nvm->nvm_buffer + off, count); @@ -1040,7 +1010,7 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, pm_runtime_put(dev); exit: - mutex_unlock(&ov2740->mutex); + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -1111,7 +1081,6 @@ static int ov2740_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "failed to find sensor\n"); } - mutex_init(&ov2740->mutex); ov2740->cur_mode = &supported_modes[0]; ret = ov2740_init_controls(ov2740); if (ret) { @@ -1119,7 +1088,7 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } - ov2740->sd.internal_ops = &ov2740_internal_ops; + ov2740->sd.state_lock = ov2740->ctrl_handler.lock; ov2740->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov2740->sd.entity.ops = &ov2740_subdev_entity_ops; ov2740->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1130,6 +1099,10 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } + ret = v4l2_subdev_init_finalize(&ov2740->sd); + if (ret) + goto probe_error_media_entity_cleanup; + /* Set the device's state to active if it's in D0 state. */ if (full_power) pm_runtime_set_active(&client->dev); @@ -1139,7 +1112,7 @@ static int ov2740_probe(struct i2c_client *client) ret = v4l2_async_register_subdev_sensor(&ov2740->sd); if (ret < 0) { dev_err_probe(dev, ret, "failed to register V4L2 subdev\n"); - goto probe_error_media_entity_cleanup; + goto probe_error_v4l2_subdev_cleanup; } ret = ov2740_register_nvmem(client, ov2740); @@ -1148,6 +1121,9 @@ static int ov2740_probe(struct i2c_client *client) return 0; +probe_error_v4l2_subdev_cleanup: + v4l2_subdev_cleanup(&ov2740->sd); + probe_error_media_entity_cleanup: media_entity_cleanup(&ov2740->sd.entity); pm_runtime_disable(&client->dev); @@ -1155,7 +1131,6 @@ static int ov2740_probe(struct i2c_client *client) probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); - mutex_destroy(&ov2740->mutex); return ret; } From f6f2ccb9455f707c7f386170515964ae749228df Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 8 Sep 2023 12:56:16 +0300 Subject: [PATCH 086/159] media: ov2740: Return -EPROBE_DEFER if no endpoint is found With ipu bridge, endpoints may only be created when ipu bridge has initialised. This may happen after the sensor driver has first probed. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil (cherry picked from commit 07d81b507df5622db40104c64dace3387bbe23b9) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov2740.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index d83ac31efd9cb8..24e468485fbf01 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -931,7 +931,7 @@ static int ov2740_check_hwcfg(struct device *dev) ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) - return -ENXIO; + return -EPROBE_DEFER; ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); From 2a1eaeceed209ad51c1957d6e1e4eb835af554fc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:36 +0300 Subject: [PATCH 087/159] media: i2c: ov9282: Drop check for reentrant .s_stream() The subdev .s_stream() operation shall not be called to start streaming on an already started subdev, or stop streaming on a stopped subdev. Remove the check that guards against that condition. The streaming field of the driver's private structure is now unused, drop it as well. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 36cb37a69ebd61d0e6acf1db05d2b17077014f1b) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/ov9282.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 8ef09b52fa22ac..a65aa8285e58b5 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -165,7 +165,6 @@ struct ov9282_mode { * @cur_mode: Pointer to current selected sensor mode * @code: Mbus code currently selected * @mutex: Mutex for serializing sensor controls - * @streaming: Flag indicating streaming state */ struct ov9282 { struct device *dev; @@ -188,7 +187,6 @@ struct ov9282 { const struct ov9282_mode *cur_mode; u32 code; struct mutex mutex; - bool streaming; }; static const s64 link_freq[] = { @@ -1037,11 +1035,6 @@ static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&ov9282->mutex); - if (ov9282->streaming == enable) { - mutex_unlock(&ov9282->mutex); - return 0; - } - if (enable) { ret = pm_runtime_resume_and_get(ov9282->dev); if (ret) @@ -1055,8 +1048,6 @@ static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(ov9282->dev); } - ov9282->streaming = enable; - mutex_unlock(&ov9282->mutex); return 0; From 8b569834f4d4eaf623a0cd842d1a65492c465419 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 27 Oct 2023 11:58:52 +0200 Subject: [PATCH 088/159] media: i2c: Fix references to pad config V4L2 subdev operations have moved from operating on a v4l2_subdev_pad_config to a v4l2_subdev_state a long time ago. Fix the few remaining incorrect references to pad config in the I2C drivers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit aeb18af18828f65eebb3dc475c62faab9cc8e9b6) Signed-off-by: Jacopo Mondi --- drivers/media/i2c/imx334.c | 8 ++++---- drivers/media/i2c/imx335.c | 8 ++++---- drivers/media/i2c/imx412.c | 8 ++++---- drivers/media/i2c/ov9282.c | 8 ++++---- drivers/media/i2c/tvp7002.c | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index b22746b773f83b..3d47c22aa3178c 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -887,14 +887,14 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, } /** - * imx334_init_pad_cfg() - Initialize sub-device pad configuration + * imx334_init_cfg() - Initialize sub-device state * @sd: pointer to imx334 V4L2 sub-device structure * @sd_state: V4L2 sub-device state * * Return: 0 if successful, error code otherwise. */ -static int imx334_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx334_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx334 *imx334 = to_imx334(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1149,7 +1149,7 @@ static const struct v4l2_subdev_video_ops imx334_video_ops = { }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { - .init_cfg = imx334_init_pad_cfg, + .init_cfg = imx334_init_cfg, .enum_mbus_code = imx334_enum_mbus_code, .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 6b670ebc0bd440..a90a815274fa09 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -629,14 +629,14 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, } /** - * imx335_init_pad_cfg() - Initialize sub-device pad configuration + * imx335_init_cfg() - Initialize sub-device state * @sd: pointer to imx335 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx335_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx335_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx335 *imx335 = to_imx335(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -864,7 +864,7 @@ static const struct v4l2_subdev_video_ops imx335_video_ops = { }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { - .init_cfg = imx335_init_pad_cfg, + .init_cfg = imx335_init_cfg, .enum_mbus_code = imx335_enum_mbus_code, .enum_frame_size = imx335_enum_frame_size, .get_fmt = imx335_get_pad_format, diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 75150e15730c28..4456c32c72f18f 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -771,14 +771,14 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, } /** - * imx412_init_pad_cfg() - Initialize sub-device pad configuration + * imx412_init_cfg() - Initialize sub-device state * @sd: pointer to imx412 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx412_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx412_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx412 *imx412 = to_imx412(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1005,7 +1005,7 @@ static const struct v4l2_subdev_video_ops imx412_video_ops = { }; static const struct v4l2_subdev_pad_ops imx412_pad_ops = { - .init_cfg = imx412_init_pad_cfg, + .init_cfg = imx412_init_cfg, .enum_mbus_code = imx412_enum_mbus_code, .enum_frame_size = imx412_enum_frame_size, .get_fmt = imx412_get_pad_format, diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index a65aa8285e58b5..8039f4e64bcfe7 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -876,14 +876,14 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, } /** - * ov9282_init_pad_cfg() - Initialize sub-device pad configuration + * ov9282_init_cfg() - Initialize sub-device state * @sd: pointer to ov9282 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov9282_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov9282 *ov9282 = to_ov9282(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1196,7 +1196,7 @@ static const struct v4l2_subdev_video_ops ov9282_video_ops = { }; static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { - .init_cfg = ov9282_init_pad_cfg, + .init_cfg = ov9282_init_cfg, .enum_mbus_code = ov9282_enum_mbus_code, .enum_frame_size = ov9282_enum_frame_size, .get_fmt = ov9282_get_pad_format, diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index a2d7bc79984930..30831b4b56d6b1 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -791,7 +791,7 @@ static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { /* * tvp7002_enum_mbus_code() - Enum supported digital video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to subdev enum mbus code struct * * Enumerate supported digital video formats for pad. @@ -813,7 +813,7 @@ tvp7002_enum_mbus_code(struct v4l2_subdev *sd, /* * tvp7002_get_pad_format() - get video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * get video format for pad. @@ -837,7 +837,7 @@ tvp7002_get_pad_format(struct v4l2_subdev *sd, /* * tvp7002_set_pad_format() - set video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * set video format for pad. From d2abb8e3d00a68703d4df61e22b0cadfe142be33 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Nov 2023 11:07:44 +0200 Subject: [PATCH 089/159] media: v4l2-subdev: Rename .init_cfg() operation to .init_state() The subdev .init_cfg() operation is affected by two issues: - It has long been extended to initialize a whole v4l2_subdev_state instead of just a v4l2_subdev_pad_config, but its name has stuck around. - Despite operating on a whole subdev state and not being directly exposed to the subdev users (either in-kernel or through the userspace API), .init_cfg() is categorized as a subdev pad operation. This participates in making the subdev API confusing for new developers. Fix it by renaming the operation to .init_state(), and make it a subdev internal operation. Signed-off-by: Laurent Pinchart Acked-by: Michael Riesch # for imx415 Acked-by: Shuah Khan # for vimc Reviewed-by: Philipp Zabel Reviewed-by: Tomi Valkeinen [Sakari Ailus: Resolved a conflict in Renesas vsp1 driver.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil (cherry picked from commit 5755be5f15d9e651ed433e5dbf7b7b968efb3064) Conflicts: drivers/media/i2c/gc2145.c drivers/media/i2c/imx219.c drivers/media/i2c/mt9m114.c drivers/media/i2c/mt9v111.c drivers/media/i2c/thp7312.c drivers/media/platform/cadence/cdns-csi2rx.c drivers/media/platform/renesas/vsp1/vsp1_entity.c drivers/media/platform/xilinx/xilinx-csi2rxss.c drivers/staging/media/starfive/camss/stf-isp.c Signed-off-by: Jacopo Mondi --- drivers/media/i2c/adv7180.c | 10 +- drivers/media/i2c/ccs/ccs-core.c | 6 +- drivers/media/i2c/ds90ub913.c | 10 +- drivers/media/i2c/ds90ub953.c | 10 +- drivers/media/i2c/ds90ub960.c | 11 +- drivers/media/i2c/gc2145.c | 1450 ++++++++++ drivers/media/i2c/hi846.c | 10 +- drivers/media/i2c/imx214.c | 12 +- drivers/media/i2c/imx219.c | 10 +- drivers/media/i2c/imx290.c | 10 +- drivers/media/i2c/imx296.c | 10 +- drivers/media/i2c/imx334.c | 12 +- drivers/media/i2c/imx335.c | 12 +- drivers/media/i2c/imx412.c | 12 +- drivers/media/i2c/imx415.c | 10 +- drivers/media/i2c/mt9m001.c | 10 +- drivers/media/i2c/mt9m111.c | 10 +- drivers/media/i2c/mt9m114.c | 2485 +++++++++++++++++ drivers/media/i2c/mt9p031.c | 8 +- drivers/media/i2c/mt9v111.c | 10 +- drivers/media/i2c/ov01a10.c | 10 +- drivers/media/i2c/ov02a10.c | 10 +- drivers/media/i2c/ov2640.c | 10 +- drivers/media/i2c/ov2680.c | 10 +- drivers/media/i2c/ov2740.c | 10 +- drivers/media/i2c/ov5640.c | 10 +- drivers/media/i2c/ov5645.c | 12 +- drivers/media/i2c/ov5670.c | 10 +- drivers/media/i2c/ov64a40.c | 10 +- drivers/media/i2c/ov7251.c | 12 +- drivers/media/i2c/ov8858.c | 10 +- drivers/media/i2c/ov9282.c | 12 +- drivers/media/i2c/st-vgxy61.c | 10 +- drivers/media/i2c/tc358746.c | 10 +- drivers/media/i2c/tda1997x.c | 10 +- drivers/media/i2c/thp7312.c | 2244 +++++++++++++++ drivers/media/i2c/tvp5150.c | 6 +- drivers/media/pci/intel/ivsc/mei_csi.c | 10 +- drivers/media/platform/cadence/cdns-csi2rx.c | 10 +- .../platform/microchip/microchip-csi2dc.c | 10 +- .../platform/microchip/microchip-isc-scaler.c | 10 +- drivers/media/platform/nxp/imx-mipi-csis.c | 10 +- drivers/media/platform/nxp/imx7-media-csi.c | 6 +- .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 10 +- .../platform/nxp/imx8-isi/imx8-isi-pipe.c | 10 +- drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 10 +- .../media/platform/raspberrypi/rp1_cfe/csi2.c | 10 +- .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 11 +- .../platform/renesas/rzg2l-cru/rzg2l-csi2.c | 10 +- .../platform/renesas/rzg2l-cru/rzg2l-ip.c | 10 +- .../media/platform/renesas/vsp1/vsp1_brx.c | 1 - .../media/platform/renesas/vsp1/vsp1_clu.c | 1 - .../media/platform/renesas/vsp1/vsp1_entity.c | 62 +- .../media/platform/renesas/vsp1/vsp1_entity.h | 2 - .../media/platform/renesas/vsp1/vsp1_hsit.c | 1 - .../media/platform/renesas/vsp1/vsp1_lif.c | 1 - .../media/platform/renesas/vsp1/vsp1_lut.c | 1 - .../media/platform/renesas/vsp1/vsp1_rwpf.c | 1 - .../media/platform/renesas/vsp1/vsp1_sru.c | 1 - .../media/platform/renesas/vsp1/vsp1_uds.c | 1 - .../media/platform/renesas/vsp1/vsp1_uif.c | 1 - .../platform/rockchip/rkisp1/rkisp1-csi.c | 10 +- .../platform/rockchip/rkisp1/rkisp1-isp.c | 10 +- .../platform/rockchip/rkisp1/rkisp1-resizer.c | 10 +- .../platform/sunxi/sun4i-csi/sun4i_csi.c | 1 + .../platform/sunxi/sun4i-csi/sun4i_csi.h | 1 + .../platform/sunxi/sun4i-csi/sun4i_v4l2.c | 9 +- .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 10 +- .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c | 10 +- .../sun8i_a83t_mipi_csi2.c | 10 +- drivers/media/platform/ti/cal/cal-camerarx.c | 10 +- drivers/media/platform/video-mux.c | 10 +- .../media/platform/xilinx/xilinx-csi2rxss.c | 20 +- .../media/test-drivers/vimc/vimc-debayer.c | 11 +- drivers/media/test-drivers/vimc/vimc-scaler.c | 11 +- drivers/media/test-drivers/vimc/vimc-sensor.c | 11 +- drivers/media/v4l2-core/v4l2-subdev.c | 20 +- drivers/staging/media/imx/imx-ic-prp.c | 2 +- drivers/staging/media/imx/imx-ic-prpencvf.c | 2 +- drivers/staging/media/imx/imx-media-csi.c | 2 +- drivers/staging/media/imx/imx-media-utils.c | 8 +- drivers/staging/media/imx/imx-media-vdic.c | 2 +- drivers/staging/media/imx/imx-media.h | 4 +- drivers/staging/media/imx/imx6-mipi-csi2.c | 2 +- .../staging/media/starfive/camss/stf-isp.c | 385 +++ .../media/sunxi/sun6i-isp/sun6i_isp_proc.c | 10 +- include/media/v4l2-subdev.h | 7 +- 87 files changed, 7049 insertions(+), 263 deletions(-) create mode 100644 drivers/media/i2c/gc2145.c create mode 100644 drivers/media/i2c/mt9m114.c create mode 100644 drivers/media/i2c/thp7312.c create mode 100644 drivers/staging/media/starfive/camss/stf-isp.c diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 66dd94773c4e9c..3a62a72488200b 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -874,8 +874,8 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, return ret; } -static int adv7180_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int adv7180_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -992,7 +992,6 @@ static const struct v4l2_subdev_core_ops adv7180_core_ops = { }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { - .init_cfg = adv7180_init_cfg, .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, @@ -1010,6 +1009,10 @@ static const struct v4l2_subdev_ops adv7180_ops = { .sensor = &adv7180_sensor_ops, }; +static const struct v4l2_subdev_internal_ops adv7180_internal_ops = { + .init_state = adv7180_init_state, +}; + static irqreturn_t adv7180_irq(int irq, void *devid) { struct adv7180_state *state = devid; @@ -1536,6 +1539,7 @@ static int adv7180_probe(struct i2c_client *client) state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + sd->internal_ops = &adv7180_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ret = adv7180_init_controls(state); diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index a2c3c572a0a44d..e27cd8b296b7ab 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3006,8 +3006,8 @@ static int ccs_init_subdev(struct ccs_sensor *sensor, return 0; } -static int ccs_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ccs_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ccs_subdev *ssd = to_ccs_subdev(sd); struct ccs_sensor *sensor = ssd->sensor; @@ -3055,7 +3055,6 @@ static const struct v4l2_subdev_video_ops ccs_video_ops = { }; static const struct v4l2_subdev_pad_ops ccs_pad_ops = { - .init_cfg = ccs_init_cfg, .enum_mbus_code = ccs_enum_mbus_code, .get_fmt = ccs_get_format, .set_fmt = ccs_set_format, @@ -3079,6 +3078,7 @@ static const struct media_entity_operations ccs_entity_ops = { }; static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { + .init_state = ccs_init_state, .registered = ccs_registered, .unregistered = ccs_unregistered, }; diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 8bb6be95678027..ca9bb29dab890c 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -443,8 +443,8 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub913_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub913_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -503,7 +503,6 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = { .get_frame_desc = ub913_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub913_set_fmt, - .init_cfg = ub913_init_cfg, }; static const struct v4l2_subdev_ops ub913_subdev_ops = { @@ -511,6 +510,10 @@ static const struct v4l2_subdev_ops ub913_subdev_ops = { .pad = &ub913_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub913_internal_ops = { + .init_state = ub913_init_state, +}; + static const struct media_entity_operations ub913_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -744,6 +747,7 @@ static int ub913_subdev_init(struct ub913_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); + priv->sd.internal_ops = &ub913_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; priv->sd.entity.ops = &ub913_entity_ops; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 4eb08e3a31c72b..16f88db1498162 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -575,8 +575,8 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub953_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub953_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -713,7 +713,6 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = { .get_frame_desc = ub953_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub953_set_fmt, - .init_cfg = ub953_init_cfg, }; static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = { @@ -727,6 +726,10 @@ static const struct v4l2_subdev_ops ub953_subdev_ops = { .pad = &ub953_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub953_internal_ops = { + .init_state = ub953_init_state, +}; + static const struct media_entity_operations ub953_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1240,6 +1243,7 @@ static int ub953_subdev_init(struct ub953_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + priv->sd.internal_ops = &ub953_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 1d1476098c92f6..ffe5f25f864762 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2906,8 +2906,8 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub960_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub960_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ub960_data *priv = sd_to_ub960(sd); @@ -2938,8 +2938,6 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub960_set_fmt, - - .init_cfg = ub960_init_cfg, }; static int ub960_log_status(struct v4l2_subdev *sd) @@ -3091,6 +3089,10 @@ static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; +static const struct v4l2_subdev_internal_ops ub960_internal_ops = { + .init_state = ub960_init_state, +}; + static const struct v4l2_subdev_ops ub960_subdev_ops = { .core = &ub960_subdev_core_ops, .pad = &ub960_pad_ops, @@ -3650,6 +3652,7 @@ static int ub960_create_subdev(struct ub960_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + priv->sd.internal_ops = &ub960_internal_ops; v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); priv->sd.ctrl_handler = &priv->ctrl_handler; diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c new file mode 100644 index 00000000000000..bef7b0e056a8d9 --- /dev/null +++ b/drivers/media/i2c/gc2145.c @@ -0,0 +1,1450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Galaxycore GC2145 camera. + * Copyright (C) 2023, STMicroelectronics SA + * + * Inspired by the imx219.c driver + * + * Datasheet v1.0 available at http://files.pine64.org/doc/datasheet/PinebookPro/GC2145%20CSP%20DataSheet%20release%20V1.0_20131201.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Chip ID */ +#define GC2145_CHIP_ID 0x2145 + +/* Page 0 */ +#define GC2145_REG_EXPOSURE CCI_REG16(0x03) +#define GC2145_REG_HBLANK CCI_REG16(0x05) +#define GC2145_REG_VBLANK CCI_REG16(0x07) +#define GC2145_REG_ROW_START CCI_REG16(0x09) +#define GC2145_REG_COL_START CCI_REG16(0x0b) +#define GC2145_REG_WIN_HEIGHT CCI_REG16(0x0d) +#define GC2145_REG_WIN_WIDTH CCI_REG16(0x0f) +#define GC2145_REG_ANALOG_MODE1 CCI_REG8(0x17) +#define GC2145_REG_OUTPUT_FMT CCI_REG8(0x84) +#define GC2145_REG_SYNC_MODE CCI_REG8(0x86) +#define GC2145_SYNC_MODE_COL_SWITCH BIT(4) +#define GC2145_SYNC_MODE_ROW_SWITCH BIT(5) +#define GC2145_REG_BYPASS_MODE CCI_REG8(0x89) +#define GC2145_BYPASS_MODE_SWITCH BIT(5) +#define GC2145_REG_DEBUG_MODE2 CCI_REG8(0x8c) +#define GC2145_REG_DEBUG_MODE3 CCI_REG8(0x8d) +#define GC2145_REG_CROP_ENABLE CCI_REG8(0x90) +#define GC2145_REG_CROP_Y CCI_REG16(0x91) +#define GC2145_REG_CROP_X CCI_REG16(0x93) +#define GC2145_REG_CROP_HEIGHT CCI_REG16(0x95) +#define GC2145_REG_CROP_WIDTH CCI_REG16(0x97) +#define GC2145_REG_GLOBAL_GAIN CCI_REG8(0xb0) +#define GC2145_REG_CHIP_ID CCI_REG16(0xf0) +#define GC2145_REG_PAD_IO CCI_REG8(0xf2) +#define GC2145_REG_PAGE_SELECT CCI_REG8(0xfe) +/* Page 3 */ +#define GC2145_REG_DPHY_ANALOG_MODE1 CCI_REG8(0x01) +#define GC2145_DPHY_MODE_PHY_CLK_EN BIT(0) +#define GC2145_DPHY_MODE_PHY_LANE0_EN BIT(1) +#define GC2145_DPHY_MODE_PHY_LANE1_EN BIT(2) +#define GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL BIT(7) +#define GC2145_REG_DPHY_ANALOG_MODE2 CCI_REG8(0x02) +#define GC2145_DPHY_CLK_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_LANE0_DIFF(a) (((a) & 0x07) << 4) +#define GC2145_REG_DPHY_ANALOG_MODE3 CCI_REG8(0x03) +#define GC2145_DPHY_LANE1_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_CLK_DELAY BIT(4) +#define GC2145_DPHY_LANE0_DELAY BIT(5) +#define GC2145_DPHY_LANE1_DELAY BIT(6) +#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) +#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) +#define GC2145_REG_FIFO_MODE CCI_REG8(0x06) +#define GC2145_FIFO_MODE_READ_GATE BIT(3) +#define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) +#define GC2145_REG_BUF_CSI2_MODE CCI_REG8(0x10) +#define GC2145_CSI2_MODE_DOUBLE BIT(0) +#define GC2145_CSI2_MODE_RAW8 BIT(2) +#define GC2145_CSI2_MODE_MIPI_EN BIT(4) +#define GC2145_CSI2_MODE_EN BIT(7) +#define GC2145_REG_MIPI_DT CCI_REG8(0x11) +#define GC2145_REG_LWC_LOW CCI_REG8(0x12) +#define GC2145_REG_LWC_HIGH CCI_REG8(0x13) +#define GC2145_REG_DPHY_MODE CCI_REG8(0x15) +#define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) +#define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) +#define GC2145_REG_T_LPX CCI_REG8(0x21) +#define GC2145_REG_T_CLK_HS_PREPARE CCI_REG8(0x22) +#define GC2145_REG_T_CLK_ZERO CCI_REG8(0x23) +#define GC2145_REG_T_CLK_PRE CCI_REG8(0x24) +#define GC2145_REG_T_CLK_POST CCI_REG8(0x25) +#define GC2145_REG_T_CLK_TRAIL CCI_REG8(0x26) +#define GC2145_REG_T_HS_EXIT CCI_REG8(0x27) +#define GC2145_REG_T_WAKEUP CCI_REG8(0x28) +#define GC2145_REG_T_HS_PREPARE CCI_REG8(0x29) +#define GC2145_REG_T_HS_ZERO CCI_REG8(0x2a) +#define GC2145_REG_T_HS_TRAIL CCI_REG8(0x2b) + +/* External clock frequency is 24.0MHz */ +#define GC2145_XCLK_FREQ (24 * HZ_PER_MHZ) + +#define GC2145_NATIVE_WIDTH 1616U +#define GC2145_NATIVE_HEIGHT 1232U + +/** + * struct gc2145_mode - GC2145 mode description + * @width: frame width (in pixels) + * @height: frame height (in pixels) + * @reg_seq: registers config sequence to enter into the mode + * @reg_seq_size: size of the sequence + * @pixel_rate: pixel rate associated with the mode + * @crop: window area captured + * @hblank: default horizontal blanking + * @vblank: default vertical blanking + * @link_freq_index: index within the link frequency menu + */ +struct gc2145_mode { + unsigned int width; + unsigned int height; + const struct cci_reg_sequence *reg_seq; + size_t reg_seq_size; + unsigned long pixel_rate; + struct v4l2_rect crop; + unsigned int hblank; + unsigned int vblank; + unsigned int link_freq_index; +}; + +#define GC2145_DEFAULT_EXPOSURE 0x04e2 +#define GC2145_DEFAULT_GLOBAL_GAIN 0x55 +static const struct cci_reg_sequence gc2145_common_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x00}, + /* SH Delay */ + {CCI_REG8(0x12), 0x2e}, + /* Flip */ + {GC2145_REG_ANALOG_MODE1, 0x14}, + /* Analog Conf */ + {CCI_REG8(0x18), 0x22}, {CCI_REG8(0x19), 0x0e}, {CCI_REG8(0x1a), 0x01}, + {CCI_REG8(0x1b), 0x4b}, {CCI_REG8(0x1c), 0x07}, {CCI_REG8(0x1d), 0x10}, + {CCI_REG8(0x1e), 0x88}, {CCI_REG8(0x1f), 0x78}, {CCI_REG8(0x20), 0x03}, + {CCI_REG8(0x21), 0x40}, {CCI_REG8(0x22), 0xa0}, {CCI_REG8(0x24), 0x16}, + {CCI_REG8(0x25), 0x01}, {CCI_REG8(0x26), 0x10}, {CCI_REG8(0x2d), 0x60}, + {CCI_REG8(0x30), 0x01}, {CCI_REG8(0x31), 0x90}, {CCI_REG8(0x33), 0x06}, + {CCI_REG8(0x34), 0x01}, + /* ISP related */ + {CCI_REG8(0x80), 0x7f}, {CCI_REG8(0x81), 0x26}, {CCI_REG8(0x82), 0xfa}, + {CCI_REG8(0x83), 0x00}, {CCI_REG8(0x84), 0x02}, {CCI_REG8(0x86), 0x02}, + {CCI_REG8(0x88), 0x03}, + {GC2145_REG_BYPASS_MODE, 0x03}, + {CCI_REG8(0x85), 0x08}, {CCI_REG8(0x8a), 0x00}, {CCI_REG8(0x8b), 0x00}, + {GC2145_REG_GLOBAL_GAIN, GC2145_DEFAULT_GLOBAL_GAIN}, + {CCI_REG8(0xc3), 0x00}, {CCI_REG8(0xc4), 0x80}, {CCI_REG8(0xc5), 0x90}, + {CCI_REG8(0xc6), 0x3b}, {CCI_REG8(0xc7), 0x46}, + /* BLK */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0x40), 0x42}, {CCI_REG8(0x41), 0x00}, {CCI_REG8(0x43), 0x5b}, + {CCI_REG8(0x5e), 0x00}, {CCI_REG8(0x5f), 0x00}, {CCI_REG8(0x60), 0x00}, + {CCI_REG8(0x61), 0x00}, {CCI_REG8(0x62), 0x00}, {CCI_REG8(0x63), 0x00}, + {CCI_REG8(0x64), 0x00}, {CCI_REG8(0x65), 0x00}, {CCI_REG8(0x66), 0x20}, + {CCI_REG8(0x67), 0x20}, {CCI_REG8(0x68), 0x20}, {CCI_REG8(0x69), 0x20}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x6a), 0x08}, {CCI_REG8(0x6b), 0x08}, + {CCI_REG8(0x6c), 0x08}, {CCI_REG8(0x6d), 0x08}, {CCI_REG8(0x6e), 0x08}, + {CCI_REG8(0x6f), 0x08}, {CCI_REG8(0x70), 0x08}, {CCI_REG8(0x71), 0x08}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x72), 0xf0}, {CCI_REG8(0x7e), 0x3c}, + {CCI_REG8(0x7f), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x48), 0x15}, {CCI_REG8(0x49), 0x00}, {CCI_REG8(0x4b), 0x0b}, + /* AEC */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {GC2145_REG_EXPOSURE, GC2145_DEFAULT_EXPOSURE}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x01), 0x04}, {CCI_REG8(0x02), 0xc0}, {CCI_REG8(0x03), 0x04}, + {CCI_REG8(0x04), 0x90}, {CCI_REG8(0x05), 0x30}, {CCI_REG8(0x06), 0x90}, + {CCI_REG8(0x07), 0x30}, {CCI_REG8(0x08), 0x80}, {CCI_REG8(0x09), 0x00}, + {CCI_REG8(0x0a), 0x82}, {CCI_REG8(0x0b), 0x11}, {CCI_REG8(0x0c), 0x10}, + {CCI_REG8(0x11), 0x10}, {CCI_REG8(0x13), 0x7b}, {CCI_REG8(0x17), 0x00}, + {CCI_REG8(0x1c), 0x11}, {CCI_REG8(0x1e), 0x61}, {CCI_REG8(0x1f), 0x35}, + {CCI_REG8(0x20), 0x40}, {CCI_REG8(0x22), 0x40}, {CCI_REG8(0x23), 0x20}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x0f), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x12), 0x35}, {CCI_REG8(0x15), 0xb0}, {CCI_REG8(0x10), 0x31}, + {CCI_REG8(0x3e), 0x28}, {CCI_REG8(0x3f), 0xb0}, {CCI_REG8(0x40), 0x90}, + {CCI_REG8(0x41), 0x0f}, + /* INTPEE */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x90), 0x6c}, {CCI_REG8(0x91), 0x03}, {CCI_REG8(0x92), 0xcb}, + {CCI_REG8(0x94), 0x33}, {CCI_REG8(0x95), 0x84}, {CCI_REG8(0x97), 0x65}, + {CCI_REG8(0xa2), 0x11}, + /* DNDD */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x80), 0xc1}, {CCI_REG8(0x81), 0x08}, {CCI_REG8(0x82), 0x05}, + {CCI_REG8(0x83), 0x08}, {CCI_REG8(0x84), 0x0a}, {CCI_REG8(0x86), 0xf0}, + {CCI_REG8(0x87), 0x50}, {CCI_REG8(0x88), 0x15}, {CCI_REG8(0x89), 0xb0}, + {CCI_REG8(0x8a), 0x30}, {CCI_REG8(0x8b), 0x10}, + /* ASDE */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x21), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xa3), 0x50}, {CCI_REG8(0xa4), 0x20}, {CCI_REG8(0xa5), 0x40}, + {CCI_REG8(0xa6), 0x80}, {CCI_REG8(0xab), 0x40}, {CCI_REG8(0xae), 0x0c}, + {CCI_REG8(0xb3), 0x46}, {CCI_REG8(0xb4), 0x64}, {CCI_REG8(0xb6), 0x38}, + {CCI_REG8(0xb7), 0x01}, {CCI_REG8(0xb9), 0x2b}, {CCI_REG8(0x3c), 0x04}, + {CCI_REG8(0x3d), 0x15}, {CCI_REG8(0x4b), 0x06}, {CCI_REG8(0x4c), 0x20}, + /* Gamma */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x10), 0x09}, {CCI_REG8(0x11), 0x0d}, {CCI_REG8(0x12), 0x13}, + {CCI_REG8(0x13), 0x19}, {CCI_REG8(0x14), 0x27}, {CCI_REG8(0x15), 0x37}, + {CCI_REG8(0x16), 0x45}, {CCI_REG8(0x17), 0x53}, {CCI_REG8(0x18), 0x69}, + {CCI_REG8(0x19), 0x7d}, {CCI_REG8(0x1a), 0x8f}, {CCI_REG8(0x1b), 0x9d}, + {CCI_REG8(0x1c), 0xa9}, {CCI_REG8(0x1d), 0xbd}, {CCI_REG8(0x1e), 0xcd}, + {CCI_REG8(0x1f), 0xd9}, {CCI_REG8(0x20), 0xe3}, {CCI_REG8(0x21), 0xea}, + {CCI_REG8(0x22), 0xef}, {CCI_REG8(0x23), 0xf5}, {CCI_REG8(0x24), 0xf9}, + {CCI_REG8(0x25), 0xff}, + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xc6), 0x20}, {CCI_REG8(0xc7), 0x2b}, + /* Gamma 2 */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x26), 0x0f}, {CCI_REG8(0x27), 0x14}, {CCI_REG8(0x28), 0x19}, + {CCI_REG8(0x29), 0x1e}, {CCI_REG8(0x2a), 0x27}, {CCI_REG8(0x2b), 0x33}, + {CCI_REG8(0x2c), 0x3b}, {CCI_REG8(0x2d), 0x45}, {CCI_REG8(0x2e), 0x59}, + {CCI_REG8(0x2f), 0x69}, {CCI_REG8(0x30), 0x7c}, {CCI_REG8(0x31), 0x89}, + {CCI_REG8(0x32), 0x98}, {CCI_REG8(0x33), 0xae}, {CCI_REG8(0x34), 0xc0}, + {CCI_REG8(0x35), 0xcf}, {CCI_REG8(0x36), 0xda}, {CCI_REG8(0x37), 0xe2}, + {CCI_REG8(0x38), 0xe9}, {CCI_REG8(0x39), 0xf3}, {CCI_REG8(0x3a), 0xf9}, + {CCI_REG8(0x3b), 0xff}, + /* YCP */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xd1), 0x32}, {CCI_REG8(0xd2), 0x32}, {CCI_REG8(0xd3), 0x40}, + {CCI_REG8(0xd6), 0xf0}, {CCI_REG8(0xd7), 0x10}, {CCI_REG8(0xd8), 0xda}, + {CCI_REG8(0xdd), 0x14}, {CCI_REG8(0xde), 0x86}, {CCI_REG8(0xed), 0x80}, + {CCI_REG8(0xee), 0x00}, {CCI_REG8(0xef), 0x3f}, {CCI_REG8(0xd8), 0xd8}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* LSC */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xc2), 0x14}, {CCI_REG8(0xc3), 0x0d}, {CCI_REG8(0xc4), 0x0c}, + {CCI_REG8(0xc8), 0x15}, {CCI_REG8(0xc9), 0x0d}, {CCI_REG8(0xca), 0x0a}, + {CCI_REG8(0xbc), 0x24}, {CCI_REG8(0xbd), 0x10}, {CCI_REG8(0xbe), 0x0b}, + {CCI_REG8(0xb6), 0x25}, {CCI_REG8(0xb7), 0x16}, {CCI_REG8(0xb8), 0x15}, + {CCI_REG8(0xc5), 0x00}, {CCI_REG8(0xc6), 0x00}, {CCI_REG8(0xc7), 0x00}, + {CCI_REG8(0xcb), 0x00}, {CCI_REG8(0xcc), 0x00}, {CCI_REG8(0xcd), 0x00}, + {CCI_REG8(0xbf), 0x07}, {CCI_REG8(0xc0), 0x00}, {CCI_REG8(0xc1), 0x00}, + {CCI_REG8(0xb9), 0x00}, {CCI_REG8(0xba), 0x00}, {CCI_REG8(0xbb), 0x00}, + {CCI_REG8(0xaa), 0x01}, {CCI_REG8(0xab), 0x01}, {CCI_REG8(0xac), 0x00}, + {CCI_REG8(0xad), 0x05}, {CCI_REG8(0xae), 0x06}, {CCI_REG8(0xaf), 0x0e}, + {CCI_REG8(0xb0), 0x0b}, {CCI_REG8(0xb1), 0x07}, {CCI_REG8(0xb2), 0x06}, + {CCI_REG8(0xb3), 0x17}, {CCI_REG8(0xb4), 0x0e}, {CCI_REG8(0xb5), 0x0e}, + {CCI_REG8(0xd0), 0x09}, {CCI_REG8(0xd1), 0x00}, {CCI_REG8(0xd2), 0x00}, + {CCI_REG8(0xd6), 0x08}, {CCI_REG8(0xd7), 0x00}, {CCI_REG8(0xd8), 0x00}, + {CCI_REG8(0xd9), 0x00}, {CCI_REG8(0xda), 0x00}, {CCI_REG8(0xdb), 0x00}, + {CCI_REG8(0xd3), 0x0a}, {CCI_REG8(0xd4), 0x00}, {CCI_REG8(0xd5), 0x00}, + {CCI_REG8(0xa4), 0x00}, {CCI_REG8(0xa5), 0x00}, {CCI_REG8(0xa6), 0x77}, + {CCI_REG8(0xa7), 0x77}, {CCI_REG8(0xa8), 0x77}, {CCI_REG8(0xa9), 0x77}, + {CCI_REG8(0xa1), 0x80}, {CCI_REG8(0xa2), 0x80}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xdf), 0x0d}, {CCI_REG8(0xdc), 0x25}, {CCI_REG8(0xdd), 0x30}, + {CCI_REG8(0xe0), 0x77}, {CCI_REG8(0xe1), 0x80}, {CCI_REG8(0xe2), 0x77}, + {CCI_REG8(0xe3), 0x90}, {CCI_REG8(0xe6), 0x90}, {CCI_REG8(0xe7), 0xa0}, + {CCI_REG8(0xe8), 0x90}, {CCI_REG8(0xe9), 0xa0}, + /* AWB */ + /* measure window */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xec), 0x06}, {CCI_REG8(0xed), 0x04}, {CCI_REG8(0xee), 0x60}, + {CCI_REG8(0xef), 0x90}, {CCI_REG8(0xb6), 0x01}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4b), 0x01}, + {CCI_REG8(0x4f), 0x00}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x71}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x91}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x70}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x90}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xb0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xd0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xf0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xef}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xae}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xce}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xad}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcd}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xac}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcc}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcb}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xab}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaa}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0b}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xeb}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xea}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x29}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x2a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x4a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x49}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x48}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x68}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x07}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4f), 0x01}, + {CCI_REG8(0x50), 0x80}, {CCI_REG8(0x51), 0xa8}, {CCI_REG8(0x52), 0x47}, + {CCI_REG8(0x53), 0x38}, {CCI_REG8(0x54), 0xc7}, {CCI_REG8(0x56), 0x0e}, + {CCI_REG8(0x58), 0x08}, {CCI_REG8(0x5b), 0x00}, {CCI_REG8(0x5c), 0x74}, + {CCI_REG8(0x5d), 0x8b}, {CCI_REG8(0x61), 0xdb}, {CCI_REG8(0x62), 0xb8}, + {CCI_REG8(0x63), 0x86}, {CCI_REG8(0x64), 0xc0}, {CCI_REG8(0x65), 0x04}, + {CCI_REG8(0x67), 0xa8}, {CCI_REG8(0x68), 0xb0}, {CCI_REG8(0x69), 0x00}, + {CCI_REG8(0x6a), 0xa8}, {CCI_REG8(0x6b), 0xb0}, {CCI_REG8(0x6c), 0xaf}, + {CCI_REG8(0x6d), 0x8b}, {CCI_REG8(0x6e), 0x50}, {CCI_REG8(0x6f), 0x18}, + {CCI_REG8(0x73), 0xf0}, {CCI_REG8(0x70), 0x0d}, {CCI_REG8(0x71), 0x60}, + {CCI_REG8(0x72), 0x80}, {CCI_REG8(0x74), 0x01}, {CCI_REG8(0x75), 0x01}, + {CCI_REG8(0x7f), 0x0c}, {CCI_REG8(0x76), 0x70}, {CCI_REG8(0x77), 0x58}, + {CCI_REG8(0x78), 0xa0}, {CCI_REG8(0x79), 0x5e}, {CCI_REG8(0x7a), 0x54}, + {CCI_REG8(0x7b), 0x58}, + /* CC */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xc0), 0x01}, {CCI_REG8(0xc1), 0x44}, {CCI_REG8(0xc2), 0xfd}, + {CCI_REG8(0xc3), 0x04}, {CCI_REG8(0xc4), 0xf0}, {CCI_REG8(0xc5), 0x48}, + {CCI_REG8(0xc6), 0xfd}, {CCI_REG8(0xc7), 0x46}, {CCI_REG8(0xc8), 0xfd}, + {CCI_REG8(0xc9), 0x02}, {CCI_REG8(0xca), 0xe0}, {CCI_REG8(0xcb), 0x45}, + {CCI_REG8(0xcc), 0xec}, {CCI_REG8(0xcd), 0x48}, {CCI_REG8(0xce), 0xf0}, + {CCI_REG8(0xcf), 0xf0}, {CCI_REG8(0xe3), 0x0c}, {CCI_REG8(0xe4), 0x4b}, + {CCI_REG8(0xe5), 0xe0}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* Dark sun */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x40), 0xbf}, {CCI_REG8(0x46), 0xcf}, +}; + +#define GC2145_640_480_PIXELRATE 30000000 +#define GC2145_640_480_LINKFREQ 120000000 +#define GC2145_640_480_HBLANK 0x0130 +#define GC2145_640_480_VBLANK 0x000c +static const struct cci_reg_sequence gc2145_mode_640_480_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x86}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Scalar more */ + {CCI_REG8(0xfd), 0x01}, {CCI_REG8(0xfa), 0x00}, + /* Crop 640-480@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x01e0}, + {GC2145_REG_CROP_WIDTH, 0x0280}, + /* Subsampling configuration */ + {CCI_REG8(0x99), 0x55}, {CCI_REG8(0x9a), 0x06}, {CCI_REG8(0x9b), 0x01}, + {CCI_REG8(0x9c), 0x23}, {CCI_REG8(0x9d), 0x00}, {CCI_REG8(0x9e), 0x00}, + {CCI_REG8(0x9f), 0x01}, {CCI_REG8(0xa0), 0x23}, {CCI_REG8(0xa1), 0x00}, + {CCI_REG8(0xa2), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x0175}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x045f}, {CCI_REG16(0x29), 0x045f}, + {CCI_REG16(0x2b), 0x045f}, {CCI_REG16(0x2d), 0x045f}, +}; + +#define GC2145_1280_720_PIXELRATE 48000000 +#define GC2145_1280_720_LINKFREQ 192000000 +#define GC2145_1280_720_HBLANK 0x0156 +#define GC2145_1280_720_VBLANK 0x0011 +static const struct cci_reg_sequence gc2145_mode_1280_720_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x83}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 240/160 */ + {GC2145_REG_ROW_START, 0x00f0}, + {GC2145_REG_COL_START, 0x00a0}, + /* Window size 736/1296 */ + {GC2145_REG_WIN_HEIGHT, 0x02e0}, + {GC2145_REG_WIN_WIDTH, 0x0510}, + /* Crop 1280-720@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x02d0}, + {GC2145_REG_CROP_WIDTH, 0x0500}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00e6}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x02b2}, {CCI_REG16(0x29), 0x02b2}, + {CCI_REG16(0x2b), 0x02b2}, {CCI_REG16(0x2d), 0x02b2}, +}; + +#define GC2145_1600_1200_PIXELRATE 60000000 +#define GC2145_1600_1200_LINKFREQ 240000000 +#define GC2145_1600_1200_HBLANK 0x0156 +#define GC2145_1600_1200_VBLANK 0x0010 +static const struct cci_reg_sequence gc2145_mode_1600_1200_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x84}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size: 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Crop 1600-1200@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x04b0}, + {GC2145_REG_CROP_WIDTH, 0x0640}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00fa}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x04e2}, {CCI_REG16(0x29), 0x04e2}, + {CCI_REG16(0x2b), 0x04e2}, {CCI_REG16(0x2d), 0x04e2}, +}; + +static const s64 gc2145_link_freq_menu[] = { + GC2145_640_480_LINKFREQ, + GC2145_1280_720_LINKFREQ, + GC2145_1600_1200_LINKFREQ, +}; + +/* Regulators supplies */ +static const char * const gc2145_supply_name[] = { + "iovdd", /* Digital I/O (1.7-3V) suppply */ + "avdd", /* Analog (2.7-3V) supply */ + "dvdd", /* Digital Core (1.7-1.9V) supply */ +}; + +#define GC2145_NUM_SUPPLIES ARRAY_SIZE(gc2145_supply_name) + +/* Mode configs */ +#define GC2145_MODE_640X480 0 +#define GC2145_MODE_1280X720 1 +#define GC2145_MODE_1600X1200 2 +static const struct gc2145_mode supported_modes[] = { + { + /* 640x480 30fps mode */ + .width = 640, + .height = 480, + .reg_seq = gc2145_mode_640_480_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_640_480_regs), + .pixel_rate = GC2145_640_480_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 640, + .height = 480, + }, + .hblank = GC2145_640_480_HBLANK, + .vblank = GC2145_640_480_VBLANK, + .link_freq_index = GC2145_MODE_640X480, + }, + { + /* 1280x720 30fps mode */ + .width = 1280, + .height = 720, + .reg_seq = gc2145_mode_1280_720_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1280_720_regs), + .pixel_rate = GC2145_1280_720_PIXELRATE, + .crop = { + .top = 160, + .left = 240, + .width = 1280, + .height = 720, + }, + .hblank = GC2145_1280_720_HBLANK, + .vblank = GC2145_1280_720_VBLANK, + .link_freq_index = GC2145_MODE_1280X720, + }, + { + /* 1600x1200 20fps mode */ + .width = 1600, + .height = 1200, + .reg_seq = gc2145_mode_1600_1200_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1600_1200_regs), + .pixel_rate = GC2145_1600_1200_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 1600, + .height = 1200, + }, + .hblank = GC2145_1600_1200_HBLANK, + .vblank = GC2145_1600_1200_VBLANK, + .link_freq_index = GC2145_MODE_1600X1200, + }, +}; + +/** + * struct gc2145_format - GC2145 pixel format description + * @code: media bus (MBUS) associated code + * @datatype: MIPI CSI2 data type + * @output_fmt: GC2145 output format + * @switch_bit: GC2145 first/second switch + */ +struct gc2145_format { + unsigned int code; + unsigned char datatype; + unsigned char output_fmt; + bool switch_bit; +}; + +/* All supported formats */ +static const struct gc2145_format supported_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x00, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x01, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x02, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x03, + }, + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .datatype = MIPI_CSI2_DT_RGB565, + .output_fmt = 0x06, + .switch_bit = true, + }, +}; + +struct gc2145_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; +}; + +struct gc2145 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct regmap *regmap; + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; + struct regulator_bulk_data supplies[GC2145_NUM_SUPPLIES]; + + /* V4L2 controls */ + struct gc2145_ctrls ctrls; + + /* Current mode */ + const struct gc2145_mode *mode; +}; + +static inline struct gc2145 *to_gc2145(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct gc2145, sd); +} + +static inline struct v4l2_subdev *gc2145_ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct gc2145, + ctrls.handler)->sd; +} + +static const struct gc2145_format * +gc2145_get_format_code(struct gc2145 *gc2145, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].code == code) + break; + } + + if (i >= ARRAY_SIZE(supported_formats)) + i = 0; + + return &supported_formats[i]; +} + +static void gc2145_update_pad_format(struct gc2145 *gc2145, + const struct gc2145_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int gc2145_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize pad format */ + format = v4l2_subdev_state_get_format(state, 0); + gc2145_update_pad_format(gc2145, &supported_modes[0], format, + MEDIA_BUS_FMT_RGB565_1X16); + + /* Initialize crop rectangle. */ + crop = v4l2_subdev_state_get_crop(state, 0); + *crop = supported_modes[0].crop; + + return 0; +} + +static int gc2145_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC2145_NATIVE_WIDTH; + sel->r.height = GC2145_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = 1600; + sel->r.height = 1200; + + return 0; + } + + return -EINVAL; +} + +static int gc2145_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + code->code = supported_formats[code->index].code; + return 0; +} + +static int gc2145_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_format *gc2145_format; + u32 code; + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + gc2145_format = gc2145_get_format_code(gc2145, fse->code); + code = gc2145_format->code; + if (fse->code != code) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int gc2145_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_mode *mode; + const struct gc2145_format *gc2145_fmt; + struct v4l2_mbus_framefmt *framefmt; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_rect *crop; + + gc2145_fmt = gc2145_get_format_code(gc2145, fmt->format.code); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + gc2145->mode = mode; + /* Update pixel_rate based on the mode */ + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mode->pixel_rate); + /* Update link_freq based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->link_freq, mode->link_freq_index); + /* Update hblank/vblank based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->hblank, mode->hblank); + __v4l2_ctrl_s_ctrl(ctrls->vblank, mode->vblank); + } + *framefmt = fmt->format; + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); + *crop = mode->crop; + + return 0; +} + +static const struct cci_reg_sequence gc2145_common_mipi_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x03}, + {GC2145_REG_DPHY_ANALOG_MODE1, GC2145_DPHY_MODE_PHY_CLK_EN | + GC2145_DPHY_MODE_PHY_LANE0_EN | + GC2145_DPHY_MODE_PHY_LANE1_EN | + GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL}, + {GC2145_REG_DPHY_ANALOG_MODE2, GC2145_DPHY_CLK_DIFF(2) | + GC2145_DPHY_LANE0_DIFF(2)}, + {GC2145_REG_DPHY_ANALOG_MODE3, GC2145_DPHY_LANE1_DIFF(0) | + GC2145_DPHY_CLK_DELAY}, + {GC2145_REG_FIFO_MODE, GC2145_FIFO_MODE_READ_GATE | + GC2145_FIFO_MODE_MIPI_CLK_MODULE}, + {GC2145_REG_DPHY_MODE, GC2145_DPHY_MODE_TRIGGER_PROG}, + /* Clock & Data lanes timing */ + {GC2145_REG_T_LPX, 0x10}, + {GC2145_REG_T_CLK_HS_PREPARE, 0x04}, {GC2145_REG_T_CLK_ZERO, 0x10}, + {GC2145_REG_T_CLK_PRE, 0x10}, {GC2145_REG_T_CLK_POST, 0x10}, + {GC2145_REG_T_CLK_TRAIL, 0x05}, + {GC2145_REG_T_HS_PREPARE, 0x03}, {GC2145_REG_T_HS_ZERO, 0x0a}, + {GC2145_REG_T_HS_TRAIL, 0x06}, +}; + +static int gc2145_config_mipi_mode(struct gc2145 *gc2145, + const struct gc2145_format *gc2145_format) +{ + u16 lwc, fifo_full_lvl; + int ret = 0; + + /* Common MIPI settings */ + cci_multi_reg_write(gc2145->regmap, gc2145_common_mipi_regs, + ARRAY_SIZE(gc2145_common_mipi_regs), &ret); + + /* + * Adjust the MIPI buffer settings. + * For YUV/RGB, LWC = image width * 2 + * For RAW8, LWC = image width + * For RAW10, LWC = image width * 1.25 + */ + lwc = gc2145->mode->width * 2; + cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); + + /* + * Adjust the MIPI FIFO Full Level + * 640x480 RGB: 0x0190 + * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 + * 1600x1200 RAW: 0x0190 + */ + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) + fifo_full_lvl = 0x0001; + else + fifo_full_lvl = 0x0190; + + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, + fifo_full_lvl >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, + fifo_full_lvl & 0xff, &ret); + + /* + * Set the FIFO gate mode / MIPI wdiv set: + * 0xf1 in case of RAW mode and 0xf0 otherwise + */ + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); + + /* Set the MIPI data type */ + cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, + gc2145_format->datatype, &ret); + + /* Configure mode and enable CSI */ + cci_write(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_RAW8 | GC2145_CSI2_MODE_DOUBLE | + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, &ret); + + return ret; +} + +static int gc2145_start_streaming(struct gc2145 *gc2145, + struct v4l2_subdev_state *state) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct gc2145_format *gc2145_format; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + /* Apply default values of current mode */ + cci_multi_reg_write(gc2145->regmap, gc2145->mode->reg_seq, + gc2145->mode->reg_seq_size, &ret); + cci_multi_reg_write(gc2145->regmap, gc2145_common_regs, + ARRAY_SIZE(gc2145_common_regs), &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + gc2145_format = gc2145_get_format_code(gc2145, fmt->code); + + /* Set the output format */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + cci_write(gc2145->regmap, GC2145_REG_OUTPUT_FMT, + gc2145_format->output_fmt, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BYPASS_MODE, + GC2145_BYPASS_MODE_SWITCH, + gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH + : 0, &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(&gc2145->ctrls.handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply ctrls\n", __func__); + goto err_rpm_put; + } + + /* Perform MIPI specific configuration */ + ret = gc2145_config_mipi_mode(gc2145, gc2145_format); + if (ret) { + dev_err(&client->dev, "%s failed to write mipi conf\n", + __func__); + goto err_rpm_put; + } + + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + return 0; + +err_rpm_put: + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + return ret; +} + +static void gc2145_stop_streaming(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret = 0; + + /* Disable lanes & mipi streaming */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x03, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, 0, + &ret); + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + if (ret) + dev_err(&client->dev, "%s failed to write regs\n", __func__); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); +} + +static int gc2145_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) + ret = gc2145_start_streaming(gc2145, state); + else + gc2145_stop_streaming(gc2145); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +/* Power/clock management functions */ +static int gc2145_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + ret = regulator_bulk_enable(GC2145_NUM_SUPPLIES, gc2145->supplies); + if (ret) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(gc2145->xclk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + goto reg_off; + } + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 0); + gpiod_set_value_cansleep(gc2145->reset_gpio, 0); + + /* + * Datasheet doesn't mention timing between PWDN/RESETB control and + * i2c access however, experimentation shows that a rather big delay is + * needed. + */ + msleep(41); + + return 0; + +reg_off: + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return ret; +} + +static int gc2145_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 1); + gpiod_set_value_cansleep(gc2145->reset_gpio, 1); + clk_disable_unprepare(gc2145->xclk); + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return 0; +} + +static int gc2145_get_regulators(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + unsigned int i; + + for (i = 0; i < GC2145_NUM_SUPPLIES; i++) + gc2145->supplies[i].supply = gc2145_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, GC2145_NUM_SUPPLIES, + gc2145->supplies); +} + +/* Verify chip ID */ +static int gc2145_identify_module(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret; + u64 chip_id; + + ret = cci_read(gc2145->regmap, GC2145_REG_CHIP_ID, &chip_id, NULL); + if (ret) { + dev_err(&client->dev, "failed to read chip id (%d)\n", ret); + return ret; + } + + if (chip_id != GC2145_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", + GC2145_CHIP_ID, chip_id); + return -EIO; + } + + return 0; +} + +static const char * const test_pattern_menu[] = { + "Disabled", + "Colored patterns", + "Uniform white", + "Uniform yellow", + "Uniform cyan", + "Uniform green", + "Uniform magenta", + "Uniform red", + "Uniform black", +}; + +#define GC2145_TEST_PATTERN_ENABLE BIT(0) +#define GC2145_TEST_PATTERN_UXGA BIT(3) + +#define GC2145_TEST_UNIFORM BIT(3) +#define GC2145_TEST_WHITE (4 << 4) +#define GC2145_TEST_YELLOW (8 << 4) +#define GC2145_TEST_CYAN (9 << 4) +#define GC2145_TEST_GREEN (6 << 4) +#define GC2145_TEST_MAGENTA (10 << 4) +#define GC2145_TEST_RED (5 << 4) +#define GC2145_TEST_BLACK (0) + +static const u8 test_pattern_val[] = { + 0, + GC2145_TEST_PATTERN_ENABLE, + GC2145_TEST_UNIFORM | GC2145_TEST_WHITE, + GC2145_TEST_UNIFORM | GC2145_TEST_YELLOW, + GC2145_TEST_UNIFORM | GC2145_TEST_CYAN, + GC2145_TEST_UNIFORM | GC2145_TEST_GREEN, + GC2145_TEST_UNIFORM | GC2145_TEST_MAGENTA, + GC2145_TEST_UNIFORM | GC2145_TEST_RED, + GC2145_TEST_UNIFORM | GC2145_TEST_BLACK, +}; + +static const struct v4l2_subdev_core_ops gc2145_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops gc2145_video_ops = { + .s_stream = gc2145_set_stream, +}; + +static const struct v4l2_subdev_pad_ops gc2145_pad_ops = { + .enum_mbus_code = gc2145_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc2145_set_pad_format, + .get_selection = gc2145_get_selection, + .enum_frame_size = gc2145_enum_frame_size, +}; + +static const struct v4l2_subdev_ops gc2145_subdev_ops = { + .core = &gc2145_core_ops, + .video = &gc2145_video_ops, + .pad = &gc2145_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc2145_subdev_internal_ops = { + .init_state = gc2145_init_state, +}; + +static int gc2145_set_ctrl_test_pattern(struct gc2145 *gc2145, int value) +{ + int ret = 0; + + if (!value) { + /* Disable test pattern */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, 0, &ret); + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + } + + /* Enable test pattern, colored or uniform */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, + GC2145_TEST_PATTERN_ENABLE | GC2145_TEST_PATTERN_UXGA, &ret); + + if (!(test_pattern_val[value] & GC2145_TEST_UNIFORM)) + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + + /* Uniform */ + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, + test_pattern_val[value], &ret); +} + +static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = gc2145_ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_HBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_VBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc2145_set_ctrl_test_pattern(gc2145, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(0), (ctrl->val ? BIT(0) : 0), NULL); + break; + case V4L2_CID_VFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(1), (ctrl->val ? BIT(1) : 0), NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc2145_ctrl_ops = { + .s_ctrl = gc2145_s_ctrl, +}; + +/* Initialize control handlers */ +static int gc2145_init_controls(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 12); + if (ret) + return ret; + + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + GC2145_640_480_PIXELRATE, + GC2145_1600_1200_PIXELRATE, 1, + supported_modes[0].pixel_rate); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc2145_link_freq_menu) - 1, + 0, gc2145_link_freq_menu); + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + 0, 0xfff, 1, GC2145_640_480_HBLANK); + + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + 0, 0x1fff, 1, GC2145_640_480_VBLANK); + + ctrls->test_pattern = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + dev_err(&client->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &gc2145_ctrl_ops, + &props); + if (ret) + goto error; + + gc2145->sd.ctrl_handler = hdl; + + return 0; + +error: + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +static int gc2145_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + ret = -EINVAL; + goto out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto out; + } + + if (ep_cfg.nr_of_link_frequencies != 3 || + ep_cfg.link_frequencies[0] != GC2145_640_480_LINKFREQ || + ep_cfg.link_frequencies[1] != GC2145_1280_720_LINKFREQ || + ep_cfg.link_frequencies[2] != GC2145_1600_1200_LINKFREQ) { + dev_err(dev, "Invalid link-frequencies provided\n"); + ret = -EINVAL; + } + +out: + v4l2_fwnode_endpoint_free(&ep_cfg); + + return ret; +} + +static int gc2145_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + unsigned int xclk_freq; + struct gc2145 *gc2145; + int ret; + + gc2145 = devm_kzalloc(&client->dev, sizeof(*gc2145), GFP_KERNEL); + if (!gc2145) + return -ENOMEM; + + v4l2_i2c_subdev_init(&gc2145->sd, client, &gc2145_subdev_ops); + gc2145->sd.internal_ops = &gc2145_subdev_internal_ops; + + /* Check the hardware configuration in device tree */ + if (gc2145_check_hwcfg(dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + gc2145->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc2145->xclk)) + return dev_err_probe(dev, PTR_ERR(gc2145->xclk), + "failed to get xclk\n"); + + xclk_freq = clk_get_rate(gc2145->xclk); + if (xclk_freq != GC2145_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + xclk_freq); + return -EINVAL; + } + + ret = gc2145_get_regulators(gc2145); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + /* Request optional reset pin */ + gc2145->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->reset_gpio), + "failed to get reset_gpio\n"); + + /* Request optional powerdown pin */ + gc2145->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->powerdown_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->powerdown_gpio), + "failed to get powerdown_gpio\n"); + + /* Initialise the regmap for further cci access */ + gc2145->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(gc2145->regmap)) + return dev_err_probe(dev, PTR_ERR(gc2145->regmap), + "failed to get cci regmap\n"); + + /* + * The sensor must be powered for gc2145_identify_module() + * to be able to read the CHIP_ID register + */ + ret = gc2145_power_on(dev); + if (ret) + return ret; + + ret = gc2145_identify_module(gc2145); + if (ret) + goto error_power_off; + + /* Set default mode */ + gc2145->mode = &supported_modes[0]; + + ret = gc2145_init_controls(gc2145); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc2145->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + gc2145->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&gc2145->sd.entity, 1, &gc2145->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + gc2145->sd.state_lock = gc2145->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(dev); + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + ret = v4l2_async_register_subdev_sensor(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&gc2145->sd); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_media_entity: + media_entity_cleanup(&gc2145->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + +error_power_off: + gc2145_power_off(dev); + + return ret; +} + +static void gc2145_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2145 *gc2145 = to_gc2145(sd); + + v4l2_subdev_cleanup(sd); + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc2145_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc2145_dt_ids[] = { + { .compatible = "galaxycore,gc2145" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, gc2145_dt_ids); + +static const struct dev_pm_ops gc2145_pm_ops = { + RUNTIME_PM_OPS(gc2145_power_off, gc2145_power_on, NULL) +}; + +static struct i2c_driver gc2145_i2c_driver = { + .driver = { + .name = "gc2145", + .of_match_table = gc2145_dt_ids, + .pm = pm_ptr(&gc2145_pm_ops), + }, + .probe = gc2145_probe, + .remove = gc2145_remove, +}; + +module_i2c_driver(gc2145_i2c_driver); + +MODULE_AUTHOR("Alain Volmat "); +MODULE_DESCRIPTION("GalaxyCore GC2145 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 8d61349835b759..678b30556b0a18 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1897,8 +1897,8 @@ static int hi846_get_selection(struct v4l2_subdev *sd, } } -static int hi846_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int hi846_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct hi846 *hi846 = to_hi846(sd); struct v4l2_mbus_framefmt *mf; @@ -1921,7 +1921,6 @@ static const struct v4l2_subdev_video_ops hi846_video_ops = { }; static const struct v4l2_subdev_pad_ops hi846_pad_ops = { - .init_cfg = hi846_init_cfg, .enum_frame_size = hi846_enum_frame_size, .enum_mbus_code = hi846_enum_mbus_code, .set_fmt = hi846_set_format, @@ -1934,6 +1933,10 @@ static const struct v4l2_subdev_ops hi846_subdev_ops = { .pad = &hi846_pad_ops, }; +static const struct v4l2_subdev_internal_ops hi846_internal_ops = { + .init_state = hi846_init_state, +}; + static const struct media_entity_operations hi846_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2097,6 +2100,7 @@ static int hi846_probe(struct i2c_client *client) return ret; v4l2_i2c_subdev_init(&hi846->sd, client, &hi846_subdev_ops); + hi846->sd.internal_ops = &hi846_internal_ops; mutex_init(&hi846->mutex); diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 72797e003d901d..ea3fb81fdbea96 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -635,8 +635,8 @@ static int imx214_get_selection(struct v4l2_subdev *sd, return 0; } -static int imx214_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx214_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { }; @@ -845,7 +845,6 @@ static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { .get_fmt = imx214_get_format, .set_fmt = imx214_set_format, .get_selection = imx214_get_selection, - .init_cfg = imx214_entity_init_cfg, }; static const struct v4l2_subdev_ops imx214_subdev_ops = { @@ -854,6 +853,10 @@ static const struct v4l2_subdev_ops imx214_subdev_ops = { .pad = &imx214_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx214_internal_ops = { + .init_state = imx214_entity_init_state, +}; + static const struct regmap_config sensor_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -996,6 +999,7 @@ static int imx214_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); + imx214->sd.internal_ops = &imx214_internal_ops; /* * Enable power initially, to avoid warnings @@ -1060,7 +1064,7 @@ static int imx214_probe(struct i2c_client *client) goto free_ctrl; } - imx214_entity_init_cfg(&imx214->sd, NULL); + imx214_entity_init_state(&imx214->sd, NULL); ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 91854f16c5b96c..51cbcdd9c25e4f 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -696,7 +696,7 @@ static void imx219_update_pad_format(struct imx219 *imx219, fmt->xfer_func = V4L2_XFER_FUNC_NONE; } -static int imx219_init_cfg(struct v4l2_subdev *sd, +static int imx219_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { struct imx219 *imx219 = to_imx219(sd); @@ -814,7 +814,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, format = v4l2_subdev_get_pad_format(sd, sd_state, 0); crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - + *format = fmt->format; *crop = mode->crop; @@ -1193,7 +1193,6 @@ static const struct v4l2_subdev_video_ops imx219_video_ops = { }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { - .init_cfg = imx219_init_cfg, .enum_mbus_code = imx219_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, @@ -1327,6 +1326,10 @@ static void imx219_free_controls(struct imx219 *imx219) v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); } +static const struct v4l2_subdev_internal_ops imx219_internal_ops = { + .init_state = imx219_init_state, +}; + static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) { struct fwnode_handle *endpoint; @@ -1388,6 +1391,7 @@ static int imx219_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); + imx219->sd.internal_ops = &imx219_internal_ops; /* Check the hardware configuration in device tree */ if (imx219_check_hwcfg(dev, imx219)) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 517c9703d7cff1..4150e6e4b9a635 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -1195,8 +1195,8 @@ static int imx290_get_selection(struct v4l2_subdev *sd, } } -static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx290_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -1221,7 +1221,6 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { }; static const struct v4l2_subdev_pad_ops imx290_pad_ops = { - .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, .enum_frame_size = imx290_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -1235,6 +1234,10 @@ static const struct v4l2_subdev_ops imx290_subdev_ops = { .pad = &imx290_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx290_internal_ops = { + .init_state = imx290_entity_init_state, +}; + static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1248,6 +1251,7 @@ static int imx290_subdev_init(struct imx290 *imx290) imx290->current_mode = &imx290_modes_ptr(imx290)[0]; v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); + imx290->sd.internal_ops = &imx290_internal_ops; imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx290->sd.dev = imx290->dev; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 4b3a9945b5a2a0..341b4975727b1e 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -874,8 +874,8 @@ static int imx296_set_selection(struct v4l2_subdev *sd, return 0; } -static int imx296_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx296_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_selection sel = { .target = V4L2_SEL_TGT_CROP, @@ -906,7 +906,6 @@ static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { .set_fmt = imx296_set_format, .get_selection = imx296_get_selection, .set_selection = imx296_set_selection, - .init_cfg = imx296_init_cfg, }; static const struct v4l2_subdev_ops imx296_subdev_ops = { @@ -914,12 +913,17 @@ static const struct v4l2_subdev_ops imx296_subdev_ops = { .pad = &imx296_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx296_internal_ops = { + .init_state = imx296_init_state, +}; + static int imx296_subdev_init(struct imx296 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx296_subdev_ops); + sensor->subdev.internal_ops = &imx296_internal_ops; ret = imx296_ctrls_init(sensor); if (ret < 0) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 3d47c22aa3178c..61f873ff646abf 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -887,14 +887,14 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, } /** - * imx334_init_cfg() - Initialize sub-device state + * imx334_init_state() - Initialize sub-device state * @sd: pointer to imx334 V4L2 sub-device structure * @sd_state: V4L2 sub-device state * * Return: 0 if successful, error code otherwise. */ -static int imx334_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx334_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx334 *imx334 = to_imx334(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1149,7 +1149,6 @@ static const struct v4l2_subdev_video_ops imx334_video_ops = { }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { - .init_cfg = imx334_init_cfg, .enum_mbus_code = imx334_enum_mbus_code, .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, @@ -1161,6 +1160,10 @@ static const struct v4l2_subdev_ops imx334_subdev_ops = { .pad = &imx334_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx334_internal_ops = { + .init_state = imx334_init_state, +}; + /** * imx334_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1313,6 +1316,7 @@ static int imx334_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); + imx334->sd.internal_ops = &imx334_internal_ops; ret = imx334_parse_hw_config(imx334); if (ret) { diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index a90a815274fa09..0e332d4922e21c 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -629,14 +629,14 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, } /** - * imx335_init_cfg() - Initialize sub-device state + * imx335_init_state() - Initialize sub-device state * @sd: pointer to imx335 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx335_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx335_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx335 *imx335 = to_imx335(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -864,7 +864,6 @@ static const struct v4l2_subdev_video_ops imx335_video_ops = { }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { - .init_cfg = imx335_init_cfg, .enum_mbus_code = imx335_enum_mbus_code, .enum_frame_size = imx335_enum_frame_size, .get_fmt = imx335_get_pad_format, @@ -876,6 +875,10 @@ static const struct v4l2_subdev_ops imx335_subdev_ops = { .pad = &imx335_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx335_internal_ops = { + .init_state = imx335_init_state, +}; + /** * imx335_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1039,6 +1042,7 @@ static int imx335_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); + imx335->sd.internal_ops = &imx335_internal_ops; ret = imx335_parse_hw_config(imx335); if (ret) { diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 4456c32c72f18f..6ce9b92972c690 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -771,14 +771,14 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, } /** - * imx412_init_cfg() - Initialize sub-device state + * imx412_init_state() - Initialize sub-device state * @sd: pointer to imx412 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx412_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx412_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx412 *imx412 = to_imx412(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1005,7 +1005,6 @@ static const struct v4l2_subdev_video_ops imx412_video_ops = { }; static const struct v4l2_subdev_pad_ops imx412_pad_ops = { - .init_cfg = imx412_init_cfg, .enum_mbus_code = imx412_enum_mbus_code, .enum_frame_size = imx412_enum_frame_size, .get_fmt = imx412_get_pad_format, @@ -1017,6 +1016,10 @@ static const struct v4l2_subdev_ops imx412_subdev_ops = { .pad = &imx412_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx412_internal_ops = { + .init_state = imx412_init_state, +}; + /** * imx412_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1185,6 +1188,7 @@ static int imx412_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); + imx412->sd.internal_ops = &imx412_internal_ops; ret = imx412_parse_hw_config(imx412); if (ret) { diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 29e4f8ba1ebeb5..1f6e9f138c0531 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -891,8 +891,8 @@ static int imx415_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -static int imx415_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx415_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .format = { @@ -916,7 +916,6 @@ static const struct v4l2_subdev_pad_ops imx415_subdev_pad_ops = { .get_fmt = imx415_get_format, .set_fmt = imx415_set_format, .get_selection = imx415_get_selection, - .init_cfg = imx415_init_cfg, }; static const struct v4l2_subdev_ops imx415_subdev_ops = { @@ -924,12 +923,17 @@ static const struct v4l2_subdev_ops imx415_subdev_ops = { .pad = &imx415_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx415_internal_ops = { + .init_state = imx415_init_state, +}; + static int imx415_subdev_init(struct imx415 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx415_subdev_ops); + sensor->subdev.internal_ops = &imx415_internal_ops; ret = imx415_ctrls_init(sensor); if (ret) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index be56e35de01180..c0cefd3fb80b92 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -656,8 +656,8 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #endif }; -static int mt9m001_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m001_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -714,7 +714,6 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { }; static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { - .init_cfg = mt9m001_init_cfg, .enum_mbus_code = mt9m001_enum_mbus_code, .get_selection = mt9m001_get_selection, .set_selection = mt9m001_set_selection, @@ -730,6 +729,10 @@ static const struct v4l2_subdev_ops mt9m001_subdev_ops = { .pad = &mt9m001_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m001_internal_ops = { + .init_state = mt9m001_init_state, +}; + static int mt9m001_probe(struct i2c_client *client) { struct mt9m001 *mt9m001; @@ -761,6 +764,7 @@ static int mt9m001_probe(struct i2c_client *client) return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + mt9m001->subdev.internal_ops = &mt9m001_internal_ops; mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 17ca92810b581f..54a7a4c623ea37 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1111,8 +1111,8 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m111_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m111_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(sd_state, 0); @@ -1156,7 +1156,6 @@ static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { - .init_cfg = mt9m111_init_cfg, .enum_mbus_code = mt9m111_enum_mbus_code, .get_selection = mt9m111_get_selection, .set_selection = mt9m111_set_selection, @@ -1171,6 +1170,10 @@ static const struct v4l2_subdev_ops mt9m111_subdev_ops = { .pad = &mt9m111_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m111_internal_ops = { + .init_state = mt9m111_init_state, +}; + /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one @@ -1275,6 +1278,7 @@ static int mt9m111_probe(struct i2c_client *client) mt9m111->ctx = &context_b; v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); + mt9m111->subdev.internal_ops = &mt9m111_internal_ops; mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c new file mode 100644 index 00000000000000..0a22f328981d0d --- /dev/null +++ b/drivers/media/i2c/mt9m114.c @@ -0,0 +1,2485 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mt9m114.c onsemi MT9M114 sensor driver + * + * Copyright (c) 2020-2023 Laurent Pinchart + * Copyright (c) 2012 Analog Devices Inc. + * + * Almost complete rewrite of work by Scott Jiang + * itself based on work from Andrew Chew . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Sysctl registers */ +#define MT9M114_CHIP_ID CCI_REG16(0x0000) +#define MT9M114_COMMAND_REGISTER CCI_REG16(0x0080) +#define MT9M114_COMMAND_REGISTER_APPLY_PATCH BIT(0) +#define MT9M114_COMMAND_REGISTER_SET_STATE BIT(1) +#define MT9M114_COMMAND_REGISTER_REFRESH BIT(2) +#define MT9M114_COMMAND_REGISTER_WAIT_FOR_EVENT BIT(3) +#define MT9M114_COMMAND_REGISTER_OK BIT(15) +#define MT9M114_RESET_AND_MISC_CONTROL CCI_REG16(0x001a) +#define MT9M114_RESET_SOC BIT(0) +#define MT9M114_PAD_SLEW CCI_REG16(0x001e) +#define MT9M114_PAD_CONTROL CCI_REG16(0x0032) + +/* XDMA registers */ +#define MT9M114_ACCESS_CTL_STAT CCI_REG16(0x0982) +#define MT9M114_PHYSICAL_ADDRESS_ACCESS CCI_REG16(0x098a) +#define MT9M114_LOGICAL_ADDRESS_ACCESS CCI_REG16(0x098e) + +/* Sensor Core registers */ +#define MT9M114_COARSE_INTEGRATION_TIME CCI_REG16(0x3012) +#define MT9M114_FINE_INTEGRATION_TIME CCI_REG16(0x3014) +#define MT9M114_RESET_REGISTER CCI_REG16(0x301a) +#define MT9M114_RESET_REGISTER_LOCK_REG BIT(3) +#define MT9M114_RESET_REGISTER_MASK_BAD BIT(9) +#define MT9M114_FLASH CCI_REG16(0x3046) +#define MT9M114_GREEN1_GAIN CCI_REG16(0x3056) +#define MT9M114_BLUE_GAIN CCI_REG16(0x3058) +#define MT9M114_RED_GAIN CCI_REG16(0x305a) +#define MT9M114_GREEN2_GAIN CCI_REG16(0x305c) +#define MT9M114_GLOBAL_GAIN CCI_REG16(0x305e) +#define MT9M114_GAIN_DIGITAL_GAIN(n) ((n) << 12) +#define MT9M114_GAIN_DIGITAL_GAIN_MASK (0xf << 12) +#define MT9M114_GAIN_ANALOG_GAIN(n) ((n) << 0) +#define MT9M114_GAIN_ANALOG_GAIN_MASK (0xff << 0) +#define MT9M114_CUSTOMER_REV CCI_REG16(0x31fe) + +/* Monitor registers */ +#define MT9M114_MON_MAJOR_VERSION CCI_REG16(0x8000) +#define MT9M114_MON_MINOR_VERSION CCI_REG16(0x8002) +#define MT9M114_MON_RELEASE_VERSION CCI_REG16(0x8004) + +/* Auto-Exposure Track registers */ +#define MT9M114_AE_TRACK_ALGO CCI_REG16(0xa804) +#define MT9M114_AE_TRACK_EXEC_AUTOMATIC_EXPOSURE BIT(0) +#define MT9M114_AE_TRACK_AE_TRACKING_DAMPENING_SPEED CCI_REG8(0xa80a) + +/* Color Correction Matrix registers */ +#define MT9M114_CCM_ALGO CCI_REG16(0xb404) +#define MT9M114_CCM_EXEC_CALC_CCM_MATRIX BIT(4) +#define MT9M114_CCM_DELTA_GAIN CCI_REG8(0xb42a) + +/* Camera Control registers */ +#define MT9M114_CAM_SENSOR_CFG_Y_ADDR_START CCI_REG16(0xc800) +#define MT9M114_CAM_SENSOR_CFG_X_ADDR_START CCI_REG16(0xc802) +#define MT9M114_CAM_SENSOR_CFG_Y_ADDR_END CCI_REG16(0xc804) +#define MT9M114_CAM_SENSOR_CFG_X_ADDR_END CCI_REG16(0xc806) +#define MT9M114_CAM_SENSOR_CFG_PIXCLK CCI_REG32(0xc808) +#define MT9M114_CAM_SENSOR_CFG_ROW_SPEED CCI_REG16(0xc80c) +#define MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN CCI_REG16(0xc80e) +#define MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX CCI_REG16(0xc810) +#define MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES CCI_REG16(0xc812) +#define MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES_MAX 65535 +#define MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK CCI_REG16(0xc814) +#define MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK_MAX 8191 +#define MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION CCI_REG16(0xc816) +#define MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW CCI_REG16(0xc818) +#define MT9M114_CAM_SENSOR_CFG_REG_0_DATA CCI_REG16(0xc826) +#define MT9M114_CAM_SENSOR_CONTROL_READ_MODE CCI_REG16(0xc834) +#define MT9M114_CAM_SENSOR_CONTROL_HORZ_MIRROR_EN BIT(0) +#define MT9M114_CAM_SENSOR_CONTROL_VERT_FLIP_EN BIT(1) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_NORMAL (0 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SKIPPING (1 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_AVERAGE (2 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SUMMING (3 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK (3 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_NORMAL (0 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SKIPPING (1 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SUMMING (3 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK (3 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN CCI_REG16(0xc836) +#define MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME CCI_REG16(0xc83c) +#define MT9M114_CAM_SENSOR_CONTROL_FINE_INTEGRATION_TIME CCI_REG16(0xc83e) +#define MT9M114_CAM_MODE_SELECT CCI_REG8(0xc84c) +#define MT9M114_CAM_MODE_SELECT_NORMAL (0 << 0) +#define MT9M114_CAM_MODE_SELECT_LENS_CALIBRATION (1 << 0) +#define MT9M114_CAM_MODE_SELECT_TEST_PATTERN (2 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT CCI_REG8(0xc84d) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID (1 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID_BARS (4 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_RANDOM (5 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_FADING_BARS (8 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_10B (10 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_8B (11 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_RED CCI_REG16(0xc84e) +#define MT9M114_CAM_MODE_TEST_PATTERN_GREEN CCI_REG16(0xc850) +#define MT9M114_CAM_MODE_TEST_PATTERN_BLUE CCI_REG16(0xc852) +#define MT9M114_CAM_CROP_WINDOW_XOFFSET CCI_REG16(0xc854) +#define MT9M114_CAM_CROP_WINDOW_YOFFSET CCI_REG16(0xc856) +#define MT9M114_CAM_CROP_WINDOW_WIDTH CCI_REG16(0xc858) +#define MT9M114_CAM_CROP_WINDOW_HEIGHT CCI_REG16(0xc85a) +#define MT9M114_CAM_CROP_CROPMODE CCI_REG8(0xc85c) +#define MT9M114_CAM_CROP_MODE_AE_AUTO_CROP_EN BIT(0) +#define MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN BIT(1) +#define MT9M114_CAM_OUTPUT_WIDTH CCI_REG16(0xc868) +#define MT9M114_CAM_OUTPUT_HEIGHT CCI_REG16(0xc86a) +#define MT9M114_CAM_OUTPUT_FORMAT CCI_REG16(0xc86c) +#define MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE BIT(0) +#define MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES BIT(1) +#define MT9M114_CAM_OUTPUT_FORMAT_MONO_ENABLE BIT(2) +#define MT9M114_CAM_OUTPUT_FORMAT_BT656_ENABLE BIT(3) +#define MT9M114_CAM_OUTPUT_FORMAT_BT656_CROP_SCALE_DISABLE BIT(4) +#define MT9M114_CAM_OUTPUT_FORMAT_FVLV_DISABLE BIT(5) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV (0 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB (1 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER (2 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_NONE (3 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK (3 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_RAWR10 (0 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PRELSC_8_2 (1 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_POSTLSC_8_2 (2 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PROCESSED8 (3 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_MASK (3 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB (0 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_555RGB (1 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_444xRGB (2 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_444RGBx (3 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_MASK (3 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV CCI_REG16(0xc86e) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_CLIP BIT(5) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_AUV_OFFSET BIT(4) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SELECT_601 BIT(3) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_NORMALISE BIT(2) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_EVEN_UV (0 << 0) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_ODD_UV (1 << 0) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_EVENU_ODDV (2 << 0) +#define MT9M114_CAM_OUTPUT_Y_OFFSET CCI_REG8(0xc870) +#define MT9M114_CAM_AET_AEMODE CCI_REG8(0xc878) +#define MT9M114_CAM_AET_EXEC_SET_INDOOR BIT(0) +#define MT9M114_CAM_AET_DISCRETE_FRAMERATE BIT(1) +#define MT9M114_CAM_AET_ADAPTATIVE_TARGET_LUMA BIT(2) +#define MT9M114_CAM_AET_ADAPTATIVE_SKIP_FRAMES BIT(3) +#define MT9M114_CAM_AET_SKIP_FRAMES CCI_REG8(0xc879) +#define MT9M114_CAM_AET_TARGET_AVERAGE_LUMA CCI_REG8(0xc87a) +#define MT9M114_CAM_AET_TARGET_AVERAGE_LUMA_DARK CCI_REG8(0xc87b) +#define MT9M114_CAM_AET_BLACK_CLIPPING_TARGET CCI_REG16(0xc87c) +#define MT9M114_CAM_AET_AE_MIN_VIRT_INT_TIME_PCLK CCI_REG16(0xc87e) +#define MT9M114_CAM_AET_AE_MIN_VIRT_DGAIN CCI_REG16(0xc880) +#define MT9M114_CAM_AET_AE_MAX_VIRT_DGAIN CCI_REG16(0xc882) +#define MT9M114_CAM_AET_AE_MIN_VIRT_AGAIN CCI_REG16(0xc884) +#define MT9M114_CAM_AET_AE_MAX_VIRT_AGAIN CCI_REG16(0xc886) +#define MT9M114_CAM_AET_AE_VIRT_GAIN_TH_EG CCI_REG16(0xc888) +#define MT9M114_CAM_AET_AE_EG_GATE_PERCENTAGE CCI_REG8(0xc88a) +#define MT9M114_CAM_AET_FLICKER_FREQ_HZ CCI_REG8(0xc88b) +#define MT9M114_CAM_AET_MAX_FRAME_RATE CCI_REG16(0xc88c) +#define MT9M114_CAM_AET_MIN_FRAME_RATE CCI_REG16(0xc88e) +#define MT9M114_CAM_AET_TARGET_GAIN CCI_REG16(0xc890) +#define MT9M114_CAM_AWB_CCM_L(n) CCI_REG16(0xc892 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_M(n) CCI_REG16(0xc8a4 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_R(n) CCI_REG16(0xc8b6 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_L_RG_GAIN CCI_REG16(0xc8c8) +#define MT9M114_CAM_AWB_CCM_L_BG_GAIN CCI_REG16(0xc8ca) +#define MT9M114_CAM_AWB_CCM_M_RG_GAIN CCI_REG16(0xc8cc) +#define MT9M114_CAM_AWB_CCM_M_BG_GAIN CCI_REG16(0xc8ce) +#define MT9M114_CAM_AWB_CCM_R_RG_GAIN CCI_REG16(0xc8d0) +#define MT9M114_CAM_AWB_CCM_R_BG_GAIN CCI_REG16(0xc8d2) +#define MT9M114_CAM_AWB_CCM_L_CTEMP CCI_REG16(0xc8d4) +#define MT9M114_CAM_AWB_CCM_M_CTEMP CCI_REG16(0xc8d6) +#define MT9M114_CAM_AWB_CCM_R_CTEMP CCI_REG16(0xc8d8) +#define MT9M114_CAM_AWB_AWB_XSCALE CCI_REG8(0xc8f2) +#define MT9M114_CAM_AWB_AWB_YSCALE CCI_REG8(0xc8f3) +#define MT9M114_CAM_AWB_AWB_WEIGHTS(n) CCI_REG16(0xc8f4 + (n) * 2) +#define MT9M114_CAM_AWB_AWB_XSHIFT_PRE_ADJ CCI_REG16(0xc904) +#define MT9M114_CAM_AWB_AWB_YSHIFT_PRE_ADJ CCI_REG16(0xc906) +#define MT9M114_CAM_AWB_AWBMODE CCI_REG8(0xc909) +#define MT9M114_CAM_AWB_MODE_AUTO BIT(1) +#define MT9M114_CAM_AWB_MODE_EXCLUSIVE_AE BIT(0) +#define MT9M114_CAM_AWB_K_R_L CCI_REG8(0xc90c) +#define MT9M114_CAM_AWB_K_G_L CCI_REG8(0xc90d) +#define MT9M114_CAM_AWB_K_B_L CCI_REG8(0xc90e) +#define MT9M114_CAM_AWB_K_R_R CCI_REG8(0xc90f) +#define MT9M114_CAM_AWB_K_G_R CCI_REG8(0xc910) +#define MT9M114_CAM_AWB_K_B_R CCI_REG8(0xc911) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART CCI_REG16(0xc914) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART CCI_REG16(0xc916) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND CCI_REG16(0xc918) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND CCI_REG16(0xc91a) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART CCI_REG16(0xc91c) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART CCI_REG16(0xc91e) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND CCI_REG16(0xc920) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND CCI_REG16(0xc922) +#define MT9M114_CAM_LL_LLMODE CCI_REG16(0xc924) +#define MT9M114_CAM_LL_START_BRIGHTNESS CCI_REG16(0xc926) +#define MT9M114_CAM_LL_STOP_BRIGHTNESS CCI_REG16(0xc928) +#define MT9M114_CAM_LL_START_SATURATION CCI_REG8(0xc92a) +#define MT9M114_CAM_LL_END_SATURATION CCI_REG8(0xc92b) +#define MT9M114_CAM_LL_START_DESATURATION CCI_REG8(0xc92c) +#define MT9M114_CAM_LL_END_DESATURATION CCI_REG8(0xc92d) +#define MT9M114_CAM_LL_START_DEMOSAICING CCI_REG8(0xc92e) +#define MT9M114_CAM_LL_START_AP_GAIN CCI_REG8(0xc92f) +#define MT9M114_CAM_LL_START_AP_THRESH CCI_REG8(0xc930) +#define MT9M114_CAM_LL_STOP_DEMOSAICING CCI_REG8(0xc931) +#define MT9M114_CAM_LL_STOP_AP_GAIN CCI_REG8(0xc932) +#define MT9M114_CAM_LL_STOP_AP_THRESH CCI_REG8(0xc933) +#define MT9M114_CAM_LL_START_NR_RED CCI_REG8(0xc934) +#define MT9M114_CAM_LL_START_NR_GREEN CCI_REG8(0xc935) +#define MT9M114_CAM_LL_START_NR_BLUE CCI_REG8(0xc936) +#define MT9M114_CAM_LL_START_NR_THRESH CCI_REG8(0xc937) +#define MT9M114_CAM_LL_STOP_NR_RED CCI_REG8(0xc938) +#define MT9M114_CAM_LL_STOP_NR_GREEN CCI_REG8(0xc939) +#define MT9M114_CAM_LL_STOP_NR_BLUE CCI_REG8(0xc93a) +#define MT9M114_CAM_LL_STOP_NR_THRESH CCI_REG8(0xc93b) +#define MT9M114_CAM_LL_START_CONTRAST_BM CCI_REG16(0xc93c) +#define MT9M114_CAM_LL_STOP_CONTRAST_BM CCI_REG16(0xc93e) +#define MT9M114_CAM_LL_GAMMA CCI_REG16(0xc940) +#define MT9M114_CAM_LL_START_CONTRAST_GRADIENT CCI_REG8(0xc942) +#define MT9M114_CAM_LL_STOP_CONTRAST_GRADIENT CCI_REG8(0xc943) +#define MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE CCI_REG8(0xc944) +#define MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE CCI_REG8(0xc945) +#define MT9M114_CAM_LL_START_GAIN_METRIC CCI_REG16(0xc946) +#define MT9M114_CAM_LL_STOP_GAIN_METRIC CCI_REG16(0xc948) +#define MT9M114_CAM_LL_START_FADE_TO_BLACK_LUMA CCI_REG16(0xc94a) +#define MT9M114_CAM_LL_STOP_FADE_TO_BLACK_LUMA CCI_REG16(0xc94c) +#define MT9M114_CAM_LL_CLUSTER_DC_TH_BM CCI_REG16(0xc94e) +#define MT9M114_CAM_LL_CLUSTER_DC_GATE_PERCENTAGE CCI_REG8(0xc950) +#define MT9M114_CAM_LL_SUMMING_SENSITIVITY_FACTOR CCI_REG8(0xc951) +#define MT9M114_CAM_LL_START_TARGET_LUMA_BM CCI_REG16(0xc952) +#define MT9M114_CAM_LL_STOP_TARGET_LUMA_BM CCI_REG16(0xc954) +#define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e) +#define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e) +#define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m)) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) ((p) << 8) +#define MT9M114_CAM_PORT_OUTPUT_CONTROL CCI_REG16(0xc984) +#define MT9M114_CAM_PORT_PORT_SELECT_PARALLEL (0 << 0) +#define MT9M114_CAM_PORT_PORT_SELECT_MIPI (1 << 0) +#define MT9M114_CAM_PORT_CLOCK_SLOWDOWN BIT(3) +#define MT9M114_CAM_PORT_TRUNCATE_RAW_BAYER BIT(4) +#define MT9M114_CAM_PORT_PIXCLK_GATE BIT(5) +#define MT9M114_CAM_PORT_CONT_MIPI_CLK BIT(6) +#define MT9M114_CAM_PORT_CHAN_NUM(vc) ((vc) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_ZERO CCI_REG16(0xc988) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_ZERO_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_EXIT_TRAIL CCI_REG16(0xc98a) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_EXIT_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_TRAIL_VALUE(n) ((n) << 0) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_POST_PRE CCI_REG16(0xc98c) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_POST_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_PRE_VALUE(n) ((n) << 0) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_ZERO CCI_REG16(0xc98e) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_ZERO_VALUE(n) ((n) << 0) + +/* System Manager registers */ +#define MT9M114_SYSMGR_NEXT_STATE CCI_REG8(0xdc00) +#define MT9M114_SYSMGR_CURRENT_STATE CCI_REG8(0xdc01) +#define MT9M114_SYSMGR_CMD_STATUS CCI_REG8(0xdc02) + +/* Patch Loader registers */ +#define MT9M114_PATCHLDR_LOADER_ADDRESS CCI_REG16(0xe000) +#define MT9M114_PATCHLDR_PATCH_ID CCI_REG16(0xe002) +#define MT9M114_PATCHLDR_FIRMWARE_ID CCI_REG32(0xe004) +#define MT9M114_PATCHLDR_APPLY_STATUS CCI_REG8(0xe008) +#define MT9M114_PATCHLDR_NUM_PATCHES CCI_REG8(0xe009) +#define MT9M114_PATCHLDR_PATCH_ID_0 CCI_REG16(0xe00a) +#define MT9M114_PATCHLDR_PATCH_ID_1 CCI_REG16(0xe00c) +#define MT9M114_PATCHLDR_PATCH_ID_2 CCI_REG16(0xe00e) +#define MT9M114_PATCHLDR_PATCH_ID_3 CCI_REG16(0xe010) +#define MT9M114_PATCHLDR_PATCH_ID_4 CCI_REG16(0xe012) +#define MT9M114_PATCHLDR_PATCH_ID_5 CCI_REG16(0xe014) +#define MT9M114_PATCHLDR_PATCH_ID_6 CCI_REG16(0xe016) +#define MT9M114_PATCHLDR_PATCH_ID_7 CCI_REG16(0xe018) + +/* SYS_STATE values (for SYSMGR_NEXT_STATE and SYSMGR_CURRENT_STATE) */ +#define MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE 0x28 +#define MT9M114_SYS_STATE_STREAMING 0x31 +#define MT9M114_SYS_STATE_START_STREAMING 0x34 +#define MT9M114_SYS_STATE_ENTER_SUSPEND 0x40 +#define MT9M114_SYS_STATE_SUSPENDED 0x41 +#define MT9M114_SYS_STATE_ENTER_STANDBY 0x50 +#define MT9M114_SYS_STATE_STANDBY 0x52 +#define MT9M114_SYS_STATE_LEAVE_STANDBY 0x54 + +/* Result status of last SET_STATE comamnd */ +#define MT9M114_SET_STATE_RESULT_ENOERR 0x00 +#define MT9M114_SET_STATE_RESULT_EINVAL 0x0c +#define MT9M114_SET_STATE_RESULT_ENOSPC 0x0d + +/* + * The minimum amount of horizontal and vertical blanking is undocumented. The + * minimum values that have been seen in register lists are 303 and 38, use + * them. + * + * Set the default to achieve 1280x960 at 30fps. + */ +#define MT9M114_MIN_HBLANK 303 +#define MT9M114_MIN_VBLANK 38 +#define MT9M114_DEF_HBLANK 323 +#define MT9M114_DEF_VBLANK 39 + +#define MT9M114_DEF_FRAME_RATE 30 +#define MT9M114_MAX_FRAME_RATE 120 + +#define MT9M114_PIXEL_ARRAY_WIDTH 1296U +#define MT9M114_PIXEL_ARRAY_HEIGHT 976U + +/* + * These values are not well documented and are semi-arbitrary. The pixel array + * minimum output size is 8 pixels larger than the minimum scaler cropped input + * width to account for the demosaicing. + */ +#define MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH (32U + 8U) +#define MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT (32U + 8U) +#define MT9M114_SCALER_CROPPED_INPUT_WIDTH 32U +#define MT9M114_SCALER_CROPPED_INPUT_HEIGHT 32U + +/* Indices into the mt9m114.ifp.tpg array. */ +#define MT9M114_TPG_PATTERN 0 +#define MT9M114_TPG_RED 1 +#define MT9M114_TPG_GREEN 2 +#define MT9M114_TPG_BLUE 3 + +/* ----------------------------------------------------------------------------- + * Data Structures + */ + +enum mt9m114_format_flag { + MT9M114_FMT_FLAG_PARALLEL = BIT(0), + MT9M114_FMT_FLAG_CSI2 = BIT(1), +}; + +struct mt9m114_format_info { + u32 code; + u32 output_format; + u32 flags; +}; + +struct mt9m114 { + struct i2c_client *client; + struct regmap *regmap; + + struct clk *clk; + struct gpio_desc *reset; + struct regulator_bulk_data supplies[3]; + struct v4l2_fwnode_endpoint bus_cfg; + + struct { + unsigned int m; + unsigned int n; + unsigned int p; + } pll; + + unsigned int pixrate; + bool streaming; + + /* Pixel Array */ + struct { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + } pa; + + /* Image Flow Processor */ + struct { + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_ctrl_handler hdl; + unsigned int frame_rate; + + struct v4l2_ctrl *tpg[4]; + } ifp; +}; + +/* ----------------------------------------------------------------------------- + * Formats + */ + +static const struct mt9m114_format_info mt9m114_format_infos[] = { + { + /* + * The first two entries are used as defaults, for parallel and + * CSI-2 buses respectively. Keep them in that order. + */ + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV, + }, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV, + }, { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB, + }, { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PROCESSED8 + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER, + .flags = MT9M114_FMT_FLAG_PARALLEL | MT9M114_FMT_FLAG_CSI2, + }, { + /* Keep the format compatible with the IFP sink pad last. */ + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_RAWR10 + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER, + .flags = MT9M114_FMT_FLAG_PARALLEL | MT9M114_FMT_FLAG_CSI2, + } +}; + +static const struct mt9m114_format_info * +mt9m114_default_format_info(struct mt9m114 *sensor) +{ + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + return &mt9m114_format_infos[1]; + else + return &mt9m114_format_infos[0]; +} + +static const struct mt9m114_format_info * +mt9m114_format_info(struct mt9m114 *sensor, unsigned int pad, u32 code) +{ + const unsigned int num_formats = ARRAY_SIZE(mt9m114_format_infos); + unsigned int flag; + unsigned int i; + + switch (pad) { + case 0: + return &mt9m114_format_infos[num_formats - 1]; + + case 1: + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + flag = MT9M114_FMT_FLAG_CSI2; + else + flag = MT9M114_FMT_FLAG_PARALLEL; + + for (i = 0; i < num_formats; ++i) { + const struct mt9m114_format_info *info = + &mt9m114_format_infos[i]; + + if (info->code == code && info->flags & flag) + return info; + } + + return mt9m114_default_format_info(sensor); + + default: + return NULL; + } +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static const struct cci_reg_sequence mt9m114_init[] = { + { MT9M114_RESET_REGISTER, MT9M114_RESET_REGISTER_MASK_BAD | + MT9M114_RESET_REGISTER_LOCK_REG | + 0x0010 }, + + /* Sensor optimization */ + { CCI_REG16(0x316a), 0x8270 }, + { CCI_REG16(0x316c), 0x8270 }, + { CCI_REG16(0x3ed0), 0x2305 }, + { CCI_REG16(0x3ed2), 0x77cf }, + { CCI_REG16(0x316e), 0x8202 }, + { CCI_REG16(0x3180), 0x87ff }, + { CCI_REG16(0x30d4), 0x6080 }, + { CCI_REG16(0xa802), 0x0008 }, + + { CCI_REG16(0x3e14), 0xff39 }, + + /* APGA */ + { MT9M114_CAM_PGA_PGA_CONTROL, 0x0000 }, + + /* Automatic White balance */ + { MT9M114_CAM_AWB_CCM_L(0), 0x0267 }, + { MT9M114_CAM_AWB_CCM_L(1), 0xff1a }, + { MT9M114_CAM_AWB_CCM_L(2), 0xffb3 }, + { MT9M114_CAM_AWB_CCM_L(3), 0xff80 }, + { MT9M114_CAM_AWB_CCM_L(4), 0x0166 }, + { MT9M114_CAM_AWB_CCM_L(5), 0x0003 }, + { MT9M114_CAM_AWB_CCM_L(6), 0xff9a }, + { MT9M114_CAM_AWB_CCM_L(7), 0xfeb4 }, + { MT9M114_CAM_AWB_CCM_L(8), 0x024d }, + { MT9M114_CAM_AWB_CCM_M(0), 0x01bf }, + { MT9M114_CAM_AWB_CCM_M(1), 0xff01 }, + { MT9M114_CAM_AWB_CCM_M(2), 0xfff3 }, + { MT9M114_CAM_AWB_CCM_M(3), 0xff75 }, + { MT9M114_CAM_AWB_CCM_M(4), 0x0198 }, + { MT9M114_CAM_AWB_CCM_M(5), 0xfffd }, + { MT9M114_CAM_AWB_CCM_M(6), 0xff9a }, + { MT9M114_CAM_AWB_CCM_M(7), 0xfee7 }, + { MT9M114_CAM_AWB_CCM_M(8), 0x02a8 }, + { MT9M114_CAM_AWB_CCM_R(0), 0x01d9 }, + { MT9M114_CAM_AWB_CCM_R(1), 0xff26 }, + { MT9M114_CAM_AWB_CCM_R(2), 0xfff3 }, + { MT9M114_CAM_AWB_CCM_R(3), 0xffb3 }, + { MT9M114_CAM_AWB_CCM_R(4), 0x0132 }, + { MT9M114_CAM_AWB_CCM_R(5), 0xffe8 }, + { MT9M114_CAM_AWB_CCM_R(6), 0xffda }, + { MT9M114_CAM_AWB_CCM_R(7), 0xfecd }, + { MT9M114_CAM_AWB_CCM_R(8), 0x02c2 }, + { MT9M114_CAM_AWB_CCM_L_RG_GAIN, 0x0075 }, + { MT9M114_CAM_AWB_CCM_L_BG_GAIN, 0x011c }, + { MT9M114_CAM_AWB_CCM_M_RG_GAIN, 0x009a }, + { MT9M114_CAM_AWB_CCM_M_BG_GAIN, 0x0105 }, + { MT9M114_CAM_AWB_CCM_R_RG_GAIN, 0x00a4 }, + { MT9M114_CAM_AWB_CCM_R_BG_GAIN, 0x00ac }, + { MT9M114_CAM_AWB_CCM_L_CTEMP, 0x0a8c }, + { MT9M114_CAM_AWB_CCM_M_CTEMP, 0x0f0a }, + { MT9M114_CAM_AWB_CCM_R_CTEMP, 0x1964 }, + { MT9M114_CAM_AWB_AWB_XSHIFT_PRE_ADJ, 51 }, + { MT9M114_CAM_AWB_AWB_YSHIFT_PRE_ADJ, 60 }, + { MT9M114_CAM_AWB_AWB_XSCALE, 3 }, + { MT9M114_CAM_AWB_AWB_YSCALE, 2 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(0), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(1), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(2), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(3), 0xe724 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(4), 0x1583 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(5), 0x2045 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(6), 0x03ff }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(7), 0x007c }, + { MT9M114_CAM_AWB_K_R_L, 0x80 }, + { MT9M114_CAM_AWB_K_G_L, 0x80 }, + { MT9M114_CAM_AWB_K_B_L, 0x80 }, + { MT9M114_CAM_AWB_K_R_R, 0x88 }, + { MT9M114_CAM_AWB_K_G_R, 0x80 }, + { MT9M114_CAM_AWB_K_B_R, 0x80 }, + + /* Low-Light Image Enhancements */ + { MT9M114_CAM_LL_START_BRIGHTNESS, 0x0020 }, + { MT9M114_CAM_LL_STOP_BRIGHTNESS, 0x009a }, + { MT9M114_CAM_LL_START_GAIN_METRIC, 0x0070 }, + { MT9M114_CAM_LL_STOP_GAIN_METRIC, 0x00f3 }, + { MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE, 0x20 }, + { MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE, 0x9a }, + { MT9M114_CAM_LL_START_SATURATION, 0x80 }, + { MT9M114_CAM_LL_END_SATURATION, 0x4b }, + { MT9M114_CAM_LL_START_DESATURATION, 0x00 }, + { MT9M114_CAM_LL_END_DESATURATION, 0xff }, + { MT9M114_CAM_LL_START_DEMOSAICING, 0x3c }, + { MT9M114_CAM_LL_START_AP_GAIN, 0x02 }, + { MT9M114_CAM_LL_START_AP_THRESH, 0x06 }, + { MT9M114_CAM_LL_STOP_DEMOSAICING, 0x64 }, + { MT9M114_CAM_LL_STOP_AP_GAIN, 0x01 }, + { MT9M114_CAM_LL_STOP_AP_THRESH, 0x0c }, + { MT9M114_CAM_LL_START_NR_RED, 0x3c }, + { MT9M114_CAM_LL_START_NR_GREEN, 0x3c }, + { MT9M114_CAM_LL_START_NR_BLUE, 0x3c }, + { MT9M114_CAM_LL_START_NR_THRESH, 0x0f }, + { MT9M114_CAM_LL_STOP_NR_RED, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_GREEN, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_BLUE, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_THRESH, 0x32 }, + { MT9M114_CAM_LL_START_CONTRAST_BM, 0x0020 }, + { MT9M114_CAM_LL_STOP_CONTRAST_BM, 0x009a }, + { MT9M114_CAM_LL_GAMMA, 0x00dc }, + { MT9M114_CAM_LL_START_CONTRAST_GRADIENT, 0x38 }, + { MT9M114_CAM_LL_STOP_CONTRAST_GRADIENT, 0x30 }, + { MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE, 0x50 }, + { MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE, 0x19 }, + { MT9M114_CAM_LL_START_FADE_TO_BLACK_LUMA, 0x0230 }, + { MT9M114_CAM_LL_STOP_FADE_TO_BLACK_LUMA, 0x0010 }, + { MT9M114_CAM_LL_CLUSTER_DC_TH_BM, 0x01cd }, + { MT9M114_CAM_LL_CLUSTER_DC_GATE_PERCENTAGE, 0x05 }, + { MT9M114_CAM_LL_SUMMING_SENSITIVITY_FACTOR, 0x40 }, + + /* Auto-Exposure */ + { MT9M114_CAM_AET_TARGET_AVERAGE_LUMA_DARK, 0x1b }, + { MT9M114_CAM_AET_AEMODE, 0x00 }, + { MT9M114_CAM_AET_TARGET_GAIN, 0x0080 }, + { MT9M114_CAM_AET_AE_MAX_VIRT_AGAIN, 0x0100 }, + { MT9M114_CAM_AET_BLACK_CLIPPING_TARGET, 0x005a }, + + { MT9M114_CCM_DELTA_GAIN, 0x05 }, + { MT9M114_AE_TRACK_AE_TRACKING_DAMPENING_SPEED, 0x20 }, + + /* Pixel array timings and integration time */ + { MT9M114_CAM_SENSOR_CFG_ROW_SPEED, 1 }, + { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN, 219 }, + { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX, 1459 }, + { MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION, 96 }, + { MT9M114_CAM_SENSOR_CFG_REG_0_DATA, 32 }, + + /* Miscellaneous settings */ + { MT9M114_PAD_SLEW, 0x0777 }, +}; + +/* ----------------------------------------------------------------------------- + * Hardware Configuration + */ + +/* Wait for a command to complete. */ +static int mt9m114_poll_command(struct mt9m114 *sensor, u32 command) +{ + unsigned int i; + u64 value; + int ret; + + for (i = 0; i < 100; ++i) { + ret = cci_read(sensor->regmap, MT9M114_COMMAND_REGISTER, &value, + NULL); + if (ret < 0) + return ret; + + if (!(value & command)) + break; + + usleep_range(5000, 6000); + } + + if (value & command) { + dev_err(&sensor->client->dev, "Command %u completion timeout\n", + command); + return -ETIMEDOUT; + } + + if (!(value & MT9M114_COMMAND_REGISTER_OK)) { + dev_err(&sensor->client->dev, "Command %u failed\n", command); + return -EIO; + } + + return 0; +} + +/* Wait for a state to be entered. */ +static int mt9m114_poll_state(struct mt9m114 *sensor, u32 state) +{ + unsigned int i; + u64 value; + int ret; + + for (i = 0; i < 100; ++i) { + ret = cci_read(sensor->regmap, MT9M114_SYSMGR_CURRENT_STATE, + &value, NULL); + if (ret < 0) + return ret; + + if (value == state) + return 0; + + usleep_range(1000, 1500); + } + + dev_err(&sensor->client->dev, "Timeout waiting for state 0x%02x\n", + state); + return -ETIMEDOUT; +} + +static int mt9m114_set_state(struct mt9m114 *sensor, u8 next_state) +{ + int ret = 0; + + /* Set the next desired state and start the state transition. */ + cci_write(sensor->regmap, MT9M114_SYSMGR_NEXT_STATE, next_state, &ret); + cci_write(sensor->regmap, MT9M114_COMMAND_REGISTER, + MT9M114_COMMAND_REGISTER_OK | + MT9M114_COMMAND_REGISTER_SET_STATE, &ret); + if (ret < 0) + return ret; + + /* Wait for the state transition to complete. */ + ret = mt9m114_poll_command(sensor, MT9M114_COMMAND_REGISTER_SET_STATE); + if (ret < 0) + return ret; + + return 0; +} + +static int mt9m114_initialize(struct mt9m114 *sensor) +{ + u32 value; + int ret; + + ret = cci_multi_reg_write(sensor->regmap, mt9m114_init, + ARRAY_SIZE(mt9m114_init), NULL); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to initialize the sensor\n"); + return ret; + } + + /* Configure the PLL. */ + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, + sensor->pll.n), + &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK, + sensor->pixrate, &ret); + + /* Configure the output mode. */ + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) { + value = MT9M114_CAM_PORT_PORT_SELECT_MIPI + | MT9M114_CAM_PORT_CHAN_NUM(0) + | 0x8000; + if (!(sensor->bus_cfg.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) + value |= MT9M114_CAM_PORT_CONT_MIPI_CLK; + } else { + value = MT9M114_CAM_PORT_PORT_SELECT_PARALLEL + | 0x8000; + } + cci_write(sensor->regmap, MT9M114_CAM_PORT_OUTPUT_CONTROL, value, &ret); + if (ret < 0) + return ret; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + if (ret < 0) + return ret; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); + if (ret < 0) + return ret; + + return 0; +} + +static int mt9m114_configure(struct mt9m114 *sensor, + struct v4l2_subdev_state *pa_state, + struct v4l2_subdev_state *ifp_state) +{ + const struct v4l2_mbus_framefmt *pa_format; + const struct v4l2_rect *pa_crop; + const struct mt9m114_format_info *ifp_info; + const struct v4l2_mbus_framefmt *ifp_format; + const struct v4l2_rect *ifp_crop; + const struct v4l2_rect *ifp_compose; + unsigned int hratio, vratio; + u64 output_format; + u64 read_mode; + int ret = 0; + + pa_format = v4l2_subdev_state_get_format(pa_state, 0); + pa_crop = v4l2_subdev_state_get_crop(pa_state, 0); + + ifp_format = v4l2_subdev_state_get_format(ifp_state, 1); + ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code); + ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0); + ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0); + + ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + &read_mode, NULL); + if (ret < 0) + return ret; + + ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + &output_format, NULL); + if (ret < 0) + return ret; + + hratio = pa_crop->width / pa_format->width; + vratio = pa_crop->height / pa_format->height; + + /* + * Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW + * register isn't clearly documented, but is always set to the number + * of active rows minus 4 divided by the vertical binning factor in all + * example sensor modes. + */ + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START, + pa_crop->left, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START, + pa_crop->top, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END, + pa_crop->width + pa_crop->left - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END, + pa_crop->height + pa_crop->top - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW, + (pa_crop->height - 4) / vratio - 1, &ret); + + read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK | + MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK); + + if (hratio > 1) + read_mode |= MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SUMMING; + if (vratio > 1) + read_mode |= MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SUMMING; + + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + read_mode, &ret); + + /* + * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left + * and top coordinates to compensate for the lines and columns removed + * by demosaicing that are taken into account in the crop rectangle but + * not in the hardware. + */ + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET, + ifp_crop->left - 4, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET, + ifp_crop->top - 4, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH, + ifp_crop->width, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT, + ifp_crop->height, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH, + ifp_compose->width, &ret); + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT, + ifp_compose->height, &ret); + + /* AWB and AE windows, use the full frame. */ + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND, + ifp_compose->width - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND, + ifp_compose->height - 1, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND, + ifp_compose->width / 5 - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND, + ifp_compose->height / 5 - 1, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE, + MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN | + MT9M114_CAM_CROP_MODE_AE_AUTO_CROP_EN, &ret); + + /* Set the media bus code. */ + output_format &= ~(MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES | + MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE); + output_format |= ifp_info->output_format; + + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + output_format, &ret); + + return ret; +} + +static int mt9m114_set_frame_rate(struct mt9m114 *sensor) +{ + u16 frame_rate = sensor->ifp.frame_rate << 8; + int ret = 0; + + cci_write(sensor->regmap, MT9M114_CAM_AET_MIN_FRAME_RATE, + frame_rate, &ret); + cci_write(sensor->regmap, MT9M114_CAM_AET_MAX_FRAME_RATE, + frame_rate, &ret); + + return ret; +} + +static int mt9m114_start_streaming(struct mt9m114 *sensor, + struct v4l2_subdev_state *pa_state, + struct v4l2_subdev_state *ifp_state) +{ + int ret; + + ret = pm_runtime_resume_and_get(&sensor->client->dev); + if (ret) + return ret; + + ret = mt9m114_configure(sensor, pa_state, ifp_state); + if (ret) + goto error; + + ret = mt9m114_set_frame_rate(sensor); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(&sensor->pa.hdl); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(&sensor->ifp.hdl); + if (ret) + goto error; + + /* + * The Change-Config state is transient and moves to the streaming + * state automatically. + */ + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + if (ret) + goto error; + + sensor->streaming = true; + + return 0; + +error: + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static int mt9m114_stop_streaming(struct mt9m114 *sensor) +{ + int ret; + + sensor->streaming = false; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Common Subdev Operations + */ + +static const struct media_entity_operations mt9m114_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Pixel Array Control Operations + */ + +static inline struct mt9m114 *pa_ctrl_to_mt9m114(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mt9m114, pa.hdl); +} + +static int mt9m114_pa_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = pa_ctrl_to_mt9m114(ctrl); + u64 value; + int ret; + + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_read(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME, + &value, NULL); + if (ret) + break; + + ctrl->val = value; + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_read(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN, + &value, NULL); + if (ret) + break; + + ctrl->val = value; + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = pa_ctrl_to_mt9m114(ctrl); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + u64 mask; + + /* V4L2 controls values are applied only when power is up. */ + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + state = v4l2_subdev_get_locked_active_state(&sensor->pa.sd); + format = v4l2_subdev_state_get_format(state, 0); + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK, + ctrl->val + format->width, &ret); + break; + + case V4L2_CID_VBLANK: + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES, + ctrl->val + format->height, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME, + ctrl->val, &ret); + break; + + case V4L2_CID_ANALOGUE_GAIN: + /* + * The CAM_SENSOR_CONTROL_ANALOG_GAIN contains linear analog + * gain values that are mapped to the GLOBAL_GAIN register + * values by the sensor firmware. + */ + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_HFLIP: + mask = MT9M114_CAM_SENSOR_CONTROL_HORZ_MIRROR_EN; + ret = cci_update_bits(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + mask, ctrl->val ? mask : 0, NULL); + break; + + case V4L2_CID_VFLIP: + mask = MT9M114_CAM_SENSOR_CONTROL_VERT_FLIP_EN; + ret = cci_update_bits(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + mask, ctrl->val ? mask : 0, NULL); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mt9m114_pa_ctrl_ops = { + .g_volatile_ctrl = mt9m114_pa_g_ctrl, + .s_ctrl = mt9m114_pa_s_ctrl, +}; + +static void mt9m114_pa_ctrl_update_exposure(struct mt9m114 *sensor, bool manual) +{ + /* + * Update the volatile flag on the manual exposure and gain controls. + * If the controls have switched to manual, read their current value + * from the hardware to ensure that control read and write operations + * will behave correctly + */ + if (manual) { + mt9m114_pa_g_ctrl(sensor->pa.exposure); + sensor->pa.exposure->cur.val = sensor->pa.exposure->val; + sensor->pa.exposure->flags &= ~V4L2_CTRL_FLAG_VOLATILE; + + mt9m114_pa_g_ctrl(sensor->pa.gain); + sensor->pa.gain->cur.val = sensor->pa.gain->val; + sensor->pa.gain->flags &= ~V4L2_CTRL_FLAG_VOLATILE; + } else { + sensor->pa.exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + sensor->pa.gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + } +} + +static void mt9m114_pa_ctrl_update_blanking(struct mt9m114 *sensor, + const struct v4l2_mbus_framefmt *format) +{ + unsigned int max_blank; + + /* Update the blanking controls ranges based on the output size. */ + max_blank = MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK_MAX + - format->width; + __v4l2_ctrl_modify_range(sensor->pa.hblank, MT9M114_MIN_HBLANK, + max_blank, 1, MT9M114_DEF_HBLANK); + + max_blank = MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES_MAX + - format->height; + __v4l2_ctrl_modify_range(sensor->pa.vblank, MT9M114_MIN_VBLANK, + max_blank, 1, MT9M114_DEF_VBLANK); +} + +/* ----------------------------------------------------------------------------- + * Pixel Array Subdev Operations + */ + +static inline struct mt9m114 *pa_to_mt9m114(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9m114, pa.sd); +} + +static int mt9m114_pa_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, 0); + + crop->left = 0; + crop->top = 0; + crop->width = MT9M114_PIXEL_ARRAY_WIDTH; + crop->height = MT9M114_PIXEL_ARRAY_HEIGHT; + + format = v4l2_subdev_state_get_format(state, 0); + + format->width = MT9M114_PIXEL_ARRAY_WIDTH; + format->height = MT9M114_PIXEL_ARRAY_HEIGHT; + format->code = MEDIA_BUS_FMT_SGRBG10_1X10; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static int mt9m114_pa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int mt9m114_pa_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index > 1) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + /* Report binning capability through frame size enumeration. */ + fse->min_width = MT9M114_PIXEL_ARRAY_WIDTH / (fse->index + 1); + fse->max_width = MT9M114_PIXEL_ARRAY_WIDTH / (fse->index + 1); + fse->min_height = MT9M114_PIXEL_ARRAY_HEIGHT / (fse->index + 1); + fse->max_height = MT9M114_PIXEL_ARRAY_HEIGHT / (fse->index + 1); + + return 0; +} + +static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mt9m114 *sensor = pa_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + unsigned int hscale; + unsigned int vscale; + + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); + + /* The sensor can bin horizontally and vertically. */ + hscale = DIV_ROUND_CLOSEST(crop->width, fmt->format.width ? : 1); + vscale = DIV_ROUND_CLOSEST(crop->height, fmt->format.height ? : 1); + format->width = crop->width / clamp(hscale, 1U, 2U); + format->height = crop->height / clamp(vscale, 1U, 2U); + + fmt->format = *format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9m114_pa_ctrl_update_blanking(sensor, format); + + return 0; +} + +static int mt9m114_pa_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = MT9M114_PIXEL_ARRAY_WIDTH; + sel->r.height = MT9M114_PIXEL_ARRAY_HEIGHT; + return 0; + + default: + return -EINVAL; + } +} + +static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mt9m114 *sensor = pa_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); + + /* + * Clamp the crop rectangle. The vertical coordinates must be even, and + * the horizontal coordinates must be a multiple of 4. + * + * FIXME: The horizontal coordinates must be a multiple of 8 when + * binning, but binning is configured after setting the selection, so + * we can't know tell here if it will be used. + */ + crop->left = ALIGN(sel->r.left, 4); + crop->top = ALIGN(sel->r.top, 2); + crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH - crop->left); + crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT - crop->top); + + sel->r = *crop; + + /* Reset the format. */ + format->width = crop->width; + format->height = crop->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9m114_pa_ctrl_update_blanking(sensor, format); + + return 0; +} + +static const struct v4l2_subdev_pad_ops mt9m114_pa_pad_ops = { + .enum_mbus_code = mt9m114_pa_enum_mbus_code, + .enum_frame_size = mt9m114_pa_enum_framesizes, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mt9m114_pa_set_fmt, + .get_selection = mt9m114_pa_get_selection, + .set_selection = mt9m114_pa_set_selection, +}; + +static const struct v4l2_subdev_ops mt9m114_pa_ops = { + .pad = &mt9m114_pa_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9m114_pa_internal_ops = { + .init_state = mt9m114_pa_init_state, +}; + +static int mt9m114_pa_init(struct mt9m114 *sensor) +{ + struct v4l2_ctrl_handler *hdl = &sensor->pa.hdl; + struct v4l2_subdev *sd = &sensor->pa.sd; + struct media_pad *pads = &sensor->pa.pad; + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + unsigned int max_exposure; + int ret; + + /* Initialize the subdev. */ + v4l2_subdev_init(sd, &mt9m114_pa_ops); + sd->internal_ops = &mt9m114_pa_internal_ops; + v4l2_i2c_subdev_set_name(sd, sensor->client, NULL, " pixel array"); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->owner = THIS_MODULE; + sd->dev = &sensor->client->dev; + v4l2_set_subdevdata(sd, sensor->client); + + /* Initialize the media entity. */ + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + sd->entity.ops = &mt9m114_entity_ops; + pads[0].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 1, pads); + if (ret < 0) + return ret; + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 7); + + /* The range of the HBLANK and VBLANK controls will be updated below. */ + sensor->pa.hblank = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_HBLANK, + MT9M114_DEF_HBLANK, + MT9M114_DEF_HBLANK, 1, + MT9M114_DEF_HBLANK); + sensor->pa.vblank = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_VBLANK, + MT9M114_DEF_VBLANK, + MT9M114_DEF_VBLANK, 1, + MT9M114_DEF_VBLANK); + + /* + * The maximum coarse integration time is the frame length in lines + * minus two. The default is taken directly from the datasheet, but + * makes little sense as auto-exposure is enabled by default. + */ + max_exposure = MT9M114_PIXEL_ARRAY_HEIGHT + MT9M114_MIN_VBLANK - 2; + sensor->pa.exposure = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_EXPOSURE, 1, + max_exposure, 1, 16); + if (sensor->pa.exposure) + sensor->pa.exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + sensor->pa.gain = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 1, + 511, 1, 32); + if (sensor->pa.gain) + sensor->pa.gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->pixrate, sensor->pixrate, 1, + sensor->pixrate); + + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + goto error; + } + + sd->state_lock = hdl->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto error; + + /* Update the range of the blanking controls based on the format. */ + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, 0); + mt9m114_pa_ctrl_update_blanking(sensor, format); + v4l2_subdev_unlock_state(state); + + sd->ctrl_handler = hdl; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pa.hdl); + media_entity_cleanup(&sensor->pa.sd.entity); + return ret; +} + +static void mt9m114_pa_cleanup(struct mt9m114 *sensor) +{ + v4l2_ctrl_handler_free(&sensor->pa.hdl); + media_entity_cleanup(&sensor->pa.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Image Flow Processor Control Operations + */ + +static const char * const mt9m114_test_pattern_menu[] = { + "Disabled", + "Solid Color", + "100% Color Bars", + "Pseudo-Random", + "Fade-to-Gray Color Bars", + "Walking Ones 10-bit", + "Walking Ones 8-bit", +}; + +/* Keep in sync with mt9m114_test_pattern_menu */ +static const unsigned int mt9m114_test_pattern_value[] = { + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID_BARS, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_RANDOM, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_FADING_BARS, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_10B, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_8B, +}; + +static inline struct mt9m114 *ifp_ctrl_to_mt9m114(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mt9m114, ifp.hdl); +} + +static int mt9m114_ifp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = ifp_ctrl_to_mt9m114(ctrl); + u32 value; + int ret = 0; + + if (ctrl->id == V4L2_CID_EXPOSURE_AUTO) + mt9m114_pa_ctrl_update_exposure(sensor, + ctrl->val != V4L2_EXPOSURE_AUTO); + + /* V4L2 controls values are applied only when power is up. */ + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + /* Control both the AWB mode and the CCM algorithm. */ + if (ctrl->val) + value = MT9M114_CAM_AWB_MODE_AUTO + | MT9M114_CAM_AWB_MODE_EXCLUSIVE_AE; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_CAM_AWB_AWBMODE, value, &ret); + + if (ctrl->val) + value = MT9M114_CCM_EXEC_CALC_CCM_MATRIX | 0x22; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_CCM_ALGO, value, &ret); + break; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_AUTO) + value = MT9M114_AE_TRACK_EXEC_AUTOMATIC_EXPOSURE + | 0x00fe; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_AE_TRACK_ALGO, value, &ret); + if (ret) + break; + + break; + + case V4L2_CID_TEST_PATTERN: + case V4L2_CID_TEST_PATTERN_RED: + case V4L2_CID_TEST_PATTERN_GREENR: + case V4L2_CID_TEST_PATTERN_BLUE: { + unsigned int pattern = sensor->ifp.tpg[MT9M114_TPG_PATTERN]->val; + + if (pattern) { + cci_write(sensor->regmap, MT9M114_CAM_MODE_SELECT, + MT9M114_CAM_MODE_SELECT_TEST_PATTERN, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT, + mt9m114_test_pattern_value[pattern - 1], &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_RED, + sensor->ifp.tpg[MT9M114_TPG_RED]->val, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_GREEN, + sensor->ifp.tpg[MT9M114_TPG_GREEN]->val, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_BLUE, + sensor->ifp.tpg[MT9M114_TPG_BLUE]->val, &ret); + } else { + cci_write(sensor->regmap, MT9M114_CAM_MODE_SELECT, + MT9M114_CAM_MODE_SELECT_NORMAL, &ret); + } + + /* + * A Config-Change needs to be issued for the change to take + * effect. If we're not streaming ignore this, the change will + * be applied when the stream is started. + */ + if (ret || !sensor->streaming) + break; + + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + break; + } + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mt9m114_ifp_ctrl_ops = { + .s_ctrl = mt9m114_ifp_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * Image Flow Processor Subdev Operations + */ + +static inline struct mt9m114 *ifp_to_mt9m114(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9m114, ifp.sd); +} + +static int mt9m114_ifp_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_subdev_state *pa_state; + struct v4l2_subdev_state *ifp_state; + int ret; + + if (!enable) + return mt9m114_stop_streaming(sensor); + + ifp_state = v4l2_subdev_lock_and_get_active_state(&sensor->ifp.sd); + pa_state = v4l2_subdev_lock_and_get_active_state(&sensor->pa.sd); + + ret = mt9m114_start_streaming(sensor, pa_state, ifp_state); + + v4l2_subdev_unlock_state(pa_state); + v4l2_subdev_unlock_state(ifp_state); + + return ret; +} + +static int mt9m114_ifp_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct v4l2_fract *ival = &interval->interval; + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + mutex_lock(sensor->ifp.hdl.lock); + + ival->numerator = 1; + ival->denominator = sensor->ifp.frame_rate; + + mutex_unlock(sensor->ifp.hdl.lock); + + return 0; +} + +static int mt9m114_ifp_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct v4l2_fract *ival = &interval->interval; + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret = 0; + + mutex_lock(sensor->ifp.hdl.lock); + + if (ival->numerator != 0 && ival->denominator != 0) + sensor->ifp.frame_rate = min_t(unsigned int, + ival->denominator / ival->numerator, + MT9M114_MAX_FRAME_RATE); + else + sensor->ifp.frame_rate = MT9M114_MAX_FRAME_RATE; + + ival->numerator = 1; + ival->denominator = sensor->ifp.frame_rate; + + if (sensor->streaming) + ret = mt9m114_set_frame_rate(sensor); + + mutex_unlock(sensor->ifp.hdl.lock); + + return ret; +} + +static int mt9m114_ifp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect *compose; + + format = v4l2_subdev_state_get_format(state, 0); + + format->width = MT9M114_PIXEL_ARRAY_WIDTH; + format->height = MT9M114_PIXEL_ARRAY_HEIGHT; + format->code = MEDIA_BUS_FMT_SGRBG10_1X10; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + crop = v4l2_subdev_state_get_crop(state, 0); + + crop->left = 4; + crop->top = 4; + crop->width = format->width - 8; + crop->height = format->height - 8; + + compose = v4l2_subdev_state_get_compose(state, 0); + + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + + format = v4l2_subdev_state_get_format(state, 1); + + format->width = compose->width; + format->height = compose->height; + format->code = mt9m114_default_format_info(sensor)->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_DEFAULT; + format->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static int mt9m114_ifp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const unsigned int num_formats = ARRAY_SIZE(mt9m114_format_infos); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + unsigned int index = 0; + unsigned int flag; + unsigned int i; + + switch (code->pad) { + case 0: + if (code->index != 0) + return -EINVAL; + + code->code = mt9m114_format_infos[num_formats - 1].code; + return 0; + + case 1: + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + flag = MT9M114_FMT_FLAG_CSI2; + else + flag = MT9M114_FMT_FLAG_PARALLEL; + + for (i = 0; i < num_formats; ++i) { + const struct mt9m114_format_info *info = + &mt9m114_format_infos[i]; + + if (info->flags & flag) { + if (index == code->index) { + code->code = info->code; + return 0; + } + + index++; + } + } + + return -EINVAL; + + default: + return -EINVAL; + } +} + +static int mt9m114_ifp_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + const struct mt9m114_format_info *info; + + if (fse->index > 0) + return -EINVAL; + + info = mt9m114_format_info(sensor, fse->pad, fse->code); + if (!info || info->code != fse->code) + return -EINVAL; + + if (fse->pad == 0) { + fse->min_width = MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH; + fse->max_width = MT9M114_PIXEL_ARRAY_WIDTH; + fse->min_height = MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT; + fse->max_height = MT9M114_PIXEL_ARRAY_HEIGHT; + } else { + const struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, 0); + + fse->max_width = crop->width; + fse->max_height = crop->height; + + fse->min_width = fse->max_width / 4; + fse->min_height = fse->max_height / 4; + } + + return 0; +} + +static int mt9m114_ifp_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + const struct mt9m114_format_info *info; + + if (fie->index > 0) + return -EINVAL; + + info = mt9m114_format_info(sensor, fie->pad, fie->code); + if (!info || info->code != fie->code) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = MT9M114_MAX_FRAME_RATE; + + return 0; +} + +static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, fmt->pad); + + if (fmt->pad == 0) { + /* Only the size can be changed on the sink pad. */ + format->width = clamp(ALIGN(fmt->format.width, 8), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH); + format->height = clamp(ALIGN(fmt->format.height, 8), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT); + } else { + const struct mt9m114_format_info *info; + + /* Only the media bus code can be changed on the source pad. */ + info = mt9m114_format_info(sensor, 1, fmt->format.code); + + format->code = info->code; + + /* If the output format is RAW10, bypass the scaler. */ + if (format->code == MEDIA_BUS_FMT_SGRBG10_1X10) + *format = *v4l2_subdev_state_get_format(state, 0); + } + + fmt->format = *format; + + return 0; +} + +static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + int ret = 0; + + /* Crop and compose are only supported on the sink pad. */ + if (sel->pad != 0) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + /* + * The crop default and bounds are equal to the sink + * format size minus 4 pixels on each side for demosaicing. + */ + format = v4l2_subdev_state_get_format(state, 0); + + sel->r.left = 4; + sel->r.top = 4; + sel->r.width = format->width - 8; + sel->r.height = format->height - 8; + break; + + case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, 0); + break; + + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* + * The compose default and bounds sizes are equal to the sink + * crop rectangle size. + */ + crop = v4l2_subdev_state_get_crop(state, 0); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = crop->width; + sel->r.height = crop->height; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect *compose; + + if (sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + /* Crop and compose are only supported on the sink pad. */ + if (sel->pad != 0) + return -EINVAL; + + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + + if (sel->target == V4L2_SEL_TGT_CROP) { + /* + * Clamp the crop rectangle. Demosaicing removes 4 pixels on + * each side of the image. + */ + crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), 4, + format->width - 4 - + MT9M114_SCALER_CROPPED_INPUT_WIDTH); + crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), 4, + format->height - 4 - + MT9M114_SCALER_CROPPED_INPUT_HEIGHT); + crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + MT9M114_SCALER_CROPPED_INPUT_WIDTH, + format->width - 4 - crop->left); + crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_SCALER_CROPPED_INPUT_HEIGHT, + format->height - 4 - crop->top); + + sel->r = *crop; + + /* Propagate to the compose rectangle. */ + compose->width = crop->width; + compose->height = crop->height; + } else { + /* + * Clamp the compose rectangle. The scaler can only downscale. + */ + compose->left = 0; + compose->top = 0; + compose->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + MT9M114_SCALER_CROPPED_INPUT_WIDTH, + crop->width); + compose->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_SCALER_CROPPED_INPUT_HEIGHT, + crop->height); + + sel->r = *compose; + } + + /* Propagate the compose rectangle to the source format. */ + format = v4l2_subdev_state_get_format(state, 1); + format->width = compose->width; + format->height = compose->height; + + return 0; +} + +static void mt9m114_ifp_unregistered(struct v4l2_subdev *sd) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + v4l2_device_unregister_subdev(&sensor->pa.sd); +} + +static int mt9m114_ifp_registered(struct v4l2_subdev *sd) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret; + + ret = v4l2_device_register_subdev(sd->v4l2_dev, &sensor->pa.sd); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to register pixel array subdev\n"); + return ret; + } + + ret = media_create_pad_link(&sensor->pa.sd.entity, 0, + &sensor->ifp.sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to link pixel array to ifp\n"); + v4l2_device_unregister_subdev(&sensor->pa.sd); + return ret; + } + + return 0; +} + +static const struct v4l2_subdev_video_ops mt9m114_ifp_video_ops = { + .s_stream = mt9m114_ifp_s_stream, + .g_frame_interval = mt9m114_ifp_g_frame_interval, + .s_frame_interval = mt9m114_ifp_s_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m114_ifp_pad_ops = { + .enum_mbus_code = mt9m114_ifp_enum_mbus_code, + .enum_frame_size = mt9m114_ifp_enum_framesizes, + .enum_frame_interval = mt9m114_ifp_enum_frameintervals, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mt9m114_ifp_set_fmt, + .get_selection = mt9m114_ifp_get_selection, + .set_selection = mt9m114_ifp_set_selection, +}; + +static const struct v4l2_subdev_ops mt9m114_ifp_ops = { + .video = &mt9m114_ifp_video_ops, + .pad = &mt9m114_ifp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9m114_ifp_internal_ops = { + .init_state = mt9m114_ifp_init_state, + .registered = mt9m114_ifp_registered, + .unregistered = mt9m114_ifp_unregistered, +}; + +static int mt9m114_ifp_init(struct mt9m114 *sensor) +{ + struct v4l2_subdev *sd = &sensor->ifp.sd; + struct media_pad *pads = sensor->ifp.pads; + struct v4l2_ctrl_handler *hdl = &sensor->ifp.hdl; + struct v4l2_ctrl *link_freq; + int ret; + + /* Initialize the subdev. */ + v4l2_i2c_subdev_init(sd, sensor->client, &mt9m114_ifp_ops); + v4l2_i2c_subdev_set_name(sd, sensor->client, NULL, " ifp"); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &mt9m114_ifp_internal_ops; + + /* Initialize the media entity. */ + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->entity.ops = &mt9m114_entity_ops; + pads[0].flags = MEDIA_PAD_FL_SINK; + pads[1].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 2, pads); + if (ret < 0) + return ret; + + sensor->ifp.frame_rate = MT9M114_DEF_FRAME_RATE; + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 8); + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + link_freq = v4l2_ctrl_new_int_menu(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_LINK_FREQ, + sensor->bus_cfg.nr_of_link_frequencies - 1, + 0, sensor->bus_cfg.link_frequencies); + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->pixrate, sensor->pixrate, 1, + sensor->pixrate); + + sensor->ifp.tpg[MT9M114_TPG_PATTERN] = + v4l2_ctrl_new_std_menu_items(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mt9m114_test_pattern_menu) - 1, + 0, 0, mt9m114_test_pattern_menu); + sensor->ifp.tpg[MT9M114_TPG_RED] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED, + 0, 1023, 1, 1023); + sensor->ifp.tpg[MT9M114_TPG_GREEN] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_GREENR, + 0, 1023, 1, 1023); + sensor->ifp.tpg[MT9M114_TPG_BLUE] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_BLUE, + 0, 1023, 1, 1023); + + v4l2_ctrl_cluster(ARRAY_SIZE(sensor->ifp.tpg), sensor->ifp.tpg); + + if (hdl->error) { + ret = hdl->error; + goto error; + } + + sd->ctrl_handler = hdl; + sd->state_lock = hdl->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto error; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->ifp.hdl); + media_entity_cleanup(&sensor->ifp.sd.entity); + return ret; +} + +static void mt9m114_ifp_cleanup(struct mt9m114 *sensor) +{ + v4l2_ctrl_handler_free(&sensor->ifp.hdl); + media_entity_cleanup(&sensor->ifp.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int mt9m114_power_on(struct mt9m114 *sensor) +{ + int ret; + + /* Enable power and clocks. */ + ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(sensor->clk); + if (ret < 0) + goto error_regulator; + + /* Perform a hard reset if available, or a soft reset otherwise. */ + if (sensor->reset) { + long freq = clk_get_rate(sensor->clk); + unsigned int duration; + + /* + * The minimum duration is 50 clock cycles, thus typically + * around 2µs. Double it to be safe. + */ + duration = DIV_ROUND_UP(2 * 50 * 1000000, freq); + + gpiod_set_value(sensor->reset, 1); + udelay(duration); + gpiod_set_value(sensor->reset, 0); + } else { + /* + * The power may have just been turned on, we need to wait for + * the sensor to be ready to accept I2C commands. + */ + usleep_range(44500, 50000); + + cci_write(sensor->regmap, MT9M114_RESET_AND_MISC_CONTROL, + MT9M114_RESET_SOC, &ret); + cci_write(sensor->regmap, MT9M114_RESET_AND_MISC_CONTROL, 0, + &ret); + + if (ret < 0) { + dev_err(&sensor->client->dev, "Soft reset failed\n"); + goto error_clock; + } + } + + /* + * Wait for the sensor to be ready to accept I2C commands by polling the + * command register to wait for initialization to complete. + */ + usleep_range(44500, 50000); + + ret = mt9m114_poll_command(sensor, MT9M114_COMMAND_REGISTER_SET_STATE); + if (ret < 0) + goto error_clock; + + if (sensor->bus_cfg.bus_type == V4L2_MBUS_PARALLEL) { + /* + * In parallel mode (OE set to low), the sensor will enter the + * streaming state after initialization. Enter the standby + * manually to stop streaming. + */ + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_STANDBY); + if (ret < 0) + goto error_clock; + } + + /* + * Before issuing any Set-State command, we must ensure that the sensor + * reaches the standby mode (either initiated manually above in + * parallel mode, or automatically after reset in MIPI mode). + */ + ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY); + if (ret < 0) + goto error_clock; + + return 0; + +error_clock: + clk_disable_unprepare(sensor->clk); +error_regulator: + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + return ret; +} + +static void mt9m114_power_off(struct mt9m114 *sensor) +{ + clk_disable_unprepare(sensor->clk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); +} + +static int __maybe_unused mt9m114_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret; + + ret = mt9m114_power_on(sensor); + if (ret) + return ret; + + ret = mt9m114_initialize(sensor); + if (ret) { + mt9m114_power_off(sensor); + return ret; + } + + return 0; +} + +static int __maybe_unused mt9m114_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + mt9m114_power_off(sensor); + + return 0; +} + +static const struct dev_pm_ops mt9m114_pm_ops = { + SET_RUNTIME_PM_OPS(mt9m114_runtime_suspend, mt9m114_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int mt9m114_clk_init(struct mt9m114 *sensor) +{ + unsigned int link_freq; + + /* Hardcode the PLL multiplier and dividers to default settings. */ + sensor->pll.m = 32; + sensor->pll.n = 1; + sensor->pll.p = 7; + + /* + * Calculate the pixel rate and link frequency. The CSI-2 bus is clocked + * for 16-bit per pixel, transmitted in DDR over a single lane. For + * parallel mode, the sensor ouputs one pixel in two PIXCLK cycles. + */ + sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m + / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); + + link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY + ? sensor->pixrate * 8 : sensor->pixrate * 2; + + if (sensor->bus_cfg.nr_of_link_frequencies != 1 || + sensor->bus_cfg.link_frequencies[0] != link_freq) { + dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); + return -EINVAL; + } + + return 0; +} + +static int mt9m114_identify(struct mt9m114 *sensor) +{ + u64 major, minor, release, customer; + u64 value; + int ret; + + ret = cci_read(sensor->regmap, MT9M114_CHIP_ID, &value, NULL); + if (ret) { + dev_err(&sensor->client->dev, "Failed to read chip ID\n"); + return -ENXIO; + } + + if (value != 0x2481) { + dev_err(&sensor->client->dev, "Invalid chip ID 0x%04llx\n", + value); + return -ENXIO; + } + + cci_read(sensor->regmap, MT9M114_MON_MAJOR_VERSION, &major, &ret); + cci_read(sensor->regmap, MT9M114_MON_MINOR_VERSION, &minor, &ret); + cci_read(sensor->regmap, MT9M114_MON_RELEASE_VERSION, &release, &ret); + cci_read(sensor->regmap, MT9M114_CUSTOMER_REV, &customer, &ret); + if (ret) { + dev_err(&sensor->client->dev, "Failed to read version\n"); + return -ENXIO; + } + + dev_dbg(&sensor->client->dev, + "monitor v%llu.%llu.%04llx customer rev 0x%04llx\n", + major, minor, release, customer); + + return 0; +} + +static int mt9m114_parse_dt(struct mt9m114 *sensor) +{ + struct fwnode_handle *fwnode = dev_fwnode(&sensor->client->dev); + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(&sensor->client->dev, "No endpoint found\n"); + return -EINVAL; + } + + sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg); + fwnode_handle_put(ep); + if (ret < 0) { + dev_err(&sensor->client->dev, "Failed to parse endpoint\n"); + goto error; + } + + switch (sensor->bus_cfg.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + case V4L2_MBUS_PARALLEL: + break; + + default: + dev_err(&sensor->client->dev, "unsupported bus type %u\n", + sensor->bus_cfg.bus_type); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static int mt9m114_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mt9m114 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->client = client; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) { + dev_err(dev, "Unable to initialize I2C\n"); + return -ENODEV; + } + + ret = mt9m114_parse_dt(sensor); + if (ret < 0) + return ret; + + /* Acquire clocks, GPIOs and regulators. */ + sensor->clk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->clk)) { + ret = PTR_ERR(sensor->clk); + dev_err_probe(dev, ret, "Failed to get clock\n"); + goto error_ep_free; + } + + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sensor->reset)) { + ret = PTR_ERR(sensor->reset); + dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); + goto error_ep_free; + } + + sensor->supplies[0].supply = "vddio"; + sensor->supplies[1].supply = "vdd"; + sensor->supplies[2].supply = "vaa"; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to get regulators\n"); + goto error_ep_free; + } + + ret = mt9m114_clk_init(sensor); + if (ret) + goto error_ep_free; + + /* + * Identify the sensor. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the sensor on manually here, and initialize it after identification + * to reach the same state as if resumed through runtime PM. + */ + ret = mt9m114_power_on(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not power on the device\n"); + goto error_ep_free; + } + + ret = mt9m114_identify(sensor); + if (ret < 0) + goto error_power_off; + + ret = mt9m114_initialize(sensor); + if (ret < 0) + goto error_power_off; + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + /* Initialize the subdevices. */ + ret = mt9m114_pa_init(sensor); + if (ret < 0) + goto error_pm_cleanup; + + ret = mt9m114_ifp_init(sensor); + if (ret < 0) + goto error_pa_cleanup; + + ret = v4l2_async_register_subdev(&sensor->ifp.sd); + if (ret < 0) + goto error_ifp_cleanup; + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; + +error_ifp_cleanup: + mt9m114_ifp_cleanup(sensor); +error_pa_cleanup: + mt9m114_pa_cleanup(sensor); +error_pm_cleanup: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); +error_power_off: + mt9m114_power_off(sensor); +error_ep_free: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static void mt9m114_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct device *dev = &client->dev; + + v4l2_async_unregister_subdev(&sensor->ifp.sd); + + mt9m114_ifp_cleanup(sensor); + mt9m114_pa_cleanup(sensor); + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt9m114_power_off(sensor); + pm_runtime_set_suspended(dev); +} + +static const struct of_device_id mt9m114_of_ids[] = { + { .compatible = "onnn,mt9m114" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt9m114_of_ids); + +static struct i2c_driver mt9m114_driver = { + .driver = { + .name = "mt9m114", + .pm = &mt9m114_pm_ops, + .of_match_table = mt9m114_of_ids, + }, + .probe = mt9m114_probe, + .remove = mt9m114_remove, +}; + +module_i2c_driver(mt9m114_driver); + +MODULE_DESCRIPTION("onsemi MT9M114 Sensor Driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 89bcd48748b916..596200d0248cf0 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -696,8 +696,8 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev, return 0; } -static int mt9p031_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9p031_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *format; @@ -1041,7 +1041,6 @@ static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { - .init_cfg = mt9p031_init_cfg, .enum_mbus_code = mt9p031_enum_mbus_code, .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, @@ -1057,6 +1056,7 @@ static const struct v4l2_subdev_ops mt9p031_subdev_ops = { }; static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .init_state = mt9p031_init_state, .registered = mt9p031_registered, .open = mt9p031_open, .close = mt9p031_close, @@ -1189,7 +1189,7 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ret = mt9p031_init_cfg(&mt9p031->subdev, NULL); + ret = mt9p031_init_state(&mt9p031->subdev, NULL); if (ret) goto done; diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 6752582cb2c7a8..b186e9160d94b3 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -948,8 +948,8 @@ static int mt9v111_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9v111_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9v111_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { *v4l2_subdev_state_get_format(sd_state, 0) = mt9v111_def_fmt; @@ -967,7 +967,6 @@ static const struct v4l2_subdev_video_ops mt9v111_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { - .init_cfg = mt9v111_init_cfg, .enum_mbus_code = mt9v111_enum_mbus_code, .enum_frame_size = mt9v111_enum_frame_size, .enum_frame_interval = mt9v111_enum_frame_interval, @@ -981,6 +980,10 @@ static const struct v4l2_subdev_ops mt9v111_ops = { .pad = &mt9v111_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9v111_internal_ops = { + .init_state = mt9v111_init_state, +}; + static const struct media_entity_operations mt9v111_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1194,6 +1197,7 @@ static int mt9v111_probe(struct i2c_client *client) mt9v111->pending = true; v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); + mt9v111->sd.internal_ops = &mt9v111_internal_ops; mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index e94f7c53e6f08b..478a39e0d5c8a2 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -777,8 +777,8 @@ static int ov01a10_set_format(struct v4l2_subdev *sd, return 0; } -static int ov01a10_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov01a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -861,7 +861,6 @@ static const struct v4l2_subdev_video_ops ov01a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = { - .init_cfg = ov01a10_init_cfg, .set_fmt = ov01a10_set_format, .get_fmt = v4l2_subdev_get_fmt, .get_selection = ov01a10_get_selection, @@ -875,6 +874,10 @@ static const struct v4l2_subdev_ops ov01a10_subdev_ops = { .pad = &ov01a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov01a10_internal_ops = { + .init_state = ov01a10_init_state, +}; + static const struct media_entity_operations ov01a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -921,6 +924,7 @@ static int ov01a10_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); + ov01a10->sd.internal_ops = &ov01a10_internal_ops; ret = ov01a10_identify_module(ov01a10); if (ret) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index aa6ad482baa392..ccce5ff7228002 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -511,8 +511,8 @@ static int __ov02a10_stop_stream(struct ov02a10 *ov02a10) SC_CTRL_MODE_STANDBY); } -static int ov02a10_entity_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov02a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -711,7 +711,6 @@ static const struct v4l2_subdev_video_ops ov02a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov02a10_pad_ops = { - .init_cfg = ov02a10_entity_init_cfg, .enum_mbus_code = ov02a10_enum_mbus_code, .enum_frame_size = ov02a10_enum_frame_sizes, .get_fmt = ov02a10_get_fmt, @@ -723,6 +722,10 @@ static const struct v4l2_subdev_ops ov02a10_subdev_ops = { .pad = &ov02a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov02a10_internal_ops = { + .init_state = ov02a10_init_state, +}; + static const struct media_entity_operations ov02a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -871,6 +874,7 @@ static int ov02a10_probe(struct i2c_client *client) "failed to check HW configuration\n"); v4l2_i2c_subdev_init(&ov02a10->subdev, client, &ov02a10_subdev_ops); + ov02a10->subdev.internal_ops = &ov02a10_internal_ops; ov02a10->mipi_clock_voltage = OV02A10_MIPI_TX_SPEED_DEFAULT; ov02a10->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index c7b4111e594349..db9dcf732d21b6 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -996,8 +996,8 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, return ret; } -static int ov2640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(sd_state, 0); @@ -1125,7 +1125,6 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { - .init_cfg = ov2640_init_cfg, .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, @@ -1142,6 +1141,10 @@ static const struct v4l2_subdev_ops ov2640_subdev_ops = { .video = &ov2640_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops ov2640_internal_ops = { + .init_state = ov2640_init_state, +}; + static int ov2640_probe_dt(struct i2c_client *client, struct ov2640_priv *priv) { @@ -1214,6 +1217,7 @@ static int ov2640_probe(struct i2c_client *client) priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + priv->subdev.internal_ops = &ov2640_internal_ops; priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; mutex_init(&priv->lock); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 832da7512b2b0f..20cf7caea5f5ae 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -755,8 +755,8 @@ static int ov2680_set_selection(struct v4l2_subdev *sd, return 0; } -static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2680_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -876,7 +876,6 @@ static const struct v4l2_subdev_video_ops ov2680_video_ops = { }; static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { - .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, @@ -891,6 +890,10 @@ static const struct v4l2_subdev_ops ov2680_subdev_ops = { .pad = &ov2680_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov2680_internal_ops = { + .init_state = ov2680_init_state, +}; + static int ov2680_mode_init(struct ov2680_dev *sensor) { /* set initial mode */ @@ -915,6 +918,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) int ret = 0; v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); + sensor->sd.internal_ops = &ov2680_internal_ops; sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 24e468485fbf01..036b62d33648f8 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -879,8 +879,8 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int ov2740_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2740_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { ov2740_update_pad_format(&supported_modes[0], v4l2_subdev_get_pad_format(sd, sd_state, 0)); @@ -897,7 +897,6 @@ static const struct v4l2_subdev_pad_ops ov2740_pad_ops = { .set_fmt = ov2740_set_format, .enum_mbus_code = ov2740_enum_mbus_code, .enum_frame_size = ov2740_enum_frame_size, - .init_cfg = ov2740_init_cfg, }; static const struct v4l2_subdev_ops ov2740_subdev_ops = { @@ -905,6 +904,10 @@ static const struct v4l2_subdev_ops ov2740_subdev_ops = { .pad = &ov2740_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { + .init_state = ov2740_init_state, +}; + static const struct media_entity_operations ov2740_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1074,6 +1077,7 @@ static int ov2740_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); + ov2740->sd.internal_ops = &ov2740_internal_ops; full_power = acpi_dev_state_d0(&client->dev); if (full_power) { ret = ov2740_identify_module(ov2740); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 3f79a3b77044f2..6fd98b8cb18124 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -3744,8 +3744,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int ov5640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ov5640_dev *sensor = to_ov5640_dev(sd); struct v4l2_mbus_framefmt *fmt = @@ -3776,7 +3776,6 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { - .init_cfg = ov5640_init_cfg, .enum_mbus_code = ov5640_enum_mbus_code, .get_fmt = ov5640_get_fmt, .set_fmt = ov5640_set_fmt, @@ -3791,6 +3790,10 @@ static const struct v4l2_subdev_ops ov5640_subdev_ops = { .pad = &ov5640_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5640_internal_ops = { + .init_state = ov5640_init_state, +}; + static int ov5640_get_regulators(struct ov5640_dev *sensor) { int i; @@ -3905,6 +3908,7 @@ static int ov5640_probe(struct i2c_client *client) return PTR_ERR(sensor->reset_gpio); v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); + sensor->sd.internal_ops = &ov5640_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 695f4f6735363f..a26ac11c989d78 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -934,8 +934,8 @@ static int ov5645_set_format(struct v4l2_subdev *sd, return 0; } -static int ov5645_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov5645_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { 0 }; @@ -1023,7 +1023,6 @@ static const struct v4l2_subdev_video_ops ov5645_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5645_subdev_pad_ops = { - .init_cfg = ov5645_entity_init_cfg, .enum_mbus_code = ov5645_enum_mbus_code, .enum_frame_size = ov5645_enum_frame_size, .get_fmt = ov5645_get_format, @@ -1036,6 +1035,10 @@ static const struct v4l2_subdev_ops ov5645_subdev_ops = { .pad = &ov5645_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5645_internal_ops = { + .init_state = ov5645_init_state, +}; + static int ov5645_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1162,6 +1165,7 @@ static int ov5645_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&ov5645->sd, client, &ov5645_subdev_ops); + ov5645->sd.internal_ops = &ov5645_internal_ops; ov5645->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov5645->pad.flags = MEDIA_PAD_FL_SOURCE; ov5645->sd.dev = &client->dev; @@ -1220,7 +1224,7 @@ static int ov5645_probe(struct i2c_client *client) pm_runtime_get_noresume(dev); pm_runtime_enable(dev); - ov5645_entity_init_cfg(&ov5645->sd, NULL); + ov5645_init_state(&ov5645->sd, NULL); ret = v4l2_async_register_subdev(&ov5645->sd); if (ret < 0) { diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 8fa925c4d98c77..57e171ee05d577 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2198,8 +2198,8 @@ static int ov5670_init_controls(struct ov5670 *ov5670) return ret; } -static int ov5670_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5670_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0); @@ -2625,7 +2625,6 @@ static const struct v4l2_subdev_video_ops ov5670_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5670_pad_ops = { - .init_cfg = ov5670_init_cfg, .enum_mbus_code = ov5670_enum_mbus_code, .get_fmt = ov5670_get_pad_format, .set_fmt = ov5670_set_pad_format, @@ -2645,6 +2644,10 @@ static const struct v4l2_subdev_ops ov5670_subdev_ops = { .sensor = &ov5670_sensor_ops, }; +static const struct v4l2_subdev_internal_ops ov5670_internal_ops = { + .init_state = ov5670_init_state, +}; + static const struct media_entity_operations ov5670_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2708,6 +2711,7 @@ static int ov5670_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); + ov5670->sd.internal_ops = &ov5670_internal_ops; ret = ov5670_regulators_probe(ov5670); if (ret) diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c index 246a2fea8db114..d48a9ab3408843 100644 --- a/drivers/media/i2c/ov64a40.c +++ b/drivers/media/i2c/ov64a40.c @@ -3052,8 +3052,8 @@ static void ov64a40_update_pad_fmt(struct ov64a40 *ov64a40, fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; } -static int ov64a40_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov64a40_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); struct v4l2_mbus_framefmt *format; @@ -3193,7 +3193,6 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov64a40_pad_ops = { - .init_cfg = ov64a40_init_cfg, .enum_mbus_code = ov64a40_enum_mbus_code, .enum_frame_size = ov64a40_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -3212,6 +3211,10 @@ static const struct v4l2_subdev_ops ov64a40_subdev_ops = { .pad = &ov64a40_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov64a40_internal_ops = { + .init_state = ov64a40_init_state, +}; + static int ov64a40_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -3547,6 +3550,7 @@ static int ov64a40_probe(struct i2c_client *client) ov64a40->dev = &client->dev; v4l2_i2c_subdev_init(&ov64a40->sd, client, &ov64a40_subdev_ops); + ov64a40->sd.internal_ops = &ov64a40_internal_ops; ov64a40->cci = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(ov64a40->cci)) { diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index b317a1ee6c86a3..ed5e458918fcd0 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1294,8 +1294,8 @@ static int ov7251_set_format(struct v4l2_subdev *sd, return ret; } -static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov7251_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -1476,7 +1476,6 @@ static const struct v4l2_subdev_video_ops ov7251_video_ops = { }; static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { - .init_cfg = ov7251_entity_init_cfg, .enum_mbus_code = ov7251_enum_mbus_code, .enum_frame_size = ov7251_enum_frame_size, .enum_frame_interval = ov7251_enum_frame_ival, @@ -1490,6 +1489,10 @@ static const struct v4l2_subdev_ops ov7251_subdev_ops = { .pad = &ov7251_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov7251_internal_ops = { + .init_state = ov7251_init_state, +}; + static int ov7251_check_hwcfg(struct ov7251 *ov7251) { struct fwnode_handle *fwnode = dev_fwnode(ov7251->dev); @@ -1739,6 +1742,7 @@ static int ov7251_probe(struct i2c_client *client) goto free_ctrl; v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.internal_ops = &ov7251_internal_ops; ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; ov7251->sd.dev = &client->dev; @@ -1796,7 +1800,7 @@ static int ov7251_probe(struct i2c_client *client) goto free_entity; } - ov7251_entity_init_cfg(&ov7251->sd, NULL); + ov7251_init_state(&ov7251->sd, NULL); return 0; diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c index 0e377613613695..174c65f768860e 100644 --- a/drivers/media/i2c/ov8858.c +++ b/drivers/media/i2c/ov8858.c @@ -1476,8 +1476,8 @@ static int ov8858_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov8858_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov8858_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct ov8858_mode *def_mode = &ov8858_modes[0]; struct v4l2_subdev_format fmt = { @@ -1494,7 +1494,6 @@ static int ov8858_init_cfg(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov8858_pad_ops = { - .init_cfg = ov8858_init_cfg, .enum_mbus_code = ov8858_enum_mbus_code, .enum_frame_size = ov8858_enum_frame_sizes, .get_fmt = v4l2_subdev_get_fmt, @@ -1512,6 +1511,10 @@ static const struct v4l2_subdev_ops ov8858_subdev_ops = { .pad = &ov8858_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov8858_internal_ops = { + .init_state = ov8858_init_state, +}; + /* ---------------------------------------------------------------------------- * Controls handling */ @@ -1899,6 +1902,7 @@ static int ov8858_probe(struct i2c_client *client) "Failed to get powerdown gpio\n"); v4l2_i2c_subdev_init(&ov8858->subdev, client, &ov8858_subdev_ops); + ov8858->subdev.internal_ops = &ov8858_internal_ops; ret = ov8858_configure_regulators(ov8858); if (ret) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 8039f4e64bcfe7..c4cc33c5354b87 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -876,14 +876,14 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, } /** - * ov9282_init_cfg() - Initialize sub-device state + * ov9282_init_state() - Initialize sub-device state * @sd: pointer to ov9282 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int ov9282_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov9282_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov9282 *ov9282 = to_ov9282(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1196,7 +1196,6 @@ static const struct v4l2_subdev_video_ops ov9282_video_ops = { }; static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { - .init_cfg = ov9282_init_cfg, .enum_mbus_code = ov9282_enum_mbus_code, .enum_frame_size = ov9282_enum_frame_size, .get_fmt = ov9282_get_pad_format, @@ -1210,6 +1209,10 @@ static const struct v4l2_subdev_ops ov9282_subdev_ops = { .pad = &ov9282_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { + .init_state = ov9282_init_state, +}; + /** * ov9282_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1398,6 +1401,7 @@ static int ov9282_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov9282->sd, client, &ov9282_subdev_ops); + ov9282->sd.internal_ops = &ov9282_internal_ops; v4l2_i2c_subdev_set_name(&ov9282->sd, client, device_get_match_data(ov9282->dev), NULL); diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c index 9e62a1dde0af50..e4357d997db341 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/st-vgxy61.c @@ -1327,8 +1327,8 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd, return ret; } -static int vgxy61_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vgxy61_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vgxy61_dev *sensor = to_vgxy61_dev(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1474,7 +1474,6 @@ static const struct v4l2_subdev_video_ops vgxy61_video_ops = { }; static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { - .init_cfg = vgxy61_init_cfg, .enum_mbus_code = vgxy61_enum_mbus_code, .get_fmt = vgxy61_get_fmt, .set_fmt = vgxy61_set_fmt, @@ -1487,6 +1486,10 @@ static const struct v4l2_subdev_ops vgxy61_subdev_ops = { .pad = &vgxy61_pad_ops, }; +static const struct v4l2_subdev_internal_ops vgxy61_internal_ops = { + .init_state = vgxy61_init_state, +}; + static const struct media_entity_operations vgxy61_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1847,6 +1850,7 @@ static int vgxy61_probe(struct i2c_client *client) device_property_read_bool(dev, "st,strobe-gpios-polarity"); v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops); + sensor->sd.internal_ops = &vgxy61_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.ops = &vgxy61_subdev_entity_ops; diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 5fcf75ef1925bf..af0305383702b6 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -740,8 +740,8 @@ static int tc358746_s_stream(struct v4l2_subdev *sd, int enable) return v4l2_subdev_call(src, video, s_stream, 0); } -static int tc358746_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int tc358746_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; @@ -1034,7 +1034,6 @@ static const struct v4l2_subdev_video_ops tc358746_video_ops = { }; static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { - .init_cfg = tc358746_init_cfg, .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, @@ -1048,6 +1047,10 @@ static const struct v4l2_subdev_ops tc358746_ops = { .pad = &tc358746_pad_ops, }; +static const struct v4l2_subdev_internal_ops tc358746_internal_ops = { + .init_state = tc358746_init_state, +}; + static const struct media_entity_operations tc358746_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -1278,6 +1281,7 @@ tc358746_init_subdev(struct tc358746 *tc358746, struct i2c_client *client) int err; v4l2_i2c_subdev_init(sd, client, &tc358746_ops); + sd->internal_ops = &tc358746_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->entity.ops = &tc358746_entity_ops; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 63c12b77ff1ebb..1ea703a9909f54 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1734,8 +1734,8 @@ static const struct v4l2_subdev_video_ops tda1997x_video_ops = { * v4l2_subdev_pad_ops */ -static int tda1997x_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tda1997x_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; @@ -1925,7 +1925,6 @@ static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { - .init_cfg = tda1997x_init_cfg, .enum_mbus_code = tda1997x_enum_mbus_code, .get_fmt = tda1997x_get_format, .set_fmt = tda1997x_set_format, @@ -2047,6 +2046,10 @@ static const struct v4l2_subdev_ops tda1997x_subdev_ops = { .pad = &tda1997x_pad_ops, }; +static const struct v4l2_subdev_internal_ops tda1997x_internal_ops = { + .init_state = tda1997x_init_state, +}; + /* ----------------------------------------------------------------------------- * v4l2_controls */ @@ -2588,6 +2591,7 @@ static int tda1997x_probe(struct i2c_client *client) /* initialize subdev */ sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); + sd->internal_ops = &tda1997x_internal_ops; snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c new file mode 100644 index 00000000000000..3d46e428e0ac08 --- /dev/null +++ b/drivers/media/i2c/thp7312.c @@ -0,0 +1,2244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 THine Electronics, Inc. + * Copyright (C) 2023 Ideas on Board Oy + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ISP registers */ + +#define THP7312_REG_FIRMWARE_VERSION_1 CCI_REG8(0xf000) +#define THP7312_REG_CAMERA_STATUS CCI_REG8(0xf001) +#define THP7312_REG_FIRMWARE_VERSION_2 CCI_REG8(0xf005) +#define THP7312_REG_SET_OUTPUT_ENABLE CCI_REG8(0xf008) +#define THP7312_OUTPUT_ENABLE 0x01 +#define THP7312_OUTPUT_DISABLE 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION CCI_REG8(0xf009) +#define THP7312_REG_SET_OUTPUT_COLOR_UYVY 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_YUY2 0x04 +#define THP7312_REG_FLIP_MIRROR CCI_REG8(0xf00c) +#define THP7312_REG_FLIP_MIRROR_FLIP BIT(0) +#define THP7312_REG_FLIP_MIRROR_MIRROR BIT(1) +#define THP7312_REG_VIDEO_IMAGE_SIZE CCI_REG8(0xf00d) +#define THP7312_VIDEO_IMAGE_SIZE_640x360 0x52 +#define THP7312_VIDEO_IMAGE_SIZE_640x460 0x03 +#define THP7312_VIDEO_IMAGE_SIZE_1280x720 0x0a +#define THP7312_VIDEO_IMAGE_SIZE_1920x1080 0x0b +#define THP7312_VIDEO_IMAGE_SIZE_3840x2160 0x0d +#define THP7312_VIDEO_IMAGE_SIZE_4160x3120 0x14 +#define THP7312_VIDEO_IMAGE_SIZE_2016x1512 0x20 +#define THP7312_VIDEO_IMAGE_SIZE_2048x1536 0x21 +#define THP7312_REG_VIDEO_FRAME_RATE_MODE CCI_REG8(0xf00f) +#define THP7312_VIDEO_FRAME_RATE_MODE1 0x80 +#define THP7312_VIDEO_FRAME_RATE_MODE2 0x81 +#define THP7312_VIDEO_FRAME_RATE_MODE3 0x82 +#define THP7312_REG_SET_DRIVING_MODE CCI_REG8(0xf010) +#define THP7312_REG_DRIVING_MODE_STATUS CCI_REG8(0xf011) +#define THP7312_REG_JPEG_COMPRESSION_FACTOR CCI_REG8(0xf01b) +#define THP7312_REG_AE_EXPOSURE_COMPENSATION CCI_REG8(0xf022) +#define THP7312_REG_AE_FLICKER_MODE CCI_REG8(0xf023) +#define THP7312_AE_FLICKER_MODE_50 0x00 +#define THP7312_AE_FLICKER_MODE_60 0x01 +#define THP7312_AE_FLICKER_MODE_DISABLE 0x80 +#define THP7312_REG_AE_FIX_FRAME_RATE CCI_REG8(0xf02e) +#define THP7312_REG_MANUAL_WB_RED_GAIN CCI_REG8(0xf036) +#define THP7312_REG_MANUAL_WB_BLUE_GAIN CCI_REG8(0xf037) +#define THP7312_REG_WB_MODE CCI_REG8(0xf039) +#define THP7312_WB_MODE_AUTO 0x00 +#define THP7312_WB_MODE_MANUAL 0x11 +#define THP7312_REG_MANUAL_FOCUS_POSITION CCI_REG16(0xf03c) +#define THP7312_REG_AF_CONTROL CCI_REG8(0xf040) +#define THP7312_REG_AF_CONTROL_AF 0x01 +#define THP7312_REG_AF_CONTROL_MANUAL 0x10 +#define THP7312_REG_AF_CONTROL_LOCK 0x80 +#define THP7312_REG_AF_SETTING CCI_REG8(0xf041) +#define THP7312_REG_AF_SETTING_ONESHOT_CONTRAST 0x00 +#define THP7312_REG_AF_SETTING_ONESHOT_PDAF 0x40 +#define THP7312_REG_AF_SETTING_ONESHOT_HYBRID 0x80 +#define THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST 0x30 +#define THP7312_REG_AF_SETTING_CONTINUOUS_PDAF 0x70 +#define THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID 0xf0 +#define THP7312_REG_AF_SUPPORT CCI_REG8(0xf043) +#define THP7312_AF_SUPPORT_PDAF BIT(1) +#define THP7312_AF_SUPPORT_CONTRAST BIT(0) +#define THP7312_REG_SATURATION CCI_REG8(0xf052) +#define THP7312_REG_SHARPNESS CCI_REG8(0xf053) +#define THP7312_REG_BRIGHTNESS CCI_REG8(0xf056) +#define THP7312_REG_CONTRAST CCI_REG8(0xf057) +#define THP7312_REG_NOISE_REDUCTION CCI_REG8(0xf059) +#define THP7312_REG_NOISE_REDUCTION_FIXED BIT(7) + +#define TH7312_REG_CUSTOM_MIPI_SET CCI_REG8(0xf0f6) +#define TH7312_REG_CUSTOM_MIPI_STATUS CCI_REG8(0xf0f7) +#define TH7312_REG_CUSTOM_MIPI_RD CCI_REG8(0xf0f8) +#define TH7312_REG_CUSTOM_MIPI_TD CCI_REG8(0xf0f9) + +/* + * Firmware update registers. Those use a different address space than the + * normal operation ISP registers. + */ + +#define THP7312_REG_FW_DRIVABILITY CCI_REG32(0xd65c) +#define THP7312_REG_FW_DEST_BANK_ADDR CCI_REG32(0xff08) +#define THP7312_REG_FW_VERIFY_RESULT CCI_REG8(0xff60) +#define THP7312_REG_FW_RESET_FLASH CCI_REG8(0xff61) +#define THP7312_REG_FW_MEMORY_IO_SETTING CCI_REG8(0xff62) +#define THP7312_FW_MEMORY_IO_GPIO0 1 +#define THP7312_FW_MEMORY_IO_GPIO1 0 +#define THP7312_REG_FW_CRC_RESULT CCI_REG32(0xff64) +#define THP7312_REG_FW_STATUS CCI_REG8(0xfffc) + +#define THP7312_FW_VERSION(major, minor) (((major) << 8) | (minor)) +#define THP7312_FW_VERSION_MAJOR(v) ((v) >> 8) +#define THP7312_FW_VERSION_MINOR(v) ((v) & 0xff) + +enum thp7312_focus_method { + THP7312_FOCUS_METHOD_CONTRAST, + THP7312_FOCUS_METHOD_PDAF, + THP7312_FOCUS_METHOD_HYBRID, +}; + +/* + * enum thp7312_focus_state - State of the focus handler + * + * @THP7312_FOCUS_STATE_MANUAL: Manual focus, controlled through the + * V4L2_CID_FOCUS_ABSOLUTE control + * @THP7312_FOCUS_STATE_AUTO: Continuous auto-focus + * @THP7312_FOCUS_STATE_LOCKED: Lock the focus to a fixed position. This state + * is entered when switching from auto to manual mode. + * @THP7312_FOCUS_STATE_ONESHOT: One-shot auto-focus + * + * Valid transitions are as follow: + * + * digraph fsm { + * node [shape=circle]; + * + * manual [label="MANUAL"]; + * auto [label="AUTO"]; + * locked [label="LOCKED"]; + * oneshot [label="ONESHOT"]; + * + * manual -> auto [label="FOCUS_AUTO <- true"] + * locked -> auto [label="FOCUS_AUTO <- true"] + * oneshot -> auto [label="FOCUS_AUTO <- true"] + * auto -> locked [label="FOCUS_AUTO <- false"] + * + * locked -> manual [label="FOCUS_ABSOLUTE <- *"] + * oneshot -> manual [label="FOCUS_ABSOLUTE <- *"] + * + * manual -> oneshot [label="FOCUS_START <- *"] + * locked -> oneshot [label="FOCUS_START <- *"] + * } + */ +enum thp7312_focus_state { + THP7312_FOCUS_STATE_MANUAL, + THP7312_FOCUS_STATE_AUTO, + THP7312_FOCUS_STATE_LOCKED, + THP7312_FOCUS_STATE_ONESHOT, +}; + +enum thp7312_boot_mode { + THP7312_BOOT_MODE_2WIRE_SLAVE = 0, + THP7312_BOOT_MODE_SPI_MASTER = 1, +}; + +struct thp7312_frame_rate { + u32 fps; + u32 link_freq; + u8 reg_frame_rate_mode; +}; + +struct thp7312_mode_info { + u32 width; + u32 height; + u8 reg_image_size; + const struct thp7312_frame_rate *rates; +}; + +static const u32 thp7312_colour_fmts[] = { + MEDIA_BUS_FMT_YUYV8_1X16, +}; + +/* regulator supplies */ +static const char * const thp7312_supply_name[] = { + "vddcore", + "vhtermrx", + "vddtx", + "vddhost", + "vddcmos", + "vddgpio-0", + "vddgpio-1", +}; + +static const struct thp7312_mode_info thp7312_mode_info_data[] = { + { + .width = 1920, + .height = 1080, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_1920x1080, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 60, 387500000, 0x82 }, + { 0 } + }, + }, { + .width = 2048, + .height = 1536, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_2048x1536, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 0 } + } + }, { + .width = 3840, + .height = 2160, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_3840x2160, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 600000000, 0x81 }, + { 0 } + }, + }, { + .width = 4160, + .height = 3120, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_4160x3120, + .rates = (const struct thp7312_frame_rate[]) { + { 20, 600000000, 0x81 }, + { 0 } + }, + }, +}; + +struct thp7312_device; + +struct thp7312_sensor_info { + const char *model; +}; + +struct thp7312_sensor { + const struct thp7312_sensor_info *info; + u8 lane_remap; +}; + +struct thp7312_device { + struct device *dev; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(thp7312_supply_name)]; + struct clk *iclk; + + u8 lane_remap; + + struct thp7312_sensor sensors[1]; + + enum thp7312_boot_mode boot_mode; + + struct v4l2_ctrl_handler ctrl_handler; + bool ctrls_applied; + + /* These are protected by v4l2 active state */ + const struct thp7312_mode_info *current_mode; + const struct thp7312_frame_rate *current_rate; + s64 link_freq; + + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct { + struct v4l2_ctrl *focus_auto; + struct v4l2_ctrl *focus_absolute; + struct v4l2_ctrl *focus_start; + struct v4l2_ctrl *focus_method; + }; + + enum thp7312_focus_state focus_state; + + struct { + struct v4l2_ctrl *noise_reduction_auto; + struct v4l2_ctrl *noise_reduction_absolute; + }; + + /* Lock to protect fw_cancel */ + struct mutex fw_lock; + struct fw_upload *fwl; + u8 *fw_write_buf; + bool fw_cancel; + + u16 fw_version; +}; + +static const struct thp7312_sensor_info thp7312_sensor_info[] = { + { + .model = "sony,imx258", + }, +}; + +static inline struct thp7312_device *to_thp7312_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct thp7312_device, sd); +} + +/* ----------------------------------------------------------------------------- + * Device Access & Configuration + */ + +#define thp7312_read_poll_timeout(dev, addr, val, cond, sleep_us, timeout_us) \ +({ \ + int __ret, __err; \ + __ret = read_poll_timeout(cci_read, __err, __err || (cond), sleep_us, \ + timeout_us, false, (dev)->regmap, addr, \ + &(val), NULL); \ + __ret ? : __err; \ +}) + +static int thp7312_map_data_lanes(u8 *lane_remap, const u8 *lanes, u8 num_lanes) +{ + u8 used_lanes = 0; + u8 val = 0; + unsigned int i; + + /* + * The value that we write to the register is the index in the + * data-lanes array, so we need to do a conversion. Do this in the same + * pass as validating data-lanes. + */ + for (i = 0; i < num_lanes; i++) { + if (lanes[i] < 1 || lanes[i] > 4) + return -EINVAL; + + if (used_lanes & (BIT(lanes[i]))) + return -EINVAL; + + used_lanes |= BIT(lanes[i]); + + /* + * data-lanes is 1-indexed while the field position in the + * register is 0-indexed. + */ + val |= i << ((lanes[i] - 1) * 2); + } + + *lane_remap = val; + + return 0; +} + +static int thp7312_set_mipi_lanes(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret = 0; + u64 val; + + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_RD, + thp7312->sensors[0].lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_TD, + thp7312->lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_SET, 1, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, TH7312_REG_CUSTOM_MIPI_STATUS, + val, val == 0x00, 100000, 2000000); + if (ret) { + dev_err(dev, "Failed to poll MIPI lane status: %d\n", ret); + return ret; + } + + return 0; +} + +static int thp7312_change_mode(struct thp7312_device *thp7312, + const struct thp7312_mode_info *mode, + const struct thp7312_frame_rate *rate) +{ + struct device *dev = thp7312->dev; + u64 val = 0; + int ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_CAMERA_STATUS, val, + val == 0x80, 20000, 200000); + if (ret < 0) { + dev_err(dev, "%s(): failed to poll ISP: %d\n", __func__, ret); + return ret; + } + + cci_write(thp7312->regmap, THP7312_REG_VIDEO_IMAGE_SIZE, + mode->reg_image_size, &ret); + cci_write(thp7312->regmap, THP7312_REG_VIDEO_FRAME_RATE_MODE, + rate->reg_frame_rate_mode, &ret); + cci_write(thp7312->regmap, THP7312_REG_JPEG_COMPRESSION_FACTOR, 0x5e, + &ret); + cci_write(thp7312->regmap, THP7312_REG_SET_DRIVING_MODE, 0x01, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_DRIVING_MODE_STATUS, + val, val == 0x01, 20000, 100000); + if (ret < 0) { + dev_err(dev, "%s(): failed\n", __func__); + return ret; + } + + return 0; +} + +static int thp7312_set_framefmt(struct thp7312_device *thp7312, + struct v4l2_mbus_framefmt *format) +{ + u8 val; + + switch (format->code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + /* YUV422, UYVY */ + val = THP7312_REG_SET_OUTPUT_COLOR_UYVY; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + /* YUV422, YUYV */ + val = THP7312_REG_SET_OUTPUT_COLOR_YUY2; + break; + default: + /* Should never happen */ + return -EINVAL; + } + + return cci_write(thp7312->regmap, + THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION, val, NULL); +} + +static int thp7312_init_mode(struct thp7312_device *thp7312, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *fmt; + int ret; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + ret = thp7312_set_framefmt(thp7312, fmt); + if (ret) + return ret; + + return thp7312_change_mode(thp7312, thp7312->current_mode, + thp7312->current_rate); +} + +static int thp7312_stream_enable(struct thp7312_device *thp7312, bool enable) +{ + return cci_write(thp7312->regmap, THP7312_REG_SET_OUTPUT_ENABLE, + enable ? THP7312_OUTPUT_ENABLE : THP7312_OUTPUT_DISABLE, + NULL); +} + +static int thp7312_check_status_stream_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u64 status = 0; + int ret; + + while (status != 0x80) { + ret = cci_read(thp7312->regmap, THP7312_REG_CAMERA_STATUS, + &status, NULL); + if (ret) + return ret; + + if (status == 0x80) { + dev_dbg(dev, "Camera initialization done\n"); + return 0; + } + + if (status != 0x00) { + dev_err(dev, "Invalid camera status %llx\n", status); + return -EINVAL; + } + + dev_dbg(dev, "Camera initializing...\n"); + usleep_range(70000, 80000); + } + + return 0; +} + +static void thp7312_reset(struct thp7312_device *thp7312) +{ + unsigned long rate; + + gpiod_set_value_cansleep(thp7312->reset_gpio, 1); + + /* + * The minimum reset duration is 8 clock cycles, make it 10 to provide + * a safety margin. + */ + rate = clk_get_rate(thp7312->iclk); + fsleep(DIV_ROUND_UP(10 * USEC_PER_SEC, rate)); + + gpiod_set_value_cansleep(thp7312->reset_gpio, 0); + + /* + * TODO: The documentation states that the device needs 2ms to + * initialize after reset is deasserted. It then proceeds to load the + * firmware from the flash memory, which takes an unspecified amount of + * time. Check if this delay could be reduced. + */ + fsleep(300000); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static void __thp7312_power_off(struct thp7312_device *thp7312) +{ + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), thp7312->supplies); + clk_disable_unprepare(thp7312->iclk); +} + +static void thp7312_power_off(struct thp7312_device *thp7312) +{ + __thp7312_power_off(thp7312); +} + +static int __thp7312_power_on(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(thp7312->iclk); + if (ret < 0) { + dev_err(dev, "clk prepare enable failed\n"); + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + return ret; + } + + /* + * We cannot assume that turning off and on again will reset, so do a + * software reset on power up. + */ + thp7312_reset(thp7312); + + return 0; +} + +static int thp7312_power_on(struct thp7312_device *thp7312) +{ + int ret; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_check_status_stream_mode(thp7312); + if (ret < 0) + goto error; + + ret = thp7312_set_mipi_lanes(thp7312); + if (ret) + goto error; + + return 0; + +error: + thp7312_power_off(thp7312); + return ret; +} + +static int __maybe_unused thp7312_pm_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + thp7312_power_off(thp7312); + + thp7312->ctrls_applied = false; + + return 0; +} + +static int __maybe_unused thp7312_pm_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + return thp7312_power_on(thp7312); +} + +static const struct dev_pm_ops thp7312_pm_ops = { + SET_RUNTIME_PM_OPS(thp7312_pm_runtime_suspend, + thp7312_pm_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev Operations + */ + +static bool thp7312_find_bus_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312_colour_fmts); ++i) { + if (thp7312_colour_fmts[i] == code) + return true; + } + + return false; +} + +static const struct thp7312_mode_info * +thp7312_find_mode(unsigned int width, unsigned int height, bool nearest) +{ + const struct thp7312_mode_info *mode; + + mode = v4l2_find_nearest_size(thp7312_mode_info_data, + ARRAY_SIZE(thp7312_mode_info_data), + width, height, width, height); + + if (!nearest && (mode->width != width || mode->height != height)) + return NULL; + + return mode; +} + +static void thp7312_set_frame_rate(struct thp7312_device *thp7312, + const struct thp7312_frame_rate *rate) +{ + thp7312->link_freq = rate->link_freq; + thp7312->current_rate = rate; +} + +static int thp7312_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(thp7312_colour_fmts)) + return -EINVAL; + + code->code = thp7312_colour_fmts[code->index]; + + return 0; +} + +static int thp7312_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (!thp7312_find_bus_code(fse->code)) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(thp7312_mode_info_data)) + return -EINVAL; + + fse->min_width = thp7312_mode_info_data[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = thp7312_mode_info_data[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int thp7312_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + const struct thp7312_frame_rate *rate; + const struct thp7312_mode_info *mode; + unsigned int index = fie->index; + + if (!thp7312_find_bus_code(fie->code)) + return -EINVAL; + + mode = thp7312_find_mode(fie->width, fie->height, false); + if (!mode) + return -EINVAL; + + for (rate = mode->rates; rate->fps; ++rate, --index) { + if (!index) { + fie->interval.numerator = 1; + fie->interval.denominator = rate->fps; + + return 0; + } + } + + return -EINVAL; +} + +static int thp7312_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct v4l2_mbus_framefmt *fmt; + const struct thp7312_mode_info *mode; + + if (!thp7312_find_bus_code(mbus_fmt->code)) + mbus_fmt->code = thp7312_colour_fmts[0]; + + mode = thp7312_find_mode(mbus_fmt->width, mbus_fmt->height, true); + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + fmt->code = mbus_fmt->code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + + *mbus_fmt = *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + thp7312->current_mode = mode; + thp7312_set_frame_rate(thp7312, &mode->rates[0]); + } + + return 0; +} + +static int thp7312_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_subdev_state *sd_state; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + fi->interval.numerator = 1; + fi->interval.denominator = thp7312->current_rate->fps; + v4l2_subdev_unlock_state(sd_state); + + return 0; +} + +static int thp7312_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + const struct thp7312_mode_info *mode; + const struct thp7312_frame_rate *best_rate = NULL; + const struct thp7312_frame_rate *rate; + struct v4l2_subdev_state *sd_state; + unsigned int best_delta = UINT_MAX; + unsigned int fps; + + /* Avoid divisions by 0, pick the highest frame if the interval is 0. */ + fps = fi->interval.numerator + ? DIV_ROUND_CLOSEST(fi->interval.denominator, fi->interval.numerator) + : UINT_MAX; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + mode = thp7312->current_mode; + + for (rate = mode->rates; rate->fps && best_delta; ++rate) { + unsigned int delta = abs(rate->fps - fps); + + if (delta <= best_delta) { + best_delta = delta; + best_rate = rate; + } + } + + thp7312_set_frame_rate(thp7312, best_rate); + + v4l2_subdev_unlock_state(sd_state); + + fi->interval.numerator = 1; + fi->interval.denominator = best_rate->fps; + + return 0; +} + +static int thp7312_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_subdev_state *sd_state; + int ret; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + if (!enable) { + thp7312_stream_enable(thp7312, false); + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + v4l2_subdev_unlock_state(sd_state); + + return 0; + } + + ret = pm_runtime_resume_and_get(thp7312->dev); + if (ret) + goto finish_unlock; + + ret = thp7312_init_mode(thp7312, sd_state); + if (ret) + goto finish_pm; + + if (!thp7312->ctrls_applied) { + ret = __v4l2_ctrl_handler_setup(&thp7312->ctrl_handler); + if (ret) + goto finish_pm; + + thp7312->ctrls_applied = true; + } + + ret = thp7312_stream_enable(thp7312, true); + if (ret) + goto finish_pm; + + goto finish_unlock; + +finish_pm: + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); +finish_unlock: + v4l2_subdev_unlock_state(sd_state); + + return ret; +} + +static int thp7312_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + const struct thp7312_mode_info *default_mode = &thp7312_mode_info_data[0]; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + /* + * default init sequence initialize thp7312 to + * YUV422 YUYV VGA@30fps + */ + fmt->code = MEDIA_BUS_FMT_YUYV8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->width = default_mode->width; + fmt->height = default_mode->height; + fmt->field = V4L2_FIELD_NONE; + + return 0; +} + +static const struct v4l2_subdev_core_ops thp7312_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops thp7312_video_ops = { + .g_frame_interval = thp7312_g_frame_interval, + .s_frame_interval = thp7312_s_frame_interval, + .s_stream = thp7312_s_stream, +}; + +static const struct v4l2_subdev_pad_ops thp7312_pad_ops = { + .enum_mbus_code = thp7312_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = thp7312_set_fmt, + .enum_frame_size = thp7312_enum_frame_size, + .enum_frame_interval = thp7312_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops thp7312_subdev_ops = { + .core = &thp7312_core_ops, + .video = &thp7312_video_ops, + .pad = &thp7312_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops thp7312_internal_ops = { + .init_state = thp7312_init_state, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Control Operations + */ + +static inline struct thp7312_device *to_thp7312_from_ctrl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct thp7312_device, ctrl_handler); +} + +/* 0: 3000cm, 18: 8cm */ +static const u16 thp7312_focus_values[] = { + 3000, 1000, 600, 450, 350, + 290, 240, 200, 170, 150, + 140, 130, 120, 110, 100, + 93, 87, 83, 80, +}; + +static int thp7312_set_focus(struct thp7312_device *thp7312) +{ + enum thp7312_focus_state new_state = thp7312->focus_state; + bool continuous; + u8 af_control; + u8 af_setting; + int ret = 0; + + /* Start by programming the manual focus position if it has changed. */ + if (thp7312->focus_absolute->is_new) { + unsigned int value; + + value = thp7312_focus_values[thp7312->focus_absolute->val]; + + ret = cci_write(thp7312->regmap, + THP7312_REG_MANUAL_FOCUS_POSITION, value, NULL); + if (ret) + return ret; + } + + /* Calculate the new focus state. */ + switch (thp7312->focus_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + break; + + case THP7312_FOCUS_STATE_AUTO: + if (!thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_LOCKED; + break; + + case THP7312_FOCUS_STATE_LOCKED: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + + case THP7312_FOCUS_STATE_ONESHOT: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + } + + /* + * If neither the state nor the focus method has changed, and no new + * one-shot focus is requested, there's nothing new to program to the + * hardware. + */ + if (thp7312->focus_state == new_state && + !thp7312->focus_method->is_new && !thp7312->focus_start->is_new) + return 0; + + continuous = new_state == THP7312_FOCUS_STATE_MANUAL || + new_state == THP7312_FOCUS_STATE_ONESHOT; + + switch (thp7312->focus_method->val) { + case THP7312_FOCUS_METHOD_CONTRAST: + default: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST + : THP7312_REG_AF_SETTING_ONESHOT_CONTRAST; + break; + case THP7312_FOCUS_METHOD_PDAF: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_PDAF + : THP7312_REG_AF_SETTING_ONESHOT_PDAF; + break; + case THP7312_FOCUS_METHOD_HYBRID: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID + : THP7312_REG_AF_SETTING_ONESHOT_HYBRID; + break; + } + + switch (new_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + af_control = THP7312_REG_AF_CONTROL_MANUAL; + break; + case THP7312_FOCUS_STATE_AUTO: + case THP7312_FOCUS_STATE_ONESHOT: + af_control = THP7312_REG_AF_CONTROL_AF; + break; + case THP7312_FOCUS_STATE_LOCKED: + af_control = THP7312_REG_AF_CONTROL_LOCK; + break; + } + + cci_write(thp7312->regmap, THP7312_REG_AF_SETTING, af_setting, &ret); + + if (new_state == THP7312_FOCUS_STATE_MANUAL && + (thp7312->focus_state == THP7312_FOCUS_STATE_AUTO || + thp7312->focus_state == THP7312_FOCUS_STATE_ONESHOT)) { + /* When switching to manual state, lock AF first. */ + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, + THP7312_REG_AF_CONTROL_LOCK, &ret); + } + + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, af_control, &ret); + + if (ret) + return ret; + + thp7312->focus_state = new_state; + + return 0; +} + +static int thp7312_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct thp7312_device *thp7312 = to_thp7312_from_ctrl(ctrl); + int ret = 0; + u8 value; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return -EINVAL; + + if (!pm_runtime_get_if_active(thp7312->dev, true)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cci_write(thp7312->regmap, THP7312_REG_BRIGHTNESS, + ctrl->val + 10, &ret); + break; + + case V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION: + /* 0 = Auto adjust frame rate, 1 = Fix frame rate */ + cci_write(thp7312->regmap, THP7312_REG_AE_FIX_FRAME_RATE, + ctrl->val ? 0 : 1, &ret); + break; + + case V4L2_CID_FOCUS_AUTO: + case V4L2_CID_FOCUS_ABSOLUTE: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_THP7312_AUTO_FOCUS_METHOD: + ret = thp7312_set_focus(thp7312); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + value = (thp7312->hflip->val ? THP7312_REG_FLIP_MIRROR_MIRROR : 0) + | (thp7312->vflip->val ? THP7312_REG_FLIP_MIRROR_FLIP : 0); + + cci_write(thp7312->regmap, THP7312_REG_FLIP_MIRROR, value, &ret); + break; + + case V4L2_CID_THP7312_NOISE_REDUCTION_AUTO: + case V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE: + value = thp7312->noise_reduction_auto->val ? 0 + : THP7312_REG_NOISE_REDUCTION_FIXED | + thp7312->noise_reduction_absolute->val; + + cci_write(thp7312->regmap, THP7312_REG_NOISE_REDUCTION, value, + &ret); + break; + + case V4L2_CID_AUTO_WHITE_BALANCE: + value = ctrl->val ? THP7312_WB_MODE_AUTO : THP7312_WB_MODE_MANUAL; + + cci_write(thp7312->regmap, THP7312_REG_WB_MODE, value, &ret); + break; + + case V4L2_CID_RED_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_RED_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_BLUE_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_BLUE_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_AUTO_EXPOSURE_BIAS: + cci_write(thp7312->regmap, THP7312_REG_AE_EXPOSURE_COMPENSATION, + ctrl->val, &ret); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) { + value = THP7312_AE_FLICKER_MODE_60; + } else if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) { + value = THP7312_AE_FLICKER_MODE_50; + } else { + if (thp7312->fw_version == THP7312_FW_VERSION(40, 3)) { + /* THP7312_AE_FLICKER_MODE_DISABLE is not supported */ + value = THP7312_AE_FLICKER_MODE_50; + } else { + value = THP7312_AE_FLICKER_MODE_DISABLE; + } + } + + cci_write(thp7312->regmap, THP7312_REG_AE_FLICKER_MODE, + value, &ret); + break; + + case V4L2_CID_SATURATION: + cci_write(thp7312->regmap, THP7312_REG_SATURATION, + ctrl->val, &ret); + break; + + case V4L2_CID_CONTRAST: + cci_write(thp7312->regmap, THP7312_REG_CONTRAST, + ctrl->val, &ret); + break; + + case V4L2_CID_SHARPNESS: + cci_write(thp7312->regmap, THP7312_REG_SHARPNESS, + ctrl->val, &ret); + break; + + default: + break; + } + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops thp7312_ctrl_ops = { + .s_ctrl = thp7312_s_ctrl, +}; + +/* + * Refer to Documentation/userspace-api/media/drivers/thp7312.rst for details. + */ +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_cdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_CONTRAST, + .max = THP7312_FOCUS_METHOD_CONTRAST, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_pdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_HYBRID, + .max = THP7312_FOCUS_METHOD_HYBRID, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_v4l2_ctrls_custom[] = { + { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION, + .name = "Low Light Compensation", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_AUTO, + .name = "Noise Reduction Auto", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE, + .name = "Noise Reduction Level", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .def = 0, + .max = 10, + .step = 1, + }, +}; + +static const s64 exp_bias_qmenu[] = { + -2000, -1667, -1333, -1000, -667, -333, 0, 333, 667, 1000, 1333, 1667, 2000 +}; + +static int thp7312_init_controls(struct thp7312_device *thp7312) +{ + struct v4l2_ctrl_handler *hdl = &thp7312->ctrl_handler; + struct device *dev = thp7312->dev; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + unsigned int num_controls; + unsigned int i; + u8 af_support; + int ret; + + /* + * Check what auto-focus methods the connected sensor supports, if any. + * Firmwares before v90.03 didn't expose the AF_SUPPORT register, + * consider both CDAF and PDAF as supported in that case. + */ + if (thp7312->fw_version >= THP7312_FW_VERSION(90, 3)) { + u64 val; + + ret = cci_read(thp7312->regmap, THP7312_REG_AF_SUPPORT, &val, + NULL); + if (ret) + return ret; + + af_support = val & (THP7312_AF_SUPPORT_PDAF | + THP7312_AF_SUPPORT_CONTRAST); + } else { + af_support = THP7312_AF_SUPPORT_PDAF + | THP7312_AF_SUPPORT_CONTRAST; + } + + num_controls = 14 + ARRAY_SIZE(thp7312_v4l2_ctrls_custom) + + (af_support ? 4 : 0); + + v4l2_ctrl_handler_init(hdl, num_controls); + + if (af_support) { + const struct v4l2_ctrl_config *af_method; + + af_method = af_support & THP7312_AF_SUPPORT_PDAF + ? &thp7312_ctrl_focus_method_pdaf + : &thp7312_ctrl_focus_method_cdaf; + + thp7312->focus_state = THP7312_FOCUS_STATE_MANUAL; + + thp7312->focus_auto = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_AUTO, + 0, 1, 1, 1); + thp7312->focus_absolute = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, + 0, ARRAY_SIZE(thp7312_focus_values), + 1, 0); + thp7312->focus_method = + v4l2_ctrl_new_custom(hdl, af_method, NULL); + thp7312->focus_start = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_FOCUS_START, + 1, 1, 1, 1); + + v4l2_ctrl_cluster(4, &thp7312->focus_auto); + } + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_RED_BALANCE, + 32, 255, 1, 64); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BLUE_BALANCE, + 32, 255, 1, 50); + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BRIGHTNESS, + -10, 10, 1, 0); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SATURATION, + 0, 31, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_CONTRAST, + 0, 20, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SHARPNESS, + 0, 31, 1, 8); + + thp7312->hflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + thp7312->vflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, &thp7312->hflip); + + v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(exp_bias_qmenu) - 1, + ARRAY_SIZE(exp_bias_qmenu) / 2, exp_bias_qmenu); + + v4l2_ctrl_new_std_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + link_freq = v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_LINK_FREQ, 0, 0, + &thp7312->link_freq); + + /* Set properties from fwnode (e.g. rotation, orientation). */ + ret = v4l2_fwnode_device_parse(dev, &props); + if (ret) { + dev_err(dev, "Failed to parse fwnode: %d\n", ret); + goto error; + } + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &thp7312_ctrl_ops, &props); + if (ret) { + dev_err(dev, "Failed to create new v4l2 ctrl for fwnode properties: %d\n", ret); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_v4l2_ctrls_custom); i++) { + const struct v4l2_ctrl_config *ctrl_cfg = + &thp7312_v4l2_ctrls_custom[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(hdl, ctrl_cfg, NULL); + + if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_AUTO) + thp7312->noise_reduction_auto = ctrl; + else if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE) + thp7312->noise_reduction_absolute = ctrl; + } + + v4l2_ctrl_cluster(2, &thp7312->noise_reduction_auto); + + if (hdl->error) { + dev_err(dev, "v4l2_ctrl_handler error\n"); + ret = hdl->error; + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + return ret; + +error: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Firmware Update + */ + +/* + * The firmware data is made of 128kB of RAM firmware, followed by a + * variable-size "header". Both are stored in flash memory. + */ +#define THP7312_FW_RAM_SIZE (128 * 1024) +#define THP7312_FW_MIN_SIZE (THP7312_FW_RAM_SIZE + 4) +#define THP7312_FW_MAX_SIZE (THP7312_FW_RAM_SIZE + 64 * 1024) + +/* + * Data is first uploaded to the THP7312 128kB SRAM, and then written to flash. + * The SRAM is exposed over I2C as 32kB banks, and up to 4kB of data can be + * transferred in a single I2C write. + */ +#define THP7312_RAM_BANK_SIZE (32 * 1024) +#define THP7312_FW_DOWNLOAD_UNIT (4 * 1024) + +#define THP7312_FLASH_MEMORY_ERASE_TIMEOUT 40 + +#define THP7312_FLASH_MAX_REG_READ_SIZE 10 +#define THP7312_FLASH_MAX_REG_DATA_SIZE 10 + +static const u8 thp7312_cmd_config_flash_mem_if[] = { + 0xd5, 0x18, 0x00, 0x00, 0x00, 0x80 +}; + +static const u8 thp7312_cmd_write_to_reg[] = { + 0xd5, 0x0c, 0x80, 0x00, 0x00, 0x00 +}; + +static const u8 thp7312_cmd_read_reg[] = { + 0xd5, 0x04 +}; + +/* + * THP7312 Write data from RAM to Flash Memory + * Command ID FF700F + * Format: FF700F AA AA AA BB BB BB + * AA AA AA: destination start address + * BB BB BB: (write size - 1) + * Source address always starts from 0 + */ +static const u8 thp7312_cmd_write_ram_to_flash[] = { 0xff, 0x70, 0x0f }; + +/* + * THP7312 Calculate CRC command + * Command ID: FF70 09 + * Format: FF70 09 AA AA AA BB BB BB + * AA AA AA: Start address of calculation + * BB BB BB: (calculate size - 1) + */ +static const u8 thp7312_cmd_calc_crc[] = { 0xff, 0x70, 0x09 }; + +static const u8 thp7312_jedec_rdid[] = { SPINOR_OP_RDID, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_rdsr[] = { SPINOR_OP_RDSR, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_wen[] = { SPINOR_OP_WREN }; + +static int thp7312_read_firmware_version(struct thp7312_device *thp7312) +{ + u64 val = 0; + int ret = 0; + u8 major; + u8 minor; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_1, &val, &ret); + major = val; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_2, &val, &ret); + minor = val; + + thp7312->fw_version = THP7312_FW_VERSION(major, minor); + return ret; +} + +static int thp7312_write_buf(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + int ret; + + ret = i2c_master_send(client, write_buf, write_size); + return ret >= 0 ? 0 : ret; +} + +static int __thp7312_flash_reg_write(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct device *dev = thp7312->dev; + u8 temp_write_buf[THP7312_FLASH_MAX_REG_DATA_SIZE + 2]; + int ret; + + if (write_size > THP7312_FLASH_MAX_REG_DATA_SIZE) { + dev_err(dev, "%s: Write size error size = %d\n", + __func__, write_size); + return -EINVAL; + } + + ret = thp7312_write_buf(thp7312, thp7312_cmd_config_flash_mem_if, + sizeof(thp7312_cmd_config_flash_mem_if)); + if (ret < 0) { + dev_err(dev, "%s: Failed to config flash memory IF: %d\n", + __func__, ret); + return ret; + } + + temp_write_buf[0] = 0xd5; + temp_write_buf[1] = 0x00; + memcpy((temp_write_buf + 2), write_buf, write_size); + ret = thp7312_write_buf(thp7312, temp_write_buf, write_size + 2); + if (ret < 0) + return ret; + + thp7312_write_buf(thp7312, thp7312_cmd_write_to_reg, + sizeof(thp7312_cmd_write_to_reg)); + + return 0; +} + +static int __thp7312_flash_reg_read(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size, + u8 *read_buf, u16 read_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + struct i2c_msg msgs[2]; + int ret; + + ret = __thp7312_flash_reg_write(thp7312, write_buf, write_size); + if (ret) + return ret; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(thp7312_cmd_read_reg), + msgs[0].buf = (u8 *)thp7312_cmd_read_reg; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = read_size; + msgs[1].buf = read_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + return ret >= 0 ? 0 : ret; +} + +#define thp7312_flash_reg_write(thp7312, wrbuf) \ + __thp7312_flash_reg_write(thp7312, wrbuf, sizeof(wrbuf)) + +#define thp7312_flash_reg_read(thp7312, wrbuf, rdbuf) \ + __thp7312_flash_reg_read(thp7312, wrbuf, sizeof(wrbuf), \ + rdbuf, sizeof(rdbuf)) + +static enum fw_upload_err thp7312_fw_prepare_config(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_MEMORY_IO_SETTING, + THP7312_FW_MEMORY_IO_GPIO0, NULL); + if (ret) { + dev_err(dev, "Failed to set flash memory I/O\n"); + return FW_UPLOAD_ERR_HW_ERROR; + } + + /* Set max drivability. */ + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DRIVABILITY, 0x00777777, + NULL); + if (ret) { + dev_err(dev, "Failed to set drivability: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_check(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[3] = { 0 }; + int ret; + + /* Get JEDEC ID */ + ret = thp7312_flash_reg_read(thp7312, thp7312_jedec_rdid, read_buf); + if (ret) { + dev_err(dev, "Failed to get JEDEC ID: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + dev_dbg(dev, "Flash Memory: JEDEC ID = 0x%x 0x%x 0x%x\n", + read_buf[0], read_buf[1], read_buf[2]); + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_reset(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_RESET_FLASH, 0x81, NULL); + if (ret) { + dev_err(dev, "Failed to reset flash memory: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +/* TODO: Erase only the amount of blocks necessary */ +static enum fw_upload_err thp7312_flash_erase(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[1] = { 0 }; + unsigned int i; + u8 block; + int ret; + + for (block = 0; block < 3; block++) { + const u8 jedec_se[] = { SPINOR_OP_SE, block, 0x00, 0x00 }; + + ret = thp7312_flash_reg_write(thp7312, thp7312_jedec_wen); + if (ret < 0) { + dev_err(dev, "Failed to enable flash for writing\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + ret = thp7312_flash_reg_write(thp7312, jedec_se); + if (ret < 0) { + dev_err(dev, "Failed to erase flash sector\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + for (i = 0; i < THP7312_FLASH_MEMORY_ERASE_TIMEOUT; i++) { + usleep_range(100000, 101000); + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, + read_buf); + + /* Check Busy bit. Busy == 0x0 means erase complete. */ + if (!(read_buf[0] & SR_WIP)) + break; + } + + if (i == THP7312_FLASH_MEMORY_ERASE_TIMEOUT) + return FW_UPLOAD_ERR_TIMEOUT; + } + + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, read_buf); + + /* Check WEL bit. */ + if (read_buf[0] & SR_WEL) + return FW_UPLOAD_ERR_HW_ERROR; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err +thp7312_write_download_data_by_unit(struct thp7312_device *thp7312, + unsigned int addr, const u8 *data, + unsigned int size) +{ + struct device *dev = thp7312->dev; + u8 *write_buf = thp7312->fw_write_buf; + int ret; + + dev_dbg(dev, "%s: addr = 0x%04x, data = 0x%p, size = %u\n", + __func__, addr, data, size); + + write_buf[0] = (addr >> 8) & 0xff; + write_buf[1] = (addr >> 0) & 0xff; + memcpy(&write_buf[2], data, size); + + /* + * THP7312 Firmware download to RAM + * Command ID (address to download): 0x0000 - 0x7fff + * Format:: 0000 XX XX XX ........ XX + */ + ret = thp7312_write_buf(thp7312, write_buf, size + 2); + if (ret < 0) + dev_err(dev, "Unit transfer ERROR %s(): ret = %d\n", __func__, ret); + + return ret >= 0 ? FW_UPLOAD_ERR_NONE : FW_UPLOAD_ERR_RW_ERROR; +} + +static enum fw_upload_err thp7312_fw_load_to_ram(struct thp7312_device *thp7312, + const u8 *data, u32 size) +{ + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + unsigned int num_banks; + unsigned int i, j; + + num_banks = DIV_ROUND_UP(size, THP7312_RAM_BANK_SIZE); + + dev_dbg(dev, "%s: loading %u bytes in SRAM (%u banks)\n", __func__, + size, num_banks); + + for (i = 0; i < num_banks; i++) { + const u32 bank_addr = 0x10000000 | (i * THP7312_RAM_BANK_SIZE); + unsigned int bank_size; + unsigned int num_chunks; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DEST_BANK_ADDR, + bank_addr, NULL); + if (ret) + return FW_UPLOAD_ERR_HW_ERROR; + + bank_size = min_t(u32, size, THP7312_RAM_BANK_SIZE); + num_chunks = DIV_ROUND_UP(bank_size, THP7312_FW_DOWNLOAD_UNIT); + + dev_dbg(dev, "%s: loading %u bytes in SRAM bank %u (%u chunks)\n", + __func__, bank_size, i, num_chunks); + + for (j = 0 ; j < num_chunks; j++) { + unsigned int chunk_addr; + unsigned int chunk_size; + + chunk_addr = j * THP7312_FW_DOWNLOAD_UNIT; + chunk_size = min_t(u32, size, THP7312_FW_DOWNLOAD_UNIT); + + ret = thp7312_write_download_data_by_unit(thp7312, chunk_addr, + data, chunk_size); + if (ret != FW_UPLOAD_ERR_NONE) { + dev_err(dev, "Unit transfer ERROR at bank transfer %s(): %d\n", + __func__, j); + return ret; + } + + data += chunk_size; + size -= chunk_size; + } + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_write_to_flash(struct thp7312_device *thp7312, + u32 dest, u32 write_size) +{ + u8 command[sizeof(thp7312_cmd_write_ram_to_flash) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_write_ram_to_flash); + u64 val; + int ret; + + memcpy(command, thp7312_cmd_write_ram_to_flash, cmd_size); + + command[cmd_size] = (dest & 0xff0000) >> 16; + command[cmd_size + 1] = (dest & 0x00ff00) >> 8; + command[cmd_size + 2] = (dest & 0x0000ff); + command[cmd_size + 3] = ((write_size - 1) & 0xff0000) >> 16; + command[cmd_size + 4] = ((write_size - 1) & 0x00ff00) >> 8; + command[cmd_size + 5] = ((write_size - 1) & 0x0000ff); + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(8000000, 8100000); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_VERIFY_RESULT, &val, + NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + return val ? FW_UPLOAD_ERR_HW_ERROR : FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_check_crc(struct thp7312_device *thp7312, + const u8 *fw_data, u32 fw_size) +{ + struct device *dev = thp7312->dev; + u16 header_size = fw_size - THP7312_FW_RAM_SIZE; + u8 command[sizeof(thp7312_cmd_calc_crc) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_calc_crc); + u32 size = THP7312_FW_RAM_SIZE - 4; + u32 fw_crc; + u64 crc; + int ret; + + memcpy(command, thp7312_cmd_calc_crc, cmd_size); + + command[cmd_size] = 0; + command[cmd_size + 1] = (header_size >> 8) & 0xff; + command[cmd_size + 2] = header_size & 0xff; + + command[cmd_size + 3] = (size >> 16) & 0xff; + command[cmd_size + 4] = (size >> 8) & 0xff; + command[cmd_size + 5] = size & 0xff; + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(2000000, 2100000); + + fw_crc = get_unaligned_be32(&fw_data[fw_size - 4]); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_CRC_RESULT, &crc, NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + if (fw_crc != crc) { + dev_err(dev, "CRC mismatch: firmware 0x%08x, flash 0x%08llx\n", + fw_crc, crc); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare(struct fw_upload *fw_upload, + const u8 *data, u32 size) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = false; + mutex_unlock(&thp7312->fw_lock); + + if (size < THP7312_FW_MIN_SIZE || size > THP7312_FW_MAX_SIZE) { + dev_err(dev, "%s: Invalid firmware size %d; must be between %d and %d\n", + __func__, size, THP7312_FW_MIN_SIZE, THP7312_FW_MAX_SIZE); + return FW_UPLOAD_ERR_INVALID_SIZE; + } + + ret = thp7312_fw_prepare_config(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_check(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_reset(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + mutex_lock(&thp7312->fw_lock); + ret = thp7312->fw_cancel ? FW_UPLOAD_ERR_CANCELED : FW_UPLOAD_ERR_NONE; + mutex_unlock(&thp7312->fw_lock); + + return ret; +} + +static enum fw_upload_err thp7312_fw_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, + u32 size, u32 *written) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + u16 header_size = size - THP7312_FW_RAM_SIZE; + enum fw_upload_err ret; + bool cancel; + + mutex_lock(&thp7312->fw_lock); + cancel = thp7312->fw_cancel; + mutex_unlock(&thp7312->fw_lock); + + if (cancel) + return FW_UPLOAD_ERR_CANCELED; + + ret = thp7312_flash_erase(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data, THP7312_FW_RAM_SIZE); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0, 0x1ffff); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data + THP7312_FW_RAM_SIZE, header_size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0x20000, header_size - 1); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_check_crc(thp7312, data, size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + dev_info(dev, "Successfully wrote firmware\n"); + + *written = size; + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_poll_complete(struct fw_upload *fw_upload) +{ + return FW_UPLOAD_ERR_NONE; +} + +/* + * This may be called asynchronously with an on-going update. All other + * functions are called sequentially in a single thread. To avoid contention on + * register accesses, only update the cancel_request flag. Other functions will + * check this flag and handle the cancel request synchronously. + */ +static void thp7312_fw_cancel(struct fw_upload *fw_upload) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = true; + mutex_unlock(&thp7312->fw_lock); +} + +static const struct fw_upload_ops thp7312_fw_upload_ops = { + .prepare = thp7312_fw_prepare, + .write = thp7312_fw_write, + .poll_complete = thp7312_fw_poll_complete, + .cancel = thp7312_fw_cancel, +}; + +static int thp7312_register_flash_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + struct fw_upload *fwl; + u64 val; + int ret; + + dev_info(dev, "booted in flash mode\n"); + + mutex_init(&thp7312->fw_lock); + + thp7312->fw_write_buf = devm_kzalloc(dev, THP7312_FW_DOWNLOAD_UNIT + 2, + GFP_KERNEL); + if (!thp7312->fw_write_buf) + return -ENOMEM; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to power on\n"); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_STATUS, &val, NULL); + if (ret) { + dev_err_probe(dev, ret, "Camera status read failed\n"); + goto error; + } + + fwl = firmware_upload_register(THIS_MODULE, dev, "thp7312-firmware", + &thp7312_fw_upload_ops, thp7312); + if (IS_ERR(fwl)) { + ret = PTR_ERR(fwl); + dev_err_probe(dev, ret, "Failed to register firmware upload\n"); + goto error; + } + + thp7312->fwl = fwl; + return 0; + +error: + __thp7312_power_off(thp7312); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int thp7312_get_regulators(struct thp7312_device *thp7312) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312->supplies); i++) + thp7312->supplies[i].supply = thp7312_supply_name[i]; + + return devm_regulator_bulk_get(thp7312->dev, + ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); +} + +static int thp7312_sensor_parse_dt(struct thp7312_device *thp7312, + struct fwnode_handle *node) +{ + struct device *dev = thp7312->dev; + struct thp7312_sensor *sensor; + const char *model; + u8 data_lanes[4]; + u32 values[4]; + unsigned int i; + u32 reg; + int ret; + + /* Retrieve the sensor index from the reg property. */ + ret = fwnode_property_read_u32(node, "reg", ®); + if (ret < 0) { + dev_err(dev, "'reg' property missing in sensor node\n"); + return -EINVAL; + } + + if (reg >= ARRAY_SIZE(thp7312->sensors)) { + dev_err(dev, "Out-of-bounds 'reg' value %u\n", reg); + return -EINVAL; + } + + sensor = &thp7312->sensors[reg]; + if (sensor->info) { + dev_err(dev, "Duplicate entry for sensor %u\n", reg); + return -EINVAL; + } + + ret = fwnode_property_read_string(node, "thine,model", &model); + if (ret < 0) { + dev_err(dev, "'thine,model' property missing in sensor node\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_sensor_info); i++) { + const struct thp7312_sensor_info *info = + &thp7312_sensor_info[i]; + + if (!strcmp(info->model, model)) { + sensor->info = info; + break; + } + } + + if (!sensor->info) { + dev_err(dev, "Unsupported sensor model %s\n", model); + return -EINVAL; + } + + ret = fwnode_property_read_u32_array(node, "data-lanes", values, + ARRAY_SIZE(values)); + if (ret < 0) { + dev_err(dev, "Failed to read property data-lanes: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data_lanes); ++i) + data_lanes[i] = values[i]; + + ret = thp7312_map_data_lanes(&sensor->lane_remap, data_lanes, + ARRAY_SIZE(data_lanes)); + if (ret) { + dev_err(dev, "Invalid sensor@%u data-lanes value\n", reg); + return ret; + } + + return 0; +} + +static int thp7312_parse_dt(struct thp7312_device *thp7312) +{ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = thp7312->dev; + struct fwnode_handle *endpoint; + struct fwnode_handle *sensors; + unsigned int num_sensors = 0; + struct fwnode_handle *node; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "Endpoint node not found\n"); + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) + return dev_err_probe(dev, ret, "Could not parse endpoint\n"); + + ret = thp7312_map_data_lanes(&thp7312->lane_remap, + ep.bus.mipi_csi2.data_lanes, + ep.bus.mipi_csi2.num_data_lanes); + if (ret) { + dev_err(dev, "Invalid data-lanes value\n"); + return ret; + } + + /* + * The thine,boot-mode property is optional and default to + * THP7312_BOOT_MODE_SPI_MASTER (1). + */ + thp7312->boot_mode = THP7312_BOOT_MODE_SPI_MASTER; + ret = device_property_read_u32(dev, "thine,boot-mode", + &thp7312->boot_mode); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Property '%s' is invalid\n", + "thine,boot-mode"); + + if (thp7312->boot_mode != THP7312_BOOT_MODE_2WIRE_SLAVE && + thp7312->boot_mode != THP7312_BOOT_MODE_SPI_MASTER) + return dev_err_probe(dev, -EINVAL, "Invalid '%s' value %u\n", + "thine,boot-mode", thp7312->boot_mode); + + /* Sensors */ + sensors = device_get_named_child_node(dev, "sensors"); + if (!sensors) { + dev_err(dev, "'sensors' child node not found\n"); + return -EINVAL; + } + + fwnode_for_each_available_child_node(sensors, node) { + if (fwnode_name_eq(node, "sensor")) { + if (!thp7312_sensor_parse_dt(thp7312, node)) + num_sensors++; + } + } + + fwnode_handle_put(sensors); + + if (!num_sensors) { + dev_err(dev, "No sensor found\n"); + return -EINVAL; + } + + return 0; +} + +static int thp7312_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev_state *sd_state; + struct thp7312_device *thp7312; + int ret; + + thp7312 = devm_kzalloc(dev, sizeof(*thp7312), GFP_KERNEL); + if (!thp7312) + return -ENOMEM; + + thp7312->dev = dev; + + thp7312->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(thp7312->regmap)) + return dev_err_probe(dev, PTR_ERR(thp7312->regmap), + "Unable to initialize I2C\n"); + + ret = thp7312_parse_dt(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_get_regulators(thp7312); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + thp7312->iclk = devm_clk_get(dev, NULL); + if (IS_ERR(thp7312->iclk)) + return dev_err_probe(dev, PTR_ERR(thp7312->iclk), + "Failed to get iclk\n"); + + thp7312->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(thp7312->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(thp7312->reset_gpio), + "Failed to get reset gpio\n"); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) + return thp7312_register_flash_mode(thp7312); + + v4l2_i2c_subdev_init(&thp7312->sd, client, &thp7312_subdev_ops); + thp7312->sd.internal_ops = &thp7312_internal_ops; + thp7312->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + thp7312->pad.flags = MEDIA_PAD_FL_SOURCE; + thp7312->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&thp7312->sd.entity, 1, &thp7312->pad); + if (ret) + return ret; + + /* + * Enable power management. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the device manually here. + */ + ret = thp7312_power_on(thp7312); + if (ret) + goto err_entity_cleanup; + + ret = thp7312_read_firmware_version(thp7312); + if (ret < 0) { + dev_err(dev, "Camera is not found\n"); + goto err_power_off; + } + + ret = thp7312_init_controls(thp7312); + if (ret) { + dev_err(dev, "Failed to initialize controls\n"); + goto err_power_off; + } + + thp7312->sd.ctrl_handler = &thp7312->ctrl_handler; + thp7312->sd.state_lock = thp7312->ctrl_handler.lock; + + ret = v4l2_subdev_init_finalize(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev active state initialization failed\n"); + goto err_free_ctrls; + } + + sd_state = v4l2_subdev_lock_and_get_active_state(&thp7312->sd); + thp7312->current_mode = &thp7312_mode_info_data[0]; + thp7312_set_frame_rate(thp7312, &thp7312->current_mode->rates[0]); + v4l2_subdev_unlock_state(sd_state); + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = v4l2_async_register_subdev(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev registration failed\n"); + goto err_pm; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + dev_info(dev, "THP7312 firmware version %02u.%02u\n", + THP7312_FW_VERSION_MAJOR(thp7312->fw_version), + THP7312_FW_VERSION_MINOR(thp7312->fw_version)); + + return 0; + +err_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + v4l2_subdev_cleanup(&thp7312->sd); +err_free_ctrls: + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); +err_power_off: + thp7312_power_off(thp7312); +err_entity_cleanup: + media_entity_cleanup(&thp7312->sd.entity); + return ret; +} + +static void thp7312_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) { + firmware_upload_unregister(thp7312->fwl); + __thp7312_power_off(thp7312); + return; + } + + v4l2_async_unregister_subdev(&thp7312->sd); + v4l2_subdev_cleanup(&thp7312->sd); + media_entity_cleanup(&thp7312->sd.entity); + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(thp7312->dev); + if (!pm_runtime_status_suspended(thp7312->dev)) + thp7312_power_off(thp7312); + pm_runtime_set_suspended(thp7312->dev); +} + +static const struct of_device_id thp7312_dt_ids[] = { + { .compatible = "thine,thp7312" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, thp7312_dt_ids); + +static struct i2c_driver thp7312_i2c_driver = { + .driver = { + .name = "thp7312", + .pm = &thp7312_pm_ops, + .of_match_table = thp7312_dt_ids, + }, + .probe = thp7312_probe, + .remove = thp7312_remove, +}; + +module_i2c_driver(thp7312_i2c_driver); + +MODULE_DESCRIPTION("THP7312 MIPI Camera Subdev Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 76a92bfb6fdee7..9fc586cfdcd874 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1209,8 +1209,8 @@ static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, /**************************************************************************** V4L2 subdev pad ops ****************************************************************************/ -static int tvp5150_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tvp5150_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; @@ -1722,7 +1722,6 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { }; static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { - .init_cfg = tvp5150_init_cfg, .enum_mbus_code = tvp5150_enum_mbus_code, .enum_frame_size = tvp5150_enum_frame_size, .set_fmt = tvp5150_fill_fmt, @@ -1741,6 +1740,7 @@ static const struct v4l2_subdev_ops tvp5150_ops = { }; static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { + .init_state = tvp5150_init_state, .registered = tvp5150_registered, .open = tvp5150_open, .close = tvp5150_close, diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index a8ee5650dfcc51..8ce21bc8831f0f 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -353,8 +353,8 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, } } -static int mei_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mei_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mbusformat; struct mei_csi *csi = sd_to_csi(sd); @@ -561,7 +561,6 @@ static const struct v4l2_subdev_video_ops mei_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = { - .init_cfg = mei_csi_init_cfg, .get_fmt = mei_csi_get_fmt, .set_fmt = mei_csi_set_fmt, }; @@ -571,6 +570,10 @@ static const struct v4l2_subdev_ops mei_csi_subdev_ops = { .pad = &mei_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops mei_csi_internal_ops = { + .init_state = mei_csi_init_state, +}; + static const struct media_entity_operations mei_csi_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -737,6 +740,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, csi->subdev.dev = &cldev->dev; v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops); + csi->subdev.internal_ops = &mei_csi_internal_ops; v4l2_set_subdevdata(&csi->subdev, csi); csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 373321184fe609..d8510707bb974f 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -365,8 +365,8 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, return 0; } -static int csi2rx_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int csi2rx_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .pad = CSI2RX_PAD_SINK, @@ -388,7 +388,6 @@ static int csi2rx_init_cfg(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2rx_set_fmt, - .init_cfg = csi2rx_init_cfg, }; static const struct v4l2_subdev_video_ops csi2rx_video_ops = { @@ -400,6 +399,10 @@ static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .pad = &csi2rx_pad_ops, }; +static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { + .init_state = csi2rx_init_state, +}; + static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, struct v4l2_async_connection *asd) @@ -606,6 +609,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.owner = THIS_MODULE; csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); + csi2rx->subdev.internal_ops = &csi2rx_internal_ops; v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index 87a2d092aad2fa..fee73260bb1e11 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -435,8 +435,8 @@ static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) return ret; } -static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, - struct v4l2_subdev_state *sd_state) +static int csi2dc_init_state(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, 0); @@ -461,7 +461,6 @@ static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { .enum_mbus_code = csi2dc_enum_mbus_code, .set_fmt = csi2dc_set_fmt, .get_fmt = csi2dc_get_fmt, - .init_cfg = csi2dc_init_cfg, }; static const struct v4l2_subdev_video_ops csi2dc_video_ops = { @@ -473,6 +472,10 @@ static const struct v4l2_subdev_ops csi2dc_subdev_ops = { .video = &csi2dc_video_ops, }; +static const struct v4l2_subdev_internal_ops csi2dc_internal_ops = { + .init_state = csi2dc_init_state, +}; + static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) @@ -677,6 +680,7 @@ static int csi2dc_probe(struct platform_device *pdev) } v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); + csi2dc->csi2dc_sd.internal_ops = &csi2dc_internal_ops; csi2dc->csi2dc_sd.owner = THIS_MODULE; csi2dc->csi2dc_sd.dev = dev; diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c index 58dffbc9fbcbbd..e83463543e2100 100644 --- a/drivers/media/platform/microchip/microchip-isc-scaler.c +++ b/drivers/media/platform/microchip/microchip-isc-scaler.c @@ -145,8 +145,8 @@ static int isc_scaler_g_sel(struct v4l2_subdev *sd, return 0; } -static int isc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int isc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, 0); @@ -170,7 +170,6 @@ static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = { .set_fmt = isc_scaler_set_fmt, .get_fmt = isc_scaler_get_fmt, .get_selection = isc_scaler_g_sel, - .init_cfg = isc_scaler_init_cfg, }; static const struct media_entity_operations isc_scaler_entity_ops = { @@ -181,11 +180,16 @@ static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = { .pad = &isc_scaler_pad_ops, }; +static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = { + .init_state = isc_scaler_init_state, +}; + int isc_scaler_init(struct isc_device *isc) { int ret; v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops); + isc->scaler_sd.internal_ops = &isc_scaler_internal_ops; isc->scaler_sd.owner = THIS_MODULE; isc->scaler_sd.dev = isc->dev; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index eb016ba98c011f..db8ff5f5c4d3cd 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1122,8 +1122,8 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return 0; } -static int mipi_csis_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mipi_csis_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = CSIS_PAD_SINK, @@ -1163,7 +1163,6 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { }; static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { - .init_cfg = mipi_csis_init_cfg, .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mipi_csis_set_fmt, @@ -1176,6 +1175,10 @@ static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { .pad = &mipi_csis_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = { + .init_state = mipi_csis_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -1350,6 +1353,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) int ret; v4l2_subdev_init(sd, &mipi_csis_subdev_ops); + sd->internal_ops = &mipi_csis_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "csis-%s", dev_name(csis->dev)); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index da29971acf26f6..1d6248211c76e0 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -1728,8 +1728,8 @@ static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx7_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct imx7_csi_pixfmt *cc; int i; @@ -2005,7 +2005,6 @@ static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { - .init_cfg = imx7_csi_init_cfg, .enum_mbus_code = imx7_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx7_csi_set_fmt, @@ -2018,6 +2017,7 @@ static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { + .init_state = imx7_csi_init_state, .registered = imx7_csi_registered, .unregistered = imx7_csi_unregistered, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 26caca55f9d666..93a55c97cd1738 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -175,8 +175,8 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, return sd; } -static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); struct v4l2_subdev_krouting routing = { }; @@ -403,7 +403,6 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { - .init_cfg = mxc_isi_crossbar_init_cfg, .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_crossbar_set_fmt, @@ -416,6 +415,10 @@ static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = { .pad = &mxc_isi_crossbar_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_crossbar_internal_ops = { + .init_state = mxc_isi_crossbar_init_state, +}; + static const struct media_entity_operations mxc_isi_cross_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -437,6 +440,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) xbar->isi = isi; v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops); + sd->internal_ops = &mxc_isi_crossbar_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, "crossbar", sizeof(sd->name)); sd->dev = isi->dev; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 14c6da392803e0..d76eb58deb096b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -341,8 +341,8 @@ mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, return v4l2_subdev_state_get_compose(state, pad); } -static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_pipe *pipe = to_isi_pipe(sd); struct v4l2_mbus_framefmt *fmt_source; @@ -682,7 +682,6 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { - .init_cfg = mxc_isi_pipe_init_cfg, .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_pipe_set_fmt, @@ -694,6 +693,10 @@ static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { .pad = &mxc_isi_pipe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = { + .init_state = mxc_isi_pipe_init_state, +}; + /* ----------------------------------------------------------------------------- * IRQ handling */ @@ -767,6 +770,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) sd = &pipe->sd; v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); + sd->internal_ops = &mxc_isi_pipe_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); sd->dev = isi->dev; diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index 02a4c546a90aea..ba2e81f24965d8 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -437,8 +437,8 @@ static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx8mq_mipi_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; @@ -535,7 +535,6 @@ static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = { - .init_cfg = imx8mq_mipi_csi_init_cfg, .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx8mq_mipi_csi_set_fmt, @@ -546,6 +545,10 @@ static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = { .pad = &imx8mq_mipi_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx8mq_mipi_csi_internal_ops = { + .init_state = imx8mq_mipi_csi_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -760,6 +763,7 @@ static int imx8mq_mipi_csi_subdev_init(struct csi_state *state) int ret; v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops); + sd->internal_ops = &imx8mq_mipi_csi_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "%s %s", MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c index 015bd3feac8d14..f4fd832c6aed11 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c +++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c @@ -462,8 +462,8 @@ void csi2_close_rx(struct csi2_device *csi2) csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); } -static int csi2_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; @@ -550,7 +550,6 @@ static int csi2_pad_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { - .init_cfg = csi2_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2_pad_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -564,6 +563,10 @@ static const struct v4l2_subdev_ops csi2_subdev_ops = { .pad = &csi2_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = csi2_init_state, +}; + int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) { unsigned int i, ret; @@ -590,6 +593,7 @@ int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) /* Initialize subdev */ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); + csi2->sd.internal_ops = &csi2_internal_ops; csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->sd.entity.ops = &csi2_entity_ops; csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c index 93dc4ec4e6891d..2878dea1750ea5 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c +++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c @@ -388,8 +388,8 @@ void pisp_fe_stop(struct pisp_fe_device *fe) pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); } -static int pisp_fe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int pisp_fe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; @@ -488,7 +488,6 @@ static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { - .init_cfg = pisp_fe_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = pisp_fe_pad_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -502,6 +501,11 @@ static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { .pad = &pisp_fe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { + .init_state = &pisp_fe_init_state, + +}; + int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) { int ret; @@ -527,6 +531,7 @@ int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) /* Initialize subdev */ v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); + fe->sd.internal_ops = &pisp_fe_internal_ops; fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; fe->sd.entity.ops = &pisp_fe_entity_ops; fe->sd.entity.name = "pisp-fe"; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index e81b5d34d0723c..93746e939b8499 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -530,8 +530,8 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, return 0; } -static int rzg2l_csi2_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CSI2_SINK, }; @@ -582,7 +582,6 @@ static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_mbus_code = rzg2l_csi2_enum_mbus_code, - .init_cfg = rzg2l_csi2_init_config, .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, @@ -593,6 +592,10 @@ static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { .pad = &rzg2l_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_csi2_internal_ops = { + .init_state = rzg2l_csi2_init_state, +}; + /* ----------------------------------------------------------------------------- * Async handling and registration of subdevices and links. */ @@ -777,6 +780,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); + csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), "csi-%s", dev_name(&pdev->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 17a59dfc836310..9f351a05893e6c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -168,8 +168,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rzg2l_cru_ip_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; @@ -192,7 +192,6 @@ static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, .enum_frame_size = rzg2l_cru_ip_enum_frame_size, - .init_cfg = rzg2l_cru_ip_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rzg2l_cru_ip_set_format, }; @@ -202,6 +201,10 @@ static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { .pad = &rzg2l_cru_ip_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { + .init_state = rzg2l_cru_ip_init_state, +}; + static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -213,6 +216,7 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.dev = cru->dev; v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); + ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; v4l2_set_subdevdata(&ip->subdev, cru); snprintf(ip->subdev.name, sizeof(ip->subdev.name), "cru-ip-%s", dev_name(cru->dev)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 95bf7b4ac9b81b..c48ebd67187f0d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -266,7 +266,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops brx_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = brx_enum_mbus_code, .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index c5217fee24f139..8fe84c6072f254 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -155,7 +155,6 @@ static int clu_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops clu_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = clu_enum_mbus_code, .enum_frame_size = clu_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index d5a49e08a607d3..a84f2c13fb9c0b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -128,6 +128,35 @@ vsp1_entity_get_pad_config(struct vsp1_entity *entity, } } +/* + * vsp1_entity_init_state - Initialize formats on all pads + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * + * Initialize all pad formats with default values in the given pad config. This + * function can be used as a handler for the subdev internal_ops::init_state + * operation. + */ +static int vsp1_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + unsigned int pad; + + /* Initialize all pad formats with default values. */ + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); + } + + return 0; +} + + /** * vsp1_entity_get_pad_format - Get a pad format from storage for an entity * @entity: the entity @@ -171,32 +200,6 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, } } -/* - * vsp1_entity_init_cfg - Initialize formats on all pads - * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * - * Initialize all pad formats with default values in the given pad config. This - * function can be used as a handler for the subdev pad::init_cfg operation. - */ -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) -{ - unsigned int pad; - - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - struct v4l2_subdev_format format = { - .pad = pad, - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); - } - - return 0; -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -425,6 +428,10 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, return ret; } +static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { + .init_state = vsp1_entity_init_state, +}; + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -659,6 +666,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, /* Initialize the V4L2 subdev. */ subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->internal_ops = &vsp1_entity_internal_ops; subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; @@ -667,7 +675,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); - vsp1_entity_init_cfg(subdev, NULL); + vsp1_entity_init_state(subdev, NULL); /* * Allocate the pad configuration to store formats and selection diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 17f98a6a972e3c..eb83875d1c5536 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -146,8 +146,6 @@ struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target); -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 361a870380c202..b547ee683644cf 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -114,7 +114,6 @@ static int hsit_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 0ab2e0c7047416..18b1036677c1dc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -68,7 +68,6 @@ static int lif_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops lif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index ac6802a325f5aa..451d24ab0b56fb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -131,7 +131,6 @@ static int lut_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops lut_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 6391a503ec97ce..36bac0a9f1262d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -243,7 +243,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index b614a2aea46118..c48857cd99d0f5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -254,7 +254,6 @@ static int sru_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sru_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 1c290cda005a3e..00d5e39d48de32 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -242,7 +242,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops uds_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 83d7f17df80e7d..75a80bbe48efa7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -176,7 +176,6 @@ static int uif_set_selection(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops uif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uif_enum_mbus_code, .enum_frame_size = uif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index f0cef766fc0cb2..4202642e052392 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -285,8 +285,8 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_csi_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; @@ -420,7 +420,6 @@ static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { .enum_mbus_code = rkisp1_csi_enum_mbus_code, - .init_cfg = rkisp1_csi_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_csi_set_fmt, }; @@ -430,6 +429,10 @@ static const struct v4l2_subdev_ops rkisp1_csi_ops = { .pad = &rkisp1_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_csi_internal_ops = { + .init_state = rkisp1_csi_init_state, +}; + int rkisp1_csi_register(struct rkisp1_device *rkisp1) { struct rkisp1_csi *csi = &rkisp1->csi; @@ -441,6 +444,7 @@ int rkisp1_csi_register(struct rkisp1_device *rkisp1) sd = &csi->sd; v4l2_subdev_init(sd, &rkisp1_csi_ops); + sd->internal_ops = &rkisp1_csi_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_csi_media_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index cd7d27240e0e10..6cd1244c30de56 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -423,8 +423,8 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rkisp1_isp_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_isp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop, *src_crop; @@ -783,7 +783,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, - .init_cfg = rkisp1_isp_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_isp_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -894,6 +893,10 @@ static const struct v4l2_subdev_ops rkisp1_isp_ops = { .pad = &rkisp1_isp_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = { + .init_state = rkisp1_isp_init_state, +}; + int rkisp1_isp_register(struct rkisp1_device *rkisp1) { struct rkisp1_isp *isp = &rkisp1->isp; @@ -904,6 +907,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) isp->rkisp1 = rkisp1; v4l2_subdev_init(sd, &rkisp1_isp_ops); + sd->internal_ops = &rkisp1_isp_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 991bc0639bc32f..128c80fe2c62d2 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -378,8 +378,8 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; @@ -617,7 +617,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = { .enum_mbus_code = rkisp1_rsz_enum_mbus_code, .get_selection = rkisp1_rsz_get_selection, .set_selection = rkisp1_rsz_set_selection, - .init_cfg = rkisp1_rsz_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_rsz_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -664,6 +663,10 @@ static const struct v4l2_subdev_ops rkisp1_rsz_ops = { .pad = &rkisp1_rsz_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_rsz_internal_ops = { + .init_state = rkisp1_rsz_init_state, +}; + static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) { if (!rsz->rkisp1) @@ -693,6 +696,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) } v4l2_subdev_init(sd, &rkisp1_rsz_ops); + sd->internal_ops = &rkisp1_rsz_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_rsz_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index aa1df66d5ac62d..dbb26c7b2f8d7a 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -215,6 +215,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) /* Initialize subdev */ v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops); + subdev->internal_ops = &sun4i_csi_subdev_internal_ops; subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; subdev->entity.ops = &sun4i_csi_subdev_entity_ops; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 8eeed87bfb13be..4e0c2df45d4d2d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -89,6 +89,7 @@ enum csi_subdev_pads { }; extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops; +extern const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops; struct sun4i_csi_format { u32 mbus; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index c37bb1d76ef65d..744197b0fccb03 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -266,8 +266,8 @@ static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int sun4i_csi_subdev_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt; @@ -334,7 +334,6 @@ sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .init_cfg = sun4i_csi_subdev_init_cfg, .get_fmt = sun4i_csi_subdev_get_fmt, .set_fmt = sun4i_csi_subdev_set_fmt, .enum_mbus_code = sun4i_csi_subdev_enum_mbus_code, @@ -344,6 +343,10 @@ const struct v4l2_subdev_ops sun4i_csi_subdev_ops = { .pad = &sun4i_csi_subdev_pad_ops, }; +const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops = { + .init_state = sun4i_csi_subdev_init_state, +}; + int sun4i_csi_v4l2_register(struct sun4i_csi *csi) { struct video_device *vdev = &csi->vdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index d57481feee0533..d006d9dd017081 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -501,8 +501,8 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; @@ -581,7 +581,6 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { - .init_cfg = sun6i_csi_bridge_init_cfg, .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, .get_fmt = sun6i_csi_bridge_get_fmt, .set_fmt = sun6i_csi_bridge_set_fmt, @@ -592,6 +591,10 @@ static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = { .pad = &sun6i_csi_bridge_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = { + .init_state = sun6i_csi_bridge_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_csi_bridge_entity_ops = { @@ -782,6 +785,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); + subdev->internal_ops = &sun6i_csi_bridge_internal_ops; strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index d2c9f5d20496ae..f9d4dc45b49077 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -305,8 +305,8 @@ sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; @@ -385,7 +385,6 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = { - .init_cfg = sun6i_mipi_csi2_init_cfg, .enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code, .get_fmt = sun6i_mipi_csi2_get_fmt, .set_fmt = sun6i_mipi_csi2_set_fmt, @@ -396,6 +395,10 @@ static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = { .pad = &sun6i_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_mipi_csi2_internal_ops = { + .init_state = sun6i_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { @@ -504,6 +507,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun6i_mipi_csi2_internal_ops; strscpy(subdev->name, SUN6I_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index d6275954af984b..4a5698eb12b77c 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -338,8 +338,8 @@ sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun8i_a83t_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun8i_a83t_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); @@ -422,7 +422,6 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { - .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, @@ -433,6 +432,10 @@ static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { .pad = &sun8i_a83t_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun8i_a83t_mipi_csi2_internal_ops = { + .init_state = sun8i_a83t_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { @@ -542,6 +545,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun8i_a83t_mipi_csi2_internal_ops; strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 61433744c6c49a..4afc2ad0033039 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -721,8 +721,8 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, return 0; } -static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .which = state ? V4L2_SUBDEV_FORMAT_TRY @@ -782,7 +782,6 @@ static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { }; static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { - .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -795,6 +794,10 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; +static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { + .init_state = cal_camerarx_sd_init_state, +}; + static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -846,6 +849,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->internal_ops = &cal_camerarx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 9095c95c0a67a7..f33b4548b88491 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -306,8 +306,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } -static int video_mux_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int video_mux_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); struct v4l2_mbus_framefmt *mbusformat; @@ -349,7 +349,6 @@ static int video_mux_get_mbus_config(struct v4l2_subdev *sd, }; static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { - .init_cfg = video_mux_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = video_mux_set_format, .get_mbus_config = video_mux_get_mbus_config, @@ -360,6 +359,10 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { .video = &video_mux_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops video_mux_internal_ops = { + .init_state = video_mux_init_state, +}; + static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asd) @@ -455,6 +458,7 @@ static int video_mux_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vmux); v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops); + vmux->subdev.internal_ops = &video_mux_internal_ops; snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%pOFn", np); vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; vmux->subdev.dev = dev; diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 3e94d4f0b62307..1ced0c1b5de3eb 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -679,18 +679,8 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, } } -/** - * xcsi2rxss_init_cfg - Initialise the pad format config to default - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * - * This function is used to initialize the pad format with the default - * values. - * - * Return: 0 on success - */ -static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int xcsi2rxss_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); struct v4l2_mbus_framefmt *format; @@ -839,7 +829,6 @@ static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { }; static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { - .init_cfg = xcsi2rxss_init_cfg, .get_fmt = xcsi2rxss_get_format, .set_fmt = xcsi2rxss_set_format, .enum_mbus_code = xcsi2rxss_enum_mbus_code, @@ -852,6 +841,10 @@ static const struct v4l2_subdev_ops xcsi2rxss_ops = { .pad = &xcsi2rxss_pad_ops }; +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = { + .init_state = xcsi2rxss_init_state, +}; + static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) { struct device *dev = xcsi2rxss->dev; @@ -1029,6 +1022,7 @@ static int xcsi2rxss_probe(struct platform_device *pdev) /* Initialize V4L2 subdevice and media entity */ subdev = &xcsi2rxss->subdev; v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->internal_ops = &xcsi2rxss_internal_ops; subdev->dev = dev; strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index a2fb32c97c8401..d72ed086e00bb4 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -150,8 +150,8 @@ static bool vimc_debayer_src_code_is_valid(u32 code) return false; } -static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_debayer_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; @@ -307,7 +307,6 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { - .init_cfg = vimc_debayer_init_cfg, .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, .get_fmt = vimc_debayer_get_fmt, @@ -395,6 +394,10 @@ static const struct v4l2_subdev_ops vimc_debayer_ops = { .video = &vimc_debayer_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_debayer_internal_ops = { + .init_state = vimc_debayer_init_state, +}; + static unsigned int vimc_debayer_get_val(const u8 *bytes, const unsigned int n_bytes) { @@ -595,6 +598,8 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, if (ret) goto err_free_hdl; + vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; + vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index f6592a789f1e7d..afe13d6af321b3 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -70,8 +70,8 @@ vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) return r; } -static int vimc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf; struct v4l2_rect *r; @@ -292,7 +292,6 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { - .init_cfg = vimc_scaler_init_cfg, .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, .get_fmt = vimc_scaler_get_fmt, @@ -347,6 +346,10 @@ static const struct v4l2_subdev_ops vimc_scaler_ops = { .video = &vimc_scaler_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { + .init_state = vimc_scaler_init_state, +}; + static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { @@ -424,6 +427,8 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, return ERR_PTR(ret); } + vscaler->sd.internal_ops = &vimc_scaler_internal_ops; + vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 676ab503f0ca11..5e34b1aed95e86 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -41,8 +41,8 @@ static const struct v4l2_mbus_framefmt fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -static int vimc_sensor_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_sensor_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { unsigned int i; @@ -183,7 +183,6 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { - .init_cfg = vimc_sensor_init_cfg, .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, .get_fmt = vimc_sensor_get_fmt, @@ -294,6 +293,10 @@ static const struct v4l2_subdev_ops vimc_sensor_ops = { .video = &vimc_sensor_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_sensor_internal_ops = { + .init_state = vimc_sensor_init_state, +}; + static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) { struct vimc_sensor_device *vsensor = @@ -429,6 +432,8 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, if (ret) goto err_free_tpg; + vsensor->sd.internal_ops = &vimc_sensor_internal_ops; + vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index fea4b4d934511d..b984d017c8d54b 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1521,16 +1521,18 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, } } - /* - * There can be no race at this point, but we lock the state anyway to - * satisfy lockdep checks. - */ - v4l2_subdev_lock_state(state); - ret = v4l2_subdev_call(sd, pad, init_cfg, state); - v4l2_subdev_unlock_state(state); + if (sd->internal_ops && sd->internal_ops->init_state) { + /* + * There can be no race at this point, but we lock the state + * anyway to satisfy lockdep checks. + */ + v4l2_subdev_lock_state(state); + ret = sd->internal_ops->init_state(sd, state); + v4l2_subdev_unlock_state(state); - if (ret < 0 && ret != -ENOIOCTLCMD) - goto err; + if (ret) + goto err; + } return state; diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 61d69f19657e39..8bd9be49cc0839 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -448,7 +448,6 @@ static int prp_registered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops prp_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = prp_enum_mbus_code, .get_fmt = prp_get_fmt, .set_fmt = prp_set_fmt, @@ -472,6 +471,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = { }; static const struct v4l2_subdev_internal_ops prp_internal_ops = { + .init_state = imx_media_init_state, .registered = prp_registered, }; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index ec73c901079ed8..04878f07eeba6b 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -1296,7 +1296,6 @@ static void prp_unregistered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops prp_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = prp_enum_mbus_code, .enum_frame_size = prp_enum_frame_size, .get_fmt = prp_get_fmt, @@ -1320,6 +1319,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = { }; static const struct v4l2_subdev_internal_ops prp_internal_ops = { + .init_state = imx_media_init_state, .registered = prp_registered, .unregistered = prp_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 33902f5daf80cf..2fc94011fe4deb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1866,7 +1866,6 @@ static const struct v4l2_subdev_video_ops csi_video_ops = { }; static const struct v4l2_subdev_pad_ops csi_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = csi_enum_mbus_code, .enum_frame_size = csi_enum_frame_size, .enum_frame_interval = csi_enum_frame_interval, @@ -1884,6 +1883,7 @@ static const struct v4l2_subdev_ops csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops csi_internal_ops = { + .init_state = imx_media_init_state, .registered = csi_registered, .unregistered = csi_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index ec4349ab48cda3..1b5af8945e6b8b 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -426,10 +426,10 @@ EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt); /* * Initializes the TRY format to the ACTIVE format on all pads - * of a subdev. Can be used as the .init_cfg pad operation. + * of a subdev. Can be used as the .init_state internal operation. */ -int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +int imx_media_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf_try; unsigned int pad; @@ -451,7 +451,7 @@ int imx_media_init_cfg(struct v4l2_subdev *sd, return 0; } -EXPORT_SYMBOL_GPL(imx_media_init_cfg); +EXPORT_SYMBOL_GPL(imx_media_init_state); /* * Default the colorspace in tryfmt to SRGB if set to an unsupported diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 792bca1d2d2518..810b38ea3ab904 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -882,7 +882,6 @@ static void vdic_unregistered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops vdic_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = vdic_enum_mbus_code, .get_fmt = vdic_get_fmt, .set_fmt = vdic_set_fmt, @@ -906,6 +905,7 @@ static const struct v4l2_subdev_ops vdic_subdev_ops = { }; static const struct v4l2_subdev_internal_ops vdic_internal_ops = { + .init_state = imx_media_init_state, .registered = vdic_registered, .unregistered = vdic_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 2640cd34dce264..f095d9134fee4a 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -192,8 +192,8 @@ static inline int imx_media_enum_ipu_formats(u32 *code, u32 index, int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus, u32 width, u32 height, u32 code, u32 field, const struct imx_media_pixfmt **cc); -int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state); +int imx_media_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state); void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 6906220ee89ef0..0d8b420616235d 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -619,7 +619,6 @@ static const struct v4l2_subdev_video_ops csi2_video_ops = { }; static const struct v4l2_subdev_pad_ops csi2_pad_ops = { - .init_cfg = imx_media_init_cfg, .get_fmt = csi2_get_fmt, .set_fmt = csi2_set_fmt, }; @@ -631,6 +630,7 @@ static const struct v4l2_subdev_ops csi2_subdev_ops = { }; static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = imx_media_init_state, .registered = csi2_registered, }; diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c new file mode 100644 index 00000000000000..6bffffb2b20c5f --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf-isp.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp.c + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include + +#include "stf-camss.h" + +#define SINK_FORMATS_INDEX 0 +#define SOURCE_FORMATS_INDEX 1 + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static const struct stf_isp_format isp_formats_sink[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct stf_isp_format isp_formats_source[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8 }, +}; + +static const struct stf_isp_format_table isp_formats_st7110[] = { + { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) }, + { isp_formats_source, ARRAY_SIZE(isp_formats_source) }, +}; + +static const struct stf_isp_format * +stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode) +{ + unsigned int i; + + for (i = 0; i < fmt_table->nfmts; i++) { + if (fmt_table->fmts[i].code == mcode) + return &fmt_table->fmts[i]; + } + + return NULL; +} + +int stf_isp_init(struct stfcamss *stfcamss) +{ + struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; + + isp_dev->stfcamss = stfcamss; + isp_dev->formats = isp_formats_st7110; + isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110); + isp_dev->current_fmt = &isp_formats_source[0]; + + return 0; +} + +static int isp_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *sd_state; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(sd_state, STF_ISP_PAD_SINK); + crop = v4l2_subdev_state_get_crop(sd_state, STF_ISP_PAD_SRC); + + if (enable) { + stf_isp_reset(isp_dev); + stf_isp_init_cfg(isp_dev); + stf_isp_settings(isp_dev, crop, fmt->code); + stf_isp_stream_set(isp_dev); + } + + v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable); + + v4l2_subdev_unlock_state(sd_state); + return 0; +} + +static void isp_try_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt) +{ + const struct stf_isp_format_table *formats; + + if (pad >= STF_ISP_PAD_MAX) { + fmt->colorspace = V4L2_COLORSPACE_SRGB; + return; + } + + if (pad == STF_ISP_PAD_SINK) + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + else if (pad == STF_ISP_PAD_SRC) + formats = &isp_dev->formats[SOURCE_FORMATS_INDEX]; + + fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + fmt->height &= ~0x1; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + + if (!stf_g_fmt_by_mcode(formats, fmt->code)) + fmt->code = formats->fmts[0].code; +} + +static int isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + const struct stf_isp_format_table *formats; + + if (code->pad == STF_ISP_PAD_SINK) { + if (code->index > ARRAY_SIZE(isp_formats_sink)) + return -EINVAL; + + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + code->code = formats->fmts[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + if (code->index > ARRAY_SIZE(isp_formats_source)) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(state, + STF_ISP_PAD_SRC); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int isp_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, fmt->pad); + if (!format) + return -EINVAL; + + isp_try_format(isp_dev, state, fmt->pad, &fmt->format); + *format = fmt->format; + + isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad], + fmt->format.code); + + /* Propagate to in crop */ + if (fmt->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection sel = { 0 }; + + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = STF_ISP_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + isp_set_selection(sd, state, &sel); + } + + return 0; +} + +static const struct v4l2_rect stf_frame_min_crop = { + .width = STFCAMSS_FRAME_MIN_WIDTH, + .height = STFCAMSS_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static void isp_try_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *crop) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, STF_ISP_PAD_SINK); + + const struct v4l2_rect bounds = { + .width = fmt->width, + .height = fmt->height, + .left = 0, + .top = 0, + }; + + v4l2_rect_set_min_size(crop, &stf_frame_min_crop); + v4l2_rect_map_inside(crop, &bounds); +} + +static int isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + if (sel->pad == STF_ISP_PAD_SINK) { + fmt.format = *v4l2_subdev_state_get_format(state, + sel->pad); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + } else if (sel->pad == STF_ISP_PAD_SRC) { + rect = v4l2_subdev_state_get_crop(state, sel->pad); + sel->r = *rect; + } + break; + + case V4L2_SEL_TGT_CROP: + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection crop = { 0 }; + + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source crop selection */ + crop.which = sel->which; + crop.pad = STF_ISP_PAD_SRC; + crop.target = V4L2_SEL_TGT_CROP; + crop.r = *rect; + isp_set_selection(sd, state, &crop); + } else if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SRC) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = STF_ISP_PAD_SRC; + fmt.format.width = rect->width; + fmt.format.height = rect->height; + isp_set_format(sd, state, &fmt); + } + + dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%dx%d\n", + sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + + return 0; +} + +static int isp_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format format = { + .pad = STF_ISP_PAD_SINK, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = 1920, + .height = 1080 + } + }; + + return isp_set_format(sd, sd_state, &format); +} + +static const struct v4l2_subdev_video_ops isp_video_ops = { + .s_stream = isp_set_stream, +}; + +static const struct v4l2_subdev_pad_ops isp_pad_ops = { + .enum_mbus_code = isp_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp_set_format, + .get_selection = isp_get_selection, + .set_selection = isp_set_selection, +}; + +static const struct v4l2_subdev_ops isp_v4l2_ops = { + .video = &isp_video_ops, + .pad = &isp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops isp_internal_ops = { + .init_state = isp_init_formats, +}; + +static const struct media_entity_operations isp_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &isp_dev->subdev; + struct media_pad *pads = isp_dev->pads; + int ret; + + v4l2_subdev_init(sd, &isp_v4l2_ops); + sd->internal_ops = &isp_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp"); + v4l2_set_subdevdata(sd, isp_dev); + + pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->entity.ops = &isp_media_ops; + ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to register subdev: %d\n", ret); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); + return ret; +} + +int stf_isp_unregister(struct stf_isp_dev *isp_dev) +{ + v4l2_device_unregister_subdev(&isp_dev->subdev); + v4l2_subdev_cleanup(&isp_dev->subdev); + media_entity_cleanup(&isp_dev->subdev.entity); + + return 0; +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c index b2ce0d4fae9572..46a334b602f192 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -256,8 +256,8 @@ sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_isp_proc_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; @@ -336,7 +336,6 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = { - .init_cfg = sun6i_isp_proc_init_cfg, .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, .get_fmt = sun6i_isp_proc_get_fmt, .set_fmt = sun6i_isp_proc_set_fmt, @@ -347,6 +346,10 @@ static const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { .pad = &sun6i_isp_proc_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_isp_proc_internal_ops = { + .init_state = sun6i_isp_proc_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_isp_proc_entity_ops = { @@ -501,6 +504,7 @@ int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); + subdev->internal_ops = &sun6i_isp_proc_internal_ops; strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b568d1690b5d84..a33128d89b3f90 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -773,7 +773,6 @@ struct v4l2_subdev_state { /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * - * @init_cfg: initialize the subdev state to default values * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler * code. * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE() ioctl handler @@ -837,8 +836,6 @@ struct v4l2_subdev_state { * directly, use v4l2_subdev_disable_streams() instead. */ struct v4l2_subdev_pad_ops { - int (*init_cfg)(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state); int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code); @@ -915,6 +912,8 @@ struct v4l2_subdev_ops { /** * struct v4l2_subdev_internal_ops - V4L2 subdev internal ops * + * @init_state: initialize the subdev state to default values + * * @registered: called when this subdev is registered. When called the v4l2_dev * field is set to the correct v4l2_device. * @@ -940,6 +939,8 @@ struct v4l2_subdev_ops { * these ops. */ struct v4l2_subdev_internal_ops { + int (*init_state)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state); int (*registered)(struct v4l2_subdev *sd); void (*unregistered)(struct v4l2_subdev *sd); int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); From b5c6666b968d435f1dcf4c978f67668f838ebc0a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:32:41 +0300 Subject: [PATCH 090/159] media: Documentation: Additional streams generally don't harm capture Having extra streams on the source end of the link that cannot be captured by the sink sub-device generally are not an issue, at least not on CSI-2 bus. Still document that there may be hardware-specific limitations. For example on parallel bus this might not work on all cases. Signed-off-by: Sakari Ailus Reviewed-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- Documentation/userspace-api/media/v4l/dev-subdev.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index f375b820ab68e7..b76e02e545125c 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -529,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source pads. Subdevice drivers that support multiplexed streams are compatible with -non-multiplexed subdev drivers, but, of course, require a routing configuration -where the link between those two types of drivers contains only a single -stream. +non-multiplexed subdev drivers. However, if the driver at the sink end of a link +does not support streams, then only stream 0 of source end may be captured. +There may be additional limitations specific to the sink device. Understanding streams ^^^^^^^^^^^^^^^^^^^^^ From ad8e15ce2b18099fb4e3e7185b20340e533442e0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:32:43 +0300 Subject: [PATCH 091/159] media: Documentation: v4l: Document internal sink pads Document internal sink pads, pads that have both SINK and INTERNAL flags set. Use the IMX219 camera sensor as an example. Signed-off-by: Sakari Ailus Reviewed-by Julien Massot --- .../userspace-api/media/v4l/dev-subdev.rst | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index b76e02e545125c..d30dcb9e2537f5 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -553,6 +553,27 @@ A stream at a specific point in the media pipeline is identified by the sub-device and a (pad, stream) pair. For sub-devices that do not support multiplexed streams the 'stream' field is always 0. +.. _v4l2-subdev-internal-source-pads: + +Internal sink pads and routing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Cases where a single sub-device source pad is traversed by multiple streams, one +or more of which originate from within the sub-device itself, are special as +there is no external sink pad for such routes. In those cases, the sources of +the internally generated streams are represented by internal sink pads, which +are sink pads that have the :ref:`MEDIA_PAD_FL_INTERNAL ` +pad flag set. + +Internal pads have all the properties of an external pad, including formats and +selections. The format in this case is the source format of the stream. An +internal pad always has a single stream only (0). + +Routes from an internal sink pad to an external source pad are typically not +modifiable but they can be activated and deactivated using the +:ref:`V4L2_SUBDEV_ROUTE_FL_ACTIVE ` flag, depending +on driver capabilities. + Interaction between routes, streams, formats and selections ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -668,3 +689,127 @@ To configure this pipeline, the userspace must take the following steps: the configurations along the stream towards the receiver, using :ref:`VIDIOC_SUBDEV_S_FMT ` ioctls to configure each stream endpoint in each sub-device. + +Internal pads setup example +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A simple example of a multiplexed stream setup might be as follows: + +- An IMX219 camera sensor source sub-device, with one source pad (0), one + internal sink pad (1) as the source of the image data and an internal sink + pad (2) as the source of the embedded data. There are two routes, one from the + internal sink pad 1 to the source 0 (image data) and another from the internal + sink pad 2 to the source pad 0 (embedded data). Both streams are always + active, i.e. there is no need to separately enable the embedded data + stream. The sensor uses the CSI-2 interface. + +- A CSI-2 receiver in the SoC. The receiver has a single sink pad (pad 0), + connected to the sensor, and two source pads (pads 1 and 2), to the DMA + engine. The receiver demultiplexes the incoming streams to the source pads. + +- DMA engines in the SoC, one for each stream. Each DMA engine is connected to a + single source pad of the receiver. + +The sensor and the receiver are modelled as V4L2 sub-devices, exposed to +userspace via /dev/v4l-subdevX device nodes. The DMA engines are modelled as +V4L2 devices, exposed to userspace via /dev/videoX nodes. + +To configure this pipeline, the userspace must take the following steps: + +1) Set up media links between entities: enable the links from the sensor to the + receiver and from the receiver to the DMA engines. This step does not differ + from normal non-multiplexed media controller setup. + +2) Configure routing + +.. flat-table:: Camera sensor. There are no configurable routes. + :header-rows: 1 + + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 1/0 + - 0/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from the sink pad + * - 2/0 + - 0/1 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from the internal sink pad + +.. flat-table:: Receiver routing table. Typically both routes need to be + explicitly set. + :header-rows: 1 + + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 0/0 + - 1/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from camera sensor + * - 0/1 + - 2/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from camera sensor + +The options available in sensor's routing configuration are dictated by hardware +capabilities: typically camera sensors always produce an image data stream while +it may be possible to enable and disable the embedded data stream. + +3) Configure formats and selections + + This example assumes that the formats are propagated from sink pad to the + source pad as-is. The tables contain fields of both struct v4l2_subdev_format + and struct v4l2_mbus_framefmt. + +.. flat-table:: Formats set on the sub-devices. Bold values are set, others are + static or propagated. The order is aligned with configured + routes. + :header-rows: 1 + :fill-cells: + + * - Sub-device + - Pad/Stream + - Width + - Height + - Code + * - :rspan:`3` IMX219 + - 1/0 + - 3296 + - 2480 + - MEDIA_BUS_FMT_SRGGB10 + * - 0/0 + - **3296** + - **2480** + - **MEDIA_BUS_FMT_SRGGB10** + * - 2/0 + - 3296 + - 2 + - MEDIA_BUS_FMT_IMX219_EMBEDDED + * - 1/1 + - 3296 + - 2 + - MEDIA_BUS_FMT_META_10 + * - :rspan:`3` CSI-2 receiver + - 0/0 + - **3296** + - **2480** + - **MEDIA_BUS_FMT_SRGGB10** + * - 1/0 + - 3296 + - 2480 + - MEDIA_BUS_FMT_SRGGB10 + * - 0/1 + - **3296** + - **2** + - **MEDIA_BUS_FMT_META_10** + * - 2/0 + - 3296 + - 2 + - MEDIA_BUS_FMT_META_10 + +The embedded data format does not need to be configured as the format is +dictated by the pixel data format in this case. From 3841a0f6190b4528025bd762814f175bbafed27b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 13 May 2014 14:06:52 +0300 Subject: [PATCH 092/159] media: uapi: ccs: Add media bus code for MIPI CCS embedded data Add new MIPI CCS embedded data media bus code (MEDIA_BUS_FMT_CCS_EMBEDDED). Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot Reviewed-by: Laurent Pinchart --- .../media/v4l/subdev-formats.rst | 32 +++++++++++++++++++ include/uapi/linux/media-bus-format.h | 3 ++ 2 files changed, 35 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index 80355baa4d8859..32247599ad6ed7 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -8386,6 +8386,10 @@ content is more or less device specific but the data is transmitted and received by multiple devices that do not process the data in any way, simply writing it to system memory for processing in software at the end of the pipeline. +The exact format of the data generated by the device is reported on the internal +source pad of the originating sub-device, using one of the more specific +metadata formats such as MEDIA_BUS_FMT_CCS_EMBEDDED. + "b" in an array cell signifies a byte of data, followed by the number of the bit and finally the bit number in subscript. "x" indicates a padding bit. @@ -8631,3 +8635,31 @@ and finally the bit number in subscript. "x" indicates a padding bit. - x - x - x + +.. _MEDIA-BUS-FMT-CCS-EMBEDDED: + +MIPI CCS Embedded Data Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`MIPI CCS `_ defines a +metadata format for sensor embedded data, which is used to store the register +configuration used for capturing a given frame. The format is defined in the CCS +specification. The media bus code for this format is +``MEDIA_BUS_FMT_CCS_EMBEDDED``. + +The CCS embedded data format definition includes three levels: + +1. Padding within CSI-2 bus :ref:`Data Unit ` as + documented in the MIPI CCS specification. + +2. The tagged data format as documented in the MIPI CCS specification. + +3. Register addresses and register documentation as documented in the MIPI CCS + specification. + +The format definition shall be used only by devices that fulfill all three +levels above. + +This mbus code are only used for "2-byte simplified tagged data format" (code +0xa) but their use may be extended further in the future, to cover other CCS +embedded data format codes. diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 950d35865de5b3..956e862150326f 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -185,4 +185,7 @@ #define MEDIA_BUS_FMT_META_20 0x8006 #define MEDIA_BUS_FMT_META_24 0x8007 +/* Specific metadata formats. Next is 0x9002. */ +#define MEDIA_BUS_FMT_CCS_EMBEDDED 0x9001 + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ From 106b869b4a0589a1632239daa408bb566e1a2385 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 21 Sep 2023 00:24:03 +0300 Subject: [PATCH 093/159] media: Documentation: ccs: Document routing Document which routes are available for the CCS driver (source) sub-device and what configuration are possible. Also update copyright. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart --- .../userspace-api/media/drivers/ccs.rst | 43 ++++++++++++++++++- .../media/v4l/subdev-formats.rst | 2 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst index 161cb65f4d98a6..02a0de9c087762 100644 --- a/Documentation/userspace-api/media/drivers/ccs.rst +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -107,4 +107,45 @@ than in the centre. Shading correction needs to be enabled for luminance correction level to have an effect. -**Copyright** |copy| 2020 Intel Corporation +.. _media-ccs-routes: + +Routes +------ + +The CCS driver implements one or two :ref:`routes ` in +its source sub-device (scaler sub-device if it exists for the device, otherwise +binner) depending on whether the sensor supports embedded data. (All CCS +compliant sensors do but the CCS driver supports preceding standards that did +not require embedded data support, too.) + +The first route of the CCS source sub-device is for pixel data (sink pad +0/stream 0 -> source pad 1/stream 0) and the second one is for embedded data +(internal sink pad 2/stream 0 -> source pad 1/stream 1). + +Embedded data +~~~~~~~~~~~~~ + +MIPI CCS supports generation of camera sensor embedded data. The media bus code +used for this format on the internal sink pad is +:ref:`MEDIA_BUS_FMT_CCS_EMBEDDDED `. + +The bit depth of the CCS pixel data affects how the sensor will output the +embedded data, adding padding to align with CSI-2 bus :term:`Data Unit` for that +particular bit depth. This is indicated by the generic metadata format on the +source pad of the sensor's source sub-device. + +Devices supporting embedded data for bit depths greater than or equal to 16 may +support more dense packing or legacy single metadata byte per data unit, or both +of these. The supported embedded data formats can be enumerated and configured +on stream 1 of the source pad (1) of the CCS source sub-device. + +The use of the denser packing results in embedded data lines being longer than +the pixel data in data units since the data units are smaller. In bytes the +embedded data lines are still not longer than the image data lines. + +The embedded data format is determined by the sub-device image data format +configured on the source sub-device. The embedded data mbus code is only changed +when the bit depth of the image data changes in the source pad of the source +sub-device. + +**Copyright** |copy| 2020, 2024 Intel Corporation diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index 32247599ad6ed7..50ffb3460560d6 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -8663,3 +8663,5 @@ levels above. This mbus code are only used for "2-byte simplified tagged data format" (code 0xa) but their use may be extended further in the future, to cover other CCS embedded data format codes. + +Also see :ref:`CCS driver documentation `. From 46319ea8d29e7eb9393960cadcdd45fc8e70cb8d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 12 Sep 2023 11:14:19 +0300 Subject: [PATCH 094/159] media: uapi: Add media bus code for ov2740 embedded data Add a media bus code for ov2740 camera sensor embedded data and document it. Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot --- .../media/v4l/subdev-formats.rst | 70 +++++++++++++++++++ include/uapi/linux/media-bus-format.h | 3 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index 50ffb3460560d6..23c7aeaaf9711d 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -8665,3 +8665,73 @@ This mbus code are only used for "2-byte simplified tagged data format" (code embedded data format codes. Also see :ref:`CCS driver documentation `. + +Omnivision OV2740 Embedded Data Format +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Omnivision OV2740 camera sensor produces the following embedded data format, +indicated by mbus code MEDIA_BUS_FMT_OV2740_EMBEDDED. The format conforms to +:ref:`CCS embedded data format ` up to level 1. + +.. flat-table:: Omnivision OV2740 Embedded Data Format. Octets at indices marked + reserved or unused have been omitted from the table. The values + are big endian byte order. + :header-rows: 1 + + * - Offset + - Size in bits (active bits if not the same as size) + - Content description + * - 4 + - 16 (10--0) + - Analogue gain + * - 6 + - 16 + - Coarse integration time + * - 10 + - 8 + - Dpc correction threshold + * - 15 + - 16 + - Output image width + * - 17 + - 16 + - Output image height + * - 23 + - 8 + - MIPI header revision number (2) + * - 31 + - 8 + - Vertical (bit 1) and horizontal flip (bit 0) + * - 32 + - 8 + - Frame duration A + * - 33 + - 8 + - Frame duration B + * - 34 + - 8 + - Context count (2) + * - 35 + - 8 + - Context select + * - 54 + - 8 + - Data pedestal bits 9--2 + * - 63 + - 8 + - Frame average bits 9--2 + * - 64 + - 16 + - Digital gain red + * - 66 + - 16 + - Digital gain greenr + * - 68 + - 16 + - Digital gain blue + * - 70 + - 16 + - Digital gain greenb + * - 89 + - 8 + - Frame counter (starts at 1, wraps to 0 after 255) diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 956e862150326f..29d26a59b784fe 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -185,7 +185,8 @@ #define MEDIA_BUS_FMT_META_20 0x8006 #define MEDIA_BUS_FMT_META_24 0x8007 -/* Specific metadata formats. Next is 0x9002. */ +/* Specific metadata formats. Next is 0x9003. */ #define MEDIA_BUS_FMT_CCS_EMBEDDED 0x9001 +#define MEDIA_BUS_FMT_OV2740_EMBEDDED 0x9002 #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ From d9e60b06644c5f30cb597529c6373afbb5f0a453 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:32:44 +0300 Subject: [PATCH 095/159] media: Documentation: Document S_ROUTING behaviour Document S_ROUTING behaviour for different devices. Generally in devices that produce streams the streams are static and some can be enabled and disabled, whereas in devices that just transport them or write them to memory, more configurability is allowed. Document this. Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot --- .../userspace-api/media/v4l/dev-subdev.rst | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index d30dcb9e2537f5..de8dfd4f11a502 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -593,6 +593,30 @@ Any configurations of a stream within a pad, such as format or selections, are independent of similar configurations on other streams. This is subject to change in the future. +Device types and routing setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Different kinds of sub-devices have differing behaviour for route activation, +depending on the hardware. In all cases, however, only routes that have the +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active. + +Devices generating the streams may allow enabling and disabling some of the +routes or the configuration is fixed. If the routes can be disabled, not +declaring the routes (or declaring them without +``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will +disable the routes while the sub-device driver retains the streams and their +format and selection configuration. The ``VIDIOC_SUBDEV_S_ROUTING`` will still +return such routes back to the user in the routes array, with the +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag unset. + +Devices transporting the streams almost always have more configurability with +respect to routing. Typically any route between the sub-device's sink and source +pads is possible, and multiple routes (usually up to certain limited number) may +be active simultaneously. For such devices, no routes are created by the driver +and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is +called on the sub-device. Such newly created routes have the device's default +configuration for format and selection rectangles. + Configuring streams ^^^^^^^^^^^^^^^^^^^ From 7b038f416606a38e35329262d146a4101b601387 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:32:46 +0300 Subject: [PATCH 096/159] media: v4l: subdev: Move G_ROUTING handling below S_ROUTING Move G_ROUTING IOCTL handling below that of S_ROUTING. G_ROUTING implementation will soon needed in handling S_ROUTING as well. Signed-off-by: Sakari Ailus Reviewed-by: Julien Massot Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b984d017c8d54b..44332220c632dd 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -906,29 +906,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); - case VIDIOC_SUBDEV_G_ROUTING: { - struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting *krouting; - - if (!v4l2_subdev_enable_streams_api) - return -ENOIOCTLCMD; - - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - return -ENOIOCTLCMD; - - memset(routing->reserved, 0, sizeof(routing->reserved)); - - krouting = &state->routing; - - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - krouting->routes, - min(krouting->num_routes, routing->len_routes) * - sizeof(*krouting->routes)); - routing->num_routes = krouting->num_routes; - - return 0; - } - case VIDIOC_SUBDEV_S_ROUTING: { struct v4l2_subdev_routing *routing = arg; struct v4l2_subdev_route *routes = @@ -1005,6 +982,29 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, return 0; } + case VIDIOC_SUBDEV_G_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_krouting *krouting; + + if (!v4l2_subdev_enable_streams_api) + return -ENOIOCTLCMD; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return -ENOIOCTLCMD; + + memset(routing->reserved, 0, sizeof(routing->reserved)); + + krouting = &state->routing; + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + krouting->routes, + min(krouting->num_routes, routing->len_routes) * + sizeof(*krouting->routes)); + routing->num_routes = krouting->num_routes; + + return 0; + } + case VIDIOC_SUBDEV_G_CLIENT_CAP: { struct v4l2_subdev_client_capability *client_cap = arg; From e25467ed59f954e003d2adcbcafac89327596596 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:32:59 +0300 Subject: [PATCH 097/159] media: mc: Add INTERNAL pad flag Internal sink pads will be used as routing endpoints in V4L2 [GS]_ROUTING IOCTLs, to indicate that the stream begins in the entity. Internal sink pads are pads that have both SINK and INTERNAL flags set. Also prevent creating links to pads that have been flagged as internal and initialising SOURCE pads with INTERNAL flag set. Signed-off-by: Sakari Ailus Reviewed-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- .../userspace-api/media/mediactl/media-types.rst | 9 +++++++++ drivers/media/mc/mc-entity.c | 15 ++++++++++++--- include/uapi/linux/media.h | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index 6332e8395263b0..200c37a1da2685 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-PAD-FL-SINK: .. _MEDIA-PAD-FL-SOURCE: .. _MEDIA-PAD-FL-MUST-CONNECT: +.. _MEDIA-PAD-FL-INTERNAL: .. flat-table:: Media pad flags :header-rows: 0 @@ -381,6 +382,14 @@ Types and flags used to represent the media graph elements enabled links even when this flag isn't set; the absence of the flag doesn't imply there is none. + * - ``MEDIA_PAD_FL_INTERNAL`` + - The internal flag indicates an internal pad that has no external + connections. As they are internal to entities, internal pads shall not + be connected with links. + + The internal flag may currently be present only in a sink pad where it + indicates that the :ref:``stream `` originates + from within the entity. One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` must be set for every pad. diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 96dd0f6ccd0d0a..db048423a33636 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -209,11 +209,16 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, mutex_lock(&mdev->graph_mutex); media_entity_for_each_pad(entity, iter) { + const u32 pad_flags = iter->flags & (MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_INTERNAL); + iter->entity = entity; iter->index = i++; - if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK | - MEDIA_PAD_FL_SOURCE)) != 1) { + if (pad_flags != MEDIA_PAD_FL_SINK && + pad_flags != (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL) && + pad_flags != MEDIA_PAD_FL_SOURCE) { ret = -EINVAL; break; } @@ -1118,7 +1123,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, for (i = 0; i < entity->num_pads; i++) { if ((entity->pads[i].flags & - (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) + (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_INTERNAL)) != pad_type) continue; if (entity->pads[i].sig_type == sig_type) @@ -1148,6 +1154,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, return -EINVAL; if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) return -EINVAL; + if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) || + WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL)) + return -EINVAL; link = media_add_link(&source->links); if (link == NULL) diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 1c80b1d6bbaf36..80cfd12a43fc1a 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -208,6 +208,7 @@ struct media_entity_desc { #define MEDIA_PAD_FL_SINK (1U << 0) #define MEDIA_PAD_FL_SOURCE (1U << 1) #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) +#define MEDIA_PAD_FL_INTERNAL (1U << 3) struct media_pad_desc { __u32 entity; /* entity ID */ From 84dbb3e0dc7fa6c51647591861ccc045e9449ff3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 16 Apr 2024 22:33:17 +0300 Subject: [PATCH 098/159] media: v4l: Add V4L2_SUBDEV_ROUTE_FL_IMMUTABLE sub-device routing flag Add a flag to denote immutable routes, V4L2_SUBDEV_ROUTE_FL_IMMUTABLE. Such routes cannot be changed and they're always active. Signed-off-by: Sakari Ailus --- Documentation/userspace-api/media/v4l/dev-subdev.rst | 4 +++- .../userspace-api/media/v4l/vidioc-subdev-g-routing.rst | 5 +++++ include/uapi/linux/v4l2-subdev.h | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index de8dfd4f11a502..5c63c8c24108e2 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -572,7 +572,9 @@ internal pad always has a single stream only (0). Routes from an internal sink pad to an external source pad are typically not modifiable but they can be activated and deactivated using the :ref:`V4L2_SUBDEV_ROUTE_FL_ACTIVE ` flag, depending -on driver capabilities. +on driver capabilities. The :ref:`V4L2_SUBDEV_ROUTE_FL_IMMUTABLE +` flag indicates that the +``V4L2_SUBDEV_ROUTE_FLAG_ACTIVE`` of the route may not be unset. Interaction between routes, streams, formats and selections ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index d386e3b0172495..6599e5c5c184f1 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -146,6 +146,11 @@ wants to retrieve the missing routes, it can issue a new * - V4L2_SUBDEV_ROUTE_FL_ACTIVE - 0x0001 - The route is enabled. Set by applications. + * - V4L2_SUBDEV_ROUTE_FL_IMMUTABLE + - 0x0002 + - The route is immutable. Set by the driver. Indicates that the + ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag of an immutable route may not be + unset. Return Value ============ diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 6dd4372dfd3fee..88e21feb473775 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -198,6 +198,11 @@ struct v4l2_subdev_capability { * on a video node. */ #define V4L2_SUBDEV_ROUTE_FL_ACTIVE (1U << 0) +/* + * Is the route immutable. The ACTIVE flag of an immutable route may not be + * changed. + */ +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE (1U << 1) /** * struct v4l2_subdev_route - A route inside a subdev From d1695bc4904c3805c38189007576515e846244d7 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 1 Oct 2024 16:09:59 +0300 Subject: [PATCH 099/159] Remove legacy CFE Signed-off-by: Tomi Valkeinen Signed-off-by: Jacopo Mondi --- drivers/media/platform/raspberrypi/Kconfig | 1 - drivers/media/platform/raspberrypi/Makefile | 1 - .../platform/raspberrypi/rp1_cfe/Kconfig | 14 - .../platform/raspberrypi/rp1_cfe/Makefile | 6 - .../media/platform/raspberrypi/rp1_cfe/cfe.c | 2423 ----------------- .../media/platform/raspberrypi/rp1_cfe/cfe.h | 43 - .../platform/raspberrypi/rp1_cfe/cfe_fmts.h | 318 --- .../media/platform/raspberrypi/rp1_cfe/csi2.c | 628 ----- .../media/platform/raspberrypi/rp1_cfe/csi2.h | 90 - .../media/platform/raspberrypi/rp1_cfe/dphy.c | 177 -- .../media/platform/raspberrypi/rp1_cfe/dphy.h | 27 - .../raspberrypi/rp1_cfe/pisp_common.h | 69 - .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 570 ---- .../platform/raspberrypi/rp1_cfe/pisp_fe.h | 53 - .../raspberrypi/rp1_cfe/pisp_fe_config.h | 272 -- .../raspberrypi/rp1_cfe/pisp_statistics.h | 62 - .../platform/raspberrypi/rp1_cfe/pisp_types.h | 144 - 17 files changed, 4898 deletions(-) delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Kconfig delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Makefile delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.c delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.c delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.c delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h delete mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig index a1850c559dbbcc..e928f979019e65 100644 --- a/drivers/media/platform/raspberrypi/Kconfig +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -3,4 +3,3 @@ comment "Raspberry Pi media platform drivers" source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" -source "drivers/media/platform/raspberrypi/rp1_cfe/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile index 4ce6c998927c17..c0d1a2dab4860c 100644 --- a/drivers/media/platform/raspberrypi/Makefile +++ b/drivers/media/platform/raspberrypi/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += pisp_be/ -obj-y += rp1_cfe/ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig b/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig deleted file mode 100644 index 8cb1255319fb7d..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# RP1 V4L2 camera support - -config VIDEO_RP1_CFE - tristate "RP1 Camera Frond End (CFE) video capture driver" - depends on VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select VIDEOBUF2_DMA_CONTIG - select V4L2_FWNODE - help - Say Y here to enable support for the RP1 Camera Front End. - - To compile this driver as a module, choose M here. The module will be - called rp1-cfe. diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/Makefile b/drivers/media/platform/raspberrypi/rp1_cfe/Makefile deleted file mode 100644 index 9709d6f603e995..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for RP1 Camera Front End driver -# -rp1-cfe-objs := cfe.o csi2.o pisp_fe.o dphy.o -obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c deleted file mode 100644 index 45bd8abbddbcc1..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c +++ /dev/null @@ -1,2423 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * RP1 Camera Front End Driver - * - * Copyright (C) 2021-2022 - Raspberry Pi Ltd. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cfe.h" -#include "cfe_fmts.h" -#include "csi2.h" -#include "pisp_fe.h" -#include "pisp_fe_config.h" -#include "pisp_statistics.h" - -#define CFE_MODULE_NAME "rp1-cfe" -#define CFE_VERSION "1.0" - -bool cfe_debug_verbose; -module_param_named(verbose_debug, cfe_debug_verbose, bool, 0644); -MODULE_PARM_DESC(verbose_debug, "verbose debugging messages"); - -#define cfe_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(&cfe->pdev->dev, fmt, ##arg); \ - } while (0) -#define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg) -#define cfe_info(fmt, arg...) dev_info(&cfe->pdev->dev, fmt, ##arg) -#define cfe_err(fmt, arg...) dev_err(&cfe->pdev->dev, fmt, ##arg) - -/* MIPICFG registers */ -#define MIPICFG_CFG 0x004 -#define MIPICFG_INTR 0x028 -#define MIPICFG_INTE 0x02c -#define MIPICFG_INTF 0x030 -#define MIPICFG_INTS 0x034 - -#define MIPICFG_CFG_SEL_CSI BIT(0) - -#define MIPICFG_INT_CSI_DMA BIT(0) -#define MIPICFG_INT_CSI_HOST BIT(2) -#define MIPICFG_INT_PISP_FE BIT(4) - -#define BPL_ALIGNMENT 16 -#define MAX_BYTESPERLINE 0xffffff00 -#define MAX_BUFFER_SIZE 0xffffff00 -/* - * Max width is therefore determined by the max stride divided by the number of - * bits per pixel. - * - * However, to avoid overflow issues let's use a 16k maximum. This lets us - * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful - * review and adjustment of the code is needed so that it will deal with - * overflows correctly. - */ -#define MAX_WIDTH 16384 -#define MAX_HEIGHT MAX_WIDTH -/* Define a nominal minimum image size */ -#define MIN_WIDTH 16 -#define MIN_HEIGHT 16 -/* Default size of the embedded buffer */ -#define DEFAULT_EMBEDDED_SIZE 16384 - -const struct v4l2_mbus_framefmt cfe_default_format = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_RAW, - .ycbcr_enc = V4L2_YCBCR_ENC_601, - .quantization = V4L2_QUANTIZATION_FULL_RANGE, - .xfer_func = V4L2_XFER_FUNC_NONE, -}; - -const struct v4l2_mbus_framefmt cfe_default_meta_format = { - .width = DEFAULT_EMBEDDED_SIZE, - .height = 1, - .code = MEDIA_BUS_FMT_SENSOR_DATA, - .field = V4L2_FIELD_NONE, -}; - -enum node_ids { - /* CSI2 HW output nodes first. */ - CSI2_CH0, - CSI2_CH1, - CSI2_CH2, - CSI2_CH3, - /* FE only nodes from here on. */ - FE_OUT0, - FE_OUT1, - FE_STATS, - FE_CONFIG, - NUM_NODES -}; - -struct node_description { - unsigned int id; - const char *name; - unsigned int caps; - unsigned int pad_flags; - unsigned int link_pad; -}; - -/* Must match the ordering of enum ids */ -static const struct node_description node_desc[NUM_NODES] = { - [CSI2_CH0] = { - .name = "csi2_ch0", - .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 0 - }, - /* - * TODO: This node should be named "csi2_ch1" and the caps should be set - * to both video and meta capture. However, to keep compatibility with - * the current libcamera, keep the name as "embedded" and support - * only meta capture. - */ - [CSI2_CH1] = { - .name = "embedded", - .caps = V4L2_CAP_META_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 1 - }, - [CSI2_CH2] = { - .name = "csi2_ch2", - .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 2 - }, - [CSI2_CH3] = { - .name = "csi2_ch3", - .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 3 - }, - [FE_OUT0] = { - .name = "fe_image0", - .caps = V4L2_CAP_VIDEO_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = FE_OUTPUT0_PAD - }, - [FE_OUT1] = { - .name = "fe_image1", - .caps = V4L2_CAP_VIDEO_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = FE_OUTPUT1_PAD - }, - [FE_STATS] = { - .name = "fe_stats", - .caps = V4L2_CAP_META_CAPTURE, - .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = FE_STATS_PAD - }, - [FE_CONFIG] = { - .name = "fe_config", - .caps = V4L2_CAP_META_OUTPUT, - .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = FE_CONFIG_PAD - }, -}; - -#define is_fe_node(node) (((node)->id) >= FE_OUT0) -#define is_csi2_node(node) (!is_fe_node(node)) - -#define node_supports_image_output(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE)) -#define node_supports_meta_output(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE)) -#define node_supports_image_input(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT)) -#define node_supports_meta_input(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT)) -#define node_supports_image(node) \ - (node_supports_image_output(node) || node_supports_image_input(node)) -#define node_supports_meta(node) \ - (node_supports_meta_output(node) || node_supports_meta_input(node)) - -#define is_image_output_node(node) \ - ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) -#define is_image_input_node(node) \ - ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) -#define is_image_node(node) \ - (is_image_output_node(node) || is_image_input_node(node)) -#define is_meta_output_node(node) \ - ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE) -#define is_meta_input_node(node) \ - ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT) -#define is_meta_node(node) \ - (is_meta_output_node(node) || is_meta_input_node(node)) - -/* To track state across all nodes. */ -#define NUM_STATES 5 -#define NODE_REGISTERED BIT(0) -#define NODE_ENABLED BIT(1) -#define NODE_STREAMING BIT(2) -#define FS_INT BIT(3) -#define FE_INT BIT(4) - -struct cfe_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct cfe_config_buffer { - struct cfe_buffer buf; - struct pisp_fe_config config; -}; - -static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb) -{ - return container_of(vb, struct cfe_buffer, vb.vb2_buf); -} - -static inline -struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf) -{ - return container_of(buf, struct cfe_config_buffer, buf); -} - -struct cfe_node { - unsigned int id; - /* Pointer pointing to current v4l2_buffer */ - struct cfe_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cfe_buffer *next_frm; - /* Used to store current pixel format */ - struct v4l2_format vid_fmt; - /* Used to store current meta format */ - struct v4l2_format meta_fmt; - /* Buffer queue used in video-buf */ - struct vb2_queue buffer_queue; - /* Queue of filled frames */ - struct list_head dma_queue; - /* lock used to access this structure */ - struct mutex lock; - /* Identifies video device for this channel */ - struct video_device video_dev; - /* Pointer to the parent handle */ - struct cfe_device *cfe; - struct media_pad pad; - unsigned int fs_count; - u64 ts; -}; - -struct cfe_device { - struct dentry *debugfs; - struct kref kref; - - /* V4l2 specific parameters */ - struct v4l2_async_connection *asd; - - /* peripheral base address */ - void __iomem *mipi_cfg_base; - - struct clk *clk; - - /* V4l2 device */ - struct v4l2_device v4l2_dev; - struct media_device mdev; - struct media_pipeline pipe; - - /* IRQ lock for node state and DMA queues */ - spinlock_t state_lock; - bool job_ready; - bool job_queued; - - /* parent device */ - struct platform_device *pdev; - /* subdevice async Notifier */ - struct v4l2_async_notifier notifier; - - /* ptr to sub device */ - struct v4l2_subdev *sensor; - - struct cfe_node node[NUM_NODES]; - DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES); - - struct csi2_device csi2; - struct pisp_fe_device fe; - - int fe_csi2_channel; -}; - -static inline bool is_fe_enabled(struct cfe_device *cfe) -{ - return cfe->fe_csi2_channel != -1; -} - -static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct cfe_device, v4l2_dev); -} - -static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset) -{ - return readl(cfe->mipi_cfg_base + offset); -} - -static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val) -{ - writel(val, cfe->mipi_cfg_base + offset); -} - -static bool check_state(struct cfe_device *cfe, unsigned long state, - unsigned int node_id) -{ - unsigned long bit; - - for_each_set_bit(bit, &state, sizeof(state)) { - if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags)) - return false; - } - return true; -} - -static void set_state(struct cfe_device *cfe, unsigned long state, - unsigned int node_id) -{ - unsigned long bit; - - for_each_set_bit(bit, &state, sizeof(state)) - set_bit(bit + (node_id * NUM_STATES), cfe->node_flags); -} - -static void clear_state(struct cfe_device *cfe, unsigned long state, - unsigned int node_id) -{ - unsigned long bit; - - for_each_set_bit(bit, &state, sizeof(state)) - clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags); -} - -static bool test_any_node(struct cfe_device *cfe, unsigned long cond) -{ - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - if (check_state(cfe, cond, i)) - return true; - } - - return false; -} - -static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond, - unsigned long cond) -{ - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - if (check_state(cfe, precond, i)) { - if (!check_state(cfe, cond, i)) - return false; - } - } - - return true; -} - -static int mipi_cfg_regs_show(struct seq_file *s, void *data) -{ - struct cfe_device *cfe = s->private; - int ret; - - ret = pm_runtime_resume_and_get(&cfe->pdev->dev); - if (ret) - return ret; - -#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg)) - DUMP(MIPICFG_CFG); - DUMP(MIPICFG_INTR); - DUMP(MIPICFG_INTE); - DUMP(MIPICFG_INTF); - DUMP(MIPICFG_INTS); -#undef DUMP - - pm_runtime_put(&cfe->pdev->dev); - - return 0; -} - -static int format_show(struct seq_file *s, void *data) -{ - struct cfe_device *cfe = s->private; - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - unsigned long sb, state = 0; - - for (sb = 0; sb < NUM_STATES; sb++) { - if (check_state(cfe, BIT(sb), i)) - state |= BIT(sb); - } - - seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i, - node_desc[i].name, state); - - if (node_supports_image(node)) - seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n" - "resolution: %ux%u\nbpl: %u\nsize: %u\n", - V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat), - node->vid_fmt.fmt.pix.pixelformat, - node->vid_fmt.fmt.pix.width, - node->vid_fmt.fmt.pix.height, - node->vid_fmt.fmt.pix.bytesperline, - node->vid_fmt.fmt.pix.sizeimage); - - if (node_supports_meta(node)) - seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\nsize: %u\n", - V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat), - node->meta_fmt.fmt.meta.dataformat, - node->meta_fmt.fmt.meta.buffersize); - } - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs); -DEFINE_SHOW_ATTRIBUTE(format); - -/* Format setup functions */ -const struct cfe_fmt *find_format_by_code(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].code == code) - return &formats[i]; - } - - return NULL; -} - -const struct cfe_fmt *find_format_by_pix(u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].fourcc == pixelformat) - return &formats[i]; - } - - return NULL; -} - -/* - * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap - * possible. - */ -u32 cfe_find_16bit_code(u32 code) -{ - const struct cfe_fmt *cfe_fmt; - - cfe_fmt = find_format_by_code(code); - - if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT]) - return 0; - - cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); - if (!cfe_fmt) - return 0; - - return cfe_fmt->code; -} - -/* - * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap - * possible. - */ -u32 cfe_find_compressed_code(u32 code) -{ - const struct cfe_fmt *cfe_fmt; - - cfe_fmt = find_format_by_code(code); - - if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED]) - return 0; - - cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); - if (!cfe_fmt) - return 0; - - return cfe_fmt->code; -} - -static int cfe_calc_format_size_bpl(struct cfe_device *cfe, - const struct cfe_fmt *fmt, - struct v4l2_format *f) -{ - unsigned int min_bytesperline; - - v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, - &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0); - - min_bytesperline = - ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT); - - if (f->fmt.pix.bytesperline > min_bytesperline && - f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) - f->fmt.pix.bytesperline = - ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT); - else - f->fmt.pix.bytesperline = min_bytesperline; - - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - - cfe_dbg("%s: " V4L2_FOURCC_CONV " size: %ux%u bpl:%u img_size:%u\n", - __func__, V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) -{ - struct cfe_buffer *buf; - unsigned int i; - dma_addr_t addr; - - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { - struct cfe_node *node = &cfe->node[i]; - unsigned int stride, size; - - if (!check_state(cfe, NODE_STREAMING, i)) - continue; - - buf = list_first_entry(&node->dma_queue, struct cfe_buffer, - list); - node->next_frm = buf; - list_del(&buf->list); - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &buf->vb.vb2_buf); - - if (is_meta_node(node)) { - size = node->meta_fmt.fmt.meta.buffersize; - stride = 0; - } else { - size = node->vid_fmt.fmt.pix.sizeimage; - stride = node->vid_fmt.fmt.pix.bytesperline; - } - - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size); - } -} - -static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) -{ - struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 }; - struct cfe_config_buffer *config_buf; - struct cfe_buffer *buf; - unsigned int i; - - for (i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - - if (!check_state(cfe, NODE_STREAMING, i)) - continue; - - buf = list_first_entry(&node->dma_queue, struct cfe_buffer, - list); - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &buf->vb.vb2_buf); - - node->next_frm = buf; - vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf; - list_del(&buf->list); - } - - config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm); - pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config); -} - -static bool cfe_check_job_ready(struct cfe_device *cfe) -{ - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - - if (!check_state(cfe, NODE_ENABLED, i)) - continue; - - if (list_empty(&node->dma_queue)) { - cfe_dbg_verbose("%s: [%s] has no buffer, unable to schedule job\n", - __func__, node_desc[i].name); - return false; - } - } - - return true; -} - -static void cfe_prepare_next_job(struct cfe_device *cfe) -{ - cfe->job_queued = true; - cfe_schedule_next_csi2_job(cfe); - if (is_fe_enabled(cfe)) - cfe_schedule_next_pisp_job(cfe); - - /* Flag if another job is ready after this. */ - cfe->job_ready = cfe_check_job_ready(cfe); - - cfe_dbg_verbose("%s: end with scheduled job\n", __func__); -} - -static void cfe_process_buffer_complete(struct cfe_node *node, - enum vb2_buffer_state state) -{ - struct cfe_device *cfe = node->cfe; - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &node->cur_frm->vb.vb2_buf); - - node->cur_frm->vb.sequence = node->fs_count - 1; - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); -} - -static void cfe_queue_event_sof(struct cfe_node *node) -{ - struct v4l2_event event = { - .type = V4L2_EVENT_FRAME_SYNC, - .u.frame_sync.frame_sequence = node->fs_count - 1, - }; - - v4l2_event_queue(&node->video_dev, &event); -} - -static void cfe_sof_isr_handler(struct cfe_node *node) -{ - struct cfe_device *cfe = node->cfe; - bool matching_fs = true; - unsigned int i; - - cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name, - node->fs_count); - - /* - * If the sensor is producing unexpected frame event ordering over a - * sustained period of time, guard against the possibility of coming - * here and orphaning the cur_frm if it's not been dequeued already. - * Unfortunately, there is not enough hardware state to tell if this - * may have occurred. - */ - if (WARN(node->cur_frm, "%s: [%s] Orphanded frame at seq %u\n", - __func__, node_desc[node->id].name, node->fs_count)) - cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR); - - node->cur_frm = node->next_frm; - node->next_frm = NULL; - node->fs_count++; - - node->ts = ktime_get_ns(); - for (i = 0; i < NUM_NODES; i++) { - if (!check_state(cfe, NODE_STREAMING, i) || i == node->id) - continue; - /* - * This checks if any other node has seen a FS. If yes, use the - * same timestamp, eventually across all node buffers. - */ - if (cfe->node[i].fs_count >= node->fs_count) - node->ts = cfe->node[i].ts; - /* - * This checks if all other node have seen a matching FS. If - * yes, we can flag another job to be queued. - */ - if (matching_fs && cfe->node[i].fs_count != node->fs_count) - matching_fs = false; - } - - if (matching_fs) - cfe->job_queued = false; - - if (node->cur_frm) - node->cur_frm->vb.vb2_buf.timestamp = node->ts; - - set_state(cfe, FS_INT, node->id); - clear_state(cfe, FE_INT, node->id); - - if (is_image_output_node(node)) - cfe_queue_event_sof(node); -} - -static void cfe_eof_isr_handler(struct cfe_node *node) -{ - struct cfe_device *cfe = node->cfe; - - cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name, - node->fs_count - 1); - - if (node->cur_frm) - cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE); - - node->cur_frm = NULL; - set_state(cfe, FE_INT, node->id); - clear_state(cfe, FS_INT, node->id); -} - -static irqreturn_t cfe_isr(int irq, void *dev) -{ - struct cfe_device *cfe = dev; - unsigned int i; - bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}; - u32 sts; - - sts = cfg_reg_read(cfe, MIPICFG_INTS); - - if (sts & MIPICFG_INT_CSI_DMA) - csi2_isr(&cfe->csi2, sof, eof); - - if (sts & MIPICFG_INT_PISP_FE) - pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS, - eof + CSI2_NUM_CHANNELS); - - spin_lock(&cfe->state_lock); - - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - - /* - * The check_state(NODE_STREAMING) is to ensure we do not loop - * over the CSI2_CHx nodes when the FE is active since they - * generate interrupts even though the node is not streaming. - */ - if (!check_state(cfe, NODE_STREAMING, i) || - !(sof[i] || eof[i])) - continue; - - /* - * There are 3 cases where we could get FS + FE_ACK at - * the same time: - * 1) FE of the current frame, and FS of the next frame. - * 2) FS + FE of the same frame. - * 3) FE of the current frame, and FS + FE of the next - * frame. To handle this, see the sof handler below. - * - * (1) is handled implicitly by the ordering of the FE and FS - * handlers below. - */ - if (eof[i]) { - /* - * The condition below tests for (2). Run the FS handler - * first before the FE handler, both for the current - * frame. - */ - if (sof[i] && !check_state(cfe, FS_INT, i)) { - cfe_sof_isr_handler(node); - sof[i] = false; - } - - cfe_eof_isr_handler(node); - } - - if (sof[i]) { - /* - * The condition below tests for (3). In such cases, we - * come in here with FS flag set in the node state from - * the previous frame since it only gets cleared in - * eof_isr_handler(). Handle the FE for the previous - * frame first before the FS handler for the current - * frame. - */ - if (check_state(cfe, FS_INT, node->id) && - !check_state(cfe, FE_INT, node->id)) { - cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n", - __func__, node_desc[node->id].name); - cfe_eof_isr_handler(node); - } - - cfe_sof_isr_handler(node); - } - - if (!cfe->job_queued && cfe->job_ready) - cfe_prepare_next_job(cfe); - } - - spin_unlock(&cfe->state_lock); - - return IRQ_HANDLED; -} - -/* - * Stream helpers - */ - -static void cfe_start_channel(struct cfe_node *node) -{ - struct cfe_device *cfe = node->cfe; - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *source_fmt; - const struct cfe_fmt *fmt; - unsigned long flags; - bool start_fe = is_fe_enabled(cfe) && - test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); - - if (start_fe) { - unsigned int width, height; - - WARN_ON(!is_fe_enabled(cfe)); - cfe_dbg("%s: %s using csi2 channel %d\n", - __func__, node_desc[FE_OUT0].name, - cfe->fe_csi2_channel); - - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, - cfe->fe_csi2_channel); - fmt = find_format_by_code(source_fmt->code); - - width = source_fmt->width; - height = source_fmt->height; - - /* Must have a valid CSI2 datatype. */ - WARN_ON(!fmt->csi_dt); - - /* - * Start the associated CSI2 Channel as well. - * - * Must write to the ADDR register to latch the ctrl values - * even if we are connected to the front end. Once running, - * this is handled by the CSI2 AUTO_ARM mode. - */ - csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel, - CSI2_MODE_FE_STREAMING, - true, false, width, height); - csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1); - pisp_fe_start(&cfe->fe); - } - - if (is_csi2_node(node)) { - unsigned int width = 0, height = 0; - - u32 mode = CSI2_MODE_NORMAL; - - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, - node_desc[node->id].link_pad - CSI2_NUM_CHANNELS); - fmt = find_format_by_code(source_fmt->code); - - /* Must have a valid CSI2 datatype. */ - WARN_ON(!fmt->csi_dt); - - if (is_image_output_node(node)) { - width = source_fmt->width; - height = source_fmt->height; - - if (node->vid_fmt.fmt.pix.pixelformat == - fmt->remap[CFE_REMAP_16BIT]) - mode = CSI2_MODE_REMAP; - else if (node->vid_fmt.fmt.pix.pixelformat == - fmt->remap[CFE_REMAP_COMPRESSED]) { - mode = CSI2_MODE_COMPRESSED; - csi2_set_compression(&cfe->csi2, node->id, - CSI2_COMPRESSION_DELTA, 0, - 0); - } - } - /* Unconditionally start this CSI2 channel. */ - csi2_start_channel(&cfe->csi2, node->id, - mode, - /* Auto arm */ - false, - /* Pack bytes */ - is_meta_node(node) ? true : false, - width, height); - } - - v4l2_subdev_unlock_state(state); - - spin_lock_irqsave(&cfe->state_lock, flags); - if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) - cfe_prepare_next_job(cfe); - spin_unlock_irqrestore(&cfe->state_lock, flags); -} - -static void cfe_stop_channel(struct cfe_node *node, bool fe_stop) -{ - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s] fe_stop %u\n", __func__, - node_desc[node->id].name, fe_stop); - - if (fe_stop) { - csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel); - pisp_fe_stop(&cfe->fe); - } - - if (is_csi2_node(node)) - csi2_stop_channel(&cfe->csi2, node->id); -} - -static void cfe_return_buffers(struct cfe_node *node, - enum vb2_buffer_state state) -{ - struct cfe_device *cfe = node->cfe; - struct cfe_buffer *buf, *tmp; - unsigned long flags; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - spin_lock_irqsave(&cfe->state_lock, flags); - list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, state); - } - - if (node->cur_frm) - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); - if (node->next_frm && node->cur_frm != node->next_frm) - vb2_buffer_done(&node->next_frm->vb.vb2_buf, state); - - node->cur_frm = NULL; - node->next_frm = NULL; - spin_unlock_irqrestore(&cfe->state_lock, flags); -} - -/* - * vb2 ops - */ - -static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct cfe_node *node = vb2_get_drv_priv(vq); - struct cfe_device *cfe = node->cfe; - unsigned int size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : - node->meta_fmt.fmt.meta.buffersize; - - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, - node->buffer_queue.type); - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) { - cfe_err("sizes[0] %i < size %u\n", sizes[0], size); - return -EINVAL; - } - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int cfe_buffer_prepare(struct vb2_buffer *vb) -{ - struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct cfe_device *cfe = node->cfe; - struct cfe_buffer *buf = to_cfe_buffer(vb); - unsigned long size; - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, vb); - - size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : - node->meta_fmt.fmt.meta.buffersize; - if (vb2_plane_size(vb, 0) < size) { - cfe_err("data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - - if (node->id == FE_CONFIG) { - struct cfe_config_buffer *b = to_cfe_config_buffer(buf); - void *addr = vb2_plane_vaddr(vb, 0); - - memcpy(&b->config, addr, sizeof(struct pisp_fe_config)); - return pisp_fe_validate_config(&cfe->fe, &b->config, - &cfe->node[FE_OUT0].vid_fmt, - &cfe->node[FE_OUT1].vid_fmt); - } - - return 0; -} - -static void cfe_buffer_queue(struct vb2_buffer *vb) -{ - struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct cfe_device *cfe = node->cfe; - struct cfe_buffer *buf = to_cfe_buffer(vb); - unsigned long flags; - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, vb); - - spin_lock_irqsave(&cfe->state_lock, flags); - - list_add_tail(&buf->list, &node->dma_queue); - - if (!cfe->job_ready) - cfe->job_ready = cfe_check_job_ready(cfe); - - if (!cfe->job_queued && cfe->job_ready && - test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { - cfe_dbg("Preparing job immediately for channel %u\n", - node->id); - cfe_prepare_next_job(cfe); - } - - spin_unlock_irqrestore(&cfe->state_lock, flags); -} - -static u64 sensor_link_rate(struct cfe_device *cfe) -{ - struct v4l2_mbus_framefmt *source_fmt; - struct v4l2_subdev_state *state; - struct media_entity *entity; - struct v4l2_subdev *subdev; - const struct cfe_fmt *fmt; - struct media_pad *pad; - s64 link_freq; - - state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, 0); - fmt = find_format_by_code(source_fmt->code); - v4l2_subdev_unlock_state(state); - - /* - * Walk up the media graph to find either the sensor entity, or another - * entity that advertises the V4L2_CID_LINK_FREQ or V4L2_CID_PIXEL_RATE - * control through the subdev. - */ - entity = &cfe->csi2.sd.entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - goto err; - - pad = media_pad_remote_pad_first(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - goto err; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - if (entity->function == MEDIA_ENT_F_CAM_SENSOR || - v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ) || - v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE)) - break; - } - - link_freq = v4l2_get_link_freq(subdev->ctrl_handler, fmt->depth, - cfe->csi2.dphy.active_lanes * 2); - if (link_freq < 0) - goto err; - - /* x2 for DDR. */ - link_freq *= 2; - cfe_info("Using a link rate of %lld Mbps\n", link_freq / (1000 * 1000)); - return link_freq; - -err: - cfe_err("Unable to determine sensor link rate, using 999 Mbps\n"); - return 999 * 1000000UL; -} - -static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct v4l2_mbus_config mbus_config = { 0 }; - struct cfe_node *node = vb2_get_drv_priv(vq); - struct cfe_device *cfe = node->cfe; - int ret; - - cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name); - - if (!check_state(cfe, NODE_ENABLED, node->id)) { - cfe_err("%s node link is not enabled.\n", - node_desc[node->id].name); - ret = -EINVAL; - goto err_streaming; - } - - ret = pm_runtime_resume_and_get(&cfe->pdev->dev); - if (ret < 0) { - cfe_err("pm_runtime_resume_and_get failed\n"); - goto err_streaming; - } - - /* When using the Frontend, we must enable the FE_CONFIG node. */ - if (is_fe_enabled(cfe) && - !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) { - cfe_err("FE enabled, but FE_CONFIG node is not\n"); - ret = -EINVAL; - goto err_pm_put; - } - - ret = media_pipeline_start(&node->pad, &cfe->pipe); - if (ret < 0) { - cfe_err("Failed to start media pipeline: %d\n", ret); - goto err_pm_put; - } - - clear_state(cfe, FS_INT | FE_INT, node->id); - set_state(cfe, NODE_STREAMING, node->id); - node->fs_count = 0; - cfe_start_channel(node); - - if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { - cfe_dbg("Not all nodes are set to streaming yet!\n"); - return 0; - } - - cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI); - cfg_reg_write(cfe, MIPICFG_INTE, MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); - - ret = v4l2_subdev_call(cfe->sensor, pad, get_mbus_config, 0, - &mbus_config); - if (ret < 0 && ret != -ENOIOCTLCMD) { - cfe_err("g_mbus_config failed\n"); - goto err_pm_put; - } - - cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; - if (!cfe->csi2.dphy.active_lanes) - cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; - if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) { - cfe_err("Device has requested %u data lanes, which is >%u configured in DT\n", - cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes); - ret = -EINVAL; - goto err_disable_cfe; - } - - cfe_dbg("Configuring CSI-2 block - %u data lanes\n", cfe->csi2.dphy.active_lanes); - cfe->csi2.dphy.dphy_rate = sensor_link_rate(cfe) / 1000000UL; - csi2_open_rx(&cfe->csi2); - - cfe_dbg("Starting sensor streaming\n"); - ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1); - if (ret < 0) { - cfe_err("stream on failed in subdev\n"); - goto err_disable_cfe; - } - - cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name); - - return 0; - -err_disable_cfe: - csi2_close_rx(&cfe->csi2); - cfe_stop_channel(node, true); - media_pipeline_stop(&node->pad); -err_pm_put: - pm_runtime_put(&cfe->pdev->dev); -err_streaming: - cfe_return_buffers(node, VB2_BUF_STATE_QUEUED); - clear_state(cfe, NODE_STREAMING, node->id); - - return ret; -} - -static void cfe_stop_streaming(struct vb2_queue *vq) -{ - struct cfe_node *node = vb2_get_drv_priv(vq); - struct cfe_device *cfe = node->cfe; - unsigned long flags; - bool fe_stop; - - cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name); - - spin_lock_irqsave(&cfe->state_lock, flags); - fe_stop = is_fe_enabled(cfe) && - test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); - - cfe->job_ready = false; - clear_state(cfe, NODE_STREAMING, node->id); - spin_unlock_irqrestore(&cfe->state_lock, flags); - - cfe_stop_channel(node, fe_stop); - - if (!test_any_node(cfe, NODE_STREAMING)) { - /* Stop streaming the sensor and disable the peripheral. */ - if (v4l2_subdev_call(cfe->sensor, video, s_stream, 0) < 0) - cfe_err("stream off failed in subdev\n"); - - csi2_close_rx(&cfe->csi2); - - cfg_reg_write(cfe, MIPICFG_INTE, 0); - } - - media_pipeline_stop(&node->pad); - - /* Clear all queued buffers for the node */ - cfe_return_buffers(node, VB2_BUF_STATE_ERROR); - - pm_runtime_put(&cfe->pdev->dev); - - cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name); -} - -static const struct vb2_ops cfe_video_qops = { - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .queue_setup = cfe_queue_setup, - .buf_prepare = cfe_buffer_prepare, - .buf_queue = cfe_buffer_queue, - .start_streaming = cfe_start_streaming, - .stop_streaming = cfe_stop_streaming, -}; - -/* - * v4l2 ioctl ops - */ - -static int cfe_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&cfe->pdev->dev)); - - cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE | - V4L2_CAP_META_OUTPUT; - - return 0; -} - -static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - unsigned int i, j; - - if (!node_supports_image_output(node)) - return -EINVAL; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { - if (f->mbus_code && formats[i].code != f->mbus_code) - continue; - - if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT || - formats[i].flags & CFE_FORMAT_FLAG_META_CAP) - continue; - - if (is_fe_node(node) && - !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT)) - continue; - - if (j == f->index) { - f->pixelformat = formats[i].fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; - } - j++; - } - - return -EINVAL; -} - -static int cfe_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - if (!node_supports_image(node)) - return -EINVAL; - - *f = node->vid_fmt; - - return 0; -} - -static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f) -{ - struct cfe_device *cfe = node->cfe; - const struct cfe_fmt *fmt; - - cfe_dbg("%s: [%s] %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", - __func__, node_desc[node->id].name, - f->fmt.pix.width, f->fmt.pix.height, - V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat)); - - if (!node_supports_image_output(node)) - return -EINVAL; - - /* - * Default to a format that works for both CSI2 and FE. - */ - fmt = find_format_by_pix(f->fmt.pix.pixelformat); - if (!fmt) - fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10); - - f->fmt.pix.pixelformat = fmt->fourcc; - - if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) { - f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT]; - fmt = find_format_by_pix(f->fmt.pix.pixelformat); - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - - cfe_calc_format_size_bpl(cfe, fmt, f); - - return 0; -} - -static int cfe_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - struct vb2_queue *q = &node->buffer_queue; - int ret; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - if (vb2_is_busy(q)) - return -EBUSY; - - ret = try_fmt_vid_cap(node, f); - if (ret) - return ret; - - node->vid_fmt = *f; - - cfe_dbg("%s: Set %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", __func__, - node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height, - V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat)); - - return 0; -} - -static int cfe_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - return try_fmt_vid_cap(node, f); -} - -static int cfe_enum_fmt_meta(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - if (!node_supports_meta(node) || f->index != 0) - return -EINVAL; - - switch (node->id) { - case CSI2_CH0...CSI2_CH3: - f->pixelformat = V4L2_META_FMT_SENSOR_DATA; - return 0; - case FE_STATS: - f->pixelformat = V4L2_META_FMT_RPI_FE_STATS; - return 0; - case FE_CONFIG: - f->pixelformat = V4L2_META_FMT_RPI_FE_CFG; - return 0; - } - - return -EINVAL; -} - -static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f) -{ - if (!node_supports_meta(node)) - return -EINVAL; - - switch (node->id) { - case CSI2_CH0...CSI2_CH3: - f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - if (!f->fmt.meta.buffersize) - f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE; - f->fmt.meta.buffersize = - min_t(u32, f->fmt.meta.buffersize, MAX_BUFFER_SIZE); - f->fmt.meta.buffersize = - ALIGN(f->fmt.meta.buffersize, BPL_ALIGNMENT); - return 0; - case FE_STATS: - f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS; - f->fmt.meta.buffersize = sizeof(struct pisp_statistics); - return 0; - case FE_CONFIG: - f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG; - f->fmt.meta.buffersize = sizeof(struct pisp_fe_config); - return 0; - } - - return -EINVAL; -} - -static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - if (!node_supports_meta(node)) - return -EINVAL; - - *f = node->meta_fmt; - - return 0; -} - -static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - struct vb2_queue *q = &node->buffer_queue; - int ret; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - - if (vb2_is_busy(q)) - return -EBUSY; - - if (!node_supports_meta(node)) - return -EINVAL; - - ret = try_fmt_meta(node, f); - if (ret) - return ret; - - node->meta_fmt = *f; - - cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__, - V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat)); - - return 0; -} - -static int cfe_try_fmt_meta(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - return try_fmt_meta(node, f); -} - -static int cfe_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - const struct cfe_fmt *fmt; - - cfe_dbg("%s [%s]\n", __func__, node_desc[node->id].name); - - if (fsize->index > 0) - return -EINVAL; - - /* check for valid format */ - fmt = find_format_by_pix(fsize->pixel_format); - if (!fmt) { - cfe_dbg("Invalid pixel code: %x\n", fsize->pixel_format); - return -EINVAL; - } - - /* TODO: Do we have limits on the step_width? */ - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = MIN_WIDTH; - fsize->stepwise.max_width = MAX_WIDTH; - fsize->stepwise.step_width = 2; - fsize->stepwise.min_height = MIN_HEIGHT; - fsize->stepwise.max_height = MAX_HEIGHT; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct video_device *vdev = video_devdata(file); - struct cfe_node *node = video_get_drvdata(vdev); - struct cfe_device *cfe = node->cfe; - int ret; - - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, - p->type); - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - p->type != V4L2_BUF_TYPE_META_CAPTURE && - p->type != V4L2_BUF_TYPE_META_OUTPUT) - return -EINVAL; - - ret = vb2_queue_change_type(vdev->queue, p->type); - if (ret) - return ret; - - return vb2_ioctl_reqbufs(file, priv, p); -} - -static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *p) -{ - struct video_device *vdev = video_devdata(file); - struct cfe_node *node = video_get_drvdata(vdev); - struct cfe_device *cfe = node->cfe; - int ret; - - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, - p->format.type); - - if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - p->format.type != V4L2_BUF_TYPE_META_CAPTURE && - p->format.type != V4L2_BUF_TYPE_META_OUTPUT) - return -EINVAL; - - ret = vb2_queue_change_type(vdev->queue, p->format.type); - if (ret) - return ret; - - return vb2_ioctl_create_bufs(file, priv, p); -} - -static int cfe_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - struct cfe_node *node = video_get_drvdata(fh->vdev); - - switch (sub->type) { - case V4L2_EVENT_FRAME_SYNC: - if (!node_supports_image_output(node)) - break; - - return v4l2_event_subscribe(fh, sub, 2, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - if (!node_supports_image_output(node) && - !node_supports_meta_output(node)) - break; - - return v4l2_event_subscribe(fh, sub, 4, NULL); - } - - return v4l2_ctrl_subscribe_event(fh, sub); -} - -static const struct v4l2_ioctl_ops cfe_ioctl_ops = { - .vidioc_querycap = cfe_querycap, - .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cfe_g_fmt, - .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap, - - .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta, - .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta, - .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta, - .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta, - - .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta, - .vidioc_g_fmt_meta_out = cfe_g_fmt_meta, - .vidioc_s_fmt_meta_out = cfe_s_fmt_meta, - .vidioc_try_fmt_meta_out = cfe_try_fmt_meta, - - .vidioc_enum_framesizes = cfe_enum_framesizes, - - .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs, - .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_subscribe_event = cfe_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification, - void *arg) -{ - struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev); - unsigned int i; - - switch (notification) { - case V4L2_DEVICE_NOTIFY_EVENT: - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - - if (check_state(cfe, NODE_REGISTERED, i)) - continue; - - v4l2_event_queue(&node->video_dev, arg); - } - break; - default: - break; - } -} - -/* cfe capture driver file operations */ -static const struct v4l2_file_operations cfe_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -}; - -static int cfe_video_link_validate(struct media_link *link) -{ - struct video_device *vd = container_of(link->sink->entity, - struct video_device, entity); - struct cfe_node *node = container_of(vd, struct cfe_node, video_dev); - struct cfe_device *cfe = node->cfe; - struct v4l2_mbus_framefmt *source_fmt; - struct v4l2_subdev_state *state; - struct v4l2_subdev *source_sd; - int ret = 0; - - cfe_dbg("%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, - node_desc[node->id].name, - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index); - - if (!media_entity_remote_source_pad_unique(link->sink->entity)) { - cfe_err("video node %s pad not connected\n", vd->name); - return -ENOTCONN; - } - - source_sd = media_entity_to_v4l2_subdev(link->source->entity); - - state = v4l2_subdev_lock_and_get_active_state(source_sd); - - source_fmt = v4l2_subdev_get_pad_format(source_sd, state, - link->source->index); - if (!source_fmt) { - ret = -EINVAL; - goto out; - } - - if (is_image_output_node(node)) { - struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix; - const struct cfe_fmt *fmt = NULL; - unsigned int i; - - if (source_fmt->width != pix_fmt->width || - source_fmt->height != pix_fmt->height) { - cfe_err("Wrong width or height %ux%u (remote pad set to %ux%u)\n", - pix_fmt->width, pix_fmt->height, - source_fmt->width, - source_fmt->height); - ret = -EINVAL; - goto out; - } - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].code == source_fmt->code && - formats[i].fourcc == pix_fmt->pixelformat) { - fmt = &formats[i]; - break; - } - } - if (!fmt) { - cfe_err("Format mismatch!\n"); - ret = -EINVAL; - goto out; - } - } else if (is_csi2_node(node) && is_meta_output_node(node)) { - struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta; - const struct cfe_fmt *fmt; - u32 source_size; - - fmt = find_format_by_code(source_fmt->code); - if (!fmt || fmt->fourcc != meta_fmt->dataformat) { - cfe_err("Metadata format mismatch!\n"); - ret = -EINVAL; - goto out; - } - - source_size = DIV_ROUND_UP(source_fmt->width * source_fmt->height * fmt->depth, 8); - - if (source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) { - cfe_err("Bad metadata mbus format\n"); - ret = -EINVAL; - goto out; - } - - if (source_size > meta_fmt->buffersize) { - cfe_err("Metadata buffer too small: %u < %u\n", - meta_fmt->buffersize, source_size); - ret = -EINVAL; - goto out; - } - } - -out: - v4l2_subdev_unlock_state(state); - - return ret; -} - -static const struct media_entity_operations cfe_media_entity_ops = { - .link_validate = cfe_video_link_validate, -}; - -static int cfe_video_link_notify(struct media_link *link, u32 flags, - unsigned int notification) -{ - struct media_device *mdev = link->graph_obj.mdev; - struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev); - struct media_entity *fe = &cfe->fe.sd.entity; - struct media_entity *csi2 = &cfe->csi2.sd.entity; - unsigned long lock_flags; - unsigned int i; - - if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH) - return 0; - - cfe_dbg("%s: %s[%u] -> %s[%u] 0x%x", __func__, - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index, flags); - - spin_lock_irqsave(&cfe->state_lock, lock_flags); - - for (i = 0; i < NUM_NODES; i++) { - if (link->sink->entity != &cfe->node[i].video_dev.entity && - link->source->entity != &cfe->node[i].video_dev.entity) - continue; - - if (link->flags & MEDIA_LNK_FL_ENABLED) - set_state(cfe, NODE_ENABLED, i); - else - clear_state(cfe, NODE_ENABLED, i); - - break; - } - - spin_unlock_irqrestore(&cfe->state_lock, lock_flags); - - if (link->source->entity != csi2) - return 0; - if (link->sink->entity != fe) - return 0; - if (link->sink->index != 0) - return 0; - - cfe->fe_csi2_channel = -1; - if (link->flags & MEDIA_LNK_FL_ENABLED) { - if (link->source->index == node_desc[CSI2_CH0].link_pad) - cfe->fe_csi2_channel = CSI2_CH0; - else if (link->source->index == node_desc[CSI2_CH1].link_pad) - cfe->fe_csi2_channel = CSI2_CH1; - else if (link->source->index == node_desc[CSI2_CH2].link_pad) - cfe->fe_csi2_channel = CSI2_CH2; - else if (link->source->index == node_desc[CSI2_CH3].link_pad) - cfe->fe_csi2_channel = CSI2_CH3; - } - - if (is_fe_enabled(cfe)) - cfe_dbg("%s: Found CSI2:%d -> FE:0 link\n", __func__, - cfe->fe_csi2_channel); - else - cfe_dbg("%s: Unable to find CSI2:x -> FE:0 link\n", __func__); - - return 0; -} - -static const struct media_device_ops cfe_media_device_ops = { - .link_notify = cfe_video_link_notify, -}; - -static void cfe_release(struct kref *kref) -{ - struct cfe_device *cfe = container_of(kref, struct cfe_device, kref); - - media_device_cleanup(&cfe->mdev); - - kfree(cfe); -} - -static void cfe_put(struct cfe_device *cfe) -{ - kref_put(&cfe->kref, cfe_release); -} - -static void cfe_get(struct cfe_device *cfe) -{ - kref_get(&cfe->kref); -} - -static void cfe_node_release(struct video_device *vdev) -{ - struct cfe_node *node = video_get_drvdata(vdev); - - cfe_put(node->cfe); -} - -static int cfe_register_node(struct cfe_device *cfe, int id) -{ - struct video_device *vdev; - const struct cfe_fmt *fmt; - struct vb2_queue *q; - struct cfe_node *node = &cfe->node[id]; - int ret; - - node->cfe = cfe; - node->id = id; - - if (node_supports_image(node)) { - if (node_supports_image_output(node)) - node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - else - node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - - fmt = find_format_by_code(cfe_default_format.code); - if (!fmt) { - cfe_err("Failed to find format code\n"); - return -EINVAL; - } - - node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc; - v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, &cfe_default_format); - - ret = try_fmt_vid_cap(node, &node->vid_fmt); - if (ret) - return ret; - } - - if (node_supports_meta(node)) { - if (node_supports_meta_output(node)) - node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; - else - node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT; - - ret = try_fmt_meta(node, &node->meta_fmt); - if (ret) - return ret; - } - - mutex_init(&node->lock); - - q = &node->buffer_queue; - q->type = node_supports_image(node) ? node->vid_fmt.type : - node->meta_fmt.type; - q->io_modes = VB2_MMAP | VB2_DMABUF; - q->drv_priv = node; - q->ops = &cfe_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer) - : sizeof(struct cfe_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &node->lock; - q->min_buffers_needed = 1; - q->dev = &cfe->pdev->dev; - - ret = vb2_queue_init(q); - if (ret) { - cfe_err("vb2_queue_init() failed\n"); - return ret; - } - - INIT_LIST_HEAD(&node->dma_queue); - - vdev = &node->video_dev; - vdev->release = cfe_node_release; - vdev->fops = &cfe_fops; - vdev->ioctl_ops = &cfe_ioctl_ops; - vdev->entity.ops = &cfe_media_entity_ops; - vdev->v4l2_dev = &cfe->v4l2_dev; - vdev->vfl_dir = (node_supports_image_output(node) || - node_supports_meta_output(node)) ? - VFL_DIR_RX : - VFL_DIR_TX; - vdev->queue = q; - vdev->lock = &node->lock; - vdev->device_caps = node_desc[id].caps; - vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; - - /* Define the device names */ - snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME, - node_desc[id].name); - - video_set_drvdata(vdev, node); - if (node->id == FE_OUT0) - vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; - node->pad.flags = node_desc[id].pad_flags; - media_entity_pads_init(&vdev->entity, 1, &node->pad); - - if (!node_supports_image(node)) { - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMEINTERVALS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMESIZES); - } - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - cfe_err("Unable to register video device %s\n", vdev->name); - return ret; - } - - cfe_info("Registered [%s] node id %d successfully as /dev/video%u\n", - vdev->name, id, vdev->num); - - /* - * Acquire a reference to cfe, which will be released when the video - * device will be unregistered and userspace will have closed all open - * file handles. - */ - cfe_get(cfe); - set_state(cfe, NODE_REGISTERED, id); - - return 0; -} - -static void cfe_unregister_nodes(struct cfe_device *cfe) -{ - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - - if (check_state(cfe, NODE_REGISTERED, i)) { - clear_state(cfe, NODE_REGISTERED, i); - video_unregister_device(&node->video_dev); - } - } -} - -static int cfe_link_node_pads(struct cfe_device *cfe) -{ - unsigned int i, source_pad = 0; - int ret; - - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { - struct cfe_node *node = &cfe->node[i]; - - if (!check_state(cfe, NODE_REGISTERED, i)) - continue; - - /* Find next source pad */ - while (source_pad < cfe->sensor->entity.num_pads && - !(cfe->sensor->entity.pads[source_pad].flags & - MEDIA_PAD_FL_SOURCE)) - source_pad++; - - if (source_pad < cfe->sensor->entity.num_pads) { - /* Sensor -> CSI2 */ - ret = media_create_pad_link(&cfe->sensor->entity, source_pad, - &cfe->csi2.sd.entity, i, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - /* Dealt with that source_pad, look at the next one next time */ - source_pad++; - } - - /* CSI2 channel # -> /dev/video# */ - ret = media_create_pad_link(&cfe->csi2.sd.entity, - node_desc[i].link_pad, - &node->video_dev.entity, 0, 0); - if (ret) - return ret; - - if (node_supports_image(node)) { - /* CSI2 channel # -> FE Input */ - ret = media_create_pad_link(&cfe->csi2.sd.entity, - node_desc[i].link_pad, - &cfe->fe.sd.entity, - FE_STREAM_PAD, 0); - if (ret) - return ret; - } - } - - for (; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - struct media_entity *src, *dst; - unsigned int src_pad, dst_pad; - - if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) { - /* FE -> /dev/video# */ - src = &cfe->fe.sd.entity; - src_pad = node_desc[i].link_pad; - dst = &node->video_dev.entity; - dst_pad = 0; - } else { - /* /dev/video# -> FE */ - dst = &cfe->fe.sd.entity; - dst_pad = node_desc[i].link_pad; - src = &node->video_dev.entity; - src_pad = 0; - } - - ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0); - if (ret) - return ret; - } - - return 0; -} - -static int cfe_probe_complete(struct cfe_device *cfe) -{ - unsigned int i; - int ret; - - cfe->v4l2_dev.notify = cfe_notify; - - for (i = 0; i < NUM_NODES; i++) { - ret = cfe_register_node(cfe, i); - if (ret) { - cfe_err("Unable to register video node %u.\n", i); - goto unregister; - } - } - - ret = cfe_link_node_pads(cfe); - if (ret) { - cfe_err("Unable to link node pads.\n"); - goto unregister; - } - - ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); - if (ret) { - cfe_err("Unable to register subdev nodes.\n"); - goto unregister; - } - - return 0; - -unregister: - cfe_unregister_nodes(cfe); - return ret; -} - -static int cfe_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); - - if (cfe->sensor) { - cfe_info("Rejecting subdev %s (Already set!!)", subdev->name); - return 0; - } - - cfe->sensor = subdev; - cfe_info("Using sensor %s for capture\n", subdev->name); - - return 0; -} - -static int cfe_async_complete(struct v4l2_async_notifier *notifier) -{ - struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); - - return cfe_probe_complete(cfe); -} - -static const struct v4l2_async_notifier_operations cfe_async_ops = { - .bound = cfe_async_bound, - .complete = cfe_async_complete, -}; - -static int of_cfe_connect_subdevs(struct cfe_device *cfe) -{ - struct platform_device *pdev = cfe->pdev; - struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct device_node *node = pdev->dev.of_node; - struct device_node *ep_node; - struct device_node *sensor_node; - unsigned int lane; - int ret = -EINVAL; - - /* Get the local endpoint and remote device. */ - ep_node = of_graph_get_next_endpoint(node, NULL); - if (!ep_node) { - cfe_err("can't get next endpoint\n"); - return -EINVAL; - } - - cfe_dbg("ep_node is %pOF\n", ep_node); - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - cfe_err("can't get remote parent\n"); - goto cleanup_exit; - } - - cfe_info("found subdevice %pOF\n", sensor_node); - - /* Parse the local endpoint and validate its configuration. */ - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); - - cfe->csi2.multipacket_line = - fwnode_property_present(of_fwnode_handle(ep_node), - "multipacket-line"); - - if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - cfe_err("endpoint node type != CSI2\n"); - return -EINVAL; - } - - for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) { - if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { - cfe_err("subdevice %pOF: data lanes reordering not supported\n", - sensor_node); - goto cleanup_exit; - } - } - - cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes; - cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags; - - cfe_dbg("subdevice %pOF: %u data lanes, flags=0x%08x, multipacket_line=%u\n", - sensor_node, cfe->csi2.dphy.max_lanes, cfe->csi2.bus_flags, - cfe->csi2.multipacket_line); - - /* Initialize and register the async notifier. */ - v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev); - cfe->notifier.ops = &cfe_async_ops; - - cfe->asd = v4l2_async_nf_add_fwnode(&cfe->notifier, - of_fwnode_handle(sensor_node), - struct v4l2_async_connection); - if (IS_ERR(cfe->asd)) { - cfe_err("Error adding subdevice: %d\n", ret); - goto cleanup_exit; - } - - ret = v4l2_async_nf_register(&cfe->notifier); - if (ret) { - cfe_err("Error registering async notifier: %d\n", ret); - ret = -EINVAL; - } - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - - return ret; -} - -static int cfe_probe(struct platform_device *pdev) -{ - struct cfe_device *cfe; - char debugfs_name[32]; - int ret; - - cfe = kzalloc(sizeof(*cfe), GFP_KERNEL); - if (!cfe) - return -ENOMEM; - - platform_set_drvdata(pdev, cfe); - - kref_init(&cfe->kref); - cfe->pdev = pdev; - cfe->fe_csi2_channel = -1; - spin_lock_init(&cfe->state_lock); - - cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(cfe->csi2.base)) { - dev_err(&pdev->dev, "Failed to get dma io block\n"); - ret = PTR_ERR(cfe->csi2.base); - goto err_cfe_put; - } - - cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(cfe->csi2.dphy.base)) { - dev_err(&pdev->dev, "Failed to get host io block\n"); - ret = PTR_ERR(cfe->csi2.dphy.base); - goto err_cfe_put; - } - - cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2); - if (IS_ERR(cfe->mipi_cfg_base)) { - dev_err(&pdev->dev, "Failed to get mipi cfg io block\n"); - ret = PTR_ERR(cfe->mipi_cfg_base); - goto err_cfe_put; - } - - cfe->fe.base = devm_platform_ioremap_resource(pdev, 3); - if (IS_ERR(cfe->fe.base)) { - dev_err(&pdev->dev, "Failed to get pisp fe io block\n"); - ret = PTR_ERR(cfe->fe.base); - goto err_cfe_put; - } - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "No IRQ resource\n"); - ret = -EINVAL; - goto err_cfe_put; - } - - ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe); - if (ret) { - dev_err(&pdev->dev, "Unable to request interrupt\n"); - ret = -EINVAL; - goto err_cfe_put; - } - - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (ret) { - dev_err(&pdev->dev, "DMA enable failed\n"); - goto err_cfe_put; - } - - /* TODO: Enable clock only when running. */ - cfe->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(cfe->clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk), - "clock not found\n"); - - cfe->mdev.dev = &pdev->dev; - cfe->mdev.ops = &cfe_media_device_ops; - strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model)); - strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial)); - snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s", - dev_name(&pdev->dev)); - - media_device_init(&cfe->mdev); - - cfe->v4l2_dev.mdev = &cfe->mdev; - - ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev); - if (ret) { - cfe_err("Unable to register v4l2 device.\n"); - goto err_cfe_put; - } - - snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s", - dev_name(&pdev->dev)); - cfe->debugfs = debugfs_create_dir(debugfs_name, NULL); - debugfs_create_file("format", 0444, cfe->debugfs, cfe, &format_fops); - debugfs_create_file("regs", 0444, cfe->debugfs, cfe, - &mipi_cfg_regs_fops); - - /* Enable the block power domain */ - pm_runtime_enable(&pdev->dev); - - ret = pm_runtime_resume_and_get(&cfe->pdev->dev); - if (ret) - goto err_runtime_disable; - - cfe->csi2.v4l2_dev = &cfe->v4l2_dev; - ret = csi2_init(&cfe->csi2, cfe->debugfs); - if (ret) { - cfe_err("Failed to init csi2 (%d)\n", ret); - goto err_runtime_put; - } - - cfe->fe.v4l2_dev = &cfe->v4l2_dev; - ret = pisp_fe_init(&cfe->fe, cfe->debugfs); - if (ret) { - cfe_err("Failed to init pisp fe (%d)\n", ret); - goto err_csi2_uninit; - } - - cfe->mdev.hw_revision = cfe->fe.hw_revision; - ret = media_device_register(&cfe->mdev); - if (ret < 0) { - cfe_err("Unable to register media-controller device.\n"); - goto err_pisp_fe_uninit; - } - - ret = of_cfe_connect_subdevs(cfe); - if (ret) { - cfe_err("Failed to connect subdevs\n"); - goto err_media_unregister; - } - - pm_runtime_put(&cfe->pdev->dev); - - return 0; - -err_media_unregister: - media_device_unregister(&cfe->mdev); -err_pisp_fe_uninit: - pisp_fe_uninit(&cfe->fe); -err_csi2_uninit: - csi2_uninit(&cfe->csi2); -err_runtime_put: - pm_runtime_put(&cfe->pdev->dev); -err_runtime_disable: - pm_runtime_disable(&pdev->dev); - debugfs_remove(cfe->debugfs); - v4l2_device_unregister(&cfe->v4l2_dev); -err_cfe_put: - cfe_put(cfe); - - return ret; -} - -static int cfe_remove(struct platform_device *pdev) -{ - struct cfe_device *cfe = platform_get_drvdata(pdev); - - debugfs_remove(cfe->debugfs); - - v4l2_async_nf_unregister(&cfe->notifier); - media_device_unregister(&cfe->mdev); - cfe_unregister_nodes(cfe); - - pisp_fe_uninit(&cfe->fe); - csi2_uninit(&cfe->csi2); - - pm_runtime_disable(&pdev->dev); - - v4l2_device_unregister(&cfe->v4l2_dev); - - cfe_put(cfe); - - return 0; -} - -static int cfe_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct cfe_device *cfe = platform_get_drvdata(pdev); - - clk_disable_unprepare(cfe->clk); - - return 0; -} - -static int cfe_runtime_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct cfe_device *cfe = platform_get_drvdata(pdev); - int ret; - - ret = clk_prepare_enable(cfe->clk); - if (ret) { - dev_err(dev, "Unable to enable clock\n"); - return ret; - } - - return 0; -} - -static const struct dev_pm_ops cfe_pm_ops = { - SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL) - SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) -}; - -static const struct of_device_id cfe_of_match[] = { - { .compatible = "raspberrypi,rp1-cfe" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, cfe_of_match); - -static struct platform_driver cfe_driver = { - .probe = cfe_probe, - .remove = cfe_remove, - .driver = { - .name = CFE_MODULE_NAME, - .of_match_table = cfe_of_match, - .pm = &cfe_pm_ops, - }, -}; - -module_platform_driver(cfe_driver); - -MODULE_AUTHOR("Naushir Patuck "); -MODULE_DESCRIPTION("RP1 Camera Front End driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(CFE_VERSION); diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h deleted file mode 100644 index 637b63a838c407..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * RP1 CFE driver. - * Copyright (c) 2021 Raspberry Pi Ltd. - * - */ -#ifndef _RP1_CFE_ -#define _RP1_CFE_ - -#include -#include -#include - -extern bool cfe_debug_verbose; - -enum cfe_remap_types { - CFE_REMAP_16BIT, - CFE_REMAP_COMPRESSED, - CFE_NUM_REMAP, -}; - -#define CFE_FORMAT_FLAG_META_OUT BIT(0) -#define CFE_FORMAT_FLAG_META_CAP BIT(1) -#define CFE_FORMAT_FLAG_FE_OUT BIT(2) - -struct cfe_fmt { - u32 fourcc; - u32 code; - u8 depth; - u8 csi_dt; - u32 remap[CFE_NUM_REMAP]; - u32 flags; -}; - -extern const struct v4l2_mbus_framefmt cfe_default_format; -extern const struct v4l2_mbus_framefmt cfe_default_meta_format; - -const struct cfe_fmt *find_format_by_code(u32 code); -const struct cfe_fmt *find_format_by_pix(u32 pixelformat); -u32 cfe_find_16bit_code(u32 code); -u32 cfe_find_compressed_code(u32 code); - -#endif diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h deleted file mode 100644 index 29c807253e6428..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h +++ /dev/null @@ -1,318 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * RP1 Camera Front End formats definition - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _CFE_FMTS_H_ -#define _CFE_FMTS_H_ - -#include "cfe.h" -#include - -static const struct cfe_fmt formats[] = { - /* YUV Formats */ - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - }, - { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - }, - { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - }, - { - /* RGB Formats */ - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - }, - { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - }, - { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - }, - { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - }, - { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - }, - { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_BGR888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - }, - { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .depth = 32, - .csi_dt = 0x0, - }, - - /* Bayer Formats */ - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10P, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10P, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10P, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10P, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12P, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12P, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12P, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12P, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR14P, - .code = MEDIA_BUS_FMT_SBGGR14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG14P, - .code = MEDIA_BUS_FMT_SGBRG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG14P, - .code = MEDIA_BUS_FMT_SGRBG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB14P, - .code = MEDIA_BUS_FMT_SRGGB14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR16, - .code = MEDIA_BUS_FMT_SBGGR16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .flags = CFE_FORMAT_FLAG_FE_OUT, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG16, - .code = MEDIA_BUS_FMT_SGBRG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .flags = CFE_FORMAT_FLAG_FE_OUT, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG16, - .code = MEDIA_BUS_FMT_SGRBG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .flags = CFE_FORMAT_FLAG_FE_OUT, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB16, - .code = MEDIA_BUS_FMT_SRGGB16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .flags = CFE_FORMAT_FLAG_FE_OUT, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, - }, - /* PiSP Compressed Mode 1 */ - { - .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, - .code = MEDIA_BUS_FMT_SRGGB16_1X16, - .depth = 8, - .flags = CFE_FORMAT_FLAG_FE_OUT, - }, - { - .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, - .code = MEDIA_BUS_FMT_SBGGR16_1X16, - .depth = 8, - .flags = CFE_FORMAT_FLAG_FE_OUT, - }, - { - .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, - .code = MEDIA_BUS_FMT_SGBRG16_1X16, - .depth = 8, - .flags = CFE_FORMAT_FLAG_FE_OUT, - }, - { - .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, - .code = MEDIA_BUS_FMT_SGRBG16_1X16, - .depth = 8, - .flags = CFE_FORMAT_FLAG_FE_OUT, - }, - /* Greyscale format */ - { - .fourcc = V4L2_PIX_FMT_GREY, - .code = MEDIA_BUS_FMT_Y8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, - }, - { - .fourcc = V4L2_PIX_FMT_Y10P, - .code = MEDIA_BUS_FMT_Y10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, - }, - { - .fourcc = V4L2_PIX_FMT_Y12P, - .code = MEDIA_BUS_FMT_Y12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, - }, - { - .fourcc = V4L2_PIX_FMT_Y14P, - .code = MEDIA_BUS_FMT_Y14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, - }, - { - .fourcc = V4L2_PIX_FMT_Y16, - .code = MEDIA_BUS_FMT_Y16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .flags = CFE_FORMAT_FLAG_FE_OUT, - .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, - }, - { - .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, - .code = MEDIA_BUS_FMT_Y16_1X16, - .depth = 8, - .flags = CFE_FORMAT_FLAG_FE_OUT, - }, - /* Embedded data format */ - { - .fourcc = V4L2_META_FMT_SENSOR_DATA, - .code = MEDIA_BUS_FMT_SENSOR_DATA, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, - .flags = CFE_FORMAT_FLAG_META_CAP, - }, - - /* Frontend formats */ - { - .fourcc = V4L2_META_FMT_RPI_FE_CFG, - .code = MEDIA_BUS_FMT_FIXED, - .flags = CFE_FORMAT_FLAG_META_OUT, - }, - { - .fourcc = V4L2_META_FMT_RPI_FE_STATS, - .code = MEDIA_BUS_FMT_FIXED, - .flags = CFE_FORMAT_FLAG_META_CAP, - }, -}; - -#endif /* _CFE_FMTS_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c deleted file mode 100644 index f4fd832c6aed11..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c +++ /dev/null @@ -1,628 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * RP1 CSI-2 Driver - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ - -#include -#include -#include -#include - -#include - -#include "csi2.h" -#include "cfe.h" - -static bool csi2_track_errors; -module_param_named(track_csi2_errors, csi2_track_errors, bool, 0); -MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); - -#define csi2_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \ - } while (0) -#define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg) -#define csi2_info(fmt, arg...) dev_info(csi2->v4l2_dev->dev, fmt, ##arg) -#define csi2_err(fmt, arg...) dev_err(csi2->v4l2_dev->dev, fmt, ##arg) - -/* CSI2-DMA registers */ -#define CSI2_STATUS 0x000 -#define CSI2_QOS 0x004 -#define CSI2_DISCARDS_OVERFLOW 0x008 -#define CSI2_DISCARDS_INACTIVE 0x00c -#define CSI2_DISCARDS_UNMATCHED 0x010 -#define CSI2_DISCARDS_LEN_LIMIT 0x014 - -#define CSI2_DISCARDS_AMOUNT_SHIFT 0 -#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0) -#define CSI2_DISCARDS_DT_SHIFT 24 -#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24) -#define CSI2_DISCARDS_VC_SHIFT 30 -#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30) - -#define CSI2_LLEV_PANICS 0x018 -#define CSI2_ULEV_PANICS 0x01c -#define CSI2_IRQ_MASK 0x020 -#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0) -#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1) -#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2) -#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3) -#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4) -#define CSI2_IRQ_MASK_IRQ_ALL \ - (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \ - CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \ - CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \ - CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE) - -#define CSI2_CTRL 0x024 -#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28) -#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c) -#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c) -#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30) -#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34) -#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38) -#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40) -#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44) -#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48) - -/* CSI2_STATUS */ -#define IRQ_FS(x) (BIT(0) << (x)) -#define IRQ_FE(x) (BIT(4) << (x)) -#define IRQ_FE_ACK(x) (BIT(8) << (x)) -#define IRQ_LE(x) (BIT(12) << (x)) -#define IRQ_LE_ACK(x) (BIT(16) << (x)) -#define IRQ_CH_MASK(x) (IRQ_FS(x) | IRQ_FE(x) | IRQ_FE_ACK(x) | IRQ_LE(x) | IRQ_LE_ACK(x)) -#define IRQ_OVERFLOW BIT(20) -#define IRQ_DISCARD_OVERFLOW BIT(21) -#define IRQ_DISCARD_LEN_LIMIT BIT(22) -#define IRQ_DISCARD_UNMATCHED BIT(23) -#define IRQ_DISCARD_INACTIVE BIT(24) - -/* CSI2_CTRL */ -#define EOP_IS_EOL BIT(0) - -/* CSI2_CH_CTRL */ -#define DMA_EN BIT(0) -#define FORCE BIT(3) -#define AUTO_ARM BIT(4) -#define IRQ_EN_FS BIT(13) -#define IRQ_EN_FE BIT(14) -#define IRQ_EN_FE_ACK BIT(15) -#define IRQ_EN_LE BIT(16) -#define IRQ_EN_LE_ACK BIT(17) -#define FLUSH_FE BIT(28) -#define PACK_LINE BIT(29) -#define PACK_BYTES BIT(30) -#define CH_MODE_MASK GENMASK(2, 1) -#define VC_MASK GENMASK(6, 5) -#define DT_MASK GENMASK(12, 7) -#define LC_MASK GENMASK(27, 18) - -/* CHx_COMPRESSION_CONTROL */ -#define COMP_OFFSET_MASK GENMASK(15, 0) -#define COMP_SHIFT_MASK GENMASK(19, 16) -#define COMP_MODE_MASK GENMASK(25, 24) - -static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) -{ - return readl(csi2->base + offset); -} - -static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val) -{ - writel(val, csi2->base + offset); - csi2_dbg_verbose("csi2: write 0x%04x -> 0x%03x\n", val, offset); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -static int csi2_regs_show(struct seq_file *s, void *data) -{ - struct csi2_device *csi2 = s->private; - unsigned int i; - int ret; - - ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev); - if (ret) - return ret; - -#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg)) -#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, csi2_reg_read(csi2, reg(idx))) - - DUMP(CSI2_STATUS); - DUMP(CSI2_DISCARDS_OVERFLOW); - DUMP(CSI2_DISCARDS_INACTIVE); - DUMP(CSI2_DISCARDS_UNMATCHED); - DUMP(CSI2_DISCARDS_LEN_LIMIT); - DUMP(CSI2_LLEV_PANICS); - DUMP(CSI2_ULEV_PANICS); - DUMP(CSI2_IRQ_MASK); - DUMP(CSI2_CTRL); - - for (i = 0; i < CSI2_NUM_CHANNELS; ++i) { - DUMP_CH(i, CSI2_CH_CTRL); - DUMP_CH(i, CSI2_CH_ADDR0); - DUMP_CH(i, CSI2_CH_ADDR1); - DUMP_CH(i, CSI2_CH_STRIDE); - DUMP_CH(i, CSI2_CH_LENGTH); - DUMP_CH(i, CSI2_CH_DEBUG); - DUMP_CH(i, CSI2_CH_FRAME_SIZE); - DUMP_CH(i, CSI2_CH_COMP_CTRL); - DUMP_CH(i, CSI2_CH_FE_FRAME_ID); - } - -#undef DUMP -#undef DUMP_CH - - pm_runtime_put(csi2->v4l2_dev->dev); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(csi2_regs); - -static int csi2_errors_show(struct seq_file *s, void *data) -{ - struct csi2_device *csi2 = s->private; - unsigned long flags; - u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; - u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; - u32 overflows; - - spin_lock_irqsave(&csi2->errors_lock, flags); - - memcpy(discards_table, csi2->discards_table, sizeof(discards_table)); - memcpy(discards_dt_table, csi2->discards_dt_table, - sizeof(discards_dt_table)); - overflows = csi2->overflows; - - csi2->overflows = 0; - memset(csi2->discards_table, 0, sizeof(discards_table)); - memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table)); - - spin_unlock_irqrestore(&csi2->errors_lock, flags); - - seq_printf(s, "Overflows %u\n", overflows); - seq_puts(s, "Discards:\n"); - seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n"); - - for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) { - seq_printf(s, "%u %10u %10u %10u %10u\n", vc, - discards_table[vc][DISCARDS_TABLE_OVERFLOW], - discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT], - discards_table[vc][DISCARDS_TABLE_UNMATCHED], - discards_table[vc][DISCARDS_TABLE_INACTIVE]); - } - - seq_printf(s, "Last DT %10u %10u %10u %10u\n", - discards_dt_table[DISCARDS_TABLE_OVERFLOW], - discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT], - discards_dt_table[DISCARDS_TABLE_UNMATCHED], - discards_dt_table[DISCARDS_TABLE_INACTIVE]); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(csi2_errors); - -static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) -{ - spin_lock(&csi2->errors_lock); - - if (status & IRQ_OVERFLOW) - csi2->overflows++; - - for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) { - static const u32 discard_bits[] = { - IRQ_DISCARD_OVERFLOW, - IRQ_DISCARD_LEN_LIMIT, - IRQ_DISCARD_UNMATCHED, - IRQ_DISCARD_INACTIVE, - }; - static const u8 discard_regs[] = { - CSI2_DISCARDS_OVERFLOW, - CSI2_DISCARDS_LEN_LIMIT, - CSI2_DISCARDS_UNMATCHED, - CSI2_DISCARDS_INACTIVE, - }; - u32 amount; - u8 dt, vc; - u32 v; - - if (!(status & discard_bits[i])) - continue; - - v = csi2_reg_read(csi2, discard_regs[i]); - csi2_reg_write(csi2, discard_regs[i], 0); - - amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >> - CSI2_DISCARDS_AMOUNT_SHIFT; - dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT; - vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT; - - csi2->discards_table[vc][i] += amount; - csi2->discards_dt_table[i] = dt; - } - - spin_unlock(&csi2->errors_lock); -} - -void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof) -{ - unsigned int i; - u32 status; - - status = csi2_reg_read(csi2, CSI2_STATUS); - csi2_dbg_verbose("ISR: STA: 0x%x\n", status); - - /* Write value back to clear the interrupts */ - csi2_reg_write(csi2, CSI2_STATUS, status); - - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { - u32 dbg; - - if ((status & IRQ_CH_MASK(i)) == 0) - continue; - - dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i)); - - csi2_dbg_verbose("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n", - i, (status & IRQ_FS(i)) ? "FS " : "", - (status & IRQ_FE(i)) ? "FE " : "", - (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "", - (status & IRQ_LE(i)) ? "LE " : "", - (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "", - dbg >> 16, - csi2->num_lines[i] ? - ((dbg & 0xffff) % csi2->num_lines[i]) : - 0); - - sof[i] = !!(status & IRQ_FS(i)); - eof[i] = !!(status & IRQ_FE_ACK(i)); - } - - if (csi2_track_errors) - csi2_isr_handle_errors(csi2, status); -} - -void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, - dma_addr_t dmaaddr, unsigned int stride, unsigned int size) -{ - u64 addr = dmaaddr; - /* - * ADDRESS0 must be written last as it triggers the double buffering - * mechanism for all buffer registers within the hardware. - */ - addr >>= 4; - csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4); - csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4); - csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32); - csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff); -} - -void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, - enum csi2_compression_mode mode, unsigned int shift, - unsigned int offset) -{ - u32 compression = 0; - - set_field(&compression, COMP_OFFSET_MASK, offset); - set_field(&compression, COMP_SHIFT_MASK, shift); - set_field(&compression, COMP_MODE_MASK, mode); - csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression); -} - -static int csi2_get_vc_dt_fallback(struct csi2_device *csi2, - unsigned int channel, u8 *vc, u8 *dt) -{ - struct v4l2_subdev *sd = &csi2->sd; - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *fmt; - const struct cfe_fmt *cfe_fmt; - - state = v4l2_subdev_get_locked_active_state(sd); - - /* Without Streams API, the channel number matches the sink pad */ - fmt = v4l2_subdev_get_pad_format(sd, state, channel); - if (!fmt) - return -EINVAL; - - cfe_fmt = find_format_by_code(fmt->code); - if (!cfe_fmt) - return -EINVAL; - - *vc = 0; - *dt = cfe_fmt->csi_dt; - - return 0; -} - -static int csi2_get_vc_dt(struct csi2_device *csi2, unsigned int channel, - u8 *vc, u8 *dt) -{ - struct v4l2_mbus_frame_desc remote_desc; - const struct media_pad *remote_pad; - struct v4l2_subdev *source_sd; - int ret; - - /* Without Streams API, the channel number matches the sink pad */ - remote_pad = media_pad_remote_pad_first(&csi2->pad[channel]); - if (!remote_pad) - return -EPIPE; - - source_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - - ret = v4l2_subdev_call(source_sd, pad, get_frame_desc, - remote_pad->index, &remote_desc); - if (ret == -ENOIOCTLCMD) { - csi2_dbg("source does not support get_frame_desc, use fallback\n"); - return csi2_get_vc_dt_fallback(csi2, channel, vc, dt); - } else if (ret) { - csi2_err("Failed to get frame descriptor\n"); - return ret; - } - - if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { - csi2_err("Frame descriptor does not describe CSI-2 link"); - return -EINVAL; - } - - if (remote_desc.num_entries != 1) { - csi2_err("Frame descriptor does not have a single entry"); - return -EINVAL; - } - - *vc = remote_desc.entry[0].bus.csi2.vc; - *dt = remote_desc.entry[0].bus.csi2.dt; - - return 0; -} - -void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, - enum csi2_mode mode, bool auto_arm, - bool pack_bytes, unsigned int width, - unsigned int height) -{ - u32 ctrl; - int ret; - u8 vc, dt; - - ret = csi2_get_vc_dt(csi2, channel, &vc, &dt); - if (ret) - return; - - csi2_dbg("%s [%u]\n", __func__, channel); - - csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0); - csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0); - csi2_reg_write(csi2, CSI2_STATUS, IRQ_CH_MASK(channel)); - - /* Enable channel and FS/FE interrupts. */ - ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | PACK_LINE; - /* PACK_BYTES ensures no striding for embedded data. */ - if (pack_bytes) - ctrl |= PACK_BYTES; - - if (auto_arm) - ctrl |= AUTO_ARM; - - if (width && height) { - set_field(&ctrl, mode, CH_MODE_MASK); - csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), - (height << 16) | width); - } else { - set_field(&ctrl, 0x0, CH_MODE_MASK); - csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0); - } - - set_field(&ctrl, vc, VC_MASK); - set_field(&ctrl, dt, DT_MASK); - csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl); - csi2->num_lines[channel] = height; -} - -void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel) -{ - csi2_dbg("%s [%u]\n", __func__, channel); - - /* Channel disable. Use FORCE to allow stopping mid-frame. */ - csi2_reg_write(csi2, CSI2_CH_CTRL(channel), FORCE); - /* Latch the above change by writing to the ADDR0 register. */ - csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); - /* Write this again, the HW needs it! */ - csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); -} - -void csi2_open_rx(struct csi2_device *csi2) -{ - csi2_reg_write(csi2, CSI2_IRQ_MASK, - csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0); - - dphy_start(&csi2->dphy); - - csi2_reg_write(csi2, CSI2_CTRL, - csi2->multipacket_line ? 0 : EOP_IS_EOL); -} - -void csi2_close_rx(struct csi2_device *csi2) -{ - dphy_stop(&csi2->dphy); - - csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); -} - -static int csi2_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) -{ - struct v4l2_mbus_framefmt *fmt; - - for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { - const struct v4l2_mbus_framefmt *def_fmt; - - /* CSI2_CH1_EMBEDDED */ - if (i == 1) - def_fmt = &cfe_default_meta_format; - else - def_fmt = &cfe_default_format; - - fmt = v4l2_subdev_get_pad_format(sd, state, i); - *fmt = *def_fmt; - - fmt = v4l2_subdev_get_pad_format(sd, state, i + CSI2_NUM_CHANNELS); - *fmt = *def_fmt; - } - - return 0; -} - -static int csi2_pad_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - if (format->pad < CSI2_NUM_CHANNELS) { - /* - * Store the sink pad format and propagate it to the source pad. - */ - - struct v4l2_mbus_framefmt *fmt; - - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - if (!fmt) - return -EINVAL; - - *fmt = format->format; - - fmt = v4l2_subdev_get_pad_format(sd, state, - format->pad + CSI2_NUM_CHANNELS); - if (!fmt) - return -EINVAL; - - format->format.field = V4L2_FIELD_NONE; - - *fmt = format->format; - } else { - /* - * Only allow changing the source pad mbus code. - */ - - struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; - u32 sink_code; - u32 code; - - sink_fmt = v4l2_subdev_get_pad_format(sd, state, - format->pad - CSI2_NUM_CHANNELS); - if (!sink_fmt) - return -EINVAL; - - source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - if (!source_fmt) - return -EINVAL; - - sink_code = sink_fmt->code; - code = format->format.code; - - /* - * If the source code from the user does not match the code in - * the sink pad, check that the source code matches either the - * 16-bit version or the compressed version of the sink code. - */ - - if (code != sink_code && - (code == cfe_find_16bit_code(sink_code) || - code == cfe_find_compressed_code(sink_code))) - source_fmt->code = code; - - format->format.code = source_fmt->code; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = csi2_pad_set_fmt, - .link_validate = v4l2_subdev_link_validate_default, -}; - -static const struct media_entity_operations csi2_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_ops csi2_subdev_ops = { - .pad = &csi2_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops csi2_internal_ops = { - .init_state = csi2_init_state, -}; - -int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) -{ - unsigned int i, ret; - - spin_lock_init(&csi2->errors_lock); - - csi2->dphy.dev = csi2->v4l2_dev->dev; - dphy_probe(&csi2->dphy); - - debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops); - - if (csi2_track_errors) - debugfs_create_file("csi2_errors", 0444, debugfs, csi2, - &csi2_errors_fops); - - for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++) - csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad), - csi2->pad); - if (ret) - return ret; - - /* Initialize subdev */ - v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); - csi2->sd.internal_ops = &csi2_internal_ops; - csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - csi2->sd.entity.ops = &csi2_entity_ops; - csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - csi2->sd.owner = THIS_MODULE; - snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2"); - - ret = v4l2_subdev_init_finalize(&csi2->sd); - if (ret) - goto err_entity_cleanup; - - ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd); - if (ret) { - csi2_err("Failed register csi2 subdev (%d)\n", ret); - goto err_subdev_cleanup; - } - - return 0; - -err_subdev_cleanup: - v4l2_subdev_cleanup(&csi2->sd); -err_entity_cleanup: - media_entity_cleanup(&csi2->sd.entity); - - return ret; -} - -void csi2_uninit(struct csi2_device *csi2) -{ - v4l2_device_unregister_subdev(&csi2->sd); - v4l2_subdev_cleanup(&csi2->sd); - media_entity_cleanup(&csi2->sd.entity); -} diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h deleted file mode 100644 index 4fff16ee940788..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * RP1 CSI-2 driver. - * Copyright (c) 2021 Raspberry Pi Ltd. - * - */ -#ifndef _RP1_CSI2_ -#define _RP1_CSI2_ - -#include -#include -#include -#include -#include - -#include "dphy.h" - -#define CSI2_NUM_CHANNELS 4 - -#define DISCARDS_TABLE_NUM_VCS 4 - -enum csi2_mode { - CSI2_MODE_NORMAL = 0, - CSI2_MODE_REMAP = 1, - CSI2_MODE_COMPRESSED = 2, - CSI2_MODE_FE_STREAMING = 3, -}; - -enum csi2_compression_mode { - CSI2_COMPRESSION_DELTA = 1, - CSI2_COMPRESSION_SIMPLE = 2, - CSI2_COMPRESSION_COMBINED = 3, -}; - -struct csi2_cfg { - u16 width; - u16 height; - u32 stride; - u32 buffer_size; -}; - -enum discards_table_index { - DISCARDS_TABLE_OVERFLOW = 0, - DISCARDS_TABLE_LENGTH_LIMIT, - DISCARDS_TABLE_UNMATCHED, - DISCARDS_TABLE_INACTIVE, - DISCARDS_TABLE_NUM_ENTRIES, -}; - -struct csi2_device { - /* Parent V4l2 device */ - struct v4l2_device *v4l2_dev; - - void __iomem *base; - - struct dphy_data dphy; - - enum v4l2_mbus_type bus_type; - unsigned int bus_flags; - bool multipacket_line; - unsigned int num_lines[CSI2_NUM_CHANNELS]; - - struct media_pad pad[CSI2_NUM_CHANNELS * 2]; - struct v4l2_subdev sd; - - /* lock for csi2 errors counters */ - spinlock_t errors_lock; - u32 overflows; - u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; - u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; -}; - -void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof); -void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, - dma_addr_t dmaaddr, unsigned int stride, - unsigned int size); -void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, - enum csi2_compression_mode mode, unsigned int shift, - unsigned int offset); -void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, - enum csi2_mode mode, bool auto_arm, - bool pack_bytes, unsigned int width, - unsigned int height); -void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel); -void csi2_open_rx(struct csi2_device *csi2); -void csi2_close_rx(struct csi2_device *csi2); -int csi2_init(struct csi2_device *csi2, struct dentry *debugfs); -void csi2_uninit(struct csi2_device *csi2); - -#endif diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c deleted file mode 100644 index 28ea3215fff5c1..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * RP1 CSI-2 Driver - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ - -#include -#include -#include - -#include "dphy.h" - -#define dphy_dbg(fmt, arg...) dev_dbg(dphy->dev, fmt, ##arg) -#define dphy_info(fmt, arg...) dev_info(dphy->dev, fmt, ##arg) -#define dphy_err(fmt, arg...) dev_err(dphy->dev, fmt, ##arg) - -/* DW dphy Host registers */ -#define VERSION 0x000 -#define N_LANES 0x004 -#define RESETN 0x008 -#define PHY_SHUTDOWNZ 0x040 -#define PHY_RSTZ 0x044 -#define PHY_RX 0x048 -#define PHY_STOPSTATE 0x04c -#define PHY_TST_CTRL0 0x050 -#define PHY_TST_CTRL1 0x054 -#define PHY2_TST_CTRL0 0x058 -#define PHY2_TST_CTRL1 0x05c - -/* DW dphy Host Transactions */ -#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44 -#define DPHY_PLL_INPUT_DIV_OFFSET 0x17 -#define DPHY_PLL_LOOP_DIV_OFFSET 0x18 -#define DPHY_PLL_DIV_CTRL_OFFSET 0x19 - -static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset) -{ - return readl(dphy->base + offset); -} - -static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data) -{ - writel(data, dphy->base + offset); -} - -static void set_tstclr(struct dphy_data *dphy, u32 val) -{ - u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0); - - dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~1) | val); -} - -static void set_tstclk(struct dphy_data *dphy, u32 val) -{ - u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0); - - dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1)); -} - -static uint8_t get_tstdout(struct dphy_data *dphy) -{ - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); - - return ((ctrl1 >> 8) & 0xff); -} - -static void set_testen(struct dphy_data *dphy, u32 val) -{ - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); - - dw_csi2_host_write(dphy, PHY_TST_CTRL1, - (ctrl1 & ~(1 << 16)) | (val << 16)); -} - -static void set_testdin(struct dphy_data *dphy, u32 val) -{ - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); - - dw_csi2_host_write(dphy, PHY_TST_CTRL1, (ctrl1 & ~0xff) | val); -} - -static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code, - uint8_t test_data) -{ - /* See page 101 of the MIPI DPHY databook. */ - set_tstclk(dphy, 1); - set_testen(dphy, 0); - set_testdin(dphy, test_code); - set_testen(dphy, 1); - set_tstclk(dphy, 0); - set_testen(dphy, 0); - set_testdin(dphy, test_data); - set_tstclk(dphy, 1); - return get_tstdout(dphy); -} - -static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps) -{ - /* See Table 5-1 on page 65 of dphy databook */ - static const u16 hsfreqrange_table[][2] = { - { 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 }, - { 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 }, - { 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 }, - { 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 }, - { 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 }, - { 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 }, - { 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 }, - { 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 }, - { 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 }, - { 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 }, - { 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 }, - { 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 }, - { 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 }, - }; - unsigned int i; - - if (mbps < 80 || mbps > 1500) - dphy_err("DPHY: Datarate %u Mbps out of range\n", mbps); - - for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) { - if (mbps <= hsfreqrange_table[i][0]) - break; - } - - dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET, - hsfreqrange_table[i][1] << 1); -} - -static void dphy_init(struct dphy_data *dphy) -{ - dw_csi2_host_write(dphy, PHY_RSTZ, 0); - dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 0); - set_tstclk(dphy, 1); - set_testen(dphy, 0); - set_tstclr(dphy, 1); - usleep_range(15, 20); - set_tstclr(dphy, 0); - usleep_range(15, 20); - - dphy_set_hsfreqrange(dphy, dphy->dphy_rate); - - usleep_range(5, 10); - dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1); - usleep_range(5, 10); - dw_csi2_host_write(dphy, PHY_RSTZ, 1); -} - -void dphy_start(struct dphy_data *dphy) -{ - dw_csi2_host_write(dphy, N_LANES, (dphy->active_lanes - 1)); - dphy_init(dphy); - dw_csi2_host_write(dphy, RESETN, 0xffffffff); - usleep_range(10, 50); -} - -void dphy_stop(struct dphy_data *dphy) -{ - /* Set only one lane (lane 0) as active (ON) */ - dw_csi2_host_write(dphy, N_LANES, 0); - dw_csi2_host_write(dphy, RESETN, 0); -} - -void dphy_probe(struct dphy_data *dphy) -{ - u32 host_ver; - u8 host_ver_major, host_ver_minor; - - host_ver = dw_csi2_host_read(dphy, VERSION); - host_ver_major = (u8)((host_ver >> 24) - '0'); - host_ver_minor = (u8)((host_ver >> 16) - '0'); - host_ver_minor = host_ver_minor * 10; - host_ver_minor += (u8)((host_ver >> 8) - '0'); - - dphy_info("DW dphy Host HW v%u.%u\n", host_ver_major, host_ver_minor); -} diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h deleted file mode 100644 index 9d7a80b3f68402..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2021 Raspberry Pi Ltd. - * - */ - -#ifndef _RP1_DPHY_ -#define _RP1_DPHY_ - -#include -#include - -struct dphy_data { - struct device *dev; - - void __iomem *base; - - u32 dphy_rate; - u32 max_lanes; - u32 active_lanes; -}; - -void dphy_probe(struct dphy_data *dphy); -void dphy_start(struct dphy_data *dphy); -void dphy_stop(struct dphy_data *dphy); - -#endif diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h deleted file mode 100644 index e91aa2ed365919..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP common definitions. - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_COMMON_H_ -#define _PISP_COMMON_H_ - -#include "pisp_types.h" - -struct pisp_bla_config { - u16 black_level_r; - u16 black_level_gr; - u16 black_level_gb; - u16 black_level_b; - u16 output_black_level; - u8 pad[2]; -}; - -struct pisp_wbg_config { - u16 gain_r; - u16 gain_g; - u16 gain_b; - u8 pad[2]; -}; - -struct pisp_compress_config { - /* value subtracted from incoming data */ - u16 offset; - u8 pad; - /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ - u8 mode; -}; - -struct pisp_decompress_config { - /* value added to reconstructed data */ - u16 offset; - u8 pad; - /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ - u8 mode; -}; - -enum pisp_axi_flags { - /* - * round down bursts to end at a 32-byte boundary, to align following - * bursts - */ - PISP_AXI_FLAG_ALIGN = 128, - /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */ - PISP_AXI_FLAG_PAD = 64, - /* for FE writer: Use Output FIFO level to trigger "panic" */ - PISP_AXI_FLAG_PANIC = 32, -}; - -struct pisp_axi_config { - /* - * burst length minus one, which must be in the range 0:15; OR'd with - * flags - */ - u8 maxlen_flags; - /* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */ - u8 cache_prot; - /* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */ - u16 qos; -}; - -#endif /* _PISP_COMMON_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c deleted file mode 100644 index 2878dea1750ea5..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c +++ /dev/null @@ -1,570 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PiSP Front End driver. - * Copyright (c) 2021 Raspberry Pi Ltd. - * - */ - -#include -#include -#include -#include -#include - -#include - -#include "pisp_fe.h" -#include "cfe.h" - -#define FE_VERSION 0x000 -#define FE_CONTROL 0x004 -#define FE_STATUS 0x008 -#define FE_FRAME_STATUS 0x00c -#define FE_ERROR_STATUS 0x010 -#define FE_OUTPUT_STATUS 0x014 -#define FE_INT_EN 0x018 -#define FE_INT_STATUS 0x01c - -/* CONTROL */ -#define FE_CONTROL_QUEUE BIT(0) -#define FE_CONTROL_ABORT BIT(1) -#define FE_CONTROL_RESET BIT(2) -#define FE_CONTROL_LATCH_REGS BIT(3) - -/* INT_EN / INT_STATUS */ -#define FE_INT_EOF BIT(0) -#define FE_INT_SOF BIT(1) -#define FE_INT_LINES0 BIT(8) -#define FE_INT_LINES1 BIT(9) -#define FE_INT_STATS BIT(16) -#define FE_INT_QREADY BIT(24) - -/* STATUS */ -#define FE_STATUS_QUEUED BIT(0) -#define FE_STATUS_WAITING BIT(1) -#define FE_STATUS_ACTIVE BIT(2) - -#define PISP_FE_CONFIG_BASE_OFFSET 0x0040 - -#define PISP_FE_ENABLE_STATS_CLUSTER \ - (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \ - PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \ - PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \ - PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS) - -#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \ - ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \ - PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i))) - -struct pisp_fe_config_param { - u32 dirty_flags; - u32 dirty_flags_extra; - size_t offset; - size_t size; -}; - -static const struct pisp_fe_config_param pisp_fe_config_map[] = { - /* *_dirty_flag_extra types */ - { 0, PISP_FE_DIRTY_GLOBAL, offsetof(struct pisp_fe_config, global), - sizeof(struct pisp_fe_global_config) }, - { 0, PISP_FE_DIRTY_FLOATING, offsetof(struct pisp_fe_config, floating_stats), - sizeof(struct pisp_fe_floating_stats_config) }, - { 0, PISP_FE_DIRTY_OUTPUT_AXI, offsetof(struct pisp_fe_config, output_axi), - sizeof(struct pisp_fe_output_axi_config) }, - /* *_dirty_flag types */ - { PISP_FE_ENABLE_INPUT, 0, offsetof(struct pisp_fe_config, input), - sizeof(struct pisp_fe_input_config) }, - { PISP_FE_ENABLE_DECOMPRESS, 0, offsetof(struct pisp_fe_config, decompress), - sizeof(struct pisp_decompress_config) }, - { PISP_FE_ENABLE_DECOMPAND, 0, offsetof(struct pisp_fe_config, decompand), - sizeof(struct pisp_fe_decompand_config) }, - { PISP_FE_ENABLE_BLA, 0, offsetof(struct pisp_fe_config, bla), - sizeof(struct pisp_bla_config) }, - { PISP_FE_ENABLE_DPC, 0, offsetof(struct pisp_fe_config, dpc), - sizeof(struct pisp_fe_dpc_config) }, - { PISP_FE_ENABLE_STATS_CROP, 0, offsetof(struct pisp_fe_config, stats_crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_BLC, 0, offsetof(struct pisp_fe_config, blc), - sizeof(struct pisp_bla_config) }, - { PISP_FE_ENABLE_CDAF_STATS, 0, offsetof(struct pisp_fe_config, cdaf_stats), - sizeof(struct pisp_fe_cdaf_stats_config) }, - { PISP_FE_ENABLE_AWB_STATS, 0, offsetof(struct pisp_fe_config, awb_stats), - sizeof(struct pisp_fe_awb_stats_config) }, - { PISP_FE_ENABLE_RGBY, 0, offsetof(struct pisp_fe_config, rgby), - sizeof(struct pisp_fe_rgby_config) }, - { PISP_FE_ENABLE_LSC, 0, offsetof(struct pisp_fe_config, lsc), - sizeof(struct pisp_fe_lsc_config) }, - { PISP_FE_ENABLE_AGC_STATS, 0, offsetof(struct pisp_fe_config, agc_stats), - sizeof(struct pisp_agc_statistics) }, - { PISP_FE_ENABLE_CROP0, 0, offsetof(struct pisp_fe_config, ch[0].crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_DOWNSCALE0, 0, offsetof(struct pisp_fe_config, ch[0].downscale), - sizeof(struct pisp_fe_downscale_config) }, - { PISP_FE_ENABLE_COMPRESS0, 0, offsetof(struct pisp_fe_config, ch[0].compress), - sizeof(struct pisp_compress_config) }, - { PISP_FE_ENABLE_OUTPUT0, 0, offsetof(struct pisp_fe_config, ch[0].output), - sizeof(struct pisp_fe_output_config) }, - { PISP_FE_ENABLE_CROP1, 0, offsetof(struct pisp_fe_config, ch[1].crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_DOWNSCALE1, 0, offsetof(struct pisp_fe_config, ch[1].downscale), - sizeof(struct pisp_fe_downscale_config) }, - { PISP_FE_ENABLE_COMPRESS1, 0, offsetof(struct pisp_fe_config, ch[1].compress), - sizeof(struct pisp_compress_config) }, - { PISP_FE_ENABLE_OUTPUT1, 0, offsetof(struct pisp_fe_config, ch[1].output), - sizeof(struct pisp_fe_output_config) }, -}; - -#define pisp_fe_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \ - } while (0) -#define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg) -#define pisp_fe_info(fmt, arg...) dev_info(fe->v4l2_dev->dev, fmt, ##arg) -#define pisp_fe_err(fmt, arg...) dev_err(fe->v4l2_dev->dev, fmt, ##arg) - -static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset) -{ - return readl(fe->base + offset); -} - -static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset, - u32 val) -{ - writel(val, fe->base + offset); - pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset); -} - -static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset, - u32 val) -{ - writel_relaxed(val, fe->base + offset); - pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset); -} - -static int pisp_regs_show(struct seq_file *s, void *data) -{ - struct pisp_fe_device *fe = s->private; - int ret; - - ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev); - if (ret) - return ret; - - pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); - -#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg)) - DUMP(FE_VERSION); - DUMP(FE_CONTROL); - DUMP(FE_STATUS); - DUMP(FE_FRAME_STATUS); - DUMP(FE_ERROR_STATUS); - DUMP(FE_OUTPUT_STATUS); - DUMP(FE_INT_EN); - DUMP(FE_INT_STATUS); -#undef DUMP - - pm_runtime_put(fe->v4l2_dev->dev); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(pisp_regs); - -static void pisp_config_write(struct pisp_fe_device *fe, - struct pisp_fe_config *config, - unsigned int start_offset, - unsigned int size) -{ - const unsigned int max_offset = - offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]); - unsigned int i, end_offset; - u32 *cfg = (u32 *)config; - - start_offset = min(start_offset, max_offset); - end_offset = min(start_offset + size, max_offset); - - cfg += start_offset >> 2; - for (i = start_offset; i < end_offset; i += 4, cfg++) - pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i, - *cfg); -} - -void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) -{ - u32 status, int_status, out_status, frame_status, error_status; - unsigned int i; - - pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); - status = pisp_fe_reg_read(fe, FE_STATUS); - out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS); - frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS); - error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS); - - int_status = pisp_fe_reg_read(fe, FE_INT_STATUS); - pisp_fe_reg_write(fe, FE_INT_STATUS, int_status); - - pisp_fe_dbg_verbose("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n", - __func__, status, out_status, frame_status, error_status, - int_status); - - /* We do not report interrupts for the input/stream pad. */ - for (i = 0; i < FE_NUM_PADS - 1; i++) { - sof[i] = !!(int_status & FE_INT_SOF); - eof[i] = !!(int_status & FE_INT_EOF); - } -} - -static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, - unsigned int c, struct v4l2_format const *f) -{ - unsigned int wbytes; - - wbytes = cfg->ch[c].output.format.width; - if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK) - wbytes *= 2; - - /* Check output image dimensions are nonzero and not too big */ - if (cfg->ch[c].output.format.width < 2 || - cfg->ch[c].output.format.height < 2 || - cfg->ch[c].output.format.height > f->fmt.pix.height || - cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline || - wbytes > f->fmt.pix.bytesperline) - return false; - - /* Check for zero-sized crops, which could cause lockup */ - if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && - ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) || - cfg->ch[c].crop.offset_y >= cfg->input.format.height || - cfg->ch[c].crop.width < 2 || - cfg->ch[c].crop.height < 2))) - return false; - - if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) && - (cfg->ch[c].downscale.output_width < 2 || - cfg->ch[c].downscale.output_height < 2)) - return false; - - return true; -} - -static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg) -{ - /* Check for zero-sized crop, which could cause lockup */ - return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) || - (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) && - cfg->stats_crop.offset_y < cfg->input.format.height && - cfg->stats_crop.width >= 2 && - cfg->stats_crop.height >= 2)); -} - -int pisp_fe_validate_config(struct pisp_fe_device *fe, - struct pisp_fe_config *cfg, - struct v4l2_format const *f0, - struct v4l2_format const *f1) -{ - unsigned int i; - - /* - * Check the input is enabled, streaming and has nonzero size; - * to avoid cases where the hardware might lock up or try to - * read inputs from memory (which this driver doesn't support). - */ - if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) || - cfg->input.streaming != 1 || cfg->input.format.width < 2 || - cfg->input.format.height < 2) { - pisp_fe_err("%s: Input config not valid", __func__); - return -EINVAL; - } - - for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { - if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) { - if (cfg->global.enables & - PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) { - pisp_fe_err("%s: Output %u not valid", - __func__, i); - return -EINVAL; - } - continue; - } - - if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0)) - return -EINVAL; - } - - if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) && - !pisp_fe_validate_stats(cfg)) { - pisp_fe_err("%s: Stats config not valid", __func__); - return -EINVAL; - } - - return 0; -} - -void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, - struct pisp_fe_config *cfg) -{ - unsigned int i; - u64 addr; - u32 status; - - /* - * Check output buffers exist and outputs are correctly configured. - * If valid, set the buffer's DMA address; otherwise disable. - */ - for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { - struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i]; - - if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) - continue; - - addr = vb2_dma_contig_plane_dma_addr(buf, 0); - cfg->output_buffer[i].addr_lo = addr & 0xffffffff; - cfg->output_buffer[i].addr_hi = addr >> 32; - } - - if (vb2_bufs[FE_STATS_PAD]) { - addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0); - cfg->stats_buffer.addr_lo = addr & 0xffffffff; - cfg->stats_buffer.addr_hi = addr >> 32; - } - - /* Set up ILINES interrupts 3/4 of the way down each output */ - cfg->ch[0].output.ilines = - max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2); - cfg->ch[1].output.ilines = - max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2); - - /* - * The hardware must have consumed the previous config by now. - * This read of status also serves as a memory barrier before the - * sequence of relaxed writes which follow. - */ - status = pisp_fe_reg_read(fe, FE_STATUS); - pisp_fe_dbg_verbose("%s: status = 0x%x\n", __func__, status); - if (WARN_ON(status & FE_STATUS_QUEUED)) - return; - - /* - * Unconditionally write buffers, global and input parameters. - * Write cropping and output parameters whenever they are enabled. - * Selectively write other parameters that have been marked as - * changed through the dirty flags. - */ - pisp_config_write(fe, cfg, 0, - offsetof(struct pisp_fe_config, decompress)); - cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL; - cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT; - cfg->dirty_flags |= (cfg->global.enables & - (PISP_FE_ENABLE_STATS_CROP | - PISP_FE_ENABLE_OUTPUT_CLUSTER(0) | - PISP_FE_ENABLE_OUTPUT_CLUSTER(1))); - for (i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { - const struct pisp_fe_config_param *p = &pisp_fe_config_map[i]; - - if (cfg->dirty_flags & p->dirty_flags || - cfg->dirty_flags_extra & p->dirty_flags_extra) - pisp_config_write(fe, cfg, p->offset, p->size); - } - - /* This final non-relaxed write serves as a memory barrier */ - pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE); -} - -void pisp_fe_start(struct pisp_fe_device *fe) -{ - pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET); - pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); - pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1); - fe->inframe_count = 0; -} - -void pisp_fe_stop(struct pisp_fe_device *fe) -{ - pisp_fe_reg_write(fe, FE_INT_EN, 0); - pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT); - usleep_range(1000, 2000); - WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); - pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); -} - -static int pisp_fe_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) -{ - struct v4l2_mbus_framefmt *fmt; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); - *fmt = cfe_default_format; - fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_CONFIG_PAD); - *fmt = cfe_default_meta_format; - fmt->code = MEDIA_BUS_FMT_FIXED; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD); - *fmt = cfe_default_format; - fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD); - *fmt = cfe_default_format; - fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD); - *fmt = cfe_default_meta_format; - fmt->code = MEDIA_BUS_FMT_FIXED; - - return 0; -} - -static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt; - const struct cfe_fmt *cfe_fmt; - - /* TODO: format propagation to source pads */ - /* TODO: format validation */ - - switch (format->pad) { - case FE_STREAM_PAD: - cfe_fmt = find_format_by_code(format->format.code); - if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) - cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); - - format->format.code = cfe_fmt->code; - format->format.field = V4L2_FIELD_NONE; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); - *fmt = format->format; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD); - *fmt = format->format; - - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD); - *fmt = format->format; - - return 0; - - case FE_OUTPUT0_PAD: - case FE_OUTPUT1_PAD: { - /* - * TODO: we should allow scaling and cropping by allowing the - * user to set the size here. - */ - struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; - u32 sink_code; - u32 code; - - sink_fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); - if (!sink_fmt) - return -EINVAL; - - source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - if (!source_fmt) - return -EINVAL; - - sink_code = sink_fmt->code; - code = format->format.code; - - /* - * If the source code from the user does not match the code in - * the sink pad, check that the source code matches the - * compressed version of the sink code. - */ - - if (code != sink_code && - code == cfe_find_compressed_code(sink_code)) - source_fmt->code = code; - - return 0; - } - - case FE_CONFIG_PAD: - case FE_STATS_PAD: - default: - return v4l2_subdev_get_fmt(sd, state, format); - } -} - -static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = pisp_fe_pad_set_fmt, - .link_validate = v4l2_subdev_link_validate_default, -}; - -static const struct media_entity_operations pisp_fe_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { - .pad = &pisp_fe_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { - .init_state = &pisp_fe_init_state, - -}; - -int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) -{ - int ret; - - debugfs_create_file("pisp_regs", 0444, debugfs, fe, &pisp_regs_fops); - - fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION); - pisp_fe_info("PiSP FE HW v%u.%u\n", - (fe->hw_revision >> 24) & 0xff, - (fe->hw_revision >> 20) & 0x0f); - - fe->pad[FE_STREAM_PAD].flags = - MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; - fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK; - fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE; - fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE; - fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad), - fe->pad); - if (ret) - return ret; - - /* Initialize subdev */ - v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); - fe->sd.internal_ops = &pisp_fe_internal_ops; - fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; - fe->sd.entity.ops = &pisp_fe_entity_ops; - fe->sd.entity.name = "pisp-fe"; - fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - fe->sd.owner = THIS_MODULE; - snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe"); - - ret = v4l2_subdev_init_finalize(&fe->sd); - if (ret) - goto err_entity_cleanup; - - ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd); - if (ret) { - pisp_fe_err("Failed register pisp fe subdev (%d)\n", ret); - goto err_subdev_cleanup; - } - - /* Must be in IDLE state (STATUS == 0) here. */ - WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); - - return 0; - -err_subdev_cleanup: - v4l2_subdev_cleanup(&fe->sd); -err_entity_cleanup: - media_entity_cleanup(&fe->sd.entity); - - return ret; -} - -void pisp_fe_uninit(struct pisp_fe_device *fe) -{ - v4l2_device_unregister_subdev(&fe->sd); - v4l2_subdev_cleanup(&fe->sd); - media_entity_cleanup(&fe->sd.entity); -} diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h deleted file mode 100644 index 12760cb55af422..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * PiSP Front End driver. - * Copyright (c) 2021 Raspberry Pi Ltd. - * - */ -#ifndef _PISP_FE_H_ -#define _PISP_FE_H_ - -#include -#include -#include -#include - -#include -#include -#include - -#include "pisp_fe_config.h" - -enum pisp_fe_pads { - FE_STREAM_PAD, - FE_CONFIG_PAD, - FE_OUTPUT0_PAD, - FE_OUTPUT1_PAD, - FE_STATS_PAD, - FE_NUM_PADS -}; - -struct pisp_fe_device { - /* Parent V4l2 device */ - struct v4l2_device *v4l2_dev; - void __iomem *base; - u32 hw_revision; - - u16 inframe_count; - struct media_pad pad[FE_NUM_PADS]; - struct v4l2_subdev sd; -}; - -void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof); -int pisp_fe_validate_config(struct pisp_fe_device *fe, - struct pisp_fe_config *cfg, - struct v4l2_format const *f0, - struct v4l2_format const *f1); -void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, - struct pisp_fe_config *cfg); -void pisp_fe_start(struct pisp_fe_device *fe); -void pisp_fe_stop(struct pisp_fe_device *fe); -int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs); -void pisp_fe_uninit(struct pisp_fe_device *fe); - -#endif diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h deleted file mode 100644 index 4b76f640894b72..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h +++ /dev/null @@ -1,272 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP Front End Driver Configuration structures - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_FE_CONFIG_ -#define _PISP_FE_CONFIG_ - -#include - -#include "pisp_statistics.h" - -#define PISP_FE_NUM_OUTPUTS 2 - -enum pisp_fe_enable { - PISP_FE_ENABLE_INPUT = 0x000001, - PISP_FE_ENABLE_DECOMPRESS = 0x000002, - PISP_FE_ENABLE_DECOMPAND = 0x000004, - PISP_FE_ENABLE_BLA = 0x000008, - PISP_FE_ENABLE_DPC = 0x000010, - PISP_FE_ENABLE_STATS_CROP = 0x000020, - PISP_FE_ENABLE_DECIMATE = 0x000040, - PISP_FE_ENABLE_BLC = 0x000080, - PISP_FE_ENABLE_CDAF_STATS = 0x000100, - PISP_FE_ENABLE_AWB_STATS = 0x000200, - PISP_FE_ENABLE_RGBY = 0x000400, - PISP_FE_ENABLE_LSC = 0x000800, - PISP_FE_ENABLE_AGC_STATS = 0x001000, - PISP_FE_ENABLE_CROP0 = 0x010000, - PISP_FE_ENABLE_DOWNSCALE0 = 0x020000, - PISP_FE_ENABLE_COMPRESS0 = 0x040000, - PISP_FE_ENABLE_OUTPUT0 = 0x080000, - PISP_FE_ENABLE_CROP1 = 0x100000, - PISP_FE_ENABLE_DOWNSCALE1 = 0x200000, - PISP_FE_ENABLE_COMPRESS1 = 0x400000, - PISP_FE_ENABLE_OUTPUT1 = 0x800000 -}; - -#define PISP_FE_ENABLE_CROP(i) (PISP_FE_ENABLE_CROP0 << (4 * (i))) -#define PISP_FE_ENABLE_DOWNSCALE(i) (PISP_FE_ENABLE_DOWNSCALE0 << (4 * (i))) -#define PISP_FE_ENABLE_COMPRESS(i) (PISP_FE_ENABLE_COMPRESS0 << (4 * (i))) -#define PISP_FE_ENABLE_OUTPUT(i) (PISP_FE_ENABLE_OUTPUT0 << (4 * (i))) - -/* - * We use the enable flags to show when blocks are "dirty", but we need some - * extra ones too. - */ -enum pisp_fe_dirty { - PISP_FE_DIRTY_GLOBAL = 0x0001, - PISP_FE_DIRTY_FLOATING = 0x0002, - PISP_FE_DIRTY_OUTPUT_AXI = 0x0004 -}; - -struct pisp_fe_global_config { - u32 enables; - u8 bayer_order; - u8 pad[3]; -}; - -struct pisp_fe_input_axi_config { - /* burst length minus one, in the range 0..15; OR'd with flags */ - u8 maxlen_flags; - /* { prot[2:0], cache[3:0] } fields */ - u8 cache_prot; - /* QoS (only 4 LS bits are used) */ - u16 qos; -}; - -struct pisp_fe_output_axi_config { - /* burst length minus one, in the range 0..15; OR'd with flags */ - u8 maxlen_flags; - /* { prot[2:0], cache[3:0] } fields */ - u8 cache_prot; - /* QoS (4 bitfields of 4 bits each for different panic levels) */ - u16 qos; - /* For Panic mode: Output FIFO panic threshold */ - u16 thresh; - /* For Panic mode: Output FIFO statistics throttle threshold */ - u16 throttle; -}; - -struct pisp_fe_input_config { - u8 streaming; - u8 pad[3]; - struct pisp_image_format_config format; - struct pisp_fe_input_axi_config axi; - /* Extra cycles delay before issuing each burst request */ - u8 holdoff; - u8 pad2[3]; -}; - -struct pisp_fe_output_config { - struct pisp_image_format_config format; - u16 ilines; - u8 pad[2]; -}; - -struct pisp_fe_input_buffer_config { - u32 addr_lo; - u32 addr_hi; - u16 frame_id; - u16 pad; -}; - -#define PISP_FE_DECOMPAND_LUT_SIZE 65 - -struct pisp_fe_decompand_config { - u16 lut[PISP_FE_DECOMPAND_LUT_SIZE]; - u16 pad; -}; - -struct pisp_fe_dpc_config { - u8 coeff_level; - u8 coeff_range; - u8 coeff_range2; -#define PISP_FE_DPC_FLAG_FOLDBACK 1 -#define PISP_FE_DPC_FLAG_VFLAG 2 - u8 flags; -}; - -#define PISP_FE_LSC_LUT_SIZE 16 - -struct pisp_fe_lsc_config { - u8 shift; - u8 pad0; - u16 scale; - u16 centre_x; - u16 centre_y; - u16 lut[PISP_FE_LSC_LUT_SIZE]; -}; - -struct pisp_fe_rgby_config { - u16 gain_r; - u16 gain_g; - u16 gain_b; - u8 maxflag; - u8 pad; -}; - -struct pisp_fe_agc_stats_config { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; - /* each weight only 4 bits */ - u8 weights[PISP_AGC_STATS_NUM_ZONES / 2]; - u16 row_offset_x; - u16 row_offset_y; - u16 row_size_x; - u16 row_size_y; - u8 row_shift; - u8 float_shift; - u8 pad1[2]; -}; - -struct pisp_fe_awb_stats_config { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; - u8 shift; - u8 pad[3]; - u16 r_lo; - u16 r_hi; - u16 g_lo; - u16 g_hi; - u16 b_lo; - u16 b_hi; -}; - -struct pisp_fe_floating_stats_region { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; -}; - -struct pisp_fe_floating_stats_config { - struct pisp_fe_floating_stats_region - regions[PISP_FLOATING_STATS_NUM_ZONES]; -}; - -#define PISP_FE_CDAF_NUM_WEIGHTS 8 - -struct pisp_fe_cdaf_stats_config { - u16 noise_constant; - u16 noise_slope; - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; - u16 skip_x; - u16 skip_y; - u32 mode; -}; - -struct pisp_fe_stats_buffer_config { - u32 addr_lo; - u32 addr_hi; -}; - -struct pisp_fe_crop_config { - u16 offset_x; - u16 offset_y; - u16 width; - u16 height; -}; - -enum pisp_fe_downscale_flags { - DOWNSCALE_BAYER = - 1, /* downscale the four Bayer components independently... */ - DOWNSCALE_BIN = - 2 /* ...without trying to preserve their spatial relationship */ -}; - -struct pisp_fe_downscale_config { - u8 xin; - u8 xout; - u8 yin; - u8 yout; - u8 flags; /* enum pisp_fe_downscale_flags */ - u8 pad[3]; - u16 output_width; - u16 output_height; -}; - -struct pisp_fe_output_buffer_config { - u32 addr_lo; - u32 addr_hi; -}; - -/* Each of the two output channels/branches: */ -struct pisp_fe_output_branch_config { - struct pisp_fe_crop_config crop; - struct pisp_fe_downscale_config downscale; - struct pisp_compress_config compress; - struct pisp_fe_output_config output; - u32 pad; -}; - -/* And finally one to rule them all: */ -struct pisp_fe_config { - /* I/O configuration: */ - struct pisp_fe_stats_buffer_config stats_buffer; - struct pisp_fe_output_buffer_config output_buffer[PISP_FE_NUM_OUTPUTS]; - struct pisp_fe_input_buffer_config input_buffer; - /* processing configuration: */ - struct pisp_fe_global_config global; - struct pisp_fe_input_config input; - struct pisp_decompress_config decompress; - struct pisp_fe_decompand_config decompand; - struct pisp_bla_config bla; - struct pisp_fe_dpc_config dpc; - struct pisp_fe_crop_config stats_crop; - u32 spare1; /* placeholder for future decimate configuration */ - struct pisp_bla_config blc; - struct pisp_fe_rgby_config rgby; - struct pisp_fe_lsc_config lsc; - struct pisp_fe_agc_stats_config agc_stats; - struct pisp_fe_awb_stats_config awb_stats; - struct pisp_fe_cdaf_stats_config cdaf_stats; - struct pisp_fe_floating_stats_config floating_stats; - struct pisp_fe_output_axi_config output_axi; - struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS]; - /* non-register fields: */ - u32 dirty_flags; /* these use pisp_fe_enable */ - u32 dirty_flags_extra; /* these use pisp_fe_dirty */ -}; - -#endif /* _PISP_FE_CONFIG_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h deleted file mode 100644 index d8d7fcb57fecc0..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h +++ /dev/null @@ -1,62 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP Front End statistics definitions - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_FE_STATISTICS_H_ -#define _PISP_FE_STATISTICS_H_ - -#define PISP_FLOATING_STATS_NUM_ZONES 4 -#define PISP_AGC_STATS_NUM_BINS 1024 -#define PISP_AGC_STATS_SIZE 16 -#define PISP_AGC_STATS_NUM_ZONES (PISP_AGC_STATS_SIZE * PISP_AGC_STATS_SIZE) -#define PISP_AGC_STATS_NUM_ROW_SUMS 512 - -struct pisp_agc_statistics_zone { - u64 Y_sum; - u32 counted; - u32 pad; -}; - -struct pisp_agc_statistics { - u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS]; - /* - * 32-bits per bin means an image (just less than) 16384x16384 pixels - * in size can weight every pixel from 0 to 15. - */ - u32 histogram[PISP_AGC_STATS_NUM_BINS]; - struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; - -#define PISP_AWB_STATS_SIZE 32 -#define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE) - -struct pisp_awb_statistics_zone { - u32 R_sum; - u32 G_sum; - u32 B_sum; - u32 counted; -}; - -struct pisp_awb_statistics { - struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES]; - struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; - -#define PISP_CDAF_STATS_SIZE 8 -#define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE) - -struct pisp_cdaf_statistics { - u64 foms[PISP_CDAF_STATS_NUM_FOMS]; - u64 floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; - -struct pisp_statistics { - struct pisp_awb_statistics awb; - struct pisp_agc_statistics agc; - struct pisp_cdaf_statistics cdaf; -}; - -#endif /* _PISP_FE_STATISTICS_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h deleted file mode 100644 index 93d2d3c27a5626..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP Front End image definitions. - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_FE_TYPES_H_ -#define _PISP_FE_TYPES_H_ - -/* This definition must match the format description in the hardware exactly! */ -struct pisp_image_format_config { - /* size in pixels */ - u16 width, height; - /* must match struct pisp_image_format below */ - u32 format; - s32 stride; - /* some planar image formats will need a second stride */ - s32 stride2; -}; - -static_assert(sizeof(struct pisp_image_format_config) == 16); - -enum pisp_bayer_order { - /* - * Note how bayer_order&1 tells you if G is on the even pixels of the - * checkerboard or not, and bayer_order&2 tells you if R is on the even - * rows or is swapped with B. Note that if the top (of the 8) bits is - * set, this denotes a monochrome or greyscale image, and the lower bits - * should all be ignored. - */ - PISP_BAYER_ORDER_RGGB = 0, - PISP_BAYER_ORDER_GBRG = 1, - PISP_BAYER_ORDER_BGGR = 2, - PISP_BAYER_ORDER_GRBG = 3, - PISP_BAYER_ORDER_GREYSCALE = 128 -}; - -enum pisp_image_format { - /* - * Precise values are mostly tbd. Generally these will be portmanteau - * values comprising bit fields and flags. This format must be shared - * throughout the PiSP. - */ - PISP_IMAGE_FORMAT_BPS_8 = 0x00000000, - PISP_IMAGE_FORMAT_BPS_10 = 0x00000001, - PISP_IMAGE_FORMAT_BPS_12 = 0x00000002, - PISP_IMAGE_FORMAT_BPS_16 = 0x00000003, - PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003, - - PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000, - PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010, - PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020, - PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030, - - PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000, - PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100, - PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200, - PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300, - - PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000, - PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000, - - PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000, - PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000, - PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000, - PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000, - PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000, - PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000, - PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000, - PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000, - PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000, - PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000, - - PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000, - PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000, - - PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000, - PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000, - PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000, - PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000, - PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000, - - /* Lastly a few specific instantiations of the above. */ - PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16, - PISP_IMAGE_FORMAT_THREE_16 = - PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL -}; - -#define PISP_IMAGE_FORMAT_bps_8(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8) -#define PISP_IMAGE_FORMAT_bps_10(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10) -#define PISP_IMAGE_FORMAT_bps_12(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12) -#define PISP_IMAGE_FORMAT_bps_16(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16) -#define PISP_IMAGE_FORMAT_bps(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ? \ - 8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : \ - 8) -#define PISP_IMAGE_FORMAT_shift(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1) -#define PISP_IMAGE_FORMAT_three_channel(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL) -#define PISP_IMAGE_FORMAT_single_channel(fmt) \ - (!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)) -#define PISP_IMAGE_FORMAT_compressed(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \ - PISP_IMAGE_FORMAT_UNCOMPRESSED) -#define PISP_IMAGE_FORMAT_sampling_444(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_444) -#define PISP_IMAGE_FORMAT_sampling_422(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_422) -#define PISP_IMAGE_FORMAT_sampling_420(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_420) -#define PISP_IMAGE_FORMAT_order_normal(fmt) \ - (!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)) -#define PISP_IMAGE_FORMAT_order_swapped(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED) -#define PISP_IMAGE_FORMAT_interleaved(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED) -#define PISP_IMAGE_FORMAT_semiplanar(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR) -#define PISP_IMAGE_FORMAT_planar(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_PLANAR) -#define PISP_IMAGE_FORMAT_wallpaper(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL) -#define PISP_IMAGE_FORMAT_HOG(fmt) \ - ((fmt) & \ - (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED)) - -#define PISP_WALLPAPER_WIDTH 128 // in bytes - -#endif /* _PISP_FE_TYPES_H_ */ From 7b4f280efff703fa997970c1a7b4d133575c7154 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 3 Oct 2024 13:31:10 +0300 Subject: [PATCH 100/159] media: uapi: Add meta formats for PiSP FE config and stats Add two meta formats for PiSP FE: V4L2_META_FMT_RPI_FE_CFG and V4L2_META_FMT_RPI_FE_STATS. The former is used to provide configuration for the FE and the latter is used to read the statistics from the FE. Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 6390834c6f9b2c5e33f52f34579efa0d0df073db) Signed-off-by: Jacopo Mondi --- .../userspace-api/media/v4l/meta-formats.rst | 1 + .../media/v4l/metafmt-pisp-fe.rst | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 69e09857d55ea9..3e976816dcafeb 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -17,6 +17,7 @@ These formats are used for the :ref:`metadata` interface only. metafmt-generic metafmt-intel-ipu3 metafmt-pisp-be + metafmt-pisp-fe metafmt-rkisp1 metafmt-sensor-data metafmt-uvc diff --git a/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst new file mode 100644 index 00000000000000..fddeada83e4afb --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst @@ -0,0 +1,39 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _v4l2-meta-fmt-rpi-fe-cfg: + +************************ +V4L2_META_FMT_RPI_FE_CFG +************************ + +Raspberry Pi PiSP Front End configuration format +================================================ + +The Raspberry Pi PiSP Front End image signal processor is configured by +userspace by providing a buffer of configuration parameters to the +`rp1-cfe-fe-config` output video device node using the +:c:type:`v4l2_meta_format` interface. + +The `Raspberry Pi PiSP technical specification +`_ +provide detailed description of the Front End configuration and programming +model. + +.. _v4l2-meta-fmt-rpi-fe-stats: + +************************** +V4L2_META_FMT_RPI_FE_STATS +************************** + +Raspberry Pi PiSP Front End statistics format +============================================= + +The Raspberry Pi PiSP Front End image signal processor provides statistics data +by writing to a buffer provided via the `rp1-cfe-fe-stats` capture video device +node using the +:c:type:`v4l2_meta_format` interface. + +The `Raspberry Pi PiSP technical specification +`_ +provide detailed description of the Front End configuration and programming +model. From 3e1245db9a66af4da4afa68568774a325c6939c9 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 3 Oct 2024 13:31:11 +0300 Subject: [PATCH 101/159] dt-bindings: media: Add bindings for raspberrypi,rp1-cfe Add DT bindings for raspberrypi,rp1-cfe. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 1358bb523949e2dd28f2396eb59707151fb79479) Signed-off-by: Jacopo Mondi --- .../bindings/media/raspberrypi,rp1-cfe.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml diff --git a/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml new file mode 100644 index 00000000000000..eba5394719b906 --- /dev/null +++ b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/raspberrypi,rp1-cfe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi PiSP Camera Front End + +maintainers: + - Tomi Valkeinen + - Raspberry Pi Kernel Maintenance + +description: | + The Raspberry Pi PiSP Camera Front End is a module in Raspberrypi 5's RP1 I/O + controller, that contains: + - MIPI D-PHY + - MIPI CSI-2 receiver + - Simple image processor (called PiSP Front End, or FE) + + The FE documentation is available at: + https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf + + The PHY and CSI-2 receiver part have no public documentation. + +properties: + compatible: + items: + - const: raspberrypi,rp1-cfe + + reg: + items: + - description: CSI-2 registers + - description: D-PHY registers + - description: MIPI CFG (a simple top-level mux) registers + - description: FE registers + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: CSI-2 RX Port + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - data-lanes + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + rp1 { + #address-cells = <2>; + #size-cells = <2>; + + csi@110000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0xc0 0x40110000 0x0 0x100>, + <0xc0 0x40114000 0x0 0x100>, + <0xc0 0x40120000 0x0 0x100>, + <0xc0 0x40124000 0x0 0x1000>; + + interrupts = <42>; + + clocks = <&rp1_clocks>; + + port { + csi_ep: endpoint { + remote-endpoint = <&cam_endpoint>; + data-lanes = <1 2>; + }; + }; + }; + }; From 48a64429ef7477544c137b5ab20185707e6b5c7a Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 3 Oct 2024 13:31:12 +0300 Subject: [PATCH 102/159] media: raspberrypi: Add support for RP1-CFE Add support for Raspberry Pi CFE. The CFE is a hardware block that contains: - MIPI D-PHY - MIPI CSI-2 receiver - Front End ISP (FE) The driver has been upported from the Raspberry Pi kernel commit 88a681df9623 ("ARM: dts: bcm2712-rpi: Add i2c_pins labels"). Co-developed-by: Naushir Patuck Signed-off-by: Naushir Patuck Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 6edb685abb2af445773876a326292b989dcb3c9f) Signed-off-by: Jacopo Mondi --- MAINTAINERS | 7 + drivers/media/platform/raspberrypi/Kconfig | 1 + drivers/media/platform/raspberrypi/Makefile | 1 + .../platform/raspberrypi/rp1-cfe/Kconfig | 15 + .../platform/raspberrypi/rp1-cfe/Makefile | 6 + .../platform/raspberrypi/rp1-cfe/cfe-fmts.h | 332 +++ .../platform/raspberrypi/rp1-cfe/cfe-trace.h | 202 ++ .../media/platform/raspberrypi/rp1-cfe/cfe.c | 2504 +++++++++++++++++ .../media/platform/raspberrypi/rp1-cfe/cfe.h | 43 + .../media/platform/raspberrypi/rp1-cfe/csi2.c | 586 ++++ .../media/platform/raspberrypi/rp1-cfe/csi2.h | 89 + .../media/platform/raspberrypi/rp1-cfe/dphy.c | 181 ++ .../media/platform/raspberrypi/rp1-cfe/dphy.h | 27 + .../platform/raspberrypi/rp1-cfe/pisp-fe.c | 605 ++++ .../platform/raspberrypi/rp1-cfe/pisp-fe.h | 53 + .../linux/media/raspberrypi/pisp_fe_config.h | 273 ++ .../media/raspberrypi/pisp_fe_statistics.h | 64 + 17 files changed, 4989 insertions(+) create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/Kconfig create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/Makefile create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/cfe.c create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/cfe.h create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/csi2.c create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/csi2.h create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/dphy.c create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/dphy.h create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c create mode 100644 drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h create mode 100644 include/uapi/linux/media/raspberrypi/pisp_fe_config.h create mode 100644 include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h diff --git a/MAINTAINERS b/MAINTAINERS index 6a6e2941c4976a..4ce399893a3f86 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18044,6 +18044,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml F: include/uapi/linux/media/raspberrypi/ +RASPBERRY PI PISP CAMERA FRONT END +M: Tomi Valkeinen +M: Raspberry Pi Kernel Maintenance +S: Maintained +F: Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml +F: drivers/media/platform/raspberrypi/rp1-cfe/ + RC-CORE / LIRC FRAMEWORK M: Sean Young L: linux-media@vger.kernel.org diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig index e928f979019e65..bd5101ffefb5bc 100644 --- a/drivers/media/platform/raspberrypi/Kconfig +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -3,3 +3,4 @@ comment "Raspberry Pi media platform drivers" source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" +source "drivers/media/platform/raspberrypi/rp1-cfe/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile index c0d1a2dab4860c..af7fde84eefe23 100644 --- a/drivers/media/platform/raspberrypi/Makefile +++ b/drivers/media/platform/raspberrypi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += pisp_be/ +obj-y += rp1-cfe/ diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig new file mode 100644 index 00000000000000..327b61f1134bc9 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig @@ -0,0 +1,15 @@ +# RP1 V4L2 camera support + +config VIDEO_RP1_CFE + tristate "Raspberry Pi RP1 Camera Front End (CFE) video capture driver" + depends on VIDEO_DEV + depends on PM + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Say Y here to enable support for the Raspberry Pi RP1 Camera Front End. + + To compile this driver as a module, choose M here. The module will be + called rp1-cfe. diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Makefile b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile new file mode 100644 index 00000000000000..3f0d0fc8570ee7 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for RP1 Camera Front End driver +# +rp1-cfe-objs := cfe.o csi2.o pisp-fe.o dphy.o +obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h new file mode 100644 index 00000000000000..7aecf7f8373344 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 Camera Front End formats definition + * + * Copyright (C) 2021-2024 - Raspberry Pi Ltd. + */ +#ifndef _CFE_FMTS_H_ +#define _CFE_FMTS_H_ + +#include "cfe.h" +#include + +static const struct cfe_fmt formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, + { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB555, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB555, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .depth = 32, + .csi_dt = 0x0, + }, + + /* Bayer Formats */ + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + }, + /* PiSP Compressed Mode 1 */ + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + /* Greyscale format */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y10P, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y12P, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y14P, + .code = MEDIA_BUS_FMT_Y14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .code = MEDIA_BUS_FMT_Y16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO }, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, + .code = MEDIA_BUS_FMT_Y16_1X16, + .depth = 8, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + + /* Embedded data formats */ + { + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + + /* Frontend formats */ + { + .fourcc = V4L2_META_FMT_RPI_FE_CFG, + .code = MEDIA_BUS_FMT_FIXED, + .flags = CFE_FORMAT_FLAG_META_OUT, + }, + { + .fourcc = V4L2_META_FMT_RPI_FE_STATS, + .code = MEDIA_BUS_FMT_FIXED, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, +}; + +#endif /* _CFE_FMTS_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h new file mode 100644 index 00000000000000..1a36259f51b7bd --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * Copyright (c) 2024 Ideas on Board Oy + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cfe + +#if !defined(_CFE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CFE_TRACE_H + +#include +#include + +TRACE_EVENT(cfe_return_buffer, + TP_PROTO(u32 node_id, u32 buf_idx, u32 queue_id), + TP_ARGS(node_id, buf_idx, queue_id), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, queue_id) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf_idx; + __entry->queue_id = queue_id; + ), + TP_printk("node=%u buf=%u, queue=%u", __entry->node_id, + __entry->buf_idx, __entry->queue_id) +); + +DECLARE_EVENT_CLASS(cfe_buffer_template, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + ), + TP_printk("node=%u buf=%u", __entry->node_id, __entry->buf_idx) +); + +DEFINE_EVENT(cfe_buffer_template, cfe_buffer_prepare, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_queue, + TP_PROTO(u32 node_id, struct vb2_buffer *buf, bool schedule_now), + TP_ARGS(node_id, buf, schedule_now), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(bool, schedule_now) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + __entry->schedule_now = schedule_now; + ), + TP_printk("node=%u buf=%u%s", __entry->node_id, __entry->buf_idx, + __entry->schedule_now ? " schedule immediately" : "") +); + +DEFINE_EVENT(cfe_buffer_template, cfe_csi2_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +DEFINE_EVENT(cfe_buffer_template, cfe_fe_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_complete, + TP_PROTO(u32 node_id, struct vb2_v4l2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, seq) + __field(u64, ts) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->vb2_buf.index; + __entry->seq = buf->sequence; + __entry->ts = buf->vb2_buf.timestamp; + ), + TP_printk("node=%u buf=%u seq=%u ts=%llu", __entry->node_id, + __entry->buf_idx, __entry->seq, __entry->ts) +); + +TRACE_EVENT(cfe_frame_start, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_frame_end, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_prepare_next_job, + TP_PROTO(bool fe_enabled), + TP_ARGS(fe_enabled), + TP_STRUCT__entry( + __field(bool, fe_enabled) + ), + TP_fast_assign( + __entry->fe_enabled = fe_enabled; + ), + TP_printk("fe_enabled=%u", __entry->fe_enabled) +); + +/* These are copied from csi2.c */ +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) + +TRACE_EVENT(csi2_irq, + TP_PROTO(u32 channel, u32 status, u32 dbg), + TP_ARGS(channel, status, dbg), + TP_STRUCT__entry( + __field(u32, channel) + __field(u32, status) + __field(u32, dbg) + ), + TP_fast_assign( + __entry->channel = channel; + __entry->status = status; + __entry->dbg = dbg; + ), + TP_printk("ch=%u flags=[ %s%s%s%s%s] frame=%u line=%u\n", + __entry->channel, + (__entry->status & CSI2_STATUS_IRQ_FS(__entry->channel)) ? + "FS " : "", + (__entry->status & CSI2_STATUS_IRQ_FE(__entry->channel)) ? + "FE " : "", + (__entry->status & CSI2_STATUS_IRQ_FE_ACK(__entry->channel)) ? + "FE_ACK " : "", + (__entry->status & CSI2_STATUS_IRQ_LE(__entry->channel)) ? + "LE " : "", + (__entry->status & CSI2_STATUS_IRQ_LE_ACK(__entry->channel)) ? + "LE_ACK " : "", + __entry->dbg >> 16, __entry->dbg & 0xffff) +); + +TRACE_EVENT(fe_irq, + TP_PROTO(u32 status, u32 output_status, u32 frame_status, + u32 error_status, u32 int_status), + TP_ARGS(status, output_status, frame_status, error_status, int_status), + TP_STRUCT__entry( + __field(u32, status) + __field(u32, output_status) + __field(u32, frame_status) + __field(u32, error_status) + __field(u32, int_status) + ), + TP_fast_assign( + __entry->status = status; + __entry->output_status = output_status; + __entry->frame_status = frame_status; + __entry->error_status = error_status; + __entry->int_status = int_status; + ), + TP_printk("status 0x%x out_status 0x%x frame_status 0x%x error_status 0x%x int_status 0x%x", + __entry->status, + __entry->output_status, + __entry->frame_status, + __entry->error_status, + __entry->int_status) +); + +#endif /* _CFE_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE ../../drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace +#include diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c new file mode 100644 index 00000000000000..045910de6c5739 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -0,0 +1,2504 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 Camera Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cfe-fmts.h" +#include "cfe.h" +#include "csi2.h" +#include "pisp-fe.h" + +#define CREATE_TRACE_POINTS +#include "cfe-trace.h" + +#define CFE_MODULE_NAME "rp1-cfe" +#define CFE_VERSION "1.0" + +#define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg) + +/* MIPICFG registers */ +#define MIPICFG_CFG 0x004 +#define MIPICFG_INTR 0x028 +#define MIPICFG_INTE 0x02c +#define MIPICFG_INTF 0x030 +#define MIPICFG_INTS 0x034 + +#define MIPICFG_CFG_SEL_CSI BIT(0) + +#define MIPICFG_INT_CSI_DMA BIT(0) +#define MIPICFG_INT_CSI_HOST BIT(2) +#define MIPICFG_INT_PISP_FE BIT(4) + +#define BPL_ALIGNMENT 16 +#define MAX_BYTESPERLINE 0xffffff00 +#define MAX_BUFFER_SIZE 0xffffff00 +/* + * Max width is therefore determined by the max stride divided by the number of + * bits per pixel. + * + * However, to avoid overflow issues let's use a 16k maximum. This lets us + * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful + * review and adjustment of the code is needed so that it will deal with + * overflows correctly. + */ +#define MAX_WIDTH 16384 +#define MAX_HEIGHT MAX_WIDTH +/* Define a nominal minimum image size */ +#define MIN_WIDTH 16 +#define MIN_HEIGHT 16 + +#define MIN_META_WIDTH 4 +#define MIN_META_HEIGHT 1 + +const struct v4l2_mbus_framefmt cfe_default_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, +}; + +enum node_ids { + /* CSI2 HW output nodes first. */ + CSI2_CH0, + CSI2_CH1, + CSI2_CH2, + CSI2_CH3, + /* FE only nodes from here on. */ + FE_OUT0, + FE_OUT1, + FE_STATS, + FE_CONFIG, + NUM_NODES +}; + +struct node_description { + enum node_ids id; + const char *name; + unsigned int caps; + unsigned int pad_flags; + unsigned int link_pad; +}; + +/* Must match the ordering of enum ids */ +static const struct node_description node_desc[NUM_NODES] = { + [CSI2_CH0] = { + .name = "csi2-ch0", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 0 + }, + /* + * At the moment the main userspace component (libcamera) doesn't + * support metadata with video nodes that support both video and + * metadata. So for the time being this node is set to only support + * V4L2_CAP_META_CAPTURE. + */ + [CSI2_CH1] = { + .name = "csi2-ch1", + .caps = V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 1 + }, + [CSI2_CH2] = { + .name = "csi2-ch2", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 2 + }, + [CSI2_CH3] = { + .name = "csi2-ch3", + .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = CSI2_PAD_FIRST_SOURCE + 3 + }, + [FE_OUT0] = { + .name = "fe-image0", + .caps = V4L2_CAP_VIDEO_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_OUTPUT0_PAD + }, + [FE_OUT1] = { + .name = "fe-image1", + .caps = V4L2_CAP_VIDEO_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_OUTPUT1_PAD + }, + [FE_STATS] = { + .name = "fe-stats", + .caps = V4L2_CAP_META_CAPTURE, + .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_STATS_PAD + }, + [FE_CONFIG] = { + .name = "fe-config", + .caps = V4L2_CAP_META_OUTPUT, + .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT, + .link_pad = FE_CONFIG_PAD + }, +}; + +#define is_fe_node(node) (((node)->id) >= FE_OUT0) +#define is_csi2_node(node) (!is_fe_node(node)) + +#define node_supports_image_output(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE) +#define node_supports_meta_output(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE) +#define node_supports_image_input(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT) +#define node_supports_meta_input(node) \ + (node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT) +#define node_supports_image(node) \ + (node_supports_image_output(node) || node_supports_image_input(node)) +#define node_supports_meta(node) \ + (node_supports_meta_output(node) || node_supports_meta_input(node)) + +#define is_image_output_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +#define is_image_input_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +#define is_image_node(node) \ + (is_image_output_node(node) || is_image_input_node(node)) +#define is_meta_output_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE) +#define is_meta_input_node(node) \ + ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT) +#define is_meta_node(node) \ + (is_meta_output_node(node) || is_meta_input_node(node)) + +/* To track state across all nodes. */ +#define NODE_REGISTERED BIT(0) +#define NODE_ENABLED BIT(1) +#define NODE_STREAMING BIT(2) +#define FS_INT BIT(3) +#define FE_INT BIT(4) +#define NUM_STATES 5 + +struct cfe_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct cfe_config_buffer { + struct cfe_buffer buf; + struct pisp_fe_config config; +}; + +static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct cfe_buffer, vb.vb2_buf); +} + +static inline +struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf) +{ + return container_of(buf, struct cfe_config_buffer, buf); +} + +struct cfe_node { + /* Node id */ + enum node_ids id; + /* Pointer pointing to current v4l2_buffer */ + struct cfe_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cfe_buffer *next_frm; + /* Used to store current pixel format */ + struct v4l2_format vid_fmt; + /* Used to store current meta format */ + struct v4l2_format meta_fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* lock used to access this structure */ + struct mutex lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct cfe_device *cfe; + /* Media pad for this node */ + struct media_pad pad; + /* Frame-start counter */ + unsigned int fs_count; + /* Timestamp of the current buffer */ + u64 ts; +}; + +struct cfe_device { + struct dentry *debugfs; + struct kref kref; + + /* peripheral base address */ + void __iomem *mipi_cfg_base; + + struct clk *clk; + + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + struct media_pipeline pipe; + + /* IRQ lock for node state and DMA queues */ + spinlock_t state_lock; + bool job_ready; + bool job_queued; + + /* parent device */ + struct platform_device *pdev; + /* subdevice async Notifier */ + struct v4l2_async_notifier notifier; + + /* Source sub device */ + struct v4l2_subdev *source_sd; + /* Source subdev's pad */ + u32 source_pad; + + struct cfe_node node[NUM_NODES]; + DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES); + + struct csi2_device csi2; + struct pisp_fe_device fe; + + int fe_csi2_channel; + + /* Mask of enabled streams */ + u64 streams_mask; +}; + +static inline bool is_fe_enabled(struct cfe_device *cfe) +{ + return cfe->fe_csi2_channel != -1; +} + +static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cfe_device, v4l2_dev); +} + +static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset) +{ + return readl(cfe->mipi_cfg_base + offset); +} + +static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val) +{ + writel(val, cfe->mipi_cfg_base + offset); +} + +static bool check_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) { + if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags)) + return false; + } + + return true; +} + +static void set_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) + set_bit(bit + (node_id * NUM_STATES), cfe->node_flags); +} + +static void clear_state(struct cfe_device *cfe, unsigned long state, + unsigned int node_id) +{ + unsigned long bit; + + for_each_set_bit(bit, &state, sizeof(state)) + clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags); +} + +static bool test_any_node(struct cfe_device *cfe, unsigned long cond) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (check_state(cfe, cond, i)) + return true; + } + + return false; +} + +static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond, + unsigned long cond) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (check_state(cfe, precond, i)) { + if (!check_state(cfe, cond, i)) + return false; + } + } + + return true; +} + +static int mipi_cfg_regs_show(struct seq_file *s, void *data) +{ + struct cfe_device *cfe = s->private; + int ret; + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret) + return ret; + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg)) + DUMP(MIPICFG_CFG); + DUMP(MIPICFG_INTR); + DUMP(MIPICFG_INTE); + DUMP(MIPICFG_INTF); + DUMP(MIPICFG_INTS); +#undef DUMP + + pm_runtime_put(&cfe->pdev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs); + +/* Format setup functions */ +const struct cfe_fmt *find_format_by_code(u32 code) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +const struct cfe_fmt *find_format_by_pix(u32 pixelformat) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == pixelformat) + return &formats[i]; + } + + return NULL; +} + +static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code, + u32 fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code && formats[i].fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + +/* + * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap + * possible. + */ +u32 cfe_find_16bit_code(u32 code) +{ + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(code); + + if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT]) + return 0; + + cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); + if (!cfe_fmt) + return 0; + + return cfe_fmt->code; +} + +/* + * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap + * possible. + */ +u32 cfe_find_compressed_code(u32 code) +{ + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(code); + + if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED]) + return 0; + + cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); + if (!cfe_fmt) + return 0; + + return cfe_fmt->code; +} + +static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) +{ + unsigned int min_bytesperline; + + v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, + &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0); + + min_bytesperline = + ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT); + + if (f->fmt.pix.bytesperline > min_bytesperline && + f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) + f->fmt.pix.bytesperline = + ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT); + else + f->fmt.pix.bytesperline = min_bytesperline; + + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__, + &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); +} + +static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) +{ + v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2, + &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT, + 0, 0); + + f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3; + f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline; + + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__, + &f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height, + f->fmt.meta.bytesperline, f->fmt.meta.buffersize); +} + +static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) +{ + struct cfe_buffer *buf; + dma_addr_t addr; + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + struct cfe_node *node = &cfe->node[i]; + unsigned int stride, size; + + if (!check_state(cfe, NODE_STREAMING, i)) + continue; + + buf = list_first_entry(&node->dma_queue, struct cfe_buffer, + list); + node->next_frm = buf; + list_del(&buf->list); + + trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf); + + if (is_meta_node(node)) { + size = node->meta_fmt.fmt.meta.buffersize; + /* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */ + stride = 0; + } else { + size = node->vid_fmt.fmt.pix.sizeimage; + stride = node->vid_fmt.fmt.pix.bytesperline; + } + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size); + } +} + +static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) +{ + struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 }; + struct cfe_config_buffer *config_buf; + struct cfe_buffer *buf; + + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_STREAMING, i)) + continue; + + buf = list_first_entry(&node->dma_queue, struct cfe_buffer, + list); + + trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf); + + node->next_frm = buf; + vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf; + list_del(&buf->list); + } + + config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm); + pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config); +} + +static bool cfe_check_job_ready(struct cfe_device *cfe) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_ENABLED, i)) + continue; + + if (list_empty(&node->dma_queue)) + return false; + } + + return true; +} + +static void cfe_prepare_next_job(struct cfe_device *cfe) +{ + trace_cfe_prepare_next_job(is_fe_enabled(cfe)); + + cfe->job_queued = true; + cfe_schedule_next_csi2_job(cfe); + if (is_fe_enabled(cfe)) + cfe_schedule_next_pisp_job(cfe); + + /* Flag if another job is ready after this. */ + cfe->job_ready = cfe_check_job_ready(cfe); +} + +static void cfe_process_buffer_complete(struct cfe_node *node, + enum vb2_buffer_state state) +{ + trace_cfe_buffer_complete(node->id, &node->cur_frm->vb); + + node->cur_frm->vb.sequence = node->fs_count - 1; + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); +} + +static void cfe_queue_event_sof(struct cfe_node *node) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = node->fs_count - 1, + }; + + v4l2_event_queue(&node->video_dev, &event); +} + +static void cfe_sof_isr(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + bool matching_fs = true; + + trace_cfe_frame_start(node->id, node->fs_count); + + /* + * If the sensor is producing unexpected frame event ordering over a + * sustained period of time, guard against the possibility of coming + * here and orphaning the cur_frm if it's not been dequeued already. + * Unfortunately, there is not enough hardware state to tell if this + * may have occurred. + */ + if (WARN(node->cur_frm, "%s: [%s] Orphanded frame at seq %u\n", + __func__, node_desc[node->id].name, node->fs_count)) + cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR); + + node->cur_frm = node->next_frm; + node->next_frm = NULL; + node->fs_count++; + + node->ts = ktime_get_ns(); + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (!check_state(cfe, NODE_STREAMING, i) || i == node->id) + continue; + /* + * This checks if any other node has seen a FS. If yes, use the + * same timestamp, eventually across all node buffers. + */ + if (cfe->node[i].fs_count >= node->fs_count) + node->ts = cfe->node[i].ts; + /* + * This checks if all other node have seen a matching FS. If + * yes, we can flag another job to be queued. + */ + if (matching_fs && cfe->node[i].fs_count != node->fs_count) + matching_fs = false; + } + + if (matching_fs) + cfe->job_queued = false; + + if (node->cur_frm) + node->cur_frm->vb.vb2_buf.timestamp = node->ts; + + set_state(cfe, FS_INT, node->id); + clear_state(cfe, FE_INT, node->id); + + if (is_image_output_node(node)) + cfe_queue_event_sof(node); +} + +static void cfe_eof_isr(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + + trace_cfe_frame_end(node->id, node->fs_count - 1); + + if (node->cur_frm) + cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE); + + node->cur_frm = NULL; + set_state(cfe, FE_INT, node->id); + clear_state(cfe, FS_INT, node->id); +} + +static irqreturn_t cfe_isr(int irq, void *dev) +{ + struct cfe_device *cfe = dev; + bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 }; + u32 sts; + + sts = cfg_reg_read(cfe, MIPICFG_INTS); + + if (sts & MIPICFG_INT_CSI_DMA) + csi2_isr(&cfe->csi2, sof, eof); + + if (sts & MIPICFG_INT_PISP_FE) + pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS, + eof + CSI2_NUM_CHANNELS); + + spin_lock(&cfe->state_lock); + + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + /* + * The check_state(NODE_STREAMING) is to ensure we do not loop + * over the CSI2_CHx nodes when the FE is active since they + * generate interrupts even though the node is not streaming. + */ + if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i])) + continue; + + /* + * There are 3 cases where we could get FS + FE_ACK at + * the same time: + * 1) FE of the current frame, and FS of the next frame. + * 2) FS + FE of the same frame. + * 3) FE of the current frame, and FS + FE of the next + * frame. To handle this, see the sof handler below. + * + * (1) is handled implicitly by the ordering of the FE and FS + * handlers below. + */ + if (eof[i]) { + /* + * The condition below tests for (2). Run the FS handler + * first before the FE handler, both for the current + * frame. + */ + if (sof[i] && !check_state(cfe, FS_INT, i)) { + cfe_sof_isr(node); + sof[i] = false; + } + + cfe_eof_isr(node); + } + + if (sof[i]) { + /* + * The condition below tests for (3). In such cases, we + * come in here with FS flag set in the node state from + * the previous frame since it only gets cleared in + * cfe_eof_isr(). Handle the FE for the previous + * frame first before the FS handler for the current + * frame. + */ + if (check_state(cfe, FS_INT, node->id) && + !check_state(cfe, FE_INT, node->id)) { + cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n", + __func__, node_desc[node->id].name); + cfe_eof_isr(node); + } + + cfe_sof_isr(node); + } + + if (!cfe->job_queued && cfe->job_ready) + cfe_prepare_next_job(cfe); + } + + spin_unlock(&cfe->state_lock); + + return IRQ_HANDLED; +} + +/* + * Stream helpers + */ + +static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + const struct cfe_fmt *cfe_fmt; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0); + if (!fmt) + return -EINVAL; + + cfe_fmt = find_format_by_code(fmt->code); + if (!cfe_fmt) + return -EINVAL; + + *vc = 0; + *dt = cfe_fmt->csi_dt; + + return 0; +} + +static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc, + u8 *dt) +{ + struct v4l2_mbus_frame_desc remote_desc; + struct v4l2_subdev_state *state; + u32 sink_stream; + unsigned int i; + int ret; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc, + cfe->source_pad, &remote_desc); + if (ret == -ENOIOCTLCMD) { + cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n"); + return cfe_get_vc_dt_fallback(cfe, vc, dt); + } else if (ret) { + cfe_err(cfe, "Failed to get frame descriptor\n"); + return ret; + } + + if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + cfe_err(cfe, "Frame descriptor does not describe CSI-2 link"); + return -EINVAL; + } + + for (i = 0; i < remote_desc.num_entries; i++) { + if (remote_desc.entry[i].stream == sink_stream) + break; + } + + if (i == remote_desc.num_entries) { + cfe_err(cfe, "Stream %u not found in remote frame desc\n", + sink_stream); + return -EINVAL; + } + + *vc = remote_desc.entry[i].bus.csi2.vc; + *dt = remote_desc.entry[i].bus.csi2.dt; + + return 0; +} + +static int cfe_start_channel(struct cfe_node *node) +{ + struct cfe_device *cfe = node->cfe; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *source_fmt; + const struct cfe_fmt *fmt; + unsigned long flags; + bool start_fe; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + start_fe = is_fe_enabled(cfe) && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + if (start_fe) { + unsigned int width, height; + u8 vc, dt; + + cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__, + node_desc[FE_OUT0].name, cfe->fe_csi2_channel); + + ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt); + if (ret) + return ret; + + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[cfe->fe_csi2_channel].link_pad); + fmt = find_format_by_code(source_fmt->code); + + width = source_fmt->width; + height = source_fmt->height; + + /* Must have a valid CSI2 datatype. */ + WARN_ON(!fmt->csi_dt); + + /* + * Start the associated CSI2 Channel as well. + * + * Must write to the ADDR register to latch the ctrl values + * even if we are connected to the front end. Once running, + * this is handled by the CSI2 AUTO_ARM mode. + */ + csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel, + CSI2_MODE_FE_STREAMING, + true, false, width, height, vc, dt); + csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1); + pisp_fe_start(&cfe->fe); + } + + if (is_csi2_node(node)) { + unsigned int width = 0, height = 0; + u8 vc, dt; + + ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt); + if (ret) { + if (start_fe) { + csi2_stop_channel(&cfe->csi2, + cfe->fe_csi2_channel); + pisp_fe_stop(&cfe->fe); + } + + return ret; + } + + u32 mode = CSI2_MODE_NORMAL; + + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[node->id].link_pad); + fmt = find_format_by_code(source_fmt->code); + + /* Must have a valid CSI2 datatype. */ + WARN_ON(!fmt->csi_dt); + + if (is_image_output_node(node)) { + u32 pixfmt; + + width = source_fmt->width; + height = source_fmt->height; + + pixfmt = node->vid_fmt.fmt.pix.pixelformat; + + if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) { + mode = CSI2_MODE_REMAP; + } else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) { + mode = CSI2_MODE_COMPRESSED; + csi2_set_compression(&cfe->csi2, node->id, + CSI2_COMPRESSION_DELTA, 0, + 0); + } + } + /* Unconditionally start this CSI2 channel. */ + csi2_start_channel(&cfe->csi2, node->id, + mode, + /* Auto arm */ + false, + /* Pack bytes */ + is_meta_node(node) ? true : false, + width, height, vc, dt); + } + + spin_lock_irqsave(&cfe->state_lock, flags); + if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) + cfe_prepare_next_job(cfe); + spin_unlock_irqrestore(&cfe->state_lock, flags); + + return 0; +} + +static void cfe_stop_channel(struct cfe_node *node, bool fe_stop) +{ + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__, + node_desc[node->id].name, fe_stop); + + if (fe_stop) { + csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel); + pisp_fe_stop(&cfe->fe); + } + + if (is_csi2_node(node)) + csi2_stop_channel(&cfe->csi2, node->id); +} + +static void cfe_return_buffers(struct cfe_node *node, + enum vb2_buffer_state state) +{ + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf, *tmp; + unsigned long flags; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + spin_lock_irqsave(&cfe->state_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (node->cur_frm) { + trace_cfe_return_buffer(node->id, + node->cur_frm->vb.vb2_buf.index, 0); + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); + } + if (node->next_frm && node->cur_frm != node->next_frm) { + trace_cfe_return_buffer(node->id, + node->next_frm->vb.vb2_buf.index, 1); + vb2_buffer_done(&node->next_frm->vb.vb2_buf, state); + } + + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&cfe->state_lock, flags); +} + +/* + * vb2 ops + */ + +static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + unsigned int size = is_image_node(node) ? + node->vid_fmt.fmt.pix.sizeimage : + node->meta_fmt.fmt.meta.buffersize; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + node->buffer_queue.type); + + if (vq->max_num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->max_num_buffers; + + if (*nplanes) { + if (sizes[0] < size) { + cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int cfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf = to_cfe_buffer(vb); + unsigned long size; + + trace_cfe_buffer_prepare(node->id, vb); + + size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : + node->meta_fmt.fmt.meta.buffersize; + if (vb2_plane_size(vb, 0) < size) { + cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + + if (node->id == FE_CONFIG) { + struct cfe_config_buffer *b = to_cfe_config_buffer(buf); + void *addr = vb2_plane_vaddr(vb, 0); + + memcpy(&b->config, addr, sizeof(struct pisp_fe_config)); + return pisp_fe_validate_config(&cfe->fe, &b->config, + &cfe->node[FE_OUT0].vid_fmt, + &cfe->node[FE_OUT1].vid_fmt); + } + + return 0; +} + +static void cfe_buffer_queue(struct vb2_buffer *vb) +{ + struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct cfe_device *cfe = node->cfe; + struct cfe_buffer *buf = to_cfe_buffer(vb); + unsigned long flags; + bool schedule_now; + + spin_lock_irqsave(&cfe->state_lock, flags); + + list_add_tail(&buf->list, &node->dma_queue); + + if (!cfe->job_ready) + cfe->job_ready = cfe_check_job_ready(cfe); + + schedule_now = !cfe->job_queued && cfe->job_ready && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + trace_cfe_buffer_queue(node->id, vb, schedule_now); + + if (schedule_now) + cfe_prepare_next_job(cfe); + + spin_unlock_irqrestore(&cfe->state_lock, flags); +} + +static s64 cfe_get_source_link_freq(struct cfe_device *cfe) +{ + struct v4l2_subdev_state *state; + s64 link_freq; + u32 bpp; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + /* + * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back + * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. + * + * With multistream input there is no single pixel rate, and thus we + * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which + * causes v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. + */ + + if (state->routing.num_routes == 1) { + struct v4l2_subdev_route *route = &state->routing.routes[0]; + struct v4l2_mbus_framefmt *source_fmt; + const struct cfe_fmt *fmt; + + source_fmt = v4l2_subdev_state_get_format(state, + route->sink_pad, + route->sink_stream); + + fmt = find_format_by_code(source_fmt->code); + if (!fmt) + return -EINVAL; + + bpp = fmt->depth; + } else { + bpp = 0; + } + + link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp, + 2 * cfe->csi2.dphy.active_lanes); + if (link_freq < 0) + cfe_err(cfe, "failed to get link freq for subdev '%s'\n", + cfe->source_sd->name); + + return link_freq; +} + +static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *route; + s64 link_freq; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!check_state(cfe, NODE_ENABLED, node->id)) { + cfe_err(cfe, "%s node link is not enabled.\n", + node_desc[node->id].name); + ret = -EINVAL; + goto err_streaming; + } + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret < 0) { + cfe_err(cfe, "pm_runtime_resume_and_get failed\n"); + goto err_streaming; + } + + /* When using the Frontend, we must enable the FE_CONFIG node. */ + if (is_fe_enabled(cfe) && + !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) { + cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n"); + ret = -EINVAL; + goto err_pm_put; + } + + ret = media_pipeline_start(&node->pad, &cfe->pipe); + if (ret < 0) { + cfe_err(cfe, "Failed to start media pipeline: %d\n", ret); + goto err_pm_put; + } + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + + clear_state(cfe, FS_INT | FE_INT, node->id); + set_state(cfe, NODE_STREAMING, node->id); + node->fs_count = 0; + + ret = cfe_start_channel(node); + if (ret) + goto err_unlock_state; + + if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { + cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n"); + v4l2_subdev_unlock_state(state); + return 0; + } + + cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI); + cfg_reg_write(cfe, MIPICFG_INTE, + MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); + + ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0, + &mbus_config); + if (ret < 0 && ret != -ENOIOCTLCMD) { + cfe_err(cfe, "g_mbus_config failed\n"); + goto err_clear_inte; + } + + cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + if (!cfe->csi2.dphy.active_lanes) + cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; + if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) { + cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n", + cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes); + ret = -EINVAL; + goto err_clear_inte; + } + + link_freq = cfe_get_source_link_freq(cfe); + if (link_freq < 0) + goto err_clear_inte; + + cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000); + csi2_open_rx(&cfe->csi2); + + cfe->streams_mask = 0; + + for_each_active_route(&state->routing, route) + cfe->streams_mask |= BIT_ULL(route->sink_stream); + + ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad, + cfe->streams_mask); + if (ret) { + cfe_err(cfe, "stream on failed in subdev\n"); + goto err_disable_cfe; + } + + cfe_dbg(cfe, "Streaming enabled\n"); + + v4l2_subdev_unlock_state(state); + + return 0; + +err_disable_cfe: + csi2_close_rx(&cfe->csi2); +err_clear_inte: + cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_stop_channel(node, + is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED, + NODE_STREAMING)); +err_unlock_state: + v4l2_subdev_unlock_state(state); + media_pipeline_stop(&node->pad); +err_pm_put: + pm_runtime_put(&cfe->pdev->dev); +err_streaming: + cfe_return_buffers(node, VB2_BUF_STATE_QUEUED); + clear_state(cfe, NODE_STREAMING, node->id); + + return ret; +} + +static void cfe_stop_streaming(struct vb2_queue *vq) +{ + struct cfe_node *node = vb2_get_drv_priv(vq); + struct cfe_device *cfe = node->cfe; + unsigned long flags; + bool fe_stop; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + spin_lock_irqsave(&cfe->state_lock, flags); + fe_stop = is_fe_enabled(cfe) && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + cfe->job_ready = false; + clear_state(cfe, NODE_STREAMING, node->id); + spin_unlock_irqrestore(&cfe->state_lock, flags); + + cfe_stop_channel(node, fe_stop); + + if (!test_any_node(cfe, NODE_STREAMING)) { + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_disable_streams(cfe->source_sd, + cfe->source_pad, + cfe->streams_mask); + if (ret) + cfe_err(cfe, "stream disable failed in subdev\n"); + + v4l2_subdev_unlock_state(state); + + csi2_close_rx(&cfe->csi2); + + cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_dbg(cfe, "%s: Streaming disabled\n", __func__); + } + + media_pipeline_stop(&node->pad); + + /* Clear all queued buffers for the node */ + cfe_return_buffers(node, VB2_BUF_STATE_ERROR); + + pm_runtime_put(&cfe->pdev->dev); +} + +static const struct vb2_ops cfe_video_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .queue_setup = cfe_queue_setup, + .buf_prepare = cfe_buffer_prepare, + .buf_queue = cfe_buffer_queue, + .start_streaming = cfe_start_streaming, + .stop_streaming = cfe_stop_streaming, +}; + +/* + * v4l2 ioctl ops + */ + +static int cfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE | + V4L2_CAP_META_OUTPUT; + + return 0; +} + +static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + unsigned int i, j; + + if (!node_supports_image_output(node)) + return -EINVAL; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { + if (f->mbus_code && formats[i].code != f->mbus_code) + continue; + + if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT || + formats[i].flags & CFE_FORMAT_FLAG_META_CAP) + continue; + + if (is_fe_node(node) && + !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT)) + continue; + + if (j == f->index) { + f->pixelformat = formats[i].fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + j++; + } + + return -EINVAL; +} + +static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + + if (!node_supports_image(node)) + return -EINVAL; + + *f = node->vid_fmt; + + return 0; +} + +static int cfe_validate_fmt_vid_cap(struct cfe_node *node, + struct v4l2_format *f) +{ + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height, + &f->fmt.pix.pixelformat); + + if (!node_supports_image_output(node)) + return -EINVAL; + + /* + * Default to a format that works for both CSI2 and FE. + */ + fmt = find_format_by_pix(f->fmt.pix.pixelformat); + if (!fmt) + fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10); + + f->fmt.pix.pixelformat = fmt->fourcc; + + if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) { + f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT]; + fmt = find_format_by_pix(f->fmt.pix.pixelformat); + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + + cfe_calc_vid_format_size_bpl(cfe, fmt, f); + + return 0; +} + +static int cfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + if (vb2_is_busy(q)) + return -EBUSY; + + ret = cfe_validate_fmt_vid_cap(node, f); + if (ret) + return ret; + + node->vid_fmt = *f; + + cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__, + node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height, + &node->vid_fmt.fmt.pix.pixelformat); + + return 0; +} + +static int cfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + return cfe_validate_fmt_vid_cap(node, f); +} + +static int cfe_enum_fmt_meta(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!node_supports_meta(node)) + return -EINVAL; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + + switch (f->index) { + case 0: + f->pixelformat = V4L2_META_FMT_GENERIC_8; + return 0; + case 1: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10; + return 0; + case 2: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12; + return 0; + default: + return -EINVAL; + } + default: + break; + } + + if (f->index != 0) + return -EINVAL; + + switch (node->id) { + case FE_STATS: + f->pixelformat = V4L2_META_FMT_RPI_FE_STATS; + return 0; + case FE_CONFIG: + f->pixelformat = V4L2_META_FMT_RPI_FE_CFG; + return 0; + default: + return -EINVAL; + } +} + +static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f) +{ + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.width, + f->fmt.meta.height, &f->fmt.meta.dataformat); + break; + case FE_STATS: + case FE_CONFIG: + cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.buffersize, + &f->fmt.meta.dataformat); + break; + default: + return -EINVAL; + } + + if (!node_supports_meta(node)) + return -EINVAL; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + fmt = find_format_by_pix(f->fmt.meta.dataformat); + if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP)) + fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10); + + f->fmt.meta.dataformat = fmt->fourcc; + + cfe_calc_meta_format_size_bpl(cfe, fmt, f); + + return 0; + case FE_STATS: + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS; + f->fmt.meta.buffersize = sizeof(struct pisp_statistics); + return 0; + case FE_CONFIG: + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_fe_config); + return 0; + default: + return -EINVAL; + } +} + +static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (!node_supports_meta(node)) + return -EINVAL; + + *f = node->meta_fmt; + + return 0; +} + +static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + + if (vb2_is_busy(q)) + return -EBUSY; + + if (!node_supports_meta(node)) + return -EINVAL; + + ret = cfe_validate_fmt_meta(node, f); + if (ret) + return ret; + + node->meta_fmt = *f; + + cfe_dbg(cfe, "%s: Set %p4cc\n", __func__, + &node->meta_fmt.fmt.meta.dataformat); + + return 0; +} + +static int cfe_try_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + return cfe_validate_fmt_meta(node, f); +} + +static int cfe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct cfe_node *node = video_drvdata(file); + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name); + + if (fsize->index > 0) + return -EINVAL; + + /* check for valid format */ + fmt = find_format_by_pix(fsize->pixel_format); + if (!fmt) { + cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format); + return -EINVAL; + } + + /* TODO: Do we have limits on the step_width? */ + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH; + fsize->stepwise.step_width = 2; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + struct cfe_node *node = video_get_drvdata(vdev); + struct cfe_device *cfe = node->cfe; + int ret; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + p->type); + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->type != V4L2_BUF_TYPE_META_CAPTURE && + p->type != V4L2_BUF_TYPE_META_OUTPUT) + return -EINVAL; + + ret = vb2_queue_change_type(vdev->queue, p->type); + if (ret) + return ret; + + return vb2_ioctl_reqbufs(file, priv, p); +} + +static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + struct cfe_node *node = video_get_drvdata(vdev); + struct cfe_device *cfe = node->cfe; + int ret; + + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + p->format.type); + + if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->format.type != V4L2_BUF_TYPE_META_CAPTURE && + p->format.type != V4L2_BUF_TYPE_META_OUTPUT) + return -EINVAL; + + ret = vb2_queue_change_type(vdev->queue, p->format.type); + if (ret) + return ret; + + return vb2_ioctl_create_bufs(file, priv, p); +} + +static int cfe_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct cfe_node *node = video_get_drvdata(fh->vdev); + + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + if (!node_supports_image_output(node)) + break; + + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (!node_supports_image_output(node) && + !node_supports_meta_output(node)) + break; + + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops cfe_ioctl_ops = { + .vidioc_querycap = cfe_querycap, + .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cfe_g_fmt, + .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap, + + .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta, + .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta, + .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta, + + .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta, + .vidioc_g_fmt_meta_out = cfe_g_fmt_meta, + .vidioc_s_fmt_meta_out = cfe_s_fmt_meta, + .vidioc_try_fmt_meta_out = cfe_try_fmt_meta, + + .vidioc_enum_framesizes = cfe_enum_framesizes, + + .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs, + .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = cfe_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (check_state(cfe, NODE_REGISTERED, i)) + continue; + + v4l2_event_queue(&node->video_dev, arg); + } + break; + default: + break; + } +} + +/* cfe capture driver file operations */ +static const struct v4l2_file_operations cfe_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int cfe_video_link_validate(struct media_link *link) +{ + struct video_device *vd = container_of(link->sink->entity, + struct video_device, entity); + struct cfe_node *node = container_of(vd, struct cfe_node, video_dev); + struct cfe_device *cfe = node->cfe; + struct v4l2_mbus_framefmt *source_fmt; + struct v4l2_subdev_state *state; + struct v4l2_subdev *source_sd; + int ret = 0; + + cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, + node_desc[node->id].name, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + if (!media_entity_remote_source_pad_unique(link->sink->entity)) { + cfe_err(cfe, "video node %s pad not connected\n", vd->name); + return -ENOTCONN; + } + + source_sd = media_entity_to_v4l2_subdev(link->source->entity); + + state = v4l2_subdev_lock_and_get_active_state(source_sd); + + source_fmt = v4l2_subdev_state_get_format(state, link->source->index); + if (!source_fmt) { + ret = -EINVAL; + goto out; + } + + if (is_image_output_node(node)) { + struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix; + const struct cfe_fmt *fmt; + + if (source_fmt->width != pix_fmt->width || + source_fmt->height != pix_fmt->height) { + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + pix_fmt->width, pix_fmt->height, + source_fmt->width, source_fmt->height); + ret = -EINVAL; + goto out; + } + + fmt = find_format_by_code_and_fourcc(source_fmt->code, + pix_fmt->pixelformat); + if (!fmt) { + cfe_err(cfe, "Format mismatch!\n"); + ret = -EINVAL; + goto out; + } + } else if (is_csi2_node(node) && is_meta_output_node(node)) { + struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta; + const struct cfe_fmt *fmt; + + if (source_fmt->width != meta_fmt->width || + source_fmt->height != meta_fmt->height) { + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + meta_fmt->width, meta_fmt->height, + source_fmt->width, source_fmt->height); + ret = -EINVAL; + goto out; + } + + fmt = find_format_by_code_and_fourcc(source_fmt->code, + meta_fmt->dataformat); + if (!fmt) { + cfe_err(cfe, "Format mismatch!\n"); + ret = -EINVAL; + goto out; + } + } + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct media_entity_operations cfe_media_entity_ops = { + .link_validate = cfe_video_link_validate, +}; + +static int cfe_video_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_device *mdev = link->graph_obj.mdev; + struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev); + struct media_entity *fe = &cfe->fe.sd.entity; + struct media_entity *csi2 = &cfe->csi2.sd.entity; + unsigned long lock_flags; + + if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH) + return 0; + + cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, flags); + + spin_lock_irqsave(&cfe->state_lock, lock_flags); + + for (unsigned int i = 0; i < NUM_NODES; i++) { + if (link->sink->entity != &cfe->node[i].video_dev.entity && + link->source->entity != &cfe->node[i].video_dev.entity) + continue; + + if (link->flags & MEDIA_LNK_FL_ENABLED) + set_state(cfe, NODE_ENABLED, i); + else + clear_state(cfe, NODE_ENABLED, i); + + break; + } + + spin_unlock_irqrestore(&cfe->state_lock, lock_flags); + + if (link->source->entity != csi2) + return 0; + if (link->sink->entity != fe) + return 0; + if (link->sink->index != 0) + return 0; + + cfe->fe_csi2_channel = -1; + if (link->flags & MEDIA_LNK_FL_ENABLED) { + if (link->source->index == node_desc[CSI2_CH0].link_pad) + cfe->fe_csi2_channel = CSI2_CH0; + else if (link->source->index == node_desc[CSI2_CH1].link_pad) + cfe->fe_csi2_channel = CSI2_CH1; + else if (link->source->index == node_desc[CSI2_CH2].link_pad) + cfe->fe_csi2_channel = CSI2_CH2; + else if (link->source->index == node_desc[CSI2_CH3].link_pad) + cfe->fe_csi2_channel = CSI2_CH3; + } + + if (is_fe_enabled(cfe)) + cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__, + cfe->fe_csi2_channel); + else + cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n", + __func__); + + return 0; +} + +static const struct media_device_ops cfe_media_device_ops = { + .link_notify = cfe_video_link_notify, +}; + +static void cfe_release(struct kref *kref) +{ + struct cfe_device *cfe = container_of(kref, struct cfe_device, kref); + + media_device_cleanup(&cfe->mdev); + + kfree(cfe); +} + +static void cfe_put(struct cfe_device *cfe) +{ + kref_put(&cfe->kref, cfe_release); +} + +static void cfe_get(struct cfe_device *cfe) +{ + kref_get(&cfe->kref); +} + +static void cfe_node_release(struct video_device *vdev) +{ + struct cfe_node *node = video_get_drvdata(vdev); + + cfe_put(node->cfe); +} + +static int cfe_register_node(struct cfe_device *cfe, int id) +{ + struct video_device *vdev; + const struct cfe_fmt *fmt; + struct vb2_queue *q; + struct cfe_node *node = &cfe->node[id]; + int ret; + + node->cfe = cfe; + node->id = id; + + if (node_supports_image(node)) { + if (node_supports_image_output(node)) + node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else + node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + fmt = find_format_by_code(cfe_default_format.code); + if (!fmt) { + cfe_err(cfe, "Failed to find format code\n"); + return -EINVAL; + } + + node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc; + v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, + &cfe_default_format); + + ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt); + if (ret) + return ret; + } + + if (node_supports_meta(node)) { + if (node_supports_meta_output(node)) + node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + else + node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT; + + ret = cfe_validate_fmt_meta(node, &node->meta_fmt); + if (ret) + return ret; + } + + mutex_init(&node->lock); + + q = &node->buffer_queue; + q->type = node_supports_image(node) ? node->vid_fmt.type : + node->meta_fmt.type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = node; + q->ops = &cfe_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer) + : sizeof(struct cfe_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &node->lock; + q->min_queued_buffers = 1; + q->dev = &cfe->pdev->dev; + + ret = vb2_queue_init(q); + if (ret) { + cfe_err(cfe, "vb2_queue_init() failed\n"); + return ret; + } + + INIT_LIST_HEAD(&node->dma_queue); + + vdev = &node->video_dev; + vdev->release = cfe_node_release; + vdev->fops = &cfe_fops; + vdev->ioctl_ops = &cfe_ioctl_ops; + vdev->entity.ops = &cfe_media_entity_ops; + vdev->v4l2_dev = &cfe->v4l2_dev; + vdev->vfl_dir = (node_supports_image_output(node) || + node_supports_meta_output(node)) ? + VFL_DIR_RX : + VFL_DIR_TX; + vdev->queue = q; + vdev->lock = &node->lock; + vdev->device_caps = node_desc[id].caps; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + + /* Define the device names */ + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME, + node_desc[id].name); + + video_set_drvdata(vdev, node); + node->pad.flags = node_desc[id].pad_flags; + media_entity_pads_init(&vdev->entity, 1, &node->pad); + + if (!node_supports_image(node)) { + v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + cfe_err(cfe, "Unable to register video device %s\n", + vdev->name); + return ret; + } + + cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n", + vdev->name, id, vdev->num); + + /* + * Acquire a reference to cfe, which will be released when the video + * device will be unregistered and userspace will have closed all open + * file handles. + */ + cfe_get(cfe); + set_state(cfe, NODE_REGISTERED, id); + + return 0; +} + +static void cfe_unregister_nodes(struct cfe_device *cfe) +{ + for (unsigned int i = 0; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (check_state(cfe, NODE_REGISTERED, i)) { + clear_state(cfe, NODE_REGISTERED, i); + video_unregister_device(&node->video_dev); + } + } +} + +static int cfe_link_node_pads(struct cfe_device *cfe) +{ + struct media_pad *remote_pad; + int ret; + + /* Source -> CSI2 */ + + ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd, + &cfe->csi2.pad[CSI2_PAD_SINK], + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + if (ret) { + cfe_err(cfe, "Failed to create links to the source: %d\n", ret); + return ret; + } + + remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]); + if (IS_ERR(remote_pad)) { + ret = PTR_ERR(remote_pad); + cfe_err(cfe, "Failed to get unique remote source pad: %d\n", + ret); + return ret; + } + + cfe->source_pad = remote_pad->index; + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + struct cfe_node *node = &cfe->node[i]; + + if (!check_state(cfe, NODE_REGISTERED, i)) + continue; + + /* CSI2 channel # -> /dev/video# */ + ret = media_create_pad_link(&cfe->csi2.sd.entity, + node_desc[i].link_pad, + &node->video_dev.entity, 0, 0); + if (ret) + return ret; + + if (node_supports_image(node)) { + /* CSI2 channel # -> FE Input */ + ret = media_create_pad_link(&cfe->csi2.sd.entity, + node_desc[i].link_pad, + &cfe->fe.sd.entity, + FE_STREAM_PAD, 0); + if (ret) + return ret; + } + } + + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { + struct cfe_node *node = &cfe->node[i]; + struct media_entity *src, *dst; + unsigned int src_pad, dst_pad; + + if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) { + /* FE -> /dev/video# */ + src = &cfe->fe.sd.entity; + src_pad = node_desc[i].link_pad; + dst = &node->video_dev.entity; + dst_pad = 0; + } else { + /* /dev/video# -> FE */ + dst = &cfe->fe.sd.entity; + dst_pad = node_desc[i].link_pad; + src = &node->video_dev.entity; + src_pad = 0; + } + + ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0); + if (ret) + return ret; + } + + return 0; +} + +static int cfe_probe_complete(struct cfe_device *cfe) +{ + int ret; + + cfe->v4l2_dev.notify = cfe_notify; + + for (unsigned int i = 0; i < NUM_NODES; i++) { + ret = cfe_register_node(cfe, i); + if (ret) { + cfe_err(cfe, "Unable to register video node %u.\n", i); + goto unregister; + } + } + + ret = cfe_link_node_pads(cfe); + if (ret) { + cfe_err(cfe, "Unable to link node pads.\n"); + goto unregister; + } + + ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); + if (ret) { + cfe_err(cfe, "Unable to register subdev nodes.\n"); + goto unregister; + } + + return 0; + +unregister: + cfe_unregister_nodes(cfe); + return ret; +} + +static int cfe_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); + + if (cfe->source_sd) { + cfe_err(cfe, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + cfe->source_sd = subdev; + + cfe_dbg(cfe, "Using source %s for capture\n", subdev->name); + + return 0; +} + +static int cfe_async_complete(struct v4l2_async_notifier *notifier) +{ + struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); + + return cfe_probe_complete(cfe); +} + +static const struct v4l2_async_notifier_operations cfe_async_ops = { + .bound = cfe_async_bound, + .complete = cfe_async_complete, +}; + +static int cfe_register_async_nf(struct cfe_device *cfe) +{ + struct platform_device *pdev = cfe->pdev; + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct fwnode_handle *local_ep_fwnode; + struct v4l2_async_connection *asd; + int ret; + + local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0, + 0, 0); + if (!local_ep_fwnode) { + cfe_err(cfe, "Failed to find local endpoint fwnode\n"); + return -ENODEV; + } + + /* Parse the local endpoint and validate its configuration. */ + ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep); + if (ret) { + cfe_err(cfe, "Failed to find remote endpoint fwnode\n"); + goto err_put_local_fwnode; + } + + for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; + lane++) { + if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { + cfe_err(cfe, "Data lanes reordering not supported\n"); + ret = -EINVAL; + goto err_put_local_fwnode; + } + } + + cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes; + cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags; + + /* Initialize and register the async notifier. */ + v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev); + cfe->notifier.ops = &cfe_async_ops; + + asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + cfe_err(cfe, "Error adding subdevice: %d\n", ret); + goto err_put_local_fwnode; + } + + ret = v4l2_async_nf_register(&cfe->notifier); + if (ret) { + cfe_err(cfe, "Error registering async notifier: %d\n", ret); + goto err_nf_cleanup; + } + + fwnode_handle_put(local_ep_fwnode); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&cfe->notifier); +err_put_local_fwnode: + fwnode_handle_put(local_ep_fwnode); + + return ret; +} + +static int cfe_probe(struct platform_device *pdev) +{ + struct cfe_device *cfe; + char debugfs_name[32]; + int ret; + + cfe = kzalloc(sizeof(*cfe), GFP_KERNEL); + if (!cfe) + return -ENOMEM; + + platform_set_drvdata(pdev, cfe); + + kref_init(&cfe->kref); + cfe->pdev = pdev; + cfe->fe_csi2_channel = -1; + spin_lock_init(&cfe->state_lock); + + cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(cfe->csi2.base)) { + dev_err(&pdev->dev, "Failed to get dma io block\n"); + ret = PTR_ERR(cfe->csi2.base); + goto err_cfe_put; + } + + cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(cfe->csi2.dphy.base)) { + dev_err(&pdev->dev, "Failed to get host io block\n"); + ret = PTR_ERR(cfe->csi2.dphy.base); + goto err_cfe_put; + } + + cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(cfe->mipi_cfg_base)) { + dev_err(&pdev->dev, "Failed to get mipi cfg io block\n"); + ret = PTR_ERR(cfe->mipi_cfg_base); + goto err_cfe_put; + } + + cfe->fe.base = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(cfe->fe.base)) { + dev_err(&pdev->dev, "Failed to get pisp fe io block\n"); + ret = PTR_ERR(cfe->fe.base); + goto err_cfe_put; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + ret = -EINVAL; + goto err_cfe_put; + } + + ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + ret = -EINVAL; + goto err_cfe_put; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "DMA enable failed\n"); + goto err_cfe_put; + } + + /* TODO: Enable clock only when running. */ + cfe->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(cfe->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk), + "clock not found\n"); + + cfe->mdev.dev = &pdev->dev; + cfe->mdev.ops = &cfe_media_device_ops; + strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model)); + strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial)); + snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s", + dev_name(&pdev->dev)); + + media_device_init(&cfe->mdev); + + cfe->v4l2_dev.mdev = &cfe->mdev; + + ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev); + if (ret) { + cfe_err(cfe, "Unable to register v4l2 device.\n"); + goto err_cfe_put; + } + + snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s", + dev_name(&pdev->dev)); + cfe->debugfs = debugfs_create_dir(debugfs_name, NULL); + debugfs_create_file("regs", 0440, cfe->debugfs, cfe, + &mipi_cfg_regs_fops); + + /* Enable the block power domain */ + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_resume_and_get(&cfe->pdev->dev); + if (ret) + goto err_runtime_disable; + + cfe->csi2.v4l2_dev = &cfe->v4l2_dev; + ret = csi2_init(&cfe->csi2, cfe->debugfs); + if (ret) { + cfe_err(cfe, "Failed to init csi2 (%d)\n", ret); + goto err_runtime_put; + } + + cfe->fe.v4l2_dev = &cfe->v4l2_dev; + ret = pisp_fe_init(&cfe->fe, cfe->debugfs); + if (ret) { + cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret); + goto err_csi2_uninit; + } + + cfe->mdev.hw_revision = cfe->fe.hw_revision; + ret = media_device_register(&cfe->mdev); + if (ret < 0) { + cfe_err(cfe, "Unable to register media-controller device.\n"); + goto err_pisp_fe_uninit; + } + + ret = cfe_register_async_nf(cfe); + if (ret) { + cfe_err(cfe, "Failed to connect subdevs\n"); + goto err_media_unregister; + } + + pm_runtime_put(&cfe->pdev->dev); + + return 0; + +err_media_unregister: + media_device_unregister(&cfe->mdev); +err_pisp_fe_uninit: + pisp_fe_uninit(&cfe->fe); +err_csi2_uninit: + csi2_uninit(&cfe->csi2); +err_runtime_put: + pm_runtime_put(&cfe->pdev->dev); +err_runtime_disable: + pm_runtime_disable(&pdev->dev); + debugfs_remove(cfe->debugfs); + v4l2_device_unregister(&cfe->v4l2_dev); +err_cfe_put: + cfe_put(cfe); + + return ret; +} + +static void cfe_remove(struct platform_device *pdev) +{ + struct cfe_device *cfe = platform_get_drvdata(pdev); + + debugfs_remove(cfe->debugfs); + + v4l2_async_nf_unregister(&cfe->notifier); + v4l2_async_nf_cleanup(&cfe->notifier); + + media_device_unregister(&cfe->mdev); + cfe_unregister_nodes(cfe); + + pisp_fe_uninit(&cfe->fe); + csi2_uninit(&cfe->csi2); + + pm_runtime_disable(&pdev->dev); + + v4l2_device_unregister(&cfe->v4l2_dev); + + cfe_put(cfe); +} + +static int cfe_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cfe_device *cfe = platform_get_drvdata(pdev); + + clk_disable_unprepare(cfe->clk); + + return 0; +} + +static int cfe_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cfe_device *cfe = platform_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(cfe->clk); + if (ret) { + dev_err(dev, "Unable to enable clock\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops cfe_pm_ops = { + SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id cfe_of_match[] = { + { .compatible = "raspberrypi,rp1-cfe" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cfe_of_match); + +static struct platform_driver cfe_driver = { + .probe = cfe_probe, + .remove = cfe_remove, + .driver = { + .name = CFE_MODULE_NAME, + .of_match_table = cfe_of_match, + .pm = &cfe_pm_ops, + }, +}; + +module_platform_driver(cfe_driver); + +MODULE_AUTHOR("Naushir Patuck "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CFE_VERSION); diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h new file mode 100644 index 00000000000000..c63cc314be3caa --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 CFE Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ +#ifndef _RP1_CFE_ +#define _RP1_CFE_ + +#include +#include +#include + +extern bool cfe_debug_verbose; + +enum cfe_remap_types { + CFE_REMAP_16BIT, + CFE_REMAP_COMPRESSED, + CFE_NUM_REMAP, +}; + +#define CFE_FORMAT_FLAG_META_OUT BIT(0) +#define CFE_FORMAT_FLAG_META_CAP BIT(1) +#define CFE_FORMAT_FLAG_FE_OUT BIT(2) + +struct cfe_fmt { + u32 fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u32 remap[CFE_NUM_REMAP]; + u32 flags; +}; + +extern const struct v4l2_mbus_framefmt cfe_default_format; + +const struct cfe_fmt *find_format_by_code(u32 code); +const struct cfe_fmt *find_format_by_pix(u32 pixelformat); +u32 cfe_find_16bit_code(u32 code); +u32 cfe_find_compressed_code(u32 code); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c new file mode 100644 index 00000000000000..35c2ab1e2cd4d5 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include +#include +#include +#include + +#include + +#include "cfe.h" +#include "csi2.h" + +#include "cfe-trace.h" + +static bool csi2_track_errors; +module_param_named(track_csi2_errors, csi2_track_errors, bool, 0); +MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); + +#define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg) +#define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg) + +/* CSI2-DMA registers */ +#define CSI2_STATUS 0x000 +#define CSI2_QOS 0x004 +#define CSI2_DISCARDS_OVERFLOW 0x008 +#define CSI2_DISCARDS_INACTIVE 0x00c +#define CSI2_DISCARDS_UNMATCHED 0x010 +#define CSI2_DISCARDS_LEN_LIMIT 0x014 + +#define CSI2_DISCARDS_AMOUNT_SHIFT 0 +#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0) +#define CSI2_DISCARDS_DT_SHIFT 24 +#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24) +#define CSI2_DISCARDS_VC_SHIFT 30 +#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30) + +#define CSI2_LLEV_PANICS 0x018 +#define CSI2_ULEV_PANICS 0x01c +#define CSI2_IRQ_MASK 0x020 +#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0) +#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1) +#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2) +#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3) +#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4) +#define CSI2_IRQ_MASK_IRQ_ALL \ + (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \ + CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \ + CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \ + CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE) + +#define CSI2_CTRL 0x024 +#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28) +#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c) +#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c) +#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30) +#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34) +#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38) +#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40) +#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44) +#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48) + +/* CSI2_STATUS */ +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) +#define CSI2_STATUS_IRQ_CH_MASK(x) \ + (CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \ + CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \ + CSI2_STATUS_IRQ_LE_ACK(x)) +#define CSI2_STATUS_IRQ_OVERFLOW BIT(20) +#define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21) +#define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22) +#define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23) +#define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24) + +/* CSI2_CTRL */ +#define CSI2_CTRL_EOP_IS_EOL BIT(0) + +/* CSI2_CH_CTRL */ +#define CSI2_CH_CTRL_DMA_EN BIT(0) +#define CSI2_CH_CTRL_FORCE BIT(3) +#define CSI2_CH_CTRL_AUTO_ARM BIT(4) +#define CSI2_CH_CTRL_IRQ_EN_FS BIT(13) +#define CSI2_CH_CTRL_IRQ_EN_FE BIT(14) +#define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15) +#define CSI2_CH_CTRL_IRQ_EN_LE BIT(16) +#define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17) +#define CSI2_CH_CTRL_FLUSH_FE BIT(28) +#define CSI2_CH_CTRL_PACK_LINE BIT(29) +#define CSI2_CH_CTRL_PACK_BYTES BIT(30) +#define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1) +#define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5) +#define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7) +#define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18) + +/* CHx_COMPRESSION_CONTROL */ +#define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0) +#define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16) +#define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24) + +static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) +{ + return readl(csi2->base + offset); +} + +static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val) +{ + writel(val, csi2->base + offset); +} + +static inline void set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static int csi2_regs_show(struct seq_file *s, void *data) +{ + struct csi2_device *csi2 = s->private; + int ret; + + ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev); + if (ret) + return ret; + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg)) +#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \ + csi2_reg_read(csi2, reg(idx))) + + DUMP(CSI2_STATUS); + DUMP(CSI2_DISCARDS_OVERFLOW); + DUMP(CSI2_DISCARDS_INACTIVE); + DUMP(CSI2_DISCARDS_UNMATCHED); + DUMP(CSI2_DISCARDS_LEN_LIMIT); + DUMP(CSI2_LLEV_PANICS); + DUMP(CSI2_ULEV_PANICS); + DUMP(CSI2_IRQ_MASK); + DUMP(CSI2_CTRL); + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { + DUMP_CH(i, CSI2_CH_CTRL); + DUMP_CH(i, CSI2_CH_ADDR0); + DUMP_CH(i, CSI2_CH_ADDR1); + DUMP_CH(i, CSI2_CH_STRIDE); + DUMP_CH(i, CSI2_CH_LENGTH); + DUMP_CH(i, CSI2_CH_DEBUG); + DUMP_CH(i, CSI2_CH_FRAME_SIZE); + DUMP_CH(i, CSI2_CH_COMP_CTRL); + DUMP_CH(i, CSI2_CH_FE_FRAME_ID); + } + +#undef DUMP +#undef DUMP_CH + + pm_runtime_put(csi2->v4l2_dev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(csi2_regs); + +static int csi2_errors_show(struct seq_file *s, void *data) +{ + struct csi2_device *csi2 = s->private; + unsigned long flags; + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; + u32 overflows; + + spin_lock_irqsave(&csi2->errors_lock, flags); + + memcpy(discards_table, csi2->discards_table, sizeof(discards_table)); + memcpy(discards_dt_table, csi2->discards_dt_table, + sizeof(discards_dt_table)); + overflows = csi2->overflows; + + csi2->overflows = 0; + memset(csi2->discards_table, 0, sizeof(discards_table)); + memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table)); + + spin_unlock_irqrestore(&csi2->errors_lock, flags); + + seq_printf(s, "Overflows %u\n", overflows); + seq_puts(s, "Discards:\n"); + seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n"); + + for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) { + seq_printf(s, "%u %10u %10u %10u %10u\n", vc, + discards_table[vc][DISCARDS_TABLE_OVERFLOW], + discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT], + discards_table[vc][DISCARDS_TABLE_UNMATCHED], + discards_table[vc][DISCARDS_TABLE_INACTIVE]); + } + + seq_printf(s, "Last DT %10u %10u %10u %10u\n", + discards_dt_table[DISCARDS_TABLE_OVERFLOW], + discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT], + discards_dt_table[DISCARDS_TABLE_UNMATCHED], + discards_dt_table[DISCARDS_TABLE_INACTIVE]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(csi2_errors); + +static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) +{ + spin_lock(&csi2->errors_lock); + + if (status & CSI2_STATUS_IRQ_OVERFLOW) + csi2->overflows++; + + for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) { + static const u32 discard_bits[] = { + CSI2_STATUS_IRQ_DISCARD_OVERFLOW, + CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT, + CSI2_STATUS_IRQ_DISCARD_UNMATCHED, + CSI2_STATUS_IRQ_DISCARD_INACTIVE, + }; + static const u8 discard_regs[] = { + CSI2_DISCARDS_OVERFLOW, + CSI2_DISCARDS_LEN_LIMIT, + CSI2_DISCARDS_UNMATCHED, + CSI2_DISCARDS_INACTIVE, + }; + u32 amount; + u8 dt, vc; + u32 v; + + if (!(status & discard_bits[i])) + continue; + + v = csi2_reg_read(csi2, discard_regs[i]); + csi2_reg_write(csi2, discard_regs[i], 0); + + amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >> + CSI2_DISCARDS_AMOUNT_SHIFT; + dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT; + vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT; + + csi2->discards_table[vc][i] += amount; + csi2->discards_dt_table[i] = dt; + } + + spin_unlock(&csi2->errors_lock); +} + +void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof) +{ + u32 status; + + status = csi2_reg_read(csi2, CSI2_STATUS); + + /* Write value back to clear the interrupts */ + csi2_reg_write(csi2, CSI2_STATUS, status); + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { + u32 dbg; + + if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0) + continue; + + dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i)); + + trace_csi2_irq(i, status, dbg); + + sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i)); + eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i)); + } + + if (csi2_track_errors) + csi2_isr_handle_errors(csi2, status); +} + +void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, + dma_addr_t dmaaddr, unsigned int stride, unsigned int size) +{ + u64 addr = dmaaddr; + /* + * ADDRESS0 must be written last as it triggers the double buffering + * mechanism for all buffer registers within the hardware. + */ + addr >>= 4; + csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4); + csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4); + csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32); + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff); +} + +void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, + enum csi2_compression_mode mode, unsigned int shift, + unsigned int offset) +{ + u32 compression = 0; + + set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset); + set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift); + set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode); + csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression); +} + +void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, + enum csi2_mode mode, bool auto_arm, bool pack_bytes, + unsigned int width, unsigned int height, + u8 vc, u8 dt) +{ + u32 ctrl; + + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); + + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0); + csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0); + csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel)); + + /* Enable channel and FS/FE interrupts. */ + ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS | + CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE; + /* PACK_BYTES ensures no striding for embedded data. */ + if (pack_bytes) + ctrl |= CSI2_CH_CTRL_PACK_BYTES; + + if (auto_arm) + ctrl |= CSI2_CH_CTRL_AUTO_ARM; + + if (width && height) { + set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK); + csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), + (height << 16) | width); + } else { + set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK); + csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0); + } + + set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK); + set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK); + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl); + csi2->num_lines[channel] = height; +} + +void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel) +{ + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); + + /* Channel disable. Use FORCE to allow stopping mid-frame. */ + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE); + /* Latch the above change by writing to the ADDR0 register. */ + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); + /* Write this again, the HW needs it! */ + csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); +} + +void csi2_open_rx(struct csi2_device *csi2) +{ + csi2_reg_write(csi2, CSI2_IRQ_MASK, + csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0); + + dphy_start(&csi2->dphy); + + csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL); +} + +void csi2_close_rx(struct csi2_device *csi2) +{ + dphy_stop(&csi2->dphy); + + csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); +} + +static int csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { { + .sink_pad = CSI2_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + int ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, + &cfe_default_format); + if (ret) + return ret; + + return 0; +} + +static int csi2_pad_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + if (format->pad == CSI2_PAD_SINK) { + /* Store the sink format and propagate it to the source. */ + + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt) { + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10); + format->format.code = cfe_fmt->code; + } + + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + format->format.field = V4L2_FIELD_NONE; + + *fmt = format->format; + } else { + /* Only allow changing the source pad mbus code. */ + + struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; + u32 sink_code; + u32 code; + + sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); + if (!sink_fmt) + return -EINVAL; + + source_fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + if (!source_fmt) + return -EINVAL; + + sink_code = sink_fmt->code; + code = format->format.code; + + /* + * Only allow changing the mbus code to: + * - The sink's mbus code + * - The 16-bit version of the sink's mbus code + * - The compressed version of the sink's mbus code + */ + if (code == sink_code || + code == cfe_find_16bit_code(sink_code) || + code == cfe_find_compressed_code(sink_code)) + source_fmt->code = code; + + format->format.code = source_fmt->code; + } + + return 0; +} + +static int csi2_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, + &cfe_default_format); + if (ret) + return ret; + + return 0; +} + +static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2_pad_set_fmt, + .set_routing = csi2_set_routing, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct media_entity_operations csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static const struct v4l2_subdev_ops csi2_subdev_ops = { + .pad = &csi2_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = csi2_init_state, +}; + +int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) +{ + unsigned int ret; + + spin_lock_init(&csi2->errors_lock); + + csi2->dphy.dev = csi2->v4l2_dev->dev; + dphy_probe(&csi2->dphy); + + debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops); + + if (csi2_track_errors) + debugfs_create_file("csi2_errors", 0440, debugfs, csi2, + &csi2_errors_fops); + + csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = CSI2_PAD_FIRST_SOURCE; + i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++) + csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad), + csi2->pad); + if (ret) + return ret; + + /* Initialize subdev */ + v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); + csi2->sd.internal_ops = &csi2_internal_ops; + csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi2->sd.entity.ops = &csi2_entity_ops; + csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + csi2->sd.owner = THIS_MODULE; + snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2"); + + ret = v4l2_subdev_init_finalize(&csi2->sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd); + if (ret) { + csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&csi2->sd); +err_entity_cleanup: + media_entity_cleanup(&csi2->sd.entity); + + return ret; +} + +void csi2_uninit(struct csi2_device *csi2) +{ + v4l2_device_unregister_subdev(&csi2->sd); + v4l2_subdev_cleanup(&csi2->sd); + media_entity_cleanup(&csi2->sd.entity); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h new file mode 100644 index 00000000000000..a8ee5de565fbd6 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#ifndef _RP1_CSI2_ +#define _RP1_CSI2_ + +#include +#include +#include +#include +#include + +#include "dphy.h" + +#define CSI2_NUM_CHANNELS 4 + +#define CSI2_PAD_SINK 0 +#define CSI2_PAD_FIRST_SOURCE 1 +#define CSI2_PAD_NUM_SOURCES 4 +#define CSI2_NUM_PADS 5 + +#define DISCARDS_TABLE_NUM_VCS 4 + +enum csi2_mode { + CSI2_MODE_NORMAL = 0, + CSI2_MODE_REMAP = 1, + CSI2_MODE_COMPRESSED = 2, + CSI2_MODE_FE_STREAMING = 3, +}; + +enum csi2_compression_mode { + CSI2_COMPRESSION_DELTA = 1, + CSI2_COMPRESSION_SIMPLE = 2, + CSI2_COMPRESSION_COMBINED = 3, +}; + +enum discards_table_index { + DISCARDS_TABLE_OVERFLOW = 0, + DISCARDS_TABLE_LENGTH_LIMIT, + DISCARDS_TABLE_UNMATCHED, + DISCARDS_TABLE_INACTIVE, + DISCARDS_TABLE_NUM_ENTRIES, +}; + +struct csi2_device { + /* Parent V4l2 device */ + struct v4l2_device *v4l2_dev; + + void __iomem *base; + + struct dphy_data dphy; + + enum v4l2_mbus_type bus_type; + unsigned int bus_flags; + unsigned int num_lines[CSI2_NUM_CHANNELS]; + + struct media_pad pad[CSI2_NUM_PADS]; + struct v4l2_subdev sd; + + /* lock for csi2 errors counters */ + spinlock_t errors_lock; + u32 overflows; + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; +}; + +void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof); +void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, + dma_addr_t dmaaddr, unsigned int stride, + unsigned int size); +void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, + enum csi2_compression_mode mode, unsigned int shift, + unsigned int offset); +void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, + enum csi2_mode mode, bool auto_arm, + bool pack_bytes, unsigned int width, + unsigned int height, u8 vc, u8 dt); +void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel); +void csi2_open_rx(struct csi2_device *csi2); +void csi2_close_rx(struct csi2_device *csi2); +int csi2_init(struct csi2_device *csi2, struct dentry *debugfs); +void csi2_uninit(struct csi2_device *csi2); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c new file mode 100644 index 00000000000000..b443f0f56ddc82 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RP1 CSI-2 Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#include +#include + +#include "dphy.h" + +#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg) +#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg) + +/* DW dphy Host registers */ +#define DPHY_VERSION 0x000 +#define DPHY_N_LANES 0x004 +#define DPHY_RESETN 0x008 +#define DPHY_PHY_SHUTDOWNZ 0x040 +#define DPHY_PHY_RSTZ 0x044 +#define DPHY_PHY_RX 0x048 +#define DPHY_PHY_STOPSTATE 0x04c +#define DPHY_PHY_TST_CTRL0 0x050 +#define DPHY_PHY_TST_CTRL1 0x054 +#define DPHY_PHY2_TST_CTRL0 0x058 +#define DPHY_PHY2_TST_CTRL1 0x05c + +/* DW dphy Host Transactions */ +#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44 +#define DPHY_PLL_INPUT_DIV_OFFSET 0x17 +#define DPHY_PLL_LOOP_DIV_OFFSET 0x18 +#define DPHY_PLL_DIV_CTRL_OFFSET 0x19 + +static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset) +{ + return readl(dphy->base + offset); +} + +static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data) +{ + writel(data, dphy->base + offset); +} + +static void set_tstclr(struct dphy_data *dphy, u32 val) +{ + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val); +} + +static void set_tstclk(struct dphy_data *dphy, u32 val) +{ + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1)); +} + +static uint8_t get_tstdout(struct dphy_data *dphy) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + return ((ctrl1 >> 8) & 0xff); +} + +static void set_testen(struct dphy_data *dphy, u32 val) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, + (ctrl1 & ~(1 << 16)) | (val << 16)); +} + +static void set_testdin(struct dphy_data *dphy, u32 val) +{ + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); + + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val); +} + +static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code, + uint8_t test_data) +{ + /* See page 101 of the MIPI DPHY databook. */ + set_tstclk(dphy, 1); + set_testen(dphy, 0); + set_testdin(dphy, test_code); + set_testen(dphy, 1); + set_tstclk(dphy, 0); + set_testen(dphy, 0); + set_testdin(dphy, test_data); + set_tstclk(dphy, 1); + return get_tstdout(dphy); +} + +static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps) +{ + /* See Table 5-1 on page 65 of dphy databook */ + static const u16 hsfreqrange_table[][2] = { + { 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 }, + { 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 }, + { 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 }, + { 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 }, + { 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 }, + { 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 }, + { 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 }, + { 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 }, + { 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 }, + { 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 }, + { 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 }, + { 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 }, + { 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 }, + }; + unsigned int i; + + if (mbps < 80 || mbps > 1500) + dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps); + + for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) { + if (mbps <= hsfreqrange_table[i][0]) + break; + } + + dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET, + hsfreqrange_table[i][1] << 1); +} + +static void dphy_init(struct dphy_data *dphy) +{ + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0); + set_tstclk(dphy, 1); + set_testen(dphy, 0); + set_tstclr(dphy, 1); + usleep_range(15, 20); + set_tstclr(dphy, 0); + usleep_range(15, 20); + + dphy_set_hsfreqrange(dphy, dphy->dphy_rate); + + usleep_range(5, 10); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1); + usleep_range(5, 10); + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1); +} + +void dphy_start(struct dphy_data *dphy) +{ + dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__, + dphy->dphy_rate, dphy->active_lanes); + + dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1)); + dphy_init(dphy); + dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff); + usleep_range(10, 50); +} + +void dphy_stop(struct dphy_data *dphy) +{ + dphy_dbg(dphy, "%s\n", __func__); + + /* Set only one lane (lane 0) as active (ON) */ + dw_csi2_host_write(dphy, DPHY_N_LANES, 0); + dw_csi2_host_write(dphy, DPHY_RESETN, 0); +} + +void dphy_probe(struct dphy_data *dphy) +{ + u32 host_ver; + u8 host_ver_major, host_ver_minor; + + host_ver = dw_csi2_host_read(dphy, DPHY_VERSION); + host_ver_major = (u8)((host_ver >> 24) - '0'); + host_ver_minor = (u8)((host_ver >> 16) - '0'); + host_ver_minor = host_ver_minor * 10; + host_ver_minor += (u8)((host_ver >> 8) - '0'); + + dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major, + host_ver_minor); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h new file mode 100644 index 00000000000000..84fa370957cc3c --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy + */ + +#ifndef _RP1_DPHY_ +#define _RP1_DPHY_ + +#include +#include + +struct dphy_data { + struct device *dev; + + void __iomem *base; + + u32 dphy_rate; + u32 max_lanes; + u32 active_lanes; +}; + +void dphy_probe(struct dphy_data *dphy); +void dphy_start(struct dphy_data *dphy); +void dphy_stop(struct dphy_data *dphy); + +#endif diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c new file mode 100644 index 00000000000000..05762b1be2bc75 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PiSP Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + */ + +#include +#include +#include +#include +#include + +#include + +#include "cfe.h" +#include "pisp-fe.h" + +#include "cfe-trace.h" + +#define FE_VERSION 0x000 +#define FE_CONTROL 0x004 +#define FE_STATUS 0x008 +#define FE_FRAME_STATUS 0x00c +#define FE_ERROR_STATUS 0x010 +#define FE_OUTPUT_STATUS 0x014 +#define FE_INT_EN 0x018 +#define FE_INT_STATUS 0x01c + +/* CONTROL */ +#define FE_CONTROL_QUEUE BIT(0) +#define FE_CONTROL_ABORT BIT(1) +#define FE_CONTROL_RESET BIT(2) +#define FE_CONTROL_LATCH_REGS BIT(3) + +/* INT_EN / INT_STATUS */ +#define FE_INT_EOF BIT(0) +#define FE_INT_SOF BIT(1) +#define FE_INT_LINES0 BIT(8) +#define FE_INT_LINES1 BIT(9) +#define FE_INT_STATS BIT(16) +#define FE_INT_QREADY BIT(24) + +/* STATUS */ +#define FE_STATUS_QUEUED BIT(0) +#define FE_STATUS_WAITING BIT(1) +#define FE_STATUS_ACTIVE BIT(2) + +#define PISP_FE_CONFIG_BASE_OFFSET 0x0040 + +#define PISP_FE_ENABLE_STATS_CLUSTER \ + (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \ + PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \ + PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \ + PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS) + +#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \ + ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \ + PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i))) + +struct pisp_fe_config_param { + u32 dirty_flags; + u32 dirty_flags_extra; + size_t offset; + size_t size; +}; + +static const struct pisp_fe_config_param pisp_fe_config_map[] = { + /* *_dirty_flag_extra types */ + { 0, PISP_FE_DIRTY_GLOBAL, + offsetof(struct pisp_fe_config, global), + sizeof(struct pisp_fe_global_config) }, + { 0, PISP_FE_DIRTY_FLOATING, + offsetof(struct pisp_fe_config, floating_stats), + sizeof(struct pisp_fe_floating_stats_config) }, + { 0, PISP_FE_DIRTY_OUTPUT_AXI, + offsetof(struct pisp_fe_config, output_axi), + sizeof(struct pisp_fe_output_axi_config) }, + /* *_dirty_flag types */ + { PISP_FE_ENABLE_INPUT, 0, + offsetof(struct pisp_fe_config, input), + sizeof(struct pisp_fe_input_config) }, + { PISP_FE_ENABLE_DECOMPRESS, 0, + offsetof(struct pisp_fe_config, decompress), + sizeof(struct pisp_decompress_config) }, + { PISP_FE_ENABLE_DECOMPAND, 0, + offsetof(struct pisp_fe_config, decompand), + sizeof(struct pisp_fe_decompand_config) }, + { PISP_FE_ENABLE_BLA, 0, + offsetof(struct pisp_fe_config, bla), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_DPC, 0, + offsetof(struct pisp_fe_config, dpc), + sizeof(struct pisp_fe_dpc_config) }, + { PISP_FE_ENABLE_STATS_CROP, 0, + offsetof(struct pisp_fe_config, stats_crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_BLC, 0, + offsetof(struct pisp_fe_config, blc), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_CDAF_STATS, 0, + offsetof(struct pisp_fe_config, cdaf_stats), + sizeof(struct pisp_fe_cdaf_stats_config) }, + { PISP_FE_ENABLE_AWB_STATS, 0, + offsetof(struct pisp_fe_config, awb_stats), + sizeof(struct pisp_fe_awb_stats_config) }, + { PISP_FE_ENABLE_RGBY, 0, + offsetof(struct pisp_fe_config, rgby), + sizeof(struct pisp_fe_rgby_config) }, + { PISP_FE_ENABLE_LSC, 0, + offsetof(struct pisp_fe_config, lsc), + sizeof(struct pisp_fe_lsc_config) }, + { PISP_FE_ENABLE_AGC_STATS, 0, + offsetof(struct pisp_fe_config, agc_stats), + sizeof(struct pisp_agc_statistics) }, + { PISP_FE_ENABLE_CROP0, 0, + offsetof(struct pisp_fe_config, ch[0].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE0, 0, + offsetof(struct pisp_fe_config, ch[0].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS0, 0, + offsetof(struct pisp_fe_config, ch[0].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT0, 0, + offsetof(struct pisp_fe_config, ch[0].output), + sizeof(struct pisp_fe_output_config) }, + { PISP_FE_ENABLE_CROP1, 0, + offsetof(struct pisp_fe_config, ch[1].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE1, 0, + offsetof(struct pisp_fe_config, ch[1].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS1, 0, + offsetof(struct pisp_fe_config, ch[1].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT1, 0, + offsetof(struct pisp_fe_config, ch[1].output), + sizeof(struct pisp_fe_output_config) }, +}; + +#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg) + +static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset) +{ + return readl(fe->base + offset); +} + +static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset, + u32 val) +{ + writel(val, fe->base + offset); +} + +static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, + u32 offset, u32 val) +{ + writel_relaxed(val, fe->base + offset); +} + +static int pisp_fe_regs_show(struct seq_file *s, void *data) +{ + struct pisp_fe_device *fe = s->private; + int ret; + + ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev); + if (ret) + return ret; + + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg)) + DUMP(FE_VERSION); + DUMP(FE_CONTROL); + DUMP(FE_STATUS); + DUMP(FE_FRAME_STATUS); + DUMP(FE_ERROR_STATUS); + DUMP(FE_OUTPUT_STATUS); + DUMP(FE_INT_EN); + DUMP(FE_INT_STATUS); +#undef DUMP + + pm_runtime_put(fe->v4l2_dev->dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs); + +static void pisp_fe_config_write(struct pisp_fe_device *fe, + struct pisp_fe_config *config, + unsigned int start_offset, unsigned int size) +{ + const unsigned int max_offset = + offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]); + unsigned int end_offset; + u32 *cfg = (u32 *)config; + + start_offset = min(start_offset, max_offset); + end_offset = min(start_offset + size, max_offset); + + cfg += start_offset >> 2; + for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++) + pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i, + *cfg); +} + +void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) +{ + u32 status, int_status, out_status, frame_status, error_status; + + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); + status = pisp_fe_reg_read(fe, FE_STATUS); + out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS); + frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS); + error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS); + + int_status = pisp_fe_reg_read(fe, FE_INT_STATUS); + pisp_fe_reg_write(fe, FE_INT_STATUS, int_status); + + trace_fe_irq(status, out_status, frame_status, error_status, + int_status); + + /* We do not report interrupts for the input/stream pad. */ + for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) { + sof[i] = !!(int_status & FE_INT_SOF); + eof[i] = !!(int_status & FE_INT_EOF); + } +} + +static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, + unsigned int c, struct v4l2_format const *f) +{ + unsigned int wbytes; + + wbytes = cfg->ch[c].output.format.width; + if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK) + wbytes *= 2; + + /* Check output image dimensions are nonzero and not too big */ + if (cfg->ch[c].output.format.width < 2 || + cfg->ch[c].output.format.height < 2 || + cfg->ch[c].output.format.height > f->fmt.pix.height || + cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline || + wbytes > f->fmt.pix.bytesperline) + return false; + + /* Check for zero-sized crops, which could cause lockup */ + if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && + ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) || + cfg->ch[c].crop.offset_y >= cfg->input.format.height || + cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2))) + return false; + + if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) && + (cfg->ch[c].downscale.output_width < 2 || + cfg->ch[c].downscale.output_height < 2)) + return false; + + return true; +} + +static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg) +{ + /* Check for zero-sized crop, which could cause lockup */ + return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) || + (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) && + cfg->stats_crop.offset_y < cfg->input.format.height && + cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2)); +} + +int pisp_fe_validate_config(struct pisp_fe_device *fe, + struct pisp_fe_config *cfg, + struct v4l2_format const *f0, + struct v4l2_format const *f1) +{ + /* + * Check the input is enabled, streaming and has nonzero size; + * to avoid cases where the hardware might lock up or try to + * read inputs from memory (which this driver doesn't support). + */ + if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) || + cfg->input.streaming != 1 || cfg->input.format.width < 2 || + cfg->input.format.height < 2) { + pisp_fe_err(fe, "%s: Input config not valid", __func__); + return -EINVAL; + } + + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) { + if (cfg->global.enables & + PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) { + pisp_fe_err(fe, "%s: Output %u not valid", + __func__, i); + return -EINVAL; + } + continue; + } + + if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0)) + return -EINVAL; + } + + if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) && + !pisp_fe_validate_stats(cfg)) { + pisp_fe_err(fe, "%s: Stats config not valid", __func__); + return -EINVAL; + } + + return 0; +} + +void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, + struct pisp_fe_config *cfg) +{ + u64 addr; + u32 status; + + /* + * Check output buffers exist and outputs are correctly configured. + * If valid, set the buffer's DMA address; otherwise disable. + */ + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i]; + + if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) + continue; + + addr = vb2_dma_contig_plane_dma_addr(buf, 0); + cfg->output_buffer[i].addr_lo = addr & 0xffffffff; + cfg->output_buffer[i].addr_hi = addr >> 32; + } + + if (vb2_bufs[FE_STATS_PAD]) { + addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0); + cfg->stats_buffer.addr_lo = addr & 0xffffffff; + cfg->stats_buffer.addr_hi = addr >> 32; + } + + /* Set up ILINES interrupts 3/4 of the way down each output */ + cfg->ch[0].output.ilines = + max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2); + cfg->ch[1].output.ilines = + max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2); + + /* + * The hardware must have consumed the previous config by now. + * This read of status also serves as a memory barrier before the + * sequence of relaxed writes which follow. + */ + status = pisp_fe_reg_read(fe, FE_STATUS); + if (WARN_ON(status & FE_STATUS_QUEUED)) + return; + + /* + * Unconditionally write buffers, global and input parameters. + * Write cropping and output parameters whenever they are enabled. + * Selectively write other parameters that have been marked as + * changed through the dirty flags. + */ + pisp_fe_config_write(fe, cfg, 0, + offsetof(struct pisp_fe_config, decompress)); + cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL; + cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT; + cfg->dirty_flags |= (cfg->global.enables & + (PISP_FE_ENABLE_STATS_CROP | + PISP_FE_ENABLE_OUTPUT_CLUSTER(0) | + PISP_FE_ENABLE_OUTPUT_CLUSTER(1))); + for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { + const struct pisp_fe_config_param *p = &pisp_fe_config_map[i]; + + if (cfg->dirty_flags & p->dirty_flags || + cfg->dirty_flags_extra & p->dirty_flags_extra) + pisp_fe_config_write(fe, cfg, p->offset, p->size); + } + + /* This final non-relaxed write serves as a memory barrier */ + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE); +} + +void pisp_fe_start(struct pisp_fe_device *fe) +{ + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET); + pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); + pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | + FE_INT_LINES0 | FE_INT_LINES1); + fe->inframe_count = 0; +} + +void pisp_fe_stop(struct pisp_fe_device *fe) +{ + pisp_fe_reg_write(fe, FE_INT_EN, 0); + pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT); + usleep_range(1000, 2000); + WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); + pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); +} + +static int pisp_fe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD); + fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_fe_config); + fmt->height = 1; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); + *fmt = cfe_default_format; + fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + + fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD); + fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_statistics); + fmt->height = 1; + + return 0; +} + +static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + const struct cfe_fmt *cfe_fmt; + + /* TODO: format propagation to source pads */ + /* TODO: format validation */ + + switch (format->pad) { + case FE_STREAM_PAD: + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); + + format->format.code = cfe_fmt->code; + format->format.field = V4L2_FIELD_NONE; + + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); + *fmt = format->format; + + return 0; + + case FE_OUTPUT0_PAD: + case FE_OUTPUT1_PAD: { + /* + * TODO: we should allow scaling and cropping by allowing the + * user to set the size here. + */ + struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; + u32 sink_code; + u32 code; + + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); + + format->format.code = cfe_fmt->code; + + sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); + if (!sink_fmt) + return -EINVAL; + + source_fmt = v4l2_subdev_state_get_format(state, format->pad); + if (!source_fmt) + return -EINVAL; + + sink_code = sink_fmt->code; + code = format->format.code; + + /* + * If the source code from the user does not match the code in + * the sink pad, check that the source code matches the + * compressed version of the sink code. + */ + + if (code != sink_code && + code == cfe_find_compressed_code(sink_code)) + source_fmt->code = code; + + return 0; + } + + case FE_CONFIG_PAD: + case FE_STATS_PAD: + default: + return v4l2_subdev_get_fmt(sd, state, format); + } +} + +static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = pisp_fe_pad_set_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static int pisp_fe_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity); + struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd); + + pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + if (link->sink->index == FE_STREAM_PAD) + return v4l2_subdev_link_validate(link); + + if (link->sink->index == FE_CONFIG_PAD) + return 0; + + return -EINVAL; +} + +static const struct media_entity_operations pisp_fe_entity_ops = { + .link_validate = pisp_fe_link_validate, +}; + +static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { + .pad = &pisp_fe_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { + .init_state = pisp_fe_init_state, +}; + +int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) +{ + int ret; + + debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops); + + fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION); + pisp_fe_info(fe, "PiSP FE HW v%u.%u\n", + (fe->hw_revision >> 24) & 0xff, + (fe->hw_revision >> 20) & 0x0f); + + fe->pad[FE_STREAM_PAD].flags = + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK; + fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE; + fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE; + fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad), + fe->pad); + if (ret) + return ret; + + /* Initialize subdev */ + v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); + fe->sd.internal_ops = &pisp_fe_internal_ops; + fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + fe->sd.entity.ops = &pisp_fe_entity_ops; + fe->sd.entity.name = "pisp-fe"; + fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + fe->sd.owner = THIS_MODULE; + snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe"); + + ret = v4l2_subdev_init_finalize(&fe->sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd); + if (ret) { + pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret); + goto err_subdev_cleanup; + } + + /* Must be in IDLE state (STATUS == 0) here. */ + WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&fe->sd); +err_entity_cleanup: + media_entity_cleanup(&fe->sd.entity); + + return ret; +} + +void pisp_fe_uninit(struct pisp_fe_device *fe) +{ + v4l2_device_unregister_subdev(&fe->sd); + v4l2_subdev_cleanup(&fe->sd); + media_entity_cleanup(&fe->sd.entity); +} diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h new file mode 100644 index 00000000000000..54d506e19cf214 --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Front End Driver + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + */ +#ifndef _PISP_FE_H_ +#define _PISP_FE_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +enum pisp_fe_pads { + FE_STREAM_PAD, + FE_CONFIG_PAD, + FE_OUTPUT0_PAD, + FE_OUTPUT1_PAD, + FE_STATS_PAD, + FE_NUM_PADS +}; + +struct pisp_fe_device { + /* Parent V4l2 device */ + struct v4l2_device *v4l2_dev; + void __iomem *base; + u32 hw_revision; + + u16 inframe_count; + struct media_pad pad[FE_NUM_PADS]; + struct v4l2_subdev sd; +}; + +void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof); +int pisp_fe_validate_config(struct pisp_fe_device *fe, + struct pisp_fe_config *cfg, + struct v4l2_format const *f0, + struct v4l2_format const *f1); +void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, + struct pisp_fe_config *cfg); +void pisp_fe_start(struct pisp_fe_device *fe); +void pisp_fe_stop(struct pisp_fe_device *fe); +int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs); +void pisp_fe_uninit(struct pisp_fe_device *fe); + +#endif diff --git a/include/uapi/linux/media/raspberrypi/pisp_fe_config.h b/include/uapi/linux/media/raspberrypi/pisp_fe_config.h new file mode 100644 index 00000000000000..77237460a3b5bd --- /dev/null +++ b/include/uapi/linux/media/raspberrypi/pisp_fe_config.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * RP1 PiSP Front End Driver Configuration structures + * + * Copyright (C) 2021 - Raspberry Pi Ltd. + * + */ +#ifndef _UAPI_PISP_FE_CONFIG_ +#define _UAPI_PISP_FE_CONFIG_ + +#include + +#include "pisp_common.h" +#include "pisp_fe_statistics.h" + +#define PISP_FE_NUM_OUTPUTS 2 + +enum pisp_fe_enable { + PISP_FE_ENABLE_INPUT = 0x000001, + PISP_FE_ENABLE_DECOMPRESS = 0x000002, + PISP_FE_ENABLE_DECOMPAND = 0x000004, + PISP_FE_ENABLE_BLA = 0x000008, + PISP_FE_ENABLE_DPC = 0x000010, + PISP_FE_ENABLE_STATS_CROP = 0x000020, + PISP_FE_ENABLE_DECIMATE = 0x000040, + PISP_FE_ENABLE_BLC = 0x000080, + PISP_FE_ENABLE_CDAF_STATS = 0x000100, + PISP_FE_ENABLE_AWB_STATS = 0x000200, + PISP_FE_ENABLE_RGBY = 0x000400, + PISP_FE_ENABLE_LSC = 0x000800, + PISP_FE_ENABLE_AGC_STATS = 0x001000, + PISP_FE_ENABLE_CROP0 = 0x010000, + PISP_FE_ENABLE_DOWNSCALE0 = 0x020000, + PISP_FE_ENABLE_COMPRESS0 = 0x040000, + PISP_FE_ENABLE_OUTPUT0 = 0x080000, + PISP_FE_ENABLE_CROP1 = 0x100000, + PISP_FE_ENABLE_DOWNSCALE1 = 0x200000, + PISP_FE_ENABLE_COMPRESS1 = 0x400000, + PISP_FE_ENABLE_OUTPUT1 = 0x800000 +}; + +#define PISP_FE_ENABLE_CROP(i) (PISP_FE_ENABLE_CROP0 << (4 * (i))) +#define PISP_FE_ENABLE_DOWNSCALE(i) (PISP_FE_ENABLE_DOWNSCALE0 << (4 * (i))) +#define PISP_FE_ENABLE_COMPRESS(i) (PISP_FE_ENABLE_COMPRESS0 << (4 * (i))) +#define PISP_FE_ENABLE_OUTPUT(i) (PISP_FE_ENABLE_OUTPUT0 << (4 * (i))) + +/* + * We use the enable flags to show when blocks are "dirty", but we need some + * extra ones too. + */ +enum pisp_fe_dirty { + PISP_FE_DIRTY_GLOBAL = 0x0001, + PISP_FE_DIRTY_FLOATING = 0x0002, + PISP_FE_DIRTY_OUTPUT_AXI = 0x0004 +}; + +struct pisp_fe_global_config { + __u32 enables; + __u8 bayer_order; + __u8 pad[3]; +} __attribute__((packed)); + +struct pisp_fe_input_axi_config { + /* burst length minus one, in the range 0..15; OR'd with flags */ + __u8 maxlen_flags; + /* { prot[2:0], cache[3:0] } fields */ + __u8 cache_prot; + /* QoS (only 4 LS bits are used) */ + __u16 qos; +} __attribute__((packed)); + +struct pisp_fe_output_axi_config { + /* burst length minus one, in the range 0..15; OR'd with flags */ + __u8 maxlen_flags; + /* { prot[2:0], cache[3:0] } fields */ + __u8 cache_prot; + /* QoS (4 bitfields of 4 bits each for different panic levels) */ + __u16 qos; + /* For Panic mode: Output FIFO panic threshold */ + __u16 thresh; + /* For Panic mode: Output FIFO statistics throttle threshold */ + __u16 throttle; +} __attribute__((packed)); + +struct pisp_fe_input_config { + __u8 streaming; + __u8 pad[3]; + struct pisp_image_format_config format; + struct pisp_fe_input_axi_config axi; + /* Extra cycles delay before issuing each burst request */ + __u8 holdoff; + __u8 pad2[3]; +} __attribute__((packed)); + +struct pisp_fe_output_config { + struct pisp_image_format_config format; + __u16 ilines; + __u8 pad[2]; +} __attribute__((packed)); + +struct pisp_fe_input_buffer_config { + __u32 addr_lo; + __u32 addr_hi; + __u16 frame_id; + __u16 pad; +} __attribute__((packed)); + +#define PISP_FE_DECOMPAND_LUT_SIZE 65 + +struct pisp_fe_decompand_config { + __u16 lut[PISP_FE_DECOMPAND_LUT_SIZE]; + __u16 pad; +} __attribute__((packed)); + +struct pisp_fe_dpc_config { + __u8 coeff_level; + __u8 coeff_range; + __u8 coeff_range2; +#define PISP_FE_DPC_FLAG_FOLDBACK 1 +#define PISP_FE_DPC_FLAG_VFLAG 2 + __u8 flags; +} __attribute__((packed)); + +#define PISP_FE_LSC_LUT_SIZE 16 + +struct pisp_fe_lsc_config { + __u8 shift; + __u8 pad0; + __u16 scale; + __u16 centre_x; + __u16 centre_y; + __u16 lut[PISP_FE_LSC_LUT_SIZE]; +} __attribute__((packed)); + +struct pisp_fe_rgby_config { + __u16 gain_r; + __u16 gain_g; + __u16 gain_b; + __u8 maxflag; + __u8 pad; +} __attribute__((packed)); + +struct pisp_fe_agc_stats_config { + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; + /* each weight only 4 bits */ + __u8 weights[PISP_AGC_STATS_NUM_ZONES / 2]; + __u16 row_offset_x; + __u16 row_offset_y; + __u16 row_size_x; + __u16 row_size_y; + __u8 row_shift; + __u8 float_shift; + __u8 pad1[2]; +} __attribute__((packed)); + +struct pisp_fe_awb_stats_config { + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; + __u8 shift; + __u8 pad[3]; + __u16 r_lo; + __u16 r_hi; + __u16 g_lo; + __u16 g_hi; + __u16 b_lo; + __u16 b_hi; +} __attribute__((packed)); + +struct pisp_fe_floating_stats_region { + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; +} __attribute__((packed)); + +struct pisp_fe_floating_stats_config { + struct pisp_fe_floating_stats_region + regions[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); + +#define PISP_FE_CDAF_NUM_WEIGHTS 8 + +struct pisp_fe_cdaf_stats_config { + __u16 noise_constant; + __u16 noise_slope; + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; + __u16 skip_x; + __u16 skip_y; + __u32 mode; +} __attribute__((packed)); + +struct pisp_fe_stats_buffer_config { + __u32 addr_lo; + __u32 addr_hi; +} __attribute__((packed)); + +struct pisp_fe_crop_config { + __u16 offset_x; + __u16 offset_y; + __u16 width; + __u16 height; +} __attribute__((packed)); + +enum pisp_fe_downscale_flags { + /* downscale the four Bayer components independently... */ + DOWNSCALE_BAYER = 1, + /* ...without trying to preserve their spatial relationship */ + DOWNSCALE_BIN = 2, +}; + +struct pisp_fe_downscale_config { + __u8 xin; + __u8 xout; + __u8 yin; + __u8 yout; + __u8 flags; /* enum pisp_fe_downscale_flags */ + __u8 pad[3]; + __u16 output_width; + __u16 output_height; +} __attribute__((packed)); + +struct pisp_fe_output_buffer_config { + __u32 addr_lo; + __u32 addr_hi; +} __attribute__((packed)); + +/* Each of the two output channels/branches: */ +struct pisp_fe_output_branch_config { + struct pisp_fe_crop_config crop; + struct pisp_fe_downscale_config downscale; + struct pisp_compress_config compress; + struct pisp_fe_output_config output; + __u32 pad; +} __attribute__((packed)); + +/* And finally one to rule them all: */ +struct pisp_fe_config { + /* I/O configuration: */ + struct pisp_fe_stats_buffer_config stats_buffer; + struct pisp_fe_output_buffer_config output_buffer[PISP_FE_NUM_OUTPUTS]; + struct pisp_fe_input_buffer_config input_buffer; + /* processing configuration: */ + struct pisp_fe_global_config global; + struct pisp_fe_input_config input; + struct pisp_decompress_config decompress; + struct pisp_fe_decompand_config decompand; + struct pisp_bla_config bla; + struct pisp_fe_dpc_config dpc; + struct pisp_fe_crop_config stats_crop; + __u32 spare1; /* placeholder for future decimate configuration */ + struct pisp_bla_config blc; + struct pisp_fe_rgby_config rgby; + struct pisp_fe_lsc_config lsc; + struct pisp_fe_agc_stats_config agc_stats; + struct pisp_fe_awb_stats_config awb_stats; + struct pisp_fe_cdaf_stats_config cdaf_stats; + struct pisp_fe_floating_stats_config floating_stats; + struct pisp_fe_output_axi_config output_axi; + struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS]; + /* non-register fields: */ + __u32 dirty_flags; /* these use pisp_fe_enable */ + __u32 dirty_flags_extra; /* these use pisp_fe_dirty */ +} __attribute__((packed)); + +#endif /* _UAPI_PISP_FE_CONFIG_ */ diff --git a/include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h b/include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h new file mode 100644 index 00000000000000..a7d42985aee813 --- /dev/null +++ b/include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * RP1 PiSP Front End statistics definitions + * + * Copyright (C) 2021 - Raspberry Pi Ltd. + * + */ +#ifndef _UAPI_PISP_FE_STATISTICS_H_ +#define _UAPI_PISP_FE_STATISTICS_H_ + +#include + +#define PISP_FLOATING_STATS_NUM_ZONES 4 +#define PISP_AGC_STATS_NUM_BINS 1024 +#define PISP_AGC_STATS_SIZE 16 +#define PISP_AGC_STATS_NUM_ZONES (PISP_AGC_STATS_SIZE * PISP_AGC_STATS_SIZE) +#define PISP_AGC_STATS_NUM_ROW_SUMS 512 + +struct pisp_agc_statistics_zone { + __u64 Y_sum; + __u32 counted; + __u32 pad; +} __attribute__((packed)); + +struct pisp_agc_statistics { + __u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS]; + /* + * 32-bits per bin means an image (just less than) 16384x16384 pixels + * in size can weight every pixel from 0 to 15. + */ + __u32 histogram[PISP_AGC_STATS_NUM_BINS]; + struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); + +#define PISP_AWB_STATS_SIZE 32 +#define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE) + +struct pisp_awb_statistics_zone { + __u32 R_sum; + __u32 G_sum; + __u32 B_sum; + __u32 counted; +} __attribute__((packed)); + +struct pisp_awb_statistics { + struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES]; + struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); + +#define PISP_CDAF_STATS_SIZE 8 +#define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE) + +struct pisp_cdaf_statistics { + __u64 foms[PISP_CDAF_STATS_NUM_FOMS]; + __u64 floating[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); + +struct pisp_statistics { + struct pisp_awb_statistics awb; + struct pisp_agc_statistics agc; + struct pisp_cdaf_statistics cdaf; +} __attribute__((packed)); + +#endif /* _UAPI_PISP_FE_STATISTICS_H_ */ From 30342f7b46c43f8dbd1067c1457e973f273b0fb9 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 1 Oct 2024 16:59:23 +0300 Subject: [PATCH 103/159] cfe: fix backporting compile issues (cherry picked from commit b0667aaac1e485efe0c8d9461c60a46e0a0984ec) Signed-off-by: Jacopo Mondi --- drivers/media/platform/raspberrypi/rp1-cfe/cfe.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c index 045910de6c5739..484fc7006bc789 100644 --- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -1025,8 +1025,8 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, node->buffer_queue.type); - if (vq->max_num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->max_num_buffers; + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; if (*nplanes) { if (sizes[0] < size) { @@ -1998,7 +1998,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id) : sizeof(struct cfe_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->lock; - q->min_queued_buffers = 1; + q->min_buffers_needed = 1; q->dev = &cfe->pdev->dev; ret = vb2_queue_init(q); @@ -2426,7 +2426,7 @@ static int cfe_probe(struct platform_device *pdev) return ret; } -static void cfe_remove(struct platform_device *pdev) +static int cfe_remove(struct platform_device *pdev) { struct cfe_device *cfe = platform_get_drvdata(pdev); @@ -2446,6 +2446,8 @@ static void cfe_remove(struct platform_device *pdev) v4l2_device_unregister(&cfe->v4l2_dev); cfe_put(cfe); + + return 0; } static int cfe_runtime_suspend(struct device *dev) From a40c58df805b4ae4b9e10846f5a9b9aca4f7493e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 3 Oct 2024 13:31:13 +0300 Subject: [PATCH 104/159] media: admin-guide: Document the Raspberry Pi CFE (rp1-cfe) Add documentation for rp1-cfe driver. Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab (cherry picked from commit 40249b1d5b3cfea2e8eadd4b5777cf2d82b86d21) Signed-off-by: Jacopo Mondi --- .../admin-guide/media/raspberrypi-rp1-cfe.dot | 27 +++++++ .../admin-guide/media/raspberrypi-rp1-cfe.rst | 78 +++++++++++++++++++ .../admin-guide/media/v4l-drivers.rst | 1 + 3 files changed, 106 insertions(+) create mode 100644 Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot create mode 100644 Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst diff --git a/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot new file mode 100644 index 00000000000000..7717f229104963 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot @@ -0,0 +1,27 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0} | csi2\n/dev/v4l-subdev0 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000011 [style=dashed] + n00000001:port1 -> n00000007:port0 + n00000001:port2 -> n00000015 + n00000001:port2 -> n00000007:port0 [style=dashed] + n00000001:port3 -> n00000019 [style=dashed] + n00000001:port3 -> n00000007:port0 [style=dashed] + n00000001:port4 -> n0000001d [style=dashed] + n00000001:port4 -> n00000007:port0 [style=dashed] + n00000007 [label="{{ 0 | 1} | pisp-fe\n/dev/v4l-subdev1 | { 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port2 -> n00000021 + n00000007:port3 -> n00000025 [style=dashed] + n00000007:port4 -> n00000029 + n0000000d [label="{imx219 6-0010\n/dev/v4l-subdev2 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000d:port0 -> n00000001:port0 [style=bold] + n00000011 [label="rp1-cfe-csi2-ch0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000015 [label="rp1-cfe-csi2-ch1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000019 [label="rp1-cfe-csi2-ch2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001d [label="rp1-cfe-csi2-ch3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000021 [label="rp1-cfe-fe-image0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n00000025 [label="rp1-cfe-fe-image1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000029 [label="rp1-cfe-fe-stats\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n0000002d [label="rp1-cfe-fe-config\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n0000002d -> n00000007:port1 +} diff --git a/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst new file mode 100644 index 00000000000000..668d978a9875b7 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst @@ -0,0 +1,78 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Raspberry Pi PiSP Camera Front End (rp1-cfe) +============================================ + +The PiSP Camera Front End +========================= + +The PiSP Camera Front End (CFE) is a module which combines a CSI-2 receiver with +a simple ISP, called the Front End (FE). + +The CFE has four DMA engines and can write frames from four separate streams +received from the CSI-2 to the memory. One of those streams can also be routed +directly to the FE, which can do minimal image processing, write two versions +(e.g. non-scaled and downscaled versions) of the received frames to memory and +provide statistics of the received frames. + +The FE registers are documented in the `Raspberry Pi Image Signal Processor +(ISP) Specification document +`_, +and example code for FE can be found in `libpisp +`_. + +The rp1-cfe driver +================== + +The Raspberry Pi PiSP Camera Front End (rp1-cfe) driver is located under +drivers/media/platform/raspberrypi/rp1-cfe. It uses the `V4L2 API` to register +a number of video capture and output devices, the `V4L2 subdev API` to register +subdevices for the CSI-2 received and the FE that connects the video devices in +a single media graph realized using the `Media Controller (MC) API`. + +The media topology registered by the `rp1-cfe` driver, in this particular +example connected to an imx219 sensor, is the following one: + +.. _rp1-cfe-topology: + +.. kernel-figure:: raspberrypi-rp1-cfe.dot + :alt: Diagram of an example media pipeline topology + :align: center + +The media graph contains the following video device nodes: + +- rp1-cfe-csi2-ch0: capture device for the first CSI-2 stream +- rp1-cfe-csi2-ch1: capture device for the second CSI-2 stream +- rp1-cfe-csi2-ch2: capture device for the third CSI-2 stream +- rp1-cfe-csi2-ch3: capture device for the fourth CSI-2 stream +- rp1-cfe-fe-image0: capture device for the first FE output +- rp1-cfe-fe-image1: capture device for the second FE output +- rp1-cfe-fe-stats: capture device for the FE statistics +- rp1-cfe-fe-config: output device for FE configuration + +rp1-cfe-csi2-chX +---------------- + +The rp1-cfe-csi2-chX capture devices are normal V4L2 capture devices which +can be used to capture video frames or metadata received from the CSI-2. + +rp1-cfe-fe-image0, rp1-cfe-fe-image1 +------------------------------------ + +The rp1-cfe-fe-image0 and rp1-cfe-fe-image1 capture devices are used to write +the processed frames to memory. + +rp1-cfe-fe-stats +---------------- + +The format of the FE statistics buffer is defined by +:c:type:`pisp_statistics` C structure and the meaning of each parameter is +described in the `PiSP specification` document. + +rp1-cfe-fe-config +----------------- + +The format of the FE configuration buffer is defined by +:c:type:`pisp_fe_config` C structure and the meaning of each parameter is +described in the `PiSP specification` document. diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 53e075cbdf7061..9a4f9ac0e6c214 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -24,6 +24,7 @@ Video4Linux (V4L) driver-specific documentation raspberrypi-pisp-be rcar-fdp1 rkisp1 + raspberrypi-rp1-cfe saa7134 si470x si4713 From 657c2cdf977abf1251ccdcc0f2dc5dd2fd79789e Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 23 Oct 2024 14:45:38 +0530 Subject: [PATCH 105/159] media: bcm2835-unicam: Pull in driver from mainline The mainline driver has support for v4l2 streams, which is used to capture two streams from the sensor (image and embedded data) Replace the BSP driver in drivers/media/platform/bcm2835 with the mainline version, checked-out at revision 8771b7f31b7f ("media: bcm2835-unicam: Depend on COMMON_CLK"). Signed-off-by: Jai Luthra Signed-off-by: Jacopo Mondi --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 2 +- .../media/platform/bcm2835/bcm2835-unicam.c | 3528 ----------------- .../platform/{bcm2835 => broadcom}/Kconfig | 10 +- .../platform/{bcm2835 => broadcom}/Makefile | 2 +- .../bcm2835-unicam-regs.h} | 143 +- .../media/platform/broadcom/bcm2835-unicam.c | 2739 +++++++++++++ 8 files changed, 2817 insertions(+), 3611 deletions(-) delete mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c rename drivers/media/platform/{bcm2835 => broadcom}/Kconfig (92%) rename drivers/media/platform/{bcm2835 => broadcom}/Makefile (60%) rename drivers/media/platform/{bcm2835/vc4-regs-unicam.h => broadcom/bcm2835-unicam-regs.h} (71%) create mode 100644 drivers/media/platform/broadcom/bcm2835-unicam.c diff --git a/MAINTAINERS b/MAINTAINERS index 4ce399893a3f86..ac1416a5dccb01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3943,7 +3943,7 @@ BROADCOM BCM2835 CAMERA DRIVER M: Raspberry Pi Kernel Maintenance L: linux-media@vger.kernel.org S: Maintained -F: drivers/media/platform/bcm2835/ +F: drivers/media/platform/broadcom/bcm2835* F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml BROADCOM BCM2835 ISP DRIVER diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8f9acc325cc8a2..31152a89cafac9 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -67,7 +67,7 @@ source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/aspeed/Kconfig" source "drivers/media/platform/atmel/Kconfig" -source "drivers/media/platform/bcm2835/Kconfig" +source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" source "drivers/media/platform/intel/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index eab239b4466a9b..48dba05e890f68 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -10,7 +10,7 @@ obj-y += amlogic/ obj-y += amphion/ obj-y += aspeed/ obj-y += atmel/ -obj-y += bcm2835/ +obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ obj-y += intel/ diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c deleted file mode 100644 index a6ad0c0fbef64a..00000000000000 --- a/drivers/media/platform/bcm2835/bcm2835-unicam.c +++ /dev/null @@ -1,3528 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * BCM283x / BCM271x Unicam Capture Driver - * - * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. - * - * Dave Stevenson - * - * Based on TI am437x driver by - * Benoit Parrot - * Lad, Prabhakar - * - * and TI CAL camera interface driver by - * Benoit Parrot - * - * - * There are two camera drivers in the kernel for BCM283x - this one - * and bcm2835-camera (currently in staging). - * - * This driver directly controls the Unicam peripheral - there is no - * involvement with the VideoCore firmware. Unicam receives CSI-2 or - * CCP2 data and writes it into SDRAM. - * The only potential processing options are to repack Bayer data into an - * alternate format, and applying windowing. - * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P - * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, - * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both - * formats where the relevant formats are defined, and will automatically - * configure the repacking as required. - * Support for windowing may be added later. - * - * It should be possible to connect this driver to any sensor with a - * suitable output interface and V4L2 subdevice driver. - * - * bcm2835-camera uses the VideoCore firmware to control the sensor, - * Unicam, ISP, and all tuner control loops. Fully processed frames are - * delivered to the driver by the firmware. It only has sensor drivers - * for Omnivision OV5647, and Sony IMX219 sensors. - * - * The two drivers are mutually exclusive for the same Unicam instance. - * The VideoCore firmware checks the device tree configuration during boot. - * If it finds device tree nodes called csi0 or csi1 it will block the - * firmware from accessing the peripheral, and bcm2835-camera will - * not be able to stream data. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "vc4-regs-unicam.h" - -#define UNICAM_MODULE_NAME "unicam" -#define UNICAM_VERSION "0.1.0" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-3"); - -static int media_controller; -module_param(media_controller, int, 0644); -MODULE_PARM_DESC(media_controller, "Use media controller API"); - -#define unicam_dbg(level, dev, fmt, arg...) \ - v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg) -#define unicam_info(dev, fmt, arg...) \ - v4l2_info(&(dev)->v4l2_dev, fmt, ##arg) -#define unicam_err(dev, fmt, arg...) \ - v4l2_err(&(dev)->v4l2_dev, fmt, ##arg) - -/* - * Unicam must request a minimum of 250Mhz from the VPU clock. - * Otherwise the input FIFOs overrun and cause image corruption. - */ -#define MIN_VPU_CLOCK_RATE (250 * 1000 * 1000) -/* - * To protect against a dodgy sensor driver never returning an error from - * enum_mbus_code, set a maximum index value to be used. - */ -#define MAX_ENUM_MBUS_CODE 128 - -/* - * Stride is a 16 bit register, but also has to be a multiple of 32. - */ -#define BPL_ALIGNMENT 32 -#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT) -/* - * Max width is therefore determined by the max stride divided by - * the number of bits per pixel. Take 32bpp as a - * worst case. - * No imposed limit on the height, so adopt a square image for want - * of anything better. - */ -#define MAX_WIDTH (MAX_BYTESPERLINE / 4) -#define MAX_HEIGHT MAX_WIDTH -/* Define a nominal minimum image size */ -#define MIN_WIDTH 16 -#define MIN_HEIGHT 16 -/* Default size of the embedded buffer */ -#define UNICAM_EMBEDDED_SIZE 16384 - -/* - * Size of the dummy buffer allocation. - * - * Due to a HW bug causing buffer overruns in circular buffer mode under certain - * (not yet fully known) conditions, the dummy buffer allocation is set to a - * a single page size, but the hardware gets programmed with a buffer size of 0. - */ -#define DUMMY_BUF_SIZE (PAGE_SIZE) - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - MAX_NODES -}; - -#define MASK_CS_DEFAULT BIT(V4L2_COLORSPACE_DEFAULT) -#define MASK_CS_SMPTE170M BIT(V4L2_COLORSPACE_SMPTE170M) -#define MASK_CS_SMPTE240M BIT(V4L2_COLORSPACE_SMPTE240M) -#define MASK_CS_REC709 BIT(V4L2_COLORSPACE_REC709) -#define MASK_CS_BT878 BIT(V4L2_COLORSPACE_BT878) -#define MASK_CS_470_M BIT(V4L2_COLORSPACE_470_SYSTEM_M) -#define MASK_CS_470_BG BIT(V4L2_COLORSPACE_470_SYSTEM_BG) -#define MASK_CS_JPEG BIT(V4L2_COLORSPACE_JPEG) -#define MASK_CS_SRGB BIT(V4L2_COLORSPACE_SRGB) -#define MASK_CS_OPRGB BIT(V4L2_COLORSPACE_OPRGB) -#define MASK_CS_BT2020 BIT(V4L2_COLORSPACE_BT2020) -#define MASK_CS_RAW BIT(V4L2_COLORSPACE_RAW) -#define MASK_CS_DCI_P3 BIT(V4L2_COLORSPACE_DCI_P3) - -#define MAX_COLORSPACE 32 - -/* - * struct unicam_fmt - Unicam media bus format information - * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. - * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded - * out to 16bpp. 0 if n/a. - * @code: V4L2 media bus format code. - * @depth: Bits per pixel as delivered from the source. - * @csi_dt: CSI data type. - * @valid_colorspaces: Bitmask of valid colorspaces so that the Media Controller - * centric try_fmt can validate the colorspace and pass - * v4l2-compliance. - * @check_variants: Flag to denote that there are multiple mediabus formats - * still in the list that could match this V4L2 format. - * @mc_skip: Media Controller shouldn't list this format via ENUM_FMT as it is - * a duplicate of an earlier format. - * @metadata_fmt: This format only applies to the metadata pad. - */ -struct unicam_fmt { - u32 fourcc; - u32 repacked_fourcc; - u32 code; - u8 depth; - u8 csi_dt; - u32 valid_colorspaces; - u8 check_variants:1; - u8 mc_skip:1; - u8 metadata_fmt:1; -}; - -static const struct unicam_fmt formats[] = { - /* YUV Formats */ - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - /* RGB Formats */ - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_BGR888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .depth = 32, - .csi_dt = 0x0, - .valid_colorspaces = MASK_CS_SRGB, - }, { - /* Bayer Formats */ - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR14P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR14, - .code = MEDIA_BUS_FMT_SBGGR14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG14P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG14, - .code = MEDIA_BUS_FMT_SGBRG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG14P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG14, - .code = MEDIA_BUS_FMT_SGRBG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB14P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB14, - .code = MEDIA_BUS_FMT_SRGGB14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR16, - .code = MEDIA_BUS_FMT_SBGGR16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG16, - .code = MEDIA_BUS_FMT_SGBRG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG16, - .code = MEDIA_BUS_FMT_SGRBG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB16, - .code = MEDIA_BUS_FMT_SRGGB16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - - /* Greyscale formats */ - .fourcc = V4L2_PIX_FMT_GREY, - .code = MEDIA_BUS_FMT_Y8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y10P, - .repacked_fourcc = V4L2_PIX_FMT_Y10, - .code = MEDIA_BUS_FMT_Y10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y12P, - .repacked_fourcc = V4L2_PIX_FMT_Y12, - .code = MEDIA_BUS_FMT_Y12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y14P, - .repacked_fourcc = V4L2_PIX_FMT_Y14, - .code = MEDIA_BUS_FMT_Y14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y16, - .code = MEDIA_BUS_FMT_Y16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, - /* Embedded data format */ - { - .fourcc = V4L2_META_FMT_SENSOR_DATA, - .code = MEDIA_BUS_FMT_SENSOR_DATA, - .depth = 8, - .metadata_fmt = 1, - } -}; - -struct unicam_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) -{ - return container_of(vb, struct unicam_buffer, vb.vb2_buf); -} - -struct unicam_node { - bool registered; - int open; - bool streaming; - unsigned int pad_id; - /* Source pad id on the sensor for this node */ - unsigned int src_pad_id; - /* Pointer pointing to current v4l2_buffer */ - struct unicam_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct unicam_buffer *next_frm; - /* video capture */ - const struct unicam_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - /* Buffer queue used in video-buf */ - struct vb2_queue buffer_queue; - /* Queue of filled frames */ - struct list_head dma_queue; - /* IRQ lock for DMA queue */ - spinlock_t dma_queue_lock; - /* lock used to access this structure */ - struct mutex lock; - /* Identifies video device for this channel */ - struct video_device video_dev; - /* Pointer to the parent handle */ - struct unicam_device *dev; - struct media_pad pad; - unsigned int embedded_lines; - struct media_pipeline pipe; - /* - * Dummy buffer intended to be used by unicam - * if we have no other queued buffers to swap to. - */ - void *dummy_buf_cpu_addr; - dma_addr_t dummy_buf_dma_addr; -}; - -struct unicam_device { - struct kref kref; - - /* V4l2 specific parameters */ - struct v4l2_async_connection *asd; - - /* peripheral base address */ - void __iomem *base; - /* clock gating base address */ - void __iomem *clk_gate_base; - /* lp clock handle */ - struct clk *clock; - /* vpu clock handle */ - struct clk *vpu_clock; - /* clock status for error handling */ - bool clocks_enabled; - /* V4l2 device */ - struct v4l2_device v4l2_dev; - struct media_device mdev; - - struct gpio_desc *sync_gpio; - - /* parent device */ - struct platform_device *pdev; - /* subdevice async Notifier */ - struct v4l2_async_notifier notifier; - unsigned int sequence; - bool frame_started; - - /* ptr to sub device */ - struct v4l2_subdev *sensor; - /* Pad config for the sensor */ - struct v4l2_subdev_state *sensor_state; - - enum v4l2_mbus_type bus_type; - /* - * Stores bus.mipi_csi2.flags for CSI2 sensors, or - * bus.mipi_csi1.strobe for CCP2. - */ - unsigned int bus_flags; - unsigned int max_data_lanes; - unsigned int active_data_lanes; - bool sensor_embedded_data; - - struct unicam_node node[MAX_NODES]; - struct v4l2_ctrl_handler ctrl_handler; - - bool mc_api; -}; - -static inline struct unicam_device * -to_unicam_device(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct unicam_device, v4l2_dev); -} - -/* Hardware access */ -static inline void clk_write(struct unicam_device *dev, u32 val) -{ - writel(val | 0x5a000000, dev->clk_gate_base); -} - -static inline u32 reg_read(struct unicam_device *dev, u32 offset) -{ - return readl(dev->base + offset); -} - -static inline void reg_write(struct unicam_device *dev, u32 offset, u32 val) -{ - writel(val, dev->base + offset); -} - -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -static inline u32 reg_read_field(struct unicam_device *dev, u32 offset, - u32 mask) -{ - return get_field(reg_read(dev, offset), mask); -} - -static inline void reg_write_field(struct unicam_device *dev, u32 offset, - u32 field, u32 mask) -{ - u32 val = reg_read(dev, offset); - - set_field(&val, field, mask); - reg_write(dev, offset, val); -} - -/* Power management functions */ -static inline int unicam_runtime_get(struct unicam_device *dev) -{ - return pm_runtime_get_sync(&dev->pdev->dev); -} - -static inline void unicam_runtime_put(struct unicam_device *dev) -{ - pm_runtime_put_sync(&dev->pdev->dev); -} - -/* Format setup functions */ -static const struct unicam_fmt *find_format_by_code(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].code == code) - return &formats[i]; - } - - return NULL; -} - -static int check_mbus_format(struct unicam_device *dev, - const struct unicam_fmt *format) -{ - unsigned int i; - int ret = 0; - - for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = i, - .pad = IMAGE_PAD, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - - if (!ret && mbus_code.code == format->code) - return 1; - } - - return 0; -} - -static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev, - u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].fourcc == pixelformat || - formats[i].repacked_fourcc == pixelformat) { - if (formats[i].check_variants && - !check_mbus_format(dev, &formats[i])) - continue; - return &formats[i]; - } - } - - return NULL; -} - -static unsigned int bytes_per_line(u32 width, const struct unicam_fmt *fmt, - u32 v4l2_fourcc) -{ - if (v4l2_fourcc == fmt->repacked_fourcc) - /* Repacking always goes to 16bpp */ - return ALIGN(width << 1, BPL_ALIGNMENT); - else - return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT); -} - -static int __subdev_get_format(struct unicam_device *dev, - struct v4l2_mbus_framefmt *fmt, int pad_id) -{ - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = dev->node[pad_id].src_pad_id, - }; - int ret; - - ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_state, - &sd_fmt); - if (ret < 0) - return ret; - - *fmt = sd_fmt.format; - - unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct unicam_device *dev, - struct v4l2_mbus_framefmt *fmt, int pad_id) -{ - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = dev->node[pad_id].src_pad_id, - }; - int ret; - - sd_fmt.format = *fmt; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_state, - &sd_fmt); - if (ret < 0) - return ret; - - *fmt = sd_fmt.format; - - if (pad_id == IMAGE_PAD) - unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width, - fmt->height, fmt->code); - else - unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__, - sd_fmt.format.code); - - return 0; -} - -static int unicam_calc_format_size_bpl(struct unicam_device *dev, - const struct unicam_fmt *fmt, - struct v4l2_format *f) -{ - unsigned int min_bytesperline; - - v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, - &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, - 0); - - min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt, - f->fmt.pix.pixelformat); - - if (f->fmt.pix.bytesperline > min_bytesperline && - f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) - f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, - BPL_ALIGNMENT); - else - f->fmt.pix.bytesperline = min_bytesperline; - - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - - unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n", - __func__, - f->fmt.pix.pixelformat, - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int unicam_reset_format(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) { - ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); - if (ret) { - unicam_err(dev, "Failed to get_format - ret %d\n", ret); - return ret; - } - - if (mbus_fmt.code != node->fmt->code) { - unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", - node->fmt->code, mbus_fmt.code); - return ret; - } - } - - if (node->pad_id == IMAGE_PAD) { - v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt); - node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt); - } else { - node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; - node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - if (dev->sensor_embedded_data) { - node->v_fmt.fmt.meta.buffersize = - mbus_fmt.width * mbus_fmt.height; - node->embedded_lines = mbus_fmt.height; - } else { - node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; - node->embedded_lines = 1; - } - } - - node->m_fmt = mbus_fmt; - return 0; -} - -static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, - unsigned int buffer_size, int pad_id) -{ - dma_addr_t endaddr = dmaaddr + buffer_size; - - if (pad_id == IMAGE_PAD) { - reg_write(dev, UNICAM_IBSA0, dmaaddr); - reg_write(dev, UNICAM_IBEA0, endaddr); - } else { - reg_write(dev, UNICAM_DBSA0, dmaaddr); - reg_write(dev, UNICAM_DBEA0, endaddr); - } -} - -static unsigned int unicam_get_lines_done(struct unicam_device *dev) -{ - dma_addr_t start_addr, cur_addr; - unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline; - struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm; - - if (!frm) - return 0; - - start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); - cur_addr = reg_read(dev, UNICAM_IBWP); - return (unsigned int)(cur_addr - start_addr) / stride; -} - -static void unicam_schedule_next_buffer(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - struct unicam_buffer *buf; - unsigned int size; - dma_addr_t addr; - - buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); - node->next_frm = buf; - list_del(&buf->list); - - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - size = (node->pad_id == IMAGE_PAD) ? - node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - - unicam_wr_dma_addr(dev, addr, size, node->pad_id); -} - -static void unicam_schedule_dummy_buffer(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - - unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n", - node->pad_id); - - unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, 0, node->pad_id); - node->next_frm = NULL; -} - -static void unicam_process_buffer_complete(struct unicam_node *node, - unsigned int sequence) -{ - node->cur_frm->vb.field = node->m_fmt.field; - node->cur_frm->vb.sequence = sequence; - - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); -} - -static void unicam_queue_event_sof(struct unicam_device *unicam) -{ - struct v4l2_event event = { - .type = V4L2_EVENT_FRAME_SYNC, - .u.frame_sync.frame_sequence = unicam->sequence, - }; - - v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event); -} - -/* - * unicam_isr : ISR handler for unicam capture - * @irq: irq number - * @dev_id: dev_id ptr - * - * It changes status of the captured buffer, takes next buffer from the queue - * and sets its address in unicam registers - */ -static irqreturn_t unicam_isr(int irq, void *dev) -{ - struct unicam_device *unicam = dev; - unsigned int lines_done = unicam_get_lines_done(dev); - unsigned int sequence = unicam->sequence; - unsigned int i; - u32 ista, sta; - bool fe; - u64 ts; - - sta = reg_read(unicam, UNICAM_STA); - /* Write value back to clear the interrupts */ - reg_write(unicam, UNICAM_STA, sta); - - ista = reg_read(unicam, UNICAM_ISTA); - /* Write value back to clear the interrupts */ - reg_write(unicam, UNICAM_ISTA, ista); - - unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d", - ista, sta, sequence, lines_done); - - if (!(sta & (UNICAM_IS | UNICAM_PI0))) - return IRQ_HANDLED; - - /* - * Look for either the Frame End interrupt or the Packet Capture status - * to signal a frame end. - */ - fe = (ista & UNICAM_FEI || sta & UNICAM_PI0); - - /* - * We must run the frame end handler first. If we have a valid next_frm - * and we get a simultaneout FE + FS interrupt, running the FS handler - * first would null out the next_frm ptr and we would have lost the - * buffer forever. - */ - if (fe) { - bool inc_seq = unicam->frame_started; - - if (unicam->sync_gpio) - gpiod_set_value(unicam->sync_gpio, 0); - /* - * Ensure we have swapped buffers already as we can't - * stop the peripheral. If no buffer is available, use a - * dummy buffer to dump out frames until we get a new buffer - * to use. - */ - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - struct unicam_node *node = &unicam->node[i]; - - if (!node->streaming) - continue; - - /* - * If cur_frm == next_frm, it means we have not had - * a chance to swap buffers, likely due to having - * multiple interrupts occurring simultaneously (like FE - * + FS + LS). In this case, we cannot signal the buffer - * as complete, as the HW will reuse that buffer. - */ - if (node->cur_frm && node->cur_frm != node->next_frm) { - /* - * This condition checks if FE + FS for the same - * frame has occurred. In such cases, we cannot - * return out the frame, as no buffer handling - * or timestamping has yet been done as part of - * the FS handler. - */ - if (!node->cur_frm->vb.vb2_buf.timestamp) { - unicam_dbg(2, unicam, "ISR: FE without FS, dropping frame\n"); - continue; - } - - unicam_process_buffer_complete(node, sequence); - node->cur_frm = node->next_frm; - node->next_frm = NULL; - inc_seq = true; - } else { - node->cur_frm = node->next_frm; - } - } - - /* - * Increment the sequence number conditionally on either a FS - * having already occurred, or in the FE + FS condition as - * caught in the FE handler above. This ensures the sequence - * number corresponds to the frames generated by the sensor, not - * the frames dequeued to userland. - */ - if (inc_seq) { - unicam->sequence++; - unicam->frame_started = false; - } - } - - if (ista & UNICAM_FSI) { - /* - * Timestamp is to be when the first data byte was captured, - * aka frame start. - */ - ts = ktime_get_ns(); - - if (unicam->sync_gpio) - gpiod_set_value(unicam->sync_gpio, 1); - - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - if (!unicam->node[i].streaming) - continue; - - if (unicam->node[i].cur_frm) - unicam->node[i].cur_frm->vb.vb2_buf.timestamp = - ts; - else - unicam_dbg(2, unicam, "ISR: [%d] Dropping frame, buffer not available at FS\n", - i); - /* - * Set the next frame output to go to a dummy frame - * if no buffer currently queued. - */ - if (!unicam->node[i].next_frm || - unicam->node[i].next_frm == unicam->node[i].cur_frm) { - unicam_schedule_dummy_buffer(&unicam->node[i]); - } else if (unicam->node[i].cur_frm) { - /* - * Repeated FS without FE. Hardware will have - * swapped buffers, but the cur_frm doesn't - * contain valid data. Return cur_frm to the - * queue. - */ - spin_lock(&unicam->node[i].dma_queue_lock); - list_add_tail(&unicam->node[i].cur_frm->list, - &unicam->node[i].dma_queue); - spin_unlock(&unicam->node[i].dma_queue_lock); - unicam->node[i].cur_frm = unicam->node[i].next_frm; - unicam->node[i].next_frm = NULL; - } - } - - unicam_queue_event_sof(unicam); - unicam->frame_started = true; - } - - /* - * Cannot swap buffer at frame end, there may be a race condition - * where the HW does not actually swap it if the new frame has - * already started. - */ - if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) { - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - if (!unicam->node[i].streaming) - continue; - - spin_lock(&unicam->node[i].dma_queue_lock); - if (!list_empty(&unicam->node[i].dma_queue) && - !unicam->node[i].next_frm) - unicam_schedule_next_buffer(&unicam->node[i]); - spin_unlock(&unicam->node[i].dma_queue_lock); - } - } - - return IRQ_HANDLED; -} - -/* V4L2 Common IOCTLs */ -static int unicam_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(&dev->pdev->dev)); - - cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; - - return 0; -} - -static int unicam_log_status(struct file *file, void *fh) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - u32 reg; - - /* status for sub devices */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status); - - unicam_info(dev, "-----Receiver status-----\n"); - unicam_info(dev, "V4L2 width/height: %ux%u\n", - node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height); - unicam_info(dev, "Mediabus format: %08x\n", node->fmt->code); - unicam_info(dev, "V4L2 format: %08x\n", - node->v_fmt.fmt.pix.pixelformat); - reg = reg_read(dev, UNICAM_IPIPE); - unicam_info(dev, "Unpacking/packing: %u / %u\n", - get_field(reg, UNICAM_PUM_MASK), - get_field(reg, UNICAM_PPM_MASK)); - unicam_info(dev, "----Live data----\n"); - unicam_info(dev, "Programmed stride: %4u\n", - reg_read(dev, UNICAM_IBLS)); - unicam_info(dev, "Detected resolution: %ux%u\n", - reg_read(dev, UNICAM_IHSTA), - reg_read(dev, UNICAM_IVSTA)); - unicam_info(dev, "Write pointer: %08x\n", - reg_read(dev, UNICAM_IBWP)); - - return 0; -} - -/* V4L2 Video Centric IOCTLs */ -static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - unsigned int index = 0; - unsigned int i; - int ret = 0; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = i, - .pad = IMAGE_PAD, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - const struct unicam_fmt *fmt; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx %d returned %d - index invalid\n", - i, ret); - return -EINVAL; - } - - fmt = find_format_by_code(mbus_code.code); - if (fmt) { - if (fmt->fourcc) { - if (index == f->index) { - f->pixelformat = fmt->fourcc; - break; - } - index++; - } - if (fmt->repacked_fourcc) { - if (index == f->index) { - f->pixelformat = fmt->repacked_fourcc; - break; - } - index++; - } - } - } - - return 0; -} - -static int unicam_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_mbus_framefmt mbus_fmt = {0}; - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt = NULL; - int ret; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - /* - * If a flip has occurred in the sensor, the fmt code might have - * changed. So we will need to re-fetch the format from the subdevice. - */ - ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); - if (ret) - return -EINVAL; - - /* Find the V4L2 format from mbus code. We must match a known format. */ - fmt = find_format_by_code(mbus_fmt.code); - if (!fmt) - return -EINVAL; - - if (node->fmt != fmt) { - /* - * The sensor format has changed so the pixelformat needs to - * be updated. Try and retain the packed/unpacked choice if - * at all possible. - */ - if (node->fmt->repacked_fourcc == - node->v_fmt.fmt.pix.pixelformat) - /* Using the repacked format */ - node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; - else - /* Using the native format */ - node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - - node->fmt = fmt; - } - - *f = node->v_fmt; - - return 0; -} - -static const struct unicam_fmt * -get_first_supported_format(struct unicam_device *dev) -{ - struct v4l2_subdev_mbus_code_enum mbus_code; - const struct unicam_fmt *fmt = NULL; - unsigned int i; - int ret = 0; - - for (i = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++i) { - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = i; - mbus_code.pad = IMAGE_PAD; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, - &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx %u returned %d - continue\n", - i, ret); - continue; - } - - unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %u\n", - dev->sensor->name, mbus_code.code, i); - - fmt = find_format_by_code(mbus_code.code); - unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n", - mbus_code.code, fmt, fmt ? fmt->fourcc : 0, - fmt ? fmt->csi_dt : 0); - if (fmt) - return fmt; - } - - return NULL; -} - -static int unicam_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_TRY, - .pad = IMAGE_PAD - }; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - const struct unicam_fmt *fmt; - int ret; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); - if (!fmt) { - /* - * Pixel format not supported by unicam. Choose the first - * supported format, and let the sensor choose something else. - */ - unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n", - f->fmt.pix.pixelformat); - - fmt = &formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code); - /* - * No support for receiving interlaced video, so never - * request it from the sensor subdev. - */ - mbus_fmt->field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_state, - &sd_fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - if (mbus_fmt->field != V4L2_FIELD_NONE) - unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); - - v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); - if (mbus_fmt->code != fmt->code) { - /* Sensor has returned an alternate format */ - fmt = find_format_by_code(mbus_fmt->code); - if (!fmt) { - /* - * The alternate format is one unicam can't support. - * Find the first format that is supported by both, and - * then set that. - */ - fmt = get_first_supported_format(dev); - mbus_fmt->code = fmt->code; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, - dev->sensor_state, &sd_fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - if (mbus_fmt->field != V4L2_FIELD_NONE) - unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); - - v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); - - if (mbus_fmt->code != fmt->code) { - /* - * We've set a format that the sensor reports - * as being supported, but it refuses to set it. - * Not much else we can do. - * Assume that the sensor driver may accept the - * format when it is set (rather than tried). - */ - unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n"); - } - } - - if (fmt->fourcc) - f->fmt.pix.pixelformat = fmt->fourcc; - else - f->fmt.pix.pixelformat = fmt->repacked_fourcc; - } - - return unicam_calc_format_size_bpl(dev, fmt, f); -} - -static int unicam_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct vb2_queue *q = &node->buffer_queue; - struct v4l2_mbus_framefmt mbus_fmt = {0}; - const struct unicam_fmt *fmt; - int ret; - - if (vb2_is_busy(q)) - return -EBUSY; - - ret = unicam_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); - if (!fmt) { - /* - * Unknown pixel format - adopt a default. - * This shouldn't happen as try_fmt should have resolved any - * issues first. - */ - fmt = get_first_supported_format(dev); - if (!fmt) - /* - * It shouldn't be possible to get here with no - * supported formats - */ - return -EINVAL; - f->fmt.pix.pixelformat = fmt->fourcc; - return -EINVAL; - } - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); - if (ret) { - unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", - __func__, ret); - return ret; - } - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - unicam_dbg(3, dev, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - node->fmt = fmt; - node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; - node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; - unicam_reset_format(node); - - unicam_dbg(3, dev, - "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", - __func__, node->v_fmt.fmt.pix.width, - node->v_fmt.fmt.pix.height, mbus_fmt.code, - node->v_fmt.fmt.pix.pixelformat); - - *f = node->v_fmt; - - return 0; -} - -static int unicam_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - u32 code; - int ret = 0; - - if (node->pad_id != METADATA_PAD || f->index != 0) - return -EINVAL; - - if (dev->sensor_embedded_data) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = f->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = METADATA_PAD, - }; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, - &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx 0 returned %d - index invalid\n", - ret); - return -EINVAL; - } - - code = mbus_code.code; - } else { - code = MEDIA_BUS_FMT_SENSOR_DATA; - } - - fmt = find_format_by_code(code); - if (fmt) - f->pixelformat = fmt->fourcc; - - return 0; -} - -static int unicam_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static int unicam_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) { - inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; - inp->std = 0; - } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) { - inp->capabilities = V4L2_IN_CAP_STD; - if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std) < 0) - inp->std = V4L2_STD_ALL; - } else { - inp->capabilities = 0; - inp->std = 0; - } - - if (v4l2_subdev_has_op(dev->sensor, video, g_input_status)) { - ret = v4l2_subdev_call(dev->sensor, video, g_input_status, - &inp->status); - if (ret < 0) - return ret; - } - - snprintf(inp->name, sizeof(inp->name), "Camera 0"); - return 0; -} - -static int unicam_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int unicam_s_input(struct file *file, void *priv, unsigned int i) -{ - /* - * FIXME: Ideally we would like to be able to query the source - * subdevice for information over the input connectors it supports, - * and map that through in to a call to video_ops->s_routing. - * There is no infrastructure support for defining that within - * devicetree at present. Until that is implemented we can't - * map a user physical connector number to s_routing input number. - */ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int unicam_querystd(struct file *file, void *priv, - v4l2_std_id *std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, querystd, std); -} - -static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, g_std, std); -} - -static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - v4l2_std_id current_std; - - ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std); - if (ret) - return ret; - - if (std == current_std) - return 0; - - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(dev->sensor, video, s_std, std); - - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; - - unicam_reset_format(node); - - return ret; -} - -static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); -} - -static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); -} - -static int unicam_s_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); -} - -static int unicam_g_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); - if (!ret) - sel->r = sdsel.r; - - return ret; -} - -static int unicam_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; - - /* check for valid format */ - fmt = find_format_by_pix(dev, fsize->pixel_format); - if (!fmt) { - unicam_dbg(3, dev, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; - } - fse.code = fmt->code; - - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - fse.index = fsize->index; - fse.pad = node->src_pad_id; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); - if (ret) - return ret; - - unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int unicam_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .pad = node->src_pad_id, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(dev, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; - - return 0; -} - -static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); -} - -static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); -} - -static int unicam_g_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); -} - -static int unicam_s_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_dv_timings current_timings; - int ret; - - ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings, - ¤t_timings); - - if (ret < 0) - return ret; - - if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) - return 0; - - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); - - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; - - unicam_reset_format(node); - - return ret; -} - -static int unicam_query_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); -} - -static int unicam_enum_dv_timings(struct file *file, void *priv, - struct v4l2_enum_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - timings->pad = node->src_pad_id; - ret = v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); - timings->pad = node->pad_id; - - return ret; -} - -static int unicam_dv_timings_cap(struct file *file, void *priv, - struct v4l2_dv_timings_cap *cap) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - cap->pad = node->src_pad_id; - ret = v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); - cap->pad = node->pad_id; - - return ret; -} - -static int unicam_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_FRAME_SYNC: - return v4l2_event_subscribe(fh, sub, 2, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - return v4l2_event_subscribe(fh, sub, 4, NULL); - } - - return v4l2_ctrl_subscribe_event(fh, sub); -} - -static void unicam_notify(struct v4l2_subdev *sd, - unsigned int notification, void *arg) -{ - struct unicam_device *dev = to_unicam_device(sd->v4l2_dev); - - switch (notification) { - case V4L2_DEVICE_NOTIFY_EVENT: - v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg); - break; - default: - break; - } -} - -/* unicam capture ioctl operations */ -static const struct v4l2_ioctl_ops unicam_ioctl_ops = { - .vidioc_querycap = unicam_querycap, - .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, - - .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap, - .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap, - .vidioc_s_fmt_meta_cap = unicam_g_fmt_meta_cap, - .vidioc_try_fmt_meta_cap = unicam_g_fmt_meta_cap, - - .vidioc_enum_input = unicam_enum_input, - .vidioc_g_input = unicam_g_input, - .vidioc_s_input = unicam_s_input, - - .vidioc_querystd = unicam_querystd, - .vidioc_s_std = unicam_s_std, - .vidioc_g_std = unicam_g_std, - - .vidioc_g_edid = unicam_g_edid, - .vidioc_s_edid = unicam_s_edid, - - .vidioc_enum_framesizes = unicam_enum_framesizes, - .vidioc_enum_frameintervals = unicam_enum_frameintervals, - - .vidioc_g_selection = unicam_g_selection, - .vidioc_s_selection = unicam_s_selection, - - .vidioc_g_parm = unicam_g_parm, - .vidioc_s_parm = unicam_s_parm, - - .vidioc_s_dv_timings = unicam_s_dv_timings, - .vidioc_g_dv_timings = unicam_g_dv_timings, - .vidioc_query_dv_timings = unicam_query_dv_timings, - .vidioc_enum_dv_timings = unicam_enum_dv_timings, - .vidioc_dv_timings_cap = unicam_dv_timings_cap, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = unicam_log_status, - .vidioc_subscribe_event = unicam_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* V4L2 Media Controller Centric IOCTLs */ - -static int unicam_mc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { - if (f->mbus_code && formats[i].code != f->mbus_code) - continue; - if (formats[i].mc_skip || formats[i].metadata_fmt) - continue; - - if (formats[i].fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; - } - j++; - } - if (formats[i].repacked_fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].repacked_fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; - } - j++; - } - } - - return -EINVAL; -} - -static int unicam_mc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static void unicam_mc_try_fmt(struct unicam_node *node, struct v4l2_format *f, - const struct unicam_fmt **ret_fmt) -{ - struct v4l2_pix_format *v4l2_format = &f->fmt.pix; - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - int is_rgb; - - /* - * Default to the first format if the requested pixel format code isn't - * supported. - */ - fmt = find_format_by_pix(dev, v4l2_format->pixelformat); - if (!fmt) { - fmt = &formats[0]; - v4l2_format->pixelformat = fmt->fourcc; - } - - unicam_calc_format_size_bpl(dev, fmt, f); - - if (v4l2_format->field == V4L2_FIELD_ANY) - v4l2_format->field = V4L2_FIELD_NONE; - - if (ret_fmt) - *ret_fmt = fmt; - - if (v4l2_format->colorspace >= MAX_COLORSPACE || - !(fmt->valid_colorspaces & (1 << v4l2_format->colorspace))) { - v4l2_format->colorspace = __ffs(fmt->valid_colorspaces); - - v4l2_format->xfer_func = - V4L2_MAP_XFER_FUNC_DEFAULT(v4l2_format->colorspace); - v4l2_format->ycbcr_enc = - V4L2_MAP_YCBCR_ENC_DEFAULT(v4l2_format->colorspace); - is_rgb = v4l2_format->colorspace == V4L2_COLORSPACE_SRGB; - v4l2_format->quantization = - V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, - v4l2_format->colorspace, - v4l2_format->ycbcr_enc); - } - - unicam_dbg(3, dev, "%s: %08x %ux%u (bytesperline %u sizeimage %u)\n", - __func__, v4l2_format->pixelformat, - v4l2_format->width, v4l2_format->height, - v4l2_format->bytesperline, v4l2_format->sizeimage); -} - -static int unicam_mc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - unicam_mc_try_fmt(node, f, NULL); - return 0; -} - -static int unicam_mc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - - if (vb2_is_busy(&node->buffer_queue)) { - unicam_dbg(3, dev, "%s device busy\n", __func__); - return -EBUSY; - } - - unicam_mc_try_fmt(node, f, &fmt); - - node->v_fmt = *f; - node->fmt = fmt; - - return 0; -} - -static int unicam_mc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - if (fsize->index > 0) - return -EINVAL; - - if (!find_format_by_pix(dev, fsize->pixel_format)) { - unicam_dbg(3, dev, "Invalid pixel format 0x%08x\n", - fsize->pixel_format); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = MIN_WIDTH; - fsize->stepwise.max_width = MAX_WIDTH; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = MIN_HEIGHT; - fsize->stepwise.max_height = MAX_HEIGHT; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int unicam_mc_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { - if (f->mbus_code && formats[i].code != f->mbus_code) - continue; - if (!formats[i].metadata_fmt) - continue; - - if (formats[i].fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].fourcc; - f->type = V4L2_BUF_TYPE_META_CAPTURE; - return 0; - } - j++; - } - } - - return -EINVAL; -} - -static int unicam_mc_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static int unicam_mc_try_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - - return 0; -} - -static int unicam_mc_s_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - unicam_mc_try_fmt_meta_cap(file, priv, f); - - node->v_fmt = *f; - - return 0; -} - -static const struct v4l2_ioctl_ops unicam_mc_ioctl_ops = { - .vidioc_querycap = unicam_querycap, - .vidioc_enum_fmt_vid_cap = unicam_mc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = unicam_mc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = unicam_mc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = unicam_mc_s_fmt_vid_cap, - - .vidioc_enum_fmt_meta_cap = unicam_mc_enum_fmt_meta_cap, - .vidioc_g_fmt_meta_cap = unicam_mc_g_fmt_meta_cap, - .vidioc_try_fmt_meta_cap = unicam_mc_try_fmt_meta_cap, - .vidioc_s_fmt_meta_cap = unicam_mc_s_fmt_meta_cap, - - .vidioc_enum_framesizes = unicam_mc_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = unicam_log_status, - .vidioc_subscribe_event = unicam_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int -unicam_mc_subdev_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); - } - - return -EINVAL; -} - -static int unicam_mc_video_link_validate(struct media_link *link) -{ - struct video_device *vd = container_of(link->sink->entity, - struct video_device, entity); - struct unicam_node *node = container_of(vd, struct unicam_node, - video_dev); - struct unicam_device *unicam = node->dev; - struct v4l2_subdev_format source_fmt; - int ret; - - if (!media_entity_remote_source_pad_unique(link->sink->entity)) { - unicam_dbg(1, unicam, - "video node %s pad not connected\n", vd->name); - return -ENOTCONN; - } - - ret = unicam_mc_subdev_link_validate_get_format(link->source, - &source_fmt); - if (ret < 0) - return 0; - - if (node->pad_id == IMAGE_PAD) { - struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; - const struct unicam_fmt *fmt; - - if (source_fmt.format.width != pix_fmt->width || - source_fmt.format.height != pix_fmt->height) { - unicam_err(unicam, - "Wrong width or height %ux%u (remote pad set to %ux%u)\n", - pix_fmt->width, pix_fmt->height, - source_fmt.format.width, - source_fmt.format.height); - return -EINVAL; - } - - fmt = find_format_by_code(source_fmt.format.code); - - if (!fmt || (fmt->fourcc != pix_fmt->pixelformat && - fmt->repacked_fourcc != pix_fmt->pixelformat)) - return -EINVAL; - } else { - struct v4l2_meta_format *meta_fmt = &node->v_fmt.fmt.meta; - - if (source_fmt.format.width != meta_fmt->buffersize || - source_fmt.format.height != 1 || - source_fmt.format.code != MEDIA_BUS_FMT_SENSOR_DATA) { - unicam_err(unicam, - "Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n", - meta_fmt->buffersize, 1, - MEDIA_BUS_FMT_SENSOR_DATA, - source_fmt.format.width, - source_fmt.format.height, - source_fmt.format.code); - return -EINVAL; - } - } - - return 0; -} - -static const struct media_entity_operations unicam_mc_entity_ops = { - .link_validate = unicam_mc_video_link_validate, -}; - -/* videobuf2 Operations */ - -static int unicam_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, - unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - unsigned int size = node->pad_id == IMAGE_PAD ? - node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) { - unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0], - size); - return -EINVAL; - } - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int unicam_buffer_prepare(struct vb2_buffer *vb) -{ - struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct unicam_device *dev = node->dev; - struct unicam_buffer *buf = to_unicam_buffer(vb); - unsigned long size; - - if (WARN_ON(!node->fmt)) - return -EINVAL; - - size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - if (vb2_plane_size(vb, 0) < size) { - unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - return 0; -} - -static void unicam_buffer_queue(struct vb2_buffer *vb) -{ - struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct unicam_buffer *buf = to_unicam_buffer(vb); - unsigned long flags; - - spin_lock_irqsave(&node->dma_queue_lock, flags); - list_add_tail(&buf->list, &node->dma_queue); - spin_unlock_irqrestore(&node->dma_queue_lock, flags); -} - -static void unicam_set_packing_config(struct unicam_device *dev) -{ - u32 pack, unpack; - u32 val; - - if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat == - dev->node[IMAGE_PAD].fmt->fourcc) { - unpack = UNICAM_PUM_NONE; - pack = UNICAM_PPM_NONE; - } else { - switch (dev->node[IMAGE_PAD].fmt->depth) { - case 8: - unpack = UNICAM_PUM_UNPACK8; - break; - case 10: - unpack = UNICAM_PUM_UNPACK10; - break; - case 12: - unpack = UNICAM_PUM_UNPACK12; - break; - case 14: - unpack = UNICAM_PUM_UNPACK14; - break; - case 16: - unpack = UNICAM_PUM_UNPACK16; - break; - default: - unpack = UNICAM_PUM_NONE; - break; - } - - /* Repacking is always to 16bpp */ - pack = UNICAM_PPM_PACK16; - } - - val = 0; - set_field(&val, unpack, UNICAM_PUM_MASK); - set_field(&val, pack, UNICAM_PPM_MASK); - reg_write(dev, UNICAM_IPIPE, val); -} - -static void unicam_cfg_image_id(struct unicam_device *dev) -{ - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 mode, hardcode VC 0 for now. */ - reg_write(dev, UNICAM_IDI0, - (0 << 6) | dev->node[IMAGE_PAD].fmt->csi_dt); - } else { - /* CCP2 mode */ - reg_write(dev, UNICAM_IDI0, - 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt); - } -} - -static void unicam_enable_ed(struct unicam_device *dev) -{ - u32 val = reg_read(dev, UNICAM_DCS); - - set_field(&val, 2, UNICAM_EDL_MASK); - /* Do not wrap at the end of the embedded data buffer */ - set_field(&val, 0, UNICAM_DBOB); - - reg_write(dev, UNICAM_DCS, val); -} - -static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) -{ - int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; - unsigned int size, i; - u32 val; - - if (line_int_freq < 128) - line_int_freq = 128; - - /* Enable lane clocks */ - val = 1; - for (i = 0; i < dev->active_data_lanes; i++) - val = val << 2 | 1; - clk_write(dev, val); - - /* Basic init */ - reg_write(dev, UNICAM_CTRL, UNICAM_MEM); - - /* Enable analogue control, and leave in reset. */ - val = UNICAM_AR; - set_field(&val, 7, UNICAM_CTATADJ_MASK); - set_field(&val, 7, UNICAM_PTATADJ_MASK); - reg_write(dev, UNICAM_ANA, val); - usleep_range(1000, 2000); - - /* Come out of reset */ - reg_write_field(dev, UNICAM_ANA, 0, UNICAM_AR); - - /* Peripheral reset */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); - - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); - - /* Enable Rx control. */ - val = reg_read(dev, UNICAM_CTRL); - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); - set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); - } else { - set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); - set_field(&val, dev->bus_flags, UNICAM_DCM_MASK); - } - /* Packet framer timeout */ - set_field(&val, 0xf, UNICAM_PFT_MASK); - set_field(&val, 128, UNICAM_OET_MASK); - reg_write(dev, UNICAM_CTRL, val); - - reg_write(dev, UNICAM_IHWIN, 0); - reg_write(dev, UNICAM_IVWIN, 0); - - /* AXI bus access QoS setup */ - val = reg_read(dev, UNICAM_PRI); - set_field(&val, 0, UNICAM_BL_MASK); - set_field(&val, 0, UNICAM_BS_MASK); - set_field(&val, 0xe, UNICAM_PP_MASK); - set_field(&val, 8, UNICAM_NP_MASK); - set_field(&val, 2, UNICAM_PT_MASK); - set_field(&val, 1, UNICAM_PE); - reg_write(dev, UNICAM_PRI, val); - - reg_write_field(dev, UNICAM_ANA, 0, UNICAM_DDL); - - val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; - set_field(&val, line_int_freq, UNICAM_LCIE_MASK); - reg_write(dev, UNICAM_ICTL, val); - reg_write(dev, UNICAM_STA, UNICAM_STA_MASK_ALL); - reg_write(dev, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); - - /* tclk_term_en */ - reg_write_field(dev, UNICAM_CLT, 2, UNICAM_CLT1_MASK); - /* tclk_settle */ - reg_write_field(dev, UNICAM_CLT, 6, UNICAM_CLT2_MASK); - /* td_term_en */ - reg_write_field(dev, UNICAM_DLT, 2, UNICAM_DLT1_MASK); - /* ths_settle */ - reg_write_field(dev, UNICAM_DLT, 6, UNICAM_DLT2_MASK); - /* trx_enable */ - reg_write_field(dev, UNICAM_DLT, 0, UNICAM_DLT3_MASK); - - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_SOE); - - /* Packet compare setup - required to avoid missing frame ends */ - val = 0; - set_field(&val, 1, UNICAM_PCE); - set_field(&val, 1, UNICAM_GI); - set_field(&val, 1, UNICAM_CPH); - set_field(&val, 0, UNICAM_PCVC_MASK); - set_field(&val, 1, UNICAM_PCDT_MASK); - reg_write(dev, UNICAM_CMP0, val); - - /* Enable clock lane and set up terminations */ - val = 0; - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 */ - set_field(&val, 1, UNICAM_CLE); - set_field(&val, 1, UNICAM_CLLPE); - if (!(dev->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { - set_field(&val, 1, UNICAM_CLTRE); - set_field(&val, 1, UNICAM_CLHSE); - } - } else { - /* CCP2 */ - set_field(&val, 1, UNICAM_CLE); - set_field(&val, 1, UNICAM_CLHSE); - set_field(&val, 1, UNICAM_CLTRE); - } - reg_write(dev, UNICAM_CLK, val); - - /* - * Enable required data lanes with appropriate terminations. - * The same value needs to be written to UNICAM_DATn registers for - * the active lanes, and 0 for inactive ones. - */ - val = 0; - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 */ - set_field(&val, 1, UNICAM_DLE); - set_field(&val, 1, UNICAM_DLLPE); - if (!(dev->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { - set_field(&val, 1, UNICAM_DLTRE); - set_field(&val, 1, UNICAM_DLHSE); - } - } else { - /* CCP2 */ - set_field(&val, 1, UNICAM_DLE); - set_field(&val, 1, UNICAM_DLHSE); - set_field(&val, 1, UNICAM_DLTRE); - } - reg_write(dev, UNICAM_DAT0, val); - - if (dev->active_data_lanes == 1) - val = 0; - reg_write(dev, UNICAM_DAT1, val); - - if (dev->max_data_lanes > 2) { - /* - * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the - * instance supports more than 2 data lanes. - */ - if (dev->active_data_lanes == 2) - val = 0; - reg_write(dev, UNICAM_DAT2, val); - - if (dev->active_data_lanes == 3) - val = 0; - reg_write(dev, UNICAM_DAT3, val); - } - - reg_write(dev, UNICAM_IBLS, - dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); - size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; - unicam_wr_dma_addr(dev, addr[IMAGE_PAD], size, IMAGE_PAD); - unicam_set_packing_config(dev); - unicam_cfg_image_id(dev); - - val = reg_read(dev, UNICAM_MISC); - set_field(&val, 1, UNICAM_FL0); - set_field(&val, 1, UNICAM_FL1); - reg_write(dev, UNICAM_MISC, val); - - if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { - size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; - unicam_enable_ed(dev); - unicam_wr_dma_addr(dev, addr[METADATA_PAD], size, METADATA_PAD); - } - - /* Enable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE); - - /* Load image pointers */ - reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK); - - /* Load embedded data buffer pointers if needed */ - if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) - reg_write_field(dev, UNICAM_DCS, 1, UNICAM_LDP); -} - -static void unicam_disable(struct unicam_device *dev) -{ - /* Analogue lane control disable */ - reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL); - - /* Stop the output engine */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE); - - /* Disable the data lanes. */ - reg_write(dev, UNICAM_DAT0, 0); - reg_write(dev, UNICAM_DAT1, 0); - - if (dev->max_data_lanes > 2) { - reg_write(dev, UNICAM_DAT2, 0); - reg_write(dev, UNICAM_DAT3, 0); - } - - /* Peripheral reset */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); - usleep_range(50, 100); - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); - - /* Disable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); - - /* Clear ED setup */ - reg_write(dev, UNICAM_DCS, 0); - - /* Disable all lane clocks */ - clk_write(dev, 0); -} - -static void unicam_return_buffers(struct unicam_node *node, - enum vb2_buffer_state state) -{ - struct unicam_buffer *buf, *tmp; - unsigned long flags; - - spin_lock_irqsave(&node->dma_queue_lock, flags); - list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, state); - } - - if (node->cur_frm) - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, - state); - if (node->next_frm && node->cur_frm != node->next_frm) - vb2_buffer_done(&node->next_frm->vb.vb2_buf, - state); - - node->cur_frm = NULL; - node->next_frm = NULL; - spin_unlock_irqrestore(&node->dma_queue_lock, flags); -} - -static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - dma_addr_t buffer_addr[MAX_NODES] = { 0 }; - unsigned long flags; - unsigned int i; - int ret; - - node->streaming = true; - if (!(dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming && - (!dev->node[METADATA_PAD].open || - dev->node[METADATA_PAD].streaming))) { - /* - * Metadata pad must be enabled before image pad if it is - * wanted. - */ - unicam_dbg(3, dev, "Not all nodes are streaming yet."); - return 0; - } - - dev->sequence = 0; - ret = unicam_runtime_get(dev); - if (ret < 0) { - unicam_dbg(3, dev, "unicam_runtime_get failed\n"); - goto err_streaming; - } - - ret = media_pipeline_start(dev->node[IMAGE_PAD].video_dev.entity.pads, - &dev->node[IMAGE_PAD].pipe); - if (ret < 0) { - unicam_err(dev, "Failed to start media pipeline: %d\n", ret); - goto err_pm_put; - } - - dev->active_data_lanes = dev->max_data_lanes; - - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - struct v4l2_mbus_config mbus_config = { 0 }; - - ret = v4l2_subdev_call(dev->sensor, pad, get_mbus_config, - 0, &mbus_config); - if (ret < 0 && ret != -ENOIOCTLCMD) { - unicam_dbg(3, dev, "g_mbus_config failed\n"); - goto error_pipeline; - } - - dev->active_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; - if (!dev->active_data_lanes) - dev->active_data_lanes = dev->max_data_lanes; - if (dev->active_data_lanes > dev->max_data_lanes) { - unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", - dev->active_data_lanes, - dev->max_data_lanes); - ret = -EINVAL; - goto error_pipeline; - } - } - - unicam_dbg(1, dev, "Running with %u data lanes\n", - dev->active_data_lanes); - - ret = clk_set_min_rate(dev->vpu_clock, MIN_VPU_CLOCK_RATE); - if (ret) { - unicam_err(dev, "failed to set up VPU clock\n"); - goto error_pipeline; - } - - ret = clk_prepare_enable(dev->vpu_clock); - if (ret) { - unicam_err(dev, "Failed to enable VPU clock: %d\n", ret); - goto error_pipeline; - } - - ret = clk_set_rate(dev->clock, 100 * 1000 * 1000); - if (ret) { - unicam_err(dev, "failed to set up CSI clock\n"); - goto err_vpu_clock; - } - - ret = clk_prepare_enable(dev->clock); - if (ret) { - unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); - goto err_vpu_clock; - } - - for (i = 0; i < ARRAY_SIZE(dev->node); i++) { - struct unicam_buffer *buf; - - if (!dev->node[i].streaming) - continue; - - spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags); - buf = list_first_entry(&dev->node[i].dma_queue, - struct unicam_buffer, list); - dev->node[i].cur_frm = buf; - dev->node[i].next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags); - - buffer_addr[i] = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - } - - dev->frame_started = false; - unicam_start_rx(dev, buffer_addr); - - ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); - if (ret < 0) { - unicam_err(dev, "stream on failed in subdev\n"); - goto err_disable_unicam; - } - - dev->clocks_enabled = true; - return 0; - -err_disable_unicam: - unicam_disable(dev); - clk_disable_unprepare(dev->clock); -err_vpu_clock: - if (clk_set_min_rate(dev->vpu_clock, 0)) - unicam_err(dev, "failed to reset the VPU clock\n"); - clk_disable_unprepare(dev->vpu_clock); -error_pipeline: - if (node->pad_id == IMAGE_PAD) - media_pipeline_stop(dev->node[IMAGE_PAD].video_dev.entity.pads); -err_pm_put: - unicam_runtime_put(dev); -err_streaming: - unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); - node->streaming = false; - - return ret; -} - -static void unicam_stop_streaming(struct vb2_queue *vq) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - - node->streaming = false; - - if (node->pad_id == IMAGE_PAD) { - /* - * Stop streaming the sensor and disable the peripheral. - * We cannot continue streaming embedded data with the - * image pad disabled. - */ - if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) - unicam_err(dev, "stream off failed in subdev\n"); - - unicam_disable(dev); - - media_pipeline_stop(node->video_dev.entity.pads); - - if (dev->clocks_enabled) { - if (clk_set_min_rate(dev->vpu_clock, 0)) - unicam_err(dev, "failed to reset the min VPU clock\n"); - - clk_disable_unprepare(dev->vpu_clock); - clk_disable_unprepare(dev->clock); - dev->clocks_enabled = false; - } - unicam_runtime_put(dev); - - } else if (node->pad_id == METADATA_PAD) { - /* - * Allow the hardware to spin in the dummy buffer. - * This is only really needed if the embedded data pad is - * disabled before the image pad. - */ - unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, 0, - METADATA_PAD); - } - - /* Clear all queued buffers for the node */ - unicam_return_buffers(node, VB2_BUF_STATE_ERROR); -} - - -static const struct vb2_ops unicam_video_qops = { - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .queue_setup = unicam_queue_setup, - .buf_prepare = unicam_buffer_prepare, - .buf_queue = unicam_buffer_queue, - .start_streaming = unicam_start_streaming, - .stop_streaming = unicam_stop_streaming, -}; - -/* - * unicam_v4l2_open : This function is based on the v4l2_fh_open helper - * function. It has been augmented to handle sensor subdevice power management, - */ -static int unicam_v4l2_open(struct file *file) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - mutex_lock(&node->lock); - - ret = v4l2_fh_open(file); - if (ret) { - unicam_err(dev, "v4l2_fh_open failed\n"); - goto unlock; - } - - node->open++; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - v4l2_fh_release(file); - node->open--; - goto unlock; - } - - ret = 0; - -unlock: - mutex_unlock(&node->lock); - return ret; -} - -static int unicam_v4l2_release(struct file *file) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev *sd = dev->sensor; - bool fh_singular; - int ret; - - mutex_lock(&node->lock); - - fh_singular = v4l2_fh_is_singular_file(file); - - ret = _vb2_fop_release(file, NULL); - - if (fh_singular) - v4l2_subdev_call(sd, core, s_power, 0); - - node->open--; - mutex_unlock(&node->lock); - - return ret; -} - -/* unicam capture driver file operations */ -static const struct v4l2_file_operations unicam_fops = { - .owner = THIS_MODULE, - .open = unicam_v4l2_open, - .release = unicam_v4l2_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -}; - -static int -unicam_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); - - if (unicam->sensor) { - unicam_info(unicam, "Rejecting subdev %s (Already set!!)", - subdev->name); - return 0; - } - - unicam->sensor = subdev; - unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name); - - return 0; -} - -static void unicam_release(struct kref *kref) -{ - struct unicam_device *unicam = - container_of(kref, struct unicam_device, kref); - - v4l2_ctrl_handler_free(&unicam->ctrl_handler); - media_device_cleanup(&unicam->mdev); - - if (unicam->sensor_state) - __v4l2_subdev_state_free(unicam->sensor_state); - - kfree(unicam); -} - -static void unicam_put(struct unicam_device *unicam) -{ - kref_put(&unicam->kref, unicam_release); -} - -static void unicam_get(struct unicam_device *unicam) -{ - kref_get(&unicam->kref); -} - -static void unicam_node_release(struct video_device *vdev) -{ - struct unicam_node *node = video_get_drvdata(vdev); - - unicam_put(node->dev); -} - -static int unicam_set_default_format(struct unicam_device *unicam, - struct unicam_node *node, - int pad_id, - const struct unicam_fmt **ret_fmt) -{ - struct v4l2_mbus_framefmt mbus_fmt = {0}; - const struct unicam_fmt *fmt; - int ret; - - if (pad_id == IMAGE_PAD) { - ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); - if (ret) { - unicam_err(unicam, "Failed to get_format - ret %d\n", - ret); - return ret; - } - - fmt = find_format_by_code(mbus_fmt.code); - if (!fmt) { - /* - * Find the first format that the sensor and unicam both - * support - */ - fmt = get_first_supported_format(unicam); - - if (fmt) { - mbus_fmt.code = fmt->code; - ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); - if (ret) - return -EINVAL; - } - } - if (mbus_fmt.field != V4L2_FIELD_NONE) { - /* Interlaced not supported - disable it now. */ - mbus_fmt.field = V4L2_FIELD_NONE; - ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); - if (ret) - return -EINVAL; - } - - if (fmt) - node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc - : fmt->repacked_fourcc; - } else { - /* Fix this node format as embedded data. */ - fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); - node->v_fmt.fmt.meta.dataformat = fmt->fourcc; - } - - *ret_fmt = fmt; - - return 0; -} - -static void unicam_mc_set_default_format(struct unicam_node *node, int pad_id) -{ - if (pad_id == IMAGE_PAD) { - struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; - - pix_fmt->width = 640; - pix_fmt->height = 480; - pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; - pix_fmt->pixelformat = formats[0].fourcc; - unicam_calc_format_size_bpl(node->dev, &formats[0], - &node->v_fmt); - node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - node->fmt = &formats[0]; - } else { - const struct unicam_fmt *fmt; - - /* Fix this node format as embedded data. */ - fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); - node->v_fmt.fmt.meta.dataformat = fmt->fourcc; - node->fmt = fmt; - - node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; - node->embedded_lines = 1; - node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; - } -} - -static int register_node(struct unicam_device *unicam, struct unicam_node *node, - enum v4l2_buf_type type, int pad_id) -{ - struct video_device *vdev; - struct vb2_queue *q; - int ret; - - node->dev = unicam; - node->pad_id = pad_id; - - if (!unicam->mc_api) { - const struct unicam_fmt *fmt; - - ret = unicam_set_default_format(unicam, node, pad_id, &fmt); - if (ret) - return ret; - node->fmt = fmt; - /* Read current subdev format */ - if (fmt) - unicam_reset_format(node); - } else { - unicam_mc_set_default_format(node, pad_id); - } - - if (!unicam->mc_api && - v4l2_subdev_has_op(unicam->sensor, video, s_std)) { - v4l2_std_id tvnorms; - - if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video, - g_tvnorms))) - /* - * Subdevice should not advertise s_std but not - * g_tvnorms - */ - return -EINVAL; - - ret = v4l2_subdev_call(unicam->sensor, video, - g_tvnorms, &tvnorms); - if (WARN_ON(ret)) - return -EINVAL; - node->video_dev.tvnorms |= tvnorms; - } - - spin_lock_init(&node->dma_queue_lock); - mutex_init(&node->lock); - - vdev = &node->video_dev; - if (pad_id == IMAGE_PAD) { - if (!unicam->mc_api) { - /* Add controls from the subdevice */ - ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler, - unicam->sensor->ctrl_handler, - NULL, - true); - if (ret < 0) - return ret; - } - - /* - * If the sensor subdevice has any controls, associate the node - * with the ctrl handler to allow access from userland. - */ - if (!list_empty(&unicam->ctrl_handler.ctrls)) - vdev->ctrl_handler = &unicam->ctrl_handler; - } - - q = &node->buffer_queue; - q->type = type; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = node; - q->ops = &unicam_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct unicam_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &node->lock; - q->min_buffers_needed = 1; - q->dev = &unicam->pdev->dev; - - ret = vb2_queue_init(q); - if (ret) { - unicam_err(unicam, "vb2_queue_init() failed\n"); - return ret; - } - - INIT_LIST_HEAD(&node->dma_queue); - - vdev->release = unicam_node_release; - vdev->fops = &unicam_fops; - vdev->ioctl_ops = unicam->mc_api ? &unicam_mc_ioctl_ops : - &unicam_ioctl_ops; - vdev->v4l2_dev = &unicam->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = q; - vdev->lock = &node->lock; - vdev->device_caps = (pad_id == IMAGE_PAD) ? - V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; - vdev->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - if (unicam->mc_api) { - vdev->device_caps |= V4L2_CAP_IO_MC; - vdev->entity.ops = &unicam_mc_entity_ops; - } - - /* Define the device names */ - snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, - pad_id == IMAGE_PAD ? "image" : "embedded"); - - video_set_drvdata(vdev, node); - if (pad_id == IMAGE_PAD) - vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; - node->pad.flags = MEDIA_PAD_FL_SINK; - media_entity_pads_init(&vdev->entity, 1, &node->pad); - - node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev, - DUMMY_BUF_SIZE, - &node->dummy_buf_dma_addr, - GFP_KERNEL); - if (!node->dummy_buf_cpu_addr) { - unicam_err(unicam, "Unable to allocate dummy buffer.\n"); - return -ENOMEM; - } - if (!unicam->mc_api) { - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); - } - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, querystd)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_DV_TIMINGS_CAP); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_G_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_S_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_QUERY_DV_TIMINGS); - } - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, - enum_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMEINTERVALS); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, - g_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, - s_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); - - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, - enum_frame_size)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMESIZES); - - if (node->pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, set_selection)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_S_SELECTION); - - if (node->pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, get_selection)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_G_SELECTION); - } - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - unicam_err(unicam, "Unable to register video device %s\n", - vdev->name); - return ret; - } - - /* - * Acquire a reference to unicam, which will be released when the video - * device will be unregistered and userspace will have closed all open - * file handles. - */ - unicam_get(unicam); - node->registered = true; - - if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) { - ret = media_create_pad_link(&unicam->sensor->entity, - node->src_pad_id, - &node->video_dev.entity, 0, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret) - unicam_err(unicam, "Unable to create pad link for %s\n", - vdev->name); - } - - return ret; -} - -static void unregister_nodes(struct unicam_device *unicam) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - struct unicam_node *node = &unicam->node[i]; - - if (node->dummy_buf_cpu_addr) { - dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE, - node->dummy_buf_cpu_addr, - node->dummy_buf_dma_addr); - } - - if (node->registered) { - node->registered = false; - video_unregister_device(&node->video_dev); - } - } -} - -static int unicam_async_complete(struct v4l2_async_notifier *notifier) -{ - static struct lock_class_key key; - struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); - unsigned int i, source_pads = 0; - int ret; - - unicam->v4l2_dev.notify = unicam_notify; - - unicam->sensor_state = __v4l2_subdev_state_alloc(unicam->sensor, - "unicam:async->lock", &key); - if (!unicam->sensor_state) - return -ENOMEM; - - for (i = 0; i < unicam->sensor->entity.num_pads; i++) { - if (unicam->sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) { - if (source_pads < MAX_NODES) { - unicam->node[source_pads].src_pad_id = i; - unicam_dbg(3, unicam, "source pad %u is index %u\n", - source_pads, i); - } - source_pads++; - } - } - if (!source_pads) { - unicam_err(unicam, "No source pads on sensor.\n"); - ret = -ENODEV; - goto unregister; - } - - ret = register_node(unicam, &unicam->node[IMAGE_PAD], - V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); - if (ret) { - unicam_err(unicam, "Unable to register image video device.\n"); - goto unregister; - } - - if (source_pads >= 2) { - unicam->sensor_embedded_data = true; - - ret = register_node(unicam, &unicam->node[METADATA_PAD], - V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD); - if (ret) { - unicam_err(unicam, "Unable to register metadata video device.\n"); - goto unregister; - } - } - - if (unicam->mc_api) - ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); - else - ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); - if (ret) { - unicam_err(unicam, "Unable to register subdev nodes.\n"); - goto unregister; - } - - /* - * Release the initial reference, all references are now owned by the - * video devices. - */ - unicam_put(unicam); - return 0; - -unregister: - unregister_nodes(unicam); - unicam_put(unicam); - - return ret; -} - -static const struct v4l2_async_notifier_operations unicam_async_ops = { - .bound = unicam_async_bound, - .complete = unicam_async_complete, -}; - -static int of_unicam_connect_subdevs(struct unicam_device *dev) -{ - struct platform_device *pdev = dev->pdev; - struct v4l2_fwnode_endpoint ep = { }; - struct device_node *ep_node; - struct device_node *sensor_node; - unsigned int lane; - int ret = -EINVAL; - - if (of_property_read_u32(pdev->dev.of_node, "brcm,num-data-lanes", - &dev->max_data_lanes) < 0) { - unicam_err(dev, "number of data lanes not set\n"); - return -EINVAL; - } - - /* Get the local endpoint and remote device. */ - ep_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!ep_node) { - unicam_dbg(3, dev, "can't get next endpoint\n"); - return -EINVAL; - } - - unicam_dbg(3, dev, "ep_node is %pOF\n", ep_node); - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - unicam_dbg(3, dev, "can't get remote parent\n"); - goto cleanup_exit; - } - - unicam_dbg(1, dev, "found subdevice %pOF\n", sensor_node); - - /* Parse the local endpoint and validate its configuration. */ - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); - - unicam_dbg(3, dev, "parsed local endpoint, bus_type %u\n", - ep.bus_type); - - dev->bus_type = ep.bus_type; - - switch (ep.bus_type) { - case V4L2_MBUS_CSI2_DPHY: - switch (ep.bus.mipi_csi2.num_data_lanes) { - case 1: - case 2: - case 4: - break; - - default: - unicam_err(dev, "subdevice %pOF: %u data lanes not supported\n", - sensor_node, - ep.bus.mipi_csi2.num_data_lanes); - goto cleanup_exit; - } - - for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) { - if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { - unicam_err(dev, "subdevice %pOF: data lanes reordering not supported\n", - sensor_node); - goto cleanup_exit; - } - } - - if (ep.bus.mipi_csi2.num_data_lanes > dev->max_data_lanes) { - unicam_err(dev, "subdevice requires %u data lanes when %u are supported\n", - ep.bus.mipi_csi2.num_data_lanes, - dev->max_data_lanes); - } - - dev->max_data_lanes = ep.bus.mipi_csi2.num_data_lanes; - dev->bus_flags = ep.bus.mipi_csi2.flags; - - break; - - case V4L2_MBUS_CCP2: - if (ep.bus.mipi_csi1.clock_lane != 0 || - ep.bus.mipi_csi1.data_lane != 1) { - unicam_err(dev, "subdevice %pOF: unsupported lanes configuration\n", - sensor_node); - goto cleanup_exit; - } - - dev->max_data_lanes = 1; - dev->bus_flags = ep.bus.mipi_csi1.strobe; - break; - - default: - /* Unsupported bus type */ - unicam_err(dev, "subdevice %pOF: unsupported bus type %u\n", - sensor_node, ep.bus_type); - goto cleanup_exit; - } - - unicam_dbg(3, dev, "subdevice %pOF: %s bus, %u data lanes, flags=0x%08x\n", - sensor_node, - dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI-2" : "CCP2", - dev->max_data_lanes, dev->bus_flags); - - /* Initialize and register the async notifier. */ - v4l2_async_nf_init(&dev->notifier, &dev->v4l2_dev); - dev->notifier.ops = &unicam_async_ops; - - dev->asd = v4l2_async_nf_add_fwnode(&dev->notifier, - of_fwnode_handle(sensor_node), - struct v4l2_async_connection); - if (IS_ERR(dev->asd)) { - unicam_err(dev, "Error adding subdevice: %d\n", ret); - goto cleanup_exit; - } - - ret = v4l2_async_nf_register(&dev->notifier); - if (ret) { - unicam_err(dev, "Error registering async notifier: %d\n", ret); - ret = -EINVAL; - } - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - - return ret; -} - -static int unicam_probe(struct platform_device *pdev) -{ - struct unicam_device *unicam; - int ret; - - unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); - if (!unicam) - return -ENOMEM; - - kref_init(&unicam->kref); - unicam->pdev = pdev; - - /* - * Adopt the current setting of the module parameter, and check if - * device tree requests it. - */ - unicam->mc_api = media_controller; - if (of_property_read_bool(pdev->dev.of_node, "brcm,media-controller")) - unicam->mc_api = true; - - unicam->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(unicam->base)) { - unicam_err(unicam, "Failed to get main io block\n"); - ret = PTR_ERR(unicam->base); - goto err_unicam_put; - } - - unicam->clk_gate_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(unicam->clk_gate_base)) { - unicam_err(unicam, "Failed to get 2nd io block\n"); - ret = PTR_ERR(unicam->clk_gate_base); - goto err_unicam_put; - } - - unicam->clock = devm_clk_get(&pdev->dev, "lp"); - if (IS_ERR(unicam->clock)) { - unicam_err(unicam, "Failed to get lp clock\n"); - ret = PTR_ERR(unicam->clock); - goto err_unicam_put; - } - - unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu"); - if (IS_ERR(unicam->vpu_clock)) { - unicam_err(unicam, "Failed to get vpu clock\n"); - ret = PTR_ERR(unicam->vpu_clock); - goto err_unicam_put; - } - - unicam->sync_gpio = devm_gpiod_get_optional(&pdev->dev, "sync", - GPIOD_OUT_LOW); - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "No IRQ resource\n"); - ret = -EINVAL; - goto err_unicam_put; - } - - ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, - "unicam_capture0", unicam); - if (ret) { - dev_err(&pdev->dev, "Unable to request interrupt\n"); - ret = -EINVAL; - goto err_unicam_put; - } - - unicam->mdev.dev = &pdev->dev; - strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, - sizeof(unicam->mdev.model)); - strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial)); - snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info), - "platform:%s", dev_name(&pdev->dev)); - unicam->mdev.hw_revision = 0; - - media_device_init(&unicam->mdev); - - unicam->v4l2_dev.mdev = &unicam->mdev; - - ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev); - if (ret) { - unicam_err(unicam, - "Unable to register v4l2 device.\n"); - goto err_unicam_put; - } - - ret = media_device_register(&unicam->mdev); - if (ret < 0) { - unicam_err(unicam, - "Unable to register media-controller device.\n"); - goto err_v4l2_unregister; - } - - /* Reserve space for the controls */ - ret = v4l2_ctrl_handler_init(&unicam->ctrl_handler, 16); - if (ret < 0) - goto err_media_unregister; - - /* set the driver data in platform device */ - platform_set_drvdata(pdev, unicam); - - ret = of_unicam_connect_subdevs(unicam); - if (ret) { - dev_err(&pdev->dev, "Failed to connect subdevs\n"); - goto err_media_unregister; - } - - /* Enable the block power domain */ - pm_runtime_enable(&pdev->dev); - - return 0; - -err_media_unregister: - media_device_unregister(&unicam->mdev); -err_v4l2_unregister: - v4l2_device_unregister(&unicam->v4l2_dev); -err_unicam_put: - unicam_put(unicam); - - return ret; -} - -static int unicam_remove(struct platform_device *pdev) -{ - struct unicam_device *unicam = platform_get_drvdata(pdev); - - unicam_dbg(2, unicam, "%s\n", __func__); - - v4l2_async_nf_unregister(&unicam->notifier); - v4l2_device_unregister(&unicam->v4l2_dev); - media_device_unregister(&unicam->mdev); - unregister_nodes(unicam); - - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static const struct of_device_id unicam_of_match[] = { - { .compatible = "brcm,bcm2835-unicam", }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, unicam_of_match); - -static struct platform_driver unicam_driver = { - .probe = unicam_probe, - .remove = unicam_remove, - .driver = { - .name = UNICAM_MODULE_NAME, - .of_match_table = of_match_ptr(unicam_of_match), - }, -}; - -module_platform_driver(unicam_driver); - -MODULE_AUTHOR("Dave Stevenson "); -MODULE_DESCRIPTION("BCM2835 Unicam driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(UNICAM_VERSION); diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/broadcom/Kconfig similarity index 92% rename from drivers/media/platform/bcm2835/Kconfig rename to drivers/media/platform/broadcom/Kconfig index 8fa00f0b2ceab7..32b76ebfcd9a19 100644 --- a/drivers/media/platform/bcm2835/Kconfig +++ b/drivers/media/platform/broadcom/Kconfig @@ -1,17 +1,19 @@ -# Broadcom VideoCore4 V4L2 camera support +# SPDX-License-Identifier: GPL-2.0 config VIDEO_BCM2835_UNICAM tristate "Broadcom BCM283x/BCM271x Unicam video capture driver" - depends on VIDEO_DEV depends on ARCH_BCM2835 || COMPILE_TEST - select VIDEO_V4L2_SUBDEV_API + depends on COMMON_CLK && PM + depends on VIDEO_DEV select MEDIA_CONTROLLER - select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG help Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver. This is a V4L2 driver that controls the CSI-2 receiver directly, independently from the VC4 firmware. + This driver is mutually exclusive with the use of bcm2835-camera. The firmware will disable all access to the peripheral from within the firmware if it finds a DT node using it, and bcm2835-camera will diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/broadcom/Makefile similarity index 60% rename from drivers/media/platform/bcm2835/Makefile rename to drivers/media/platform/broadcom/Makefile index a98aba03598abf..03d2045aba2e4d 100644 --- a/drivers/media/platform/bcm2835/Makefile +++ b/drivers/media/platform/broadcom/Makefile @@ -1,3 +1,3 @@ -# Makefile for BCM2835 Unicam driver +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h similarity index 71% rename from drivers/media/platform/bcm2835/vc4-regs-unicam.h rename to drivers/media/platform/broadcom/bcm2835-unicam-regs.h index ae059a171d0fe0..fce6fa92707cac 100644 --- a/drivers/media/platform/bcm2835/vc4-regs-unicam.h +++ b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h @@ -8,57 +8,59 @@ #ifndef VC4_REGS_UNICAM_H #define VC4_REGS_UNICAM_H +#include + /* * The following values are taken from files found within the code drop * made by Broadcom for the BCM21553 Graphics Driver, predominantly in * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h. * They have been modified to be only the register offset. */ -#define UNICAM_CTRL 0x000 -#define UNICAM_STA 0x004 -#define UNICAM_ANA 0x008 -#define UNICAM_PRI 0x00c -#define UNICAM_CLK 0x010 -#define UNICAM_CLT 0x014 -#define UNICAM_DAT0 0x018 -#define UNICAM_DAT1 0x01c -#define UNICAM_DAT2 0x020 -#define UNICAM_DAT3 0x024 -#define UNICAM_DLT 0x028 -#define UNICAM_CMP0 0x02c -#define UNICAM_CMP1 0x030 -#define UNICAM_CAP0 0x034 -#define UNICAM_CAP1 0x038 -#define UNICAM_ICTL 0x100 -#define UNICAM_ISTA 0x104 -#define UNICAM_IDI0 0x108 -#define UNICAM_IPIPE 0x10c -#define UNICAM_IBSA0 0x110 -#define UNICAM_IBEA0 0x114 -#define UNICAM_IBLS 0x118 -#define UNICAM_IBWP 0x11c -#define UNICAM_IHWIN 0x120 -#define UNICAM_IHSTA 0x124 -#define UNICAM_IVWIN 0x128 -#define UNICAM_IVSTA 0x12c -#define UNICAM_ICC 0x130 -#define UNICAM_ICS 0x134 -#define UNICAM_IDC 0x138 -#define UNICAM_IDPO 0x13c -#define UNICAM_IDCA 0x140 -#define UNICAM_IDCD 0x144 -#define UNICAM_IDS 0x148 -#define UNICAM_DCS 0x200 -#define UNICAM_DBSA0 0x204 -#define UNICAM_DBEA0 0x208 -#define UNICAM_DBWP 0x20c -#define UNICAM_DBCTL 0x300 -#define UNICAM_IBSA1 0x304 -#define UNICAM_IBEA1 0x308 -#define UNICAM_IDI1 0x30c -#define UNICAM_DBSA1 0x310 -#define UNICAM_DBEA1 0x314 -#define UNICAM_MISC 0x400 +#define UNICAM_CTRL 0x000 +#define UNICAM_STA 0x004 +#define UNICAM_ANA 0x008 +#define UNICAM_PRI 0x00c +#define UNICAM_CLK 0x010 +#define UNICAM_CLT 0x014 +#define UNICAM_DAT0 0x018 +#define UNICAM_DAT1 0x01c +#define UNICAM_DAT2 0x020 +#define UNICAM_DAT3 0x024 +#define UNICAM_DLT 0x028 +#define UNICAM_CMP0 0x02c +#define UNICAM_CMP1 0x030 +#define UNICAM_CAP0 0x034 +#define UNICAM_CAP1 0x038 +#define UNICAM_ICTL 0x100 +#define UNICAM_ISTA 0x104 +#define UNICAM_IDI0 0x108 +#define UNICAM_IPIPE 0x10c +#define UNICAM_IBSA0 0x110 +#define UNICAM_IBEA0 0x114 +#define UNICAM_IBLS 0x118 +#define UNICAM_IBWP 0x11c +#define UNICAM_IHWIN 0x120 +#define UNICAM_IHSTA 0x124 +#define UNICAM_IVWIN 0x128 +#define UNICAM_IVSTA 0x12c +#define UNICAM_ICC 0x130 +#define UNICAM_ICS 0x134 +#define UNICAM_IDC 0x138 +#define UNICAM_IDPO 0x13c +#define UNICAM_IDCA 0x140 +#define UNICAM_IDCD 0x144 +#define UNICAM_IDS 0x148 +#define UNICAM_DCS 0x200 +#define UNICAM_DBSA0 0x204 +#define UNICAM_DBEA0 0x208 +#define UNICAM_DBWP 0x20c +#define UNICAM_DBCTL 0x300 +#define UNICAM_IBSA1 0x304 +#define UNICAM_IBEA1 0x308 +#define UNICAM_IDI1 0x30c +#define UNICAM_DBSA1 0x310 +#define UNICAM_DBEA1 0x314 +#define UNICAM_MISC 0x400 /* * The following bitmasks are from the kernel released by Broadcom @@ -113,18 +115,9 @@ #define UNICAM_DI BIT(24) #define UNICAM_STA_MASK_ALL \ - (UNICAM_DL + \ - UNICAM_SBE + \ - UNICAM_PBE + \ - UNICAM_HOE + \ - UNICAM_PLE + \ - UNICAM_SSC + \ - UNICAM_CRCE + \ - UNICAM_IFO + \ - UNICAM_OFO + \ - UNICAM_PS + \ - UNICAM_PI0 + \ - UNICAM_PI1) + (UNICAM_SBE | UNICAM_PBE | UNICAM_HOE | UNICAM_PLE | UNICAM_SSC | \ + UNICAM_CRCE | UNICAM_IFO | UNICAM_OFO | UNICAM_DL | UNICAM_PS | \ + UNICAM_PI0 | UNICAM_PI1) /* UNICAM_ANA Register */ #define UNICAM_APD BIT(0) @@ -165,7 +158,7 @@ #define UNICAM_DLFO BIT(28) #define UNICAM_DLSTE BIT(29) -#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO) +#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE | UNICAM_DLFO) /* UNICAM_DLT Register */ #define UNICAM_DLT1_MASK GENMASK(7, 0) @@ -192,28 +185,28 @@ #define UNICAM_FEI BIT(1) #define UNICAM_LCI BIT(2) -#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI) +#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI | UNICAM_FEI | UNICAM_LCI) /* UNICAM_IPIPE Register */ #define UNICAM_PUM_MASK GENMASK(2, 0) - /* Unpacking modes */ - #define UNICAM_PUM_NONE 0 - #define UNICAM_PUM_UNPACK6 1 - #define UNICAM_PUM_UNPACK7 2 - #define UNICAM_PUM_UNPACK8 3 - #define UNICAM_PUM_UNPACK10 4 - #define UNICAM_PUM_UNPACK12 5 - #define UNICAM_PUM_UNPACK14 6 - #define UNICAM_PUM_UNPACK16 7 +/* Unpacking modes */ +#define UNICAM_PUM_NONE 0 +#define UNICAM_PUM_UNPACK6 1 +#define UNICAM_PUM_UNPACK7 2 +#define UNICAM_PUM_UNPACK8 3 +#define UNICAM_PUM_UNPACK10 4 +#define UNICAM_PUM_UNPACK12 5 +#define UNICAM_PUM_UNPACK14 6 +#define UNICAM_PUM_UNPACK16 7 #define UNICAM_DDM_MASK GENMASK(6, 3) #define UNICAM_PPM_MASK GENMASK(9, 7) - /* Packing modes */ - #define UNICAM_PPM_NONE 0 - #define UNICAM_PPM_PACK8 1 - #define UNICAM_PPM_PACK10 2 - #define UNICAM_PPM_PACK12 3 - #define UNICAM_PPM_PACK14 4 - #define UNICAM_PPM_PACK16 5 +/* Packing modes */ +#define UNICAM_PPM_NONE 0 +#define UNICAM_PPM_PACK8 1 +#define UNICAM_PPM_PACK10 2 +#define UNICAM_PPM_PACK12 3 +#define UNICAM_PPM_PACK14 4 +#define UNICAM_PPM_PACK16 5 #define UNICAM_DEM_MASK GENMASK(11, 10) #define UNICAM_DEBL_MASK GENMASK(14, 12) #define UNICAM_ICM_MASK GENMASK(16, 15) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c new file mode 100644 index 00000000000000..6dcc6c13a7540e --- /dev/null +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -0,0 +1,2739 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BCM283x / BCM271x Unicam Capture Driver + * + * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. + * Copyright (C) 2024 - Ideas on Board + * + * Dave Stevenson + * + * Based on TI am437x driver by + * Benoit Parrot + * Lad, Prabhakar + * + * and TI CAL camera interface driver by + * Benoit Parrot + * + * + * There are two camera drivers in the kernel for BCM283x - this one and + * bcm2835-camera (currently in staging). + * + * This driver directly controls the Unicam peripheral - there is no + * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data + * and writes it into SDRAM. The only potential processing options are to + * repack Bayer data into an alternate format, and applying windowing. The + * repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P to + * V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, but + * not generically up to V4L2_PIX_FMT_Sxxxx16. Support for windowing may be + * added later. + * + * It should be possible to connect this driver to any sensor with a suitable + * output interface and V4L2 subdevice driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm2835-unicam-regs.h" + +#define UNICAM_MODULE_NAME "unicam" + +/* + * Unicam must request a minimum of 250Mhz from the VPU clock. + * Otherwise the input FIFOs overrun and cause image corruption. + */ +#define UNICAM_MIN_VPU_CLOCK_RATE (250 * 1000 * 1000) + +/* Unicam has an internal DMA alignment constraint of 16 bytes for each line. */ +#define UNICAM_DMA_BPL_ALIGNMENT 16 + +/* + * The image stride is stored in a 16 bit register, and needs to be aligned to + * the DMA constraint. As the ISP in the same SoC has a 32 bytes alignment + * constraint on its input, set the image stride alignment to 32 bytes here as + * well to avoid incompatible configurations. + */ +#define UNICAM_IMAGE_BPL_ALIGNMENT 32 +#define UNICAM_IMAGE_MAX_BPL ((1U << 16) - UNICAM_IMAGE_BPL_ALIGNMENT) + +/* + * Max width is therefore determined by the max stride divided by the number of + * bits per pixel. Take 32bpp as a worst case. No imposed limit on the height, + * so adopt a square image for want of anything better. + */ +#define UNICAM_IMAGE_MIN_WIDTH 16 +#define UNICAM_IMAGE_MIN_HEIGHT 16 +#define UNICAM_IMAGE_MAX_WIDTH (UNICAM_IMAGE_MAX_BPL / 4) +#define UNICAM_IMAGE_MAX_HEIGHT UNICAM_IMAGE_MAX_WIDTH + +/* + * There's no intrinsic limits on the width and height for embedded data. Use + * the same maximum values as for the image, to avoid overflows in the image + * size computation. + */ +#define UNICAM_META_MIN_WIDTH 1 +#define UNICAM_META_MIN_HEIGHT 1 +#define UNICAM_META_MAX_WIDTH UNICAM_IMAGE_MAX_WIDTH +#define UNICAM_META_MAX_HEIGHT UNICAM_IMAGE_MAX_HEIGHT + +/* + * Size of the dummy buffer. Can be any size really, but the DMA + * allocation works in units of page sizes. + */ +#define UNICAM_DUMMY_BUF_SIZE PAGE_SIZE + +enum unicam_pad { + UNICAM_SD_PAD_SINK, + UNICAM_SD_PAD_SOURCE_IMAGE, + UNICAM_SD_PAD_SOURCE_METADATA, + UNICAM_SD_NUM_PADS +}; + +enum unicam_node_type { + UNICAM_IMAGE_NODE, + UNICAM_METADATA_NODE, + UNICAM_MAX_NODES +}; + +/* + * struct unicam_format_info - Unicam media bus format information + * @fourcc: V4L2 pixel format FCC identifier. 0 if n/a. + * @unpacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded + * out to 16bpp. 0 if n/a. + * @code: V4L2 media bus format code. + * @depth: Bits per pixel as delivered from the source. + * @csi_dt: CSI data type. + * @unpack: PUM value when unpacking to @unpacked_fourcc + */ +struct unicam_format_info { + u32 fourcc; + u32 unpacked_fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u8 unpack; +}; + +struct unicam_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t dma_addr; + unsigned int size; +}; + +static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct unicam_buffer, vb.vb2_buf); +} + +struct unicam_node { + bool registered; + unsigned int id; + + /* Pointer to the current v4l2_buffer */ + struct unicam_buffer *cur_frm; + /* Pointer to the next v4l2_buffer */ + struct unicam_buffer *next_frm; + /* Used to store current pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct unicam_device *dev; + struct media_pad pad; + /* + * Dummy buffer intended to be used by unicam + * if we have no other queued buffers to swap to. + */ + struct unicam_buffer dummy_buf; + void *dummy_buf_cpu_addr; +}; + +struct unicam_device { + struct kref kref; + + /* peripheral base address */ + void __iomem *base; + /* clock gating base address */ + void __iomem *clk_gate_base; + /* lp clock handle */ + struct clk *clock; + /* vpu clock handle */ + struct clk *vpu_clock; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + + /* parent device */ + struct device *dev; + /* subdevice async notifier */ + struct v4l2_async_notifier notifier; + unsigned int sequence; + + /* Sensor node */ + struct { + struct v4l2_subdev *subdev; + struct media_pad *pad; + } sensor; + + /* Internal subdev */ + struct { + struct v4l2_subdev sd; + struct media_pad pads[UNICAM_SD_NUM_PADS]; + unsigned int enabled_streams; + } subdev; + + enum v4l2_mbus_type bus_type; + /* + * Stores bus.mipi_csi2.flags for CSI2 sensors, or + * bus.mipi_csi1.strobe for CCP2. + */ + unsigned int bus_flags; + unsigned int max_data_lanes; + + struct { + struct media_pipeline pipe; + unsigned int num_data_lanes; + unsigned int nodes; + } pipe; + + /* Lock used for the video devices of both nodes */ + struct mutex lock; + struct unicam_node node[UNICAM_MAX_NODES]; +}; + +static inline struct unicam_device * +notifier_to_unicam_device(struct v4l2_async_notifier *notifier) +{ + return container_of(notifier, struct unicam_device, notifier); +} + +static inline struct unicam_device * +sd_to_unicam_device(struct v4l2_subdev *sd) +{ + return container_of(sd, struct unicam_device, subdev.sd); +} + +static void unicam_release(struct kref *kref) +{ + struct unicam_device *unicam = + container_of(kref, struct unicam_device, kref); + + if (unicam->mdev.dev) + media_device_cleanup(&unicam->mdev); + + mutex_destroy(&unicam->lock); + kfree(unicam); +} + +static struct unicam_device *unicam_get(struct unicam_device *unicam) +{ + kref_get(&unicam->kref); + + return unicam; +} + +static void unicam_put(struct unicam_device *unicam) +{ + kref_put(&unicam->kref, unicam_release); +} + +/* ----------------------------------------------------------------------------- + * Misc helper functions + */ + +static inline bool unicam_sd_pad_is_source(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad != UNICAM_SD_PAD_SINK; +} + +static inline bool is_metadata_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_META_CAPTURE; +} + +static inline bool is_image_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_VIDEO_CAPTURE; +} + +/* ----------------------------------------------------------------------------- + * Format data table and helper functions + */ + +static const struct v4l2_mbus_framefmt unicam_default_image_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + .flags = 0, +}; + +static const struct v4l2_mbus_framefmt unicam_default_meta_format = { + .width = 640, + .height = 2, + .code = MEDIA_BUS_FMT_META_8, + .field = V4L2_FIELD_NONE, +}; + +static const struct unicam_format_info unicam_image_formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + /* Bayer Formats */ + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR14, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG14, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG14, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB14, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + /* 16 bit Bayer formats could be supported. */ + + /* Greyscale formats */ + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_Y10P, + .unpacked_fourcc = V4L2_PIX_FMT_Y10, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_Y12P, + .unpacked_fourcc = V4L2_PIX_FMT_Y12, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_Y14P, + .unpacked_fourcc = V4L2_PIX_FMT_Y14, + .code = MEDIA_BUS_FMT_Y14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, +}; + +static const struct unicam_format_info unicam_meta_formats[] = { + { + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, + .depth = 8, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_14, + .code = MEDIA_BUS_FMT_META_14, + .depth = 14, + }, +}; + +/* Format setup functions */ +static const struct unicam_format_info * +unicam_find_format_by_code(u32 code, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +static const struct unicam_format_info * +unicam_find_format_by_fourcc(u32 fourcc, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; ++i) { + if (formats[i].fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + +static void unicam_calc_image_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_pix_format *pix) +{ + u32 min_bpl; + + v4l_bound_align_image(&pix->width, UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH, 2, + &pix->height, UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT, 0, 0); + + /* Unpacking always goes to 16bpp */ + if (pix->pixelformat == fmtinfo->unpacked_fourcc) + min_bpl = pix->width * 2; + else + min_bpl = pix->width * fmtinfo->depth / 8; + min_bpl = ALIGN(min_bpl, UNICAM_IMAGE_BPL_ALIGNMENT); + + pix->bytesperline = ALIGN(pix->bytesperline, UNICAM_IMAGE_BPL_ALIGNMENT); + pix->bytesperline = clamp_t(unsigned int, pix->bytesperline, min_bpl, + UNICAM_IMAGE_MAX_BPL); + + pix->sizeimage = pix->height * pix->bytesperline; +} + +static void unicam_calc_meta_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_meta_format *meta) +{ + v4l_bound_align_image(&meta->width, UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH, 0, + &meta->height, UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT, 0, 0); + + meta->bytesperline = ALIGN(meta->width * fmtinfo->depth / 8, + UNICAM_DMA_BPL_ALIGNMENT); + meta->buffersize = meta->height * meta->bytesperline; +} + +/* ----------------------------------------------------------------------------- + * Hardware handling + */ + +static inline void unicam_clk_write(struct unicam_device *unicam, u32 val) +{ + /* Pass the CM_PASSWORD along with the value. */ + writel(val | 0x5a000000, unicam->clk_gate_base); +} + +static inline u32 unicam_reg_read(struct unicam_device *unicam, u32 offset) +{ + return readl(unicam->base + offset); +} + +static inline void unicam_reg_write(struct unicam_device *unicam, u32 offset, u32 val) +{ + writel(val, unicam->base + offset); +} + +static inline int unicam_get_field(u32 value, u32 mask) +{ + return (value & mask) >> __ffs(mask); +} + +static inline void unicam_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offset, + u32 field, u32 mask) +{ + u32 val = unicam_reg_read(unicam, offset); + + unicam_set_field(&val, field, mask); + unicam_reg_write(unicam, offset, val); +} + +static void unicam_wr_dma_addr(struct unicam_node *node, + struct unicam_buffer *buf) +{ + dma_addr_t endaddr = buf->dma_addr + buf->size; + + if (node->id == UNICAM_IMAGE_NODE) { + unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_IBEA0, endaddr); + } else { + unicam_reg_write(node->dev, UNICAM_DBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_DBEA0, endaddr); + } +} + +static unsigned int unicam_get_lines_done(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + unsigned int stride = node->fmt.fmt.pix.bytesperline; + struct unicam_buffer *frm = node->cur_frm; + dma_addr_t cur_addr; + + if (!frm) + return 0; + + cur_addr = unicam_reg_read(unicam, UNICAM_IBWP); + return (unsigned int)(cur_addr - frm->dma_addr) / stride; +} + +static void unicam_schedule_next_buffer(struct unicam_node *node) +{ + struct unicam_buffer *buf; + + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->next_frm = buf; + list_del(&buf->list); + + unicam_wr_dma_addr(node, buf); +} + +static void unicam_schedule_dummy_buffer(struct unicam_node *node) +{ + int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE; + + dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id); + + unicam_wr_dma_addr(node, &node->dummy_buf); + + node->next_frm = NULL; +} + +static void unicam_process_buffer_complete(struct unicam_node *node, + unsigned int sequence) +{ + node->cur_frm->vb.field = node->fmt.fmt.pix.field; + node->cur_frm->vb.sequence = sequence; + + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void unicam_queue_event_sof(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = unicam->sequence, + }; + + v4l2_event_queue(&node->video_dev, &event); +} + +static irqreturn_t unicam_isr(int irq, void *dev) +{ + struct unicam_device *unicam = dev; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; + unsigned int i; + u32 ista, sta; + bool fe; + u64 ts; + + sta = unicam_reg_read(unicam, UNICAM_STA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_STA, sta); + + ista = unicam_reg_read(unicam, UNICAM_ISTA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_ISTA, ista); + + dev_dbg(unicam->dev, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d\n", + ista, sta, sequence, lines_done); + + if (!(sta & (UNICAM_IS | UNICAM_PI0))) + return IRQ_HANDLED; + + /* + * Look for either the Frame End interrupt or the Packet Capture status + * to signal a frame end. + */ + fe = ista & UNICAM_FEI || sta & UNICAM_PI0; + + /* + * We must run the frame end handler first. If we have a valid next_frm + * and we get a simultaneout FE + FS interrupt, running the FS handler + * first would null out the next_frm ptr and we would have lost the + * buffer forever. + */ + if (fe) { + /* + * Ensure we have swapped buffers already as we can't + * stop the peripheral. If no buffer is available, use a + * dummy buffer to dump out frames until we get a new buffer + * to use. + */ + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + /* + * If cur_frm == next_frm, it means we have not had + * a chance to swap buffers, likely due to having + * multiple interrupts occurring simultaneously (like FE + * + FS + LS). In this case, we cannot signal the buffer + * as complete, as the HW will reuse that buffer. + */ + if (node->cur_frm && node->cur_frm != node->next_frm) + unicam_process_buffer_complete(node, sequence); + node->cur_frm = node->next_frm; + } + unicam->sequence++; + } + + if (ista & UNICAM_FSI) { + /* + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ + ts = ktime_get_ns(); + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + if (node->cur_frm) + node->cur_frm->vb.vb2_buf.timestamp = ts; + else + dev_dbg(unicam->v4l2_dev.dev, + "ISR: [%d] Dropping frame, buffer not available at FS\n", + i); + /* + * Set the next frame output to go to a dummy frame + * if we have not managed to obtain another frame + * from the queue. + */ + unicam_schedule_dummy_buffer(node); + } + + unicam_queue_event_sof(unicam); + } + + /* + * Cannot swap buffer at frame end, there may be a race condition + * where the HW does not actually swap it if the new frame has + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) { + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + spin_lock(&node->dma_queue_lock); + if (!list_empty(&node->dma_queue) && !node->next_frm) + unicam_schedule_next_buffer(node); + spin_unlock(&node->dma_queue_lock); + } + } + + if (unicam_reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) { + /* Switch out of trigger mode if selected */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); + unicam_reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM); + } + return IRQ_HANDLED; +} + +static void unicam_set_packing_config(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + u32 pack, unpack; + u32 val; + + if (node->fmt.fmt.pix.pixelformat == fmtinfo->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { + unpack = fmtinfo->unpack; + /* Repacking is always to 16bpp */ + pack = UNICAM_PPM_PACK16; + } + + val = 0; + unicam_set_field(&val, unpack, UNICAM_PUM_MASK); + unicam_set_field(&val, pack, UNICAM_PPM_MASK); + unicam_reg_write(unicam, UNICAM_IPIPE, val); +} + +static void unicam_cfg_image_id(struct unicam_device *unicam, u8 vc, u8 dt) +{ + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, (vc << 6) | dt); + } else { + /* CCP2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, 0x80 | dt); + } +} + +static void unicam_enable_ed(struct unicam_device *unicam) +{ + u32 val = unicam_reg_read(unicam, UNICAM_DCS); + + unicam_set_field(&val, 2, UNICAM_EDL_MASK); + /* Do not wrap at the end of the embedded data buffer */ + unicam_set_field(&val, 0, UNICAM_DBOB); + + unicam_reg_write(unicam, UNICAM_DCS, val); +} + +static int unicam_get_image_vc_dt(struct unicam_device *unicam, + struct v4l2_subdev_state *state, + u8 *vc, u8 *dt) +{ + struct v4l2_mbus_frame_desc fd; + u32 stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + UNICAM_SD_PAD_SOURCE_IMAGE, + 0, NULL, &stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_frame_desc, + unicam->sensor.pad->index, &fd); + if (ret) + return ret; + + /* Only CSI-2 supports DTs. */ + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (unsigned int i = 0; i < fd.num_entries; ++i) { + const struct v4l2_mbus_frame_desc_entry *fde = &fd.entry[i]; + + if (fde->stream == stream) { + *vc = fde->bus.csi2.vc; + *dt = fde->bus.csi2.dt; + return 0; + } + } + + return -EINVAL; +} + +static void unicam_start_rx(struct unicam_device *unicam, + struct v4l2_subdev_state *state) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + const struct unicam_format_info *fmtinfo; + const struct v4l2_mbus_framefmt *fmt; + unsigned int line_int_freq; + u8 vc, dt; + u32 val; + int ret; + + fmt = v4l2_subdev_state_get_format(state, UNICAM_SD_PAD_SOURCE_IMAGE, 0); + fmtinfo = unicam_find_format_by_code(fmt->code, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) + return; + + /* + * Enable lane clocks. The register is structured as follows: + * + * [9:8] - DAT3 + * [7:6] - DAT2 + * [5:4] - DAT1 + * [3:2] - DAT0 + * [1:0] - CLK + * + * Enabled lane must be set to b01, and disabled lanes to b00. The clock + * lane is always enabled. + */ + val = 0x155 & GENMASK(unicam->pipe.num_data_lanes * 2 + 1, 0); + unicam_clk_write(unicam, val); + + /* Basic init */ + unicam_reg_write(unicam, UNICAM_CTRL, UNICAM_MEM); + + /* Enable analogue control, and leave in reset. */ + val = UNICAM_AR; + unicam_set_field(&val, 7, UNICAM_CTATADJ_MASK); + unicam_set_field(&val, 7, UNICAM_PTATADJ_MASK); + unicam_reg_write(unicam, UNICAM_ANA, val); + usleep_range(1000, 2000); + + /* Come out of reset */ + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_AR); + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Enable Rx control. */ + val = unicam_reg_read(unicam, UNICAM_CTRL); + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + unicam_set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); + unicam_set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); + } else { + unicam_set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); + unicam_set_field(&val, unicam->bus_flags, UNICAM_DCM_MASK); + } + /* Packet framer timeout */ + unicam_set_field(&val, 0xf, UNICAM_PFT_MASK); + unicam_set_field(&val, 128, UNICAM_OET_MASK); + unicam_reg_write(unicam, UNICAM_CTRL, val); + + unicam_reg_write(unicam, UNICAM_IHWIN, 0); + unicam_reg_write(unicam, UNICAM_IVWIN, 0); + + /* AXI bus access QoS setup */ + val = unicam_reg_read(unicam, UNICAM_PRI); + unicam_set_field(&val, 0, UNICAM_BL_MASK); + unicam_set_field(&val, 0, UNICAM_BS_MASK); + unicam_set_field(&val, 0xe, UNICAM_PP_MASK); + unicam_set_field(&val, 8, UNICAM_NP_MASK); + unicam_set_field(&val, 2, UNICAM_PT_MASK); + unicam_set_field(&val, 1, UNICAM_PE); + unicam_reg_write(unicam, UNICAM_PRI, val); + + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL); + + /* Always start in trigger frame capture mode (UNICAM_FCM set) */ + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB; + line_int_freq = max(fmt->height >> 2, 128); + unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK); + unicam_reg_write(unicam, UNICAM_ICTL, val); + unicam_reg_write(unicam, UNICAM_STA, UNICAM_STA_MASK_ALL); + unicam_reg_write(unicam, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); + + /* tclk_term_en */ + unicam_reg_write_field(unicam, UNICAM_CLT, 2, UNICAM_CLT1_MASK); + /* tclk_settle */ + unicam_reg_write_field(unicam, UNICAM_CLT, 6, UNICAM_CLT2_MASK); + /* td_term_en */ + unicam_reg_write_field(unicam, UNICAM_DLT, 2, UNICAM_DLT1_MASK); + /* ths_settle */ + unicam_reg_write_field(unicam, UNICAM_DLT, 6, UNICAM_DLT2_MASK); + /* trx_enable */ + unicam_reg_write_field(unicam, UNICAM_DLT, 0, UNICAM_DLT3_MASK); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_SOE); + + /* Packet compare setup - required to avoid missing frame ends */ + val = 0; + unicam_set_field(&val, 1, UNICAM_PCE); + unicam_set_field(&val, 1, UNICAM_GI); + unicam_set_field(&val, 1, UNICAM_CPH); + unicam_set_field(&val, 0, UNICAM_PCVC_MASK); + unicam_set_field(&val, 1, UNICAM_PCDT_MASK); + unicam_reg_write(unicam, UNICAM_CMP0, val); + + /* Enable clock lane and set up terminations */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_CLTRE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + unicam_set_field(&val, 1, UNICAM_CLTRE); + } + unicam_reg_write(unicam, UNICAM_CLK, val); + + /* + * Enable required data lanes with appropriate terminations. + * The same value needs to be written to UNICAM_DATn registers for + * the active lanes, and 0 for inactive ones. + */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_DLTRE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + unicam_set_field(&val, 1, UNICAM_DLTRE); + } + unicam_reg_write(unicam, UNICAM_DAT0, val); + + if (unicam->pipe.num_data_lanes == 1) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT1, val); + + if (unicam->max_data_lanes > 2) { + /* + * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the + * instance supports more than 2 data lanes. + */ + if (unicam->pipe.num_data_lanes == 2) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT2, val); + + if (unicam->pipe.num_data_lanes == 3) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT3, val); + } + + unicam_reg_write(unicam, UNICAM_IBLS, + node->fmt.fmt.pix.bytesperline); + unicam_wr_dma_addr(node, node->cur_frm); + unicam_set_packing_config(unicam, fmtinfo); + + ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt); + if (ret) { + /* + * If the source doesn't support frame descriptors, default to + * VC 0 and use the DT corresponding to the format. + */ + vc = 0; + dt = fmtinfo->csi_dt; + } + + unicam_cfg_image_id(unicam, vc, dt); + + val = unicam_reg_read(unicam, UNICAM_MISC); + unicam_set_field(&val, 1, UNICAM_FL0); + unicam_set_field(&val, 1, UNICAM_FL1); + unicam_reg_write(unicam, UNICAM_MISC, val); + + /* Enable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. + */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); +} + +static void unicam_start_metadata(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE]; + + unicam_enable_ed(unicam); + unicam_wr_dma_addr(node, node->cur_frm); + unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP); +} + +static void unicam_disable(struct unicam_device *unicam) +{ + /* Analogue lane control disable */ + unicam_reg_write_field(unicam, UNICAM_ANA, 1, UNICAM_DDL); + + /* Stop the output engine */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_SOE); + + /* Disable the data lanes. */ + unicam_reg_write(unicam, UNICAM_DAT0, 0); + unicam_reg_write(unicam, UNICAM_DAT1, 0); + + if (unicam->max_data_lanes > 2) { + unicam_reg_write(unicam, UNICAM_DAT2, 0); + unicam_reg_write(unicam, UNICAM_DAT3, 0); + } + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + usleep_range(50, 100); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + /* Disable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Clear ED setup */ + unicam_reg_write(unicam, UNICAM_DCS, 0); + + /* Disable all lane clocks */ + unicam_clk_write(unicam, 0); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static int __unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_route *route; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + for_each_active_route(&state->routing, route) { + const struct v4l2_mbus_framefmt *def_fmt; + struct v4l2_mbus_framefmt *fmt; + + if (route->source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) + def_fmt = &unicam_default_image_format; + else + def_fmt = &unicam_default_meta_format; + + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); + *fmt = *def_fmt; + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + *fmt = *def_fmt; + } + + return 0; +} + +static int unicam_subdev_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UNICAM_SD_PAD_SINK, + .sink_stream = 0, + .source_pad = UNICAM_SD_PAD_SOURCE_IMAGE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad. */ + return __unicam_subdev_set_routing(sd, state, &routing); +} + +static int unicam_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + u32 pad, stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + code->pad, code->stream, + &pad, &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(code->pad)) { + /* No transcoding, source and sink codes must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (code->index > 0) + return -EINVAL; + + code->code = fmt->code; + } else { + const struct unicam_format_info *formats; + unsigned int num_formats; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + if (code->index >= num_formats) + return -EINVAL; + + code->code = formats[code->index].code; + } + + return 0; +} + +static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + u32 pad, stream; + int ret; + + if (fse->index > 0) + return -EINVAL; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, fse->pad, + fse->stream, &pad, + &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(fse->pad)) { + /* No transcoding, source and sink formats must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (fse->code != fmt->code) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + } else { + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_code(fse->code, pad); + if (!fmtinfo) + return -EINVAL; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + fse->min_width = UNICAM_IMAGE_MIN_WIDTH; + fse->max_width = UNICAM_IMAGE_MAX_WIDTH; + fse->min_height = UNICAM_IMAGE_MIN_HEIGHT; + fse->max_height = UNICAM_IMAGE_MAX_HEIGHT; + } else { + fse->min_width = UNICAM_META_MIN_WIDTH; + fse->max_width = UNICAM_META_MAX_WIDTH; + fse->min_height = UNICAM_META_MIN_HEIGHT; + fse->max_height = UNICAM_META_MAX_HEIGHT; + } + } + + return 0; +} + +static int unicam_subdev_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + struct v4l2_mbus_framefmt *sink_format, *source_format; + const struct unicam_format_info *fmtinfo; + u32 source_pad, source_stream; + int ret; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + unicam->subdev.enabled_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (unicam_sd_pad_is_source(format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* + * Allowed formats for the stream on the sink pad depend on what source + * pad the stream is routed to. Find the corresponding source pad and + * use it to validate the media bus code. + */ + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + format->pad, format->stream, + &source_pad, &source_stream); + if (ret) + return ret; + + fmtinfo = unicam_find_format_by_code(format->format.code, source_pad); + if (!fmtinfo) { + fmtinfo = source_pad == UNICAM_SD_PAD_SOURCE_IMAGE + ? &unicam_image_formats[0] : &unicam_meta_formats[0]; + format->format.code = fmtinfo->code; + } + + if (source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + } else { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + + /* Colorspace don't apply to metadata. */ + format->format.colorspace = 0; + format->format.ycbcr_enc = 0; + format->format.quantization = 0; + format->format.xfer_func = 0; + } + + sink_format = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + source_format = v4l2_subdev_state_get_format(state, source_pad, + source_stream); + *sink_format = format->format; + *source_format = format->format; + + return 0; +} + +static int unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && unicam->subdev.enabled_streams) + return -EBUSY; + + return __unicam_subdev_set_routing(sd, state, routing); +} + +static int unicam_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + if (!unicam->subdev.enabled_streams) { + /* Configure and start Unicam. */ + unicam->sequence = 0; + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + unicam_start_metadata(unicam); + + unicam_start_rx(unicam, state); + } + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + if (ret) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + return ret; + } + + unicam->subdev.enabled_streams |= BIT(other_stream); + + return 0; +} + +static int unicam_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + v4l2_subdev_disable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + + unicam->subdev.enabled_streams &= ~BIT(other_stream); + + if (!unicam->subdev.enabled_streams) + unicam_disable(unicam); + + return 0; +} + +static const struct v4l2_subdev_pad_ops unicam_subdev_pad_ops = { + .enum_mbus_code = unicam_subdev_enum_mbus_code, + .enum_frame_size = unicam_subdev_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = unicam_subdev_set_format, + .set_routing = unicam_subdev_set_routing, + .enable_streams = unicam_sd_enable_streams, + .disable_streams = unicam_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops unicam_subdev_ops = { + .pad = &unicam_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops unicam_subdev_internal_ops = { + .init_state = unicam_subdev_init_state, +}; + +static const struct media_entity_operations unicam_subdev_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static int unicam_subdev_init(struct unicam_device *unicam) +{ + struct v4l2_subdev *sd = &unicam->subdev.sd; + int ret; + + v4l2_subdev_init(sd, &unicam_subdev_ops); + sd->internal_ops = &unicam_subdev_internal_ops; + v4l2_set_subdevdata(sd, unicam); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &unicam_subdev_media_ops; + sd->dev = unicam->dev; + sd->owner = THIS_MODULE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + + strscpy(sd->name, "unicam", sizeof(sd->name)); + + unicam->subdev.pads[UNICAM_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_IMAGE].flags = MEDIA_PAD_FL_SOURCE; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_METADATA].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(unicam->subdev.pads), + unicam->subdev.pads); + if (ret) { + dev_err(unicam->dev, "Failed to initialize media entity: %d\n", + ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(unicam->dev, "Failed to initialize subdev: %d\n", ret); + goto err_entity; + } + + ret = v4l2_device_register_subdev(&unicam->v4l2_dev, sd); + if (ret) { + dev_err(unicam->dev, "Failed to register subdev: %d\n", ret); + goto err_subdev; + } + + return 0; + +err_subdev: + v4l2_subdev_cleanup(sd); +err_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +static void unicam_subdev_cleanup(struct unicam_device *unicam) +{ + v4l2_subdev_cleanup(&unicam->subdev.sd); + media_entity_cleanup(&unicam->subdev.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Videobuf2 queue operations + */ + +static int unicam_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (*nplanes) { + if (sizes[0] < size) { + dev_dbg(node->dev->dev, "sizes[0] %i < size %u\n", + sizes[0], size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int unicam_buffer_prepare(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (vb2_plane_size(vb, 0) < size) { + dev_dbg(node->dev->dev, + "data will not fit into plane (%lu < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + buf->dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = size; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + + return 0; +} + +static void unicam_return_buffers(struct unicam_node *node, + enum vb2_buffer_state state) +{ + struct unicam_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + state); + if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + state); + + node->cur_frm = NULL; + node->next_frm = NULL; +} + +static int unicam_num_data_lanes(struct unicam_device *unicam) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + unsigned int num_data_lanes; + int ret; + + if (unicam->bus_type != V4L2_MBUS_CSI2_DPHY) + return unicam->max_data_lanes; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_mbus_config, + unicam->sensor.pad->index, &mbus_config); + if (ret == -ENOIOCTLCMD) + return unicam->max_data_lanes; + + if (ret < 0) { + dev_err(unicam->dev, "Failed to get mbus config: %d\n", ret); + return ret; + } + + num_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && num_data_lanes != 4) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, invalid\n", + unicam->sensor.subdev->name, num_data_lanes); + return -EINVAL; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, >%u configured in DT\n", + unicam->sensor.subdev->name, num_data_lanes, + unicam->max_data_lanes); + return -EINVAL; + } + + return num_data_lanes; +} + +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + struct unicam_buffer *buf; + struct media_pipeline_pad_iter iter; + struct media_pad *pad; + unsigned long flags; + int ret; + + dev_dbg(unicam->dev, "Starting stream on %s device\n", + is_metadata_node(node) ? "metadata" : "image"); + + /* + * Start the pipeline. This validates all links, and populates the + * pipeline structure. + */ + ret = video_device_pipeline_start(&node->video_dev, &unicam->pipe.pipe); + if (ret < 0) { + dev_dbg(unicam->dev, "Failed to start media pipeline: %d\n", ret); + goto err_buffers; + } + + /* + * Determine which video nodes are included in the pipeline, and get the + * number of data lanes. + */ + if (unicam->pipe.pipe.start_count == 1) { + unicam->pipe.nodes = 0; + + media_pipeline_for_each_pad(&unicam->pipe.pipe, &iter, pad) { + if (pad->entity != &unicam->subdev.sd.entity) + continue; + + if (pad->index == UNICAM_SD_PAD_SOURCE_IMAGE) + unicam->pipe.nodes |= BIT(UNICAM_IMAGE_NODE); + else if (pad->index == UNICAM_SD_PAD_SOURCE_METADATA) + unicam->pipe.nodes |= BIT(UNICAM_METADATA_NODE); + } + + if (!(unicam->pipe.nodes & BIT(UNICAM_IMAGE_NODE))) { + dev_dbg(unicam->dev, + "Pipeline does not include image node\n"); + ret = -EPIPE; + goto err_pipeline; + } + + ret = unicam_num_data_lanes(unicam); + if (ret < 0) + goto err_pipeline; + + unicam->pipe.num_data_lanes = ret; + + dev_dbg(unicam->dev, "Running with %u data lanes, nodes %u\n", + unicam->pipe.num_data_lanes, unicam->pipe.nodes); + } + + /* Arm the node with the first buffer from the DMA queue. */ + spin_lock_irqsave(&node->dma_queue_lock, flags); + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->cur_frm = buf; + node->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&node->dma_queue_lock, flags); + + /* + * Wait for all the video devices in the pipeline to have been started + * before starting the hardware. In the general case, this would + * prevent capturing multiple streams independently. However, the + * Unicam DMA engines are not generic, they have been designed to + * capture image data and embedded data from the same camera sensor. + * Not only does the main use case not benefit from independent + * capture, it requires proper synchronization of the streams at start + * time. + */ + if (unicam->pipe.pipe.start_count < hweight32(unicam->pipe.nodes)) + return 0; + + ret = pm_runtime_resume_and_get(unicam->dev); + if (ret < 0) { + dev_err(unicam->dev, "PM runtime resume failed: %d\n", ret); + goto err_pipeline; + } + + /* Enable the streams on the source. */ + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_pm_put; + } + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) { + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_disable_streams; + } + } + + return 0; + +err_disable_streams: + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, BIT(0)); +err_pm_put: + pm_runtime_put_sync(unicam->dev); +err_pipeline: + video_device_pipeline_stop(&node->video_dev); +err_buffers: + unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void unicam_stop_streaming(struct vb2_queue *vq) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + + /* Stop the hardware when the first video device gets stopped. */ + if (unicam->pipe.pipe.start_count == hweight32(unicam->pipe.nodes)) { + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + + pm_runtime_put(unicam->dev); + } + + video_device_pipeline_stop(&node->video_dev); + + /* Clear all queued buffers for the node */ + unicam_return_buffers(node, VB2_BUF_STATE_ERROR); +} + +static void unicam_buffer_queue(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + + spin_lock_irq(&node->dma_queue_lock); + list_add_tail(&buf->list, &node->dma_queue); + spin_unlock_irq(&node->dma_queue_lock); +} + +static const struct vb2_ops unicam_video_qops = { + .queue_setup = unicam_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = unicam_buffer_prepare, + .start_streaming = unicam_start_streaming, + .stop_streaming = unicam_stop_streaming, + .buf_queue = unicam_buffer_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 video device operations + */ + +static int unicam_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; + + return 0; +} + +static int unicam_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int index; + unsigned int i; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_image_formats); i++) { + if (f->mbus_code && unicam_image_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].fourcc; + return 0; + } + + index++; + + if (!unicam_image_formats[i].unpacked_fourcc) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].unpacked_fourcc; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + *f = node->fmt; + + return 0; +} + +static void __unicam_try_fmt_vid(struct unicam_node *node, + struct v4l2_pix_format *pix) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(pix->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (!fmtinfo) { + fmtinfo = &unicam_image_formats[0]; + pix->pixelformat = fmtinfo->fourcc; + } + + unicam_calc_image_size_bpl(node->dev, fmtinfo, pix); + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; +} + +static int unicam_try_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_vid(node, &f->fmt.pix); + return 0; +} + +static int unicam_s_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_vid(node, &f->fmt.pix); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_fmt_meta(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int i, index; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_meta_formats); i++) { + if (f->mbus_code && unicam_meta_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_meta_formats[i].fourcc; + f->type = V4L2_BUF_TYPE_META_CAPTURE; + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + f->fmt.meta = node->fmt.fmt.meta; + + return 0; +} + +static const struct unicam_format_info * +__unicam_try_fmt_meta(struct unicam_node *node, struct v4l2_meta_format *meta) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(meta->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (!fmtinfo) { + fmtinfo = &unicam_meta_formats[0]; + meta->dataformat = fmtinfo->fourcc; + } + + unicam_calc_meta_size_bpl(node->dev, fmtinfo, meta); + + return fmtinfo; +} + +static int unicam_try_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_meta(node, &f->fmt.meta); + return 0; +} + +static int unicam_s_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_meta(node, &f->fmt.meta); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct unicam_node *node = video_drvdata(file); + int ret = -EINVAL; + + if (fsize->index > 0) + return ret; + + if (is_image_node(node)) { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_IMAGE)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_IMAGE_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_IMAGE_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_IMAGE_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_IMAGE_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } else { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_METADATA)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_META_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_META_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_META_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_META_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } + + return 0; +} + +static int unicam_log_status(struct file *file, void *fh) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *unicam = node->dev; + u32 reg; + + /* status for sub devices */ + v4l2_device_call_all(&unicam->v4l2_dev, 0, core, log_status); + + dev_info(unicam->dev, "-----Receiver status-----\n"); + dev_info(unicam->dev, "V4L2 width/height: %ux%u\n", + node->fmt.fmt.pix.width, node->fmt.fmt.pix.height); + dev_info(unicam->dev, "V4L2 format: %08x\n", + node->fmt.fmt.pix.pixelformat); + reg = unicam_reg_read(unicam, UNICAM_IPIPE); + dev_info(unicam->dev, "Unpacking/packing: %u / %u\n", + unicam_get_field(reg, UNICAM_PUM_MASK), + unicam_get_field(reg, UNICAM_PPM_MASK)); + dev_info(unicam->dev, "----Live data----\n"); + dev_info(unicam->dev, "Programmed stride: %4u\n", + unicam_reg_read(unicam, UNICAM_IBLS)); + dev_info(unicam->dev, "Detected resolution: %ux%u\n", + unicam_reg_read(unicam, UNICAM_IHSTA), + unicam_reg_read(unicam, UNICAM_IVSTA)); + dev_info(unicam->dev, "Write pointer: %08x\n", + unicam_reg_read(unicam, UNICAM_IBWP)); + + return 0; +} + +static int unicam_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 2, NULL); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops unicam_ioctl_ops = { + .vidioc_querycap = unicam_querycap, + + .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid, + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid, + + .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta, + .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta, + .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta, + + .vidioc_enum_framesizes = unicam_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = unicam_log_status, + .vidioc_subscribe_event = unicam_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* unicam capture driver file operations */ +static const struct v4l2_file_operations unicam_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int unicam_video_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct unicam_node *node = video_get_drvdata(vdev); + const u32 pad = is_image_node(node) ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + ret = -EINVAL; + goto out; + } + + if (is_image_node(node)) { + const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width || + fmt->field != format->field) { + dev_dbg(node->dev->dev, + "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n", + fmt->width, fmt->height, fmtinfo->code, + v4l2_field_names[fmt->field], + format->width, format->height, format->code, + v4l2_field_names[format->field]); + ret = -EPIPE; + } + } else { + const struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width) { + dev_dbg(node->dev->dev, + "meta: (%u x %u) 0x%04x != (%u x %u) 0x%04x\n", + fmt->width, fmt->height, fmtinfo->code, + format->width, format->height, format->code); + ret = -EPIPE; + } + } + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static const struct media_entity_operations unicam_video_media_ops = { + .link_validate = unicam_video_link_validate, +}; + +static void unicam_node_release(struct video_device *vdev) +{ + struct unicam_node *node = video_get_drvdata(vdev); + + unicam_put(node->dev); +} + +static void unicam_set_default_format(struct unicam_node *node) +{ + if (is_image_node(node)) { + struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo = + &unicam_image_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + v4l2_fill_pix_format(fmt, &unicam_default_image_format); + fmt->pixelformat = fmtinfo->fourcc; + unicam_calc_image_size_bpl(node->dev, fmtinfo, fmt); + } else { + struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + const struct unicam_format_info *fmtinfo = + &unicam_meta_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + + fmt->dataformat = fmtinfo->fourcc; + fmt->width = unicam_default_meta_format.width; + fmt->height = unicam_default_meta_format.height; + unicam_calc_meta_size_bpl(node->dev, fmtinfo, fmt); + } +} + +static int unicam_register_node(struct unicam_device *unicam, + enum unicam_node_type type) +{ + const u32 pad_index = type == UNICAM_IMAGE_NODE + ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + struct unicam_node *node = &unicam->node[type]; + struct video_device *vdev = &node->video_dev; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + node->dev = unicam_get(unicam); + node->id = type; + + spin_lock_init(&node->dma_queue_lock); + + INIT_LIST_HEAD(&node->dma_queue); + + /* Initialize the videobuf2 queue. */ + q->type = type == UNICAM_IMAGE_NODE ? V4L2_BUF_TYPE_VIDEO_CAPTURE + : V4L2_BUF_TYPE_META_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = node; + q->ops = &unicam_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct unicam_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &unicam->lock; + q->min_queued_buffers = 1; + q->dev = unicam->dev; + + ret = vb2_queue_init(q); + if (ret) { + dev_err(unicam->dev, "vb2_queue_init() failed\n"); + goto err_unicam_put; + } + + /* Initialize the video device. */ + vdev->release = unicam_node_release; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; + vdev->v4l2_dev = &unicam->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &unicam->lock; + vdev->device_caps = type == UNICAM_IMAGE_NODE + ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &unicam_video_media_ops; + + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, + type == UNICAM_IMAGE_NODE ? "image" : "embedded"); + + video_set_drvdata(vdev, node); + + if (type == UNICAM_IMAGE_NODE) + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + + node->pad.flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret) + goto err_unicam_put; + + node->dummy_buf.size = UNICAM_DUMMY_BUF_SIZE; + node->dummy_buf_cpu_addr = dma_alloc_coherent(unicam->dev, + node->dummy_buf.size, + &node->dummy_buf.dma_addr, + GFP_KERNEL); + if (!node->dummy_buf_cpu_addr) { + dev_err(unicam->dev, "Unable to allocate dummy buffer.\n"); + ret = -ENOMEM; + goto err_entity_cleanup; + } + + unicam_set_default_format(node); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(unicam->dev, "Unable to register video device %s\n", + vdev->name); + goto err_dma_free; + } + + node->registered = true; + + ret = media_create_pad_link(&unicam->subdev.sd.entity, + pad_index, + &node->video_dev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + /* + * No need for cleanup, the caller will unregister the + * video device, which will drop the reference on the + * device and trigger the cleanup. + */ + dev_err(unicam->dev, "Unable to create pad link for %s\n", + unicam->sensor.subdev->name); + return ret; + } + + return 0; + +err_dma_free: + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_unicam_put: + unicam_put(unicam); + return ret; +} + +static void unicam_unregister_nodes(struct unicam_device *unicam) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (node->registered) { + vb2_video_unregister_device(&node->video_dev); + node->registered = false; + } + + if (node->dummy_buf_cpu_addr) + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); + } +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int unicam_runtime_resume(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + int ret; + + ret = clk_set_min_rate(unicam->vpu_clock, UNICAM_MIN_VPU_CLOCK_RATE); + if (ret) { + dev_err(unicam->dev, "failed to set up VPU clock\n"); + return ret; + } + + ret = clk_prepare_enable(unicam->vpu_clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable VPU clock: %d\n", ret); + goto err_vpu_clock; + } + + ret = clk_set_rate(unicam->clock, 100 * 1000 * 1000); + if (ret) { + dev_err(unicam->dev, "failed to set up CSI clock\n"); + goto err_vpu_prepare; + } + + ret = clk_prepare_enable(unicam->clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable CSI clock: %d\n", ret); + goto err_vpu_prepare; + } + + return 0; + +err_vpu_prepare: + clk_disable_unprepare(unicam->vpu_clock); +err_vpu_clock: + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + return ret; +} + +static int unicam_runtime_suspend(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + + clk_disable_unprepare(unicam->clock); + + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + clk_disable_unprepare(unicam->vpu_clock); + + return 0; +} + +static const struct dev_pm_ops unicam_pm_ops = { + RUNTIME_PM_OPS(unicam_runtime_suspend, unicam_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 async notifier + */ + +static int unicam_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + struct media_pad *sink = &unicam->subdev.pads[UNICAM_SD_PAD_SINK]; + struct media_pad *source; + int ret; + + dev_dbg(unicam->dev, "Using sensor %s for capture\n", + subdev->name); + + ret = v4l2_create_fwnode_links_to_pad(subdev, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + + source = media_pad_remote_pad_unique(sink); + if (IS_ERR(source)) { + dev_err(unicam->dev, "No connected sensor pad\n"); + return PTR_ERR(source); + } + + unicam->sensor.subdev = subdev; + unicam->sensor.pad = source; + + return 0; +} + +static int unicam_async_complete(struct v4l2_async_notifier *notifier) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + int ret; + + ret = unicam_register_node(unicam, UNICAM_IMAGE_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register image video device.\n"); + goto unregister; + } + + ret = unicam_register_node(unicam, UNICAM_METADATA_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register metadata video device.\n"); + goto unregister; + } + + ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); + if (ret) { + dev_err(unicam->dev, "Unable to register subdev nodes.\n"); + goto unregister; + } + + return 0; + +unregister: + unicam_unregister_nodes(unicam); + unicam_put(unicam); + + return ret; +} + +static const struct v4l2_async_notifier_operations unicam_async_ops = { + .bound = unicam_async_bound, + .complete = unicam_async_complete, +}; + +static int unicam_async_nf_init(struct unicam_device *unicam) +{ + struct v4l2_fwnode_endpoint ep = { }; + struct fwnode_handle *ep_handle; + struct v4l2_async_connection *asc; + int ret; + + ret = of_property_read_u32(unicam->dev->of_node, "brcm,num-data-lanes", + &unicam->max_data_lanes); + if (ret < 0) { + dev_err(unicam->dev, "Missing %s DT property\n", + "brcm,num-data-lanes"); + return -EINVAL; + } + + /* Get and parse the local endpoint. */ + ep_handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(unicam->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep_handle) { + dev_err(unicam->dev, "No endpoint found\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep_handle, &ep); + if (ret) { + dev_err(unicam->dev, "Failed to parse endpoint: %d\n", ret); + goto error; + } + + unicam->bus_type = ep.bus_type; + + switch (ep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: { + unsigned int num_data_lanes = ep.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && + num_data_lanes != 4) { + dev_err(unicam->dev, "%u data lanes not supported\n", + num_data_lanes); + ret = -EINVAL; + goto error; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Endpoint uses %u data lanes when %u are supported\n", + num_data_lanes, unicam->max_data_lanes); + ret = -EINVAL; + goto error; + } + + unicam->max_data_lanes = num_data_lanes; + unicam->bus_flags = ep.bus.mipi_csi2.flags; + break; + } + + case V4L2_MBUS_CCP2: + unicam->max_data_lanes = 1; + unicam->bus_flags = ep.bus.mipi_csi1.strobe; + break; + + default: + /* Unsupported bus type */ + dev_err(unicam->dev, "Unsupported bus type %u\n", ep.bus_type); + ret = -EINVAL; + goto error; + } + + /* Initialize and register the async notifier. */ + v4l2_async_nf_init(&unicam->notifier, &unicam->v4l2_dev); + + asc = v4l2_async_nf_add_fwnode_remote(&unicam->notifier, ep_handle, + struct v4l2_async_connection); + fwnode_handle_put(ep_handle); + ep_handle = NULL; + + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + dev_err(unicam->dev, "Failed to add entry to notifier: %d\n", + ret); + goto error; + } + + unicam->notifier.ops = &unicam_async_ops; + + ret = v4l2_async_nf_register(&unicam->notifier); + if (ret) { + dev_err(unicam->dev, "Error registering device notifier: %d\n", + ret); + goto error; + } + + return 0; + +error: + fwnode_handle_put(ep_handle); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & remove + */ + +static int unicam_media_init(struct unicam_device *unicam) +{ + int ret; + + unicam->mdev.dev = unicam->dev; + strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, + sizeof(unicam->mdev.model)); + unicam->mdev.hw_revision = 0; + + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; + + ret = v4l2_device_register(unicam->dev, &unicam->v4l2_dev); + if (ret < 0) { + dev_err(unicam->dev, "Unable to register v4l2 device\n"); + goto err_media_cleanup; + } + + ret = media_device_register(&unicam->mdev); + if (ret < 0) { + dev_err(unicam->dev, + "Unable to register media-controller device\n"); + goto err_v4l2_unregister; + } + + return 0; + +err_v4l2_unregister: + v4l2_device_unregister(&unicam->v4l2_dev); +err_media_cleanup: + media_device_cleanup(&unicam->mdev); + return ret; +} + +static int unicam_probe(struct platform_device *pdev) +{ + struct unicam_device *unicam; + int ret; + + unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); + if (!unicam) + return -ENOMEM; + + kref_init(&unicam->kref); + mutex_init(&unicam->lock); + + unicam->dev = &pdev->dev; + platform_set_drvdata(pdev, unicam); + + unicam->base = devm_platform_ioremap_resource_byname(pdev, "unicam"); + if (IS_ERR(unicam->base)) { + ret = PTR_ERR(unicam->base); + goto err_unicam_put; + } + + unicam->clk_gate_base = devm_platform_ioremap_resource_byname(pdev, "cmi"); + if (IS_ERR(unicam->clk_gate_base)) { + ret = PTR_ERR(unicam->clk_gate_base); + goto err_unicam_put; + } + + unicam->clock = devm_clk_get(&pdev->dev, "lp"); + if (IS_ERR(unicam->clock)) { + dev_err(unicam->dev, "Failed to get lp clock\n"); + ret = PTR_ERR(unicam->clock); + goto err_unicam_put; + } + + unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu"); + if (IS_ERR(unicam->vpu_clock)) { + dev_err(unicam->dev, "Failed to get vpu clock\n"); + ret = PTR_ERR(unicam->vpu_clock); + goto err_unicam_put; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_unicam_put; + + ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, + "unicam_capture0", unicam); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + goto err_unicam_put; + } + + /* Enable the block power domain. */ + pm_runtime_enable(&pdev->dev); + + ret = unicam_media_init(unicam); + if (ret) + goto err_pm_runtime; + + ret = unicam_subdev_init(unicam); + if (ret) + goto err_media_unregister; + + ret = unicam_async_nf_init(unicam); + if (ret) + goto err_subdev_unregister; + + return 0; + +err_subdev_unregister: + unicam_subdev_cleanup(unicam); +err_media_unregister: + media_device_unregister(&unicam->mdev); +err_pm_runtime: + pm_runtime_disable(&pdev->dev); +err_unicam_put: + unicam_put(unicam); + + return ret; +} + +static void unicam_remove(struct platform_device *pdev) +{ + struct unicam_device *unicam = platform_get_drvdata(pdev); + + unicam_unregister_nodes(unicam); + v4l2_device_unregister(&unicam->v4l2_dev); + media_device_unregister(&unicam->mdev); + v4l2_async_nf_unregister(&unicam->notifier); + + unicam_subdev_cleanup(unicam); + + unicam_put(unicam); + + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id unicam_of_match[] = { + { .compatible = "brcm,bcm2835-unicam", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, unicam_of_match); + +static struct platform_driver unicam_driver = { + .probe = unicam_probe, + .remove = unicam_remove, + .driver = { + .name = UNICAM_MODULE_NAME, + .pm = pm_ptr(&unicam_pm_ops), + .of_match_table = unicam_of_match, + }, +}; + +module_platform_driver(unicam_driver); + +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("BCM2835 Unicam driver"); +MODULE_LICENSE("GPL"); From 28ba811b17fd29315ab41583da8643cc92380c81 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 24 Oct 2024 15:54:10 +0200 Subject: [PATCH 106/159] media: broadcom: bcm2835: Fix compile issues on BSP Fix a few minor issues to allow the mainline driver version to compile on the v6.6.y RaspberryPi BSP. Signed-off-by: Jacopo Mondi --- drivers/media/platform/broadcom/bcm2835-unicam.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 6dcc6c13a7540e..833b20f00b1321 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -2242,7 +2242,7 @@ static int unicam_register_node(struct unicam_device *unicam, q->buf_struct_size = sizeof(struct unicam_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &unicam->lock; - q->min_queued_buffers = 1; + q->min_buffers_needed = 1; q->dev = unicam->dev; ret = vb2_queue_init(q); @@ -2700,7 +2700,7 @@ static int unicam_probe(struct platform_device *pdev) return ret; } -static void unicam_remove(struct platform_device *pdev) +static int unicam_remove(struct platform_device *pdev) { struct unicam_device *unicam = platform_get_drvdata(pdev); @@ -2714,6 +2714,8 @@ static void unicam_remove(struct platform_device *pdev) unicam_put(unicam); pm_runtime_disable(&pdev->dev); + + return 0; } static const struct of_device_id unicam_of_match[] = { From 1bac79aed6dd1116ee4ad94c3ee2489970a0fcd1 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Fri, 16 Jun 2023 16:24:19 +0100 Subject: [PATCH 107/159] drivers: media: bcm2835_unicam: Improve frame sequence count handling Ensure that the frame sequence counter is incremented only if a previous frame start interrupt has occurred, or a frame start + frame end has occurred simultaneously. This corresponds the sequence number with the actual number of frames produced by the sensor, not the number of frame buffers dequeued back to userland. Signed-off-by: Naushir Patuck (cherry picked from commit ea9953fe8cff5e0b8e456f36558b054ade871ffe) Signed-off-by: Jacopo Mondi --- .../media/platform/broadcom/bcm2835-unicam.c | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 833b20f00b1321..16d2fec57f46af 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -199,6 +199,7 @@ struct unicam_device { /* subdevice async notifier */ struct v4l2_async_notifier notifier; unsigned int sequence; + bool frame_started; /* Sensor node */ struct { @@ -742,6 +743,8 @@ static irqreturn_t unicam_isr(int irq, void *dev) * buffer forever. */ if (fe) { + bool inc_seq = unicam->frame_started; + /* * Ensure we have swapped buffers already as we can't * stop the peripheral. If no buffer is available, use a @@ -761,11 +764,24 @@ static irqreturn_t unicam_isr(int irq, void *dev) * + FS + LS). In this case, we cannot signal the buffer * as complete, as the HW will reuse that buffer. */ - if (node->cur_frm && node->cur_frm != node->next_frm) + if (node->cur_frm && node->cur_frm != node->next_frm) { unicam_process_buffer_complete(node, sequence); + inc_seq = true; + } node->cur_frm = node->next_frm; } - unicam->sequence++; + + /* + * Increment the sequence number conditionally on either a FS + * having already occurred, or in the FE + FS condition as + * caught in the FE handler above. This ensures the sequence + * number corresponds to the frames generated by the sensor, not + * the frames dequeued to userland. + */ + if (inc_seq) { + unicam->sequence++; + unicam->frame_started = false; + } } if (ista & UNICAM_FSI) { @@ -795,6 +811,7 @@ static irqreturn_t unicam_isr(int irq, void *dev) } unicam_queue_event_sof(unicam); + unicam->frame_started = true; } /* @@ -1413,6 +1430,7 @@ static int unicam_sd_enable_streams(struct v4l2_subdev *sd, if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) unicam_start_metadata(unicam); + unicam->frame_started = false; unicam_start_rx(unicam, state); } From 9d55895d0f0b8bfa1bbfbcb312f3b994fa6c5175 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 3 Apr 2024 16:06:08 +0100 Subject: [PATCH 108/159] media: bcm2835-unicam: Add option for a GPIO to reflect FS/FE timing The legacy stack had an option to have a GPIO track frame start and end events to give basic synchronisation to the incoming image stream. https://forums.raspberrypi.com/viewtopic.php?t=190314 Replicate this in the kernel Unicam driver. Signed-off-by: Dave Stevenson (cherry picked from commit 6a0ae67b699bef301e835b814a416067567ecd39) Signed-off-by: Jacopo Mondi --- drivers/media/platform/broadcom/bcm2835-unicam.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 16d2fec57f46af..1a6a0eacd49d9f 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,8 @@ struct unicam_device { struct v4l2_device v4l2_dev; struct media_device mdev; + struct gpio_desc *sync_gpio; + /* parent device */ struct device *dev; /* subdevice async notifier */ @@ -745,6 +748,9 @@ static irqreturn_t unicam_isr(int irq, void *dev) if (fe) { bool inc_seq = unicam->frame_started; + if (unicam->sync_gpio) + gpiod_set_value(unicam->sync_gpio, 0); + /* * Ensure we have swapped buffers already as we can't * stop the peripheral. If no buffer is available, use a @@ -790,6 +796,10 @@ static irqreturn_t unicam_isr(int irq, void *dev) * aka frame start. */ ts = ktime_get_ns(); + + if (unicam->sync_gpio) + gpiod_set_value(unicam->sync_gpio, 1); + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { struct unicam_node *node = &unicam->node[i]; @@ -2678,6 +2688,9 @@ static int unicam_probe(struct platform_device *pdev) goto err_unicam_put; } + unicam->sync_gpio = devm_gpiod_get_optional(&pdev->dev, "sync", + GPIOD_OUT_LOW); + ret = platform_get_irq(pdev, 0); if (ret < 0) goto err_unicam_put; From fb610c565a53ab39983ac76de6f41df89e80089d Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 23 Oct 2024 14:52:03 +0530 Subject: [PATCH 109/159] uapi: bcm2835-isp: Re-assign V4L2_CID_USER_BCM2835_ISP_BASE V4L2_CID_USER_BCM2835_ISP_BASE is defined in RPi BSP as (V4L2_CID_USER_BASE + 0x10e0) However in mainline this address has been occupied by other control bases. Align V4L2_CID_USER_BCM2835_ISP_BASE to the last available identifier available in mainline. Signed-off-by: Jai Luthra Signed-off-by: Jacopo Mondi --- include/uapi/linux/v4l2-controls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 22bfc38787cc34..bd1cc0646aad3e 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -205,7 +205,7 @@ enum v4l2_colorfx { /* The base for the bcm2835-isp driver controls. * We reserve 16 controls for this driver. */ -#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10e0) +#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x11d0) /* * The base for IMX500 driver controls. From 2f5af07b055c0c255b7f70c46745e7fa39a9f446 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 23 Oct 2024 14:48:03 +0530 Subject: [PATCH 110/159] ARM: dts: bcm270x: Update unicam node The unicam driver from mainline expects additional required properties: reg-names and brcm,num-data-lanes. Signed-off-by: Jai Luthra --- arch/arm/boot/dts/broadcom/bcm270x.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/broadcom/bcm270x.dtsi b/arch/arm/boot/dts/broadcom/bcm270x.dtsi index e077bd2711230c..049136ba201663 100644 --- a/arch/arm/boot/dts/broadcom/bcm270x.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm270x.dtsi @@ -89,6 +89,8 @@ compatible = "brcm,bcm2835-unicam"; reg = <0x7e800000 0x800>, <0x7e802000 0x4>; + reg-names = "unicam", "cmi"; + brcm,num-data-lanes = <2>; interrupts = <2 6>; clocks = <&clocks BCM2835_CLOCK_CAM0>, <&firmware_clocks 4>; @@ -104,6 +106,8 @@ compatible = "brcm,bcm2835-unicam"; reg = <0x7e801000 0x800>, <0x7e802004 0x4>; + reg-names = "unicam", "cmi"; + brcm,num-data-lanes = <2>; interrupts = <2 7>; clocks = <&clocks BCM2835_CLOCK_CAM1>, <&firmware_clocks 4>; From 1ff5b3fbcd4b31621f81a41d9775162b3244ea5a Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 17:59:47 +0530 Subject: [PATCH 111/159] Revert "fixup downstream patch post driver conversion to CCI_REG" This reverts commit ccb344967e5fa0616cdd8fded929612b01121997. --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 51cbcdd9c25e4f..bbcd451e1c63c3 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -88,7 +88,7 @@ /* HBLANK control range */ #define IMX219_PPL_MIN 3448 #define IMX219_PPL_MAX 0x7ff0 -#define IMX219_REG_HTS CCI_REG16(0x0162) +#define IMX219_REG_HTS 0x0162 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) From dc07b3a0e922cee86a34a3f17da7d4850f4a25f0 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 17:59:56 +0530 Subject: [PATCH 112/159] Revert "media: i2c: imx219: fix binning and rate_factor for 480p and 1232p" This reverts commit 24e8a7d7181be45e0d67335f05bf0773fe6ad261. --- drivers/media/i2c/imx219.c | 144 ++++++++++--------------------------- 1 file changed, 39 insertions(+), 105 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index bbcd451e1c63c3..60c90bb60b1e69 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -104,8 +104,8 @@ /* Binning Mode */ #define IMX219_REG_BINNING_MODE CCI_REG16(0x0174) #define IMX219_BINNING_NONE 0x0000 -#define IMX219_BINNING_2X2_NORMAL 0x0101 -#define IMX219_BINNING_2X2_SPECIAL 0x0303 +#define IMX219_BINNING_2X2 0x0101 +#define IMX219_BINNING_2X2_ANALOG 0x0303 #define IMX219_REG_CSI_DATA_FORMAT_A CCI_REG16(0x018c) @@ -171,18 +171,6 @@ enum pad_types { NUM_PADS }; -enum binning_mode { - BINNING_NONE, - BINNING_NORMAL_2x2, - BINNING_SPECIAL_2x2, -}; - -enum binning_bit_depths { - BINNING_IDX_8_BIT, - BINNING_IDX_10_BIT, - BINNING_IDX_MAX -}; - struct imx219_reg { u16 address; u8 val; @@ -209,8 +197,11 @@ struct imx219_mode { /* Default register values */ struct imx219_reg_list reg_list; - /* binning mode based on format code */ - enum binning_mode binning[BINNING_IDX_MAX]; + /* 2x2 binning is used */ + bool binning; + + /* Relative pixel clock rate factor for the mode. */ + unsigned int rate_factor; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -415,10 +406,8 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs, }, - .binning = { - [BINNING_IDX_8_BIT] = BINNING_NONE, - [BINNING_IDX_10_BIT] = BINNING_NONE, - }, + .binning = false, + .rate_factor = 1, }, { /* 1080P 30fps cropped */ @@ -435,10 +424,8 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs, }, - .binning = { - [BINNING_IDX_8_BIT] = BINNING_NONE, - [BINNING_IDX_10_BIT] = BINNING_NONE, - }, + .binning = false, + .rate_factor = 1, }, { /* 2x2 binned 30fps mode */ @@ -455,10 +442,8 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs, }, - .binning = { - [BINNING_IDX_8_BIT] = BINNING_SPECIAL_2x2, - [BINNING_IDX_10_BIT] = BINNING_NORMAL_2x2, - }, + .binning = true, + .rate_factor = 1, }, { /* 640x480 30fps mode */ @@ -475,10 +460,12 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs, }, - .binning = { - [BINNING_IDX_8_BIT] = BINNING_SPECIAL_2x2, - [BINNING_IDX_10_BIT] = BINNING_SPECIAL_2x2, - }, + .binning = true, + /* + * This mode uses a special 2x2 binning that doubles the + * internal pixel clock rate. + */ + .rate_factor = 2, }, }; @@ -536,64 +523,12 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } -static int imx219_resolve_binning(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format, - enum binning_mode *binning) -{ - u32 fmt; - - if (format) - fmt = format->code; - else - fmt = MEDIA_BUS_FMT_SRGGB10_1X10; - - switch (fmt) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - *binning = imx219->mode->binning[BINNING_IDX_8_BIT]; - return 0; - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - *binning = imx219->mode->binning[BINNING_IDX_10_BIT]; - return 0; - } - return -EINVAL; -} - -static int imx219_get_rate_factor(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) -{ - enum binning_mode binning = BINNING_NONE; - int ret; - - ret = imx219_resolve_binning(imx219, format, &binning); - if (ret < 0) - return ret; - - switch (binning) { - case BINNING_NONE: - case BINNING_NORMAL_2x2: - return 1; - case BINNING_SPECIAL_2x2: - return 2; - } - return -EINVAL; -} - static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx219 *imx219 = container_of(ctrl->handler, struct imx219, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); int ret = 0; - const struct v4l2_mbus_framefmt *format; - struct v4l2_subdev_state *state; - int rate_factor; if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -615,10 +550,6 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) if (pm_runtime_get_if_in_use(&client->dev) == 0) return 0; - state = v4l2_subdev_get_locked_active_state(&imx219->sd); - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - rate_factor = imx219_get_rate_factor(imx219, format); - switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: cci_write(imx219->regmap, IMX219_REG_ANALOG_GAIN, @@ -626,7 +557,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val / rate_factor, &ret); + ctrl->val / imx219->mode->rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -643,7 +574,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - (imx219->mode->height + ctrl->val) / rate_factor, &ret); + (imx219->mode->height + ctrl->val) / imx219->mode->rate_factor, &ret); break; case V4L2_CID_HBLANK: cci_write(imx219->regmap, IMX219_REG_HTS, @@ -784,11 +715,10 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static unsigned long imx219_get_pixel_rate(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) +static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : - IMX219_PIXEL_RATE_4LANE) * imx219_get_rate_factor(imx219, format); + IMX219_PIXEL_RATE_4LANE) * imx219->mode->rate_factor; } static int imx219_set_pad_format(struct v4l2_subdev *sd, @@ -851,7 +781,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); /* Scale the pixel rate based on the mode specific factor */ - pixel_rate = imx219_get_pixel_rate(imx219, &fmt->format); + pixel_rate = imx219_get_pixel_rate(imx219); __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, pixel_rate, 1, pixel_rate); } @@ -889,20 +819,24 @@ static int imx219_set_framefmt(struct imx219 *imx219, static int imx219_set_binning(struct imx219 *imx219, const struct v4l2_mbus_framefmt *format) { - enum binning_mode binning = BINNING_NONE; - - imx219_resolve_binning(imx219, format, &binning); - - switch (binning) { - case BINNING_NONE: + if (!imx219->mode->binning) return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, IMX219_BINNING_NONE, NULL); - case BINNING_NORMAL_2x2: + + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2_NORMAL, NULL); - case BINNING_SPECIAL_2x2: + IMX219_BINNING_2X2_ANALOG, NULL); + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2_SPECIAL, NULL); + IMX219_BINNING_2X2, NULL); } return -EINVAL; @@ -1223,7 +1157,7 @@ static int imx219_init_controls(struct imx219 *imx219) return ret; /* By default, PIXEL_RATE is read only */ - pixel_rate = imx219_get_pixel_rate(imx219, NULL); + pixel_rate = imx219_get_pixel_rate(imx219); imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_PIXEL_RATE, pixel_rate, pixel_rate, 1, From 35840aaccb9a6925fe172d7b9473b55e71574848 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 18:00:21 +0530 Subject: [PATCH 113/159] Revert "media: i2c: imx219: Scale the pixel clock rate for the 640x480 mode" This reverts commit caebe4fe817b5079723e21430590460fbc842123. --- drivers/media/i2c/imx219.c | 41 +++++++++++--------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 60c90bb60b1e69..7ac037dd7a7f26 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -199,9 +199,6 @@ struct imx219_mode { /* 2x2 binning is used */ bool binning; - - /* Relative pixel clock rate factor for the mode. */ - unsigned int rate_factor; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -407,7 +404,6 @@ static const struct imx219_mode supported_modes[] = { .regs = mode_3280x2464_regs, }, .binning = false, - .rate_factor = 1, }, { /* 1080P 30fps cropped */ @@ -425,7 +421,6 @@ static const struct imx219_mode supported_modes[] = { .regs = mode_1920_1080_regs, }, .binning = false, - .rate_factor = 1, }, { /* 2x2 binned 30fps mode */ @@ -443,7 +438,6 @@ static const struct imx219_mode supported_modes[] = { .regs = mode_1640_1232_regs, }, .binning = true, - .rate_factor = 1, }, { /* 640x480 30fps mode */ @@ -461,11 +455,6 @@ static const struct imx219_mode supported_modes[] = { .regs = mode_640_480_regs, }, .binning = true, - /* - * This mode uses a special 2x2 binning that doubles the - * internal pixel clock rate. - */ - .rate_factor = 2, }, }; @@ -557,7 +546,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val / imx219->mode->rate_factor, &ret); + ctrl->val, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -574,7 +563,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - (imx219->mode->height + ctrl->val) / imx219->mode->rate_factor, &ret); + imx219->mode->height + ctrl->val, &ret); break; case V4L2_CID_HBLANK: cci_write(imx219->regmap, IMX219_REG_HTS, @@ -715,19 +704,13 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) -{ - return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : - IMX219_PIXEL_RATE_4LANE) * imx219->mode->rate_factor; -} - static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; - int exposure_max, exposure_def, hblank, pixel_rate; + int exposure_max, exposure_def, hblank; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; @@ -779,11 +762,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, IMX219_PPL_MAX - mode->width, 1, IMX219_PPL_MIN - mode->width); __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); - - /* Scale the pixel rate based on the mode specific factor */ - pixel_rate = imx219_get_pixel_rate(imx219); - __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, - pixel_rate, 1, pixel_rate); } } else { format = v4l2_subdev_get_pad_format(sd, sd_state, 1); @@ -1141,6 +1119,11 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { }; +static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) +{ + return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE; +} + /* Initialize control handlers */ static int imx219_init_controls(struct imx219 *imx219) { @@ -1148,7 +1131,7 @@ static int imx219_init_controls(struct imx219 *imx219) struct v4l2_ctrl_handler *ctrl_hdlr; unsigned int height = imx219->mode->height; struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank, pixel_rate; + int exposure_max, exposure_def, hblank; int i, ret; ctrl_hdlr = &imx219->ctrl_handler; @@ -1157,11 +1140,11 @@ static int imx219_init_controls(struct imx219 *imx219) return ret; /* By default, PIXEL_RATE is read only */ - pixel_rate = imx219_get_pixel_rate(imx219); imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_PIXEL_RATE, - pixel_rate, pixel_rate, 1, - pixel_rate); + imx219_get_pixel_rate(imx219), + imx219_get_pixel_rate(imx219), 1, + imx219_get_pixel_rate(imx219)); imx219->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, From a10eae5a944e6d7ba570e0bf459a84a55abacb36 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 18:03:18 +0530 Subject: [PATCH 114/159] Revert "media: imx219: Advertise embedded data node on media pad 1" This reverts commit 41d7a5a226cc5a649b08d7f9ba3997730d878dbb. --- drivers/media/i2c/imx219.c | 167 +++++++++++++------------------------ 1 file changed, 56 insertions(+), 111 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7ac037dd7a7f26..143054b0074852 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -161,21 +161,6 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U -/* Embedded metadata stream structure */ -#define IMX219_EMBEDDED_LINE_WIDTH 16384 -#define IMX219_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - -struct imx219_reg { - u16 address; - u8 val; -}; - struct imx219_reg_list { unsigned int num_of_regs; const struct cci_reg_sequence *regs; @@ -460,7 +445,7 @@ static const struct imx219_mode supported_modes[] = { struct imx219 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; + struct media_pad pad; struct regmap *regmap; struct clk *xclk; /* system clock to IMX219 */ @@ -635,13 +620,6 @@ static int imx219_init_state(struct v4l2_subdev *sd, crop->width = IMX219_PIXEL_ARRAY_WIDTH; crop->height = IMX219_PIXEL_ARRAY_HEIGHT; - /* Initialize try_fmt for the embedded metadata pad */ - format = v4l2_subdev_get_pad_format(sd, state, 1); - format->code = MEDIA_BUS_FMT_SENSOR_DATA; - format->width = IMX219_EMBEDDED_LINE_WIDTH; - format->height = IMX219_NUM_EMBEDDED_LINES; - format->field = V4L2_FIELD_NONE; - return 0; } @@ -651,20 +629,10 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); - if (code->pad >= NUM_PADS) + if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) - return -EINVAL; - - code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); - } else { - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SENSOR_DATA; - } + code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); return 0; } @@ -676,30 +644,17 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, struct imx219 *imx219 = to_imx219(sd); u32 code; - if (fse->pad >= NUM_PADS) + if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - code = imx219_get_format_code(imx219, fse->code); - if (fse->code != code) - return -EINVAL; - - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; + code = imx219_get_format_code(imx219, fse->code); + if (fse->code != code) + return -EINVAL; - fse->min_width = IMX219_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX219_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; return 0; } @@ -714,59 +669,50 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); - if (fmt->pad == IMAGE_PAD) { - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); - - imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - - *format = fmt->format; - *crop = mode->crop; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - u32 prev_hts = imx219->mode->width + imx219->hblank->val; - - imx219->mode = mode; - /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - __v4l2_ctrl_s_ctrl(imx219->vblank, - mode->vts_def - mode->height); - /* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def - 4; - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - __v4l2_ctrl_modify_range(imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_hts - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, - IMX219_PPL_MIN - mode->width, - IMX219_PPL_MAX - mode->width, - 1, IMX219_PPL_MIN - mode->width); - __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); - } - } else { - format = v4l2_subdev_get_pad_format(sd, sd_state, 1); - /* Don't allow the embedded data format to be changed */ - fmt->format = *format; + imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + + format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + + *format = fmt->format; + *crop = mode->crop; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + u32 prev_hts = imx219->mode->width + imx219->hblank->val; + + imx219->mode = mode; + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + __v4l2_ctrl_s_ctrl(imx219->vblank, + mode->vts_def - mode->height); + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); + /* + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. + */ + hblank = prev_hts - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, + IMX219_PPL_MIN - mode->width, + IMX219_PPL_MAX - mode->width, + 1, IMX219_PPL_MIN - mode->width); + __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); } return 0; @@ -1389,10 +1335,9 @@ static int imx219_probe(struct i2c_client *client) imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pad */ - imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + imx219->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad); + ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; From 965471988febc0917b4a15636700914538cf6124 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 18:03:37 +0530 Subject: [PATCH 115/159] Revert "media: i2c: imx219: make HBLANK r/w to allow longer exposures" This reverts commit 1ca6523a7173ac73f612c4633fc6308c6b5aade9. --- drivers/media/i2c/imx219.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 143054b0074852..46601ee0d59b66 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -85,10 +85,8 @@ #define IMX219_FLL_STEP 1 #define IMX219_FLL_DEFAULT 0x0c98 -/* HBLANK control range */ -#define IMX219_PPL_MIN 3448 -#define IMX219_PPL_MAX 0x7ff0 -#define IMX219_REG_HTS 0x0162 +/* HBLANK control - read only */ +#define IMX219_PPL_DEFAULT 3448 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) @@ -550,10 +548,6 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) cci_write(imx219->regmap, IMX219_REG_VTS, imx219->mode->height + ctrl->val, &ret); break; - case V4L2_CID_HBLANK: - cci_write(imx219->regmap, IMX219_REG_HTS, - imx219->mode->width + ctrl->val, &ret); - break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, ctrl->val, &ret); @@ -683,8 +677,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, *crop = mode->crop; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - u32 prev_hts = imx219->mode->width + imx219->hblank->val; - imx219->mode = mode; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, @@ -701,18 +693,13 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, exposure_max, imx219->exposure->step, exposure_def); /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. + * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeble in any + * way other than changing the mode. */ - hblank = prev_hts - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, - IMX219_PPL_MIN - mode->width, - IMX219_PPL_MAX - mode->width, - 1, IMX219_PPL_MIN - mode->width); - __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + hblank = IMX219_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, + hblank); } return 0; @@ -1106,11 +1093,12 @@ static int imx219_init_controls(struct imx219 *imx219) V4L2_CID_VBLANK, IMX219_VBLANK_MIN, IMX219_VTS_MAX - height, 1, imx219->mode->vts_def - height); - hblank = IMX219_PPL_MIN - imx219->mode->width; + hblank = IMX219_PPL_DEFAULT - imx219->mode->width; imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, - IMX219_PPL_MIN - imx219->mode->width, + V4L2_CID_HBLANK, hblank, hblank, 1, hblank); + if (imx219->hblank) + imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; exposure_max = imx219->mode->vts_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; From 5193cb6ca9e5cffc6f281507fe290b8fe1ccbbed Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 21 Oct 2024 18:03:45 +0530 Subject: [PATCH 116/159] Revert "media: i2c: imx219: Correct the minimum vblanking value" This reverts commit 4b249d9f2436af70ed9a8c2a34be0786f3fe026c. --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 46601ee0d59b66..af5d89df5f0e46 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -77,7 +77,7 @@ #define IMX219_VTS_30FPS_640x480 0x06e3 #define IMX219_VTS_MAX 0xffff -#define IMX219_VBLANK_MIN 32 +#define IMX219_VBLANK_MIN 4 /*Frame Length Line*/ #define IMX219_FLL_MIN 0x08a6 From 51b484419e427cfdbbfc7b8eddbac2e8ee284918 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:13 +0300 Subject: [PATCH 117/159] media: i2c: imx219: Drop check for reentrant .s_stream() The subdev .s_stream() operation shall not be called to start streaming on an already started subdev, or stop streaming on a stopped subdev. Remove the check that guards against that condition. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index af5d89df5f0e46..771102f5b7363a 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -889,9 +889,6 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - if (imx219->streaming == enable) - goto unlock; - if (enable) { /* * Apply default & customized values From 008a242a38c8bc0da6af33fc401f6da6061229e0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Sep 2023 21:16:49 +0300 Subject: [PATCH 118/159] media: i2c: imx219: Drop system suspend and resume handlers Stopping streaming on a camera pipeline at system suspend time, and restarting it at system resume time, requires coordinated action between the bridge driver and the camera sensor driver. This is handled by the bridge driver calling the sensor's .s_stream() handler at system suspend and resume time. There is thus no need for the sensor to independently implement system sleep PM operations. Drop them. The streaming field of the driver's private structure is now unused, drop it as well. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 41 -------------------------------------- 1 file changed, 41 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 771102f5b7363a..7988e2ba4668a6 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -465,9 +465,6 @@ struct imx219 { /* Current mode */ const struct imx219_mode *mode; - /* Streaming on/off */ - bool streaming; - /* Two or Four lanes */ u8 lanes; }; @@ -901,8 +898,6 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) imx219_stop_streaming(imx219); } - imx219->streaming = enable; - unlock: v4l2_subdev_unlock_state(state); return ret; @@ -954,41 +949,6 @@ static int imx219_power_off(struct device *dev) return 0; } -static int __maybe_unused imx219_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx219 *imx219 = to_imx219(sd); - - if (imx219->streaming) - imx219_stop_streaming(imx219); - - return 0; -} - -static int __maybe_unused imx219_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_subdev_state *state; - int ret; - - if (imx219->streaming) { - state = v4l2_subdev_lock_and_get_active_state(sd); - ret = imx219_start_streaming(imx219, state); - v4l2_subdev_unlock_state(state); - if (ret) - goto error; - } - - return 0; - -error: - imx219_stop_streaming(imx219); - imx219->streaming = false; - - return ret; -} - static int imx219_get_regulators(struct imx219 *imx219) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); @@ -1386,7 +1346,6 @@ static const struct of_device_id imx219_dt_ids[] = { MODULE_DEVICE_TABLE(of, imx219_dt_ids); static const struct dev_pm_ops imx219_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume) SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL) }; From 412a86e66cd1907d2449be84a579506ae672139b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:51 +0300 Subject: [PATCH 119/159] media: i2c: imx219: Drop unused macros Drop a handful of macros that are not used and don't provide any value. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7988e2ba4668a6..78fbb89b468522 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -79,12 +79,6 @@ #define IMX219_VBLANK_MIN 4 -/*Frame Length Line*/ -#define IMX219_FLL_MIN 0x08a6 -#define IMX219_FLL_MAX 0xffff -#define IMX219_FLL_STEP 1 -#define IMX219_FLL_DEFAULT 0x0c98 - /* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448 @@ -133,10 +127,6 @@ #define IMX219_TESTP_COLOUR_MIN 0 #define IMX219_TESTP_COLOUR_MAX 0x03ff #define IMX219_TESTP_COLOUR_STEP 1 -#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX -#define IMX219_TESTP_GREENR_DEFAULT 0 -#define IMX219_TESTP_BLUE_DEFAULT 0 -#define IMX219_TESTP_GREENB_DEFAULT 0 #define IMX219_REG_TP_WINDOW_WIDTH CCI_REG16(0x0624) #define IMX219_REG_TP_WINDOW_HEIGHT CCI_REG16(0x0626) From c60020b46ee26c5293f29b18b0e7c354aa64cb82 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:54 +0300 Subject: [PATCH 120/159] media: i2c: imx219: Fix test pattern window for 640x480 mode The 640x480 mode specifies incorrect values for the TP_WINDOW_WIDTH and TP_WINDOW_HEIGHT registers, which likely got copied from the 1640x1232 mode. They should be identical to the X_OUTPUT_SIZE and Y_OUTPUT_SIZE registers as for all the other modes, to avoid cropping the test pattern. Fix them. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 78fbb89b468522..6845791b87bb1c 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -263,8 +263,8 @@ static const struct cci_reg_sequence mode_640_480_regs[] = { { IMX219_REG_Y_ADD_END_A, 1711 }, { IMX219_REG_X_OUTPUT_SIZE, 640 }, { IMX219_REG_Y_OUTPUT_SIZE, 480 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1640 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1232 }, + { IMX219_REG_TP_WINDOW_WIDTH, 640 }, + { IMX219_REG_TP_WINDOW_HEIGHT, 480 }, }; static const struct cci_reg_sequence raw8_framefmt_regs[] = { From cdf988f6441623835cf81e93b7a5591437a28933 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:55 +0300 Subject: [PATCH 121/159] media: i2c: imx219: Set mode registers programmatically Replace the per-mode register arrays with code that sets the same register values using the mode definitions. This avoids duplicating information in two different places. The error check for invalid formats in imx219_set_framefmt() is dropped as the format is guaranteed to be valid. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 134 ++++++++++--------------------------- 1 file changed, 36 insertions(+), 98 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 6845791b87bb1c..93f41b48f95ea1 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -149,11 +149,6 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U -struct imx219_reg_list { - unsigned int num_of_regs; - const struct cci_reg_sequence *regs; -}; - /* Mode : resolution and related config&values */ struct imx219_mode { /* Frame width */ @@ -167,9 +162,6 @@ struct imx219_mode { /* V-timing */ unsigned int vts_def; - /* Default register values */ - struct imx219_reg_list reg_list; - /* 2x2 binning is used */ bool binning; }; @@ -218,65 +210,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_EXCK_FREQ, IMX219_EXCK_FREQ(IMX219_XCLK_FREQ / 1000000) }, }; -/* - * Register sets lifted off the i2C interface from the Raspberry Pi firmware - * driver. - * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. - */ -static const struct cci_reg_sequence mode_3280x2464_regs[] = { - { IMX219_REG_X_ADD_STA_A, 0 }, - { IMX219_REG_X_ADD_END_A, 3279 }, - { IMX219_REG_Y_ADD_STA_A, 0 }, - { IMX219_REG_Y_ADD_END_A, 2463 }, - { IMX219_REG_X_OUTPUT_SIZE, 3280 }, - { IMX219_REG_Y_OUTPUT_SIZE, 2464 }, - { IMX219_REG_TP_WINDOW_WIDTH, 3280 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 2464 }, -}; - -static const struct cci_reg_sequence mode_1920_1080_regs[] = { - { IMX219_REG_X_ADD_STA_A, 680 }, - { IMX219_REG_X_ADD_END_A, 2599 }, - { IMX219_REG_Y_ADD_STA_A, 692 }, - { IMX219_REG_Y_ADD_END_A, 1771 }, - { IMX219_REG_X_OUTPUT_SIZE, 1920 }, - { IMX219_REG_Y_OUTPUT_SIZE, 1080 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1920 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1080 }, -}; - -static const struct cci_reg_sequence mode_1640_1232_regs[] = { - { IMX219_REG_X_ADD_STA_A, 0 }, - { IMX219_REG_X_ADD_END_A, 3279 }, - { IMX219_REG_Y_ADD_STA_A, 0 }, - { IMX219_REG_Y_ADD_END_A, 2463 }, - { IMX219_REG_X_OUTPUT_SIZE, 1640 }, - { IMX219_REG_Y_OUTPUT_SIZE, 1232 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1640 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1232 }, -}; - -static const struct cci_reg_sequence mode_640_480_regs[] = { - { IMX219_REG_X_ADD_STA_A, 1000 }, - { IMX219_REG_X_ADD_END_A, 2279 }, - { IMX219_REG_Y_ADD_STA_A, 752 }, - { IMX219_REG_Y_ADD_END_A, 1711 }, - { IMX219_REG_X_OUTPUT_SIZE, 640 }, - { IMX219_REG_Y_OUTPUT_SIZE, 480 }, - { IMX219_REG_TP_WINDOW_WIDTH, 640 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 480 }, -}; - -static const struct cci_reg_sequence raw8_framefmt_regs[] = { - { IMX219_REG_CSI_DATA_FORMAT_A, 0x0808 }, - { IMX219_REG_OPPXCK_DIV, 8 }, -}; - -static const struct cci_reg_sequence raw10_framefmt_regs[] = { - { IMX219_REG_CSI_DATA_FORMAT_A, 0x0a0a }, - { IMX219_REG_OPPXCK_DIV, 10 }, -}; - static const s64 imx219_link_freq_menu[] = { IMX219_DEFAULT_LINK_FREQ, }; @@ -372,10 +305,6 @@ static const struct imx219_mode supported_modes[] = { .height = 2464 }, .vts_def = IMX219_VTS_15FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), - .regs = mode_3280x2464_regs, - }, .binning = false, }, { @@ -389,10 +318,6 @@ static const struct imx219_mode supported_modes[] = { .height = 1080 }, .vts_def = IMX219_VTS_30FPS_1080P, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), - .regs = mode_1920_1080_regs, - }, .binning = false, }, { @@ -406,10 +331,6 @@ static const struct imx219_mode supported_modes[] = { .height = 2464 }, .vts_def = IMX219_VTS_30FPS_BINNED, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), - .regs = mode_1640_1232_regs, - }, .binning = true, }, { @@ -423,10 +344,6 @@ static const struct imx219_mode supported_modes[] = { .height = 960 }, .vts_def = IMX219_VTS_30FPS_640x480, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_640_480_regs), - .regs = mode_640_480_regs, - }, .binning = true, }, }; @@ -695,23 +612,53 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, static int imx219_set_framefmt(struct imx219 *imx219, const struct v4l2_mbus_framefmt *format) { + const struct imx219_mode *mode = imx219->mode; + unsigned int bpp; + int ret = 0; + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: - return cci_multi_reg_write(imx219->regmap, raw8_framefmt_regs, - ARRAY_SIZE(raw8_framefmt_regs), NULL); + bpp = 8; + break; case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10: - return cci_multi_reg_write(imx219->regmap, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs), NULL); + default: + bpp = 10; + break; } - return -EINVAL; + cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, + mode->crop.left - IMX219_PIXEL_ARRAY_LEFT, &ret); + cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, + mode->crop.left - IMX219_PIXEL_ARRAY_LEFT + mode->crop.width - 1, + &ret); + cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, + mode->crop.top - IMX219_PIXEL_ARRAY_TOP, &ret); + cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, + mode->crop.top - IMX219_PIXEL_ARRAY_TOP + mode->crop.height - 1, + &ret); + + cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, + format->width, &ret); + cci_write(imx219->regmap, IMX219_REG_Y_OUTPUT_SIZE, + format->height, &ret); + + cci_write(imx219->regmap, IMX219_REG_TP_WINDOW_WIDTH, + format->width, &ret); + cci_write(imx219->regmap, IMX219_REG_TP_WINDOW_HEIGHT, + format->height, &ret); + + cci_write(imx219->regmap, IMX219_REG_CSI_DATA_FORMAT_A, + (bpp << 8) | bpp, &ret); + cci_write(imx219->regmap, IMX219_REG_OPPXCK_DIV, bpp, &ret); + + return ret; } static int imx219_set_binning(struct imx219 *imx219, @@ -783,7 +730,6 @@ static int imx219_start_streaming(struct imx219 *imx219, { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); const struct v4l2_mbus_framefmt *format; - const struct imx219_reg_list *reg_list; int ret; ret = pm_runtime_resume_and_get(&client->dev); @@ -805,15 +751,7 @@ static int imx219_start_streaming(struct imx219 *imx219, goto err_rpm_put; } - /* Apply default values of current mode */ - reg_list = &imx219->mode->reg_list; - ret = cci_multi_reg_write(imx219->regmap, reg_list->regs, - reg_list->num_of_regs, NULL); - if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); - goto err_rpm_put; - } - + /* Apply format and crop settings. */ format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); ret = imx219_set_framefmt(imx219, format); if (ret) { From fdeaf48104a99477c28839047f742ebaee7ac6f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:56 +0300 Subject: [PATCH 122/159] media: i2c: imx219: Merge format and binning setting functions The imx219_set_binning() function sets registers based on the bpp value, which is computed in imx219_set_framefmt(). As both functions are called from the same place consecutively, and set registers based on the selected mode, merge them together to simplify the code. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 43 +++++++++----------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 93f41b48f95ea1..b5fbedbe364755 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -614,6 +614,7 @@ static int imx219_set_framefmt(struct imx219 *imx219, { const struct imx219_mode *mode = imx219->mode; unsigned int bpp; + u64 bin_mode; int ret = 0; switch (format->code) { @@ -644,6 +645,15 @@ static int imx219_set_framefmt(struct imx219 *imx219, mode->crop.top - IMX219_PIXEL_ARRAY_TOP + mode->crop.height - 1, &ret); + if (!imx219->mode->binning) + bin_mode = IMX219_BINNING_NONE; + else if (bpp == 8) + bin_mode = IMX219_BINNING_2X2_ANALOG; + else + bin_mode = IMX219_BINNING_2X2; + + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, bin_mode, &ret); + cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, format->width, &ret); cci_write(imx219->regmap, IMX219_REG_Y_OUTPUT_SIZE, @@ -661,32 +671,6 @@ static int imx219_set_framefmt(struct imx219 *imx219, return ret; } -static int imx219_set_binning(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) -{ - if (!imx219->mode->binning) - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_NONE, NULL); - - switch (format->code) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2_ANALOG, NULL); - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2, NULL); - } - - return -EINVAL; -} - static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -760,13 +744,6 @@ static int imx219_start_streaming(struct imx219 *imx219, goto err_rpm_put; } - ret = imx219_set_binning(imx219, format); - if (ret) { - dev_err(&client->dev, "%s failed to set binning: %d\n", - __func__, ret); - goto err_rpm_put; - } - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); if (ret) From aaa0fb1baf817af8705f120e8ec2bec207c5b44f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:57 +0300 Subject: [PATCH 123/159] media: i2c: imx219: Initialize ycbcr_enc While the ycbcr_enc field doesn't apply to raw formats, leaving it uninitialized makes the driver behave in a less deterministic way. Fix it by picking the default value for the colorspace. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index b5fbedbe364755..56ed28251e3400 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -495,6 +495,7 @@ static void imx219_update_pad_format(struct imx219 *imx219, fmt->height = mode->height; fmt->field = V4L2_FIELD_NONE; fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_XFER_FUNC_NONE; } From b5a25383f1bf752b8fe4ed1f9980d660aaa89415 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:58 +0300 Subject: [PATCH 124/159] media: i2c: imx219: Use active crop rectangle to configure registers Configure the crop-related registers from the values stored in the active crop rectangle instead of the mode structure. This removes usage of the mode from the imx219_set_framefmt(). No functional change is intended. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 56ed28251e3400..72b7edd600a3f3 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -611,13 +611,17 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, } static int imx219_set_framefmt(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) + struct v4l2_subdev_state *state) { - const struct imx219_mode *mode = imx219->mode; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; unsigned int bpp; u64 bin_mode; int ret = 0; + format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + crop = v4l2_subdev_get_pad_crop(&imx219->sd, state, 0); + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: @@ -636,15 +640,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, } cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, - mode->crop.left - IMX219_PIXEL_ARRAY_LEFT, &ret); + crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, - mode->crop.left - IMX219_PIXEL_ARRAY_LEFT + mode->crop.width - 1, - &ret); + crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, - mode->crop.top - IMX219_PIXEL_ARRAY_TOP, &ret); + crop->top - IMX219_PIXEL_ARRAY_TOP, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, - mode->crop.top - IMX219_PIXEL_ARRAY_TOP + mode->crop.height - 1, - &ret); + crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); if (!imx219->mode->binning) bin_mode = IMX219_BINNING_NONE; @@ -714,7 +716,6 @@ static int imx219_start_streaming(struct imx219 *imx219, struct v4l2_subdev_state *state) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - const struct v4l2_mbus_framefmt *format; int ret; ret = pm_runtime_resume_and_get(&client->dev); @@ -737,8 +738,7 @@ static int imx219_start_streaming(struct imx219 *imx219, } /* Apply format and crop settings. */ - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - ret = imx219_set_framefmt(imx219, format); + ret = imx219_set_framefmt(imx219, state); if (ret) { dev_err(&client->dev, "%s failed to set frame format: %d\n", __func__, ret); From b33a870a30b2a9e0d95b199f7e1d31c5ee834d62 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:32:59 +0300 Subject: [PATCH 125/159] media: i2c: imx219: Infer binning settings from format and crop Compare the format and crop rectangle dimensions to infer binning settings, instead of storing the binning mode in the imx219_mode structure. This removes duplicate information from the mode. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 72b7edd600a3f3..92057914dd2efc 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -161,9 +161,6 @@ struct imx219_mode { /* V-timing */ unsigned int vts_def; - - /* 2x2 binning is used */ - bool binning; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -305,7 +302,6 @@ static const struct imx219_mode supported_modes[] = { .height = 2464 }, .vts_def = IMX219_VTS_15FPS, - .binning = false, }, { /* 1080P 30fps cropped */ @@ -318,7 +314,6 @@ static const struct imx219_mode supported_modes[] = { .height = 1080 }, .vts_def = IMX219_VTS_30FPS_1080P, - .binning = false, }, { /* 2x2 binned 30fps mode */ @@ -331,7 +326,6 @@ static const struct imx219_mode supported_modes[] = { .height = 2464 }, .vts_def = IMX219_VTS_30FPS_BINNED, - .binning = true, }, { /* 640x480 30fps mode */ @@ -344,7 +338,6 @@ static const struct imx219_mode supported_modes[] = { .height = 960 }, .vts_def = IMX219_VTS_30FPS_640x480, - .binning = true, }, }; @@ -648,7 +641,7 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - if (!imx219->mode->binning) + if (format->width == crop->width && format->height == crop->height) bin_mode = IMX219_BINNING_NONE; else if (bpp == 8) bin_mode = IMX219_BINNING_2X2_ANALOG; From 6a9e2dc16066b7a08057435af3d8ac1c0a12ff9e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:00 +0300 Subject: [PATCH 126/159] media: i2c: imx219: Access height from active format in imx219_set_ctrl Use the active format height instead of the mode height in imx219_set_ctrl(). This prepares for dropping the mode field from the imx219 structure. The state is retrieved using v4l2_subdev_get_locked_active_state() as the subdev active state and the control handler share the same lock. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 92057914dd2efc..0e329dbeea1ae7 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -397,13 +397,18 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) struct imx219 *imx219 = container_of(ctrl->handler, struct imx219, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx219->sd); + format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; /* Update max exposure while meeting expected vblanking */ - exposure_max = imx219->mode->height + ctrl->val - 4; + exposure_max = format->height + ctrl->val - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; __v4l2_ctrl_modify_range(imx219->exposure, @@ -443,7 +448,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - imx219->mode->height + ctrl->val, &ret); + format->height + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, From f2c4c21e62d0d0607a40ebcb621f1a4897883827 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:01 +0300 Subject: [PATCH 127/159] media: i2c: imx219: Don't store the current mode in the imx219 structure The mode field of the imx219 structure is only used in imx219_init_controls(), after the probe function sets it to point to the default mode. Use the default mode directly when initializing controls, and drop the mode field from the imx219 structure. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0e329dbeea1ae7..1b20e7ea03b0e4 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -362,9 +362,6 @@ struct imx219 { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; - /* Current mode */ - const struct imx219_mode *mode; - /* Two or Four lanes */ u8 lanes; }; @@ -580,7 +577,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, *crop = mode->crop; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - imx219->mode = mode; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, IMX219_VTS_MAX - mode->height, 1, @@ -922,8 +918,8 @@ static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) static int imx219_init_controls(struct imx219 *imx219) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct imx219_mode *mode = &supported_modes[0]; struct v4l2_ctrl_handler *ctrl_hdlr; - unsigned int height = imx219->mode->height; struct v4l2_fwnode_device_properties props; int exposure_max, exposure_def, hblank; int i, ret; @@ -952,15 +948,15 @@ static int imx219_init_controls(struct imx219 *imx219) /* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_VBLANK, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - height, 1, - imx219->mode->vts_def - height); - hblank = IMX219_PPL_DEFAULT - imx219->mode->width; + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + hblank = IMX219_PPL_DEFAULT - mode->width; imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); if (imx219->hblank) imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - exposure_max = imx219->mode->vts_def - 4; + exposure_max = mode->vts_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, @@ -1152,10 +1148,8 @@ static int imx219_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Set default mode to max resolution */ - imx219->mode = &supported_modes[0]; - - /* sensor doesn't enter LP-11 state upon power up until and unless + /* + * Sensor doesn't enter LP-11 state upon power up until and unless * streaming is started, so upon power up switch the modes to: * streaming -> standby */ From cae854745e58daee46ca33dfd6e661d39e222b67 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:02 +0300 Subject: [PATCH 128/159] media: i2c: imx219: Drop IMX219_VTS_* macros The IMX219_VTS_* macros define default VTS values for the modes supported by the driver. They are used in a single place, and hinder readability compared to using the value directly as a decimal number. Drop them. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 1b20e7ea03b0e4..5c332c176caa72 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -71,10 +71,6 @@ /* V_TIMING internal */ #define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_15FPS 0x0dc6 -#define IMX219_VTS_30FPS_1080P 0x06e3 -#define IMX219_VTS_30FPS_BINNED 0x06e3 -#define IMX219_VTS_30FPS_640x480 0x06e3 #define IMX219_VTS_MAX 0xffff #define IMX219_VBLANK_MIN 4 @@ -301,7 +297,7 @@ static const struct imx219_mode supported_modes[] = { .width = 3280, .height = 2464 }, - .vts_def = IMX219_VTS_15FPS, + .vts_def = 3526, }, { /* 1080P 30fps cropped */ @@ -313,7 +309,7 @@ static const struct imx219_mode supported_modes[] = { .width = 1920, .height = 1080 }, - .vts_def = IMX219_VTS_30FPS_1080P, + .vts_def = 1763, }, { /* 2x2 binned 30fps mode */ @@ -325,7 +321,7 @@ static const struct imx219_mode supported_modes[] = { .width = 3280, .height = 2464 }, - .vts_def = IMX219_VTS_30FPS_BINNED, + .vts_def = 1763, }, { /* 640x480 30fps mode */ @@ -337,7 +333,7 @@ static const struct imx219_mode supported_modes[] = { .width = 1280, .height = 960 }, - .vts_def = IMX219_VTS_30FPS_640x480, + .vts_def = 1763, }, }; From 11ae0d97a7da201cef6d588ce3fe68296d2efd12 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:03 +0300 Subject: [PATCH 129/159] media: i2c: imx219: Group functions by purpose Move functions around to group them by purpose, in order to improve readability. No functional change is intended. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 589 +++++++++++++++++++------------------ 1 file changed, 302 insertions(+), 287 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 5c332c176caa72..67f7117c8fd7d4 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -385,6 +385,10 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } +/* ----------------------------------------------------------------------------- + * Controls + */ + static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx219 *imx219 = @@ -476,130 +480,135 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { .s_ctrl = imx219_set_ctrl, }; -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) +static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { - /* Bayer order varies with flips */ - fmt->code = imx219_get_format_code(imx219, code); - fmt->width = mode->width; - fmt->height = mode->height; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_XFER_FUNC_NONE; + return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE; } -static int imx219_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +/* Initialize control handlers */ +static int imx219_init_controls(struct imx219 *imx219) { - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - /* Initialize the format. */ - format = v4l2_subdev_get_pad_format(sd, state, 0); - imx219_update_pad_format(imx219, &supported_modes[0], format, - MEDIA_BUS_FMT_SRGGB10_1X10); - - /* Initialize the crop rectangle. */ - crop = v4l2_subdev_get_pad_crop(sd, state, 0); - crop->top = IMX219_PIXEL_ARRAY_TOP; - crop->left = IMX219_PIXEL_ARRAY_LEFT; - crop->width = IMX219_PIXEL_ARRAY_WIDTH; - crop->height = IMX219_PIXEL_ARRAY_HEIGHT; + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct imx219_mode *mode = &supported_modes[0]; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int exposure_max, exposure_def, hblank; + int i, ret; - return 0; -} + ctrl_hdlr = &imx219->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); + if (ret) + return ret; -static int imx219_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct imx219 *imx219 = to_imx219(sd); + /* By default, PIXEL_RATE is read only */ + imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_PIXEL_RATE, + imx219_get_pixel_rate(imx219), + imx219_get_pixel_rate(imx219), 1, + imx219_get_pixel_rate(imx219)); - if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) - return -EINVAL; + imx219->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, + (imx219->lanes == 2) ? imx219_link_freq_menu : + imx219_link_freq_4lane_menu); + if (imx219->link_freq) + imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VBLANK, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + hblank = IMX219_PPL_DEFAULT - mode->width; + imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx219->hblank) + imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX219_EXPOSURE_MIN, exposure_max, + IMX219_EXPOSURE_STEP, + exposure_def); - return 0; -} + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, + IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); -static int imx219_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct imx219 *imx219 = to_imx219(sd); - u32 code; + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, + IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; + imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx219->hflip) + imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - code = imx219_get_format_code(imx219, fse->code); - if (fse->code != code) - return -EINVAL; + imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx219->vflip) + imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx219_test_pattern_menu) - 1, + 0, 0, imx219_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX219_TESTP_COLOUR_MIN, + IMX219_TESTP_COLOUR_MAX, + IMX219_TESTP_COLOUR_STEP, + IMX219_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } - return 0; -} + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } -static int imx219_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx219 *imx219 = to_imx219(sd); - const struct imx219_mode *mode; - int exposure_max, exposure_def, hblank; - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, + &props); + if (ret) + goto error; - imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + imx219->sd.ctrl_handler = ctrl_hdlr; - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + return 0; - *format = fmt->format; - *crop = mode->crop; +error: + v4l2_ctrl_handler_free(ctrl_hdlr); - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - __v4l2_ctrl_s_ctrl(imx219->vblank, - mode->vts_def - mode->height); - /* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def - 4; - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - __v4l2_ctrl_modify_range(imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); - /* - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank - * depends on mode->width only, and is not changeble in any - * way other than changing the mode. - */ - hblank = IMX219_PPL_DEFAULT - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, - hblank); - } + return ret; +} - return 0; +static void imx219_free_controls(struct imx219 *imx219) +{ + v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); } +/* ----------------------------------------------------------------------------- + * Subdev operations + */ + static int imx219_set_framefmt(struct imx219 *imx219, struct v4l2_subdev_state *state) { @@ -664,37 +673,6 @@ static int imx219_set_framefmt(struct imx219 *imx219, return ret; } -static int imx219_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); - return 0; - } - - case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = IMX219_NATIVE_WIDTH; - sel->r.height = IMX219_NATIVE_HEIGHT; - - return 0; - - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; - - return 0; - } - - return -EINVAL; -} - static int imx219_configure_lanes(struct imx219 *imx219) { return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, @@ -799,14 +777,196 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) return ret; } -/* Power/clock management functions */ -static int imx219_power_on(struct device *dev) +static void imx219_update_pad_format(struct imx219 *imx219, + const struct imx219_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx219 *imx219 = to_imx219(sd); - int ret; + /* Bayer order varies with flips */ + fmt->code = imx219_get_format_code(imx219, code); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} - ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, +static int imx219_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx219 *imx219 = to_imx219(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize the format. */ + format = v4l2_subdev_get_pad_format(sd, state, 0); + imx219_update_pad_format(imx219, &supported_modes[0], format, + MEDIA_BUS_FMT_SRGGB10_1X10); + + /* Initialize the crop rectangle. */ + crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop->top = IMX219_PIXEL_ARRAY_TOP; + crop->left = IMX219_PIXEL_ARRAY_LEFT; + crop->width = IMX219_PIXEL_ARRAY_WIDTH; + crop->height = IMX219_PIXEL_ARRAY_HEIGHT; + + return 0; +} + +static int imx219_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx219 *imx219 = to_imx219(sd); + + if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) + return -EINVAL; + + code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); + + return 0; +} + +static int imx219_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx219 *imx219 = to_imx219(sd); + u32 code; + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + code = imx219_get_format_code(imx219, fse->code); + if (fse->code != code) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int imx219_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx219 *imx219 = to_imx219(sd); + const struct imx219_mode *mode; + int exposure_max, exposure_def, hblank; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + + format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + + *format = fmt->format; + *crop = mode->crop; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + __v4l2_ctrl_s_ctrl(imx219->vblank, + mode->vts_def - mode->height); + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); + /* + * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeble in any + * way other than changing the mode. + */ + hblank = IMX219_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, + hblank); + } + + return 0; +} + +static int imx219_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX219_NATIVE_WIDTH; + sel->r.height = IMX219_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX219_PIXEL_ARRAY_TOP; + sel->r.left = IMX219_PIXEL_ARRAY_LEFT; + sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_subdev_core_ops imx219_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx219_video_ops = { + .s_stream = imx219_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx219_pad_ops = { + .enum_mbus_code = imx219_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx219_set_pad_format, + .get_selection = imx219_get_selection, + .enum_frame_size = imx219_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx219_subdev_ops = { + .core = &imx219_core_ops, + .video = &imx219_video_ops, + .pad = &imx219_pad_ops, +}; + + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int imx219_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx219 *imx219 = to_imx219(sd); + int ret; + + ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, imx219->supplies); if (ret) { dev_err(dev, "%s: failed to enable regulators\n", @@ -845,6 +1005,10 @@ static int imx219_power_off(struct device *dev) return 0; } +/* ----------------------------------------------------------------------------- + * Probe & remove + */ + static int imx219_get_regulators(struct imx219 *imx219) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); @@ -881,155 +1045,6 @@ static int imx219_identify_module(struct imx219 *imx219) return 0; } -static const struct v4l2_subdev_core_ops imx219_core_ops = { - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, -}; - -static const struct v4l2_subdev_pad_ops imx219_pad_ops = { - .enum_mbus_code = imx219_enum_mbus_code, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = imx219_set_pad_format, - .get_selection = imx219_get_selection, - .enum_frame_size = imx219_enum_frame_size, -}; - -static const struct v4l2_subdev_ops imx219_subdev_ops = { - .core = &imx219_core_ops, - .video = &imx219_video_ops, - .pad = &imx219_pad_ops, -}; - - -static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) -{ - return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE; -} - -/* Initialize control handlers */ -static int imx219_init_controls(struct imx219 *imx219) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - const struct imx219_mode *mode = &supported_modes[0]; - struct v4l2_ctrl_handler *ctrl_hdlr; - struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank; - int i, ret; - - ctrl_hdlr = &imx219->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); - if (ret) - return ret; - - /* By default, PIXEL_RATE is read only */ - imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_PIXEL_RATE, - imx219_get_pixel_rate(imx219), - imx219_get_pixel_rate(imx219), 1, - imx219_get_pixel_rate(imx219)); - - imx219->link_freq = - v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, - (imx219->lanes == 2) ? imx219_link_freq_menu : - imx219_link_freq_4lane_menu); - if (imx219->link_freq) - imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - /* Initial vblank/hblank/exposure parameters based on current mode */ - imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_VBLANK, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - hblank = IMX219_PPL_DEFAULT - mode->width; - imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, - 1, hblank); - if (imx219->hblank) - imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - exposure_max = mode->vts_def - 4; - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_EXPOSURE, - IMX219_EXPOSURE_MIN, exposure_max, - IMX219_EXPOSURE_STEP, - exposure_def); - - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, - IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); - - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, - IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); - - imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - if (imx219->hflip) - imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - - imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - if (imx219->vflip) - imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(imx219_test_pattern_menu) - 1, - 0, 0, imx219_test_pattern_menu); - for (i = 0; i < 4; i++) { - /* - * The assumption is that - * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 - * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 - * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 - */ - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_TEST_PATTERN_RED + i, - IMX219_TESTP_COLOUR_MIN, - IMX219_TESTP_COLOUR_MAX, - IMX219_TESTP_COLOUR_STEP, - IMX219_TESTP_COLOUR_MAX); - /* The "Solid color" pattern is white by default */ - } - - if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", - __func__, ret); - goto error; - } - - ret = v4l2_fwnode_device_parse(&client->dev, &props); - if (ret) - goto error; - - ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, - &props); - if (ret) - goto error; - - imx219->sd.ctrl_handler = ctrl_hdlr; - - return 0; - -error: - v4l2_ctrl_handler_free(ctrl_hdlr); - - return ret; -} - -static void imx219_free_controls(struct imx219 *imx219) -{ - v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); -} - static const struct v4l2_subdev_internal_ops imx219_internal_ops = { .init_state = imx219_init_state, }; From ec3abc4035d5ec09ded6d2dec229150b1c4c8b12 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:04 +0300 Subject: [PATCH 130/159] media: i2c: imx219: Remove unneeded goto Simplify the imx219_set_stream() by removing an unneeded goto statement, and its corresponding error label. The natural flow of the function is correct. While at it, drop a useless comment. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 67f7117c8fd7d4..0e3ffa8241d4a1 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -760,19 +760,11 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - if (enable) { - /* - * Apply default & customized values - * and then start streaming. - */ + if (enable) ret = imx219_start_streaming(imx219, state); - if (ret) - goto unlock; - } else { + else imx219_stop_streaming(imx219); - } -unlock: v4l2_subdev_unlock_state(state); return ret; } From 9f18d3032fda9db8a0b15f3362c599e4fe1fae39 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:05 +0300 Subject: [PATCH 131/159] media: i2c: imx219: Implement .init_cfg() using .set_fmt() Instead of duplicating the logical implemented in the .set_fmt() operation in .init_cfg(), call .set_fmt() directly. This centralizes the format and crop rectangle calculations in a single place. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 40 +++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0e3ffa8241d4a1..00b750c1c35f63 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -784,28 +784,6 @@ static void imx219_update_pad_format(struct imx219 *imx219, fmt->xfer_func = V4L2_XFER_FUNC_NONE; } -static int imx219_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - /* Initialize the format. */ - format = v4l2_subdev_get_pad_format(sd, state, 0); - imx219_update_pad_format(imx219, &supported_modes[0], format, - MEDIA_BUS_FMT_SRGGB10_1X10); - - /* Initialize the crop rectangle. */ - crop = v4l2_subdev_get_pad_crop(sd, state, 0); - crop->top = IMX219_PIXEL_ARRAY_TOP; - crop->left = IMX219_PIXEL_ARRAY_LEFT; - crop->width = IMX219_PIXEL_ARRAY_WIDTH; - crop->height = IMX219_PIXEL_ARRAY_HEIGHT; - - return 0; -} - static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -924,6 +902,24 @@ static int imx219_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static int imx219_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = supported_modes[0].width, + .height = supported_modes[0].height, + }, + }; + + imx219_set_pad_format(sd, state, &fmt); + + return 0; +} + static const struct v4l2_subdev_core_ops imx219_core_ops = { .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, From 2824dd0d64e1ffda245f6122e7b673b934504023 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:06 +0300 Subject: [PATCH 132/159] media: i2c: imx219: Separate horizontal and vertical binning The IMX219 has distinct binning registers for the horizontal and vertical directions. Calculate their value and write them separately. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 39 ++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 00b750c1c35f63..0b88aaa674edb7 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -90,10 +90,11 @@ #define IMX219_REG_ORIENTATION CCI_REG8(0x0172) /* Binning Mode */ -#define IMX219_REG_BINNING_MODE CCI_REG16(0x0174) -#define IMX219_BINNING_NONE 0x0000 -#define IMX219_BINNING_2X2 0x0101 -#define IMX219_BINNING_2X2_ANALOG 0x0303 +#define IMX219_REG_BINNING_MODE_H CCI_REG8(0x0174) +#define IMX219_REG_BINNING_MODE_V CCI_REG8(0x0175) +#define IMX219_BINNING_NONE 0x00 +#define IMX219_BINNING_X2 0x01 +#define IMX219_BINNING_X2_ANALOG 0x03 #define IMX219_REG_CSI_DATA_FORMAT_A CCI_REG16(0x018c) @@ -615,7 +616,7 @@ static int imx219_set_framefmt(struct imx219 *imx219, const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; unsigned int bpp; - u64 bin_mode; + u64 bin_h, bin_v; int ret = 0; format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); @@ -647,14 +648,28 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - if (format->width == crop->width && format->height == crop->height) - bin_mode = IMX219_BINNING_NONE; - else if (bpp == 8) - bin_mode = IMX219_BINNING_2X2_ANALOG; - else - bin_mode = IMX219_BINNING_2X2; + switch (crop->width / format->width) { + case 1: + default: + bin_h = IMX219_BINNING_NONE; + break; + case 2: + bin_h = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; + break; + } + + switch (crop->height / format->height) { + case 1: + default: + bin_v = IMX219_BINNING_NONE; + break; + case 2: + bin_v = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; + break; + } - cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, bin_mode, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, bin_v, &ret); cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, format->width, &ret); From f010f058be4bddf01baffe37a17a747fc166c822 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:07 +0300 Subject: [PATCH 133/159] media: i2c: imx219: Calculate crop rectangle dynamically Calculate the crop rectangle size and location dynamically when setting the format, instead of storing it in the imx219_mode structure. This removes duplicated information from the mode, to guarantee consistency. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 45 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0b88aaa674edb7..08fbec00d0b498 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -153,9 +154,6 @@ struct imx219_mode { /* Frame height */ unsigned int height; - /* Analog crop rectangle. */ - struct v4l2_rect crop; - /* V-timing */ unsigned int vts_def; }; @@ -292,48 +290,24 @@ static const struct imx219_mode supported_modes[] = { /* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height = 2464 - }, .vts_def = 3526, }, { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .crop = { - .left = 688, - .top = 700, - .width = 1920, - .height = 1080 - }, .vts_def = 1763, }, { /* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height = 2464 - }, .vts_def = 1763, }, { /* 640x480 30fps mode */ .width = 640, .height = 480, - .crop = { - .left = 1008, - .top = 760, - .width = 1280, - .height = 960 - }, .vts_def = 1763, }, }; @@ -844,6 +818,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, int exposure_max, exposure_def, hblank; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + unsigned int bin_h, bin_v; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), @@ -853,10 +828,20 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); format = v4l2_subdev_get_pad_format(sd, sd_state, 0); - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - *format = fmt->format; - *crop = mode->crop; + + /* + * Use binning to maximize the crop rectangle size, and centre it in the + * sensor. + */ + bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); + bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + crop->width = format->width * bin_h; + crop->height = format->height * bin_v; + crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; + crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Update limits and set FPS to default */ From 55cfe8ff5a2656883418dc7699202e8da8c72d25 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:08 +0300 Subject: [PATCH 134/159] media: i2c: imx219: Name all subdev state variables 'state' Subdev state variables are named with a mix of 'state' and 'sd_state' through the driver. To improve consistency, name them all 'state'. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 08fbec00d0b498..02253bd472f465 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -774,7 +774,7 @@ static void imx219_update_pad_format(struct imx219 *imx219, } static int imx219_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { struct imx219 *imx219 = to_imx219(sd); @@ -788,7 +788,7 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, } static int imx219_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { struct imx219 *imx219 = to_imx219(sd); @@ -810,7 +810,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, } static int imx219_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); @@ -827,7 +827,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_get_pad_format(sd, state, 0); *format = fmt->format; /* @@ -837,7 +837,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); + crop = v4l2_subdev_get_pad_crop(sd, state, 0); crop->width = format->width * bin_h; crop->height = format->height * bin_v; crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; @@ -872,12 +872,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, } static int imx219_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { switch (sel->target) { case V4L2_SEL_TGT_CROP: { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); + sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); return 0; } From c05eff133ed89829a4ce29a919838ced606f02f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 24 Sep 2023 18:33:09 +0300 Subject: [PATCH 135/159] media: i2c: imx219: Move variables to inner scope The exposure_max, exposure_def and hblank variables are only used in an inner scope in the imx219_set_pad_format() function. Move them to that scope to keep them closer to their usage and improve readability. Signed-off-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 02253bd472f465..2e3707005459ff 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -815,7 +815,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; - int exposure_max, exposure_def, hblank; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; unsigned int bin_h, bin_v; @@ -844,6 +843,10 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + int exposure_max; + int exposure_def; + int hblank; + /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, IMX219_VTS_MAX - mode->height, 1, From 60ff995c63ec8a92db407cd7aa779fe99268011c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 19 Aug 2023 02:40:28 +0300 Subject: [PATCH 136/159] media: i2c: imx219: Inline imx219_update_pad_format() in its caller The imx219_update_pad_format() is short and called from a single place, in imx219_set_pad_format(). Inline the code in the caller to keep all format adjustments grouped in a single place and improve readability. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Dave Stevenson --- drivers/media/i2c/imx219.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 2e3707005459ff..cd04ae295bab17 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -758,21 +758,6 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) return ret; } -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) -{ - /* Bayer order varies with flips */ - fmt->code = imx219_get_format_code(imx219, code); - fmt->width = mode->width; - fmt->height = mode->height; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_XFER_FUNC_NONE; -} - static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) @@ -819,12 +804,23 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_rect *crop; unsigned int bin_h, bin_v; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + fmt->format.code = imx219_get_format_code(imx219, fmt->format.code); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; format = v4l2_subdev_get_pad_format(sd, state, 0); *format = fmt->format; From 6ce9fce83f8020f51edf3612e78479995e5fd8cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Aug 2023 01:46:16 +0300 Subject: [PATCH 137/159] media: i2c: imx219: Add internal image sink pad Use the newly added internal pad API to expose the internal configuration of the sensor to userspace in a standard manner. This also paves the way for adding support for embedded data with an additional internal pad. To maintain compatibility with existing userspace that may operate on pad 0 unconditionally, keep the source pad numbered 0 and number the internal image pad 1. Signed-off-by: Laurent Pinchart --- Changes since v7: - Set IMX219_PAD_SOURCE to 0 explicitly - Update comment to clarify the whole image pad format is fixed - Compare code->index with 0 - Drop unneeded parentheses --- drivers/media/i2c/imx219.c | 182 ++++++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 43 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index cd04ae295bab17..0e978c393df93b 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -140,6 +140,7 @@ #define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 /* IMX219 native and active pixel array size. */ +#define IMX219_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX219_NATIVE_WIDTH 3296U #define IMX219_NATIVE_HEIGHT 2480U #define IMX219_PIXEL_ARRAY_LEFT 8U @@ -312,9 +313,15 @@ static const struct imx219_mode supported_modes[] = { }, }; +enum imx219_pad_ids { + IMX219_PAD_SOURCE = 0, + IMX219_PAD_IMAGE, + IMX219_NUM_PADS, +}; + struct imx219 { struct v4l2_subdev sd; - struct media_pad pad; + struct media_pad pads[IMX219_NUM_PADS]; struct regmap *regmap; struct clk *xclk; /* system clock to IMX219 */ @@ -374,7 +381,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -593,8 +600,8 @@ static int imx219_set_framefmt(struct imx219 *imx219, u64 bin_h, bin_v; int ret = 0; - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - crop = v4l2_subdev_get_pad_crop(&imx219->sd, state, 0); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: @@ -764,10 +771,25 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); - if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) - return -EINVAL; + if (code->pad == IMX219_PAD_IMAGE) { + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX219_NATIVE_FORMAT; + } else { + /* + * On the source pad, the sensor supports multiple raw formats + * with different bit depths. + */ + u32 format; - code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); + if (code->index >= ARRAY_SIZE(imx219_mbus_formats) / 4) + return -EINVAL; + + format = imx219_mbus_formats[code->index * 4]; + code->code = imx219_get_format_code(imx219, format); + } return 0; } @@ -777,19 +799,25 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum *fse) { struct imx219 *imx219 = to_imx219(sd); - u32 code; - - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - code = imx219_get_format_code(imx219, fse->code); - if (fse->code != code) - return -EINVAL; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; + if (fse->pad == IMX219_PAD_IMAGE) { + if (fse->code != IMX219_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX219_NATIVE_WIDTH; + fse->max_width = IMX219_NATIVE_WIDTH; + fse->min_height = IMX219_NATIVE_HEIGHT; + fse->max_height = IMX219_NATIVE_HEIGHT; + } else { + if (fse->code != imx219_get_format_code(imx219, fse->code) || + fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } return 0; } @@ -801,9 +829,17 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; struct v4l2_mbus_framefmt *format; + struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int bin_h, bin_v; + /* + * The driver is mode-based, the format can be set on the source pad + * only. + */ + if (fmt->pad != IMX219_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, fmt); + /* * Adjust the requested format to match the closest mode. The Bayer * order varies with flips. @@ -822,22 +858,51 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; - format = v4l2_subdev_get_pad_format(sd, state, 0); + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed format. */ + format = v4l2_subdev_state_get_format(state, IMX219_PAD_IMAGE); *format = fmt->format; + format->code = IMX219_NATIVE_FORMAT; + format->width = IMX219_NATIVE_WIDTH; + format->height = IMX219_NATIVE_HEIGHT; /* * Use binning to maximize the crop rectangle size, and centre it in the * sensor. */ - bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); - bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / fmt->format.width, 2U); + bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / fmt->format.height, 2U); - crop = v4l2_subdev_get_pad_crop(sd, state, 0); - crop->width = format->width * bin_h; - crop->height = format->height * bin_v; + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); + crop->width = fmt->format.width * bin_h; + crop->height = fmt->format.height * bin_v; crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; + /* + * The compose rectangle models binning, its size is the sensor output + * size. + */ + compose = v4l2_subdev_state_get_compose(state, IMX219_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; + + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_SOURCE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); + *format = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; @@ -874,13 +939,13 @@ static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); - return 0; - } + struct v4l2_rect *compose; + switch (sel->target) { case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX219_PAD_IMAGE) + return -EINVAL; + sel->r.top = 0; sel->r.left = 0; sel->r.width = IMX219_NATIVE_WIDTH; @@ -890,11 +955,35 @@ static int imx219_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX219_PAD_IMAGE: + sel->r.top = IMX219_PIXEL_ARRAY_TOP; + sel->r.left = IMX219_PIXEL_ARRAY_LEFT; + sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX219_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(state, + IMX219_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + + break; + + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + return 0; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX219_PAD_IMAGE) + return -EINVAL; + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); return 0; } @@ -902,11 +991,11 @@ static int imx219_get_selection(struct v4l2_subdev *sd, } static int imx219_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) + struct v4l2_subdev_state *state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, - .pad = 0, + .pad = IMX219_PAD_SOURCE, .format = { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .width = supported_modes[0].width, @@ -942,6 +1031,9 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { .pad = &imx219_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx219_internal_ops = { + .init_state = imx219_init_state, +}; /* ----------------------------------------------------------------------------- * Power management @@ -1032,10 +1124,6 @@ static int imx219_identify_module(struct imx219 *imx219) return 0; } -static const struct v4l2_subdev_internal_ops imx219_internal_ops = { - .init_state = imx219_init_state, -}; - static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) { struct fwnode_handle *endpoint; @@ -1175,10 +1263,18 @@ static int imx219_probe(struct i2c_client *client) V4L2_SUBDEV_FL_HAS_EVENTS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pad */ - imx219->pad.flags = MEDIA_PAD_FL_SOURCE; + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal image pad, the external source pad is numbered 0 and the + * internal image pad numbered 1. + */ + imx219->pads[IMX219_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx219->pads[IMX219_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_INTERNAL; - ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); + ret = media_entity_pads_init(&imx219->sd.entity, + ARRAY_SIZE(imx219->pads), imx219->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; From 6048ed9b051bb70a3950fb0916e33a30922e8413 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Aug 2023 01:46:16 +0300 Subject: [PATCH 138/159] media: i2c: imx219: Add image stream In preparation for embedded data stream support, introduce a new imx219_stream_ids enumeration for stream IDs, with a single value, IMX219_STREAM_IMAGE for the image data stream. Use it when accessing the formats, crop and compose rectangles on the source pad. This is meant to reduce the size of further commits, and doesn't introduce any functional change. Signed-off-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0e978c393df93b..a54bacf5ae3b8a 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -319,6 +319,10 @@ enum imx219_pad_ids { IMX219_NUM_PADS, }; +enum imx219_stream_ids { + IMX219_STREAM_IMAGE, +}; + struct imx219 { struct v4l2_subdev sd; struct media_pad pads[IMX219_NUM_PADS]; @@ -381,7 +385,8 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); - format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -600,7 +605,8 @@ static int imx219_set_framefmt(struct imx219 *imx219, u64 bin_h, bin_v; int ret = 0; - format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); switch (format->code) { @@ -894,13 +900,15 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, * No mode use digital crop, the source pad crop rectangle size and * format are thus identical to the image pad compose rectangle. */ - crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_SOURCE); + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; - format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); *format = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { From 97c2003ddc1ad5756e92ad7af3b671e35ced4cb9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Apr 2024 14:45:32 +0300 Subject: [PATCH 139/159] media: i2c: imx219: Switch to .{enable,disable}_streams() operations The imx219 driver implements the .s_stream() operation, which is not compatible with streams. In preparation for the addition of streams support in the driver, switch to the .enable_streams() and .disable_streams() operations. Signed-off-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index a54bacf5ae3b8a..1582d53fcbb185 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -754,23 +754,6 @@ static void imx219_stop_streaming(struct imx219 *imx219) pm_runtime_put(&client->dev); } -static int imx219_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_subdev_state *state; - int ret = 0; - - state = v4l2_subdev_lock_and_get_active_state(sd); - - if (enable) - ret = imx219_start_streaming(imx219, state); - else - imx219_stop_streaming(imx219); - - v4l2_subdev_unlock_state(state); - return ret; -} - static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) @@ -998,6 +981,26 @@ static int imx219_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static int imx219_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx219 *imx219 = to_imx219(sd); + + return imx219_start_streaming(imx219, state); +} + +static int imx219_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx219 *imx219 = to_imx219(sd); + + imx219_stop_streaming(imx219); + + return 0; +} + static int imx219_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { @@ -1021,21 +1024,18 @@ static const struct v4l2_subdev_core_ops imx219_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx219_pad_ops = { .enum_mbus_code = imx219_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, + .enable_streams = imx219_enable_streams, + .disable_streams = imx219_disable_streams, }; static const struct v4l2_subdev_ops imx219_subdev_ops = { .core = &imx219_core_ops, - .video = &imx219_video_ops, .pad = &imx219_pad_ops, }; From 489edcf91eaee68869d2de8e169f9cfeaeb7137b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Aug 2023 01:46:16 +0300 Subject: [PATCH 140/159] media: i2c: imx219: Report internal routes to userspace Usage of internal pads creates a route internal to the subdev, and the V4L2 camera sensor API requires such routes to be reported to userspace. Create the route in the .init_state() operation. Internal routing support requires stream support, so set the V4L2_SUBDEV_FL_STREAMS flag. As the route is immutable, there's no need to implement the .set_routing() operation. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- Changes since v7: - Update commit message to match the changes in v6 - Fix name of STREAMS flag in commit message - Use IMX219_STREAM_IMAGE Changes since v6: - Drop change to get format API in imx219_set_ctrl() - Fix function name in commit message - Set V4L2_SUBDEV_ROUTE_FL_IMMUTABLE flag on route --- drivers/media/i2c/imx219.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 1582d53fcbb185..7240c863eed8d1 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -1004,15 +1004,36 @@ static int imx219_disable_streams(struct v4l2_subdev *sd, static int imx219_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { + struct v4l2_subdev_route routes[1] = { + { + .sink_pad = IMX219_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX219_PAD_SOURCE, + .source_stream = IMX219_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, .pad = IMX219_PAD_SOURCE, + .stream = IMX219_STREAM_IMAGE, .format = { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .width = supported_modes[0].width, .height = supported_modes[0].height, }, }; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; imx219_set_pad_format(sd, state, &fmt); @@ -1268,7 +1289,8 @@ static int imx219_probe(struct i2c_client *client) /* Initialize subdev */ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STREAMS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* From aca395a1763f5b597cd859800bdca170bbc56150 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 Aug 2023 19:48:07 +0300 Subject: [PATCH 141/159] media: i2c: imx219: Report streams using frame descriptors Implement the .get_frame_desc() subdev operation to report information about streams to the connected CSI-2 receiver. This is required to let the CSI-2 receiver driver know about virtual channels and data types for each stream. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- Changes since v7: - Use IMX219_STREAM_IMAGE - Drop unneeded memset() Changes since v6: - Replace v4l2_subdev_state_get_stream_format() with v4l2_subdev_state_get_format() - Make imx219_format_bpp() return an unsigned int --- drivers/media/i2c/imx219.c | 66 ++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7240c863eed8d1..7d2f2c7c4555fc 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -596,35 +597,37 @@ static void imx219_free_controls(struct imx219 *imx219) * Subdev operations */ -static int imx219_set_framefmt(struct imx219 *imx219, - struct v4l2_subdev_state *state) +static unsigned int imx219_format_bpp(u32 code) { - const struct v4l2_mbus_framefmt *format; - const struct v4l2_rect *crop; - unsigned int bpp; - u64 bin_h, bin_v; - int ret = 0; - - format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, - IMX219_STREAM_IMAGE); - crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); - - switch (format->code) { + switch (code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: - bpp = 8; - break; + return 8; case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10: default: - bpp = 10; - break; + return 10; } +} + +static int imx219_set_framefmt(struct imx219 *imx219, + struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + unsigned int bpp; + u64 bin_h, bin_v; + int ret = 0; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); + bpp = imx219_format_bpp(format->code); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); @@ -981,6 +984,34 @@ static int imx219_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static int imx219_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; + + if (pad != IMX219_PAD_SOURCE) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX219_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = imx219_format_bpp(code) == 8 + ? MIPI_CSI2_DT_RAW8 : MIPI_CSI2_DT_RAW10; + + return 0; +} + static int imx219_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1051,6 +1082,7 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = { .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, + .get_frame_desc = imx219_get_frame_desc, .enable_streams = imx219_enable_streams, .disable_streams = imx219_disable_streams, }; From e3bc87d1a674937c05edafd8f4efc30bd2b87792 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Aug 2023 00:22:27 +0300 Subject: [PATCH 142/159] media: i2c: imx219: Add embedded data support The IMX219 generates embedded data unconditionally. Report it as an additional stream, with a new internal embedded data pad, and update subdev operations accordingly. Signed-off-by: Laurent Pinchart --- Changes since v7: - Compare fse->index with 0 in imx219_enum_frame_size() - Don't set route array size explicitly in imx219_init_state() - Implement imx219_format_edata() on top of imx219_format_bpp() - Handle streams in .{enable,disable}_streams() Changes since v6: - Get format from IMX219_STREAM_IMAGE in imx219_set_ctrl() - Fix mbus code for second stream in imx219_get_frame_desc() - Set V4L2_SUBDEV_ROUTE_FL_IMMUTABLE flag on route --- drivers/media/i2c/imx219.c | 174 ++++++++++++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7d2f2c7c4555fc..a161e555323d12 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -149,6 +149,9 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U +/* Embedded metadata stream height */ +#define IMX219_EMBEDDED_DATA_HEIGHT 2U + /* Mode : resolution and related config&values */ struct imx219_mode { /* Frame width */ @@ -317,11 +320,13 @@ static const struct imx219_mode supported_modes[] = { enum imx219_pad_ids { IMX219_PAD_SOURCE = 0, IMX219_PAD_IMAGE, + IMX219_PAD_EDATA, IMX219_NUM_PADS, }; enum imx219_stream_ids { IMX219_STREAM_IMAGE, + IMX219_STREAM_EDATA, }; struct imx219 { @@ -615,6 +620,19 @@ static unsigned int imx219_format_bpp(u32 code) } } +/* Return the embedded data format corresponding to an image format. */ +static u32 imx219_format_edata(u32 code) +{ + switch (imx219_format_bpp(code)) { + case 8: + return MEDIA_BUS_FMT_META_8; + + case 10: + default: + return MEDIA_BUS_FMT_META_10; + } +} + static int imx219_set_framefmt(struct imx219 *imx219, struct v4l2_subdev_state *state) { @@ -763,17 +781,33 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); - if (code->pad == IMX219_PAD_IMAGE) { + switch (code->pad) { + case IMX219_PAD_IMAGE: /* The internal image pad is hardwired to the native format. */ if (code->index > 0) return -EINVAL; code->code = IMX219_NATIVE_FORMAT; - } else { - /* - * On the source pad, the sensor supports multiple raw formats - * with different bit depths. - */ + return 0; + + case IMX219_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX219_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX219_STREAM_IMAGE) { u32 format; if (code->index >= ARRAY_SIZE(imx219_mbus_formats) / 4) @@ -781,6 +815,15 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, format = imx219_mbus_formats[code->index * 4]; code->code = imx219_get_format_code(imx219, format); + } else { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -792,7 +835,8 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); - if (fse->pad == IMX219_PAD_IMAGE) { + switch (fse->pad) { + case IMX219_PAD_IMAGE: if (fse->code != IMX219_NATIVE_FORMAT || fse->index > 0) return -EINVAL; @@ -800,7 +844,24 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, fse->max_width = IMX219_NATIVE_WIDTH; fse->min_height = IMX219_NATIVE_HEIGHT; fse->max_height = IMX219_NATIVE_HEIGHT; - } else { + return 0; + + case IMX219_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX219_NATIVE_WIDTH; + fse->max_width = IMX219_NATIVE_WIDTH; + fse->min_height = IMX219_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX219_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX219_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX219_STREAM_IMAGE) { if (fse->code != imx219_get_format_code(imx219, fse->code) || fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; @@ -809,6 +870,21 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, fse->max_width = fse->min_width; fse->min_height = supported_modes[fse->index].height; fse->max_height = fse->min_height; + } else { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + if (fse->code != fmt->code) + return -EINVAL; + + if (fse->index > 0) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX219_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX219_EMBEDDED_DATA_HEIGHT; } return 0; @@ -820,6 +896,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; + struct v4l2_mbus_framefmt *ed_format; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; struct v4l2_rect *crop; @@ -827,9 +904,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, /* * The driver is mode-based, the format can be set on the source pad - * only. + * only, and only for the image streeam. */ - if (fmt->pad != IMX219_PAD_SOURCE) + if (fmt->pad != IMX219_PAD_SOURCE || fmt->stream != IMX219_STREAM_IMAGE) return v4l2_subdev_get_fmt(sd, state, fmt); /* @@ -897,6 +974,21 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, IMX219_STREAM_IMAGE); *format = fmt->format; + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(state, IMX219_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX219_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + *format = *ed_format; + format->code = imx219_format_edata(format->code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; @@ -935,6 +1027,13 @@ static int imx219_get_selection(struct v4l2_subdev *sd, { struct v4l2_rect *compose; + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX219_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { case V4L2_SEL_TGT_NATIVE_SIZE: if (sel->pad != IMX219_PAD_IMAGE) @@ -987,28 +1086,34 @@ static int imx219_get_selection(struct v4l2_subdev *sd, static int imx219_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { - const struct v4l2_mbus_framefmt *fmt; struct v4l2_subdev_state *state; - u32 code; + u32 img_code; + u32 ed_code; if (pad != IMX219_PAD_SOURCE) return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, - IMX219_STREAM_IMAGE); - code = fmt->code; + img_code = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE)->code; + ed_code = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA)->code; v4l2_subdev_unlock_state(state); fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; - fd->num_entries = 1; + fd->num_entries = 2; - fd->entry[0].pixelcode = code; + fd->entry[0].pixelcode = img_code; fd->entry[0].stream = IMX219_STREAM_IMAGE; fd->entry[0].bus.csi2.vc = 0; - fd->entry[0].bus.csi2.dt = imx219_format_bpp(code) == 8 + fd->entry[0].bus.csi2.dt = imx219_format_bpp(img_code) == 8 ? MIPI_CSI2_DT_RAW8 : MIPI_CSI2_DT_RAW10; + fd->entry[1].pixelcode = ed_code; + fd->entry[1].stream = IMX219_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; + return 0; } @@ -1018,6 +1123,13 @@ static int imx219_enable_streams(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX219_STREAM_IMAGE))) + return 0; + return imx219_start_streaming(imx219, state); } @@ -1027,6 +1139,13 @@ static int imx219_disable_streams(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX219_STREAM_IMAGE))) + return 0; + imx219_stop_streaming(imx219); return 0; @@ -1035,7 +1154,7 @@ static int imx219_disable_streams(struct v4l2_subdev *sd, static int imx219_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { - struct v4l2_subdev_route routes[1] = { + struct v4l2_subdev_route routes[] = { { .sink_pad = IMX219_PAD_IMAGE, .sink_stream = 0, @@ -1043,6 +1162,13 @@ static int imx219_init_state(struct v4l2_subdev *sd, .source_stream = IMX219_STREAM_IMAGE, .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, + }, { + .sink_pad = IMX219_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX219_PAD_SOURCE, + .source_stream = IMX219_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, }, }; struct v4l2_subdev_krouting routing = { @@ -1066,6 +1192,10 @@ static int imx219_init_state(struct v4l2_subdev *sd, if (ret) return ret; + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ imx219_set_pad_format(sd, state, &fmt); return 0; @@ -1328,12 +1458,14 @@ static int imx219_probe(struct i2c_client *client) /* * Initialize the pads. To preserve backward compatibility with * userspace that used the sensor before the introduction of the - * internal image pad, the external source pad is numbered 0 and the - * internal image pad numbered 1. + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. */ imx219->pads[IMX219_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; imx219->pads[IMX219_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL; + imx219->pads[IMX219_PAD_EDATA].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_INTERNAL; ret = media_entity_pads_init(&imx219->sd.entity, ARRAY_SIZE(imx219->pads), imx219->pads); From 93f7e0cd8f6c774e51e50f66458422ef217f5765 Mon Sep 17 00:00:00 2001 From: David Plowman Date: Tue, 25 Jan 2022 15:48:53 +0000 Subject: [PATCH 143/159] media: i2c: imx219: Correct the minimum vblanking value The datasheet for this sensor documents the minimum vblanking as being 32 lines. It does fix some problems with occasional black lines at the bottom of images (tested on Raspberry Pi). Signed-off-by: David Plowman --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index a161e555323d12..b1fb0c280d389f 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -75,7 +75,7 @@ #define IMX219_REG_VTS CCI_REG16(0x0160) #define IMX219_VTS_MAX 0xffff -#define IMX219_VBLANK_MIN 4 +#define IMX219_VBLANK_MIN 32 /* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448 From 4b6dba89bed2a3eda41423d5a43776e4993632f5 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 14 Sep 2023 12:23:05 +0100 Subject: [PATCH 144/159] media: i2c: imx219: make HBLANK r/w to allow longer exposures The HBLANK control was read-only, and always configured such that the sensor HTS register was 3448. This limited the maximum exposure time that could be achieved to around 1.26 secs. Make HBLANK read/write so that the line time can be extended, and thereby allow longer exposures (and slower frame rates). Retain the overall HTS setting when changing modes rather than resetting it to a default. Signed-off-by: Dave Stevenson Signed-off-by: Jai Luthra --- drivers/media/i2c/imx219.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index b1fb0c280d389f..40e6aae80fd5af 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -77,8 +77,10 @@ #define IMX219_VBLANK_MIN 32 -/* HBLANK control - read only */ -#define IMX219_PPL_DEFAULT 3448 +/* HBLANK control range */ +#define IMX219_PPL_MIN 3448 +#define IMX219_PPL_MAX 0x7ff0 +#define IMX219_REG_HTS 0x0162 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) @@ -440,6 +442,10 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) cci_write(imx219->regmap, IMX219_REG_VTS, format->height + ctrl->val, &ret); break; + case V4L2_CID_HBLANK: + cci_write(imx219->regmap, IMX219_REG_HTS, + format->width + ctrl->val, &ret); + break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, ctrl->val, &ret); @@ -514,12 +520,11 @@ static int imx219_init_controls(struct imx219 *imx219) V4L2_CID_VBLANK, IMX219_VBLANK_MIN, IMX219_VTS_MAX - mode->height, 1, mode->vts_def - mode->height); - hblank = IMX219_PPL_DEFAULT - mode->width; + hblank = IMX219_PPL_MIN - mode->width; imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, + V4L2_CID_HBLANK, hblank, + IMX219_PPL_MIN - mode->width, 1, hblank); - if (imx219->hblank) - imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; exposure_max = mode->vts_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; @@ -990,6 +995,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, format->code = imx219_format_edata(format->code); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + u32 prev_hts = format->width + imx219->hblank->val; int exposure_max; int exposure_def; int hblank; @@ -1009,13 +1015,18 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, exposure_max, imx219->exposure->step, exposure_def); /* - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank - * depends on mode->width only, and is not changeble in any - * way other than changing the mode. + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. */ - hblank = IMX219_PPL_DEFAULT - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, - hblank); + hblank = prev_hts - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, + IMX219_PPL_MIN - mode->width, + IMX219_PPL_MAX - mode->width, + 1, IMX219_PPL_MIN - mode->width); + __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); } return 0; From 79065470919c1bf6b004668048068ca1ad35474e Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 8 Feb 2022 13:49:11 +0000 Subject: [PATCH 145/159] media: i2c: imx219: Scale the pixel clock rate for the 640x480 mode The 640x480 mode uses a special binning mode for high framerate operation where the pixel rate is effectively doubled. Account for this when setting up the pixel clock rate, and applying the vblank and exposure controls. Signed-off-by: Naushir Patuck Signed-off-by: Jai Luthra --- drivers/media/i2c/imx219.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 40e6aae80fd5af..420bb28fcfe45e 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -163,6 +163,9 @@ struct imx219_mode { /* V-timing */ unsigned int vts_def; + + /* Relative pixel clock rate factor for the mode. */ + unsigned int rate_factor; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -298,24 +301,32 @@ static const struct imx219_mode supported_modes[] = { .width = 3280, .height = 2464, .vts_def = 3526, + .rate_factor = 1, }, { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, .vts_def = 1763, + .rate_factor = 1, }, { /* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, .vts_def = 1763, + .rate_factor = 1, }, { /* 640x480 30fps mode */ .width = 640, .height = 480, .vts_def = 1763, + /* + * This mode uses a special 2x2 binning that doubles the + * internal pixel clock rate. + */ + .rate_factor = 2, }, }; @@ -354,6 +365,9 @@ struct imx219 { /* Two or Four lanes */ u8 lanes; + + /* Rate factor */ + unsigned int rate_factor; }; static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd) @@ -423,7 +437,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val, &ret); + ctrl->val / imx219->rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -440,7 +454,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - format->height + ctrl->val, &ret); + (format->height + ctrl->val) / imx219->rate_factor, &ret); break; case V4L2_CID_HBLANK: cci_write(imx219->regmap, IMX219_REG_HTS, @@ -481,7 +495,8 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { - return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE; + return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : + IMX219_PIXEL_RATE_4LANE) * imx219->rate_factor; } /* Initialize control handlers */ @@ -491,7 +506,7 @@ static int imx219_init_controls(struct imx219 *imx219) const struct imx219_mode *mode = &supported_modes[0]; struct v4l2_ctrl_handler *ctrl_hdlr; struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank; + int exposure_max, exposure_def, hblank, pixel_rate; int i, ret; ctrl_hdlr = &imx219->ctrl_handler; @@ -500,11 +515,11 @@ static int imx219_init_controls(struct imx219 *imx219) return ret; /* By default, PIXEL_RATE is read only */ + pixel_rate = imx219_get_pixel_rate(imx219); imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_PIXEL_RATE, - imx219_get_pixel_rate(imx219), - imx219_get_pixel_rate(imx219), 1, - imx219_get_pixel_rate(imx219)); + pixel_rate, pixel_rate, 1, + pixel_rate); imx219->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, @@ -999,6 +1014,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, int exposure_max; int exposure_def; int hblank; + int pixel_rate; + + imx219->rate_factor = mode->rate_factor; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, @@ -1027,6 +1045,11 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, IMX219_PPL_MAX - mode->width, 1, IMX219_PPL_MIN - mode->width); __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + /* Scale the pixel rate based on the mode specific factor */ + pixel_rate = imx219_get_pixel_rate(imx219); + __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); } return 0; From 2003f3d077ba89746cee2082e7043d5822c38f5a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 27 Dec 2023 19:51:45 +0000 Subject: [PATCH 146/159] fixup downstream patch post driver conversion to CCI_REG HTS was still using the raw register ID. Fixes: dd26d43ddb7f ("media: i2c: imx219: make HBLANK r/w to allow longer exposures") Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 420bb28fcfe45e..fc4b9ca207f71f 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -80,7 +80,7 @@ /* HBLANK control range */ #define IMX219_PPL_MIN 3448 #define IMX219_PPL_MAX 0x7ff0 -#define IMX219_REG_HTS 0x0162 +#define IMX219_REG_HTS CCI_REG16(0x0162) #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) From 5601b5a4f08405ce5745cdbdc40b8e5a958661ae Mon Sep 17 00:00:00 2001 From: Vinay Varma Date: Tue, 22 Oct 2024 12:30:23 +0530 Subject: [PATCH 147/159] media: i2c: imx219: fix binning and rate_factor for 480p and 1232p At a high FPS with RAW10, there is frame corruption for 480p because the rate_factor of 2 is used with the normal 2x2 bining [1]. This commit ties the rate_factor to the selected binning mode. For the 480p mode, analog 2x2 binning mode with a rate_factor of 2 is always used. For the 1232p mode the normal 2x2 binning mode is used for RAW10 while analog 2x2 binning mode is used for RAW8. [1] raspberrypi#5493 Signed-off-by: Vinay Varma Reworked due to upstream changes Signed-off-by: Dave Stevenson Reworked again due to upstream changes Signed-off-by: Jai Luthra --- drivers/media/i2c/imx219.c | 108 +++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 35 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index fc4b9ca207f71f..2d23ccece489ad 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -154,6 +154,18 @@ /* Embedded metadata stream height */ #define IMX219_EMBEDDED_DATA_HEIGHT 2U +enum binning_mode { + BINNING_NONE, + BINNING_X2, + BINNING_ANALOG_X2, +}; + +enum binning_bit_depths { + BINNING_IDX_8_BIT, + BINNING_IDX_10_BIT, + BINNING_IDX_MAX +}; + /* Mode : resolution and related config&values */ struct imx219_mode { /* Frame width */ @@ -164,8 +176,8 @@ struct imx219_mode { /* V-timing */ unsigned int vts_def; - /* Relative pixel clock rate factor for the mode. */ - unsigned int rate_factor; + /* binning mode based on format code */ + enum binning_mode binning[BINNING_IDX_MAX]; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -301,32 +313,40 @@ static const struct imx219_mode supported_modes[] = { .width = 3280, .height = 2464, .vts_def = 3526, - .rate_factor = 1, + .binning = { + [BINNING_IDX_8_BIT] = BINNING_NONE, + [BINNING_IDX_10_BIT] = BINNING_NONE, + }, }, { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, .vts_def = 1763, - .rate_factor = 1, + .binning = { + [BINNING_IDX_8_BIT] = BINNING_NONE, + [BINNING_IDX_10_BIT] = BINNING_NONE, + }, }, { /* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, .vts_def = 1763, - .rate_factor = 1, + .binning = { + [BINNING_IDX_8_BIT] = BINNING_ANALOG_X2, + [BINNING_IDX_10_BIT] = BINNING_X2, + }, }, { /* 640x480 30fps mode */ .width = 640, .height = 480, .vts_def = 1763, - /* - * This mode uses a special 2x2 binning that doubles the - * internal pixel clock rate. - */ - .rate_factor = 2, + .binning = { + [BINNING_IDX_8_BIT] = BINNING_ANALOG_X2, + [BINNING_IDX_10_BIT] = BINNING_ANALOG_X2, + }, }, }; @@ -366,8 +386,8 @@ struct imx219 { /* Two or Four lanes */ u8 lanes; - /* Rate factor */ - unsigned int rate_factor; + /* Binning mode */ + enum binning_mode binning; }; static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd) @@ -393,6 +413,18 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } +static int imx219_get_rate_factor(struct imx219 *imx219) +{ + switch (imx219->binning) { + case BINNING_NONE: + case BINNING_X2: + return 1; + case BINNING_ANALOG_X2: + return 2; + } + return -EINVAL; +} + /* ----------------------------------------------------------------------------- * Controls */ @@ -404,11 +436,13 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + int rate_factor; int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, IMX219_STREAM_IMAGE); + rate_factor = imx219_get_rate_factor(imx219); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -437,7 +471,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val / imx219->rate_factor, &ret); + ctrl->val / rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -454,7 +488,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - (format->height + ctrl->val) / imx219->rate_factor, &ret); + (format->height + ctrl->val) / rate_factor, &ret); break; case V4L2_CID_HBLANK: cci_write(imx219->regmap, IMX219_REG_HTS, @@ -496,7 +530,7 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : - IMX219_PIXEL_RATE_4LANE) * imx219->rate_factor; + IMX219_PIXEL_RATE_4LANE) * imx219_get_rate_factor(imx219); } /* Initialize control handlers */ @@ -658,8 +692,8 @@ static int imx219_set_framefmt(struct imx219 *imx219, { const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; - unsigned int bpp; - u64 bin_h, bin_v; + u64 binning; + u32 bpp; int ret = 0; format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, @@ -676,28 +710,20 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - switch (crop->width / format->width) { - case 1: - default: - bin_h = IMX219_BINNING_NONE; + switch (imx219->binning) { + case BINNING_NONE: + binning = IMX219_BINNING_NONE; break; - case 2: - bin_h = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - - switch (crop->height / format->height) { - case 1: - default: - bin_v = IMX219_BINNING_NONE; + case BINNING_X2: + binning = IMX219_BINNING_X2; break; - case 2: - bin_v = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; + case BINNING_ANALOG_X2: + binning = IMX219_BINNING_X2_ANALOG; break; } - cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); - cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, bin_v, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, binning, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, binning, &ret); cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, format->width, &ret); @@ -1016,7 +1042,19 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, int hblank; int pixel_rate; - imx219->rate_factor = mode->rate_factor; + /* Update binning mode based on format */ + switch (imx219_format_bpp(fmt->format.code)) { + case 8: + imx219->binning = mode->binning[BINNING_IDX_8_BIT]; + break; + + case 10: + imx219->binning = mode->binning[BINNING_IDX_10_BIT]; + break; + + default: + imx219->binning = BINNING_NONE; + } /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, From 546fab174f9cfa26a45acb49589a9fce20927bad Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Wed, 20 Mar 2024 09:45:24 +0000 Subject: [PATCH 148/159] drivers: media: i2c: Add streams support to IMX477 Add the V4L2 streams API support for embedded data in the IMX477 device driver. This also updates the drier to use the V4L2 subdev state API. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- drivers/media/i2c/imx477.c | 712 ++++++++++++++++++++----------------- 1 file changed, 392 insertions(+), 320 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 720192f484cbe8..edb18d75ca23e4 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -112,17 +113,8 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_REG_XVS_IO_CTRL 0x3040 #define IMX477_REG_EXTOUT_EN 0x4b81 -/* Embedded metadata stream structure */ -#define IMX477_EMBEDDED_LINE_WIDTH 16384 -#define IMX477_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* IMX477 native and active pixel array size. */ +#define IMX477_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB12_1X12 #define IMX477_NATIVE_WIDTH 4072U #define IMX477_NATIVE_HEIGHT 3176U #define IMX477_PIXEL_ARRAY_LEFT 8U @@ -130,6 +122,9 @@ enum pad_types { #define IMX477_PIXEL_ARRAY_WIDTH 4056U #define IMX477_PIXEL_ARRAY_HEIGHT 3040U +/* Embedded metadata stream height */ +#define IMX477_EMBEDDED_DATA_HEIGHT 2U + struct imx477_reg { u16 address; u8 val; @@ -1076,7 +1071,7 @@ static const struct imx477_mode supported_modes_10bit[] = { * - v flip * - h&v flips */ -static const u32 codes[] = { +static const u32 imx477_codes[] = { /* 12-bit modes. */ MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, @@ -1126,6 +1121,18 @@ static const char * const imx477_supply_name[] = { #define IMX477_XCLR_MIN_DELAY_US 8000 #define IMX477_XCLR_DELAY_RANGE_US 1000 +enum imx477_pad_ids { + IMX477_PAD_SOURCE, + IMX477_PAD_IMAGE, + IMX477_PAD_EDATA, + IMX477_NUM_PADS, +}; + +enum imx477_stream_ids { + IMX477_STREAM_IMAGE, + IMX477_STREAM_EDATA, +}; + struct imx477_compatible_data { unsigned int chip_id; struct imx477_reg_list extra_regs; @@ -1133,9 +1140,7 @@ struct imx477_compatible_data { struct imx477 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - unsigned int fmt_code; + struct media_pad pads[IMX477_NUM_PADS]; struct clk *xclk; u32 xclk_freq; @@ -1161,15 +1166,6 @@ struct imx477 { /* Trigger mode */ int trigger_mode_of; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1289,62 +1285,17 @@ static u32 imx477_get_format_code(struct imx477 *imx477, u32 code) { unsigned int i; - lockdep_assert_held(&imx477->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == code) + for (i = 0; i < ARRAY_SIZE(imx477_codes); i++) + if (imx477_codes[i] == code) break; - if (i >= ARRAY_SIZE(codes)) + if (i >= ARRAY_SIZE(imx477_codes)) i = 0; i = (i & ~3) | (imx477->vflip->val ? 2 : 0) | (imx477->hflip->val ? 1 : 0); - return codes[i]; -} - -static void imx477_set_default_format(struct imx477 *imx477) -{ - /* Set default mode to max resolution */ - imx477->mode = &supported_modes_12bit[0]; - imx477->fmt_code = MEDIA_BUS_FMT_SRGGB12_1X12; -} - -static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx477 *imx477 = to_imx477(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_state_get_format(fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx477->mutex); - - /* Initialize try_fmt for the image pad */ - try_fmt_img->width = supported_modes_12bit[0].width; - try_fmt_img->height = supported_modes_12bit[0].height; - try_fmt_img->code = imx477_get_format_code(imx477, - MEDIA_BUS_FMT_SRGGB12_1X12); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX477_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX477_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); - try_crop->left = IMX477_PIXEL_ARRAY_LEFT; - try_crop->top = IMX477_PIXEL_ARRAY_TOP; - try_crop->width = IMX477_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX477_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx477->mutex); - - return 0; + return imx477_codes[i]; } static void imx477_adjust_exposure_range(struct imx477 *imx477) @@ -1385,8 +1336,11 @@ static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) struct imx477 *imx477 = container_of(ctrl->handler, struct imx477, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); + struct v4l2_subdev_state *state; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx477->sd); + /* * The VBLANK control may change the limits of usable exposure, so check * and adjust if necessary. @@ -1473,20 +1427,52 @@ static int imx477_enum_mbus_code(struct v4l2_subdev *sd, { struct imx477 *imx477 = to_imx477(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX477_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index >= (ARRAY_SIZE(codes) / 4)) + switch (code->pad) { + case IMX477_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX477_NATIVE_FORMAT; + return 0; + + case IMX477_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX477_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX477_STREAM_IMAGE) { + u32 format; + + if (code->index >= (ARRAY_SIZE(imx477_codes) / 4)) return -EINVAL; - code->code = imx477_get_format_code(imx477, - codes[code->index * 4]); + format = imx477_codes[code->index * 4]; + code->code = imx477_get_format_code(imx477, format); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1498,19 +1484,39 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, { struct imx477 *imx477 = to_imx477(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX477_PAD_IMAGE: + if (fse->code != IMX477_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX477_NATIVE_WIDTH; + fse->max_width = IMX477_NATIVE_WIDTH; + fse->min_height = IMX477_NATIVE_HEIGHT; + fse->max_height = IMX477_NATIVE_HEIGHT; + return 0; - if (fse->pad == IMAGE_PAD) { + case IMX477_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX477_NATIVE_WIDTH; + fse->max_width = IMX477_NATIVE_WIDTH; + fse->min_height = IMX477_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX477_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX477_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX477_STREAM_IMAGE) { const struct imx477_mode *mode_list; unsigned int num_modes; get_mode_table(fse->code, &mode_list, &num_modes); - - if (fse->index >= num_modes) - return -EINVAL; - - if (fse->code != imx477_get_format_code(imx477, fse->code)) + if (fse->code != imx477_get_format_code(imx477, fse->code) || + fse->index >= num_modes) return -EINVAL; fse->min_width = mode_list[fse->index].width; @@ -1518,77 +1524,22 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = mode_list[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; - - fse->min_width = IMX477_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX477_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } + struct v4l2_mbus_framefmt *fmt; - return 0; -} - -static void imx477_reset_colorspace(struct v4l2_mbus_framefmt *fmt) -{ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} - -static void imx477_update_image_pad_format(struct imx477 *imx477, - const struct imx477_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx477_reset_colorspace(&fmt->format); -} - -static void imx477_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX477_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX477_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} - -static int imx477_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx477 *imx477 = to_imx477(sd); - - if (fmt->pad >= NUM_PADS) - return -EINVAL; + fmt = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + if (fse->code != fmt->code) + return -EINVAL; - mutex_lock(&imx477->mutex); + if (fse->index) + return -EINVAL; - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx477_get_format_code(imx477, try_fmt->code) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx477_update_image_pad_format(imx477, imx477->mode, - fmt); - fmt->format.code = - imx477_get_format_code(imx477, imx477->fmt_code); - } else { - imx477_update_metadata_pad_format(fmt); - } + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX477_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX477_EMBEDDED_DATA_HEIGHT; } - mutex_unlock(&imx477->mutex); return 0; } @@ -1635,106 +1586,271 @@ static void imx477_set_framing_limits(struct imx477 *imx477) __v4l2_ctrl_s_ctrl(imx477->hblank, hblank_min); } +static int imx477_format_bpp(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + return 12; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static int imx477_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; + + if (pad != IMX477_PAD_SOURCE) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; + + memset(fd->entry, 0, sizeof(fd->entry)); + + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX477_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = imx477_format_bpp(code) == 10 + ? MIPI_CSI2_DT_RAW10 : MIPI_CSI2_DT_RAW12; + + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX477_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; + + return 0; +} + static int imx477_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx477_mode *mode; struct imx477 *imx477 = to_imx477(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx477_mode *mode_list; + const struct imx477_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + unsigned int num_modes; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX477_PAD_SOURCE || fmt->stream != IMX477_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx477->mutex); + get_mode_table(fmt->format.code, &mode_list, &num_modes); - if (fmt->pad == IMAGE_PAD) { - const struct imx477_mode *mode_list; - unsigned int num_modes; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); - /* Bayer order varies with flips */ - fmt->format.code = imx477_get_format_code(imx477, - fmt->format.code); - - get_mode_table(fmt->format.code, &mode_list, &num_modes); - - mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - fmt->format.width, - fmt->format.height); - imx477_update_image_pad_format(imx477, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else if (imx477->mode != mode) { - imx477->mode = mode; - imx477->fmt_code = fmt->format.code; - imx477_set_framing_limits(imx477); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx477_update_metadata_pad_format(fmt); - } - } + fmt->format.code = imx477_get_format_code(imx477, fmt->format.code); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_IMAGE); + *format = fmt->format; + format->code = IMX477_NATIVE_FORMAT; + format->width = IMX477_NATIVE_WIDTH; + format->height = IMX477_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX477_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX477_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; - mutex_unlock(&imx477->mutex); + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + *format = fmt->format; + + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX477_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + *format = *ed_format; + format->code = imx477_format_bpp(format->code) == 10 + ? MEDIA_BUS_FMT_META_10 : MEDIA_BUS_FMT_META_12; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx477->mode = mode; + imx477_set_framing_limits(imx477); + } return 0; } -static const struct v4l2_rect * -__imx477_get_pad_crop(struct imx477 *imx477, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx477_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx477->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX477_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX477_PAD_SOURCE, + .source_stream = IMX477_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX477_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX477_PAD_SOURCE, + .source_stream = IMX477_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX477_PAD_SOURCE, + .stream = IMX477_STREAM_IMAGE, + .format = { + .code = IMX477_NATIVE_FORMAT, + .width = supported_modes_12bit[0].width, + .height = supported_modes_12bit[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; - return NULL; + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; + + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx477_set_pad_format(sd, state, &fmt); + + return 0; } static int imx477_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx477 *imx477 = to_imx477(sd); + struct v4l2_rect *compose; - mutex_lock(&imx477->mutex); - sel->r = *__imx477_get_pad_crop(imx477, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx477->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX477_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX477_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX477_NATIVE_WIDTH; sel->r.height = IMX477_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX477_PIXEL_ARRAY_LEFT; - sel->r.top = IMX477_PIXEL_ARRAY_TOP; - sel->r.width = IMX477_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX477_PAD_IMAGE: + sel->r.top = IMX477_PIXEL_ARRAY_TOP; + sel->r.left = IMX477_PIXEL_ARRAY_LEFT; + sel->r.width = IMX477_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX477_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX477_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX477_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1749,6 +1865,10 @@ static int imx477_start_streaming(struct imx477 *imx477) const struct imx477_reg_list *extra_regs; int ret, tm; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + if (!imx477->common_regs_written) { ret = imx477_write_regs(imx477, mode_common_regs, ARRAY_SIZE(mode_common_regs)); @@ -1768,7 +1888,7 @@ static int imx477_start_streaming(struct imx477 *imx477) if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } imx477->common_regs_written = true; } @@ -1778,7 +1898,7 @@ static int imx477_start_streaming(struct imx477 *imx477) ret = imx477_write_regs(imx477, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } /* Set on-sensor DPC. */ @@ -1788,7 +1908,7 @@ static int imx477_start_streaming(struct imx477 *imx477) /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler); if (ret) - return ret; + goto error_pm_put; /* Set vsync trigger mode: 0=standalone, 1=source, 2=sink */ tm = (imx477->trigger_mode_of >= 0) ? imx477->trigger_mode_of : trigger_mode; @@ -1802,8 +1922,16 @@ static int imx477_start_streaming(struct imx477 *imx477) IMX477_REG_VALUE_08BIT, (tm == 1) ? 1 : 0); /* set stream on register */ - return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, - IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING); + ret = imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, + IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1821,53 +1949,28 @@ static void imx477_stop_streaming(struct imx477 *imx477) /* Stop driving XVS out (there is still a weak pull-up) */ imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN, IMX477_REG_VALUE_08BIT, 0); + + pm_runtime_put(&client->dev); } static int imx477_set_stream(struct v4l2_subdev *sd, int enable) { struct imx477 *imx477 = to_imx477(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx477->mutex); - if (imx477->streaming == enable) { - mutex_unlock(&imx477->mutex); - return 0; - } + state = v4l2_subdev_lock_and_get_active_state(sd); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } - - /* - * Apply default & customized values - * and then start streaming. - */ + if (enable) ret = imx477_start_streaming(imx477); - if (ret) - goto err_rpm_put; - } else { + else imx477_stop_streaming(imx477); - pm_runtime_put(&client->dev); - } - - imx477->streaming = enable; /* vflip and hflip cannot change during streaming */ __v4l2_ctrl_grab(imx477->vflip, enable); __v4l2_ctrl_grab(imx477->hflip, enable); - mutex_unlock(&imx477->mutex); - - return ret; - -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx477->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1922,39 +2025,6 @@ static int imx477_power_off(struct device *dev) return 0; } -static int __maybe_unused imx477_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx477 *imx477 = to_imx477(sd); - - if (imx477->streaming) - imx477_stop_streaming(imx477); - - return 0; -} - -static int __maybe_unused imx477_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx477 *imx477 = to_imx477(sd); - int ret; - - if (imx477->streaming) { - ret = imx477_start_streaming(imx477); - if (ret) - goto error; - } - - return 0; - -error: - imx477_stop_streaming(imx477); - imx477->streaming = 0; - return ret; -} - static int imx477_get_regulators(struct imx477 *imx477) { struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); @@ -2005,10 +2075,11 @@ static const struct v4l2_subdev_video_ops imx477_video_ops = { static const struct v4l2_subdev_pad_ops imx477_pad_ops = { .enum_mbus_code = imx477_enum_mbus_code, - .get_fmt = imx477_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx477_set_pad_format, .get_selection = imx477_get_selection, .enum_frame_size = imx477_enum_frame_size, + .get_frame_desc = imx477_get_frame_desc, }; static const struct v4l2_subdev_ops imx477_subdev_ops = { @@ -2018,7 +2089,7 @@ static const struct v4l2_subdev_ops imx477_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx477_internal_ops = { - .open = imx477_open, + .init_state = imx477_init_state, }; /* Initialize control handlers */ @@ -2035,9 +2106,6 @@ static int imx477_init_controls(struct imx477 *imx477) if (ret) return ret; - mutex_init(&imx477->mutex); - ctrl_hdlr->lock = &imx477->mutex; - /* By default, PIXEL_RATE is read only */ imx477->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -2057,7 +2125,7 @@ static int imx477_init_controls(struct imx477 *imx477) /* * Create the controls here, but mode specific limits are setup - * in the imx477_set_framing_limits() call below. + * in the imx477_set_framing_limits() call. */ imx477->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -2127,18 +2195,10 @@ static int imx477_init_controls(struct imx477 *imx477) imx477->sd.ctrl_handler = ctrl_hdlr; - mutex_lock(&imx477->mutex); - - /* Setup exposure and frame/line length limits. */ - imx477_set_framing_limits(imx477); - - mutex_unlock(&imx477->mutex); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx477->mutex); return ret; } @@ -2146,7 +2206,6 @@ static int imx477_init_controls(struct imx477 *imx477) static void imx477_free_controls(struct imx477 *imx477) { v4l2_ctrl_handler_free(imx477->sd.ctrl_handler); - mutex_destroy(&imx477->mutex); } static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) @@ -2245,6 +2304,7 @@ static int imx477_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops); + imx477->sd.internal_ops = &imx477_internal_ops; match = of_match_device(imx477_dt_ids, dev); if (!match) @@ -2296,43 +2356,58 @@ static int imx477_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Initialize default format */ - imx477_set_default_format(imx477); - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - /* This needs the pm runtime to be registered. */ ret = imx477_init_controls(imx477); if (ret) goto error_power_off; /* Initialize subdev */ - imx477->sd.internal_ops = &imx477_internal_ops; imx477->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; imx477->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pads */ - imx477->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx477->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx477->sd.entity, NUM_PADS, imx477->pad); + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. + */ + imx477->pads[IMX477_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx477->pads[IMX477_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx477->pads[IMX477_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx477->sd.entity, + ARRAY_SIZE(imx477->pads), imx477->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx477->sd.state_lock = imx477->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx477->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx477->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx477->sd); + error_media_entity: media_entity_cleanup(&imx477->sd.entity); @@ -2340,8 +2415,6 @@ static int imx477_probe(struct i2c_client *client) imx477_free_controls(imx477); error_power_off: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); imx477_power_off(&client->dev); return ret; @@ -2365,7 +2438,6 @@ static void imx477_remove(struct i2c_client *client) MODULE_DEVICE_TABLE(of, imx477_dt_ids); static const struct dev_pm_ops imx477_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx477_suspend, imx477_resume) SET_RUNTIME_PM_OPS(imx477_power_off, imx477_power_on, NULL) }; From 7b3734aa6f97fd6bb0278a222c36fd8fe6279c36 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 25 Oct 2024 13:18:23 +0530 Subject: [PATCH 149/159] media: i2c: imx477: Switch to .{enable,disable}_streams() operations The .s_stream() operation is not compatible with streams, so switch to the .{enable,disable}_streams() operations. Signed-off-by: Jai Luthra --- drivers/media/i2c/imx477.c | 52 +++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index edb18d75ca23e4..df4a66c7dc07ac 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -1953,26 +1953,45 @@ static void imx477_stop_streaming(struct imx477 *imx477) pm_runtime_put(&client->dev); } -static int imx477_set_stream(struct v4l2_subdev *sd, int enable) +static int imx477_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx477 *imx477 = to_imx477(sd); - struct v4l2_subdev_state *state; - int ret = 0; - - state = v4l2_subdev_lock_and_get_active_state(sd); - if (enable) - ret = imx477_start_streaming(imx477); - else - imx477_stop_streaming(imx477); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX477_STREAM_IMAGE))) + return 0; /* vflip and hflip cannot change during streaming */ - __v4l2_ctrl_grab(imx477->vflip, enable); - __v4l2_ctrl_grab(imx477->hflip, enable); + __v4l2_ctrl_grab(imx477->vflip, true); + __v4l2_ctrl_grab(imx477->hflip, true); - v4l2_subdev_unlock_state(state); + return imx477_start_streaming(imx477); +} - return ret; +static int imx477_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx477 *imx477 = to_imx477(sd); + + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX477_STREAM_IMAGE))) + return 0; + + __v4l2_ctrl_grab(imx477->vflip, false); + __v4l2_ctrl_grab(imx477->hflip, false); + + imx477_stop_streaming(imx477); + + return 0; } /* Power/clock management functions */ @@ -2069,10 +2088,6 @@ static const struct v4l2_subdev_core_ops imx477_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx477_video_ops = { - .s_stream = imx477_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx477_pad_ops = { .enum_mbus_code = imx477_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, @@ -2080,11 +2095,12 @@ static const struct v4l2_subdev_pad_ops imx477_pad_ops = { .get_selection = imx477_get_selection, .enum_frame_size = imx477_enum_frame_size, .get_frame_desc = imx477_get_frame_desc, + .enable_streams = imx477_enable_streams, + .disable_streams = imx477_disable_streams, }; static const struct v4l2_subdev_ops imx477_subdev_ops = { .core = &imx477_core_ops, - .video = &imx477_video_ops, .pad = &imx477_pad_ops, }; From f26b91084a084092b4fd02350b4437944bc650ae Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Wed, 20 Mar 2024 09:45:33 +0000 Subject: [PATCH 150/159] drivers: media: i2c: Add streams support to IMX708 Add the V4L2 streams API support for embedded/pdaf/hdr data in the IMX708 device driver. This also updates the drier to use the V4L2 subdev state API. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- drivers/media/i2c/imx708.c | 669 ++++++++++++++++++++----------------- 1 file changed, 354 insertions(+), 315 deletions(-) diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c index d2c822202cafc1..5ed1d05cc1a639 100644 --- a/drivers/media/i2c/imx708.c +++ b/drivers/media/i2c/imx708.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -118,22 +119,8 @@ MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5] #define IMX708_LPF_INTENSITY_DISABLED 0x01 #define IMX708_LPF_INTENSITY 0xC429 -/* - * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). - * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) - * of embedded data, one line of PDAF data, and two lines of AE-HIST data - * (AE histograms are valid for HDR mode and empty in non-HDR modes). - */ -#define IMX708_EMBEDDED_LINE_WIDTH (5 * 5760) -#define IMX708_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* IMX708 native and active pixel array size. */ +#define IMX708_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX708_NATIVE_WIDTH 4640U #define IMX708_NATIVE_HEIGHT 2658U #define IMX708_PIXEL_ARRAY_LEFT 16U @@ -141,6 +128,15 @@ enum pad_types { #define IMX708_PIXEL_ARRAY_WIDTH 4608U #define IMX708_PIXEL_ARRAY_HEIGHT 2592U +/* + * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). + * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) + * of embedded data, one line of PDAF data, and two lines of AE-HIST data + * (AE histograms are valid for HDR mode and empty in non-HDR modes). + */ +#define IMX708_EMBEDDED_DATA_WIDTH IMX708_PIXEL_ARRAY_WIDTH +#define IMX708_EMBEDDED_DATA_HEIGHT 5U + struct imx708_reg { u16 address; u8 val; @@ -822,11 +818,21 @@ static const char * const imx708_supply_name[] = { #define IMX708_XCLR_MIN_DELAY_US 8000 #define IMX708_XCLR_DELAY_RANGE_US 1000 +enum imx708_pad_ids { + IMX708_PAD_SOURCE, + IMX708_PAD_IMAGE, + IMX708_PAD_EDATA, + IMX708_NUM_PADS, +}; + +enum imx708_stream_ids { + IMX708_STREAM_IMAGE, + IMX708_STREAM_EDATA, +}; + struct imx708 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - struct v4l2_mbus_framefmt fmt; + struct media_pad pads[IMX708_NUM_PADS]; struct clk *inclk; u32 inclk_freq; @@ -850,15 +856,6 @@ struct imx708 { /* Current mode */ const struct imx708_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -976,73 +973,12 @@ static u32 imx708_get_format_code(struct imx708 *imx708) { unsigned int i; - lockdep_assert_held(&imx708->mutex); - i = (imx708->vflip->val ? 2 : 0) | (imx708->hflip->val ? 1 : 0); return codes[i]; } -static void imx708_set_default_format(struct imx708 *imx708) -{ - struct v4l2_mbus_framefmt *fmt = &imx708->fmt; - - /* Set default mode to max resolution */ - imx708->mode = &supported_modes_10bit_no_hdr[0]; - - /* fmt->code not set as it will always be computed based on flips */ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = imx708->mode->width; - fmt->height = imx708->mode->height; - fmt->field = V4L2_FIELD_NONE; -} - -static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx708 *imx708 = to_imx708(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_state_get_format(fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx708->mutex); - - /* Initialize try_fmt for the image pad */ - if (imx708->hdr_mode->val) { - try_fmt_img->width = supported_modes_10bit_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_hdr[0].height; - } else { - try_fmt_img->width = supported_modes_10bit_no_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_no_hdr[0].height; - } - try_fmt_img->code = imx708_get_format_code(imx708); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX708_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX708_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); - try_crop->left = IMX708_PIXEL_ARRAY_LEFT; - try_crop->top = IMX708_PIXEL_ARRAY_TOP; - try_crop->width = IMX708_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX708_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx708->mutex); - - return 0; -} - static int imx708_set_exposure(struct imx708 *imx708, unsigned int val) { val = max(val, imx708->mode->exposure_lines_min); @@ -1135,9 +1071,12 @@ static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct imx708, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); const struct imx708_mode *mode_list; + struct v4l2_subdev_state *state; unsigned int code, num_modes; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx708->sd); + switch (ctrl->id) { case V4L2_CID_VBLANK: /* @@ -1251,19 +1190,49 @@ static int imx708_enum_mbus_code(struct v4l2_subdev *sd, { struct imx708 *imx708 = to_imx708(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX708_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { + switch (code->pad) { + case IMX708_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX708_NATIVE_FORMAT; + return 0; + + case IMX708_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX708_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX708_STREAM_IMAGE) { if (code->index >= (ARRAY_SIZE(codes) / 4)) return -EINVAL; code->code = imx708_get_format_code(imx708); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1275,20 +1244,41 @@ static int imx708_enum_frame_size(struct v4l2_subdev *sd, { struct imx708 *imx708 = to_imx708(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX708_PAD_IMAGE: + if (fse->code != IMX708_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX708_NATIVE_WIDTH; + fse->max_width = IMX708_NATIVE_WIDTH; + fse->min_height = IMX708_NATIVE_HEIGHT; + fse->max_height = IMX708_NATIVE_HEIGHT; + return 0; + + case IMX708_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->max_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->min_height = IMX708_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX708_EMBEDDED_DATA_HEIGHT; + return 0; - if (fse->pad == IMAGE_PAD) { + case IMX708_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX708_STREAM_IMAGE) { const struct imx708_mode *mode_list; unsigned int num_modes; get_mode_table(fse->code, &mode_list, &num_modes, imx708->hdr_mode->val); - if (fse->index >= num_modes) - return -EINVAL; - - if (fse->code != imx708_get_format_code(imx708)) + if (fse->code != imx708_get_format_code(imx708) || + fse->index >= num_modes) return -EINVAL; fse->min_width = mode_list[fse->index].width; @@ -1296,76 +1286,56 @@ static int imx708_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = mode_list[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + if (fse->code != fmt->code) return -EINVAL; - fse->min_width = IMX708_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX708_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; + if (fse->index) + return -EINVAL; + + fse->min_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->max_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->min_height = IMX708_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX708_EMBEDDED_DATA_HEIGHT; } return 0; } -static void imx708_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +static int imx708_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) { - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; -static void imx708_update_image_pad_format(struct imx708 *imx708, - const struct imx708_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx708_reset_colorspace(&fmt->format); -} + if (pad != IMX708_PAD_SOURCE) + return -EINVAL; -static void imx708_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX708_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX708_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); -static int imx708_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx708 *imx708 = to_imx708(sd); + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + memset(fd->entry, 0, sizeof(fd->entry)); - mutex_lock(&imx708->mutex); + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX708_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = MIPI_CSI2_DT_RAW10; - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, fmt->pad); - /* update the code which could change due to vflip or hflip */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx708_get_format_code(imx708) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx708_update_image_pad_format(imx708, imx708->mode, - fmt); - fmt->format.code = imx708_get_format_code(imx708); - } else { - imx708_update_metadata_pad_format(fmt); - } - } + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX708_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; - mutex_unlock(&imx708->mutex); return 0; } @@ -1373,100 +1343,214 @@ static int imx708_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx708_mode *mode; struct imx708 *imx708 = to_imx708(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx708_mode *mode_list; + const struct imx708_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + unsigned int num_modes; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX708_PAD_SOURCE || fmt->stream != IMX708_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx708->mutex); + get_mode_table(fmt->format.code, &mode_list, &num_modes, + imx708->hdr_mode->val); - if (fmt->pad == IMAGE_PAD) { - const struct imx708_mode *mode_list; - unsigned int num_modes; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); - /* Bayer order varies with flips */ - fmt->format.code = imx708_get_format_code(imx708); + fmt->format.code = imx708_get_format_code(imx708); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_IMAGE); + *format = fmt->format; + format->code = IMX708_NATIVE_FORMAT; + format->width = IMX708_NATIVE_WIDTH; + format->height = IMX708_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX708_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX708_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; - get_mode_table(fmt->format.code, &mode_list, &num_modes, - imx708->hdr_mode->val); + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; - mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - fmt->format.width, - fmt->format.height); - imx708_update_image_pad_format(imx708, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - imx708->mode = mode; - imx708_set_framing_limits(imx708); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx708_update_metadata_pad_format(fmt); - } - } + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + *format = fmt->format; - mutex_unlock(&imx708->mutex); + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = IMX708_EMBEDDED_DATA_WIDTH; + ed_format->height = IMX708_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + *format = *ed_format; + format->code = MEDIA_BUS_FMT_META_10; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx708->mode = mode; + imx708_set_framing_limits(imx708); + } return 0; } -static const struct v4l2_rect * -__imx708_get_pad_crop(struct imx708 *imx708, struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx708_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx708->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX708_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX708_PAD_SOURCE, + .source_stream = IMX708_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX708_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX708_PAD_SOURCE, + .source_stream = IMX708_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX708_PAD_SOURCE, + .stream = IMX708_STREAM_IMAGE, + .format = { + .code = IMX708_NATIVE_FORMAT, + .width = supported_modes_10bit_no_hdr[0].width, + .height = supported_modes_10bit_no_hdr[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; - return NULL; + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx708_set_pad_format(sd, state, &fmt); + + return 0; } static int imx708_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx708 *imx708 = to_imx708(sd); + struct v4l2_rect *compose; - mutex_lock(&imx708->mutex); - sel->r = *__imx708_get_pad_crop(imx708, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx708->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX708_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX708_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX708_NATIVE_WIDTH; sel->r.height = IMX708_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX708_PIXEL_ARRAY_LEFT; - sel->r.top = IMX708_PIXEL_ARRAY_TOP; - sel->r.width = IMX708_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX708_PAD_IMAGE: + sel->r.top = IMX708_PIXEL_ARRAY_TOP; + sel->r.left = IMX708_PIXEL_ARRAY_LEFT; + sel->r.width = IMX708_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX708_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX708_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX708_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1481,13 +1565,17 @@ static int imx708_start_streaming(struct imx708 *imx708) int i, ret; u32 val; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + if (!imx708->common_regs_written) { ret = imx708_write_regs(imx708, mode_common_regs, ARRAY_SIZE(mode_common_regs)); if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L, @@ -1509,9 +1597,8 @@ static int imx708_start_streaming(struct imx708 *imx708) if (ret) { dev_err(&client->dev, "%s failed to set PDAF gains\n", __func__); - return ret; + goto error_pm_put; } - imx708->common_regs_written = true; } @@ -1520,9 +1607,8 @@ static int imx708_start_streaming(struct imx708 *imx708) ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } - /* Update the link frequency registers */ freq_regs = &link_freq_regs[imx708->link_freq_idx]; ret = imx708_write_regs(imx708, freq_regs->regs, @@ -1530,7 +1616,7 @@ static int imx708_start_streaming(struct imx708 *imx708) if (ret) { dev_err(&client->dev, "%s failed to set link frequency registers\n", __func__); - return ret; + goto error_pm_put; } /* Quad Bayer re-mosaic adjustments (for full-resolution mode only) */ @@ -1547,15 +1633,21 @@ static int imx708_start_streaming(struct imx708 *imx708) IMX708_REG_VALUE_08BIT, IMX708_LPF_INTENSITY_DISABLED); } - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler); if (ret) - return ret; - + goto error_pm_put; /* set stream on register */ - return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, - IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1569,54 +1661,33 @@ static void imx708_stop_streaming(struct imx708 *imx708) IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); + + pm_runtime_put(&client->dev); } static int imx708_set_stream(struct v4l2_subdev *sd, int enable) { struct imx708 *imx708 = to_imx708(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx708->mutex); - if (imx708->streaming == enable) { - mutex_unlock(&imx708->mutex); - return 0; - } + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } - /* * Apply default & customized values * and then start streaming. */ ret = imx708_start_streaming(imx708); - if (ret) - goto err_rpm_put; - } else { + } else imx708_stop_streaming(imx708); - pm_runtime_put(&client->dev); - } - - imx708->streaming = enable; /* vflip/hflip and hdr mode cannot change during streaming */ __v4l2_ctrl_grab(imx708->vflip, enable); __v4l2_ctrl_grab(imx708->hflip, enable); __v4l2_ctrl_grab(imx708->hdr_mode, enable); - mutex_unlock(&imx708->mutex); - - return ret; - -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx708->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1673,39 +1744,6 @@ static int imx708_power_off(struct device *dev) return 0; } -static int __maybe_unused imx708_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - - if (imx708->streaming) - imx708_stop_streaming(imx708); - - return 0; -} - -static int __maybe_unused imx708_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - int ret; - - if (imx708->streaming) { - ret = imx708_start_streaming(imx708); - if (ret) - goto error; - } - - return 0; - -error: - imx708_stop_streaming(imx708); - imx708->streaming = 0; - return ret; -} - static int imx708_get_regulators(struct imx708 *imx708) { struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); @@ -1762,10 +1800,11 @@ static const struct v4l2_subdev_video_ops imx708_video_ops = { static const struct v4l2_subdev_pad_ops imx708_pad_ops = { .enum_mbus_code = imx708_enum_mbus_code, - .get_fmt = imx708_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx708_set_pad_format, .get_selection = imx708_get_selection, .enum_frame_size = imx708_enum_frame_size, + .get_frame_desc = imx708_get_frame_desc, }; static const struct v4l2_subdev_ops imx708_subdev_ops = { @@ -1775,7 +1814,7 @@ static const struct v4l2_subdev_ops imx708_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx708_internal_ops = { - .open = imx708_open, + .init_state = imx708_init_state, }; static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = { @@ -1805,9 +1844,6 @@ static int imx708_init_controls(struct imx708 *imx708) if (ret) return ret; - mutex_init(&imx708->mutex); - ctrl_hdlr->lock = &imx708->mutex; - /* By default, PIXEL_RATE is read only */ imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1823,7 +1859,7 @@ static int imx708_init_controls(struct imx708 *imx708) /* * Create the controls here, but mode specific limits are setup - * in the imx708_set_framing_limits() call below. + * in the imx708_set_framing_limits() call. */ imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -1898,14 +1934,10 @@ static int imx708_init_controls(struct imx708 *imx708) imx708->sd.ctrl_handler = ctrl_hdlr; - /* Setup exposure and frame/line length limits. */ - imx708_set_framing_limits(imx708); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx708->mutex); return ret; } @@ -1913,7 +1945,6 @@ static int imx708_init_controls(struct imx708 *imx708) static void imx708_free_controls(struct imx708 *imx708) { v4l2_ctrl_handler_free(imx708->sd.ctrl_handler); - mutex_destroy(&imx708->mutex); } static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) @@ -1982,6 +2013,7 @@ static int imx708_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx708->sd, client, &imx708_subdev_ops); + imx708->sd.internal_ops = &imx708_internal_ops; /* Check the hardware configuration in device tree */ if (imx708_check_hwcfg(dev, imx708)) @@ -2019,53 +2051,61 @@ static int imx708_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Initialize default format */ - imx708_set_default_format(imx708); - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - /* This needs the pm runtime to be registered. */ ret = imx708_init_controls(imx708); if (ret) - goto error_pm_runtime; + goto error_power_off; /* Initialize subdev */ imx708->sd.internal_ops = &imx708_internal_ops; imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STREAMS; imx708->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pads */ - imx708->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx708->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx708->sd.entity, NUM_PADS, imx708->pad); + imx708->pads[IMX708_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx708->pads[IMX708_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx708->pads[IMX708_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx708->sd.entity, + ARRAY_SIZE(imx708->pads), imx708->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx708->sd.state_lock = imx708->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx708->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx708->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx708->sd); + error_media_entity: media_entity_cleanup(&imx708->sd.entity); error_handler_free: imx708_free_controls(imx708); -error_pm_runtime: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - error_power_off: imx708_power_off(&client->dev); @@ -2094,7 +2134,6 @@ static const struct of_device_id imx708_dt_ids[] = { MODULE_DEVICE_TABLE(of, imx708_dt_ids); static const struct dev_pm_ops imx708_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume) SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL) }; From daaf01733495d538dca58b22784048b67ea01de7 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 25 Oct 2024 13:22:07 +0530 Subject: [PATCH 151/159] media: i2c: imx708: Switch to .{enable,disable}_streams() operations The .s_stream() operation is not compatible with streams, so switch to the .{enable,disable}_streams() operations. Signed-off-by: Jai Luthra --- drivers/media/i2c/imx708.c | 59 +++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c index 5ed1d05cc1a639..5cdc84dbdfa527 100644 --- a/drivers/media/i2c/imx708.c +++ b/drivers/media/i2c/imx708.c @@ -1665,31 +1665,47 @@ static void imx708_stop_streaming(struct imx708 *imx708) pm_runtime_put(&client->dev); } -static int imx708_set_stream(struct v4l2_subdev *sd, int enable) +static int imx708_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx708 *imx708 = to_imx708(sd); - struct v4l2_subdev_state *state; - int ret = 0; - state = v4l2_subdev_lock_and_get_active_state(sd); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX708_STREAM_IMAGE))) + return 0; - if (enable) { - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx708_start_streaming(imx708); - } else - imx708_stop_streaming(imx708); + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx708->vflip, true); + __v4l2_ctrl_grab(imx708->hflip, true); + __v4l2_ctrl_grab(imx708->hdr_mode, true); - /* vflip/hflip and hdr mode cannot change during streaming */ - __v4l2_ctrl_grab(imx708->vflip, enable); - __v4l2_ctrl_grab(imx708->hflip, enable); - __v4l2_ctrl_grab(imx708->hdr_mode, enable); + return imx708_start_streaming(imx708); +} - v4l2_subdev_unlock_state(state); +static int imx708_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx708 *imx708 = to_imx708(sd); - return ret; + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX708_STREAM_IMAGE))) + return 0; + + __v4l2_ctrl_grab(imx708->vflip, false); + __v4l2_ctrl_grab(imx708->hflip, false); + __v4l2_ctrl_grab(imx708->hdr_mode, false); + + imx708_stop_streaming(imx708); + + return 0; } /* Power/clock management functions */ @@ -1794,10 +1810,6 @@ static const struct v4l2_subdev_core_ops imx708_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx708_video_ops = { - .s_stream = imx708_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx708_pad_ops = { .enum_mbus_code = imx708_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, @@ -1805,11 +1817,12 @@ static const struct v4l2_subdev_pad_ops imx708_pad_ops = { .get_selection = imx708_get_selection, .enum_frame_size = imx708_enum_frame_size, .get_frame_desc = imx708_get_frame_desc, + .enable_streams = imx708_enable_streams, + .disable_streams = imx708_disable_streams, }; static const struct v4l2_subdev_ops imx708_subdev_ops = { .core = &imx708_core_ops, - .video = &imx708_video_ops, .pad = &imx708_pad_ops, }; From c41fb1246125c185e5fb1fba20d2b30efb8cc42c Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Thu, 21 Mar 2024 13:13:09 +0000 Subject: [PATCH 152/159] drivers: media: i2c: Add streams support to IMX519 Add the V4L2 streams API support for embedded data in the IMX519 device driver. This also updates the drier to use the V4L2 subdev state API. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- drivers/media/i2c/imx519.c | 641 +++++++++++++++++++++---------------- 1 file changed, 359 insertions(+), 282 deletions(-) diff --git a/drivers/media/i2c/imx519.c b/drivers/media/i2c/imx519.c index 0f5661ff7e967a..66f597a454eea2 100644 --- a/drivers/media/i2c/imx519.c +++ b/drivers/media/i2c/imx519.c @@ -2,8 +2,9 @@ /* * A V4L2 driver for Sony IMX519 cameras. * Copyright (C) 2021 Arducam Technology co., Ltd. + * Copyright (C) 2024 Raspberry Pi Ltd * - * Based on Sony IMX477 camera driver + * Based on Sony IMX519 camera driver * Copyright (C) 2020 Raspberry Pi (Trading) Ltd */ #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +68,7 @@ #define IMX519_ANA_GAIN_DEFAULT 0x0 /* Digital gain control */ -#define IMX519_REG_DIGITAL_GAIN 0x020e +#define IMX519_REG_DIGITAL_GAIN 0x020ef #define IMX519_DGTL_GAIN_MIN 0x0100 #define IMX519_DGTL_GAIN_MAX 0xffff #define IMX519_DGTL_GAIN_DEFAULT 0x0100 @@ -93,17 +95,11 @@ #define IMX519_TEST_PATTERN_B_DEFAULT 0 #define IMX519_TEST_PATTERN_GB_DEFAULT 0 -/* Embedded metadata stream structure */ -#define IMX519_EMBEDDED_LINE_WIDTH (5820 * 3) -#define IMX519_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; +/* Embedded metadata stream height */ +#define IMX519_EMBEDDED_DATA_HEIGHT 3U /* IMX519 native and active pixel array size. */ +#define IMX519_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX519_NATIVE_WIDTH 4672U #define IMX519_NATIVE_HEIGHT 3648U #define IMX519_PIXEL_ARRAY_LEFT 8U @@ -1024,11 +1020,22 @@ static const char * const imx519_supply_name[] = { #define IMX519_XCLR_MIN_DELAY_US 8000 #define IMX519_XCLR_DELAY_RANGE_US 1000 + +enum imx519_pad_ids { + IMX519_PAD_SOURCE, + IMX519_PAD_IMAGE, + IMX519_PAD_EDATA, + IMX519_NUM_PADS, +}; + +enum imx519_stream_ids { + IMX519_STREAM_IMAGE, + IMX519_STREAM_EDATA, +}; + struct imx519 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - unsigned int fmt_code; + struct media_pad pads[IMX519_NUM_PADS]; struct clk *xclk; @@ -1047,15 +1054,6 @@ struct imx519 { /* Current mode */ const struct imx519_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1145,49 +1143,12 @@ static u32 imx519_get_format_code(struct imx519 *imx519) { unsigned int i; - lockdep_assert_held(&imx519->mutex); - i = (imx519->vflip->val ? 2 : 0) | (imx519->hflip->val ? 1 : 0); return codes[i]; } -static int imx519_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx519 *imx519 = to_imx519(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_state_get_format(fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx519->mutex); - - /* Initialize try_fmt for the image pad */ - try_fmt_img->width = supported_modes_10bit[0].width; - try_fmt_img->height = supported_modes_10bit[0].height; - try_fmt_img->code = imx519_get_format_code(imx519); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX519_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX519_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); - try_crop->left = IMX519_PIXEL_ARRAY_LEFT; - try_crop->top = IMX519_PIXEL_ARRAY_TOP; - try_crop->width = IMX519_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX519_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx519->mutex); - - return 0; -} - static void imx519_adjust_exposure_range(struct imx519 *imx519) { int exposure_max, exposure_def; @@ -1226,8 +1187,11 @@ static int imx519_set_ctrl(struct v4l2_ctrl *ctrl) struct imx519 *imx519 = container_of(ctrl->handler, struct imx519, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd); + struct v4l2_subdev_state *state; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx519->sd); + /* * The VBLANK control may change the limits of usable exposure, so check * and adjust if necessary. @@ -1304,25 +1268,59 @@ static const struct v4l2_ctrl_ops imx519_ctrl_ops = { .s_ctrl = imx519_set_ctrl, }; + static int imx519_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx519 *imx519 = to_imx519(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX519_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { + switch (code->pad) { + case IMX519_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX519_NATIVE_FORMAT; + return 0; + + case IMX519_PAD_EDATA: if (code->index > 0) return -EINVAL; + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX519_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX519_STREAM_IMAGE) { + u32 format; + + if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; + + format = codes[code->index * 4]; code->code = imx519_get_format_code(imx519); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1334,14 +1332,35 @@ static int imx519_enum_frame_size(struct v4l2_subdev *sd, { struct imx519 *imx519 = to_imx519(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX519_PAD_IMAGE: + if (fse->code != IMX519_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX519_NATIVE_WIDTH; + fse->max_width = IMX519_NATIVE_WIDTH; + fse->min_height = IMX519_NATIVE_HEIGHT; + fse->max_height = IMX519_NATIVE_HEIGHT; + return 0; - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes_10bit)) + case IMX519_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) return -EINVAL; - if (fse->code != imx519_get_format_code(imx519)) + fse->min_width = IMX519_NATIVE_WIDTH; + fse->max_width = IMX519_NATIVE_WIDTH; + fse->min_height = IMX519_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX519_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX519_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX519_STREAM_IMAGE) { + if (fse->code != imx519_get_format_code(imx519) || + fse->index >= ARRAY_SIZE(supported_modes_10bit)) return -EINVAL; fse->min_width = supported_modes_10bit[fse->index].width; @@ -1349,77 +1368,56 @@ static int imx519_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = supported_modes_10bit[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + if (fse->code != fmt->code) return -EINVAL; - fse->min_width = IMX519_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX519_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; + if (fse->index) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX519_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX519_EMBEDDED_DATA_HEIGHT; } return 0; } -static void imx519_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +static int imx519_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) { - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; -static void imx519_update_image_pad_format(struct imx519 *imx519, - const struct imx519_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx519_reset_colorspace(&fmt->format); -} + if (pad != IMX519_PAD_SOURCE) + return -EINVAL; -static void imx519_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX519_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX519_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); -static int imx519_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx519 *imx519 = to_imx519(sd); + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + memset(fd->entry, 0, sizeof(fd->entry)); - mutex_lock(&imx519->mutex); + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX519_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = MIPI_CSI2_DT_RAW10; - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(sd_state, fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx519_get_format_code(imx519) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx519_update_image_pad_format(imx519, imx519->mode, - fmt); - fmt->format.code = - imx519_get_format_code(imx519); - } else { - imx519_update_metadata_pad_format(fmt); - } - } + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX519_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; - mutex_unlock(&imx519->mutex); return 0; } @@ -1469,99 +1467,217 @@ static void imx519_set_framing_limits(struct imx519 *imx519) __v4l2_ctrl_modify_range(imx519->hblank, hblank, hblank, 1, hblank); } + static int imx519_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx519_mode *mode; struct imx519 *imx519 = to_imx519(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx519_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX519_PAD_SOURCE || fmt->stream != IMX519_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx519->mutex); - - if (fmt->pad == IMAGE_PAD) { - /* Bayer order varies with flips */ - fmt->format.code = imx519_get_format_code(imx519); - - mode = v4l2_find_nearest_size(supported_modes_10bit, - ARRAY_SIZE(supported_modes_10bit), - width, height, - fmt->format.width, - fmt->format.height); - imx519_update_image_pad_format(imx519, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - imx519->mode = mode; - imx519->fmt_code = fmt->format.code; - imx519_set_framing_limits(imx519); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx519_update_metadata_pad_format(fmt); - } - } - mutex_unlock(&imx519->mutex); + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(supported_modes_10bit, + ARRAY_SIZE(supported_modes_10bit), + width, height, fmt->format.width, + fmt->format.height); + + fmt->format.code = imx519_get_format_code(imx519); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_IMAGE); + *format = fmt->format; + format->code = IMX519_NATIVE_FORMAT; + format->width = IMX519_NATIVE_WIDTH; + format->height = IMX519_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX519_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX519_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; + + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + *format = fmt->format; + + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX519_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + *format = *ed_format; + format->code = MEDIA_BUS_FMT_META_10; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx519->mode = mode; + imx519_set_framing_limits(imx519); + } return 0; } -static const struct v4l2_rect * -__imx519_get_pad_crop(struct imx519 *imx519, struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx519_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx519->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX519_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX519_PAD_SOURCE, + .source_stream = IMX519_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX519_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX519_PAD_SOURCE, + .source_stream = IMX519_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX519_PAD_SOURCE, + .stream = IMX519_STREAM_IMAGE, + .format = { + .code = IMX519_NATIVE_FORMAT, + .width = supported_modes_10bit[0].width, + .height = supported_modes_10bit[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; - return NULL; + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; + + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx519_set_pad_format(sd, state, &fmt); + + return 0; } static int imx519_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx519 *imx519 = to_imx519(sd); + struct v4l2_rect *compose; - mutex_lock(&imx519->mutex); - sel->r = *__imx519_get_pad_crop(imx519, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx519->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX519_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX519_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX519_NATIVE_WIDTH; sel->r.height = IMX519_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX519_PIXEL_ARRAY_LEFT; - sel->r.top = IMX519_PIXEL_ARRAY_TOP; - sel->r.width = IMX519_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX519_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX519_PAD_IMAGE: + sel->r.top = IMX519_PIXEL_ARRAY_TOP; + sel->r.left = IMX519_PIXEL_ARRAY_LEFT; + sel->r.width = IMX519_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX519_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX519_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX519_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + + break; + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX519_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1575,6 +1691,10 @@ static int imx519_start_streaming(struct imx519 *imx519) const struct imx519_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto error_pm_put; + if (!imx519->common_regs_written) { ret = imx519_write_regs(imx519, mode_common_regs, ARRAY_SIZE(mode_common_regs)); @@ -1582,7 +1702,7 @@ static int imx519_start_streaming(struct imx519 *imx519) if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } imx519->common_regs_written = true; } @@ -1592,17 +1712,25 @@ static int imx519_start_streaming(struct imx519 *imx519) ret = imx519_write_regs(imx519, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx519->sd.ctrl_handler); if (ret) - return ret; + goto error_pm_put; /* set stream on register */ - return imx519_write_reg(imx519, IMX519_REG_MODE_SELECT, - IMX519_REG_VALUE_08BIT, IMX519_MODE_STREAMING); + ret = imx519_write_reg(imx519, IMX519_REG_MODE_SELECT, + IMX519_REG_VALUE_08BIT, IMX519_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1616,53 +1744,28 @@ static void imx519_stop_streaming(struct imx519 *imx519) IMX519_REG_VALUE_08BIT, IMX519_MODE_STANDBY); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); + + pm_runtime_put(&client->dev); } static int imx519_set_stream(struct v4l2_subdev *sd, int enable) { struct imx519 *imx519 = to_imx519(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx519->mutex); - if (imx519->streaming == enable) { - mutex_unlock(&imx519->mutex); - return 0; - } - - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } + state = v4l2_subdev_lock_and_get_active_state(sd); - /* - * Apply default & customized values - * and then start streaming. - */ + if (enable) ret = imx519_start_streaming(imx519); - if (ret) - goto err_rpm_put; - } else { + else imx519_stop_streaming(imx519); - pm_runtime_put(&client->dev); - } - - imx519->streaming = enable; /* vflip and hflip cannot change during streaming */ __v4l2_ctrl_grab(imx519->vflip, enable); __v4l2_ctrl_grab(imx519->hflip, enable); - mutex_unlock(&imx519->mutex); - - return ret; - -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx519->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1717,39 +1820,6 @@ static int imx519_power_off(struct device *dev) return 0; } -static int __maybe_unused imx519_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx519 *imx519 = to_imx519(sd); - - if (imx519->streaming) - imx519_stop_streaming(imx519); - - return 0; -} - -static int __maybe_unused imx519_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx519 *imx519 = to_imx519(sd); - int ret; - - if (imx519->streaming) { - ret = imx519_start_streaming(imx519); - if (ret) - goto error; - } - - return 0; - -error: - imx519_stop_streaming(imx519); - imx519->streaming = 0; - return ret; -} - static int imx519_get_regulators(struct imx519 *imx519) { struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd); @@ -1800,10 +1870,11 @@ static const struct v4l2_subdev_video_ops imx519_video_ops = { static const struct v4l2_subdev_pad_ops imx519_pad_ops = { .enum_mbus_code = imx519_enum_mbus_code, - .get_fmt = imx519_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx519_set_pad_format, .get_selection = imx519_get_selection, .enum_frame_size = imx519_enum_frame_size, + .get_frame_desc = imx519_get_frame_desc, }; static const struct v4l2_subdev_ops imx519_subdev_ops = { @@ -1813,7 +1884,7 @@ static const struct v4l2_subdev_ops imx519_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx519_internal_ops = { - .open = imx519_open, + .init_state = imx519_init_state, }; /* Initialize control handlers */ @@ -1831,9 +1902,6 @@ static int imx519_init_controls(struct imx519 *imx519) if (ret) return ret; - mutex_init(&imx519->mutex); - ctrl_hdlr->lock = &imx519->mutex; - /* By default, PIXEL_RATE is read only */ imx519->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx519_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1852,7 +1920,7 @@ static int imx519_init_controls(struct imx519 *imx519) /* * Create the controls here, but mode specific limits are setup - * in the imx519_set_framing_limits() call below. + * in the imx519_set_framing_limits() call. */ imx519->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx519_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -1926,14 +1994,10 @@ static int imx519_init_controls(struct imx519 *imx519) imx519->sd.ctrl_handler = ctrl_hdlr; - /* Setup exposure and frame/line length limits. */ - imx519_set_framing_limits(imx519); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx519->mutex); return ret; } @@ -1941,7 +2005,6 @@ static int imx519_init_controls(struct imx519 *imx519) static void imx519_free_controls(struct imx519 *imx519) { v4l2_ctrl_handler_free(imx519->sd.ctrl_handler); - mutex_destroy(&imx519->mutex); } static int imx519_check_hwcfg(struct device *dev) @@ -2009,6 +2072,7 @@ static int imx519_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx519->sd, client, &imx519_subdev_ops); + imx519->sd.internal_ops = &imx519_internal_ops; match = of_match_device(imx519_dt_ids, dev); if (!match) @@ -2054,14 +2118,6 @@ static int imx519_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Set default mode to max resolution */ - imx519->mode = &supported_modes_10bit[0]; - imx519->fmt_code = MEDIA_BUS_FMT_SRGGB10_1X10; - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); /* This needs the pm runtime to be registered. */ ret = imx519_init_controls(imx519); @@ -2071,27 +2127,51 @@ static int imx519_probe(struct i2c_client *client) /* Initialize subdev */ imx519->sd.internal_ops = &imx519_internal_ops; imx519->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; imx519->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pads */ - imx519->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx519->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx519->sd.entity, NUM_PADS, imx519->pad); + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. + */ + imx519->pads[IMX519_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx519->pads[IMX519_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx519->pads[IMX519_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx519->sd.entity, + ARRAY_SIZE(imx519->pads), imx519->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx519->sd.state_lock = imx519->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx519->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx519->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx519->sd); + error_media_entity: media_entity_cleanup(&imx519->sd.entity); @@ -2099,8 +2179,6 @@ static int imx519_probe(struct i2c_client *client) imx519_free_controls(imx519); error_power_off: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); imx519_power_off(&client->dev); return ret; @@ -2124,7 +2202,6 @@ static void imx519_remove(struct i2c_client *client) MODULE_DEVICE_TABLE(of, imx519_dt_ids); static const struct dev_pm_ops imx519_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx519_suspend, imx519_resume) SET_RUNTIME_PM_OPS(imx519_power_off, imx519_power_on, NULL) }; From d043f5948bdb74ae6c3a662772b6e244d20ea712 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 25 Oct 2024 13:08:21 +0530 Subject: [PATCH 153/159] media: i2c: imx519: Switch to .{enable,disable}_streams() operations The .s_stream() operation is not compatible with streams, so switch to the .{enable,disable}_streams() operations. Signed-off-by: Jai Luthra --- drivers/media/i2c/imx519.c | 52 +++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/imx519.c b/drivers/media/i2c/imx519.c index 66f597a454eea2..5e93c26575184d 100644 --- a/drivers/media/i2c/imx519.c +++ b/drivers/media/i2c/imx519.c @@ -1748,26 +1748,45 @@ static void imx519_stop_streaming(struct imx519 *imx519) pm_runtime_put(&client->dev); } -static int imx519_set_stream(struct v4l2_subdev *sd, int enable) +static int imx519_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx519 *imx519 = to_imx519(sd); - struct v4l2_subdev_state *state; - int ret = 0; - - state = v4l2_subdev_lock_and_get_active_state(sd); - if (enable) - ret = imx519_start_streaming(imx519); - else - imx519_stop_streaming(imx519); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX519_STREAM_IMAGE))) + return 0; /* vflip and hflip cannot change during streaming */ - __v4l2_ctrl_grab(imx519->vflip, enable); - __v4l2_ctrl_grab(imx519->hflip, enable); + __v4l2_ctrl_grab(imx519->vflip, true); + __v4l2_ctrl_grab(imx519->hflip, true); - v4l2_subdev_unlock_state(state); + return imx519_start_streaming(imx519); +} - return ret; +static int imx519_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx519 *imx519 = to_imx519(sd); + + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX519_STREAM_IMAGE))) + return 0; + + __v4l2_ctrl_grab(imx519->vflip, false); + __v4l2_ctrl_grab(imx519->hflip, false); + + imx519_stop_streaming(imx519); + + return 0; } /* Power/clock management functions */ @@ -1864,10 +1883,6 @@ static const struct v4l2_subdev_core_ops imx519_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx519_video_ops = { - .s_stream = imx519_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx519_pad_ops = { .enum_mbus_code = imx519_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, @@ -1875,11 +1890,12 @@ static const struct v4l2_subdev_pad_ops imx519_pad_ops = { .get_selection = imx519_get_selection, .enum_frame_size = imx519_enum_frame_size, .get_frame_desc = imx519_get_frame_desc, + .enable_streams = imx519_enable_streams, + .disable_streams = imx519_disable_streams, }; static const struct v4l2_subdev_ops imx519_subdev_ops = { .core = &imx519_core_ops, - .video = &imx519_video_ops, .pad = &imx519_pad_ops, }; From 4258f1424830628519a416c1995d3c9626a17dce Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Thu, 21 Mar 2024 13:34:47 +0000 Subject: [PATCH 154/159] drivers: media: arducam_64mp: Remove legacy embedded data support With the introduction of the streams API, the legacy embedded data support has been removed. Pull out all code associated with the legacy support. Note that this driver has not been converted to use the streams API, so embedded data will be unavailable. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- drivers/media/i2c/arducam_64mp.c | 145 +++++++++---------------------- 1 file changed, 40 insertions(+), 105 deletions(-) diff --git a/drivers/media/i2c/arducam_64mp.c b/drivers/media/i2c/arducam_64mp.c index 2a38df8bd35e00..651d2b04c9c1ad 100644 --- a/drivers/media/i2c/arducam_64mp.c +++ b/drivers/media/i2c/arducam_64mp.c @@ -94,16 +94,6 @@ #define ARDUCAM_64MP_TEST_PATTERN_B_DEFAULT 0 #define ARDUCAM_64MP_TEST_PATTERN_GB_DEFAULT 0 -/* Embedded metadata stream structure */ -#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (11560 * 3) -#define ARDUCAM_64MP_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* ARDUCAM_64MP native and active pixel array size. */ #define ARDUCAM_64MP_NATIVE_WIDTH 9344U #define ARDUCAM_64MP_NATIVE_HEIGHT 7032U @@ -1422,7 +1412,7 @@ static const char * const arducam_64mp_supply_name[] = { struct arducam_64mp { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; + struct media_pad pad; unsigned int fmt_code; @@ -1555,9 +1545,7 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_state_get_format(fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; mutex_lock(&arducam_64mp->mutex); @@ -1568,14 +1556,8 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_img->code = arducam_64mp_get_format_code(arducam_64mp); try_fmt_img->field = V4L2_FIELD_NONE; - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - /* Initialize try_crop */ - try_crop = v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT; try_crop->top = ARDUCAM_64MP_PIXEL_ARRAY_TOP; try_crop->width = ARDUCAM_64MP_PIXEL_ARRAY_WIDTH; @@ -1731,20 +1713,13 @@ static int arducam_64mp_enum_mbus_code(struct v4l2_subdev *sd, { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= 1) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index > 0) - return -EINVAL; + if (code->index > 0) + return -EINVAL; - code->code = arducam_64mp_get_format_code(arducam_64mp); - } else { - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SENSOR_DATA; - } + code->code = arducam_64mp_get_format_code(arducam_64mp); return 0; } @@ -1755,29 +1730,19 @@ static int arducam_64mp_enum_frame_size(struct v4l2_subdev *sd, { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fse->pad >= NUM_PADS) + if (fse->pad >= 1) return -EINVAL; - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - if (fse->code != arducam_64mp_get_format_code(arducam_64mp)) - return -EINVAL; + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; + if (fse->code != arducam_64mp_get_format_code(arducam_64mp)) + return -EINVAL; - fse->min_width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; return 0; } @@ -1803,22 +1768,13 @@ arducam_64mp_update_image_pad_format(struct arducam_64mp *arducam_64mp, arducam_64mp_reset_colorspace(&fmt->format); } -static void -arducam_64mp_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - fmt->format.height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} - static int arducam_64mp_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fmt->pad >= NUM_PADS) + if (fmt->pad >= 1) return -EINVAL; mutex_lock(&arducam_64mp->mutex); @@ -1827,20 +1783,13 @@ static int arducam_64mp_get_pad_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - arducam_64mp_get_format_code(arducam_64mp) : - MEDIA_BUS_FMT_SENSOR_DATA; + try_fmt->code = arducam_64mp_get_format_code(arducam_64mp); fmt->format = *try_fmt; } else { - if (fmt->pad == IMAGE_PAD) { - arducam_64mp_update_image_pad_format(arducam_64mp, - arducam_64mp->mode, - fmt); - fmt->format.code = - arducam_64mp_get_format_code(arducam_64mp); - } else { - arducam_64mp_update_metadata_pad_format(fmt); - } + arducam_64mp_update_image_pad_format(arducam_64mp, + arducam_64mp->mode, fmt); + fmt->format.code = + arducam_64mp_get_format_code(arducam_64mp); } mutex_unlock(&arducam_64mp->mutex); @@ -1908,39 +1857,26 @@ static int arducam_64mp_set_pad_format(struct v4l2_subdev *sd, const struct arducam_64mp_mode *mode; struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fmt->pad >= NUM_PADS) + if (fmt->pad >= 1) return -EINVAL; mutex_lock(&arducam_64mp->mutex); - if (fmt->pad == IMAGE_PAD) { - /* Bayer order varies with flips */ - fmt->format.code = arducam_64mp_get_format_code(arducam_64mp); - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, - fmt->format.height); - arducam_64mp_update_image_pad_format(arducam_64mp, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - arducam_64mp->mode = mode; - arducam_64mp->fmt_code = fmt->format.code; - arducam_64mp_set_framing_limits(arducam_64mp); - } + /* Bayer order varies with flips */ + fmt->format.code = arducam_64mp_get_format_code(arducam_64mp); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + arducam_64mp_update_image_pad_format(arducam_64mp, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + *framefmt = fmt->format; } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - arducam_64mp_update_metadata_pad_format(fmt); - } + arducam_64mp->mode = mode; + arducam_64mp->fmt_code = fmt->format.code; + arducam_64mp_set_framing_limits(arducam_64mp); } mutex_unlock(&arducam_64mp->mutex); @@ -2545,11 +2481,10 @@ static int arducam_64mp_probe(struct i2c_client *client) arducam_64mp->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pads */ - arducam_64mp->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - arducam_64mp->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + arducam_64mp->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&arducam_64mp->sd.entity, NUM_PADS, - arducam_64mp->pad); + ret = media_entity_pads_init(&arducam_64mp->sd.entity, 1, + &arducam_64mp->pad); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; From 1f57232903fcff83820196011b3ba3731bfa0050 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 13 Sep 2023 00:42:11 +0300 Subject: [PATCH 155/159] media: uapi: v4l: subdev: Enable streams API Remove v4l2_subdev_enable_streams_api variable that was used to easily enable streams API for development, and conditions that use the variable. This patch enables the streams API for V4L2 sub-device interface which allows transporting multiple streams on a single MC link. Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-subdev.c | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 44332220c632dd..06adf9f82e38db 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -26,15 +26,6 @@ #include #include -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -/* - * The Streams API is an experimental feature. To use the Streams API, set - * 'v4l2_subdev_enable_streams_api' to 1 below. - */ - -static bool v4l2_subdev_enable_streams_api; -#endif - /* * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set * of streams. @@ -565,13 +556,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, V4L2_SUBDEV_CLIENT_CAP_STREAMS; int rval; - /* - * If the streams API is not enabled, remove V4L2_SUBDEV_CAP_STREAMS. - * Remove this when the API is no longer experimental. - */ - if (!v4l2_subdev_enable_streams_api) - streams_subdev = false; - switch (cmd) { case VIDIOC_SUBDEV_QUERYCAP: { struct v4l2_subdev_capability *cap = arg; @@ -913,9 +897,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_krouting krouting = {}; unsigned int i; - if (!v4l2_subdev_enable_streams_api) - return -ENOIOCTLCMD; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; @@ -986,9 +967,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_routing *routing = arg; struct v4l2_subdev_krouting *krouting; - if (!v4l2_subdev_enable_streams_api) - return -ENOIOCTLCMD; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; @@ -1016,14 +994,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_S_CLIENT_CAP: { struct v4l2_subdev_client_capability *client_cap = arg; - /* - * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not - * enabled. Remove this when streams API is no longer - * experimental. - */ - if (!v4l2_subdev_enable_streams_api) - client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; - /* Filter out unsupported capabilities */ client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; From 360f46f0a08b2aec0ec90fc86b50f8fa595ee374 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 19 Nov 2024 15:25:18 +0000 Subject: [PATCH 156/159] drivers: media: unicam: Allow setting of unpacked formats When matching formats via try_fmt/set_fmt ioctls, test for the unpacked formats as well as packed formats. This allows userland clients setup unpacking to 16-bits from the 10/12/14-packed CSI2 formats. Signed-off-by: Naushir Patuck --- drivers/media/platform/broadcom/bcm2835-unicam.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 1a6a0eacd49d9f..7283fbcc0a258b 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -550,7 +550,8 @@ unicam_find_format_by_fourcc(u32 fourcc, u32 pad) } for (i = 0; i < num_formats; ++i) { - if (formats[i].fourcc == fourcc) + if (formats[i].fourcc == fourcc || + formats[i].unpacked_fourcc == fourcc) return &formats[i]; } From 737b5055b42d2aa3ee5e73bd199eca35b756e9ef Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 19 Nov 2024 15:30:37 +0000 Subject: [PATCH 157/159] drivers: media: unicam: Disable trigger mode operation The imx219/imx708 sensors frequently generate a single corrupt frame (image or embedded data) when the sensor first starts. This can either be a missing line, or invalid samples within the line. This only occurrs using the upstream Unicam kernel driver. Disabling trigger mode elimiates this corruption. Since trigger mode is a legacy feature copied from the firmware driver and not expected to be needed, remove it. Tested on the Raspberry Pi cameras and shows no ill effects. Signed-off-by: Naushir Patuck --- drivers/media/platform/broadcom/bcm2835-unicam.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 7283fbcc0a258b..7f9cdf03fd12d9 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -844,11 +844,6 @@ static irqreturn_t unicam_isr(int irq, void *dev) } } - if (unicam_reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) { - /* Switch out of trigger mode if selected */ - unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); - unicam_reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM); - } return IRQ_HANDLED; } @@ -1012,8 +1007,7 @@ static void unicam_start_rx(struct unicam_device *unicam, unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL); - /* Always start in trigger frame capture mode (UNICAM_FCM set) */ - val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB; + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; line_int_freq = max(fmt->height >> 2, 128); unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK); unicam_reg_write(unicam, UNICAM_ICTL, val); From ca95c84c445477cd2afee99e98be10f182905e62 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 19 Nov 2024 15:33:34 +0000 Subject: [PATCH 158/159] media: bcm2835-unicam: Fix for possible dummy buffer overrun The Unicam hardware has been observed to cause a buffer overrun when using the dummy buffer as a circular buffer. The conditions that cause the overrun are not fully known, but it seems to occur when the memory bus is heavily loaded. To avoid the overrun, program the hardware with a buffer size of 0 when using the dummy buffer. This will cause overrun into the allocated dummy buffer, but avoid out of bounds writes. Signed-off-by: Naushir Patuck --- .../media/platform/broadcom/bcm2835-unicam.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 7f9cdf03fd12d9..0c8e791b2aa03e 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -641,9 +641,9 @@ static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offs } static void unicam_wr_dma_addr(struct unicam_node *node, - struct unicam_buffer *buf) + struct unicam_buffer *buf, unsigned int size) { - dma_addr_t endaddr = buf->dma_addr + buf->size; + dma_addr_t endaddr = buf->dma_addr + size; if (node->id == UNICAM_IMAGE_NODE) { unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr); @@ -676,7 +676,7 @@ static void unicam_schedule_next_buffer(struct unicam_node *node) node->next_frm = buf; list_del(&buf->list); - unicam_wr_dma_addr(node, buf); + unicam_wr_dma_addr(node, buf, buf->size); } static void unicam_schedule_dummy_buffer(struct unicam_node *node) @@ -684,8 +684,13 @@ static void unicam_schedule_dummy_buffer(struct unicam_node *node) int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE; dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id); - - unicam_wr_dma_addr(node, &node->dummy_buf); + /* + * Due to a HW bug causing buffer overruns in circular buffer mode under + * certain (not yet fully known) conditions, the dummy buffer allocation + * is set to a a single page size, but the hardware gets programmed with + * a buffer size of 0. + */ + unicam_wr_dma_addr(node, &node->dummy_buf, 0); node->next_frm = NULL; } @@ -1096,7 +1101,7 @@ static void unicam_start_rx(struct unicam_device *unicam, unicam_reg_write(unicam, UNICAM_IBLS, node->fmt.fmt.pix.bytesperline); - unicam_wr_dma_addr(node, node->cur_frm); + unicam_wr_dma_addr(node, node->cur_frm, node->cur_frm->size); unicam_set_packing_config(unicam, fmtinfo); ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt); @@ -1134,7 +1139,7 @@ static void unicam_start_metadata(struct unicam_device *unicam) struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE]; unicam_enable_ed(unicam); - unicam_wr_dma_addr(node, node->cur_frm); + unicam_wr_dma_addr(node, node->cur_frm, node->cur_frm->size); unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP); } From c48f001bca773a34cf7daa5ea038b7bb3a9b5b82 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 19 Nov 2024 15:36:45 +0000 Subject: [PATCH 159/159] media: bcm2835-unicam: Correctly handle FS + FE ISR condtion This change aligns the FS/FE interrupt handling with the Raspberry Pi kernel downstream Unicam driver. If we get a simultaneous FS + FE interrupt for the same frame, it cannot be marked as completed and returned to userland as the framebuffer will be refilled by Unicam on the next sensor frame. Additionally, the timestamp will be set to 0 as the FS interrupt handling code will not have run yet. To avoid these problems, the frame is considered dropped in the FE handler, and will be returned to userland on the subsequent sensor frame. Signed-off-by: Naushir Patuck --- .../media/platform/broadcom/bcm2835-unicam.c | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 0c8e791b2aa03e..390e3318dd64a6 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -777,10 +777,25 @@ static irqreturn_t unicam_isr(int irq, void *dev) * as complete, as the HW will reuse that buffer. */ if (node->cur_frm && node->cur_frm != node->next_frm) { + /* + * This condition checks if FE + FS for the same + * frame has occurred. In such cases, we cannot + * return out the frame, as no buffer handling + * or timestamping has yet been done as part of + * the FS handler. + */ + if (!node->cur_frm->vb.vb2_buf.timestamp) { + dev_dbg(unicam->v4l2_dev.dev, + "ISR: FE without FS, dropping frame\n"); + continue; + } + unicam_process_buffer_complete(node, sequence); + node->cur_frm = node->next_frm; + node->next_frm = NULL; inc_seq = true; - } - node->cur_frm = node->next_frm; + } else + node->cur_frm = node->next_frm; } /* @@ -820,10 +835,25 @@ static irqreturn_t unicam_isr(int irq, void *dev) i); /* * Set the next frame output to go to a dummy frame - * if we have not managed to obtain another frame - * from the queue. + * if no buffer currently queued. */ - unicam_schedule_dummy_buffer(node); + if (!node->next_frm || + node->next_frm == node->cur_frm) { + unicam_schedule_dummy_buffer(node); + } else if (unicam->node[i].cur_frm) { + /* + * Repeated FS without FE. Hardware will have + * swapped buffers, but the cur_frm doesn't + * contain valid data. Return cur_frm to the + * queue. + */ + spin_lock(&node->dma_queue_lock); + list_add_tail(&node->cur_frm->list, + &node->dma_queue); + spin_unlock(&node->dma_queue_lock); + node->cur_frm = node->next_frm; + node->next_frm = NULL; + } } unicam_queue_event_sof(unicam);