libcamera: android: Add optional mirrored value for config
diff mbox series

Message ID 20250716204625.3221592-1-ballway@chromium.org
State New
Headers show
Series
  • libcamera: android: Add optional mirrored value for config
Related show

Commit Message

Allen Ballway July 16, 2025, 8:46 p.m. UTC
Adds an optional `mirrored` value to the HAL config, which is used to modify
the `orientation` of the camera config. No rotation is used, so it can
only set the orientation to `Orientation::Rotate0Mirror`.

This enables sensors which are incorrectly mirrored to be corrected.

Signed-off-by: Allen Ballway <ballway@chromium.org>
---
 src/android/camera_device.cpp     |  6 ++++++
 src/android/camera_device.h       |  1 +
 src/android/camera_hal_config.cpp | 14 ++++++++++++++
 src/android/camera_hal_config.h   |  1 +
 4 files changed, 22 insertions(+)

--
2.50.0.727.gbf7dc18ff4-goog

Comments

Kieran Bingham July 18, 2025, 12:11 p.m. UTC | #1
Quoting Allen Ballway (2025-07-16 21:46:20)
> Adds an optional `mirrored` value to the HAL config, which is used to modify
> the `orientation` of the camera config. No rotation is used, so it can
> only set the orientation to `Orientation::Rotate0Mirror`.
> 
> This enables sensors which are incorrectly mirrored to be corrected.
> 
> Signed-off-by: Allen Ballway <ballway@chromium.org>
> ---
>  src/android/camera_device.cpp     |  6 ++++++
>  src/android/camera_device.h       |  1 +
>  src/android/camera_hal_config.cpp | 14 ++++++++++++++
>  src/android/camera_hal_config.h   |  1 +
>  4 files changed, 22 insertions(+)
> 
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 80ff248c2..b20e9f3db 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
>                 orientation_ = 0;
>         }
> 
> +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
>         return capabilities_.initialize(camera_, orientation_, facing_);
>  }
> 
> @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
>                 return -EINVAL;
>         }
> 
> +       // Rotation is unsupported, so if mirrored just set orientation.

Is that true? I thought we usually support 0 and 180 rotations on most
sensors at least ?

We (almost) always use /*  */ comments in libcamera ... it's ... probably a
stylistic choice because we are mostly started out as C / Linux-kernel
devs.



> +       if (mirrored_) {
> +               config->orientation = Orientation::Rotate0Mirror;
> +       }
> +

How does this interact with rotation as well ? will it take precedence
against 180 rotations?

>         /*
>          * Clear and remove any existing configuration from previous calls, and
>          * ensure the required entries are available without further
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index 194ca3030..d304a1285 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -126,6 +126,7 @@ private:
> 
>         int facing_;
>         int orientation_;
> +       int mirrored_;
> 
>         CameraMetadata lastSettings_;
>  };
> diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> index 7ef451ef8..a2beba5d3 100644
> --- a/src/android/camera_hal_config.cpp
> +++ b/src/android/camera_hal_config.cpp
> @@ -33,6 +33,7 @@ private:
>         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
>         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
>         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> 
>         std::map<std::string, CameraConfigData> *cameras_;
>  };
> @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
>         if (parseRotation(cameraObject, cameraConfigData))
>                 return -EINVAL;
> 
> +       /* Parse optional property "mirrored" */
> +       parseMirrored(cameraObject, cameraConfigData);
> +
>         return 0;
>  }
> 
> @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
>         return 0;
>  }
> 
> +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> +                                           CameraConfigData &cameraConfigData)
> +{
> +       if (!cameraObject.contains("mirrored"))
> +               return -EINVAL;
> +
> +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> +       return 0;
> +}
> +
>  CameraHalConfig::CameraHalConfig()
>         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
>  {
> diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> index a4bedb6e6..a96190470 100644
> --- a/src/android/camera_hal_config.h
> +++ b/src/android/camera_hal_config.h
> @@ -15,6 +15,7 @@
>  struct CameraConfigData {
>         int facing = -1;
>         int rotation = -1;
> +       bool mirrored = false;
>  };
> 
>  class CameraHalConfig final : public libcamera::Extensible
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
Allen Ballway July 18, 2025, 3:27 p.m. UTC | #2
On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
>
> Quoting Allen Ballway (2025-07-16 21:46:20)
> > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > the `orientation` of the camera config. No rotation is used, so it can
> > only set the orientation to `Orientation::Rotate0Mirror`.
> >
> > This enables sensors which are incorrectly mirrored to be corrected.
> >
> > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > ---
> >  src/android/camera_device.cpp     |  6 ++++++
> >  src/android/camera_device.h       |  1 +
> >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> >  src/android/camera_hal_config.h   |  1 +
> >  4 files changed, 22 insertions(+)
> >
> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > index 80ff248c2..b20e9f3db 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> >                 orientation_ = 0;
> >         }
> >
> > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> >         return capabilities_.initialize(camera_, orientation_, facing_);
> >  }
> >
> > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> >                 return -EINVAL;
> >         }
> >
> > +       // Rotation is unsupported, so if mirrored just set orientation.
>
> Is that true? I thought we usually support 0 and 180 rotations on most
> sensors at least ?

Ah I misunderstood the below comments/errors about not supporting stream
rotations. It seems the sensors on my test device don't support rotation. I will
send out a v2 with this comment removed assuming the rest of this patch
looks good.
>
> We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> stylistic choice because we are mostly started out as C / Linux-kernel
> devs.
>
>
>
> > +       if (mirrored_) {
> > +               config->orientation = Orientation::Rotate0Mirror;
> > +       }
> > +
>
> How does this interact with rotation as well ? will it take precedence
> against 180 rotations?

I can't confirm because my sensors aren't affected by the rotation config
but as far as I can tell the rotation would be unaffected by setting the
orientation to mirror. There doesn't seem to be anywhere setting the
orientation in the Android layer, so any rotation would happen independently.
And the mirroring will just set the HFLIP on the kernel driver, so any higher
level rotation would just rotate the flipped output as expected.
>
> >         /*
> >          * Clear and remove any existing configuration from previous calls, and
> >          * ensure the required entries are available without further
> > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > index 194ca3030..d304a1285 100644
> > --- a/src/android/camera_device.h
> > +++ b/src/android/camera_device.h
> > @@ -126,6 +126,7 @@ private:
> >
> >         int facing_;
> >         int orientation_;
> > +       int mirrored_;
> >
> >         CameraMetadata lastSettings_;
> >  };
> > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > index 7ef451ef8..a2beba5d3 100644
> > --- a/src/android/camera_hal_config.cpp
> > +++ b/src/android/camera_hal_config.cpp
> > @@ -33,6 +33,7 @@ private:
> >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> >
> >         std::map<std::string, CameraConfigData> *cameras_;
> >  };
> > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> >         if (parseRotation(cameraObject, cameraConfigData))
> >                 return -EINVAL;
> >
> > +       /* Parse optional property "mirrored" */
> > +       parseMirrored(cameraObject, cameraConfigData);
> > +
> >         return 0;
> >  }
> >
> > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> >         return 0;
> >  }
> >
> > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > +                                           CameraConfigData &cameraConfigData)
> > +{
> > +       if (!cameraObject.contains("mirrored"))
> > +               return -EINVAL;
> > +
> > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > +       return 0;
> > +}
> > +
> >  CameraHalConfig::CameraHalConfig()
> >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> >  {
> > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > index a4bedb6e6..a96190470 100644
> > --- a/src/android/camera_hal_config.h
> > +++ b/src/android/camera_hal_config.h
> > @@ -15,6 +15,7 @@
> >  struct CameraConfigData {
> >         int facing = -1;
> >         int rotation = -1;
> > +       bool mirrored = false;
> >  };
> >
> >  class CameraHalConfig final : public libcamera::Extensible
> > --
> > 2.50.0.727.gbf7dc18ff4-goog
> >
Kieran Bingham July 21, 2025, 10:44 a.m. UTC | #3
Quoting Allen Ballway (2025-07-18 16:27:23)
> On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
> <kieran.bingham@ideasonboard.com> wrote:
> >
> > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > the `orientation` of the camera config. No rotation is used, so it can
> > > only set the orientation to `Orientation::Rotate0Mirror`.
> > >
> > > This enables sensors which are incorrectly mirrored to be corrected.
> > >
> > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > ---
> > >  src/android/camera_device.cpp     |  6 ++++++
> > >  src/android/camera_device.h       |  1 +
> > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > >  src/android/camera_hal_config.h   |  1 +
> > >  4 files changed, 22 insertions(+)
> > >
> > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > index 80ff248c2..b20e9f3db 100644
> > > --- a/src/android/camera_device.cpp
> > > +++ b/src/android/camera_device.cpp
> > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > >                 orientation_ = 0;
> > >         }
> > >
> > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > >  }
> > >
> > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > >                 return -EINVAL;
> > >         }
> > >
> > > +       // Rotation is unsupported, so if mirrored just set orientation.
> >
> > Is that true? I thought we usually support 0 and 180 rotations on most
> > sensors at least ?
> 
> Ah I misunderstood the below comments/errors about not supporting stream
> rotations. It seems the sensors on my test device don't support rotation. I will
> send out a v2 with this comment removed assuming the rest of this patch
> looks good.
> >
> > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > stylistic choice because we are mostly started out as C / Linux-kernel
> > devs.
> >
> >
> >
> > > +       if (mirrored_) {
> > > +               config->orientation = Orientation::Rotate0Mirror;
> > > +       }
> > > +
> >
> > How does this interact with rotation as well ? will it take precedence
> > against 180 rotations?
> 
> I can't confirm because my sensors aren't affected by the rotation config
> but as far as I can tell the rotation would be unaffected by setting the
> orientation to mirror. There doesn't seem to be anywhere setting the
> orientation in the Android layer, so any rotation would happen independently.
> And the mirroring will just set the HFLIP on the kernel driver, so any higher
> level rotation would just rotate the flipped output as expected.

