diff mbox series

[PATCHv2] media: v4l2-ctrls: add support for fraction_bits

Message ID 69b1537c-73cb-4913-a6f4-b4ac3b50588d@xs4all.nl
State New
Headers show
Series [PATCHv2] media: v4l2-ctrls: add support for fraction_bits | expand

Commit Message

Hans Verkuil Nov. 21, 2023, 7:49 a.m. UTC
This adds support for the fraction_bits field, used with integer controls.
This allows fixed point formats to be described.

The fraction_bits field is only exposed through VIDIOC_QUERY_EXT_CTRL.

For a given signed two's complement Qf fixed point value 'f' equals
fraction_bits.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Changes in v2: use div64_s64

This is the cleaned up version of my earlier RFC patch.

Shengjiu, for your next patch series just use this patch.

Note that I dropped the inline helpers in videodev2.h, but I am not sure
if there shouldn't still be a helper added to include/media/v4l2-ctrls.h
in order to fill in a fixed point value.

Regards,

	Hans
---
 .../media/v4l/vidioc-queryctrl.rst            | 11 ++-
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  1 +
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 93 +++++++++++++++----
 include/media/v4l2-ctrls.h                    |  7 +-
 include/uapi/linux/videodev2.h                |  3 +-
 5 files changed, 95 insertions(+), 20 deletions(-)

Comments

Shengjiu Wang Jan. 22, 2024, 2:35 a.m. UTC | #1
> 
> This adds support for the fraction_bits field, used with integer controls.
> This allows fixed point formats to be described.
> 
> The fraction_bits field is only exposed through VIDIOC_QUERY_EXT_CTRL.
> 
> For a given signed two's complement Qf fixed point value 'f' equals
> fraction_bits.
> 
> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

Tested-by: Shengjiu Wang <shengjiu.wang@nxp.com>

Best regards
Wang Shengjiu

