diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp
index 4835a034..c57d3e18 100644
--- a/src/ipa/ipu3/algorithms/af.cpp
+++ b/src/ipa/ipu3/algorithms/af.cpp
@@ -21,6 +21,8 @@
 
 #include <libcamera/base/log.h>
 
+#include <libcamera/control_ids.h>
+
 #include <libcamera/ipa/core_ipa_interface.h>
 
 #include "libipa/histogram.h"
@@ -109,7 +111,8 @@ static struct ipu3_uapi_af_filter_config afFilterConfigDefault = {
  */
 Af::Af()
 	: focus_(0), bestFocus_(0), currentVariance_(0.0), previousVariance_(0.0),
-	  coarseCompleted_(false), fineCompleted_(false)
+	  coarseCompleted_(false), fineCompleted_(false), maxChange_(kMaxChange),
+	  afMode_(controls::AfModeAuto)
 {
 }
 
@@ -194,6 +197,69 @@ int Af::configure(IPAContext &context, const IPAConfigInfo &configInfo)
 	return 0;
 }
 
+/**
+ * \brief AF controls handler
+ *
+ * Put the control parameter to the corresponding variables when receiving the controls
+ * from the user.
+ * \param[in] context The shared IPA context
+ * \param[in] frame Frame number
+ * \param[in] controls control list of the request
+ */
+void Af::queueRequest([[maybe_unused]] IPAContext &context,
+		      [[maybe_unused]] const uint32_t frame,
+		      const ControlList &controls)
+{
+	for (auto const &ctrl : controls) {
+		unsigned int ctrlEnum = ctrl.first;
+		const ControlValue &ctrlValue = ctrl.second;
+
+		switch (ctrlEnum) {
+		case controls::AF_MODE:
+			afModeSet(ctrlValue.get<int32_t>());
+			break;
+		case controls::LENS_POSITION:
+			lensPosition_ = ctrlValue.get<float>();
+		}
+	}
+}
+
+/**
+ * \brief AF Mode set
+ *
+ * Set AF mode, including manual, auto, and continuous.
+ *
+ * \param[in] AF operation mode 0, 1, 2 are manual, auto, and continuous, respectively.
+ */
+
+void Af::afModeSet(uint32_t mode)
+{
+	switch (mode) {
+	case controls::AfModeManual:
+	case controls::AfModeAuto:
+		afMode_ = mode;
+		break;
+	case controls::AfModeContinuous:
+		afMode_ = mode;
+		maxChange_ = 0.05;
+		break;
+	default:
+		afMode_ = controls::AfModeAuto;
+	}
+
+	LOG(IPU3Af, Debug) << "AfMode set " << mode;
+}
+
+/**
+ * \brief Set lens position
+ *
+ * Set user-defined lens position to the focus steps.
+ */
+void Af::afLensPositionSet(IPAContext &context)
+{
+	context.activeState.af.focus = kMaxFocusSteps * lensPosition_;
+}
+
 /**
  * \brief AF coarse scan
  *
@@ -397,7 +463,7 @@ bool Af::afIsOutOfFocus(IPAContext context)
 			   << " Current VCM step: "
 			   << context.activeState.af.focus;
 
-	if (var_ratio > kMaxChange)
+	if (var_ratio > maxChange_)
 		return true;
 	else
 		return false;
@@ -432,21 +498,29 @@ void Af::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameCon
 	Span<const y_table_item_t> y_items(reinterpret_cast<const y_table_item_t *>(&stats->af_raw_buffer.y_table),
 					   afRawBufferLen);
 
-	/*
-	 * Calculate the mean and the variance of AF statistics for a given grid.
-	 * For coarse: y1 are used.
-	 * For fine: y2 results are used.
-	 */
-	currentVariance_ = afEstimateVariance(y_items, !coarseCompleted_);
+	switch (afMode_) {
+	case controls::AfModeManual:
+		afLensPositionSet(context);
+		break;
+	case controls::AfModeContinuous:
+	case controls::AfModeAuto:
+	default:
+		/*
+		 * Calculate the mean and the variance of AF statistics for a given grid.
+		 * For coarse: y1 are used.
+		 * For fine: y2 results are used.
+		 */
+		currentVariance_ = afEstimateVariance(y_items, !coarseCompleted_);
 
-	if (!context.activeState.af.stable) {
-		afCoarseScan(context);
-		afFineScan(context);
-	} else {
-		if (afIsOutOfFocus(context))
-			afReset(context);
-		else
-			afIgnoreFrameReset();
+		if (!context.activeState.af.stable) {
+			afCoarseScan(context);
+			afFineScan(context);
+		} else {
+			if (afIsOutOfFocus(context))
+				afReset(context);
+			else
+				afIgnoreFrameReset();
+		}
 	}
 }
 
diff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h
index ccf015f3..77bab2a7 100644
--- a/src/ipa/ipu3/algorithms/af.h
+++ b/src/ipa/ipu3/algorithms/af.h
@@ -26,6 +26,7 @@ class Af : public Algorithm
 		uint16_t y1_avg;
 		uint16_t y2_avg;
 	} y_table_item_t;
+
 public:
 	Af();
 	~Af() = default;
@@ -34,6 +35,9 @@ public:
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void process(IPAContext &context, IPAFrameContext *frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
+	void queueRequest([[maybe_unused]] IPAContext &context,
+			  [[maybe_unused]] const uint32_t frame,
+			  [[maybe_unused]] const ControlList &controls) override;
 
 private:
 	void afCoarseScan(IPAContext &context);
@@ -46,6 +50,11 @@ private:
 
 	bool afIsOutOfFocus(IPAContext context);
 
+	void afModeSet(uint32_t mode);
+	void afModeGet();
+
+	void afLensPositionSet(IPAContext &context);
+
 	/* VCM step configuration. It is the current setting of the VCM step. */
 	uint32_t focus_;
 	/* The best VCM step. It is a local optimum VCM step during scanning. */
@@ -62,6 +71,12 @@ private:
 	bool coarseCompleted_;
 	/* If the fine scan completes, it is set to true. */
 	bool fineCompleted_;
+	/* Max focus change ratio to determine */
+	double maxChange_;
+	/* Relative lens position in percentage. */
+	double lensPosition_;
+	/* Af operation mode. */
+	uint32_t afMode_;
 };
 
 } /* namespace ipa::ipu3::algorithms */