Some level of rotation is supposed to be handled automatically by
setting the physical orientation and rotation in device tree.

Then libcamera will 'aim' for a 0 rotation by default.

I guess the issue here is that we don't have a way in
device-tree/firmware to mark that the camera is mirrored.

what does this actually 'mean' by the way? How is the camera physically
mirrored? Is this really a bug in the driver somewhere or something
else?

I am weary that this patch is possibly a workaround for a problem
somewhere else ?

--
Kieran

> >
> > >         /*
> > >          * Clear and remove any existing configuration from previous calls, and
> > >          * ensure the required entries are available without further
> > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > index 194ca3030..d304a1285 100644
> > > --- a/src/android/camera_device.h
> > > +++ b/src/android/camera_device.h
> > > @@ -126,6 +126,7 @@ private:
> > >
> > >         int facing_;
> > >         int orientation_;
> > > +       int mirrored_;
> > >
> > >         CameraMetadata lastSettings_;
> > >  };
> > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > index 7ef451ef8..a2beba5d3 100644
> > > --- a/src/android/camera_hal_config.cpp
> > > +++ b/src/android/camera_hal_config.cpp
> > > @@ -33,6 +33,7 @@ private:
> > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > >
> > >         std::map<std::string, CameraConfigData> *cameras_;
> > >  };
> > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > >         if (parseRotation(cameraObject, cameraConfigData))
> > >                 return -EINVAL;
> > >
> > > +       /* Parse optional property "mirrored" */
> > > +       parseMirrored(cameraObject, cameraConfigData);
> > > +
> > >         return 0;
> > >  }
> > >
> > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > >         return 0;
> > >  }
> > >
> > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > +                                           CameraConfigData &cameraConfigData)
> > > +{
> > > +       if (!cameraObject.contains("mirrored"))
> > > +               return -EINVAL;
> > > +
> > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > +       return 0;
> > > +}
> > > +
> > >  CameraHalConfig::CameraHalConfig()
> > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > >  {
> > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > index a4bedb6e6..a96190470 100644
> > > --- a/src/android/camera_hal_config.h
> > > +++ b/src/android/camera_hal_config.h
> > > @@ -15,6 +15,7 @@
> > >  struct CameraConfigData {
> > >         int facing = -1;
> > >         int rotation = -1;
> > > +       bool mirrored = false;
> > >  };
> > >
> > >  class CameraHalConfig final : public libcamera::Extensible
> > > --
> > > 2.50.0.727.gbf7dc18ff4-goog
> > >
Jacopo Mondi July 21, 2025, 11:14 a.m. UTC | #4
Hi Kieran

On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> Quoting Allen Ballway (2025-07-18 16:27:23)
> > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
> > <kieran.bingham@ideasonboard.com> wrote:
> > >
> > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > >
> > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > >
> > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > ---
> > > >  src/android/camera_device.cpp     |  6 ++++++
> > > >  src/android/camera_device.h       |  1 +
> > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > >  src/android/camera_hal_config.h   |  1 +
> > > >  4 files changed, 22 insertions(+)
> > > >
> > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > index 80ff248c2..b20e9f3db 100644
> > > > --- a/src/android/camera_device.cpp
> > > > +++ b/src/android/camera_device.cpp
> > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > >                 orientation_ = 0;
> > > >         }
> > > >
> > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > >  }
> > > >
> > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > >                 return -EINVAL;
> > > >         }
> > > >
> > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > >
> > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > sensors at least ?
> >
> > Ah I misunderstood the below comments/errors about not supporting stream
> > rotations. It seems the sensors on my test device don't support rotation. I will
> > send out a v2 with this comment removed assuming the rest of this patch
> > looks good.
> > >
> > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > devs.
> > >
> > >
> > >
> > > > +       if (mirrored_) {
> > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > +       }
> > > > +

Also no {} for single line statement.

It's amusing how many times the same stylistic comments had to
repeated specifically to chromeos developers. We also have tools
available for checking this kind of issues before submitting.

> > >
> > > How does this interact with rotation as well ? will it take precedence
> > > against 180 rotations?
> >
> > I can't confirm because my sensors aren't affected by the rotation config
> > but as far as I can tell the rotation would be unaffected by setting the
> > orientation to mirror. There doesn't seem to be anywhere setting the
> > orientation in the Android layer, so any rotation would happen independently.
> > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > level rotation would just rotate the flipped output as expected.
>
> Some level of rotation is supposed to be handled automatically by
> setting the physical orientation and rotation in device tree.
>
> Then libcamera will 'aim' for a 0 rotation by default.
>
> I guess the issue here is that we don't have a way in
> device-tree/firmware to mark that the camera is mirrored.
>
> what does this actually 'mean' by the way? How is the camera physically
> mirrored? Is this really a bug in the driver somewhere or something
> else?
>
> I am weary that this patch is possibly a workaround for a problem
> somewhere else ?

If my recollection is right, when it comes to the camera HAL:

1) Rotation is first parsed from DT, but there we can only express
   [0, 90, 270, 360] so we don't have a "mirror" option

2) If no "rotation" property is specified in DT, then the HAL
   falls-back  to parse a "rotation" property from configuration file,
   where again we can only express an angle between [0 - 360]

All of this seems to suggest it is not possible to express in DT or
HAL configuration file the "mirroring" option. However this opens the
question that Kieran has asked already. How is the camera module
physically mirrored ? is the pixel array reading direction inverted in
the driver ?

>
> --
> Kieran
>
> > >
> > > >         /*
> > > >          * Clear and remove any existing configuration from previous calls, and
> > > >          * ensure the required entries are available without further
> > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > index 194ca3030..d304a1285 100644
> > > > --- a/src/android/camera_device.h
> > > > +++ b/src/android/camera_device.h
> > > > @@ -126,6 +126,7 @@ private:
> > > >
> > > >         int facing_;
> > > >         int orientation_;
> > > > +       int mirrored_;
> > > >
> > > >         CameraMetadata lastSettings_;
> > > >  };
> > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > index 7ef451ef8..a2beba5d3 100644
> > > > --- a/src/android/camera_hal_config.cpp
> > > > +++ b/src/android/camera_hal_config.cpp
> > > > @@ -33,6 +33,7 @@ private:
> > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > >
> > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > >  };
> > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > >                 return -EINVAL;
> > > >
> > > > +       /* Parse optional property "mirrored" */
> > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > +
> > > >         return 0;
> > > >  }
> > > >
> > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > >         return 0;
> > > >  }
> > > >
> > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > +                                           CameraConfigData &cameraConfigData)
> > > > +{
> > > > +       if (!cameraObject.contains("mirrored"))
> > > > +               return -EINVAL;
> > > > +
> > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > +       return 0;
> > > > +}
> > > > +
> > > >  CameraHalConfig::CameraHalConfig()
> > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > >  {
> > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > index a4bedb6e6..a96190470 100644
> > > > --- a/src/android/camera_hal_config.h
> > > > +++ b/src/android/camera_hal_config.h
> > > > @@ -15,6 +15,7 @@
> > > >  struct CameraConfigData {
> > > >         int facing = -1;
> > > >         int rotation = -1;
> > > > +       bool mirrored = false;
> > > >  };
> > > >
> > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > --
> > > > 2.50.0.727.gbf7dc18ff4-goog
> > > >
Allen Ballway July 22, 2025, 2:51 p.m. UTC | #5
Hi all,

On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Kieran
>
> On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
> > > <kieran.bingham@ideasonboard.com> wrote:
> > > >
> > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > >
> > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > >
> > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > ---
> > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > >  src/android/camera_device.h       |  1 +
> > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > >  src/android/camera_hal_config.h   |  1 +
> > > > >  4 files changed, 22 insertions(+)
> > > > >
> > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > index 80ff248c2..b20e9f3db 100644
> > > > > --- a/src/android/camera_device.cpp
> > > > > +++ b/src/android/camera_device.cpp
> > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > >                 orientation_ = 0;
> > > > >         }
> > > > >
> > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > >  }
> > > > >
> > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > >                 return -EINVAL;
> > > > >         }
> > > > >
> > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > >
> > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > sensors at least ?
> > >
> > > Ah I misunderstood the below comments/errors about not supporting stream
> > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > send out a v2 with this comment removed assuming the rest of this patch
> > > looks good.
> > > >
> > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > devs.
> > > >
> > > >
> > > >
> > > > > +       if (mirrored_) {
> > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > +       }
> > > > > +
>
> Also no {} for single line statement.
>
> It's amusing how many times the same stylistic comments had to
> repeated specifically to chromeos developers. We also have tools
> available for checking this kind of issues before submitting.
>

