[v2] libcamera: libipa: camera_sensor: Add onsemi AR0144 sensor properties
diff mbox series

Message ID 20240703231136.5732-1-laurent.pinchart@ideasonboard.com
State Accepted
Commit 74513c398762737996267b3da7dc6af3c782bd05
Headers show
Series
  • [v2] libcamera: libipa: camera_sensor: Add onsemi AR0144 sensor properties
Related show

Commit Message

Laurent Pinchart July 3, 2024, 11:11 p.m. UTC
Provide the onsemi AR0144 camera sensor properties and registration with
libipa for the gain code helpers.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Now that the kernel driver has been posted to the linux-media mailing
list ([1]), it's time for a second version of this patch.

[1] https://lore.kernel.org/r/20240603230301.26550-1-laurent.pinchart@ideasonboard.com

Changes since RFC:

- Add default case
- Add black level
---
 src/ipa/libipa/camera_sensor_helper.cpp       | 94 +++++++++++++++++++
 .../sensor/camera_sensor_properties.cpp       |  9 ++
 2 files changed, 103 insertions(+)


base-commit: 196abb8d1d6e0fe9d190315e72a85eb12d16a554

Comments

Kieran Bingham July 23, 2024, 3:41 p.m. UTC | #1
Quoting Laurent Pinchart (2024-07-04 00:11:36)
> Provide the onsemi AR0144 camera sensor properties and registration with
> libipa for the gain code helpers.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> Now that the kernel driver has been posted to the linux-media mailing
> list ([1]), it's time for a second version of this patch.
> 
> [1] https://lore.kernel.org/r/20240603230301.26550-1-laurent.pinchart@ideasonboard.com
> 
> Changes since RFC:
> 
> - Add default case
> - Add black level
> ---
>  src/ipa/libipa/camera_sensor_helper.cpp       | 94 +++++++++++++++++++
>  .../sensor/camera_sensor_properties.cpp       |  9 ++
>  2 files changed, 103 insertions(+)

