[v4,17/21] ipa: mali-c55: Convert AWB to UQ4_8 usage
diff mbox series

Message ID 20251114005428.90024-18-kieran.bingham@ideasonboard.com
State New
Headers show
Series
  • libipa: Introduce a Quantized type
Related show

Commit Message

Kieran Bingham Nov. 14, 2025, 12:54 a.m. UTC
Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8
format. This ensures that reporting of gains in metadata reflect the true
AWB gains applied.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 src/ipa/mali-c55/algorithms/awb.cpp | 44 ++++++++++++++---------------
 src/ipa/mali-c55/ipa_context.h      | 10 ++++---
 2 files changed, 28 insertions(+), 26 deletions(-)

Comments

Barnabás Pőcze Nov. 17, 2025, 12:08 p.m. UTC | #1
Hi

2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:
> Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8
> format. This ensures that reporting of gains in metadata reflect the true
> AWB gains applied.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>   src/ipa/mali-c55/algorithms/awb.cpp | 44 ++++++++++++++---------------
>   src/ipa/mali-c55/ipa_context.h      | 10 ++++---
>   2 files changed, 28 insertions(+), 26 deletions(-)
> 
> diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
> index 3d546e5a854b..6e213f43e762 100644
> --- a/src/ipa/mali-c55/algorithms/awb.cpp
> +++ b/src/ipa/mali-c55/algorithms/awb.cpp
> @@ -37,8 +37,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context,
>   	 * for the first frame we will make no assumptions and leave the R/B
>   	 * channels unmodified.
>   	 */
> -	context.activeState.awb.rGain = 1.0;
> -	context.activeState.awb.bGain = 1.0;
> +	context.activeState.awb.rGain = 1.0f;
> +	context.activeState.awb.bGain = 1.0f;
>   
>   	return 0;
>   }
> @@ -50,8 +50,8 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
>   	block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
>   	block.header->size = sizeof(struct mali_c55_params_awb_gains);
>   
> -	double rGain = context.activeState.awb.rGain;
> -	double bGain = context.activeState.awb.bGain;
> +	UQ4_8 rGain = context.activeState.awb.rGain;
> +	UQ4_8 bGain = context.activeState.awb.bGain;
>   
>   	/*
>   	 * The gains here map as follows:
> @@ -63,10 +63,10 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
>   	 * This holds true regardless of the bayer order of the input data, as
>   	 * the mapping is done internally in the ISP.
>   	 */
> -	block.awb_gains->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
> -	block.awb_gains->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> -	block.awb_gains->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> -	block.awb_gains->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
> +	block.awb_gains->gain00 = rGain.quantized();
> +	block.awb_gains->gain01 = UQ4_8(1.0f).quantized();
> +	block.awb_gains->gain10 = UQ4_8(1.0f).quantized();
> +	block.awb_gains->gain11 = bGain.quantized();
>   
>   	frameContext.awb.rGain = rGain;
>   	frameContext.awb.bGain = bGain;
> @@ -162,8 +162,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>   		 * The statistics are in Q4.8 format, so we convert to double
>   		 * here.
>   		 */
> -		rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr);
> -		bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br);
> +		rgSum += UQ4_8(awb_ratios[i].avg_rg_gr).value();
> +		bgSum += UQ4_8(awb_ratios[i].avg_bg_br).value();
>   		counted_zones++;
>   	}
>   
> @@ -186,8 +186,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>   	 * figure by the gains that were applied when the statistics for this
>   	 * frame were generated.
>   	 */
> -	double rRatio = rgAvg / frameContext.awb.rGain;
> -	double bRatio = bgAvg / frameContext.awb.bGain;
> +	double rRatio = rgAvg / frameContext.awb.rGain.value();
> +	double bRatio = bgAvg / frameContext.awb.bGain.value();
>   
>   	/*
>   	 * And then we can simply invert the ratio to find the gain we should
> @@ -203,24 +203,24 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>   	 * want to fix the miscolouring as quickly as possible.
>   	 */
>   	double speed = frame < kNumStartupFrames ? 1.0 : 0.2;
> -	rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed);
> -	bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed);
> +	rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0 - speed);
> +	bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0 - speed);
>   
> -	context.activeState.awb.rGain = rGain;
> -	context.activeState.awb.bGain = bGain;
> +	context.activeState.awb.rGain = static_cast<float>(rGain);
> +	context.activeState.awb.bGain = static_cast<float>(bGain);