Apologies, I ran checkstyle.py on the change but found no issues, if there
are other tools for finding style issues I'll be sure to run them in the future.
I'll update this one as well in v2.
> > > >
> > > > How does this interact with rotation as well ? will it take precedence
> > > > against 180 rotations?
> > >
> > > I can't confirm because my sensors aren't affected by the rotation config
> > > but as far as I can tell the rotation would be unaffected by setting the
> > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > orientation in the Android layer, so any rotation would happen independently.
> > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > level rotation would just rotate the flipped output as expected.
> >
> > Some level of rotation is supposed to be handled automatically by
> > setting the physical orientation and rotation in device tree.
> >
> > Then libcamera will 'aim' for a 0 rotation by default.
> >
> > I guess the issue here is that we don't have a way in
> > device-tree/firmware to mark that the camera is mirrored.
> >
> > what does this actually 'mean' by the way? How is the camera physically
> > mirrored? Is this really a bug in the driver somewhere or something
> > else?
> >
> > I am weary that this patch is possibly a workaround for a problem
> > somewhere else ?
>
> If my recollection is right, when it comes to the camera HAL:
>
> 1) Rotation is first parsed from DT, but there we can only express
>    [0, 90, 270, 360] so we don't have a "mirror" option
>
> 2) If no "rotation" property is specified in DT, then the HAL
>    falls-back  to parse a "rotation" property from configuration file,
>    where again we can only express an angle between [0 - 360]
>
> All of this seems to suggest it is not possible to express in DT or
> HAL configuration file the "mirroring" option. However this opens the
> question that Kieran has asked already. How is the camera module
> physically mirrored ? is the pixel array reading direction inverted in
> the driver ?
>

I'm not 100% sure where the mirroring is coming from. I confirmed it
through qcam on Ubuntu as well as through the camera app and Chromium
on a ChromiumOs build so it's not just an application bug. The location and
orientation coming from ACPI both seem to be correct. The driver seems
to be fine on all but some Surface Go devices so it seems unrelated.
Anything below that is infeasible to fix and the sensor drivers don't
maintain quirks for particular misbehaving devices, so the HAL seems
to me the best place to add a mirrored config and pipe it down to the
driver through the existing Orientation.

Thanks,
Allen
> >
> > --
> > Kieran
> >
> > > >
> > > > >         /*
> > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > >          * ensure the required entries are available without further
> > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > index 194ca3030..d304a1285 100644
> > > > > --- a/src/android/camera_device.h
> > > > > +++ b/src/android/camera_device.h
> > > > > @@ -126,6 +126,7 @@ private:
> > > > >
> > > > >         int facing_;
> > > > >         int orientation_;
> > > > > +       int mirrored_;
> > > > >
> > > > >         CameraMetadata lastSettings_;
> > > > >  };
> > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > --- a/src/android/camera_hal_config.cpp
> > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > @@ -33,6 +33,7 @@ private:
> > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > >
> > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > >  };
> > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > >                 return -EINVAL;
> > > > >
> > > > > +       /* Parse optional property "mirrored" */
> > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > +
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > +{
> > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > >  CameraHalConfig::CameraHalConfig()
> > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > >  {
> > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > index a4bedb6e6..a96190470 100644
> > > > > --- a/src/android/camera_hal_config.h
> > > > > +++ b/src/android/camera_hal_config.h
> > > > > @@ -15,6 +15,7 @@
> > > > >  struct CameraConfigData {
> > > > >         int facing = -1;
> > > > >         int rotation = -1;
> > > > > +       bool mirrored = false;
> > > > >  };
> > > > >
> > > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > > --
> > > > > 2.50.0.727.gbf7dc18ff4-goog
> > > > >
Jacopo Mondi July 23, 2025, 8:15 a.m. UTC | #6
Hi Allen

On Tue, Jul 22, 2025 at 07:51:53AM -0700, Allen Ballway wrote:
> Hi all,
>
> On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi Kieran
> >
> > On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
> > > > <kieran.bingham@ideasonboard.com> wrote:
> > > > >
> > > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > > >
> > > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > > >
> > > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > > ---
> > > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > > >  src/android/camera_device.h       |  1 +
> > > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > > >  src/android/camera_hal_config.h   |  1 +
> > > > > >  4 files changed, 22 insertions(+)
> > > > > >
> > > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > > index 80ff248c2..b20e9f3db 100644
> > > > > > --- a/src/android/camera_device.cpp
> > > > > > +++ b/src/android/camera_device.cpp
> > > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > > >                 orientation_ = 0;
> > > > > >         }
> > > > > >
> > > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > > >  }
> > > > > >
> > > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > > >                 return -EINVAL;
> > > > > >         }
> > > > > >
> > > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > > >
> > > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > > sensors at least ?
> > > >
> > > > Ah I misunderstood the below comments/errors about not supporting stream
> > > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > > send out a v2 with this comment removed assuming the rest of this patch
> > > > looks good.
> > > > >
> > > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > > devs.
> > > > >
> > > > >
> > > > >
> > > > > > +       if (mirrored_) {
> > > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > > +       }
> > > > > > +
> >
> > Also no {} for single line statement.
> >
> > It's amusing how many times the same stylistic comments had to
> > repeated specifically to chromeos developers. We also have tools
> > available for checking this kind of issues before submitting.
> >
>
> Apologies, I ran checkstyle.py on the change but found no issues, if there
> are other tools for finding style issues I'll be sure to run them in the future.
> I'll update this one as well in v2.

This is one of your first contributions, so it has been unfair ranting
to you about the past :)

I'm just suprised a team of specialized and highly skilled engineers
continue pushing their conventions (out of habit I presume) to a different
code base. My frustration comes from having repeated the same comments
over and over in the past years. Apologies if you got ranted to.

> > > > >
> > > > > How does this interact with rotation as well ? will it take precedence
> > > > > against 180 rotations?
> > > >
> > > > I can't confirm because my sensors aren't affected by the rotation config
> > > > but as far as I can tell the rotation would be unaffected by setting the
> > > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > > orientation in the Android layer, so any rotation would happen independently.
> > > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > > level rotation would just rotate the flipped output as expected.
> > >
> > > Some level of rotation is supposed to be handled automatically by
> > > setting the physical orientation and rotation in device tree.
> > >
> > > Then libcamera will 'aim' for a 0 rotation by default.
> > >
> > > I guess the issue here is that we don't have a way in
> > > device-tree/firmware to mark that the camera is mirrored.
> > >
> > > what does this actually 'mean' by the way? How is the camera physically
> > > mirrored? Is this really a bug in the driver somewhere or something
> > > else?
> > >
> > > I am weary that this patch is possibly a workaround for a problem
> > > somewhere else ?
> >
> > If my recollection is right, when it comes to the camera HAL:
> >
> > 1) Rotation is first parsed from DT, but there we can only express
> >    [0, 90, 270, 360] so we don't have a "mirror" option
> >
> > 2) If no "rotation" property is specified in DT, then the HAL
> >    falls-back  to parse a "rotation" property from configuration file,
> >    where again we can only express an angle between [0 - 360]
> >
> > All of this seems to suggest it is not possible to express in DT or
> > HAL configuration file the "mirroring" option. However this opens the
> > question that Kieran has asked already. How is the camera module
> > physically mirrored ? is the pixel array reading direction inverted in
> > the driver ?
> >
>
> I'm not 100% sure where the mirroring is coming from. I confirmed it
> through qcam on Ubuntu as well as through the camera app and Chromium
> on a ChromiumOs build so it's not just an application bug. The location and
> orientation coming from ACPI both seem to be correct. The driver seems

