diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
index 9f95a61c..2438d68d 100644
--- a/src/ipa/ipu3/ipa_context.cpp
+++ b/src/ipa/ipu3/ipa_context.cpp
@@ -222,13 +222,33 @@ IPAFrameContext::IPAFrameContext(uint32_t id, const ControlList &reqControls)
  * \brief A FIFO circular queue holding IPAFrameContext(s)
  *
  * FCQueue holds all the IPAFrameContext(s) related to frames required
- * to be processed by the IPA at a given point.
+ * to be processed by the IPA at a given point. An IPAFrameContext is created
+ * on the first call FCQueue::get(frame) for that frame. Subsequent calls to
+ * FCQueue::get() with the same frame number shall return the IPAFrameContext
+ * previously created until the frame is marked as complete through
+ * FCQueue::completeFrame(frame).
+ */
+
+/**
+ * \var FCQueue::head_
+ * \brief A pointer to the a IPAFrameContext next expected to complete
+ */
+
+/**
+ * \var FCQueue::tail_
+ * \brief A pointer to the latest IPAFrameContext created
+ */
+
+/**
+ * \var FCQueue::isFull_
+ * \brief Flag set when the FCQueue is full
  */
 
 /**
  * \brief FCQueue constructor
  */
 FCQueue::FCQueue()
+	: head_(nullptr), tail_(nullptr), isFull_(false)
 {
 	clear();
 }
@@ -238,21 +258,75 @@ FCQueue::FCQueue()
  * \param[in] frame Frame number for which the IPAFrameContext needs to
  * retrieved
  *
- * \return Pointer to the IPAFrameContext
+ * This function returns the IPAFrameContext for the desired frame. If the
+ * frame context does not exist in the queue, the next available slot in the
+ * queue is returned. It is the responsibility of the caller to fill the correct
+ * IPAFrameContext parameters of newly returned IPAFrameContext.
+ *
+ * \return Pointer to the IPAFrameContext or nullptr if frame context couldn't
+ * be created
  */
 IPAFrameContext *FCQueue::get(uint32_t frame)
 {
-	IPAFrameContext &frameContext = this->at(frame % kMaxFrameContexts);
+	if (frame <= tail_->frame) {
+		IPAFrameContext &frameContext = this->at(frame % kMaxFrameContexts);
+		if (frame != frameContext.frame)
+			LOG(IPAIPU3, Warning)
+				<< "Got wrong frame context for frame " << frame;
+
+		return &frameContext;
+	} else {
+		if (isFull_) {
+			LOG(IPAIPU3, Warning)
+				<< "Cannot create frame context for frame " << frame
+				<< " since FCQueue is full";
+
+			return nullptr;
+		}
+
+		/*
+		 * Frame context doesn't exist yet so get the next available
+		 * position in the queue.
+		 */
+		tail_ = &this->at((tail_->frame + 1) % kMaxFrameContexts);
+		tail_->frame = frame;
+
+		/* Check if the queue is full to avoid over-queueing later */
+		if (tail_->frame - head_->frame >= kMaxFrameContexts - 1)
+			isFull_ = true;
 
-	if (frame != frameContext.frame) {
+		return tail_;
+	}
+}
+
+/**
+ * \brief Notifies the FCQueue that a frame has been completed
+ * \param[in] frame The completed frame number
+ */
+void FCQueue::completeFrame(uint32_t frame)
+{
+	if (head_->frame != frame)
 		LOG(IPAIPU3, Warning)
-			<< "Got wrong frame context for frame" << frame
-			<< " or frame context doesn't exist yet";
+			<< "Frame " << frame << " completed out-of-sync?";
+
+	if (frame > tail_->frame) {
+		LOG(IPAIPU3, Error)
+			<< "Completing a frame " << frame
+			<< " not present in the queue is disallowed";
+		return;
 	}
 
-	return &frameContext;
+	head_ = this->get(frame + 1);
+
+	if (isFull_)
+		isFull_ = false;
 }
 
+/**
+ * \fn FCQueue::isFull()
+ * \brief Checks whether the frame context queue is full
+ */
+
 /**
  * \brief Clear the FCQueue by resetting all the entries in the ring-buffer
  */
@@ -260,6 +334,13 @@ void FCQueue::clear()
 {
 	IPAFrameContext initFrameContext;
 	this->fill(initFrameContext);
+
+	isFull_ = false;
+
+	/* Intialise 0th index to frame 0 */
+	this->at(0).frame = 0;
+	tail_ = &this->at(0);
+	head_ = tail_;
 }
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
index 56d281f6..475855da 100644
--- a/src/ipa/ipu3/ipa_context.h
+++ b/src/ipa/ipu3/ipa_context.h
@@ -93,9 +93,18 @@ class FCQueue : public std::array<IPAFrameContext, kMaxFrameContexts>
 {
 public:
 	FCQueue();
+	FCQueue &operator[](const FCQueue &) = delete;
 
 	void clear();
+	void completeFrame(uint32_t frame);
 	IPAFrameContext *get(uint32_t frame);
+	bool isFull() { return isFull_; }
+
+private:
+	IPAFrameContext *head_;
+	IPAFrameContext *tail_;
+
+	bool isFull_;
 };
 
 struct IPAContext {
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 0843d882..1d6ee515 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -605,6 +605,8 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 	 */
 
 	metadataReady.emit(frame, ctrls);
+
+	context_.frameContexts.completeFrame(frame);
 }
 
 /**
@@ -618,7 +620,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls)
 {
 	/* \todo Start processing for 'frame' based on 'controls'. */
-	context_.frameContexts[frame % kMaxFrameContexts] = { frame, controls };
+	*context_.frameContexts.get(frame) = { frame, controls };
 }
 
 /**