I'm wondering if it might make sense to replace the `double`s with `float`s.
But otherwise the transformation looks ok.

Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>



>   
>   	metadata.set(controls::ColourGains, {
> -		static_cast<float>(frameContext.awb.rGain),
> -		static_cast<float>(frameContext.awb.bGain),
> +		frameContext.awb.rGain.value(),
> +		frameContext.awb.bGain.value(),
>   	});
>   
>   	LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": "
>   		<< "Average R/G Ratio: " << rgAvg
>   		<< ", Average B/G Ratio: " << bgAvg
> -		<< "\nrGain applied to this frame: " << frameContext.awb.rGain
> -		<< ", bGain applied to this frame: " << frameContext.awb.bGain
> -		<< "\nrGain to apply: " << context.activeState.awb.rGain
> -		<< ", bGain to apply: " << context.activeState.awb.bGain;
> +		<< "\nrGain applied to this frame: " << frameContext.awb.rGain.value()
> +		<< ", bGain applied to this frame: " << frameContext.awb.bGain.value()
> +		<< "\nrGain to apply: " << context.activeState.awb.rGain.value()
> +		<< ", bGain to apply: " << context.activeState.awb.bGain.value();
>   }
>   
>   REGISTER_IPA_ALGORITHM(Awb, "Awb")
> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
> index 13885eb83b5c..86d060a731cb 100644
> --- a/src/ipa/mali-c55/ipa_context.h
> +++ b/src/ipa/mali-c55/ipa_context.h
> @@ -14,6 +14,8 @@
>   
>   #include <libipa/fc_queue.h>
>   
> +#include "libipa/fixedpoint.h"
> +
>   namespace libcamera {
>   
>   namespace ipa::mali_c55 {
> @@ -53,8 +55,8 @@ struct IPAActiveState {
>   	} agc;
>   
>   	struct {
> -		double rGain;
> -		double bGain;
> +		UQ4_8 rGain;
> +		UQ4_8 bGain;
>   	} awb;
>   };
>   
> @@ -66,8 +68,8 @@ struct IPAFrameContext : public FrameContext {
>   	} agc;
>   
>   	struct {
> -		double rGain;
> -		double bGain;
> +		UQ4_8 rGain;
> +		UQ4_8 bGain;
>   	} awb;
>   };
>
Kieran Bingham Nov. 17, 2025, 12:12 p.m. UTC | #2
Quoting Barnabás Pőcze (2025-11-17 12:08:37)
> Hi
> 
> 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:
> > Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8
> > format. This ensures that reporting of gains in metadata reflect the true
> > AWB gains applied.
> > 
> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > ---
> >   src/ipa/mali-c55/algorithms/awb.cpp | 44 ++++++++++++++---------------
> >   src/ipa/mali-c55/ipa_context.h      | 10 ++++---
> >   2 files changed, 28 insertions(+), 26 deletions(-)
> > 
> > diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
> > index 3d546e5a854b..6e213f43e762 100644
> > --- a/src/ipa/mali-c55/algorithms/awb.cpp
> > +++ b/src/ipa/mali-c55/algorithms/awb.cpp
> > @@ -37,8 +37,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context,
> >        * for the first frame we will make no assumptions and leave the R/B
> >        * channels unmodified.
> >        */
> > -     context.activeState.awb.rGain = 1.0;
> > -     context.activeState.awb.bGain = 1.0;
> > +     context.activeState.awb.rGain = 1.0f;
> > +     context.activeState.awb.bGain = 1.0f;
> >   
> >       return 0;
> >   }
> > @@ -50,8 +50,8 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
> >       block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
> >       block.header->size = sizeof(struct mali_c55_params_awb_gains);
> >   
> > -     double rGain = context.activeState.awb.rGain;
> > -     double bGain = context.activeState.awb.bGain;
> > +     UQ4_8 rGain = context.activeState.awb.rGain;
> > +     UQ4_8 bGain = context.activeState.awb.bGain;
> >   
> >       /*
> >        * The gains here map as follows:
> > @@ -63,10 +63,10 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
> >        * This holds true regardless of the bayer order of the input data, as
> >        * the mapping is done internally in the ISP.
> >        */
> > -     block.awb_gains->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
> > -     block.awb_gains->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> > -     block.awb_gains->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> > -     block.awb_gains->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
> > +     block.awb_gains->gain00 = rGain.quantized();
> > +     block.awb_gains->gain01 = UQ4_8(1.0f).quantized();
> > +     block.awb_gains->gain10 = UQ4_8(1.0f).quantized();
> > +     block.awb_gains->gain11 = bGain.quantized();
> >   
> >       frameContext.awb.rGain = rGain;
> >       frameContext.awb.bGain = bGain;
> > @@ -162,8 +162,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
> >                * The statistics are in Q4.8 format, so we convert to double
> >                * here.
> >                */
> > -             rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr);
> > -             bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br);
> > +             rgSum += UQ4_8(awb_ratios[i].avg_rg_gr).value();
> > +             bgSum += UQ4_8(awb_ratios[i].avg_bg_br).value();
> >               counted_zones++;
> >       }
> >   
> > @@ -186,8 +186,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
> >        * figure by the gains that were applied when the statistics for this
> >        * frame were generated.
> >        */
> > -     double rRatio = rgAvg / frameContext.awb.rGain;
> > -     double bRatio = bgAvg / frameContext.awb.bGain;
> > +     double rRatio = rgAvg / frameContext.awb.rGain.value();
> > +     double bRatio = bgAvg / frameContext.awb.bGain.value();
> >   
> >       /*
> >        * And then we can simply invert the ratio to find the gain we should
> > @@ -203,24 +203,24 @@ void Awb::process(IPAContext &context, const uint32_t frame,
> >        * want to fix the miscolouring as quickly as possible.
> >        */
> >       double speed = frame < kNumStartupFrames ? 1.0 : 0.2;
> > -     rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed);
> > -     bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed);
> > +     rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0 - speed);
> > +     bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0 - speed);
> >   
> > -     context.activeState.awb.rGain = rGain;
> > -     context.activeState.awb.bGain = bGain;
> > +     context.activeState.awb.rGain = static_cast<float>(rGain);
> > +     context.activeState.awb.bGain = static_cast<float>(bGain);
> 
> I'm wondering if it might make sense to replace the `double`s with `float`s.
> But otherwise the transformation looks ok.