The 'rotation' property from DTS cannot express 'mirrored', and to me
this seems correct, as 'rotation' is about expressing the mounting
rotation of the camera sensor (0, 90, 180, 270)

Mirroring comes from inverting the reading directions of the pixel
units on the pixel array, what is usually called an HFLIP in v4l2
drivers and we require drivers to not apply any V/HFLIP by default
(something we might want to document in libcamera as well, not just in
Linux).

> to be fine on all but some Surface Go devices so it seems unrelated.

What do you mean with "seems fine" ? It doesn't mirror ?

> Anything below that is infeasible to fix and the sensor drivers don't
> maintain quirks for particular misbehaving devices, so the HAL seems
> to me the best place to add a mirrored config and pipe it down to the
> driver through the existing Orientation.

Which sensor are we talking about and where does its driver live ? Can
we see it ? Because some downstream (but also upstream) drivers
sometimes flip by default.


>
> Thanks,
> Allen
> > >
> > > --
> > > Kieran
> > >
> > > > >
> > > > > >         /*
> > > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > > >          * ensure the required entries are available without further
> > > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > > index 194ca3030..d304a1285 100644
> > > > > > --- a/src/android/camera_device.h
> > > > > > +++ b/src/android/camera_device.h
> > > > > > @@ -126,6 +126,7 @@ private:
> > > > > >
> > > > > >         int facing_;
> > > > > >         int orientation_;
> > > > > > +       int mirrored_;
> > > > > >
> > > > > >         CameraMetadata lastSettings_;
> > > > > >  };
> > > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > > --- a/src/android/camera_hal_config.cpp
> > > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > > @@ -33,6 +33,7 @@ private:
> > > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > >
> > > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > > >  };
> > > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > > >                 return -EINVAL;
> > > > > >
> > > > > > +       /* Parse optional property "mirrored" */
> > > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > > +
> > > > > >         return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > > >         return 0;
> > > > > >  }
> > > > > >
> > > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > > +{
> > > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > > +               return -EINVAL;
> > > > > > +
> > > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > > +       return 0;
> > > > > > +}
> > > > > > +
> > > > > >  CameraHalConfig::CameraHalConfig()
> > > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > > >  {
> > > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > > index a4bedb6e6..a96190470 100644
> > > > > > --- a/src/android/camera_hal_config.h
> > > > > > +++ b/src/android/camera_hal_config.h
> > > > > > @@ -15,6 +15,7 @@
> > > > > >  struct CameraConfigData {
> > > > > >         int facing = -1;
> > > > > >         int rotation = -1;
> > > > > > +       bool mirrored = false;
> > > > > >  };
> > > > > >
> > > > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > > > --
> > > > > > 2.50.0.727.gbf7dc18ff4-goog
> > > > > >
Allen Ballway July 23, 2025, 3:14 p.m. UTC | #7
Hi Jacopo,

On Wed, Jul 23, 2025 at 1:15 AM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Allen
>
> On Tue, Jul 22, 2025 at 07:51:53AM -0700, Allen Ballway wrote:
> > Hi all,
> >
> > On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi Kieran
> > >
> > > On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > > > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham
> > > > > <kieran.bingham@ideasonboard.com> wrote:
> > > > > >
> > > > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > > > >
> > > > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > > > >
> > > > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > > > ---
> > > > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > > > >  src/android/camera_device.h       |  1 +
> > > > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > > > >  src/android/camera_hal_config.h   |  1 +
> > > > > > >  4 files changed, 22 insertions(+)
> > > > > > >
> > > > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > > > index 80ff248c2..b20e9f3db 100644
> > > > > > > --- a/src/android/camera_device.cpp
> > > > > > > +++ b/src/android/camera_device.cpp
> > > > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > > > >                 orientation_ = 0;
> > > > > > >         }
> > > > > > >
> > > > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > > > >  }
> > > > > > >
> > > > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > > > >                 return -EINVAL;
> > > > > > >         }
> > > > > > >
> > > > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > > > >
> > > > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > > > sensors at least ?
> > > > >
> > > > > Ah I misunderstood the below comments/errors about not supporting stream
> > > > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > > > send out a v2 with this comment removed assuming the rest of this patch
> > > > > looks good.
> > > > > >
> > > > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > > > devs.
> > > > > >
> > > > > >
> > > > > >
> > > > > > > +       if (mirrored_) {
> > > > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > > > +       }
> > > > > > > +
> > >
> > > Also no {} for single line statement.
> > >
> > > It's amusing how many times the same stylistic comments had to
> > > repeated specifically to chromeos developers. We also have tools
> > > available for checking this kind of issues before submitting.
> > >
> >
> > Apologies, I ran checkstyle.py on the change but found no issues, if there
> > are other tools for finding style issues I'll be sure to run them in the future.
> > I'll update this one as well in v2.
>
> This is one of your first contributions, so it has been unfair ranting
> to you about the past :)
>
> I'm just suprised a team of specialized and highly skilled engineers
> continue pushing their conventions (out of habit I presume) to a different
> code base. My frustration comes from having repeated the same comments
> over and over in the past years. Apologies if you got ranted to.
>

Sorry to have added to the frustration, and no worries :)
> > > > > >
> > > > > > How does this interact with rotation as well ? will it take precedence
> > > > > > against 180 rotations?
> > > > >
> > > > > I can't confirm because my sensors aren't affected by the rotation config
> > > > > but as far as I can tell the rotation would be unaffected by setting the
> > > > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > > > orientation in the Android layer, so any rotation would happen independently.
> > > > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > > > level rotation would just rotate the flipped output as expected.
> > > >
> > > > Some level of rotation is supposed to be handled automatically by
> > > > setting the physical orientation and rotation in device tree.
> > > >
> > > > Then libcamera will 'aim' for a 0 rotation by default.
> > > >
> > > > I guess the issue here is that we don't have a way in
> > > > device-tree/firmware to mark that the camera is mirrored.
> > > >
> > > > what does this actually 'mean' by the way? How is the camera physically
> > > > mirrored? Is this really a bug in the driver somewhere or something
> > > > else?
> > > >
> > > > I am weary that this patch is possibly a workaround for a problem
> > > > somewhere else ?
> > >
> > > If my recollection is right, when it comes to the camera HAL:
> > >
> > > 1) Rotation is first parsed from DT, but there we can only express
> > >    [0, 90, 270, 360] so we don't have a "mirror" option
> > >
> > > 2) If no "rotation" property is specified in DT, then the HAL
> > >    falls-back  to parse a "rotation" property from configuration file,
> > >    where again we can only express an angle between [0 - 360]
> > >
> > > All of this seems to suggest it is not possible to express in DT or
> > > HAL configuration file the "mirroring" option. However this opens the
> > > question that Kieran has asked already. How is the camera module
> > > physically mirrored ? is the pixel array reading direction inverted in
> > > the driver ?
> > >
> >
> > I'm not 100% sure where the mirroring is coming from. I confirmed it
> > through qcam on Ubuntu as well as through the camera app and Chromium
> > on a ChromiumOs build so it's not just an application bug. The location and
> > orientation coming from ACPI both seem to be correct. The driver seems
>
> The 'rotation' property from DTS cannot express 'mirrored', and to me
> this seems correct, as 'rotation' is about expressing the mounting
> rotation of the camera sensor (0, 90, 180, 270)
>
> Mirroring comes from inverting the reading directions of the pixel
> units on the pixel array, what is usually called an HFLIP in v4l2
> drivers and we require drivers to not apply any V/HFLIP by default
> (something we might want to document in libcamera as well, not just in
> Linux).
>
> > to be fine on all but some Surface Go devices so it seems unrelated.
>
> What do you mean with "seems fine" ? It doesn't mirror ?
>

