[PATCH v9 10/15] media: uapi: Add V4L2_CTRL_TYPE_FIXED_POINT
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri Nov 17 23:55:30 AEDT 2023
Hi Hans,
Thank you for the patch.
On Fri, Nov 17, 2023 at 01:07:44PM +0100, Hans Verkuil wrote:
> Here is an RFC patch adding support for 'fraction_bits'. It's lacking
> documentation, but it can be used for testing.
>
> It was rather a pain logging fixed point number in a reasonable format,
> but I think it is OK.
>
> In userspace (where you can use floating point) it is a lot easier:
>
> printf("%.*g\n", fraction_bits, (double)v * (1.0 / (1ULL << fraction_bits)));
>
> I decided to only expose fraction_bits in struct v4l2_query_ext_ctrl.
> I could add it to struct v4l2_queryctrl, but I did not think that was
> necessary. Other opinions are welcome.
Agreed.
> In the meantime, let me know if this works for your patch series. If it
> does, then I can clean this up.
>
> Regards,
>
> Hans
>
> Signed-off-by: Hans Verkuil <hverkuil-cisco at xs4all.nl>
> ---
> drivers/media/v4l2-core/v4l2-ctrls-api.c | 1 +
> drivers/media/v4l2-core/v4l2-ctrls-core.c | 72 +++++++++++++++++++----
> include/media/v4l2-ctrls.h | 7 ++-
> include/uapi/linux/videodev2.h | 20 ++++++-
> 4 files changed, 85 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index 002ea6588edf..3132df315b17 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -1101,6 +1101,7 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr
> qc->elems = ctrl->elems;
> qc->nr_of_dims = ctrl->nr_of_dims;
> memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0]));
> + qc->fraction_bits = ctrl->fraction_bits;
> qc->minimum = ctrl->minimum;
> qc->maximum = ctrl->maximum;
> qc->default_value = ctrl->default_value;
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index a662fb60f73f..0e08a371af5c 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -252,12 +252,42 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> }
> EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
>
> +static void v4l2_ctrl_log_fp(s64 v, unsigned int fraction_bits)
> +{
> + s64 i = v4l2_fp_integer(v, fraction_bits);
> + s64 f = v4l2_fp_fraction(v, fraction_bits);
> +
> + if (!f) {
> + pr_cont("%lld", i);
> + } else if (fraction_bits < 20) {
> + u64 div = 1ULL << fraction_bits;
> +
> + if (!i && f < 0)
> + pr_cont("-%lld/%llu", -f, div);
> + else if (!i)
> + pr_cont("%lld/%llu", f, div);
> + else if (i < 0 || f < 0)
> + pr_cont("-%lld-%llu/%llu", -i, -f, div);
> + else
> + pr_cont("%lld+%llu/%llu", i, f, div);
> + } else {
> + if (!i && f < 0)
> + pr_cont("-%lld/(2^%u)", -f, fraction_bits);
> + else if (!i)
> + pr_cont("%lld/(2^%u)", f, fraction_bits);
> + else if (i < 0 || f < 0)
> + pr_cont("-%lld-%llu/(2^%u)", -i, -f, fraction_bits);
> + else
> + pr_cont("%lld+%llu/(2^%u)", i, f, fraction_bits);
> + }
> +}
> +
> void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> {
> union v4l2_ctrl_ptr ptr = ctrl->p_cur;
>
> if (ctrl->is_array) {
> - unsigned i;
> + unsigned int i;
>
> for (i = 0; i < ctrl->nr_of_dims; i++)
> pr_cont("[%u]", ctrl->dims[i]);
> @@ -266,7 +296,10 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>
> switch (ctrl->type) {
> case V4L2_CTRL_TYPE_INTEGER:
> - pr_cont("%d", *ptr.p_s32);
> + if (!ctrl->fraction_bits)
> + pr_cont("%d", *ptr.p_s32);
> + else
> + v4l2_ctrl_log_fp(*ptr.p_s32, ctrl->fraction_bits);
> break;
> case V4L2_CTRL_TYPE_BOOLEAN:
> pr_cont("%s", *ptr.p_s32 ? "true" : "false");
> @@ -281,19 +314,31 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> pr_cont("0x%08x", *ptr.p_s32);
> break;
> case V4L2_CTRL_TYPE_INTEGER64:
> - pr_cont("%lld", *ptr.p_s64);
> + if (!ctrl->fraction_bits)
> + pr_cont("%lld", *ptr.p_s64);
> + else
> + v4l2_ctrl_log_fp(*ptr.p_s64, ctrl->fraction_bits);
> break;
> case V4L2_CTRL_TYPE_STRING:
> pr_cont("%s", ptr.p_char);
> break;
> case V4L2_CTRL_TYPE_U8:
> - pr_cont("%u", (unsigned)*ptr.p_u8);
> + if (!ctrl->fraction_bits)
> + pr_cont("%u", (unsigned int)*ptr.p_u8);
> + else
> + v4l2_ctrl_log_fp((unsigned int)*ptr.p_u8, ctrl->fraction_bits);
> break;
> case V4L2_CTRL_TYPE_U16:
> - pr_cont("%u", (unsigned)*ptr.p_u16);
> + if (!ctrl->fraction_bits)
> + pr_cont("%u", (unsigned int)*ptr.p_u16);
> + else
> + v4l2_ctrl_log_fp((unsigned int)*ptr.p_u16, ctrl->fraction_bits);
> break;
> case V4L2_CTRL_TYPE_U32:
> - pr_cont("%u", (unsigned)*ptr.p_u32);
> + if (!ctrl->fraction_bits)
> + pr_cont("%u", (unsigned int)*ptr.p_u32);
> + else
> + v4l2_ctrl_log_fp((unsigned int)*ptr.p_u32, ctrl->fraction_bits);
> break;
> case V4L2_CTRL_TYPE_H264_SPS:
> pr_cont("H264_SPS");
> @@ -1752,7 +1797,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> u32 id, const char *name, enum v4l2_ctrl_type type,
> s64 min, s64 max, u64 step, s64 def,
> const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
> - u32 flags, const char * const *qmenu,
> + u32 fraction_bits, u32 flags, const char * const *qmenu,
> const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> void *priv)
> {
> @@ -1939,6 +1984,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> ctrl->name = name;
> ctrl->type = type;
> ctrl->flags = flags;
> + ctrl->fraction_bits = fraction_bits;
> ctrl->minimum = min;
> ctrl->maximum = max;
> ctrl->step = step;
> @@ -2037,7 +2083,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
> ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name,
> type, min, max,
> is_menu ? cfg->menu_skip_mask : step, def,
> - cfg->dims, cfg->elem_size,
> + cfg->dims, cfg->elem_size, cfg->fraction_bits,
> flags, qmenu, qmenu_int, cfg->p_def, priv);
> if (ctrl)
> ctrl->is_private = cfg->is_private;
> @@ -2062,7 +2108,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
> return NULL;
> }
> return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> - min, max, step, def, NULL, 0,
> + min, max, step, def, NULL, 0, 0,
> flags, NULL, NULL, ptr_null, NULL);
> }
> EXPORT_SYMBOL(v4l2_ctrl_new_std);
> @@ -2095,7 +2141,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
> return NULL;
> }
> return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> - 0, max, mask, def, NULL, 0,
> + 0, max, mask, def, NULL, 0, 0,
> flags, qmenu, qmenu_int, ptr_null, NULL);
> }
> EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
> @@ -2127,7 +2173,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
> return NULL;
> }
> return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> - 0, max, mask, def, NULL, 0,
> + 0, max, mask, def, NULL, 0, 0,
> flags, qmenu, NULL, ptr_null, NULL);
>
> }
> @@ -2149,7 +2195,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> return NULL;
> }
> return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> - min, max, step, def, NULL, 0,
> + min, max, step, def, NULL, 0, 0,
> flags, NULL, NULL, p_def, NULL);
> }
> EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
> @@ -2173,7 +2219,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
> return NULL;
> }
> return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> - 0, max, 0, def, NULL, 0,
> + 0, max, 0, def, NULL, 0, 0,
> flags, NULL, qmenu_int, ptr_null, NULL);
> }
> EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 59679a42b3e7..c35514c5bf88 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -211,7 +211,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
> * except for dynamic arrays. In that case it is in the range of
> * 1 to @p_array_alloc_elems.
> * @dims: The size of each dimension.
> - * @nr_of_dims:The number of dimensions in @dims.
> + * @nr_of_dims: The number of dimensions in @dims.
> + * @fraction_bits: The number of fraction bits for fixed point values.
> * @menu_skip_mask: The control's skip mask for menu controls. This makes it
> * easy to skip menu items that are not valid. If bit X is set,
> * then menu item X is skipped. Of course, this only works for
> @@ -228,6 +229,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
> * :math:`ceil(\frac{maximum - minimum}{step}) + 1`.
> * Used only if the @type is %V4L2_CTRL_TYPE_INTEGER_MENU.
> * @flags: The control's flags.
> + * @fraction_bits: The number of fraction bits for fixed point values.
> * @priv: The control's private pointer. For use by the driver. It is
> * untouched by the control framework. Note that this pointer is
> * not freed when the control is deleted. Should this be needed
> @@ -286,6 +288,7 @@ struct v4l2_ctrl {
> u32 new_elems;
> u32 dims[V4L2_CTRL_MAX_DIMS];
> u32 nr_of_dims;
> + u32 fraction_bits;
> union {
> u64 step;
> u64 menu_skip_mask;
> @@ -426,6 +429,7 @@ struct v4l2_ctrl_handler {
> * @dims: The size of each dimension.
> * @elem_size: The size in bytes of the control.
> * @flags: The control's flags.
> + * @fraction_bits: The number of fraction bits for fixed point values.
> * @menu_skip_mask: The control's skip mask for menu controls. This makes it
> * easy to skip menu items that are not valid. If bit X is set,
> * then menu item X is skipped. Of course, this only works for
> @@ -455,6 +459,7 @@ struct v4l2_ctrl_config {
> u32 dims[V4L2_CTRL_MAX_DIMS];
> u32 elem_size;
> u32 flags;
> + u32 fraction_bits;
> u64 menu_skip_mask;
> const char * const *qmenu;
> const s64 *qmenu_int;
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index c3d4e490ce7c..26ecac19722a 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1944,9 +1944,27 @@ struct v4l2_query_ext_ctrl {
> __u32 elems;
> __u32 nr_of_dims;
> __u32 dims[V4L2_CTRL_MAX_DIMS];
> - __u32 reserved[32];
> + __u32 fraction_bits;
Thinking forward, what if the control uses a different type of
quantization, for instance if the control is exponential instead of
linear ? Is this something we want to plan for already (without
necessarily implementing it yet) ? For instance, the CCS spec defines a
linear gain model where the gain is expressed as
gain = (m0 * x + c0) / (m1 * x + c1)
where x is the control value, gain is the real gain, and m0, c0, m1 and
c1 are device-specific 16-bit integer constants (with the constraint
that one of m0 and m1 has to be zero, but not both).
There's also an exponential model in CCS, with
gain = linear_gain * 2 ^ exponential_gain
where linear_gain and exponential_gain are U8.8 values.
> + __u32 reserved[31];
> };
>
> +static inline __s64 v4l2_fp_compose(__s64 i, __s64 f, unsigned int fraction_bits)
> +{
> + return (i << fraction_bits) + f;
> +}
> +
> +static inline __s64 v4l2_fp_integer(__s64 v, unsigned int fraction_bits)
> +{
> + return v / (1LL << fraction_bits);
> +}
> +
> +static inline __s64 v4l2_fp_fraction(__s64 v, unsigned int fraction_bits)
> +{
> + __u64 mask = (1ULL << fraction_bits) - 1;
> +
> + return v < 0 ? -((-v) & mask) : (v & mask);
> +}
I woudln't add static inline functions to the UAPI. They cause licensing
issues, and increase the UAPI surface, thus making UAPI evolutions more
difficult.
> +
> /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
> struct v4l2_querymenu {
> __u32 id;
--
Regards,
Laurent Pinchart
More information about the Linuxppc-dev
mailing list