Yes, these gains here don't need to be doubles. I'll add a preceeding
patch.

> 
> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> 
> 
> 
> >   
> >       metadata.set(controls::ColourGains, {
> > -             static_cast<float>(frameContext.awb.rGain),
> > -             static_cast<float>(frameContext.awb.bGain),
> > +             frameContext.awb.rGain.value(),
> > +             frameContext.awb.bGain.value(),
> >       });
> >   
> >       LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": "
> >               << "Average R/G Ratio: " << rgAvg
> >               << ", Average B/G Ratio: " << bgAvg
> > -             << "\nrGain applied to this frame: " << frameContext.awb.rGain
> > -             << ", bGain applied to this frame: " << frameContext.awb.bGain
> > -             << "\nrGain to apply: " << context.activeState.awb.rGain
> > -             << ", bGain to apply: " << context.activeState.awb.bGain;
> > +             << "\nrGain applied to this frame: " << frameContext.awb.rGain.value()
> > +             << ", bGain applied to this frame: " << frameContext.awb.bGain.value()
> > +             << "\nrGain to apply: " << context.activeState.awb.rGain.value()
> > +             << ", bGain to apply: " << context.activeState.awb.bGain.value();
> >   }
> >   
> >   REGISTER_IPA_ALGORITHM(Awb, "Awb")
> > diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
> > index 13885eb83b5c..86d060a731cb 100644
> > --- a/src/ipa/mali-c55/ipa_context.h
> > +++ b/src/ipa/mali-c55/ipa_context.h
> > @@ -14,6 +14,8 @@
> >   
> >   #include <libipa/fc_queue.h>
> >   
> > +#include "libipa/fixedpoint.h"
> > +
> >   namespace libcamera {
> >   
> >   namespace ipa::mali_c55 {
> > @@ -53,8 +55,8 @@ struct IPAActiveState {
> >       } agc;
> >   
> >       struct {
> > -             double rGain;
> > -             double bGain;
> > +             UQ4_8 rGain;
> > +             UQ4_8 bGain;
> >       } awb;
> >   };
> >   
> > @@ -66,8 +68,8 @@ struct IPAFrameContext : public FrameContext {
> >       } agc;
> >   
> >       struct {
> > -             double rGain;
> > -             double bGain;
> > +             UQ4_8 rGain;
> > +             UQ4_8 bGain;
> >       } awb;
> >   };
> >   
>
Isaac Scott Nov. 18, 2025, 12:41 p.m. UTC | #3
Hi Kieran,