"Seems fine" as in I don't have a device to test and confirm that it actually
works as expected and isn't mirrored, but the only references I find to
mirrored output from the sensor are for the Surface devices, and I would
expect that to be a prominent enough issue that it would be raised
somewhere.
> > Anything below that is infeasible to fix and the sensor drivers don't
> > maintain quirks for particular misbehaving devices, so the HAL seems
> > to me the best place to add a mirrored config and pipe it down to the
> > driver through the existing Orientation.
>
> Which sensor are we talking about and where does its driver live ? Can
> we see it ? Because some downstream (but also upstream) drivers
> sometimes flip by default.
>
This is the ov8865 sensor, driver is drivers/media/i2c/ov8865.c in the
upstream kernel. I confirmedthat the default state is unflipped, and
actually have a separate patch out (https://lkml.org/lkml/2025/7/22/1443)
because there's a bug in the driver which ends up resetting the flip
states back to default after we set it.

Thanks,
Allen
>
> >
> > Thanks,
> > Allen
> > > >
> > > > --
> > > > Kieran
> > > >
> > > > > >
> > > > > > >         /*
> > > > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > > > >          * ensure the required entries are available without further
> > > > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > > > index 194ca3030..d304a1285 100644
> > > > > > > --- a/src/android/camera_device.h
> > > > > > > +++ b/src/android/camera_device.h
> > > > > > > @@ -126,6 +126,7 @@ private:
> > > > > > >
> > > > > > >         int facing_;
> > > > > > >         int orientation_;
> > > > > > > +       int mirrored_;
> > > > > > >
> > > > > > >         CameraMetadata lastSettings_;
> > > > > > >  };
> > > > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > > > --- a/src/android/camera_hal_config.cpp
> > > > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > > > @@ -33,6 +33,7 @@ private:
> > > > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > >
> > > > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > > > >  };
> > > > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > > > >                 return -EINVAL;
> > > > > > >
> > > > > > > +       /* Parse optional property "mirrored" */
> > > > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > > > +
> > > > > > >         return 0;
> > > > > > >  }
> > > > > > >
> > > > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > > > >         return 0;
> > > > > > >  }
> > > > > > >
> > > > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > > > +{
> > > > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > > > +               return -EINVAL;
> > > > > > > +
> > > > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > >  CameraHalConfig::CameraHalConfig()
> > > > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > > > >  {
> > > > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > > > index a4bedb6e6..a96190470 100644
> > > > > > > --- a/src/android/camera_hal_config.h
> > > > > > > +++ b/src/android/camera_hal_config.h
> > > > > > > @@ -15,6 +15,7 @@
> > > > > > >  struct CameraConfigData {
> > > > > > >         int facing = -1;
> > > > > > >         int rotation = -1;
> > > > > > > +       bool mirrored = false;
> > > > > > >  };
> > > > > > >
> > > > > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > > > > --
> > > > > > > 2.50.0.727.gbf7dc18ff4-goog
> > > > > > >
Laurent Pinchart July 23, 2025, 3:43 p.m. UTC | #8
On Wed, Jul 23, 2025 at 08:14:45AM -0700, Allen Ballway wrote:
> On Wed, Jul 23, 2025 at 1:15 AM Jacopo Mondi wrote:
> > On Tue, Jul 22, 2025 at 07:51:53AM -0700, Allen Ballway wrote:
> > > On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi wrote:
> > > > On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > > > > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > > > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham wrote:
> > > > > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > > > > >
> > > > > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > > > > >
> > > > > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > > > > ---
> > > > > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > > > > >  src/android/camera_device.h       |  1 +
> > > > > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > > > > >  src/android/camera_hal_config.h   |  1 +
> > > > > > > >  4 files changed, 22 insertions(+)
> > > > > > > >
> > > > > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > > > > index 80ff248c2..b20e9f3db 100644
> > > > > > > > --- a/src/android/camera_device.cpp
> > > > > > > > +++ b/src/android/camera_device.cpp
> > > > > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > > > > >                 orientation_ = 0;
> > > > > > > >         }
> > > > > > > >
> > > > > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > > > > >  }
> > > > > > > >
> > > > > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > > > > >                 return -EINVAL;
> > > > > > > >         }
> > > > > > > >
> > > > > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > > > > >
> > > > > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > > > > sensors at least ?
> > > > > >
> > > > > > Ah I misunderstood the below comments/errors about not supporting stream
> > > > > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > > > > send out a v2 with this comment removed assuming the rest of this patch
> > > > > > looks good.
> > > > > > >
> > > > > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > > > > devs.
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > +       if (mirrored_) {
> > > > > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > > > > +       }
> > > > > > > > +
> > > >
> > > > Also no {} for single line statement.
> > > >
> > > > It's amusing how many times the same stylistic comments had to
> > > > repeated specifically to chromeos developers. We also have tools
> > > > available for checking this kind of issues before submitting.
> > > >
> > >
> > > Apologies, I ran checkstyle.py on the change but found no issues, if there
> > > are other tools for finding style issues I'll be sure to run them in the future.
> > > I'll update this one as well in v2.
> >
> > This is one of your first contributions, so it has been unfair ranting
> > to you about the past :)
> >
> > I'm just suprised a team of specialized and highly skilled engineers
> > continue pushing their conventions (out of habit I presume) to a different
> > code base. My frustration comes from having repeated the same comments
> > over and over in the past years. Apologies if you got ranted to.
> 
> Sorry to have added to the frustration, and no worries :)
> 
> > > > > > >
> > > > > > > How does this interact with rotation as well ? will it take precedence
> > > > > > > against 180 rotations?
> > > > > >
> > > > > > I can't confirm because my sensors aren't affected by the rotation config
> > > > > > but as far as I can tell the rotation would be unaffected by setting the
> > > > > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > > > > orientation in the Android layer, so any rotation would happen independently.
> > > > > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > > > > level rotation would just rotate the flipped output as expected.
> > > > >
> > > > > Some level of rotation is supposed to be handled automatically by
> > > > > setting the physical orientation and rotation in device tree.
> > > > >
> > > > > Then libcamera will 'aim' for a 0 rotation by default.
> > > > >
> > > > > I guess the issue here is that we don't have a way in
> > > > > device-tree/firmware to mark that the camera is mirrored.
> > > > >
> > > > > what does this actually 'mean' by the way? How is the camera physically
> > > > > mirrored? Is this really a bug in the driver somewhere or something
> > > > > else?
> > > > >
> > > > > I am weary that this patch is possibly a workaround for a problem
> > > > > somewhere else ?
> > > >
> > > > If my recollection is right, when it comes to the camera HAL:
> > > >
> > > > 1) Rotation is first parsed from DT, but there we can only express
> > > >    [0, 90, 270, 360] so we don't have a "mirror" option
> > > >
> > > > 2) If no "rotation" property is specified in DT, then the HAL
> > > >    falls-back  to parse a "rotation" property from configuration file,
> > > >    where again we can only express an angle between [0 - 360]
> > > >
> > > > All of this seems to suggest it is not possible to express in DT or
> > > > HAL configuration file the "mirroring" option. However this opens the
> > > > question that Kieran has asked already. How is the camera module
> > > > physically mirrored ? is the pixel array reading direction inverted in
> > > > the driver ?
> > >
> > > I'm not 100% sure where the mirroring is coming from. I confirmed it
> > > through qcam on Ubuntu as well as through the camera app and Chromium
> > > on a ChromiumOs build so it's not just an application bug. The location and
> > > orientation coming from ACPI both seem to be correct. The driver seems
> >
> > The 'rotation' property from DTS cannot express 'mirrored', and to me
> > this seems correct, as 'rotation' is about expressing the mounting
> > rotation of the camera sensor (0, 90, 180, 270)
> >
> > Mirroring comes from inverting the reading directions of the pixel
> > units on the pixel array, what is usually called an HFLIP in v4l2
> > drivers and we require drivers to not apply any V/HFLIP by default
> > (something we might want to document in libcamera as well, not just in
> > Linux).
> >
> > > to be fine on all but some Surface Go devices so it seems unrelated.
> >
> > What do you mean with "seems fine" ? It doesn't mirror ?
> 
> "Seems fine" as in I don't have a device to test and confirm that it actually
> works as expected and isn't mirrored, but the only references I find to
> mirrored output from the sensor are for the Surface devices, and I would
> expect that to be a prominent enough issue that it would be raised
> somewhere.

Do you mean that all devices you know about that integrate an OV8865
display mirrored images, or is there a mix of devices with the same
sensor that display mirrored and non-mirrored images ?

