Skip to content

Implement video control framework #82158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

ngphibang
Copy link
Collaborator

@ngphibang ngphibang commented Nov 27, 2024

The control APIs seems simple enough, but quickly becomes very hard to implement correctly in drivers. Also, much of the code needed to handle controls is actually not driver specific and can be moved to the video core framework.

This PR implements the video control framework which not only aims to make life as easy as possible for the driver developers but also to enable handling of different types of controls which actually, cannot be handled by the current APIs.

Note that the implementation relies on the presence of a struct video_device which will be also served as a basis for futher improvements of the video framework in the future such as pipeline management, buffer management, m2m support, etc.

All the implementation happens inside the framework and for drivers-only usage, no public header added.

Currently, the set/get_ctrl() APIs in the video framework are defined as below:

static inline int video_get/set_ctrl(const struct device *dev, unsigned int cid, void *value)
{
	const struct video_driver_api *api = (const struct video_driver_api *)dev->api;

	if (api->get/set_ctrl == NULL) {
		return -ENOSYS;
	}

	return api->get/set_ctrl(dev, cid, value);
}

It is also the common pattern for other video APIs (e.g. get/set_format) as well as other subsystems (e.g. display), i.e. the subsystems just define the APIs but do nothing, all the work are handled by the drivers themselves.

Apart from this, a problem with this API is that, it makes all drivers in a pipeline propagate the get_ctrl() from root to sensor. At the sensor, the control value is obtained by reading directly the registers. While this works for some "volatile" controls like gain, exposure, in most of the cases, reading control value directly from registers are not only inefficient but also sometimes impossible, e.g. controls that need "complex" computation and "scatter" through several registers, e.g. hue control in ov5640 driver. That's why controls need to be cached in memory.

Tested on

  • ov5640 -> mipicsi2rx -> csi (i.MX RT1170)
  • mt9m114 -> csi (i.MX RT1064)
  • video sw generator

Features list

  • Add get_volatile_ctrl() API for controls that needs to be read freshly from hardware e.g. gain, exposure (in some contexts).
  • Add control cluster/auto-cluster (group of dependent controls represented by a master control)
  • Add query_ctrl() API to query min, max, default value of a control
  • Add (more) supports for controls of menu type
  • Add correct support for controls of int64 type (pixel rate)
main: - Supported controls:
                       Brightness 0x00980900 (int)    (flags=0x00) : min=-15 max=15 step=1 default=0 value=0 
                         Contrast 0x00980901 (int)    (flags=0x00) : min=0 max=255 step=1 default=0 value=0 
                       Saturation 0x00980902 (int)    (flags=0x00) : min=0 max=255 step=1 default=64 value=64 
                              Hue 0x00980903 (int)    (flags=0x00) : min=0 max=359 step=1 default=0 value=0 
                  Gain, Automatic 0x00980912 (int)    (flags=0x10) : min=0 max=1 step=1 default=1 value=1 
                  Horizontal Flip 0x00980914 (bool)   (flags=0x00) : min=0 max=1 step=1 default=0 value=0 
                    Vertical Flip 0x00980915 (bool)   (flags=0x00) : min=0 max=1 step=1 default=0 value=0 
             Power Line Frequency 0x00980918 (menu)   (flags=0x00) : min=0 max=3 step=1 default=1 value=1 
                                  0: Disabled
                                  1: 50 Hz
                                  2: 60 Hz
                                  3: Auto
                    Analogue Gain 0x009e0903 (int)    (flags=0x0c) : min=0 max=1023 step=1 default=0 value=16 
                       Pixel Rate 0x009f0902 (int64)  (flags=0x01) : min=24000000 max=96000000 step=1 default=48000000 value=48000000 
                     Test Pattern 0x009f0903 (menu)   (flags=0x00) : min=0 max=4 step=1 default=0 value=0 
                                  0: Disabled
                                  1: Color bars
                                  2: Color bars with rolling bar
                                  3: Color squares
                                  4: Color squares with rolling bar

@decsny
Copy link
Member

decsny commented Nov 27, 2024

Is this going to be tested?

int video_ctrl_init(struct video_ctrl *ctrl, struct video_ctrl_handler *hdl, uint32_t id,
int32_t min, int32_t max, uint32_t step, int32_t def);