Thank you for the patch!

Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>

Quoting Kieran Bingham (2025-11-14 00:54:21)
> Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8
> format. This ensures that reporting of gains in metadata reflect the true
> AWB gains applied.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  src/ipa/mali-c55/algorithms/awb.cpp | 44 ++++++++++++++---------------
>  src/ipa/mali-c55/ipa_context.h      | 10 ++++---
>  2 files changed, 28 insertions(+), 26 deletions(-)
> 
> diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
> index 3d546e5a854b..6e213f43e762 100644
> --- a/src/ipa/mali-c55/algorithms/awb.cpp
> +++ b/src/ipa/mali-c55/algorithms/awb.cpp
> @@ -37,8 +37,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context,
>          * for the first frame we will make no assumptions and leave the R/B
>          * channels unmodified.
>          */
> -       context.activeState.awb.rGain = 1.0;
> -       context.activeState.awb.bGain = 1.0;
> +       context.activeState.awb.rGain = 1.0f;
> +       context.activeState.awb.bGain = 1.0f;
>  
>         return 0;
>  }
> @@ -50,8 +50,8 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
>         block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
>         block.header->size = sizeof(struct mali_c55_params_awb_gains);
>  
> -       double rGain = context.activeState.awb.rGain;
> -       double bGain = context.activeState.awb.bGain;
> +       UQ4_8 rGain = context.activeState.awb.rGain;
> +       UQ4_8 bGain = context.activeState.awb.bGain;
>  
>         /*
>          * The gains here map as follows:
> @@ -63,10 +63,10 @@ size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
>          * This holds true regardless of the bayer order of the input data, as
>          * the mapping is done internally in the ISP.
>          */
> -       block.awb_gains->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
> -       block.awb_gains->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> -       block.awb_gains->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
> -       block.awb_gains->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
> +       block.awb_gains->gain00 = rGain.quantized();
> +       block.awb_gains->gain01 = UQ4_8(1.0f).quantized();
> +       block.awb_gains->gain10 = UQ4_8(1.0f).quantized();
> +       block.awb_gains->gain11 = bGain.quantized();
>  
>         frameContext.awb.rGain = rGain;
>         frameContext.awb.bGain = bGain;
> @@ -162,8 +162,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>                  * The statistics are in Q4.8 format, so we convert to double
>                  * here.
>                  */
> -               rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr);
> -               bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br);
> +               rgSum += UQ4_8(awb_ratios[i].avg_rg_gr).value();
> +               bgSum += UQ4_8(awb_ratios[i].avg_bg_br).value();
>                 counted_zones++;
>         }
>  
> @@ -186,8 +186,8 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>          * figure by the gains that were applied when the statistics for this
>          * frame were generated.
>          */
> -       double rRatio = rgAvg / frameContext.awb.rGain;
> -       double bRatio = bgAvg / frameContext.awb.bGain;
> +       double rRatio = rgAvg / frameContext.awb.rGain.value();
> +       double bRatio = bgAvg / frameContext.awb.bGain.value();
>  
>         /*
>          * And then we can simply invert the ratio to find the gain we should
> @@ -203,24 +203,24 @@ void Awb::process(IPAContext &context, const uint32_t frame,
>          * want to fix the miscolouring as quickly as possible.
>          */
>         double speed = frame < kNumStartupFrames ? 1.0 : 0.2;
> -       rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed);
> -       bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed);
> +       rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0 - speed);
> +       bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0 - speed);
>  
> -       context.activeState.awb.rGain = rGain;
> -       context.activeState.awb.bGain = bGain;
> +       context.activeState.awb.rGain = static_cast<float>(rGain);
> +       context.activeState.awb.bGain = static_cast<float>(bGain);
>  
>         metadata.set(controls::ColourGains, {
> -               static_cast<float>(frameContext.awb.rGain),
> -               static_cast<float>(frameContext.awb.bGain),
> +               frameContext.awb.rGain.value(),
> +               frameContext.awb.bGain.value(),
>         });
>  
>         LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": "
>                 << "Average R/G Ratio: " << rgAvg
>                 << ", Average B/G Ratio: " << bgAvg
> -               << "\nrGain applied to this frame: " << frameContext.awb.rGain
> -               << ", bGain applied to this frame: " << frameContext.awb.bGain
> -               << "\nrGain to apply: " << context.activeState.awb.rGain
> -               << ", bGain to apply: " << context.activeState.awb.bGain;
> +               << "\nrGain applied to this frame: " << frameContext.awb.rGain.value()
> +               << ", bGain applied to this frame: " << frameContext.awb.bGain.value()
> +               << "\nrGain to apply: " << context.activeState.awb.rGain.value()
> +               << ", bGain to apply: " << context.activeState.awb.bGain.value();
>  }
>  
>  REGISTER_IPA_ALGORITHM(Awb, "Awb")
> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
> index 13885eb83b5c..86d060a731cb 100644
> --- a/src/ipa/mali-c55/ipa_context.h
> +++ b/src/ipa/mali-c55/ipa_context.h
> @@ -14,6 +14,8 @@
>  
>  #include <libipa/fc_queue.h>
>  
> +#include "libipa/fixedpoint.h"
> +
>  namespace libcamera {
>  
>  namespace ipa::mali_c55 {
> @@ -53,8 +55,8 @@ struct IPAActiveState {
>         } agc;
>  
>         struct {
> -               double rGain;
> -               double bGain;
> +               UQ4_8 rGain;
> +               UQ4_8 bGain;
>         } awb;
>  };
>  
> @@ -66,8 +68,8 @@ struct IPAFrameContext : public FrameContext {
>         } agc;
>  
>         struct {
> -               double rGain;
> -               double bGain;
> +               UQ4_8 rGain;
> +               UQ4_8 bGain;
>         } awb;
>  };
>  
> -- 
> 2.51.1
>