> > > Anything below that is infeasible to fix and the sensor drivers don't
> > > maintain quirks for particular misbehaving devices, so the HAL seems
> > > to me the best place to add a mirrored config and pipe it down to the
> > > driver through the existing Orientation.
> >
> > Which sensor are we talking about and where does its driver live ? Can
> > we see it ? Because some downstream (but also upstream) drivers
> > sometimes flip by default.
> 
> This is the ov8865 sensor, driver is drivers/media/i2c/ov8865.c in the
> upstream kernel. I confirmedthat the default state is unflipped, and
> actually have a separate patch out (https://lkml.org/lkml/2025/7/22/1443)
> because there's a bug in the driver which ends up resetting the flip
> states back to default after we set it.
> 
> > > > > > > >         /*
> > > > > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > > > > >          * ensure the required entries are available without further
> > > > > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > > > > index 194ca3030..d304a1285 100644
> > > > > > > > --- a/src/android/camera_device.h
> > > > > > > > +++ b/src/android/camera_device.h
> > > > > > > > @@ -126,6 +126,7 @@ private:
> > > > > > > >
> > > > > > > >         int facing_;
> > > > > > > >         int orientation_;
> > > > > > > > +       int mirrored_;
> > > > > > > >
> > > > > > > >         CameraMetadata lastSettings_;
> > > > > > > >  };
> > > > > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > > > > --- a/src/android/camera_hal_config.cpp
> > > > > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > > > > @@ -33,6 +33,7 @@ private:
> > > > > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > >
> > > > > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > > > > >  };
> > > > > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > > > > >                 return -EINVAL;
> > > > > > > >
> > > > > > > > +       /* Parse optional property "mirrored" */
> > > > > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > > > > +
> > > > > > > >         return 0;
> > > > > > > >  }
> > > > > > > >
> > > > > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > > > > >         return 0;
> > > > > > > >  }
> > > > > > > >
> > > > > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > > > > +{
> > > > > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > > > > +               return -EINVAL;
> > > > > > > > +
> > > > > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > > > > +       return 0;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > >  CameraHalConfig::CameraHalConfig()
> > > > > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > > > > >  {
> > > > > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > > > > index a4bedb6e6..a96190470 100644
> > > > > > > > --- a/src/android/camera_hal_config.h
> > > > > > > > +++ b/src/android/camera_hal_config.h
> > > > > > > > @@ -15,6 +15,7 @@
> > > > > > > >  struct CameraConfigData {
> > > > > > > >         int facing = -1;
> > > > > > > >         int rotation = -1;
> > > > > > > > +       bool mirrored = false;
> > > > > > > >  };
> > > > > > > >
> > > > > > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > > > > > --
> > > > > > > > 2.50.0.727.gbf7dc18ff4-goog
> > > > > > > >
Allen Ballway July 23, 2025, 3:54 p.m. UTC | #9
On Wed, Jul 23, 2025 at 8:43 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> On Wed, Jul 23, 2025 at 08:14:45AM -0700, Allen Ballway wrote:
> > On Wed, Jul 23, 2025 at 1:15 AM Jacopo Mondi wrote:
> > > On Tue, Jul 22, 2025 at 07:51:53AM -0700, Allen Ballway wrote:
> > > > On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi wrote:
> > > > > On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > > > > > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > > > > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham wrote:
> > > > > > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > > > > > >
> > > > > > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > > > > > ---
> > > > > > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > > > > > >  src/android/camera_device.h       |  1 +
> > > > > > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > > > > > >  src/android/camera_hal_config.h   |  1 +
> > > > > > > > >  4 files changed, 22 insertions(+)
> > > > > > > > >
> > > > > > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > > > > > index 80ff248c2..b20e9f3db 100644
> > > > > > > > > --- a/src/android/camera_device.cpp
> > > > > > > > > +++ b/src/android/camera_device.cpp
> > > > > > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > > > > > >                 orientation_ = 0;
> > > > > > > > >         }
> > > > > > > > >
> > > > > > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > > > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > > > > > >                 return -EINVAL;
> > > > > > > > >         }
> > > > > > > > >
> > > > > > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > > > > > >
> > > > > > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > > > > > sensors at least ?
> > > > > > >
> > > > > > > Ah I misunderstood the below comments/errors about not supporting stream
> > > > > > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > > > > > send out a v2 with this comment removed assuming the rest of this patch
> > > > > > > looks good.
> > > > > > > >
> > > > > > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > > > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > > > > > devs.
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > +       if (mirrored_) {
> > > > > > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > > > > > +       }
> > > > > > > > > +
> > > > >
> > > > > Also no {} for single line statement.
> > > > >
> > > > > It's amusing how many times the same stylistic comments had to
> > > > > repeated specifically to chromeos developers. We also have tools
> > > > > available for checking this kind of issues before submitting.
> > > > >
> > > >
> > > > Apologies, I ran checkstyle.py on the change but found no issues, if there
> > > > are other tools for finding style issues I'll be sure to run them in the future.
> > > > I'll update this one as well in v2.
> > >
> > > This is one of your first contributions, so it has been unfair ranting
> > > to you about the past :)
> > >
> > > I'm just suprised a team of specialized and highly skilled engineers
> > > continue pushing their conventions (out of habit I presume) to a different
> > > code base. My frustration comes from having repeated the same comments
> > > over and over in the past years. Apologies if you got ranted to.
> >
> > Sorry to have added to the frustration, and no worries :)
> >
> > > > > > > >
> > > > > > > > How does this interact with rotation as well ? will it take precedence
> > > > > > > > against 180 rotations?
> > > > > > >
> > > > > > > I can't confirm because my sensors aren't affected by the rotation config
> > > > > > > but as far as I can tell the rotation would be unaffected by setting the
> > > > > > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > > > > > orientation in the Android layer, so any rotation would happen independently.
> > > > > > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > > > > > level rotation would just rotate the flipped output as expected.
> > > > > >
> > > > > > Some level of rotation is supposed to be handled automatically by
> > > > > > setting the physical orientation and rotation in device tree.
> > > > > >
> > > > > > Then libcamera will 'aim' for a 0 rotation by default.
> > > > > >
> > > > > > I guess the issue here is that we don't have a way in
> > > > > > device-tree/firmware to mark that the camera is mirrored.
> > > > > >
> > > > > > what does this actually 'mean' by the way? How is the camera physically
> > > > > > mirrored? Is this really a bug in the driver somewhere or something
> > > > > > else?
> > > > > >
> > > > > > I am weary that this patch is possibly a workaround for a problem
> > > > > > somewhere else ?
> > > > >
> > > > > If my recollection is right, when it comes to the camera HAL:
> > > > >
> > > > > 1) Rotation is first parsed from DT, but there we can only express
> > > > >    [0, 90, 270, 360] so we don't have a "mirror" option
> > > > >
> > > > > 2) If no "rotation" property is specified in DT, then the HAL
> > > > >    falls-back  to parse a "rotation" property from configuration file,
> > > > >    where again we can only express an angle between [0 - 360]
> > > > >
> > > > > All of this seems to suggest it is not possible to express in DT or
> > > > > HAL configuration file the "mirroring" option. However this opens the
> > > > > question that Kieran has asked already. How is the camera module
> > > > > physically mirrored ? is the pixel array reading direction inverted in
> > > > > the driver ?
> > > >
> > > > I'm not 100% sure where the mirroring is coming from. I confirmed it
> > > > through qcam on Ubuntu as well as through the camera app and Chromium
> > > > on a ChromiumOs build so it's not just an application bug. The location and
> > > > orientation coming from ACPI both seem to be correct. The driver seems
> > >
> > > The 'rotation' property from DTS cannot express 'mirrored', and to me
> > > this seems correct, as 'rotation' is about expressing the mounting
> > > rotation of the camera sensor (0, 90, 180, 270)
> > >
> > > Mirroring comes from inverting the reading directions of the pixel
> > > units on the pixel array, what is usually called an HFLIP in v4l2
> > > drivers and we require drivers to not apply any V/HFLIP by default
> > > (something we might want to document in libcamera as well, not just in
> > > Linux).
> > >
> > > > to be fine on all but some Surface Go devices so it seems unrelated.
> > >
> > > What do you mean with "seems fine" ? It doesn't mirror ?
> >
> > "Seems fine" as in I don't have a device to test and confirm that it actually
> > works as expected and isn't mirrored, but the only references I find to
> > mirrored output from the sensor are for the Surface devices, and I would
> > expect that to be a prominent enough issue that it would be raised
> > somewhere.
>
> Do you mean that all devices you know about that integrate an OV8865
> display mirrored images, or is there a mix of devices with the same
> sensor that display mirrored and non-mirrored images ?
>
I have only been able to test on Surface Go devices with this sensor, and
all of them have been mirrored. I'm unsure what other devices use this
sensor, but from a lack of complaints or issue reports around mirroring
make me assume that they receive non-mirrored images.
> > > > Anything below that is infeasible to fix and the sensor drivers don't
> > > > maintain quirks for particular misbehaving devices, so the HAL seems
> > > > to me the best place to add a mirrored config and pipe it down to the
> > > > driver through the existing Orientation.
> > >
> > > Which sensor are we talking about and where does its driver live ? Can
> > > we see it ? Because some downstream (but also upstream) drivers
> > > sometimes flip by default.
> >
> > This is the ov8865 sensor, driver is drivers/media/i2c/ov8865.c in the
> > upstream kernel. I confirmedthat the default state is unflipped, and
> > actually have a separate patch out (https://lkml.org/lkml/2025/7/22/1443)
> > because there's a bug in the driver which ends up resetting the flip
> > states back to default after we set it.
> >
> > > > > > > > >         /*
> > > > > > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > > > > > >          * ensure the required entries are available without further
> > > > > > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > > > > > index 194ca3030..d304a1285 100644
> > > > > > > > > --- a/src/android/camera_device.h
> > > > > > > > > +++ b/src/android/camera_device.h
> > > > > > > > > @@ -126,6 +126,7 @@ private:
> > > > > > > > >
> > > > > > > > >         int facing_;
> > > > > > > > >         int orientation_;
> > > > > > > > > +       int mirrored_;
> > > > > > > > >
> > > > > > > > >         CameraMetadata lastSettings_;
> > > > > > > > >  };
> > > > > > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > > > > > --- a/src/android/camera_hal_config.cpp
> > > > > > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > > > > > @@ -33,6 +33,7 @@ private:
> > > > > > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > > > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > >
> > > > > > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > > > > > >  };
> > > > > > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > > > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > > > > > >                 return -EINVAL;
> > > > > > > > >
> > > > > > > > > +       /* Parse optional property "mirrored" */
> > > > > > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > > > > > +
> > > > > > > > >         return 0;
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > > > > > >         return 0;
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > > > > > +{
> > > > > > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > > > > > +               return -EINVAL;
> > > > > > > > > +
> > > > > > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > > > > > +       return 0;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > >  CameraHalConfig::CameraHalConfig()
> > > > > > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > > > > > >  {
> > > > > > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > > > > > index a4bedb6e6..a96190470 100644
> > > > > > > > > --- a/src/android/camera_hal_config.h
> > > > > > > > > +++ b/src/android/camera_hal_config.h
> > > > > > > > > @@ -15,6 +15,7 @@
> > > > > > > > >  struct CameraConfigData {
> > > > > > > > >         int facing = -1;
> > > > > > > > >         int rotation = -1;
> > > > > > > > > +       bool mirrored = false;
> > > > > > > > >  };
> > > > > > > > >
> > > > > > > > >  class CameraHalConfig final : public libcamera::Extensible
> > > > > > > > > --
> > > > > > > > > 2.50.0.727.gbf7dc18ff4-goog
> > > > > > > > >
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart July 23, 2025, 4:14 p.m. UTC | #10
On Wed, Jul 23, 2025 at 08:54:20AM -0700, Allen Ballway wrote:
> On Wed, Jul 23, 2025 at 8:43 AM Laurent Pinchart wrote:
> > On Wed, Jul 23, 2025 at 08:14:45AM -0700, Allen Ballway wrote:
> > > On Wed, Jul 23, 2025 at 1:15 AM Jacopo Mondi wrote:
> > > > On Tue, Jul 22, 2025 at 07:51:53AM -0700, Allen Ballway wrote:
> > > > > On Mon, Jul 21, 2025 at 4:14 AM Jacopo Mondi wrote:
> > > > > > On Mon, Jul 21, 2025 at 11:44:18AM +0100, Kieran Bingham wrote:
> > > > > > > Quoting Allen Ballway (2025-07-18 16:27:23)
> > > > > > > > On Fri, Jul 18, 2025 at 5:11 AM Kieran Bingham wrote:
> > > > > > > > > Quoting Allen Ballway (2025-07-16 21:46:20)
> > > > > > > > > > Adds an optional `mirrored` value to the HAL config, which is used to modify
> > > > > > > > > > the `orientation` of the camera config. No rotation is used, so it can
> > > > > > > > > > only set the orientation to `Orientation::Rotate0Mirror`.
> > > > > > > > > >
> > > > > > > > > > This enables sensors which are incorrectly mirrored to be corrected.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Allen Ballway <ballway@chromium.org>
> > > > > > > > > > ---
> > > > > > > > > >  src/android/camera_device.cpp     |  6 ++++++
> > > > > > > > > >  src/android/camera_device.h       |  1 +
> > > > > > > > > >  src/android/camera_hal_config.cpp | 14 ++++++++++++++
> > > > > > > > > >  src/android/camera_hal_config.h   |  1 +
> > > > > > > > > >  4 files changed, 22 insertions(+)
> > > > > > > > > >
> > > > > > > > > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > > > > > > > > index 80ff248c2..b20e9f3db 100644
> > > > > > > > > > --- a/src/android/camera_device.cpp
> > > > > > > > > > +++ b/src/android/camera_device.cpp
> > > > > > > > > > @@ -377,6 +377,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
> > > > > > > > > >                 orientation_ = 0;
> > > > > > > > > >         }
> > > > > > > > > >
> > > > > > > > > > +       mirrored_ = cameraConfigData && cameraConfigData->mirrored;
> > > > > > > > > >         return capabilities_.initialize(camera_, orientation_, facing_);
> > > > > > > > > >  }
> > > > > > > > > >
> > > > > > > > > > @@ -547,6 +548,11 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > > > > > > > > >                 return -EINVAL;
> > > > > > > > > >         }
> > > > > > > > > >
> > > > > > > > > > +       // Rotation is unsupported, so if mirrored just set orientation.
> > > > > > > > >
> > > > > > > > > Is that true? I thought we usually support 0 and 180 rotations on most
> > > > > > > > > sensors at least ?
> > > > > > > >
> > > > > > > > Ah I misunderstood the below comments/errors about not supporting stream
> > > > > > > > rotations. It seems the sensors on my test device don't support rotation. I will
> > > > > > > > send out a v2 with this comment removed assuming the rest of this patch
> > > > > > > > looks good.
> > > > > > > > >
> > > > > > > > > We (almost) always use /*  */ comments in libcamera ... it's ... probably a
> > > > > > > > > stylistic choice because we are mostly started out as C / Linux-kernel
> > > > > > > > > devs.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > +       if (mirrored_) {
> > > > > > > > > > +               config->orientation = Orientation::Rotate0Mirror;
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > >
> > > > > > Also no {} for single line statement.
> > > > > >
> > > > > > It's amusing how many times the same stylistic comments had to
> > > > > > repeated specifically to chromeos developers. We also have tools
> > > > > > available for checking this kind of issues before submitting.
> > > > > >
> > > > >
> > > > > Apologies, I ran checkstyle.py on the change but found no issues, if there
> > > > > are other tools for finding style issues I'll be sure to run them in the future.
> > > > > I'll update this one as well in v2.
> > > >
> > > > This is one of your first contributions, so it has been unfair ranting
> > > > to you about the past :)
> > > >
> > > > I'm just suprised a team of specialized and highly skilled engineers
> > > > continue pushing their conventions (out of habit I presume) to a different
> > > > code base. My frustration comes from having repeated the same comments
> > > > over and over in the past years. Apologies if you got ranted to.
> > >
> > > Sorry to have added to the frustration, and no worries :)
> > >
> > > > > > > > >
> > > > > > > > > How does this interact with rotation as well ? will it take precedence
> > > > > > > > > against 180 rotations?
> > > > > > > >
> > > > > > > > I can't confirm because my sensors aren't affected by the rotation config
> > > > > > > > but as far as I can tell the rotation would be unaffected by setting the
> > > > > > > > orientation to mirror. There doesn't seem to be anywhere setting the
> > > > > > > > orientation in the Android layer, so any rotation would happen independently.
> > > > > > > > And the mirroring will just set the HFLIP on the kernel driver, so any higher
> > > > > > > > level rotation would just rotate the flipped output as expected.
> > > > > > >
> > > > > > > Some level of rotation is supposed to be handled automatically by
> > > > > > > setting the physical orientation and rotation in device tree.
> > > > > > >
> > > > > > > Then libcamera will 'aim' for a 0 rotation by default.
> > > > > > >
> > > > > > > I guess the issue here is that we don't have a way in
> > > > > > > device-tree/firmware to mark that the camera is mirrored.
> > > > > > >
> > > > > > > what does this actually 'mean' by the way? How is the camera physically
> > > > > > > mirrored? Is this really a bug in the driver somewhere or something
> > > > > > > else?
> > > > > > >
> > > > > > > I am weary that this patch is possibly a workaround for a problem
> > > > > > > somewhere else ?
> > > > > >
> > > > > > If my recollection is right, when it comes to the camera HAL:
> > > > > >
> > > > > > 1) Rotation is first parsed from DT, but there we can only express
> > > > > >    [0, 90, 270, 360] so we don't have a "mirror" option
> > > > > >
> > > > > > 2) If no "rotation" property is specified in DT, then the HAL
> > > > > >    falls-back  to parse a "rotation" property from configuration file,
> > > > > >    where again we can only express an angle between [0 - 360]
> > > > > >
> > > > > > All of this seems to suggest it is not possible to express in DT or
> > > > > > HAL configuration file the "mirroring" option. However this opens the
> > > > > > question that Kieran has asked already. How is the camera module
> > > > > > physically mirrored ? is the pixel array reading direction inverted in
> > > > > > the driver ?
> > > > >
> > > > > I'm not 100% sure where the mirroring is coming from. I confirmed it
> > > > > through qcam on Ubuntu as well as through the camera app and Chromium
> > > > > on a ChromiumOs build so it's not just an application bug. The location and
> > > > > orientation coming from ACPI both seem to be correct. The driver seems
> > > >
> > > > The 'rotation' property from DTS cannot express 'mirrored', and to me
> > > > this seems correct, as 'rotation' is about expressing the mounting
> > > > rotation of the camera sensor (0, 90, 180, 270)
> > > >
> > > > Mirroring comes from inverting the reading directions of the pixel
> > > > units on the pixel array, what is usually called an HFLIP in v4l2
> > > > drivers and we require drivers to not apply any V/HFLIP by default
> > > > (something we might want to document in libcamera as well, not just in
> > > > Linux).
> > > >
> > > > > to be fine on all but some Surface Go devices so it seems unrelated.
> > > >
> > > > What do you mean with "seems fine" ? It doesn't mirror ?
> > >
> > > "Seems fine" as in I don't have a device to test and confirm that it actually
> > > works as expected and isn't mirrored, but the only references I find to
> > > mirrored output from the sensor are for the Surface devices, and I would
> > > expect that to be a prominent enough issue that it would be raised
> > > somewhere.
> >
> > Do you mean that all devices you know about that integrate an OV8865
> > display mirrored images, or is there a mix of devices with the same
> > sensor that display mirrored and non-mirrored images ?
> 
> I have only been able to test on Surface Go devices with this sensor, and
> all of them have been mirrored. I'm unsure what other devices use this
> sensor, but from a lack of complaints or issue reports around mirroring
> make me assume that they receive non-mirrored images.