> ---
> Changes in v2: use div64_s64
> 
> This is the cleaned up version of my earlier RFC patch.
> 
> Shengjiu, for your next patch series just use this patch.
> 
> Note that I dropped the inline helpers in videodev2.h, but I am not sure if
> there shouldn't still be a helper added to include/media/v4l2-ctrls.h in order
> to fill in a fixed point value.
> 
> Regards,
> 
>         Hans
> ---
>  .../media/v4l/vidioc-queryctrl.rst            | 11 ++-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  1 +
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 93 +++++++++++++++----
>  include/media/v4l2-ctrls.h                    |  7 +-
>  include/uapi/linux/videodev2.h                |  3 +-
>  5 files changed, 95 insertions(+), 20 deletions(-)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 4d38acafe8e1..e65c7e5d78ec 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -267,7 +267,16 @@ See also the examples in :ref:`control`.
>        - The size of each dimension. The first ``nr_of_dims`` elements of
>         this array must be non-zero, all remaining elements must be zero.
>      * - __u32
> -      - ``reserved``\ [32]
> +      - ``fraction_bits``
> +      - The number of least significant bits of the control value that
> +        form the fraction of the fixed point value. This is 0 if the value
> +       is a regular integer. This can be used with all integer control types
> +       (``INTEGER``, ``INTEGER64``, ``U8``, ``U16`` and ``U32``).
> +       For the signed types the signed two's complement representation is
> used.
> +       This field applies to the control value as well as the ``minimum``,
> +       ``maximum``, ``step`` and ``default_value`` fields.
> +    * - __u32
> +      - ``reserved``\ [31]
>        - Reserved for future extensions. Applications and drivers must set
>         the array to zero.
> 
> 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..b86ceaaacbd7 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -252,12 +252,61 @@ 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, f, mask;
> +
> +       if (!fraction_bits) {
> +               pr_cont("%lld", v);
> +               return;
> +       }
> +
> +       mask = (1ULL << fraction_bits) - 1;
> +
> +       /*
> +        * Note: this function does not support fixed point u64 with
> +        * fraction_bits set to 64. At the moment there is no U64
> +        * control type, but if that is added, then this code will have
> +        * to add support for it.
> +        */
> +       if (fraction_bits >= 63)
> +               i = v < 0 ? -1 : 0;
> +       else
> +               i = div64_s64(v, 1LL << fraction_bits);
> +
> +       f = v < 0 ? -((-v) & mask) : (v & mask);
> +
> +       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 +315,7 @@ 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);
> +               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 +330,19
> @@ 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);
> +               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);
> +               v4l2_ctrl_log_fp(*ptr.p_u8, ctrl->fraction_bits);
>                 break;
>         case V4L2_CTRL_TYPE_U16:
> -               pr_cont("%u", (unsigned)*ptr.p_u16);
> +               v4l2_ctrl_log_fp(*ptr.p_u16, ctrl->fraction_bits);
>                 break;
>         case V4L2_CTRL_TYPE_U32:
> -               pr_cont("%u", (unsigned)*ptr.p_u32);
> +               v4l2_ctrl_log_fp(*ptr.p_u32, ctrl->fraction_bits);
>                 break;
>         case V4L2_CTRL_TYPE_H264_SPS:
>                 pr_cont("H264_SPS");
> @@ -1752,11 +1801,12 @@ 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)
>  {
>         struct v4l2_ctrl *ctrl;
> +       unsigned int max_fraction_bits = 0;
>         unsigned sz_extra;
>         unsigned nr_of_dims = 0;
>         unsigned elems = 1;
> @@ -1778,20 +1828,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl,
> 
>         /* Prefill elem_size for all types handled by std_type_ops */
>         switch ((u32)type) {
> +       case V4L2_CTRL_TYPE_INTEGER:
> +               elem_size = sizeof(s32);
> +               max_fraction_bits = 31;
> +               break;
>         case V4L2_CTRL_TYPE_INTEGER64:
>                 elem_size = sizeof(s64);
> +               max_fraction_bits = 63;
>                 break;
>         case V4L2_CTRL_TYPE_STRING:
>                 elem_size = max + 1;
>                 break;
>         case V4L2_CTRL_TYPE_U8:
>                 elem_size = sizeof(u8);
> +               max_fraction_bits = 8;
>                 break;
>         case V4L2_CTRL_TYPE_U16:
>                 elem_size = sizeof(u16);
> +               max_fraction_bits = 16;
>                 break;
>         case V4L2_CTRL_TYPE_U32:
>                 elem_size = sizeof(u32);
> +               max_fraction_bits = 32;
>                 break;
>         case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
>                 elem_size = sizeof(struct v4l2_ctrl_mpeg2_sequence); @@ -1875,10
> +1933,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler
> *hdl,
>         }
> 
>         /* Sanity checks */
> -       if (id == 0 || name == NULL || !elem_size ||
> -           id >= V4L2_CID_PRIVATE_BASE ||
> -           (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
> -           (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {
> +       if (id == 0 || !name || !elem_size ||
> +           fraction_bits > max_fraction_bits || id >= V4L2_CID_PRIVATE_BASE ||
> +           (type == V4L2_CTRL_TYPE_MENU && !qmenu) ||
> +           (type == V4L2_CTRL_TYPE_INTEGER_MENU && !qmenu_int)) {
>                 handler_set_err(hdl, -ERANGE);
>                 return NULL;
>         }
> @@ -1939,6 +1997,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 +2096,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 +2121,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 +2154,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 +2186,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 +2208,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 +2232,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..3813212a5cd3 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1944,7 +1944,8 @@ struct v4l2_query_ext_ctrl {
>         __u32                elems;
>         __u32                nr_of_dims;
>         __u32                dims[V4L2_CTRL_MAX_DIMS];
> -       __u32                reserved[32];
> +       __u32                fraction_bits;
> +       __u32                reserved[31];
>  };
> 
>  /*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
> --
> 2.42.0
diff mbox series

Patch

diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 4d38acafe8e1..e65c7e5d78ec 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -267,7 +267,16 @@  See also the examples in :ref:`control`.
       - The size of each dimension. The first ``nr_of_dims`` elements of
 	this array must be non-zero, all remaining elements must be zero.
     * - __u32
-      - ``reserved``\ [32]
+      - ``fraction_bits``
+      - The number of least significant bits of the control value that
+        form the fraction of the fixed point value. This is 0 if the value
+	is a regular integer. This can be used with all integer control types
+	(``INTEGER``, ``INTEGER64``, ``U8``, ``U16`` and ``U32``).
+	For the signed types the signed two's complement representation is used.
+	This field applies to the control value as well as the ``minimum``,
+	``maximum``, ``step`` and ``default_value`` fields.
+    * - __u32
+      - ``reserved``\ [31]
       - Reserved for future extensions. Applications and drivers must set
 	the array to zero.

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..b86ceaaacbd7 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -252,12 +252,61 @@  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, f, mask;
+
+	if (!fraction_bits) {
+		pr_cont("%lld", v);
+		return;
+	}
+
+	mask = (1ULL << fraction_bits) - 1;
+
+	/*
+	 * Note: this function does not support fixed point u64 with
+	 * fraction_bits set to 64. At the moment there is no U64
+	 * control type, but if that is added, then this code will have
+	 * to add support for it.
+	 */
+	if (fraction_bits >= 63)
+		i = v < 0 ? -1 : 0;
+	else
+		i = div64_s64(v, 1LL << fraction_bits);
+
+	f = v < 0 ? -((-v) & mask) : (v & mask);
+
+	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 +315,7 @@  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);
+		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 +330,19 @@  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);
+		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);
+		v4l2_ctrl_log_fp(*ptr.p_u8, ctrl->fraction_bits);
 		break;
 	case V4L2_CTRL_TYPE_U16:
-		pr_cont("%u", (unsigned)*ptr.p_u16);
+		v4l2_ctrl_log_fp(*ptr.p_u16, ctrl->fraction_bits);
 		break;
 	case V4L2_CTRL_TYPE_U32:
-		pr_cont("%u", (unsigned)*ptr.p_u32);
+		v4l2_ctrl_log_fp(*ptr.p_u32, ctrl->fraction_bits);
 		break;
 	case V4L2_CTRL_TYPE_H264_SPS:
 		pr_cont("H264_SPS");
@@ -1752,11 +1801,12 @@  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)
 {
 	struct v4l2_ctrl *ctrl;
+	unsigned int max_fraction_bits = 0;
 	unsigned sz_extra;
 	unsigned nr_of_dims = 0;
 	unsigned elems = 1;
@@ -1778,20 +1828,28 @@  static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,

 	/* Prefill elem_size for all types handled by std_type_ops */
 	switch ((u32)type) {
+	case V4L2_CTRL_TYPE_INTEGER:
+		elem_size = sizeof(s32);
+		max_fraction_bits = 31;
+		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
 		elem_size = sizeof(s64);
+		max_fraction_bits = 63;
 		break;
 	case V4L2_CTRL_TYPE_STRING:
 		elem_size = max + 1;
 		break;
 	case V4L2_CTRL_TYPE_U8:
 		elem_size = sizeof(u8);
+		max_fraction_bits = 8;
 		break;
 	case V4L2_CTRL_TYPE_U16:
 		elem_size = sizeof(u16);
+		max_fraction_bits = 16;
 		break;
 	case V4L2_CTRL_TYPE_U32:
 		elem_size = sizeof(u32);
+		max_fraction_bits = 32;
 		break;
 	case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
 		elem_size = sizeof(struct v4l2_ctrl_mpeg2_sequence);
@@ -1875,10 +1933,10 @@  static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	}

 	/* Sanity checks */
-	if (id == 0 || name == NULL || !elem_size ||
-	    id >= V4L2_CID_PRIVATE_BASE ||
-	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
-	    (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {
+	if (id == 0 || !name || !elem_size ||
+	    fraction_bits > max_fraction_bits || id >= V4L2_CID_PRIVATE_BASE ||
+	    (type == V4L2_CTRL_TYPE_MENU && !qmenu) ||
+	    (type == V4L2_CTRL_TYPE_INTEGER_MENU && !qmenu_int)) {
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
@@ -1939,6 +1997,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 +2096,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 +2121,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 +2154,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 +2186,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 +2208,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 +2232,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..3813212a5cd3 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1944,7 +1944,8 @@  struct v4l2_query_ext_ctrl {
 	__u32                elems;
 	__u32                nr_of_dims;
 	__u32                dims[V4L2_CTRL_MAX_DIMS];
-	__u32		     reserved[32];
+	__u32                fraction_bits;
+	__u32		     reserved[31];
 };

 /*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */