media: i2c: imx258: Implement HFLIP and VFLIP controls.
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Tue, 15 Jun 2021 17:29:52 +0000 (18:29 +0100)
committerPhil Elwell <8911409+pelwell@users.noreply.github.com>
Fri, 8 Apr 2022 13:40:20 +0000 (14:40 +0100)
The sensor supports H & V flips, so implement the relevant controls.
Note that the Bayer order changes with these flips, therefore
they set the V4L2_CTRL_FLAG_MODIFY_LAYOUT property.

As we now support flips, remove the restriction of the sensor only
probing if rotated 180 degrees, but do take that value and initialise
VFLIP and HFLIP based on it.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
drivers/media/i2c/imx258.c

index c150742..7e38744 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/pm_runtime.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
 #include <asm/unaligned.h>
 
 #define IMX258_REG_VALUE_08BIT         1
@@ -69,7 +70,8 @@
 
 /* Orientation */
 #define REG_MIRROR_FLIP_CONTROL                0x0101
-#define REG_CONFIG_MIRROR_FLIP         0x03
+#define REG_CONFIG_MIRROR_HFLIP                0x01
+#define REG_CONFIG_MIRROR_VFLIP                0x02
 #define REG_CONFIG_FLIP_TEST_PATTERN   0x02
 
 /* Input clock frequency in Hz */
@@ -504,6 +506,22 @@ static const struct imx258_reg mode_1048_780_regs[] = {
        { 0x0220, 0x00 },
 };
 
+/*
+ * The supported formats.
+ * This table MUST contain 4 entries per format, to cover the various flip
+ * combinations in the order
+ * - no flip
+ * - h flip
+ * - v flip
+ * - h&v flips
+ */
+static const u32 codes[] = {
+       /* 10-bit modes. */
+       MEDIA_BUS_FMT_SRGGB10_1X10,
+       MEDIA_BUS_FMT_SGRBG10_1X10,
+       MEDIA_BUS_FMT_SGBRG10_1X10,
+       MEDIA_BUS_FMT_SBGGR10_1X10
+};
 static const char * const imx258_test_pattern_menu[] = {
        "Disabled",
        "Solid Colour",
@@ -605,6 +623,8 @@ struct imx258 {
        struct v4l2_ctrl *vblank;
        struct v4l2_ctrl *hblank;
        struct v4l2_ctrl *exposure;
+       struct v4l2_ctrl *hflip;
+       struct v4l2_ctrl *vflip;
 
        /* Current mode */
        const struct imx258_mode *cur_mode;
@@ -700,16 +720,29 @@ static int imx258_write_regs(struct imx258 *imx258,
        return 0;
 }
 
+/* Get bayer order based on flip setting. */
+static u32 imx258_get_format_code(struct imx258 *imx258)
+{
+       unsigned int i;
+
+       lockdep_assert_held(&imx258->mutex);
+
+       i = (imx258->vflip->val ? 2 : 0) |
+           (imx258->hflip->val ? 1 : 0);
+
+       return codes[i];
+}
 /* Open sub-device */
 static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
+       struct imx258 *imx258 = to_imx258(sd);
        struct v4l2_mbus_framefmt *try_fmt =
                v4l2_subdev_get_try_format(sd, fh->state, 0);
 
        /* Initialize try_fmt */
        try_fmt->width = supported_modes[0].width;
        try_fmt->height = supported_modes[0].height;
-       try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+       try_fmt->code = imx258_get_format_code(imx258);
        try_fmt->field = V4L2_FIELD_NONE;
 
        return 0;
@@ -775,10 +808,6 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
                ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
                                IMX258_REG_VALUE_16BIT,
                                ctrl->val);
-               ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
-                               IMX258_REG_VALUE_08BIT,
-                               !ctrl->val ? REG_CONFIG_MIRROR_FLIP :
-                               REG_CONFIG_FLIP_TEST_PATTERN);
                break;
        case V4L2_CID_WIDE_DYNAMIC_RANGE:
                if (!ctrl->val) {
@@ -796,6 +825,15 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
                                               BIT(IMX258_HDR_RATIO_MAX));
                }
                break;
+       case V4L2_CID_VFLIP:
+       case V4L2_CID_HFLIP:
+               ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
+                                      IMX258_REG_VALUE_08BIT,
+                                      (imx258->hflip->val ?
+                                       REG_CONFIG_MIRROR_HFLIP : 0) |
+                                      (imx258->vflip->val ?
+                                       REG_CONFIG_MIRROR_VFLIP : 0));
+               break;
        default:
                dev_info(&client->dev,
                         "ctrl(id:0x%x,val:0x%x) is not handled\n",
@@ -817,11 +855,13 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
                                  struct v4l2_subdev_state *sd_state,
                                  struct v4l2_subdev_mbus_code_enum *code)
 {
-       /* Only one bayer order(GRBG) is supported */
+       struct imx258 *imx258 = to_imx258(sd);
+
+       /* Only one bayer format (10 bit) is supported */
        if (code->index > 0)
                return -EINVAL;
 
-       code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+       code->code = imx258_get_format_code(imx258);
 
        return 0;
 }