I wouldn't be so sure about that honestly.

In any case, there's no way the Surface machines have mounted a mirror
in front of the sensor. This issue may be a bug in the driver that
affects all platforms, or possibly a difference in behaviour between
different revisions of the sensor. In any case, the fix shouldn't be in
libcamera, especially not in the HAL implementation.

> > > > > Anything below that is infeasible to fix and the sensor drivers don't
> > > > > maintain quirks for particular misbehaving devices, so the HAL seems
> > > > > to me the best place to add a mirrored config and pipe it down to the
> > > > > driver through the existing Orientation.
> > > >
> > > > Which sensor are we talking about and where does its driver live ? Can
> > > > we see it ? Because some downstream (but also upstream) drivers
> > > > sometimes flip by default.
> > >
> > > This is the ov8865 sensor, driver is drivers/media/i2c/ov8865.c in the
> > > upstream kernel. I confirmedthat the default state is unflipped, and
> > > actually have a separate patch out (https://lkml.org/lkml/2025/7/22/1443)
> > > because there's a bug in the driver which ends up resetting the flip
> > > states back to default after we set it.
> > >
> > > > > > > > > >         /*
> > > > > > > > > >          * Clear and remove any existing configuration from previous calls, and
> > > > > > > > > >          * ensure the required entries are available without further
> > > > > > > > > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > > > > > > > > index 194ca3030..d304a1285 100644
> > > > > > > > > > --- a/src/android/camera_device.h
> > > > > > > > > > +++ b/src/android/camera_device.h
> > > > > > > > > > @@ -126,6 +126,7 @@ private:
> > > > > > > > > >
> > > > > > > > > >         int facing_;
> > > > > > > > > >         int orientation_;
> > > > > > > > > > +       int mirrored_;
> > > > > > > > > >
> > > > > > > > > >         CameraMetadata lastSettings_;
> > > > > > > > > >  };
> > > > > > > > > > diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
> > > > > > > > > > index 7ef451ef8..a2beba5d3 100644
> > > > > > > > > > --- a/src/android/camera_hal_config.cpp
> > > > > > > > > > +++ b/src/android/camera_hal_config.cpp
> > > > > > > > > > @@ -33,6 +33,7 @@ private:
> > > > > > > > > >         int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
> > > > > > > > > >         int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > > >         int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > > > +       int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);
> > > > > > > > > >
> > > > > > > > > >         std::map<std::string, CameraConfigData> *cameras_;
> > > > > > > > > >  };
> > > > > > > > > > @@ -106,6 +107,9 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
> > > > > > > > > >         if (parseRotation(cameraObject, cameraConfigData))
> > > > > > > > > >                 return -EINVAL;
> > > > > > > > > >
> > > > > > > > > > +       /* Parse optional property "mirrored" */
> > > > > > > > > > +       parseMirrored(cameraObject, cameraConfigData);
> > > > > > > > > > +
> > > > > > > > > >         return 0;
> > > > > > > > > >  }
> > > > > > > > > >
> > > > > > > > > > @@ -145,6 +149,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
> > > > > > > > > >         return 0;
> > > > > > > > > >  }
> > > > > > > > > >
> > > > > > > > > > +int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
> > > > > > > > > > +                                           CameraConfigData &cameraConfigData)
> > > > > > > > > > +{
> > > > > > > > > > +       if (!cameraObject.contains("mirrored"))
> > > > > > > > > > +               return -EINVAL;
> > > > > > > > > > +
> > > > > > > > > > +       cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > >  CameraHalConfig::CameraHalConfig()
> > > > > > > > > >         : Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
> > > > > > > > > >  {
> > > > > > > > > > diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
> > > > > > > > > > index a4bedb6e6..a96190470 100644
> > > > > > > > > > --- a/src/android/camera_hal_config.h
> > > > > > > > > > +++ b/src/android/camera_hal_config.h
> > > > > > > > > > @@ -15,6 +15,7 @@
> > > > > > > > > >  struct CameraConfigData {
> > > > > > > > > >         int facing = -1;
> > > > > > > > > >         int rotation = -1;
> > > > > > > > > > +       bool mirrored = false;
> > > > > > > > > >  };
> > > > > > > > > >
> > > > > > > > > >  class CameraHalConfig final : public libcamera::Extensible

Patch
diff mbox series

diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 80ff248c2..b20e9f3db 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -377,6 +377,7 @@  int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
 		orientation_ = 0;
 	}

+	mirrored_ = cameraConfigData && cameraConfigData->mirrored;
 	return capabilities_.initialize(camera_, orientation_, facing_);
 }