Patch
diff mbox series

diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
index 3d546e5a854b..6e213f43e762 100644
--- a/src/ipa/mali-c55/algorithms/awb.cpp
+++ b/src/ipa/mali-c55/algorithms/awb.cpp
@@ -37,8 +37,8 @@  int Awb::configure([[maybe_unused]] IPAContext &context,
 	 * for the first frame we will make no assumptions and leave the R/B
 	 * channels unmodified.
 	 */
-	context.activeState.awb.rGain = 1.0;
-	context.activeState.awb.bGain = 1.0;
+	context.activeState.awb.rGain = 1.0f;
+	context.activeState.awb.bGain = 1.0f;
 
 	return 0;
 }
@@ -50,8 +50,8 @@  size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
 	block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
 	block.header->size = sizeof(struct mali_c55_params_awb_gains);
 
-	double rGain = context.activeState.awb.rGain;
-	double bGain = context.activeState.awb.bGain;
+	UQ4_8 rGain = context.activeState.awb.rGain;
+	UQ4_8 bGain = context.activeState.awb.bGain;
 
 	/*
 	 * The gains here map as follows:
@@ -63,10 +63,10 @@  size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context
 	 * This holds true regardless of the bayer order of the input data, as
 	 * the mapping is done internally in the ISP.
 	 */
