From 9e089a649a229fb08942ada75b0f1c7f5814e065 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:26 +0530 Subject: [PATCH 01/16] media: i2c: imx334: Configure lane mode dynamically Configure the lane mode dynamically from the streaming function instead of using a hardcoded value. Signed-off-by: Tarang Raval [Sakari Ailus: Fix checkpatch.pl issue, lower Dynamically.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 9d4d15df8dcf..561ed2c87005 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -47,6 +47,8 @@ #define IMX334_EXPOSURE_DEFAULT 0x0648 #define IMX334_REG_LANEMODE CCI_REG8(0x3a01) +#define IMX334_CSI_4_LANE_MODE 3 +#define IMX334_CSI_8_LANE_MODE 7 /* Window cropping Settings */ #define IMX334_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) @@ -240,7 +242,6 @@ static const struct cci_reg_sequence common_mode_regs[] = { { IMX334_REG_HADD_VADD, 0x00 }, { IMX334_REG_VALID_EXPAND, 0x03 }, { IMX334_REG_TCYCLE, 0x00 }, - { IMX334_REG_LANEMODE, 0x03 }, { IMX334_REG_TCLKPOST, 0x007f }, { IMX334_REG_TCLKPREPARE, 0x0037 }, { IMX334_REG_TCLKTRAIL, 0x0037 }, @@ -876,6 +877,13 @@ static int imx334_start_streaming(struct imx334 *imx334) return ret; } + ret = cci_write(imx334->cci, IMX334_REG_LANEMODE, + IMX334_CSI_4_LANE_MODE, NULL); + if (ret) { + dev_err(imx334->dev, "failed to configure lanes\n"); + return ret; + } + ret = imx334_set_framefmt(imx334); if (ret) { dev_err(imx334->dev, "%s failed to set frame format: %d\n", -- 2.51.0 From a6dde677b93795a04f43f90427723bb4c2a50e5e Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:27 +0530 Subject: [PATCH 02/16] media: i2c: imx334: Fix power management and control handling Some controls may need the sensor to be powered on to update their values. Currently, only the exposure control does this. To ensure proper handling, the power-up sequence is moved outside the switch-case. Additionally, VBLANK control is now processed earlier so its changes can correctly affect other controls. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 561ed2c87005..e8422d2fadfd 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -578,8 +578,7 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) u32 exposure; int ret; - switch (ctrl->id) { - case V4L2_CID_VBLANK: + if (ctrl->id == V4L2_CID_VBLANK) { imx334->vblank = imx334->vblank_ctrl->val; dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u\n", @@ -592,13 +591,24 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) imx334->cur_mode->height - IMX334_EXPOSURE_OFFSET, 1, IMX334_EXPOSURE_DEFAULT); + if (ret) + return ret; + } + + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx334->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + exposure = imx334->exp_ctrl->val; + analog_gain = imx334->again_ctrl->val; + + ret = imx334_update_exp_gain(imx334, exposure, analog_gain); + break; case V4L2_CID_EXPOSURE: - /* Set controls only if sensor is in power on state */ - if (!pm_runtime_get_if_in_use(imx334->dev)) - return 0; - exposure = ctrl->val; analog_gain = imx334->again_ctrl->val; @@ -607,8 +617,6 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx334_update_exp_gain(imx334, exposure, analog_gain); - pm_runtime_put(imx334->dev); - break; case V4L2_CID_PIXEL_RATE: case V4L2_CID_LINK_FREQ: @@ -640,6 +648,8 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; } + pm_runtime_put(imx334->dev); + return ret; } -- 2.51.0 From b493cd3c03641f9bbaa9787e43ca92163cb50051 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:28 +0530 Subject: [PATCH 03/16] media: i2c: imx334: Fix runtime PM handling in remove function pm_runtime_suspended() only checks the current runtime PM status and does not modify it, making it ineffective in this context. This could result in improper power management if the device remains active when removed. This patch fixes the issue by introducing a check with pm_runtime_status_suspended() to determine if the device is already suspended. If it is not, it calls imx334_power_off() to power down the device and then uses pm_runtime_set_suspended() to correctly update the runtime PM status to suspended. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index e8422d2fadfd..2ede0787be14 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -1328,7 +1328,10 @@ static void imx334_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - pm_runtime_suspended(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + imx334_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } mutex_destroy(&imx334->mutex); } -- 2.51.0 From 01dfdf6a80c57151af0589af0db7adbbdd1361c7 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:29 +0530 Subject: [PATCH 04/16] media: i2c: imx334: Enable runtime PM before sub-device registration Runtime PM is fully initialized before calling v4l2_async_register_subdev_sensor(). Moving the runtime PM initialization earlier prevents potential access to an uninitialized or powered-down device. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 2ede0787be14..412ab469b130 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -1287,6 +1287,9 @@ static int imx334_probe(struct i2c_client *client) goto error_handler_free; } + pm_runtime_set_active(imx334->dev); + pm_runtime_enable(imx334->dev); + ret = v4l2_async_register_subdev_sensor(&imx334->sd); if (ret < 0) { dev_err(imx334->dev, @@ -1294,13 +1297,13 @@ static int imx334_probe(struct i2c_client *client) goto error_media_entity; } - pm_runtime_set_active(imx334->dev); - pm_runtime_enable(imx334->dev); pm_runtime_idle(imx334->dev); return 0; error_media_entity: + pm_runtime_disable(imx334->dev); + pm_runtime_set_suspended(imx334->dev); media_entity_cleanup(&imx334->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); -- 2.51.0 From 9d382f6a9978916317b3fb4ef07b5fec684adde0 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:30 +0530 Subject: [PATCH 05/16] media: i2c: imx334: Use subdev state lock for synchronization Replace the custom mutex in the imx334 driver with the V4L2 subdev state lock for control synchronization. Initialize the subdev with v4l2_subdev_init_finalize in imx334_probe, adding proper cleanup in error paths and imx334_remove. This aligns the driver with V4L2 standards. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 52 +++++++++++++------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 412ab469b130..fa11619bb368 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -184,7 +184,6 @@ struct imx334_mode { * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls * @link_freq_bitmap: Menu bitmap for link_freq_ctrl * @cur_code: current selected format code */ @@ -207,7 +206,6 @@ struct imx334 { }; u32 vblank; const struct imx334_mode *cur_mode; - struct mutex mutex; unsigned long link_freq_bitmap; u32 cur_code; }; @@ -755,8 +753,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, { struct imx334 *imx334 = to_imx334(sd); - mutex_lock(&imx334->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -767,8 +763,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); } - mutex_unlock(&imx334->mutex); - return 0; } @@ -788,8 +782,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, const struct imx334_mode *mode; int ret = 0; - mutex_lock(&imx334->mutex); - mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, @@ -810,8 +802,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, imx334->cur_mode = mode; } - mutex_unlock(&imx334->mutex); - return ret; } @@ -830,8 +820,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - mutex_lock(&imx334->mutex); - imx334_fill_pad_format(imx334, imx334->cur_mode, &fmt); __v4l2_ctrl_modify_range(imx334->link_freq_ctrl, 0, @@ -839,8 +827,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, ~(imx334->link_freq_bitmap), __ffs(imx334->link_freq_bitmap)); - mutex_unlock(&imx334->mutex); - return imx334_set_pad_format(sd, sd_state, &fmt); } @@ -943,12 +929,10 @@ static int imx334_set_stream(struct v4l2_subdev *sd, int enable) struct imx334 *imx334 = to_imx334(sd); int ret; - mutex_lock(&imx334->mutex); - if (enable) { ret = pm_runtime_resume_and_get(imx334->dev); if (ret < 0) - goto error_unlock; + return ret; ret = imx334_start_streaming(imx334); if (ret) @@ -958,15 +942,10 @@ static int imx334_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(imx334->dev); } - mutex_unlock(&imx334->mutex); - return 0; error_power_off: pm_runtime_put(imx334->dev); -error_unlock: - mutex_unlock(&imx334->mutex); - return ret; } @@ -1145,9 +1124,6 @@ static int imx334_init_controls(struct imx334 *imx334) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx334->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; imx334->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1249,12 +1225,10 @@ static int imx334_probe(struct i2c_client *client) return dev_err_probe(imx334->dev, ret, "HW configuration is not supported\n"); - mutex_init(&imx334->mutex); - ret = imx334_power_on(imx334->dev); if (ret) { dev_err_probe(imx334->dev, ret, "failed to power-on the sensor\n"); - goto error_mutex_destroy; + return ret; } /* Check module identity */ @@ -1287,6 +1261,13 @@ static int imx334_probe(struct i2c_client *client) goto error_handler_free; } + imx334->sd.state_lock = imx334->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx334->sd); + if (ret < 0) { + dev_err(imx334->dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + pm_runtime_set_active(imx334->dev); pm_runtime_enable(imx334->dev); @@ -1294,23 +1275,26 @@ static int imx334_probe(struct i2c_client *client) if (ret < 0) { dev_err(imx334->dev, "failed to register async subdev: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } pm_runtime_idle(imx334->dev); return 0; -error_media_entity: +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx334->sd); pm_runtime_disable(imx334->dev); pm_runtime_set_suspended(imx334->dev); + +error_media_entity: media_entity_cleanup(&imx334->sd.entity); + error_handler_free: v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); + error_power_off: imx334_power_off(imx334->dev); -error_mutex_destroy: - mutex_destroy(&imx334->mutex); return ret; } @@ -1324,9 +1308,9 @@ error_mutex_destroy: static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx334 *imx334 = to_imx334(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1335,8 +1319,6 @@ static void imx334_remove(struct i2c_client *client) imx334_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); } - - mutex_destroy(&imx334->mutex); } static const struct dev_pm_ops imx334_pm_ops = { -- 2.51.0 From 6f1b74c1a686c93b404bd57d73577a6b5b19c5c3 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:31 +0530 Subject: [PATCH 06/16] media: i2c: imx334: switch to {enable,disable}_streams Switch from s_stream to enable_streams and disable_streams callbacks. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 78 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index fa11619bb368..fc875072f859 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -847,21 +847,31 @@ static int imx334_set_framefmt(struct imx334 *imx334) } /** - * imx334_start_streaming() - Start sensor stream - * @imx334: pointer to imx334 device + * imx334_enable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are enabled + * @streams_mask: bitmask specifying the streams to enable * * Return: 0 if successful, error code otherwise. */ -static int imx334_start_streaming(struct imx334 *imx334) +static int imx334_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx334 *imx334 = to_imx334(sd); const struct imx334_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(imx334->dev); + if (ret < 0) + return ret; + ret = cci_multi_reg_write(imx334->cci, common_mode_regs, ARRAY_SIZE(common_mode_regs), NULL); if (ret) { dev_err(imx334->dev, "fail to write common registers\n"); - return ret; + goto err_rpm_put; } /* Write sensor mode registers */ @@ -870,28 +880,28 @@ static int imx334_start_streaming(struct imx334 *imx334) reg_list->num_of_regs, NULL); if (ret) { dev_err(imx334->dev, "fail to write initial registers\n"); - return ret; + goto err_rpm_put; } ret = cci_write(imx334->cci, IMX334_REG_LANEMODE, IMX334_CSI_4_LANE_MODE, NULL); if (ret) { dev_err(imx334->dev, "failed to configure lanes\n"); - return ret; + goto err_rpm_put; } ret = imx334_set_framefmt(imx334); if (ret) { dev_err(imx334->dev, "%s failed to set frame format: %d\n", __func__, ret); - return ret; + goto err_rpm_put; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); if (ret) { dev_err(imx334->dev, "fail to setup handler\n"); - return ret; + goto err_rpm_put; } /* Start streaming */ @@ -899,53 +909,39 @@ static int imx334_start_streaming(struct imx334 *imx334) IMX334_MODE_STREAMING, NULL); if (ret) { dev_err(imx334->dev, "fail to start streaming\n"); - return ret; + goto err_rpm_put; } return 0; -} -/** - * imx334_stop_streaming() - Stop sensor stream - * @imx334: pointer to imx334 device - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_stop_streaming(struct imx334 *imx334) -{ - return cci_write(imx334->cci, IMX334_REG_MODE_SELECT, - IMX334_MODE_STANDBY, NULL); +err_rpm_put: + pm_runtime_put(imx334->dev); + return ret; } /** - * imx334_set_stream() - Enable sensor streaming - * @sd: pointer to imx334 subdevice - * @enable: set to enable sensor streaming + * imx334_disable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are disabled + * @streams_mask: bitmask specifying the streams to disable * * Return: 0 if successful, error code otherwise. */ -static int imx334_set_stream(struct v4l2_subdev *sd, int enable) +static int imx334_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx334 *imx334 = to_imx334(sd); int ret; - if (enable) { - ret = pm_runtime_resume_and_get(imx334->dev); - if (ret < 0) - return ret; - - ret = imx334_start_streaming(imx334); - if (ret) - goto error_power_off; - } else { - imx334_stop_streaming(imx334); - pm_runtime_put(imx334->dev); - } - - return 0; + ret = cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STANDBY, NULL); + if (ret) + dev_err(imx334->dev, "%s failed to stop stream\n", __func__); -error_power_off: pm_runtime_put(imx334->dev); + return ret; } @@ -1040,7 +1036,7 @@ done_endpoint_free: /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx334_video_ops = { - .s_stream = imx334_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { @@ -1048,6 +1044,8 @@ static const struct v4l2_subdev_pad_ops imx334_pad_ops = { .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, .set_fmt = imx334_set_pad_format, + .enable_streams = imx334_enable_streams, + .disable_streams = imx334_disable_streams, }; static const struct v4l2_subdev_ops imx334_subdev_ops = { -- 2.51.0 From 29d69273fefd0bc2a66b5e22f3a4a4c7a53bfa02 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 3 Mar 2025 11:30:22 +0100 Subject: [PATCH 07/16] media: remove STA2x11 media pci driver With commit dcbb01fbb7ae ("x86/pci: Remove old STA2x11 support"), the STA2X11 Video Input Port driver is not needed and cannot be built anymore. Remove the driver and its reference in media documentation. Signed-off-by: Lukas Bulwahn Signed-off-by: Hans Verkuil --- .../admin-guide/media/pci-cardlist.rst | 1 - drivers/media/pci/Kconfig | 1 - drivers/media/pci/Makefile | 2 - drivers/media/pci/sta2x11/Kconfig | 16 - drivers/media/pci/sta2x11/Makefile | 2 - drivers/media/pci/sta2x11/sta2x11_vip.c | 1270 ----------------- drivers/media/pci/sta2x11/sta2x11_vip.h | 29 - 7 files changed, 1321 deletions(-) delete mode 100644 drivers/media/pci/sta2x11/Kconfig delete mode 100644 drivers/media/pci/sta2x11/Makefile delete mode 100644 drivers/media/pci/sta2x11/sta2x11_vip.c delete mode 100644 drivers/media/pci/sta2x11/sta2x11_vip.h diff --git a/Documentation/admin-guide/media/pci-cardlist.rst b/Documentation/admin-guide/media/pci-cardlist.rst index 7d8e3c8987db..239879634ea5 100644 --- a/Documentation/admin-guide/media/pci-cardlist.rst +++ b/Documentation/admin-guide/media/pci-cardlist.rst @@ -86,7 +86,6 @@ saa7134 Philips SAA7134 saa7164 NXP SAA7164 smipcie SMI PCIe DVBSky cards solo6x10 Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264) -sta2x11_vip STA2X11 VIP Video For Linux tw5864 Techwell TW5864 video/audio grabber and encoder tw686x Intersil/Techwell TW686x tw68 Techwell tw68x Video For Linux diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 7f65aa609388..eebb16c58f3d 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -15,7 +15,6 @@ if MEDIA_CAMERA_SUPPORT source "drivers/media/pci/mgb4/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" -source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index f18c7e15abe3..02763ad88511 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -22,8 +22,6 @@ obj-y += ttpci/ \ # Please keep it alphabetically sorted by Kconfig name # (e. g. LC_ALL=C sort Makefile) -obj-$(CONFIG_STA2X11_VIP) += sta2x11/ - obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_COBALT) += cobalt/ obj-$(CONFIG_VIDEO_CX18) += cx18/ diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig deleted file mode 100644 index 118b922c08c3..000000000000 --- a/drivers/media/pci/sta2x11/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config STA2X11_VIP - tristate "STA2X11 VIP Video For Linux" - depends on PCI && VIDEO_DEV && I2C - depends on STA2X11 || COMPILE_TEST - select GPIOLIB if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF2_DMA_CONTIG - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - Say Y for support for STA2X11 VIP (Video Input Port) capture - device. - - To compile this driver as a module, choose M here: the - module will be called sta2x11_vip. diff --git a/drivers/media/pci/sta2x11/Makefile b/drivers/media/pci/sta2x11/Makefile deleted file mode 100644 index bb684a7b6270..000000000000 --- a/drivers/media/pci/sta2x11/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c deleted file mode 100644 index 3049bad20f14..000000000000 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ /dev/null @@ -1,1270 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * This is the driver for the STA2x11 Video Input Port. - * - * Copyright (C) 2012 ST Microelectronics - * author: Federico Vaga - * Copyright (C) 2010 WindRiver Systems, Inc. - * authors: Andreas Kies - * Vlad Lungu - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sta2x11_vip.h" - -#define DRV_VERSION "1.3" - -#ifndef PCI_DEVICE_ID_STMICRO_VIP -#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D -#endif - -#define MAX_FRAMES 4 - -/*Register offsets*/ -#define DVP_CTL 0x00 -#define DVP_TFO 0x04 -#define DVP_TFS 0x08 -#define DVP_BFO 0x0C -#define DVP_BFS 0x10 -#define DVP_VTP 0x14 -#define DVP_VBP 0x18 -#define DVP_VMP 0x1C -#define DVP_ITM 0x98 -#define DVP_ITS 0x9C -#define DVP_STA 0xA0 -#define DVP_HLFLN 0xA8 -#define DVP_RGB 0xC0 -#define DVP_PKZ 0xF0 - -/*Register fields*/ -#define DVP_CTL_ENA 0x00000001 -#define DVP_CTL_RST 0x80000000 -#define DVP_CTL_DIS (~0x00040001) - -#define DVP_IT_VSB 0x00000008 -#define DVP_IT_VST 0x00000010 -#define DVP_IT_FIFO 0x00000020 - -#define DVP_HLFLN_SD 0x00000001 - -#define SAVE_COUNT 8 -#define AUX_COUNT 3 -#define IRQ_COUNT 1 - - -struct vip_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; - dma_addr_t dma; -}; -static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) -{ - return container_of(vb2, struct vip_buffer, vb); -} - -/** - * struct sta2x11_vip - All internal data for one instance of device - * @v4l2_dev: device registered in v4l layer - * @video_dev: properties of our device - * @pdev: PCI device - * @adapter: contains I2C adapter information - * @register_save_area: All relevant register are saved here during suspend - * @decoder: contains information about video DAC - * @ctrl_hdl: handler for control framework - * @format: pixel format, fixed UYVY - * @std: video standard (e.g. PAL/NTSC) - * @input: input line for video signal ( 0 or 1 ) - * @disabled: Device is in power down state - * @slock: for excluse access of registers - * @vb_vidq: queue maintained by videobuf2 layer - * @buffer_list: list of buffer in use - * @sequence: sequence number of acquired buffer - * @active: current active buffer - * @lock: used in videobuf2 callback - * @v4l_lock: serialize its video4linux ioctls - * @tcount: Number of top frames - * @bcount: Number of bottom frames - * @overflow: Number of FIFO overflows - * @iomem: hardware base address - * @config: I2C and gpio config from platform - * - * All non-local data is accessed via this structure. - */ -struct sta2x11_vip { - struct v4l2_device v4l2_dev; - struct video_device video_dev; - struct pci_dev *pdev; - struct i2c_adapter *adapter; - unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; - struct v4l2_subdev *decoder; - struct v4l2_ctrl_handler ctrl_hdl; - - - struct v4l2_pix_format format; - v4l2_std_id std; - unsigned int input; - int disabled; - spinlock_t slock; - - struct vb2_queue vb_vidq; - struct list_head buffer_list; - unsigned int sequence; - struct vip_buffer *active; /* current active buffer */ - spinlock_t lock; /* Used in videobuf2 callback */ - struct mutex v4l_lock; - - /* Interrupt counters */ - int tcount, bcount; - int overflow; - - void __iomem *iomem; /* I/O Memory */ - struct vip_config *config; -}; - -static const unsigned int registers_to_save[AUX_COUNT] = { - DVP_HLFLN, DVP_RGB, DVP_PKZ -}; - -static struct v4l2_pix_format formats_50[] = { - { /*PAL interlaced */ - .width = 720, - .height = 576, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 576, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*PAL top */ - .width = 720, - .height = 288, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_TOP, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 288, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*PAL bottom */ - .width = 720, - .height = 288, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_BOTTOM, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 288, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - -}; - -static struct v4l2_pix_format formats_60[] = { - { /*NTSC interlaced */ - .width = 720, - .height = 480, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 480, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*NTSC top */ - .width = 720, - .height = 240, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_TOP, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 240, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*NTSC bottom */ - .width = 720, - .height = 240, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_BOTTOM, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 240, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, -}; - -/* Write VIP register */ -static inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val) -{ - iowrite32((val), (vip->iomem)+(reg)); -} -/* Read VIP register */ -static inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg) -{ - return ioread32((vip->iomem)+(reg)); -} -/* Start DMA acquisition */ -static void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf) -{ - unsigned long offset = 0; - - if (vip->format.field == V4L2_FIELD_INTERLACED) - offset = vip->format.width * 2; - - spin_lock_irq(&vip->slock); - /* Enable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA); - /* Set Top and Bottom Field memory address */ - reg_write(vip, DVP_VTP, (u32)vip_buf->dma); - reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset); - spin_unlock_irq(&vip->slock); -} - -/* Fetch the next buffer to activate */ -static void vip_active_buf_next(struct sta2x11_vip *vip) -{ - /* Get the next buffer */ - spin_lock(&vip->lock); - if (list_empty(&vip->buffer_list)) {/* No available buffer */ - spin_unlock(&vip->lock); - return; - } - vip->active = list_first_entry(&vip->buffer_list, - struct vip_buffer, - list); - /* Reset Top and Bottom counter */ - vip->tcount = 0; - vip->bcount = 0; - spin_unlock(&vip->lock); - if (vb2_is_streaming(&vip->vb_vidq)) { /* streaming is on */ - start_dma(vip, vip->active); /* start dma capture */ - } -} - - -/* Videobuf2 Operations */ -static int queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - - if (!(*nbuffers) || *nbuffers < MAX_FRAMES) - *nbuffers = MAX_FRAMES; - - *nplanes = 1; - sizes[0] = vip->format.sizeimage; - - vip->sequence = 0; - vip->active = NULL; - vip->tcount = 0; - vip->bcount = 0; - - return 0; -}; -static int buffer_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0); - INIT_LIST_HEAD(&vip_buf->list); - return 0; -} - -static int buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - unsigned long size; - - size = vip->format.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&vip_buf->vb.vb2_buf, 0, size); - - return 0; -} -static void buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - spin_lock(&vip->lock); - list_add_tail(&vip_buf->list, &vip->buffer_list); - if (!vip->active) { /* No active buffer, active the first one */ - vip->active = list_first_entry(&vip->buffer_list, - struct vip_buffer, - list); - if (vb2_is_streaming(&vip->vb_vidq)) /* streaming is on */ - start_dma(vip, vip_buf); /* start dma capture */ - } - spin_unlock(&vip->lock); -} -static void buffer_finish(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - /* Buffer handled, remove it from the list */ - spin_lock(&vip->lock); - list_del_init(&vip_buf->list); - spin_unlock(&vip->lock); - - if (vb2_is_streaming(vb->vb2_queue)) - vip_active_buf_next(vip); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - - spin_lock_irq(&vip->slock); - /* Enable interrupt VSYNC Top and Bottom*/ - reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); - spin_unlock_irq(&vip->slock); - - if (count) - start_dma(vip, vip->active); - - return 0; -} - -/* abort streaming and wait for last buffer */ -static void stop_streaming(struct vb2_queue *vq) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - struct vip_buffer *vip_buf, *node; - - /* Disable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); - /* Disable all interrupts */ - reg_write(vip, DVP_ITM, 0); - - /* Release all active buffers */ - spin_lock(&vip->lock); - list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) { - vb2_buffer_done(&vip_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - list_del(&vip_buf->list); - } - spin_unlock(&vip->lock); -} - -static const struct vb2_ops vip_video_qops = { - .queue_setup = queue_setup, - .buf_init = buffer_init, - .buf_prepare = buffer_prepare, - .buf_finish = buffer_finish, - .buf_queue = buffer_queue, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, -}; - - -/* File Operations */ -static const struct v4l2_file_operations vip_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll -}; - - -/** - * vidioc_querycap - return capabilities of device - * @file: descriptor of device - * @cap: contains return values - * @priv: unused - * - * the capabilities of the device are returned - * - * return value: 0, no error. - */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); - return 0; -} - -/** - * vidioc_s_std - set video standard - * @file: descriptor of device - * @std: contains standard to be set - * @priv: unused - * - * the video standard is set - * - * return value: 0, no error. - * - * -EIO, no input signal detected - * - * other, returned from video DAC. - */ -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - /* - * This is here for backwards compatibility only. - * The use of V4L2_STD_ALL to trigger a querystd is non-standard. - */ - if (std == V4L2_STD_ALL) { - v4l2_subdev_call(vip->decoder, video, querystd, &std); - if (std == V4L2_STD_UNKNOWN) - return -EIO; - } - - if (vip->std != std) { - vip->std = std; - if (V4L2_STD_525_60 & std) - vip->format = formats_60[0]; - else - vip->format = formats_50[0]; - } - - return v4l2_subdev_call(vip->decoder, video, s_std, std); -} - -/** - * vidioc_g_std - get video standard - * @file: descriptor of device - * @priv: unused - * @std: contains return values - * - * the current video standard is returned - * - * return value: 0, no error. - */ -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - *std = vip->std; - return 0; -} - -/** - * vidioc_querystd - get possible video standards - * @file: descriptor of device - * @priv: unused - * @std: contains return values - * - * all possible video standards are returned - * - * return value: delivered by video DAC routine. - */ -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - return v4l2_subdev_call(vip->decoder, video, querystd, std); -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index > 1) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; - sprintf(inp->name, "Camera %u", inp->index); - - return 0; -} - -/** - * vidioc_s_input - set input line - * @file: descriptor of device - * @priv: unused - * @i: new input line number - * - * the current active input line is set - * - * return value: 0, no error. - * - * -EINVAL, line number out of range - */ -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct sta2x11_vip *vip = video_drvdata(file); - int ret; - - if (i > 1) - return -EINVAL; - ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0); - - if (!ret) - vip->input = i; - - return 0; -} - -/** - * vidioc_g_input - return input line - * @file: descriptor of device - * @priv: unused - * @i: returned input line number - * - * the current active input line is returned - * - * return value: always 0. - */ -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - *i = vip->input; - return 0; -} - -/** - * vidioc_enum_fmt_vid_cap - return video capture format - * @file: descriptor of device - * @priv: unused - * @f: returned format information - * - * returns name and format of video capture - * Only UYVY is supported by hardware. - * - * return value: always 0. - */ -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - - if (f->index != 0) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - return 0; -} - -/** - * vidioc_try_fmt_vid_cap - set video capture format - * @file: descriptor of device - * @priv: unused - * @f: new format - * - * new video format is set which includes width and - * field type. width is fixed to 720, no scaling. - * Only UYVY is supported by this hardware. - * the minimum height is 200, the maximum is 576 (PAL) - * - * return value: 0, no error - * - * -EINVAL, pixel or field format not supported - * - */ -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - int interlace_lim; - - if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) { - v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n"); - return -EINVAL; - } - - if (V4L2_STD_525_60 & vip->std) - interlace_lim = 240; - else - interlace_lim = 288; - - switch (f->fmt.pix.field) { - default: - case V4L2_FIELD_ANY: - if (interlace_lim < f->fmt.pix.height) - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - f->fmt.pix.field = V4L2_FIELD_BOTTOM; - break; - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - if (interlace_lim < f->fmt.pix.height) - f->fmt.pix.height = interlace_lim; - break; - case V4L2_FIELD_INTERLACED: - break; - } - - /* It is the only supported format */ - f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - f->fmt.pix.height &= ~1; - if (2 * interlace_lim < f->fmt.pix.height) - f->fmt.pix.height = 2 * interlace_lim; - if (200 > f->fmt.pix.height) - f->fmt.pix.height = 200; - f->fmt.pix.width = 720; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -/** - * vidioc_s_fmt_vid_cap - set current video format parameters - * @file: descriptor of device - * @priv: unused - * @f: returned format information - * - * set new capture format - * return value: 0, no error - * - * other, delivered by video DAC routine. - */ -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - unsigned int t_stop, b_stop, pitch; - int ret; - - ret = vidioc_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - if (vb2_is_busy(&vip->vb_vidq)) { - /* Can't change format during acquisition */ - v4l2_err(&vip->v4l2_dev, "device busy\n"); - return -EBUSY; - } - vip->format = f->fmt.pix; - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - t_stop = ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1); - b_stop = t_stop; - pitch = 4 * vip->format.width; - break; - case V4L2_FIELD_TOP: - t_stop = ((vip->format.height - 1) << 16) | - (2 * vip->format.width - 1); - b_stop = (0 << 16) | (2 * vip->format.width - 1); - pitch = 2 * vip->format.width; - break; - case V4L2_FIELD_BOTTOM: - t_stop = (0 << 16) | (2 * vip->format.width - 1); - b_stop = (vip->format.height << 16) | - (2 * vip->format.width - 1); - pitch = 2 * vip->format.width; - break; - default: - v4l2_err(&vip->v4l2_dev, "unknown field format\n"); - return -EINVAL; - } - - spin_lock_irq(&vip->slock); - /* Y-X Top Field Offset */ - reg_write(vip, DVP_TFO, 0); - /* Y-X Bottom Field Offset */ - reg_write(vip, DVP_BFO, 0); - /* Y-X Top Field Stop*/ - reg_write(vip, DVP_TFS, t_stop); - /* Y-X Bottom Field Stop */ - reg_write(vip, DVP_BFS, b_stop); - /* Video Memory Pitch */ - reg_write(vip, DVP_VMP, pitch); - spin_unlock_irq(&vip->slock); - - return 0; -} - -/** - * vidioc_g_fmt_vid_cap - get current video format parameters - * @file: descriptor of device - * @priv: unused - * @f: contains format information - * - * returns current video format parameters - * - * return value: 0, always successful - */ -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - f->fmt.pix = vip->format; - - return 0; -} - -static const struct v4l2_ioctl_ops vip_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - /* FMT handling */ - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - /* Buffer handlers */ - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - /* Stream on/off */ - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Standard handling */ - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, - /* Input handling */ - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - /* Log status ioctl */ - .vidioc_log_status = v4l2_ctrl_log_status, - /* Event handling */ - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device video_dev_template = { - .name = KBUILD_MODNAME, - .release = video_device_release_empty, - .fops = &vip_fops, - .ioctl_ops = &vip_ioctl_ops, - .tvnorms = V4L2_STD_ALL, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, -}; - -/** - * vip_irq - interrupt routine - * @irq: Number of interrupt ( not used, correct number is assumed ) - * @data: local data structure containing all information - * - * check for both frame interrupts set ( top and bottom ). - * check FIFO overflow, but limit number of log messages after open. - * signal a complete buffer if done - * - * return value: IRQ_NONE, interrupt was not generated by VIP - * - * IRQ_HANDLED, interrupt done. - */ -static irqreturn_t vip_irq(int irq, void *data) -{ - struct sta2x11_vip *vip = data; - unsigned int status; - - status = reg_read(vip, DVP_ITS); - - if (!status) /* No interrupt to handle */ - return IRQ_NONE; - - if (status & DVP_IT_FIFO) - if (vip->overflow++ > 5) - pr_info("VIP: fifo overflow\n"); - - if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) { - /* this is bad, we are too slow, hope the condition is gone - * on the next frame */ - return IRQ_HANDLED; - } - - if (status & DVP_IT_VST) - if ((++vip->tcount) < 2) - return IRQ_HANDLED; - if (status & DVP_IT_VSB) { - vip->bcount++; - return IRQ_HANDLED; - } - - if (vip->active) { /* Acquisition is over on this buffer */ - /* Disable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); - /* Remove the active buffer from the list */ - vip->active->vb.vb2_buf.timestamp = ktime_get_ns(); - vip->active->vb.sequence = vip->sequence++; - vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE); - } - - return IRQ_HANDLED; -} - -static void sta2x11_vip_init_register(struct sta2x11_vip *vip) -{ - /* Register initialization */ - spin_lock_irq(&vip->slock); - /* Clean interrupt */ - reg_read(vip, DVP_ITS); - /* Enable Half Line per vertical */ - reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD); - /* Reset VIP control */ - reg_write(vip, DVP_CTL, DVP_CTL_RST); - /* Clear VIP control */ - reg_write(vip, DVP_CTL, 0); - spin_unlock_irq(&vip->slock); -} -static void sta2x11_vip_clear_register(struct sta2x11_vip *vip) -{ - spin_lock_irq(&vip->slock); - /* Disable interrupt */ - reg_write(vip, DVP_ITM, 0); - /* Reset VIP Control */ - reg_write(vip, DVP_CTL, DVP_CTL_RST); - /* Clear VIP Control */ - reg_write(vip, DVP_CTL, 0); - /* Clean VIP Interrupt */ - reg_read(vip, DVP_ITS); - spin_unlock_irq(&vip->slock); -} -static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) -{ - int err; - - err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29)); - if (err) { - v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask"); - return err; - } - memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue)); - vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ; - vip->vb_vidq.drv_priv = vip; - vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer); - vip->vb_vidq.ops = &vip_video_qops; - vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; - vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vip->vb_vidq.dev = &vip->pdev->dev; - vip->vb_vidq.lock = &vip->v4l_lock; - err = vb2_queue_init(&vip->vb_vidq); - if (err) - return err; - INIT_LIST_HEAD(&vip->buffer_list); - spin_lock_init(&vip->lock); - return 0; -} - -static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) -{ - /* - * Inititialize an empty control so VIP can inerithing controls - * from ADV7180 - */ - v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0); - - vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl; - if (vip->ctrl_hdl.error) { - int err = vip->ctrl_hdl.error; - - v4l2_ctrl_handler_free(&vip->ctrl_hdl); - return err; - } - - return 0; -} - -/** - * vip_gpio_reserve - reserve gpio pin - * @dev: device - * @pin: GPIO pin number - * @dir: direction, input or output - * @name: GPIO pin name - * - */ -static int vip_gpio_reserve(struct device *dev, int pin, int dir, - const char *name) -{ - struct gpio_desc *desc = gpio_to_desc(pin); - int ret = -ENODEV; - - if (!gpio_is_valid(pin)) - return ret; - - ret = gpio_request(pin, name); - if (ret) { - dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name); - return ret; - } - - ret = gpiod_direction_output(desc, dir); - if (ret) { - dev_err(dev, "Failed to set direction for pin %d (%s)\n", - pin, name); - gpio_free(pin); - return ret; - } - - ret = gpiod_export(desc, false); - if (ret) { - dev_err(dev, "Failed to export pin %d (%s)\n", pin, name); - gpio_free(pin); - return ret; - } - - return 0; -} - -/** - * vip_gpio_release - release gpio pin - * @dev: device - * @pin: GPIO pin number - * @name: GPIO pin name - * - */ -static void vip_gpio_release(struct device *dev, int pin, const char *name) -{ - if (gpio_is_valid(pin)) { - struct gpio_desc *desc = gpio_to_desc(pin); - - dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); - gpiod_unexport(desc); - gpio_free(pin); - } -} - -/** - * sta2x11_vip_init_one - init one instance of video device - * @pdev: PCI device - * @ent: (not used) - * - * allocate reset pins for DAC. - * Reset video DAC, this is done via reset line. - * allocate memory for managing device - * request interrupt - * map IO region - * register device - * find and initialize video DAC - * - * return value: 0, no error - * - * -ENOMEM, no memory - * - * -ENODEV, device could not be detected or registered - */ -static int sta2x11_vip_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret; - struct sta2x11_vip *vip; - struct vip_config *config; - - /* Check if hardware support 26-bit DMA */ - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) { - dev_err(&pdev->dev, "26-bit DMA addressing not available\n"); - return -EINVAL; - } - /* Enable PCI */ - ret = pci_enable_device(pdev); - if (ret) - return ret; - - /* Get VIP platform data */ - config = dev_get_platdata(&pdev->dev); - if (!config) { - dev_info(&pdev->dev, "VIP slot disabled\n"); - ret = -EINVAL; - goto disable; - } - - /* Power configuration */ - ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, - config->pwr_name); - if (ret) - goto disable; - - ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, - config->reset_name); - if (ret) { - vip_gpio_release(&pdev->dev, config->pwr_pin, - config->pwr_name); - goto disable; - } - - if (gpio_is_valid(config->pwr_pin)) { - /* Datasheet says 5ms between PWR and RST */ - usleep_range(5000, 25000); - gpio_direction_output(config->pwr_pin, 1); - } - - if (gpio_is_valid(config->reset_pin)) { - /* Datasheet says 5ms between PWR and RST */ - usleep_range(5000, 25000); - gpio_direction_output(config->reset_pin, 1); - } - usleep_range(5000, 25000); - - /* Allocate a new VIP instance */ - vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); - if (!vip) { - ret = -ENOMEM; - goto release_gpios; - } - vip->pdev = pdev; - vip->std = V4L2_STD_PAL; - vip->format = formats_50[0]; - vip->config = config; - mutex_init(&vip->v4l_lock); - - ret = sta2x11_vip_init_controls(vip); - if (ret) - goto free_mem; - ret = v4l2_device_register(&pdev->dev, &vip->v4l2_dev); - if (ret) - goto free_mem; - - dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n", - (unsigned long)pci_resource_start(pdev, 0), - (unsigned long)pci_resource_len(pdev, 0), pdev->irq); - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, KBUILD_MODNAME); - if (ret) - goto unreg; - - vip->iomem = pci_iomap(pdev, 0, 0x100); - if (!vip->iomem) { - ret = -ENOMEM; - goto release; - } - - pci_enable_msi(pdev); - - /* Initialize buffer */ - ret = sta2x11_vip_init_buffer(vip); - if (ret) - goto unmap; - - spin_lock_init(&vip->slock); - - ret = request_irq(pdev->irq, vip_irq, IRQF_SHARED, KBUILD_MODNAME, vip); - if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); - ret = -ENODEV; - goto release_buf; - } - - /* Initialize and register video device */ - vip->video_dev = video_dev_template; - vip->video_dev.v4l2_dev = &vip->v4l2_dev; - vip->video_dev.queue = &vip->vb_vidq; - vip->video_dev.lock = &vip->v4l_lock; - video_set_drvdata(&vip->video_dev, vip); - - ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1); - if (ret) - goto vrelease; - - /* Get ADV7180 subdevice */ - vip->adapter = i2c_get_adapter(vip->config->i2c_id); - if (!vip->adapter) { - ret = -ENODEV; - dev_err(&pdev->dev, "no I2C adapter found\n"); - goto vunreg; - } - - vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter, - "adv7180", vip->config->i2c_addr, - NULL); - if (!vip->decoder) { - ret = -ENODEV; - dev_err(&pdev->dev, "no decoder found\n"); - goto vunreg; - } - - i2c_put_adapter(vip->adapter); - v4l2_subdev_call(vip->decoder, core, init, 0); - - sta2x11_vip_init_register(vip); - - dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n"); - return 0; - -vunreg: - video_set_drvdata(&vip->video_dev, NULL); -vrelease: - vb2_video_unregister_device(&vip->video_dev); - free_irq(pdev->irq, vip); -release_buf: - pci_disable_msi(pdev); -unmap: - pci_iounmap(pdev, vip->iomem); -release: - pci_release_regions(pdev); -unreg: - v4l2_device_unregister(&vip->v4l2_dev); -free_mem: - kfree(vip); -release_gpios: - vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name); - vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name); -disable: - /* - * do not call pci_disable_device on sta2x11 because it break all - * other Bus masters on this EP - */ - return ret; -} - -/** - * sta2x11_vip_remove_one - release device - * @pdev: PCI device - * - * Undo everything done in .._init_one - * - * unregister video device - * free interrupt - * unmap ioadresses - * free memory - * free GPIO pins - */ -static void sta2x11_vip_remove_one(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - - sta2x11_vip_clear_register(vip); - - video_set_drvdata(&vip->video_dev, NULL); - vb2_video_unregister_device(&vip->video_dev); - free_irq(pdev->irq, vip); - pci_disable_msi(pdev); - pci_iounmap(pdev, vip->iomem); - pci_release_regions(pdev); - - v4l2_device_unregister(&vip->v4l2_dev); - - vip_gpio_release(&pdev->dev, vip->config->pwr_pin, - vip->config->pwr_name); - vip_gpio_release(&pdev->dev, vip->config->reset_pin, - vip->config->reset_name); - - kfree(vip); - /* - * do not call pci_disable_device on sta2x11 because it break all - * other Bus masters on this EP - */ -} - -/** - * sta2x11_vip_suspend - set device into power save mode - * @dev_d: PCI device - * - * all relevant registers are saved and an attempt to set a new state is made. - * - * return value: 0 always indicate success, - * even if device could not be disabled. (workaround for hardware problem) - */ -static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - unsigned long flags; - int i; - - spin_lock_irqsave(&vip->slock, flags); - vip->register_save_area[0] = reg_read(vip, DVP_CTL); - reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); - vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM); - reg_write(vip, DVP_ITM, 0); - for (i = 1; i < SAVE_COUNT; i++) - vip->register_save_area[i] = reg_read(vip, 4 * i); - for (i = 0; i < AUX_COUNT; i++) - vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = - reg_read(vip, registers_to_save[i]); - spin_unlock_irqrestore(&vip->slock, flags); - - vip->disabled = 1; - - pr_info("VIP: suspend\n"); - return 0; -} - -/** - * sta2x11_vip_resume - resume device operation - * @dev_d : PCI device - * - * return value: 0, no error. - * - * other, could not set device to power on state. - */ -static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - unsigned long flags; - int i; - - pr_info("VIP: resume\n"); - - vip->disabled = 0; - - spin_lock_irqsave(&vip->slock, flags); - for (i = 1; i < SAVE_COUNT; i++) - reg_write(vip, 4 * i, vip->register_save_area[i]); - for (i = 0; i < AUX_COUNT; i++) - reg_write(vip, registers_to_save[i], - vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); - reg_write(vip, DVP_CTL, vip->register_save_area[0]); - reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); - spin_unlock_irqrestore(&vip->slock, flags); - return 0; -} - -static const struct pci_device_id sta2x11_vip_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, - {0,} -}; - -static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, - sta2x11_vip_suspend, - sta2x11_vip_resume); - -static struct pci_driver sta2x11_vip_driver = { - .name = KBUILD_MODNAME, - .probe = sta2x11_vip_init_one, - .remove = sta2x11_vip_remove_one, - .id_table = sta2x11_vip_pci_tbl, - .driver.pm = &sta2x11_vip_pm_ops, -}; - -static int __init sta2x11_vip_init_module(void) -{ - return pci_register_driver(&sta2x11_vip_driver); -} - -static void __exit sta2x11_vip_exit_module(void) -{ - pci_unregister_driver(&sta2x11_vip_driver); -} - -#ifdef MODULE -module_init(sta2x11_vip_init_module); -module_exit(sta2x11_vip_exit_module); -#else -late_initcall_sync(sta2x11_vip_init_module); -#endif - -MODULE_DESCRIPTION("STA2X11 Video Input Port driver"); -MODULE_AUTHOR("Wind River"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl); diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.h b/drivers/media/pci/sta2x11/sta2x11_vip.h deleted file mode 100644 index de6000e7943e..000000000000 --- a/drivers/media/pci/sta2x11/sta2x11_vip.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2011 Wind River Systems, Inc. - * - * Author: Anders Wallin - */ - -#ifndef __STA2X11_VIP_H -#define __STA2X11_VIP_H - -/** - * struct vip_config - video input configuration data - * @pwr_name: ADV powerdown name - * @pwr_pin: ADV powerdown pin - * @reset_name: ADV reset name - * @reset_pin: ADV reset pin - * @i2c_id: ADV i2c adapter ID - * @i2c_addr: ADV i2c address - */ -struct vip_config { - const char *pwr_name; - int pwr_pin; - const char *reset_name; - int reset_pin; - int i2c_id; - int i2c_addr; -}; - -#endif /* __STA2X11_VIP_H */ -- 2.51.0 From df8375bbe2d5bb6d7f86701b15426674f2529a18 Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:16:59 -0500 Subject: [PATCH 08/16] media: v4l2-common: Add RGBR format info Add missing RGBR entry in the v4l2_format_info[] table. RGBR has identical format information to RGBP, as it is a big endian variant of RGB-5-6-5 pixel encoding according to the description in videodev2.h. Signed-off-by: Matthew Majewski Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index aa86b8c6aa75..4ee4aa19efe6 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -250,6 +250,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, -- 2.51.0 From bbd0df9bfe1c085bf9b0a315dd5fb58e491dfa5b Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:17:00 -0500 Subject: [PATCH 09/16] media: vim2m: Simplify try_fmt Clean up vidioc_try_fmt with the following changes: 1. remove unsused vim2m_fmt parameter 2. use clamp() macro to restrain width/height bounds 3. use ALIGN() macro to align width/height 4. use v4l2_fill_pixfmt to set bytesperline/sizeimage Signed-off-by: Matthew Majewski Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vim2m.c | 33 +++++++++++------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index 0fe97e208c02..ba3419820ff1 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -26,6 +26,7 @@ #include #include #include +#include MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); MODULE_AUTHOR("Pawel Osciak, "); @@ -755,31 +756,21 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return vidioc_g_fmt(file2ctx(file), f); } -static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f) { - int walign, halign; - /* - * V4L2 specification specifies the driver corrects the - * format struct if any of the dimensions is unsupported - */ - if (f->fmt.pix.height < MIN_H) - f->fmt.pix.height = MIN_H; - else if (f->fmt.pix.height > MAX_H) - f->fmt.pix.height = MAX_H; + int width, height, walign, halign; - if (f->fmt.pix.width < MIN_W) - f->fmt.pix.width = MIN_W; - else if (f->fmt.pix.width > MAX_W) - f->fmt.pix.width = MAX_W; + width = clamp(f->fmt.pix.width, MIN_W, MAX_W); + height = clamp(f->fmt.pix.width, MIN_H, MAX_H); get_alignment(f->fmt.pix.pixelformat, &walign, &halign); - f->fmt.pix.width &= ~(walign - 1); - f->fmt.pix.height &= ~(halign - 1); - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + width = ALIGN(width, walign); + height = ALIGN(height, halign); + f->fmt.pix.field = V4L2_FIELD_NONE; - return 0; + return v4l2_fill_pixfmt(&f->fmt.pix, f->fmt.pix.pixelformat, + width, height); } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, @@ -804,7 +795,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; f->fmt.pix.quantization = ctx->quant; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -827,7 +818,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f); } static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) -- 2.51.0 From c09acbbfff2037fe594396388e0c28eeeca79012 Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:17:01 -0500 Subject: [PATCH 10/16] media: vim2m: Add parametized support for multiplanar API Add support for the mulitiplaner API. The device can now act as either a multi-planar or a single-planar device depending on a module parameter, similar to the way vivid behaves. Multiplanar support was added by implementing the appropate try/get/set mplane functions, and by modifying the queue_setup() and buf_prepare() functions to handle multiple planes. Implementation was inspired by vivid. Signed-off-by: Matthew Majewski Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vim2m.c | 306 +++++++++++++++++++++++++---- 1 file changed, 267 insertions(+), 39 deletions(-) diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index ba3419820ff1..1d1a9e768505 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -43,6 +43,10 @@ static unsigned int default_transtime = 40; /* Max 25 fps */ module_param(default_transtime, uint, 0644); MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); +static unsigned int multiplanar = 1; +module_param(multiplanar, uint, 0644); +MODULE_PARM_DESC(multiplanar, "1 (default) creates a single planar device, 2 creates multiplanar device."); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -135,7 +139,8 @@ static struct vim2m_fmt formats[] = { struct vim2m_q_data { unsigned int width; unsigned int height; - unsigned int sizeimage; + unsigned int num_mem_planes; + unsigned int sizeimage[VIDEO_MAX_PLANES]; unsigned int sequence; struct vim2m_fmt *fmt; }; @@ -194,6 +199,7 @@ struct vim2m_dev { struct mutex dev_mutex; struct v4l2_m2m_dev *m2m_dev; + bool multiplanar; }; struct vim2m_ctx { @@ -238,8 +244,10 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return &ctx->q_data[V4L2_M2M_SRC]; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return &ctx->q_data[V4L2_M2M_DST]; default: return NULL; @@ -250,8 +258,10 @@ static const char *type_name(enum v4l2_buf_type type) { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return "Output"; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return "Capture"; default: return "Invalid"; @@ -721,6 +731,7 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vb2_queue *vq; struct vim2m_q_data *q_data; + int ret; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -730,12 +741,12 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) if (!q_data) return -EINVAL; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; + ret = v4l2_fill_pixfmt(&f->fmt.pix, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fmt->fourcc; - f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; - f->fmt.pix.sizeimage = q_data->sizeimage; f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -744,33 +755,102 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return 0; } +static int vidioc_g_fmt_mplane(struct vim2m_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct vim2m_q_data *q_data; + int ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return 0; +} + static int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } -static int vidioc_try_fmt(struct v4l2_format *f) +static int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) { - int width, height, walign, halign; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; - width = clamp(f->fmt.pix.width, MIN_W, MAX_W); - height = clamp(f->fmt.pix.width, MIN_H, MAX_H); + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_try_fmt(struct v4l2_format *f, bool is_mplane) +{ + int walign, halign, ret; + int width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + int height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : + f->fmt.pix.pixelformat; + + width = clamp(width, MIN_W, MAX_W); + height = clamp(height, MIN_H, MAX_H); - get_alignment(f->fmt.pix.pixelformat, &walign, &halign); + get_alignment(pixfmt, &walign, &halign); width = ALIGN(width, walign); height = ALIGN(height, halign); f->fmt.pix.field = V4L2_FIELD_NONE; - return v4l2_fill_pixfmt(&f->fmt.pix, f->fmt.pix.pixelformat, - width, height); + if (is_mplane) { + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, pixfmt, width, + height); + } else { + ret = v4l2_fill_pixfmt(&f->fmt.pix, pixfmt, width, height); + } + return ret; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, @@ -778,6 +858,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -795,7 +879,36 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; f->fmt.pix.quantization = ctx->quant; - return vidioc_try_fmt(f); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return vidioc_try_fmt(f, true); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -803,6 +916,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -818,13 +935,45 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - return vidioc_try_fmt(f); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + if (!f->fmt.pix_mp.colorspace) + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + + return vidioc_try_fmt(f, true); } static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vim2m_q_data *q_data; struct vb2_queue *vq; + unsigned int i; + bool is_mplane = ctx->dev->multiplanar; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : f->fmt.pix.pixelformat; + u32 width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + u32 height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -839,11 +988,17 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return -EBUSY; } - q_data->fmt = find_format(f->fmt.pix.pixelformat); - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->sizeimage = q_data->width * q_data->height - * q_data->fmt->depth >> 3; + q_data->fmt = find_format(pixfmt); + q_data->width = width; + q_data->height = height; + if (is_mplane) { + q_data->num_mem_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + q_data->sizeimage[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } else { + q_data->sizeimage[0] = f->fmt.pix.sizeimage; + q_data->num_mem_planes = 1; + } dprintk(ctx->dev, 1, "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n", @@ -861,6 +1016,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) @@ -869,12 +1028,32 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return vidioc_s_fmt(file2ctx(file), f); } +static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_cap_mplane(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(file2ctx(file), f); +} + static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); int ret; + if (dev->multiplanar) + return -ENOTTY; + ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; @@ -889,6 +1068,30 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } +static int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + int ret; + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_out_mplane(file, priv, f); + if (ret) + return ret; + + ret = vidioc_s_fmt(file2ctx(file), f); + if (!ret) { + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quant = f->fmt.pix_mp.quantization; + } + return ret; +} + static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) { struct vim2m_ctx *ctx = @@ -939,11 +1142,17 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, @@ -972,23 +1181,32 @@ static int vim2m_queue_setup(struct vb2_queue *vq, { struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); struct vim2m_q_data *q_data; - unsigned int size, count = *nbuffers; + unsigned int size, p, count = *nbuffers; q_data = get_q_data(ctx, vq->type); if (!q_data) return -EINVAL; - size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + size = 0; + for (p = 0; p < q_data->num_mem_planes; p++) + size += q_data->sizeimage[p]; while (size * count > MEM2MEM_VID_MEM_LIMIT) (count)--; *nbuffers = count; - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; + if (*nplanes) { + if (*nplanes != q_data->num_mem_planes) + return -EINVAL; + for (p = 0; p < q_data->num_mem_planes; p++) { + if (sizes[p] < q_data->sizeimage[p]) + return -EINVAL; + } + } else { + *nplanes = q_data->num_mem_planes; + for (p = 0; p < q_data->num_mem_planes; p++) + sizes[p] = q_data->sizeimage[p]; + } dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n", type_name(vq->type), count, size); @@ -1015,21 +1233,24 @@ static int vim2m_buf_prepare(struct vb2_buffer *vb) { struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; + unsigned int p; dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type)); q_data = get_q_data(ctx, vb->vb2_queue->type); if (!q_data) return -EINVAL; - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - dprintk(ctx->dev, 1, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - vb2_set_plane_payload(vb, 0, q_data->sizeimage); + for (p = 0; p < q_data->num_mem_planes; p++) { + if (vb2_plane_size(vb, p) < q_data->sizeimage[p]) { + dprintk(ctx->dev, 1, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, p), + (long)q_data->sizeimage[p]); + return -EINVAL; + } + vb2_set_plane_payload(vb, p, q_data->sizeimage[p]); + } return 0; } @@ -1100,7 +1321,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vim2m_ctx *ctx = priv; int ret; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1114,7 +1336,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, if (ret) return ret; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1188,10 +1411,11 @@ static int vim2m_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; ctx->q_data[V4L2_M2M_SRC].width = 640; ctx->q_data[V4L2_M2M_SRC].height = 480; - ctx->q_data[V4L2_M2M_SRC].sizeimage = + ctx->q_data[V4L2_M2M_SRC].sizeimage[0] = ctx->q_data[V4L2_M2M_SRC].width * ctx->q_data[V4L2_M2M_SRC].height * (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); + ctx->q_data[V4L2_M2M_SRC].num_mem_planes = 1; ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->colorspace = V4L2_COLORSPACE_REC709; @@ -1268,7 +1492,7 @@ static const struct video_device vim2m_videodev = { .ioctl_ops = &vim2m_ioctl_ops, .minor = -1, .release = vim2m_device_release, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, + .device_caps = V4L2_CAP_STREAMING, }; static const struct v4l2_m2m_ops m2m_ops = { @@ -1299,10 +1523,14 @@ static int vim2m_probe(struct platform_device *pdev) atomic_set(&dev->num_inst, 0); mutex_init(&dev->dev_mutex); + dev->multiplanar = (multiplanar == 2); + dev->vfd = vim2m_videodev; vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; + vfd->device_caps |= (dev->multiplanar) ? V4L2_CAP_VIDEO_M2M_MPLANE : + V4L2_CAP_VIDEO_M2M; video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); -- 2.51.0 From 728c0d50994764a3783ce4993b54e4e2855aab3b Mon Sep 17 00:00:00 2001 From: Aakarsh Jain Date: Wed, 5 Mar 2025 11:23:08 +0530 Subject: [PATCH 11/16] media: s5p-mfc: Support for handling RET_ENC_BUFFER_FULL interrupt When output encoded buffer size provided by userspace is insufficient with current encoding parameters, it leads to RET_ENC_BUFFER_FULL interrupt which was not handled in IRQ handler. On handling of RET_ENC_BUFFER_FULL interrupt leads to NAL_ABORT command from host to risc which in turn leads to RET_NAL_ABORT interrupt. On receiving RET_NAL_ABORT driver clears workbit and VB2 queues for cleaner closing of MFC instance. When user encounters "Call on DQBUF after unrecoverable error", userspace should close fd and restart with larger output encoder buffer size. Signed-off-by: Aakarsh Jain Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil --- .../media/platform/samsung/s5p-mfc/regs-mfc-v6.h | 1 + drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c | 14 ++++++++++++++ .../platform/samsung/s5p-mfc/s5p_mfc_common.h | 1 + .../platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c | 5 +++++ 4 files changed, 21 insertions(+) diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h index fa49fe580e1a..075a58b50b8c 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h @@ -45,6 +45,7 @@ #define S5P_FIMV_H2R_CMD_WAKEUP_V6 8 #define S5P_FIMV_CH_LAST_FRAME_V6 9 #define S5P_FIMV_H2R_CMD_FLUSH_V6 10 +#define S5P_FIMV_H2R_CMD_NAL_ABORT_V6 11 /* RMVME: REALLOC used? */ #define S5P_FIMV_CH_FRAME_START_REALLOC_V6 5 diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index c8e0ee383af3..9f89bd2620c7 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -739,6 +739,20 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) ctx->state = MFCINST_RUNNING; goto irq_cleanup_hw; + case S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET: + ctx->state = MFCINST_NAL_ABORT; + s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + set_work_bit(ctx); + WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + break; + + case S5P_MFC_R2H_CMD_NAL_ABORT_RET: + ctx->state = MFCINST_ERROR; + s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst); + s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src); + goto irq_cleanup_hw; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h index 3cc2a4f5c40a..86c316c1ff8f 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h @@ -141,6 +141,7 @@ enum s5p_mfc_inst_state { MFCINST_RES_CHANGE_INIT, MFCINST_RES_CHANGE_FLUSH, MFCINST_RES_CHANGE_END, + MFCINST_NAL_ABORT, }; /* diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c index 0c636090d723..98f8292b3173 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c @@ -2229,6 +2229,11 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) case MFCINST_HEAD_PRODUCED: ret = s5p_mfc_run_init_enc_buffers(ctx); break; + case MFCINST_NAL_ABORT: + mfc_write(dev, ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, + dev, S5P_FIMV_H2R_CMD_NAL_ABORT_V6, NULL); + break; default: ret = -EAGAIN; } -- 2.51.0 From c1c01458af573a0c33058117fdaf62146031c0fa Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 9 Mar 2025 00:14:31 +0000 Subject: [PATCH 12/16] media: pvrusb2: Remove unused pvr2_std_create_enum pvr2_std_create_enum() has been unused since 2012's commit c0bb609fdc0b ("[media] pvrusb2: Get rid of obsolete code for video standard enumeration") Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/usb/pvrusb2/pvrusb2-std.c | 167 ------------------------ drivers/media/usb/pvrusb2/pvrusb2-std.h | 6 - 2 files changed, 173 deletions(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index e7ab41401577..81c994e62241 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -212,173 +212,6 @@ unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, } -// Template data for possible enumerated video standards. Here we group -// standards which share common frame rates and resolution. -static struct v4l2_standard generic_standards[] = { - { - .id = (TSTD_B|TSTD_B1| - TSTD_D|TSTD_D1| - TSTD_G| - TSTD_H| - TSTD_I| - TSTD_K|TSTD_K1| - TSTD_L| - V4L2_STD_SECAM_LC | - TSTD_N|TSTD_Nc), - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = (TSTD_M| - V4L2_STD_NTSC_M_JP| - V4L2_STD_NTSC_M_KR), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is a total wild guess - .id = (TSTD_60), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is total wild guess - .id = V4L2_STD_NTSC_443, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - -static struct v4l2_standard *match_std(v4l2_std_id id) -{ - unsigned int idx; - for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { - if (generic_standards[idx].id & id) { - return generic_standards + idx; - } - } - return NULL; -} - -static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) -{ - struct v4l2_standard *template; - int idx; - unsigned int bcnt; - template = match_std(id); - if (!template) return 0; - idx = std->index; - memcpy(std,template,sizeof(*template)); - std->index = idx; - std->id = id; - bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); - std->name[bcnt] = 0; - pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s", - std->index,std->name); - return !0; -} - -/* These are special cases of combined standards that we should enumerate - separately if the component pieces are present. */ -static v4l2_std_id std_mixes[] = { - V4L2_STD_PAL_B | V4L2_STD_PAL_G, - V4L2_STD_PAL_D | V4L2_STD_PAL_K, - V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, - V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, -}; - -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id) -{ - unsigned int std_cnt = 0; - unsigned int idx,bcnt,idx2; - v4l2_std_id idmsk,cmsk,fmsk; - struct v4l2_standard *stddefs; - - if (pvrusb2_debug & PVR2_TRACE_STD) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); - pvr2_trace( - PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", - (int)id,bcnt,buf); - } - - *countptr = 0; - std_cnt = 0; - fmsk = 0; - for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (match_std(idmsk)) { - std_cnt++; - continue; - } - fmsk |= idmsk; - } - - for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { - if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; - } - - /* Don't complain about ATSC standard values */ - fmsk &= ~CSTD_ATSC; - - if (fmsk) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "***WARNING*** Failed to classify the following standard(s): %.*s", - bcnt,buf); - } - - pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)", - std_cnt); - if (!std_cnt) return NULL; // paranoia - - stddefs = kcalloc(std_cnt, sizeof(struct v4l2_standard), - GFP_KERNEL); - if (!stddefs) - return NULL; - - for (idx = 0; idx < std_cnt; idx++) - stddefs[idx].index = idx; - - idx = 0; - - /* Enumerate potential special cases */ - for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); - idx2++) { - if (!(id & std_mixes[idx2])) continue; - if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; - } - /* Now enumerate individual pieces */ - for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; - idx++; - } - - *countptr = std_cnt; - return stddefs; -} - v4l2_std_id pvr2_std_get_usable(void) { return CSTD_ALL; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h index d8b4c6dc72fe..74b05ecb9708 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h @@ -23,12 +23,6 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, v4l2_std_id id); -// Create an array of suitable v4l2_standard structures given a bit mask of -// video standards to support. The array is allocated from the heap, and -// the number of elements is returned in the first argument. -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id); - // Return mask of which video standard bits are valid v4l2_std_id pvr2_std_get_usable(void); -- 2.51.0 From 1d5f88f053480326873115092bc116b7d14916ba Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 11 Mar 2025 15:20:14 +0800 Subject: [PATCH 13/16] media: vidtv: Terminating the subsequent process of initialization failure syzbot reported a slab-use-after-free Read in vidtv_mux_init. [1] After PSI initialization fails, the si member is accessed again, resulting in this uaf. After si initialization fails, the subsequent process needs to be exited. [1] BUG: KASAN: slab-use-after-free in vidtv_mux_pid_ctx_init drivers/media/test-drivers/vidtv/vidtv_mux.c:78 [inline] BUG: KASAN: slab-use-after-free in vidtv_mux_init+0xac2/0xbe0 drivers/media/test-drivers/vidtv/vidtv_mux.c:524 Read of size 8 at addr ffff88802fa42acc by task syz.2.37/6059 CPU: 0 UID: 0 PID: 6059 Comm: syz.2.37 Not tainted 6.14.0-rc5-syzkaller #0 Hardware name: Google Compute Engine, BIOS Google 02/12/2025 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:408 [inline] print_report+0xc3/0x670 mm/kasan/report.c:521 kasan_report+0xd9/0x110 mm/kasan/report.c:634 vidtv_mux_pid_ctx_init drivers/media/test-drivers/vidtv/vidtv_mux.c:78 vidtv_mux_init+0xac2/0xbe0 drivers/media/test-drivers/vidtv/vidtv_mux.c:524 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call+0x151f/0x1720 arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f871d58d169 Code: Unable to access opcode bytes at 0x7f871d58d13f. RSP: 002b:00007fff4b19a788 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f871d58d169 RDX: 0000000000000064 RSI: 0000000000000000 RDI: 0000000000000000 RBP: 00007fff4b19a7ec R08: 0000000b4b19a87f R09: 00000000000927c0 R10: 0000000000000001 R11: 0000000000000246 R12: 0000000000000003 R13: 00000000000927c0 R14: 000000000001d553 R15: 00007fff4b19a840 Allocated by task 6059: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 poison_kmalloc_redzone mm/kasan/common.c:377 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:394 kmalloc_noprof include/linux/slab.h:901 [inline] kzalloc_noprof include/linux/slab.h:1037 [inline] vidtv_psi_pat_table_init drivers/media/test-drivers/vidtv/vidtv_psi.c:970 vidtv_channel_si_init drivers/media/test-drivers/vidtv/vidtv_channel.c:423 vidtv_mux_init drivers/media/test-drivers/vidtv/vidtv_mux.c:519 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 6059: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:576 poison_slab_object mm/kasan/common.c:247 [inline] __kasan_slab_free+0x51/0x70 mm/kasan/common.c:264 kasan_slab_free include/linux/kasan.h:233 [inline] slab_free_hook mm/slub.c:2353 [inline] slab_free mm/slub.c:4609 [inline] kfree+0x2c4/0x4d0 mm/slub.c:4757 vidtv_channel_si_init drivers/media/test-drivers/vidtv/vidtv_channel.c:499 vidtv_mux_init drivers/media/test-drivers/vidtv/vidtv_mux.c:519 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 3be8037960bc ("media: vidtv: add error checks") Cc: stable@vger.kernel.org Reported-by: syzbot+0d33ab192bd50b6c91e6@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0d33ab192bd50b6c91e6 Signed-off-by: Edward Adam Davis Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c index 7838e6272712..f3023e91b3eb 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_channel.c +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c @@ -497,7 +497,7 @@ free_sdt: vidtv_psi_sdt_table_destroy(m->si.sdt); free_pat: vidtv_psi_pat_table_destroy(m->si.pat); - return 0; + return -EINVAL; } void vidtv_channel_si_destroy(struct vidtv_mux *m) -- 2.51.0 From 1a27fce0fa79ef32c37f7e1b2d49357da7f2e2b2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20T=C5=AFma?= Date: Tue, 11 Mar 2025 18:31:41 +0100 Subject: [PATCH 14/16] docs: media: mgb4: Improve mgb4 driver documentation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add some basic info about the HW/driver + contact info. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/mgb4.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/admin-guide/media/mgb4.rst b/Documentation/admin-guide/media/mgb4.rst index f69d331e3cb1..5ac69b833a7a 100644 --- a/Documentation/admin-guide/media/mgb4.rst +++ b/Documentation/admin-guide/media/mgb4.rst @@ -1,8 +1,17 @@ .. SPDX-License-Identifier: GPL-2.0 +.. include:: + The mgb4 driver =============== +Copyright |copy| 2023 - 2025 Digiteq Automotive + author: Martin Tůma + +This is a v4l2 device driver for the Digiteq Automotive FrameGrabber 4, a PCIe +card capable of capturing and generating FPD-Link III and GMSL2/3 video streams +as used in the automotive industry. + sysfs interface --------------- -- 2.51.0 From ca7af8040ed1a2a034d6fecd8a8c0ddbf819a1bf Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 14 Mar 2025 12:39:40 +0000 Subject: [PATCH 15/16] media: vivid: Fix requirement about webcam_intervals Since commit f0b4a2c037c0 ("media: vivid: Extend FPS rates offered by simulated webcam") we do not require twice as many intervals as sizes. In fact, now we have 13 intervals and 6 sizes. Fix the comment. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b166d90177c6..623ba1e5e547 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -33,8 +33,7 @@ static const struct v4l2_frmsize_discrete webcam_sizes[] = { }; /* - * Intervals must be in increasing order and there must be twice as many - * elements in this array as there are in webcam_sizes. + * Intervals must be in increasing order. */ static const struct v4l2_fract webcam_intervals[] = { { 1, 1 }, -- 2.51.0 From 7ca9a4d9bdc30e148d3096db23e689ffe4f548c1 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 14 Mar 2025 12:39:41 +0000 Subject: [PATCH 16/16] media: vivid: Add more webcam resolutions Add 3 more common resolution for webcams. This is required to increase the test coverage of unit tests based on vivid. Co-developed-by: Hidenori Kobayashi Signed-off-by: Hidenori Kobayashi Signed-off-by: Ricardo Ribalda Tested-by: Hidenori Kobayashi # v4l2-compliance, Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 623ba1e5e547..df726961222b 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -25,9 +25,12 @@ /* Sizes must be in increasing order */ static const struct v4l2_frmsize_discrete webcam_sizes[] = { { 320, 180 }, + { 320, 240 }, { 640, 360 }, { 640, 480 }, { 1280, 720 }, + { 1280, 960 }, + { 1600, 1200 }, { 1920, 1080 }, { 3840, 2160 }, }; -- 2.51.0