@@ -830,10 +870,11 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
                                  struct v4l2_subdev_state *sd_state,
                                  struct v4l2_subdev_frame_size_enum *fse)
 {
+       struct imx258 *imx258 = to_imx258(sd);
        if (fse->index >= ARRAY_SIZE(supported_modes))
                return -EINVAL;
 
-       if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+       if (fse->code != imx258_get_format_code(imx258))
                return -EINVAL;
 
        fse->min_width = supported_modes[fse->index].width;
@@ -844,12 +885,13 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
        return 0;
 }
 
-static void imx258_update_pad_format(const struct imx258_mode *mode,
+static void imx258_update_pad_format(struct imx258 *imx258,
+                                    const struct imx258_mode *mode,
                                     struct v4l2_subdev_format *fmt)
 {
        fmt->format.width = mode->width;
        fmt->format.height = mode->height;
-       fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+       fmt->format.code = imx258_get_format_code(imx258);
        fmt->format.field = V4L2_FIELD_NONE;
 }
 
@@ -862,7 +904,7 @@ static int __imx258_get_pad_format(struct imx258 *imx258,
                                                          sd_state,
                                                          fmt->pad);
        else
-               imx258_update_pad_format(imx258->cur_mode, fmt);
+               imx258_update_pad_format(imx258, imx258->cur_mode, fmt);
 
        return 0;
 }
@@ -896,13 +938,12 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
 
        mutex_lock(&imx258->mutex);
 
-       /* Only one raw bayer(GBRG) order is supported */
-       fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+       fmt->format.code = imx258_get_format_code(imx258);
 
        mode = v4l2_find_nearest_size(supported_modes,
                ARRAY_SIZE(supported_modes), width, height,
                fmt->format.width, fmt->format.height);
-       imx258_update_pad_format(mode, fmt);
+       imx258_update_pad_format(imx258, mode, fmt);
        if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
                framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
                *framefmt = fmt->format;
@@ -959,15 +1000,6 @@ static int imx258_start_streaming(struct imx258 *imx258)
                return ret;
        }
 
-       /* Set Orientation be 180 degree */
-       ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
-                              IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
-       if (ret) {
-               dev_err(&client->dev, "%s failed to set orientation\n",
-                       __func__);
-               return ret;
-       }
-
        /* Apply customized values from user */
        ret =  __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
        if (ret)
@@ -1142,6 +1174,7 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = {
 static int imx258_init_controls(struct imx258 *imx258)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+       struct v4l2_fwnode_device_properties props;
        struct v4l2_ctrl_handler *ctrl_hdlr;
        s64 vblank_def;
        s64 vblank_min;
@@ -1150,7 +1183,7 @@ static int imx258_init_controls(struct imx258 *imx258)
        int ret;
 
        ctrl_hdlr = &imx258->ctrl_handler;
-       ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+       ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
        if (ret)
                return ret;
 
@@ -1219,6 +1252,21 @@ static int imx258_init_controls(struct imx258 *imx258)
                                ARRAY_SIZE(imx258_test_pattern_menu) - 1,
                                0, 0, imx258_test_pattern_menu);
 
+       ret = v4l2_fwnode_device_parse(&client->dev, &props);
+       if (ret)
+               goto error;
+
+       imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
+                                         V4L2_CID_HFLIP, 0, 1, 1,
+                                         props.rotation == 180 ? 1 : 0);
+       if (imx258->hflip)
+               imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+       imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
+                                         V4L2_CID_VFLIP, 0, 1, 1,
+                                         props.rotation == 180 ? 1 : 0);
+       if (imx258->vflip)
+               imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
        if (ctrl_hdlr->error) {
                ret = ctrl_hdlr->error;
                dev_err(&client->dev, "%s control init failed (%d)\n",
@@ -1270,14 +1318,6 @@ static int imx258_probe(struct i2c_client *client)
                return -EINVAL;
        }
 
-       /*
-        * Check that the device is mounted upside down. The driver only
-        * supports a single pixel order right now.
-        */
-       ret = device_property_read_u32(&client->dev, "rotation", &val);
-       if (ret || val != 180)
-               return -EINVAL;
-
        /* Initialize subdev */
        v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);