struct video_ctrl *video_ctrl_find(struct video_ctrl_handler *hdl, uint32_t id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we return the struct by pointer instead?

@josuah josuah added Enhancement Changes/Updates/Additions to existing features RFC Request For Comments: want input from the community Release Notes Required Release notes required for this change labels Nov 27, 2024
@josuah
Copy link
Collaborator

josuah commented Nov 27, 2024

I like the direction it is going! Extending the video APIs is mandatory to support some hardware. It also could benefit from reusing mechanisms already present in Zephyr to make it fit more naturally (presentation below). Unless I missed some requirements or goals.

Here is how I understand it [EDIT: the JPEG encoder is a "memory-to-memory" type (m2m) and not part of the devicetree chain like it is suggested below]

new-video-framework-illustration

Currently every driver stores their const struct device *source_dev somewhere locally in their struct xxx_config without a standard way to access or declare these pointers.

AFAIU, your proposal is to register source_dev along with other related structs into iterable sections that can be looped through to navigate this graph of video devices at runtime:

video_async_init(&data->notifier, dev, &data->vdev, NULL, config->source_devs, 1);

Going from a struct device * to a struct video_notifier * requires looping through all the struct video_notifiers * registered as there is no standard offset for the struct video_notifier *.

It seems like grouping all of these related structures into just one struct video_data accessible like this would remove the need to have all these accessor, register, ... functions like what USB UDC drivers do:

struct video_data *data = dev->data;
struct xxx_data = data->priv;

new-video-framework-proposal

For the Network Ethernet drivers, it seems like being solved with helper functions:
https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/net/ethernet.h#L583-L584

struct video_data *data = get_video_data(dev);

It looked like this could avoid any iterable sections this way.

Was it something you considered and discarded the idea? If so, I'd be curious to learn why as maybe I missed something important in this.

I will continue the review when I get a chance. Thank you!

@josuah
Copy link
Collaborator

josuah commented Nov 27, 2024

Is this going to be tested?

There is a fake MIPI RX driver and fake imager made for this user-case of unit-testing API changes:

These are "fake" but actually produce frames, so can be tested in hardware too.

@ngphibang ngphibang changed the title Implement video control framework and more Implement video control framework Nov 28, 2024
@josuah
Copy link
Collaborator

josuah commented Nov 28, 2024

This PR could also provide some way to list all existing video devices via DEVICE_API_IS(video, dev):

@ngphibang
Copy link
Collaborator Author

This PR could also provide some way to list all existing video devices via DEVICE_API_IS(video, dev):

#82102
#71773

I will rebase this PR once the video API is moved to use DEVICE_API but I don't think this PR is related to that one.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 23 out of 26 changed files in this pull request and generated 2 comments.

Files not reviewed (3)
  • cmake/linker_script/common/common-ram.cmake: Language not supported
  • drivers/video/CMakeLists.txt: Language not supported
  • drivers/video/video.ld: Language not supported
Comments suppressed due to low confidence (1)

drivers/video/ov5640.c:1307

  • The initialization for the JPEG compression quality control is applied to the 'saturation' field instead of the dedicated 'jpeg' field defined in the control structure; update to use the correct field for clarity and functionality.
ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_JPEG_COMPRESSION_QUALITY, ...);

@ngphibang ngphibang dismissed stale reviews from mmahadevan108, loicpoulain, and josuah via 0fd7cc5 April 15, 2025 20:26
@ngphibang ngphibang force-pushed the implement_video_control_framework branch from 88bf76d to 0fd7cc5 Compare April 15, 2025 20:26
@github-actions github-actions bot added the Release Notes To be mentioned in the release notes label Apr 15, 2025
Copy link
Collaborator Author

@ngphibang ngphibang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force push:

  • Fixed copilot's founded issues.
  • Added some proposed comments to the code.
  • Added API changes to the release notes 4.2. I didn't think the 4.2 release note is already available :-).

@ngphibang ngphibang requested a review from kartben April 15, 2025 21:05
@kartben kartben dismissed their stale review April 15, 2025 23:14

Addressed - thanks!

Introduce a new video device structure representing a device in a
video pipeline. Each video device embeds a pointer to its "source"
device and other "video" characteristics.