-	block.awb_gains->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
-	block.awb_gains->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
-	block.awb_gains->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
-	block.awb_gains->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
+	block.awb_gains->gain00 = rGain.quantized();
+	block.awb_gains->gain01 = UQ4_8(1.0f).quantized();
+	block.awb_gains->gain10 = UQ4_8(1.0f).quantized();
+	block.awb_gains->gain11 = bGain.quantized();
 
 	frameContext.awb.rGain = rGain;
 	frameContext.awb.bGain = bGain;
@@ -162,8 +162,8 @@  void Awb::process(IPAContext &context, const uint32_t frame,
 		 * The statistics are in Q4.8 format, so we convert to double
 		 * here.
 		 */
-		rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr);
-		bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br);
+		rgSum += UQ4_8(awb_ratios[i].avg_rg_gr).value();
+		bgSum += UQ4_8(awb_ratios[i].avg_bg_br).value();
 		counted_zones++;
 	}
 
@@ -186,8 +186,8 @@  void Awb::process(IPAContext &context, const uint32_t frame,
 	 * figure by the gains that were applied when the statistics for this
 	 * frame were generated.
 	 */
-	double rRatio = rgAvg / frameContext.awb.rGain;
-	double bRatio = bgAvg / frameContext.awb.bGain;
+	double rRatio = rgAvg / frameContext.awb.rGain.value();
+	double bRatio = bgAvg / frameContext.awb.bGain.value();
 
 	/*
 	 * And then we can simply invert the ratio to find the gain we should
@@ -203,24 +203,24 @@  void Awb::process(IPAContext &context, const uint32_t frame,
 	 * want to fix the miscolouring as quickly as possible.
 	 */
 	double speed = frame < kNumStartupFrames ? 1.0 : 0.2;
-	rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed);
-	bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed);
+	rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0 - speed);
+	bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0 - speed);
 
-	context.activeState.awb.rGain = rGain;
-	context.activeState.awb.bGain = bGain;
+	context.activeState.awb.rGain = static_cast<float>(rGain);
+	context.activeState.awb.bGain = static_cast<float>(bGain);
 
 	metadata.set(controls::ColourGains, {
-		static_cast<float>(frameContext.awb.rGain),
-		static_cast<float>(frameContext.awb.bGain),
+		frameContext.awb.rGain.value(),
+		frameContext.awb.bGain.value(),
 	});
 
 	LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": "
 		<< "Average R/G Ratio: " << rgAvg
 		<< ", Average B/G Ratio: " << bgAvg
-		<< "\nrGain applied to this frame: " << frameContext.awb.rGain
-		<< ", bGain applied to this frame: " << frameContext.awb.bGain
-		<< "\nrGain to apply: " << context.activeState.awb.rGain
-		<< ", bGain to apply: " << context.activeState.awb.bGain;
+		<< "\nrGain applied to this frame: " << frameContext.awb.rGain.value()
+		<< ", bGain applied to this frame: " << frameContext.awb.bGain.value()
+		<< "\nrGain to apply: " << context.activeState.awb.rGain.value()
+		<< ", bGain to apply: " << context.activeState.awb.bGain.value();
 }
 
 REGISTER_IPA_ALGORITHM(Awb, "Awb")
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index 13885eb83b5c..86d060a731cb 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -14,6 +14,8 @@ 
 
 #include <libipa/fc_queue.h>
 
+#include "libipa/fixedpoint.h"
+
 namespace libcamera {
 
 namespace ipa::mali_c55 {
@@ -53,8 +55,8 @@  struct IPAActiveState {
 	} agc;
 
 	struct {
-		double rGain;
-		double bGain;
+		UQ4_8 rGain;
+		UQ4_8 bGain;
 	} awb;
 };
 
@@ -66,8 +68,8 @@  struct IPAFrameContext : public FrameContext {
 	} agc;
 
 	struct {
-		double rGain;
-		double bGain;
+		UQ4_8 rGain;
+		UQ4_8 bGain;
 	} awb;
 };