diff --git a/src/ipa/raspberrypi/controller/rpi/awb.cpp b/src/ipa/raspberrypi/controller/rpi/awb.cpp
index 1c65eda8..bb637f10 100644
--- a/src/ipa/raspberrypi/controller/rpi/awb.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/awb.cpp
@@ -175,9 +175,9 @@ void Awb::Initialise()
 
 unsigned int Awb::GetConvergenceFrames() const
 {
-	// If colour gains have been explicitly set, there is no convergence
+	// If not in auto mode, there is no convergence
 	// to happen, so no need to drop any frames - return zero.
-	if (manual_r_ && manual_b_)
+	if (!isAutoEnabled())
 		return 0;
 	else
 		return config_.convergence_frames;
@@ -193,38 +193,47 @@ void Awb::SetManualGains(double manual_r, double manual_b)
 	// If any of these are 0.0, we swich back to auto.
 	manual_r_ = manual_r;
 	manual_b_ = manual_b;
+	// If not in auto mode, set these values into the sync_results which
+	// means that Prepare() will adopt them immediately.
+	if (!isAutoEnabled()) {
+		sync_results_.gain_r = prev_sync_results_.gain_r = manual_r_;
+		sync_results_.gain_g = prev_sync_results_.gain_g = 1.0;
+		sync_results_.gain_b = prev_sync_results_.gain_b = manual_b_;
+	}
 }
 
 void Awb::SwitchMode([[maybe_unused]] CameraMode const &camera_mode,
 		     Metadata *metadata)
 {
-	// If fixed colour gains have been set, we should let other algorithms
-	// know by writing it into the image metadata.
-	if (manual_r_ != 0.0 && manual_b_ != 0.0) {
-		prev_sync_results_.gain_r = manual_r_;
-		prev_sync_results_.gain_g = 1.0;
-		prev_sync_results_.gain_b = manual_b_;
-		// If we're starting up for the first time, try and
-		// "dead reckon" the corresponding colour temperature.
-		if (first_switch_mode_ && config_.bayes) {
-			Pwl ct_r_inverse = config_.ct_r.Inverse();
-			Pwl ct_b_inverse = config_.ct_b.Inverse();
-			double ct_r = ct_r_inverse.Eval(ct_r_inverse.Domain().Clip(1 / manual_r_));
-			double ct_b = ct_b_inverse.Eval(ct_b_inverse.Domain().Clip(1 / manual_b_));
-			prev_sync_results_.temperature_K = (ct_r + ct_b) / 2;
-		}
-		sync_results_ = prev_sync_results_;
+	// On the first mode switch we'll have no meaningful colour
+	// temperature, so try to dead reckon one if in manual mode.
+	if (!isAutoEnabled() && first_switch_mode_ && config_.bayes) {
+		Pwl ct_r_inverse = config_.ct_r.Inverse();
+		Pwl ct_b_inverse = config_.ct_b.Inverse();
+		double ct_r = ct_r_inverse.Eval(ct_r_inverse.Domain().Clip(1 / manual_r_));
+		double ct_b = ct_b_inverse.Eval(ct_b_inverse.Domain().Clip(1 / manual_b_));
+		prev_sync_results_.temperature_K = (ct_r + ct_b) / 2;
+		sync_results_.temperature_K = prev_sync_results_.temperature_K;
 	}
+	// Let other algorithms know the current white balance values.
 	metadata->Set("awb.status", prev_sync_results_);
 	first_switch_mode_ = false;
 }
 
+bool Awb::isAutoEnabled() const
+{
+	return manual_r_ == 0.0 || manual_b_ == 0.0;
+}
+
 void Awb::fetchAsyncResults()
 {
 	LOG(RPiAwb, Debug) << "Fetch AWB results";
 	async_finished_ = false;
 	async_started_ = false;
-	sync_results_ = async_results_;
+	// It's possible manual gains could be set even while the async
+	// thread was running, so only copy the results if still in auto mode.
+	if (isAutoEnabled())
+		sync_results_ = async_results_;
 }
 
 void Awb::restartAsync(StatisticsPtr &stats, double lux)
@@ -289,8 +298,10 @@ void Awb::Process(StatisticsPtr &stats, Metadata *image_metadata)
 	if (frame_phase_ < (int)config_.frame_period)
 		frame_phase_++;
 	LOG(RPiAwb, Debug) << "frame_phase " << frame_phase_;
-	if (frame_phase_ >= (int)config_.frame_period ||
-	    frame_count_ < (int)config_.startup_frames) {
+	// We do not restart the async thread if we're not in auto mode.
+	if (isAutoEnabled() &&
+	    (frame_phase_ >= (int)config_.frame_period ||
+	     frame_count_ < (int)config_.startup_frames)) {
 		// Update any settings and any image metadata that we need.
 		struct LuxStatus lux_status = {};
 		lux_status.lux = 400; // in case no metadata
@@ -614,29 +625,18 @@ void Awb::awbGrey()
 
 void Awb::doAwb()
 {
-	if (manual_r_ != 0.0 && manual_b_ != 0.0) {
-		async_results_.temperature_K = 4500; // don't know what it is
-		async_results_.gain_r = manual_r_;
-		async_results_.gain_g = 1.0;
-		async_results_.gain_b = manual_b_;
+	prepareStats();
+	LOG(RPiAwb, Debug) << "Valid zones: " << zones_.size();
+	if (zones_.size() > config_.min_regions) {
+		if (config_.bayes)
+			awbBayes();
+		else
+			awbGrey();
 		LOG(RPiAwb, Debug)
-			<< "Using manual white balance: gain_r "
-			<< async_results_.gain_r << " gain_b "
-			<< async_results_.gain_b;
-	} else {
-		prepareStats();
-		LOG(RPiAwb, Debug) << "Valid zones: " << zones_.size();
-		if (zones_.size() > config_.min_regions) {
-			if (config_.bayes)
-				awbBayes();
-			else
-				awbGrey();
-			LOG(RPiAwb, Debug)
-				<< "CT found is "
-				<< async_results_.temperature_K
-				<< " with gains r " << async_results_.gain_r
-				<< " and b " << async_results_.gain_b;
-		}
+			<< "CT found is "
+			<< async_results_.temperature_K
+			<< " with gains r " << async_results_.gain_r
+			<< " and b " << async_results_.gain_b;
 	}
 }
 
diff --git a/src/ipa/raspberrypi/controller/rpi/awb.hpp b/src/ipa/raspberrypi/controller/rpi/awb.hpp
index f113c642..45ba9e25 100644
--- a/src/ipa/raspberrypi/controller/rpi/awb.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/awb.hpp
@@ -108,6 +108,7 @@ public:
 	};
 
 private:
+	bool isAutoEnabled() const;
 	// configuration is read-only, and available to both threads
 	AwbConfig config_;
 	std::thread async_thread_;