This structure give the video framework an access to all video features
of the device and a hierachical view of the video pipeline that the
device belongs to.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Implement the video control framework with the following features:

- Drivers initialize the control with a valid value range at boot which
  guides the application developer and the framework. Hence, the video
  framework could do all common works for drivers e.g., sanity check.

- Controls need to be cached to memory. Video framework handles
  get_ctrl(), drivers don't need to implement this API. It is because
  reading control value directly from registers are not only inefficient
  but also sometimes impossible, e.g. controls that scatter through
  several registers. Only "volatile" control needs to be updated at
  runtime.

- Only the devices (e.g., sensors) owning the controls need to
  implement set_ctrl(). Other devices of the pipeline do not need to
  propagate set_ctrl() and hence, do not need to implement this API

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Application can query information about a control given the control id,
the framework fill the rest of the structure. Application can also
enumerate all kinds of device's supported controls by iterating with
VIDEO_CTRL_FLAG_NEXT_CTRL on the same API.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Add supports for controls that need 64-bit integer such as pixel rate.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Add get_volatile_ctrl() driver's API to retrieve the current value of a
control marked as volatile, e.g. gain, exposure. This function triggers
a hardware register reading instead of returning a cached value to ensure
that users always get a fresh value which is constantly updated by the HW.

Note that the driver is responsible for marking a control as volatile by
setting VIDEO_CTRL_FLAG_VOLATILE when registering a control because not
all hardwares work the same way for the same control.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
For controls that are dependent from others, we need to "cluster" them.
Whenever one or more controls of the same cluster are set or gotten,
only the callback of the 1st control of the cluster, i.e. the master
control, is called. The master control is the one that represents the
whole cluster.

A common type of control cluster is "auto"-cluster, e.g. auto_gain/gain,
auto_exposure/exposure, auto_white_balance/red_balance/blue_balance,
etc. If the cluster is in automatic mode, then the manual controls are
marked inactive and volatile which are read via get_volatile_ctrl().
If the cluster is put in manual mode, then the manual controls should
become active again and the volatile flag is cleared.

Re-implement the ov5640's autogain/analogue_gain controls with the new
auto cluster mechanism so that it work correctly and fully.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Add support for controls of menu types, standard menu and drivers'
defined menu.

Rework the ov5640's test pattern and power line frequency controls using
this new support.

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Add changes related to the new video control framework

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
@ngphibang ngphibang force-pushed the implement_video_control_framework branch from 0fd7cc5 to 4c5e6be Compare April 18, 2025 08:24
@ngphibang
Copy link
Collaborator Author

Just rebased on the latest main as release-note-4.2 has evolved.

@ngphibang ngphibang requested review from josuah and loicpoulain April 18, 2025 08:25
@ngphibang
Copy link
Collaborator Author

@kartben @josuah @loicpoulain : Could you revisit this ? I don't know if it needs a re-approval ...
Thanks !

Copy link
Collaborator

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find more issues in it so far.

I feel like it will not be able to spot everything down the last small glitch by looking at the code, and testing it will help like it did for the zephyr,video-sw-generator. In which cases more bugfix PRs can be sent :)

  • The CI tested it (zephyr,video-emul-imager and zephyr,video-emul-rx) and no bug found.
  • The video shell PR tested it and no bug found.

@ngphibang
Copy link
Collaborator Author

I tested this on the video sw generator, mt9m114 and ov5640. The ov5640 currently has the most complete and complex control types, especially the menu control, clustered control. There are currently very few drivers implementing controls and amongst these drivers, very few control types are implemented, so not much things to test. I believe one of the reason is the lack of support in the framework.

Hope that having this will encourage people to implement more interesting controls in their drivers, like the one in #88825.

@kartben kartben merged commit a250e91 into zephyrproject-rtos:main Apr 21, 2025
24 checks passed
@ngphibang ngphibang deleted the implement_video_control_framework branch June 24, 2025 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Build System area: Samples Samples area: Shields Shields (add-on boards) area: Video Video subsystem Enhancement Changes/Updates/Additions to existing features platform: ESP32 Espressif ESP32 platform: NXP Drivers NXP Semiconductors, drivers platform: STM32 ST Micro STM32 Release Notes Required Release notes required for this change Release Notes To be mentioned in the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants