diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h
index 519f7160..5bc14f4e 100644
--- a/include/libcamera/ipa/raspberrypi.h
+++ b/include/libcamera/ipa/raspberrypi.h
@@ -46,7 +46,8 @@ static const ControlInfoMap Controls({
 		{ &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },
 		{ &controls::FrameDurationLimits, ControlInfo(INT64_C(1000), INT64_C(1000000000)) },
 		{ &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) },
-		{ &controls::AfState, ControlInfo(controls::AfStateValues) }
+		{ &controls::AfMode, ControlInfo(controls::AfModeValues) },
+		{ &controls::AfState, ControlInfo(controls::AfStateValues) },
 	}, controls::controls);
 
 } /* namespace RPi */
diff --git a/src/ipa/raspberrypi/controller/iob/af.cpp b/src/ipa/raspberrypi/controller/iob/af.cpp
index b03e52c5..e09514c4 100644
--- a/src/ipa/raspberrypi/controller/iob/af.cpp
+++ b/src/ipa/raspberrypi/controller/iob/af.cpp
@@ -40,7 +40,7 @@ Af::Af(Controller *controller)
 	: AfAlgorithm(controller), focus_(0), bestFocus_(0),
 	  currentContrast_(0.0), previousContrast_(0.0), maxContrast_(0.0),
 	  maxStep_(0), coarseCompleted_(false), fineCompleted_(false),
-	  mode_(0)
+	  mode_(libcamera::controls::AfModeManual)
 {
 }
 
@@ -49,9 +49,13 @@ char const *Af::Name() const
 	return NAME;
 }
 
-void Af::SetMode([[maybe_unused]] const uint32_t &mode)
+void Af::SetMode(const uint32_t &mode)
 {
-	mode_ = mode;
+	if (mode != mode_) {
+		LOG(IoBAf, Debug) << "Switched AF mode from " << mode_
+				  << " to " << mode;
+		mode_ = mode;
+	}
 }
 
 void Af::Trigger()
@@ -78,7 +82,7 @@ void Af::Initialise()
 {
 	status_.lensPosition = 0.0;
 	maxContrast_ = 0.0;
-	status_.state = libcamera::controls::AfStateScanning;
+	status_.state = libcamera::controls::AfStateIdle;
 }
 
 void Af::Prepare(Metadata *image_metadata)
@@ -162,7 +166,7 @@ void Af::afReset()
 	LOG(IoBAf, Debug) << "Reset AF parameters";
 	status_.lensPosition = 0;
 	focus_ = 0;
-	status_.state = libcamera::controls::AfStateIdle;
+	status_.state = libcamera::controls::AfStateScanning;
 	previousContrast_ = 0.0;
 	coarseCompleted_ = false;
 	fineCompleted_ = false;
@@ -195,12 +199,18 @@ void Af::Process(StatisticsPtr &stats, [[maybe_unused]] Metadata *image_metadata
 		currentContrast_ += stats->focus_stats[i].contrast_val[1][1]
 				  / stats->focus_stats[i].contrast_val_num[1][1];
 
+	/* Depending on the mode, we may or may not process the stats */
+	if (status_.state == libcamera::controls::AfStateIdle)
+	    return;
+
 	if (status_.state != libcamera::controls::AfStateFocused) {
 		afCoarseScan();
 		afFineScan();
 	} else {
-		if (afIsOutOfFocus())
-			afReset();
+		/* We can re-start the scan at any moment in AfModeContinuous */
+		if (mode_ == libcamera::controls::AfModeContinuous)
+			if (afIsOutOfFocus())
+				afReset();
 	}
 }
 
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index a4e1c834..226388a7 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -30,6 +30,7 @@
 
 #include "libcamera/internal/mapped_framebuffer.h"
 
+#include "af_algorithm.hpp"
 #include "af_status.h"
 #include "agc_algorithm.hpp"
 #include "agc_status.h"
@@ -956,6 +957,20 @@ void IPARPi::queueRequest(const ControlList &controls)
 			break;
 		}
 
+		case controls::AF_MODE: {
+			RPiController::AfAlgorithm *af = dynamic_cast<RPiController::AfAlgorithm *>(
+				controller_.GetAlgorithm("iob.af"));
+			if (!af) {
+				LOG(IPARPI, Warning)
+					<< "Could not set AF_MODE - no AF algorithm";
+				break;
+			}
+
+			int32_t idx = ctrl.second.get<int32_t>();
+			af->SetMode(idx);
+			break;
+		}
+
 		default:
 			LOG(IPARPI, Warning)
 				<< "Ctrl " << controls::controls.at(ctrl.first)->name()