I think I'll be able to test this soon. Might as well be in by then ;-)

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> 
> diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
> index a1339c83c4cb..1cae0b3840af 100644
> --- a/src/ipa/libipa/camera_sensor_helper.cpp
> +++ b/src/ipa/libipa/camera_sensor_helper.cpp
> @@ -8,6 +8,7 @@
>  #include "camera_sensor_helper.h"
>  
>  #include <cmath>
> +#include <limits>
>  
>  #include <libcamera/base/log.h>
>  
> @@ -398,6 +399,99 @@ static constexpr double expGainDb(double step)
>         return log2_10 * step / 20;
>  }
>  
> +class CameraSensorHelperAr0144 : public CameraSensorHelper
> +{
> +public:
> +       CameraSensorHelperAr0144()
> +       {
> +               /* Power-on default value: 168 at 12bits. */
> +               blackLevel_ = 2688;
> +       }
> +
> +       uint32_t gainCode(double gain) const override
> +       {
> +               /* The recommended minimum gain is 1.6842 to avoid artifacts. */
> +               gain = std::clamp(gain, 1.0 / (1.0 - 13.0 / 32.0), 18.45);
> +
> +               /*
> +                * The analogue gain is made of a coarse exponential gain in
> +                * the range [2^0, 2^4] and a fine inversely linear gain in the
> +                * range [1.0, 2.0[. There is an additional fixed 1.153125
> +                * multiplier when the coarse gain reaches 2^2.
> +                */
> +
> +               if (gain > 4.0)
> +                       gain /= 1.153125;
> +
> +               unsigned int coarse = std::log2(gain);
> +               unsigned int fine = (1 - (1 << coarse) / gain) * 32;
> +
> +               /* The fine gain rounding depends on the coarse gain. */
> +               if (coarse == 1 || coarse == 3)
> +                       fine &= ~1;
> +               else if (coarse == 4)
> +                       fine &= ~3;
> +
> +               return (coarse << 4) | (fine & 0xf);
> +       }
> +
> +       double gain(uint32_t gainCode) const override
> +       {
> +               unsigned int coarse = gainCode >> 4;
> +               unsigned int fine = gainCode & 0xf;
> +               unsigned int d1;
> +               double d2, m;
> +
> +               switch (coarse) {
> +               default:
> +               case 0:
> +                       d1 = 1;
> +                       d2 = 32.0;
> +                       m = 1.0;
> +                       break;
> +               case 1:
> +                       d1 = 2;
> +                       d2 = 16.0;
> +                       m = 1.0;
> +                       break;
> +               case 2:
> +                       d1 = 1;
> +                       d2 = 32.0;
> +                       m = 1.153125;
> +                       break;
> +               case 3:
> +                       d1 = 2;
> +                       d2 = 16.0;
> +                       m = 1.153125;
> +                       break;
> +               case 4:
> +                       d1 = 4;
> +                       d2 = 8.0;
> +                       m = 1.153125;
> +                       break;
> +               }
> +
> +               /*
> +                * With infinite precision, the calculated gain would be exact,
> +                * and the reverse conversion with gainCode() would produce the
> +                * same gain code. In the real world, rounding errors may cause
> +                * the calculated gain to be lower by an amount negligible for
> +                * all purposes, except for the reverse conversion. Converting
> +                * the gain to a gain code could then return the quantized value
> +                * just lower than the original gain code. To avoid this, tests
> +                * showed that adding the machine epsilon to the multiplier m is
> +                * sufficient.
> +                */
> +               m += std::numeric_limits<decltype(m)>::epsilon();
> +
> +               return m * (1 << coarse) / (1.0 - (fine / d1) / d2);
> +       }
> +
> +private:
> +       static constexpr double kStep_ = 16;
> +};
> +REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144)
> +
>  class CameraSensorHelperAr0521 : public CameraSensorHelper
>  {
>  public:
> diff --git a/src/libcamera/sensor/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp
> index b18524d85b37..4e5217ab51ef 100644
> --- a/src/libcamera/sensor/camera_sensor_properties.cpp
> +++ b/src/libcamera/sensor/camera_sensor_properties.cpp
> @@ -52,6 +52,15 @@ LOG_DEFINE_CATEGORY(CameraSensorProperties)
>  const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor)
>  {
>         static const std::map<std::string, const CameraSensorProperties> sensorProps = {
> +               { "ar0144", {
> +                       .unitCellSize = { 3000, 3000 },
> +                       .testPatternModes = {
> +                               { controls::draft::TestPatternModeOff, 0 },
> +                               { controls::draft::TestPatternModeSolidColor, 1 },
> +                               { controls::draft::TestPatternModeColorBars, 2 },
> +                               { controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
> +                       },
> +               } },
>                 { "ar0521", {
>                         .unitCellSize = { 2200, 2200 },
>                         .testPatternModes = {
> 
> base-commit: 196abb8d1d6e0fe9d190315e72a85eb12d16a554
> -- 
> Regards,
> 
> Laurent Pinchart
>
Laurent Pinchart July 23, 2024, 8:32 p.m. UTC | #2
Hi Kieran,

On Tue, Jul 23, 2024 at 04:41:31PM +0100, Kieran Bingham wrote:
> Quoting Laurent Pinchart (2024-07-04 00:11:36)
> > Provide the onsemi AR0144 camera sensor properties and registration with
> > libipa for the gain code helpers.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> > Now that the kernel driver has been posted to the linux-media mailing
> > list ([1]), it's time for a second version of this patch.
> > 
> > [1] https://lore.kernel.org/r/20240603230301.26550-1-laurent.pinchart@ideasonboard.com
> > 
> > Changes since RFC:
> > 
> > - Add default case
> > - Add black level
> > ---
> >  src/ipa/libipa/camera_sensor_helper.cpp       | 94 +++++++++++++++++++
> >  .../sensor/camera_sensor_properties.cpp       |  9 ++
> >  2 files changed, 103 insertions(+)
> 
> I think I'll be able to test this soon. Might as well be in by then ;-)
> 
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

The driver hasn't been merged upstream, but is on its way. I've sent an
initial patch series and will continue working on it. Can I merge this
patch already, or should I hold on ?

> > diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
> > index a1339c83c4cb..1cae0b3840af 100644
> > --- a/src/ipa/libipa/camera_sensor_helper.cpp
> > +++ b/src/ipa/libipa/camera_sensor_helper.cpp
> > @@ -8,6 +8,7 @@
> >  #include "camera_sensor_helper.h"
> >  
> >  #include <cmath>
> > +#include <limits>
> >  
> >  #include <libcamera/base/log.h>
> >  
> > @@ -398,6 +399,99 @@ static constexpr double expGainDb(double step)
> >         return log2_10 * step / 20;
> >  }
> >  
> > +class CameraSensorHelperAr0144 : public CameraSensorHelper
> > +{
> > +public:
> > +       CameraSensorHelperAr0144()
> > +       {
> > +               /* Power-on default value: 168 at 12bits. */
> > +               blackLevel_ = 2688;
> > +       }
> > +
> > +       uint32_t gainCode(double gain) const override
> > +       {
> > +               /* The recommended minimum gain is 1.6842 to avoid artifacts. */
> > +               gain = std::clamp(gain, 1.0 / (1.0 - 13.0 / 32.0), 18.45);
> > +
> > +               /*
> > +                * The analogue gain is made of a coarse exponential gain in
> > +                * the range [2^0, 2^4] and a fine inversely linear gain in the
> > +                * range [1.0, 2.0[. There is an additional fixed 1.153125
> > +                * multiplier when the coarse gain reaches 2^2.
> > +                */
> > +
> > +               if (gain > 4.0)
> > +                       gain /= 1.153125;
> > +
> > +               unsigned int coarse = std::log2(gain);
> > +               unsigned int fine = (1 - (1 << coarse) / gain) * 32;
> > +
> > +               /* The fine gain rounding depends on the coarse gain. */
> > +               if (coarse == 1 || coarse == 3)
> > +                       fine &= ~1;
> > +               else if (coarse == 4)
> > +                       fine &= ~3;
> > +
> > +               return (coarse << 4) | (fine & 0xf);
> > +       }
> > +
> > +       double gain(uint32_t gainCode) const override
> > +       {
> > +               unsigned int coarse = gainCode >> 4;
> > +               unsigned int fine = gainCode & 0xf;
> > +               unsigned int d1;
> > +               double d2, m;
> > +
> > +               switch (coarse) {
> > +               default:
> > +               case 0:
> > +                       d1 = 1;
> > +                       d2 = 32.0;
> > +                       m = 1.0;
> > +                       break;
> > +               case 1:
> > +                       d1 = 2;
> > +                       d2 = 16.0;
> > +                       m = 1.0;
> > +                       break;
> > +               case 2:
> > +                       d1 = 1;
> > +                       d2 = 32.0;
> > +                       m = 1.153125;
> > +                       break;
> > +               case 3:
> > +                       d1 = 2;
> > +                       d2 = 16.0;
> > +                       m = 1.153125;
> > +                       break;
> > +               case 4:
> > +                       d1 = 4;
> > +                       d2 = 8.0;
> > +                       m = 1.153125;
> > +                       break;
> > +               }
> > +
> > +               /*
> > +                * With infinite precision, the calculated gain would be exact,
> > +                * and the reverse conversion with gainCode() would produce the
> > +                * same gain code. In the real world, rounding errors may cause
> > +                * the calculated gain to be lower by an amount negligible for
> > +                * all purposes, except for the reverse conversion. Converting
> > +                * the gain to a gain code could then return the quantized value
> > +                * just lower than the original gain code. To avoid this, tests
> > +                * showed that adding the machine epsilon to the multiplier m is
> > +                * sufficient.
> > +                */
> > +               m += std::numeric_limits<decltype(m)>::epsilon();
> > +
> > +               return m * (1 << coarse) / (1.0 - (fine / d1) / d2);
> > +       }
> > +
> > +private:
> > +       static constexpr double kStep_ = 16;
> > +};
> > +REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144)
> > +
> >  class CameraSensorHelperAr0521 : public CameraSensorHelper
> >  {
> >  public:
> > diff --git a/src/libcamera/sensor/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp
> > index b18524d85b37..4e5217ab51ef 100644
> > --- a/src/libcamera/sensor/camera_sensor_properties.cpp
> > +++ b/src/libcamera/sensor/camera_sensor_properties.cpp
> > @@ -52,6 +52,15 @@ LOG_DEFINE_CATEGORY(CameraSensorProperties)
> >  const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor)
> >  {
> >         static const std::map<std::string, const CameraSensorProperties> sensorProps = {
> > +               { "ar0144", {
> > +                       .unitCellSize = { 3000, 3000 },
> > +                       .testPatternModes = {
> > +                               { controls::draft::TestPatternModeOff, 0 },
> > +                               { controls::draft::TestPatternModeSolidColor, 1 },
> > +                               { controls::draft::TestPatternModeColorBars, 2 },
> > +                               { controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
> > +                       },
> > +               } },
> >                 { "ar0521", {
> >                         .unitCellSize = { 2200, 2200 },
> >                         .testPatternModes = {
> > 
> > base-commit: 196abb8d1d6e0fe9d190315e72a85eb12d16a554
Kieran Bingham July 24, 2024, 7:52 a.m. UTC | #3
Quoting Laurent Pinchart (2024-07-23 21:32:57)
> Hi Kieran,
> 
> On Tue, Jul 23, 2024 at 04:41:31PM +0100, Kieran Bingham wrote:
> > Quoting Laurent Pinchart (2024-07-04 00:11:36)
> > > Provide the onsemi AR0144 camera sensor properties and registration with
> > > libipa for the gain code helpers.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > > Now that the kernel driver has been posted to the linux-media mailing
> > > list ([1]), it's time for a second version of this patch.
> > > 
> > > [1] https://lore.kernel.org/r/20240603230301.26550-1-laurent.pinchart@ideasonboard.com
> > > 
> > > Changes since RFC:
> > > 
> > > - Add default case
> > > - Add black level
> > > ---
> > >  src/ipa/libipa/camera_sensor_helper.cpp       | 94 +++++++++++++++++++
> > >  .../sensor/camera_sensor_properties.cpp       |  9 ++
> > >  2 files changed, 103 insertions(+)
> > 
> > I think I'll be able to test this soon. Might as well be in by then ;-)
> > 
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> The driver hasn't been merged upstream, but is on its way. I've sent an
> initial patch series and will continue working on it. Can I merge this
> patch already, or should I hold on ?

Driver is public, posted upstream, expected to land when reviews
complete, and the interface isn't expected to change. I think that meets
criteria to merge to libcamera support for this.

--
Kieran



 
> > > diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
> > > index a1339c83c4cb..1cae0b3840af 100644
> > > --- a/src/ipa/libipa/camera_sensor_helper.cpp
> > > +++ b/src/ipa/libipa/camera_sensor_helper.cpp
> > > @@ -8,6 +8,7 @@
> > >  #include "camera_sensor_helper.h"
> > >  
> > >  #include <cmath>
> > > +#include <limits>
> > >  
> > >  #include <libcamera/base/log.h>
> > >  
> > > @@ -398,6 +399,99 @@ static constexpr double expGainDb(double step)
> > >         return log2_10 * step / 20;
> > >  }
> > >  
> > > +class CameraSensorHelperAr0144 : public CameraSensorHelper
> > > +{
> > > +public:
> > > +       CameraSensorHelperAr0144()
> > > +       {
> > > +               /* Power-on default value: 168 at 12bits. */
> > > +               blackLevel_ = 2688;
> > > +       }
> > > +
> > > +       uint32_t gainCode(double gain) const override
> > > +       {
> > > +               /* The recommended minimum gain is 1.6842 to avoid artifacts. */
> > > +               gain = std::clamp(gain, 1.0 / (1.0 - 13.0 / 32.0), 18.45);
> > > +
> > > +               /*
> > > +                * The analogue gain is made of a coarse exponential gain in
> > > +                * the range [2^0, 2^4] and a fine inversely linear gain in the
> > > +                * range [1.0, 2.0[. There is an additional fixed 1.153125
> > > +                * multiplier when the coarse gain reaches 2^2.
> > > +                */
> > > +
> > > +               if (gain > 4.0)
> > > +                       gain /= 1.153125;
> > > +
> > > +               unsigned int coarse = std::log2(gain);
> > > +               unsigned int fine = (1 - (1 << coarse) / gain) * 32;
> > > +
> > > +               /* The fine gain rounding depends on the coarse gain. */
> > > +               if (coarse == 1 || coarse == 3)
> > > +                       fine &= ~1;
> > > +               else if (coarse == 4)
> > > +                       fine &= ~3;
> > > +
> > > +               return (coarse << 4) | (fine & 0xf);
> > > +       }
> > > +
> > > +       double gain(uint32_t gainCode) const override
> > > +       {
> > > +               unsigned int coarse = gainCode >> 4;
> > > +               unsigned int fine = gainCode & 0xf;
> > > +               unsigned int d1;
> > > +               double d2, m;
> > > +
> > > +               switch (coarse) {
> > > +               default:
> > > +               case 0:
> > > +                       d1 = 1;
> > > +                       d2 = 32.0;
> > > +                       m = 1.0;
> > > +                       break;
> > > +               case 1:
> > > +                       d1 = 2;
> > > +                       d2 = 16.0;
> > > +                       m = 1.0;
> > > +                       break;
> > > +               case 2:
> > > +                       d1 = 1;
> > > +                       d2 = 32.0;
> > > +                       m = 1.153125;
> > > +                       break;
> > > +               case 3:
> > > +                       d1 = 2;
> > > +                       d2 = 16.0;
> > > +                       m = 1.153125;
> > > +                       break;
> > > +               case 4:
> > > +                       d1 = 4;
> > > +                       d2 = 8.0;
> > > +                       m = 1.153125;
> > > +                       break;
> > > +               }
> > > +
> > > +               /*
> > > +                * With infinite precision, the calculated gain would be exact,
> > > +                * and the reverse conversion with gainCode() would produce the
> > > +                * same gain code. In the real world, rounding errors may cause
> > > +                * the calculated gain to be lower by an amount negligible for
> > > +                * all purposes, except for the reverse conversion. Converting
> > > +                * the gain to a gain code could then return the quantized value
> > > +                * just lower than the original gain code. To avoid this, tests
> > > +                * showed that adding the machine epsilon to the multiplier m is
> > > +                * sufficient.
> > > +                */
> > > +               m += std::numeric_limits<decltype(m)>::epsilon();
> > > +
> > > +               return m * (1 << coarse) / (1.0 - (fine / d1) / d2);
> > > +       }
> > > +
> > > +private:
> > > +       static constexpr double kStep_ = 16;
> > > +};
> > > +REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144)
> > > +
> > >  class CameraSensorHelperAr0521 : public CameraSensorHelper
> > >  {
> > >  public:
> > > diff --git a/src/libcamera/sensor/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp
> > > index b18524d85b37..4e5217ab51ef 100644
> > > --- a/src/libcamera/sensor/camera_sensor_properties.cpp
> > > +++ b/src/libcamera/sensor/camera_sensor_properties.cpp
> > > @@ -52,6 +52,15 @@ LOG_DEFINE_CATEGORY(CameraSensorProperties)
> > >  const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor)
> > >  {
> > >         static const std::map<std::string, const CameraSensorProperties> sensorProps = {
> > > +               { "ar0144", {
> > > +                       .unitCellSize = { 3000, 3000 },
> > > +                       .testPatternModes = {
> > > +                               { controls::draft::TestPatternModeOff, 0 },
> > > +                               { controls::draft::TestPatternModeSolidColor, 1 },
> > > +                               { controls::draft::TestPatternModeColorBars, 2 },
> > > +                               { controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
> > > +                       },
> > > +               } },
> > >                 { "ar0521", {
> > >                         .unitCellSize = { 2200, 2200 },
> > >                         .testPatternModes = {
> > > 
> > > base-commit: 196abb8d1d6e0fe9d190315e72a85eb12d16a554
> 
> -- 
> Regards,
> 
> Laurent Pinchart

Patch
diff mbox series

diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
index a1339c83c4cb..1cae0b3840af 100644
--- a/src/ipa/libipa/camera_sensor_helper.cpp
+++ b/src/ipa/libipa/camera_sensor_helper.cpp
@@ -8,6 +8,7 @@ 
 #include "camera_sensor_helper.h"
 
 #include <cmath>
+#include <limits>
 
 #include <libcamera/base/log.h>
 
@@ -398,6 +399,99 @@  static constexpr double expGainDb(double step)
 	return log2_10 * step / 20;
 }
 
+class CameraSensorHelperAr0144 : public CameraSensorHelper
+{
+public:
+	CameraSensorHelperAr0144()
+	{
+		/* Power-on default value: 168 at 12bits. */
+		blackLevel_ = 2688;
+	}
+
+	uint32_t gainCode(double gain) const override
+	{
+		/* The recommended minimum gain is 1.6842 to avoid artifacts. */
+		gain = std::clamp(gain, 1.0 / (1.0 - 13.0 / 32.0), 18.45);
+
+		/*
+		 * The analogue gain is made of a coarse exponential gain in
+		 * the range [2^0, 2^4] and a fine inversely linear gain in the
+		 * range [1.0, 2.0[. There is an additional fixed 1.153125
+		 * multiplier when the coarse gain reaches 2^2.
+		 */
+
+		if (gain > 4.0)
+			gain /= 1.153125;
+
+		unsigned int coarse = std::log2(gain);
+		unsigned int fine = (1 - (1 << coarse) / gain) * 32;
+
+		/* The fine gain rounding depends on the coarse gain. */
+		if (coarse == 1 || coarse == 3)
+			fine &= ~1;
+		else if (coarse == 4)
+			fine &= ~3;
+
+		return (coarse << 4) | (fine & 0xf);
+	}
+
+	double gain(uint32_t gainCode) const override
+	{
+		unsigned int coarse = gainCode >> 4;
+		unsigned int fine = gainCode & 0xf;
+		unsigned int d1;
+		double d2, m;
+
+		switch (coarse) {
+		default:
+		case 0:
+			d1 = 1;
+			d2 = 32.0;
+			m = 1.0;
+			break;
+		case 1:
+			d1 = 2;
+			d2 = 16.0;
+			m = 1.0;
+			break;
+		case 2:
+			d1 = 1;
+			d2 = 32.0;
+			m = 1.153125;
+			break;
+		case 3:
+			d1 = 2;
+			d2 = 16.0;
+			m = 1.153125;
+			break;
+		case 4:
+			d1 = 4;
+			d2 = 8.0;
+			m = 1.153125;
+			break;
+		}
+
+		/*
+		 * With infinite precision, the calculated gain would be exact,
+		 * and the reverse conversion with gainCode() would produce the
+		 * same gain code. In the real world, rounding errors may cause
+		 * the calculated gain to be lower by an amount negligible for
+		 * all purposes, except for the reverse conversion. Converting
+		 * the gain to a gain code could then return the quantized value
+		 * just lower than the original gain code. To avoid this, tests
+		 * showed that adding the machine epsilon to the multiplier m is
+		 * sufficient.
+		 */
+		m += std::numeric_limits<decltype(m)>::epsilon();
+
+		return m * (1 << coarse) / (1.0 - (fine / d1) / d2);
+	}
+
+private:
+	static constexpr double kStep_ = 16;
+};
+REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144)
+
 class CameraSensorHelperAr0521 : public CameraSensorHelper
 {
 public:
diff --git a/src/libcamera/sensor/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp
index b18524d85b37..4e5217ab51ef 100644
--- a/src/libcamera/sensor/camera_sensor_properties.cpp
+++ b/src/libcamera/sensor/camera_sensor_properties.cpp
@@ -52,6 +52,15 @@  LOG_DEFINE_CATEGORY(CameraSensorProperties)
 const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor)
 {
 	static const std::map<std::string, const CameraSensorProperties> sensorProps = {
+		{ "ar0144", {
+			.unitCellSize = { 3000, 3000 },
+			.testPatternModes = {
+				{ controls::draft::TestPatternModeOff, 0 },
+				{ controls::draft::TestPatternModeSolidColor, 1 },
+				{ controls::draft::TestPatternModeColorBars, 2 },
+				{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
+			},
+		} },
 		{ "ar0521", {
 			.unitCellSize = { 2200, 2200 },
 			.testPatternModes = {