@@ -547,6 +548,11 @@  int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 		return -EINVAL;
 	}

+	// Rotation is unsupported, so if mirrored just set orientation.
+	if (mirrored_) {
+		config->orientation = Orientation::Rotate0Mirror;
+	}
+
 	/*
 	 * Clear and remove any existing configuration from previous calls, and
 	 * ensure the required entries are available without further
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 194ca3030..d304a1285 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -126,6 +126,7 @@  private:

 	int facing_;
 	int orientation_;
+	int mirrored_;

 	CameraMetadata lastSettings_;
 };
diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
index 7ef451ef8..a2beba5d3 100644
--- a/src/android/camera_hal_config.cpp
+++ b/src/android/camera_hal_config.cpp
@@ -33,6 +33,7 @@  private:
 	int parseCameraConfigData(const std::string &cameraId, const YamlObject &);
 	int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData);
 	int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData);
+	int parseMirrored(const YamlObject &, CameraConfigData &cameraConfigData);

 	std::map<std::string, CameraConfigData> *cameras_;
 };
@@ -106,6 +107,9 @@  int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId,
 	if (parseRotation(cameraObject, cameraConfigData))
 		return -EINVAL;

+	/* Parse optional property "mirrored" */
+	parseMirrored(cameraObject, cameraConfigData);
+
 	return 0;
 }

@@ -145,6 +149,16 @@  int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject,
 	return 0;
 }

+int CameraHalConfig::Private::parseMirrored(const YamlObject &cameraObject,
+					    CameraConfigData &cameraConfigData)
+{
+	if (!cameraObject.contains("mirrored"))
+		return -EINVAL;
+
+	cameraConfigData.mirrored = cameraObject["mirrored"].get<bool>(false);
+	return 0;
+}
+
 CameraHalConfig::CameraHalConfig()
 	: Extensible(std::make_unique<Private>()), exists_(false), valid_(false)
 {
diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
index a4bedb6e6..a96190470 100644
--- a/src/android/camera_hal_config.h
+++ b/src/android/camera_hal_config.h
@@ -15,6 +15,7 @@ 
 struct CameraConfigData {
 	int facing = -1;
 	int rotation = -1;
+	bool mirrored = false;
 };

 class CameraHalConfig final : public libcamera::Extensible