On 17/11/2023 14:07, Sakari Ailus wrote:
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 wonder if we could add a printk() format specifier for this. Doesn't need to be done right now though, just an idea.
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.
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@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
break; case V4L2_CTRL_TYPE_BOOLEAN: pr_cont("%s", *ptr.p_s32 ? "true" : "false");v4l2_ctrl_log_fp(*ptr.p_s32, ctrl->fraction_bits);
@@ -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
break; case V4L2_CTRL_TYPE_STRING: pr_cont("%s", ptr.p_char); break; case V4L2_CTRL_TYPE_U8:v4l2_ctrl_log_fp(*ptr.p_s64, ctrl->fraction_bits);
pr_cont("%u", (unsigned)*ptr.p_u8);
if (!ctrl->fraction_bits)
pr_cont("%u", (unsigned int)*ptr.p_u8);
else
break; case V4L2_CTRL_TYPE_U16:v4l2_ctrl_log_fp((unsigned int)*ptr.p_u8, ctrl->fraction_bits);
pr_cont("%u", (unsigned)*ptr.p_u16);
if (!ctrl->fraction_bits)
pr_cont("%u", (unsigned int)*ptr.p_u16);
else
break; case V4L2_CTRL_TYPE_U32:v4l2_ctrl_log_fp((unsigned int)*ptr.p_u16, ctrl->fraction_bits);
pr_cont("%u", (unsigned)*ptr.p_u32);
if (!ctrl->fraction_bits)
pr_cont("%u", (unsigned int)*ptr.p_u32);
else
break; case V4L2_CTRL_TYPE_H264_SPS: pr_cont("H264_SPS");v4l2_ctrl_log_fp((unsigned int)*ptr.p_u32, ctrl->fraction_bits);
@@ -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,
if (ctrl) ctrl->is_private = cfg->is_private;cfg->dims, cfg->elem_size, cfg->fraction_bits, flags, qmenu, qmenu_int, cfg->p_def, priv);
@@ -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;
u8 would suffice. Not that we'd be short of space but still...
- __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);
Why not just:
return v >> fraction_bits;
That works if v >= 0, but not for v < 0. Getting this right for negative fixed point values was rather tricky. Actually, it is still wrong if fraction_bits == 63. This works:
static inline long long v4l2_fp_integer(long long v, unsigned int fraction_bits) { if (fraction_bits >= 63) return v < 0 ? -1 : 0; return v / (1ULL << fraction_bits); }
I'd use macros so you could use whatever control types with this without casting. E.g.
#define V4L2_FP_INTEGER(v, fraction_bits) ((v) >> fraction_bits)
A more generic way to expose this could be to have base and exponent, the base being 2 in this case. Just an idea. This would of course be a little bit more difficult to use.
To be honest, I am not at all certain this should be in a public header. I am inclined to drop it, especially since in userspace you can just use floating point operations which makes working with fixed point a lot easier.
The code to extract the integer and fraction part is really only relevant when logging the fixed point value.
Regards,
Hans
+}
+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);
+}
/* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ struct v4l2_querymenu { __u32 id;