diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index ae64b247..a6ba4a07 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -33,6 +33,8 @@
 #include "libcamera/internal/shared_mem_object.h"
 #include "libcamera/internal/software_isp/debayer_params.h"
 
+#include "software_isp/swstats_cpu.h"
+
 namespace libcamera {
 
 class DebayerCpu;
@@ -100,6 +102,9 @@ private:
 	DebayerParams debayerParams_;
 	std::queue<uint32_t> availableParams_;
 	bool allocateParamsBuffers(const unsigned int bufferCount);
+	std::unique_ptr<SwStatsCpu> allocateStatsBuffers(
+		std::vector<SharedFD> &fdStats,
+		const unsigned int bufferCount);
 	DmaBufAllocator dmaHeap_;
 
 	std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index 5d9f5008..1e224b6f 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -12,6 +12,8 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <utility>
+#include <vector>
 
 #include <libcamera/base/shared_fd.h>
 
@@ -20,7 +22,9 @@
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/shared_mem_object.h"
 #include "libcamera/internal/software_isp/debayer_params.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
 
 #include "debayer_cpu.h"
 
@@ -79,17 +83,15 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
 	if (!allocateParamsBuffers(bufferCount))
 		return;
 
-	auto stats = std::make_unique<SwStatsCpu>();
-	if (!stats->isValid()) {
-		LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
-		return;
-	}
-	stats->statsReady.connect(this, &SoftwareIsp::statsReady);
-
 	std::vector<SharedFD> fdParams;
 	for (auto &item : sharedParams_)
 		fdParams.emplace_back(item.second.fd());
 
+	std::vector<SharedFD> fdStats;
+	auto stats = allocateStatsBuffers(fdStats, bufferCount);
+	if (!stats)
+		return;
+
 	debayer_ = std::make_unique<DebayerCpu>(std::move(stats), fdParams);
 	debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
 	debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
@@ -169,6 +171,34 @@ bool SoftwareIsp::allocateParamsBuffers(const unsigned int bufferCount)
 	return true;
 }
 
+std::unique_ptr<SwStatsCpu> SoftwareIsp::allocateStatsBuffers(
+	std::vector<SharedFD> &fdStats,
+	const unsigned int bufferCount)
+{
+	auto sharedStats = std::make_unique<std::map<uint32_t, SharedMemObject<SwIspStats>>>();
+	for (unsigned int i = 0; i < bufferCount; i++) {
+		auto shared = SharedMemObject<SwIspStats>("softIsp_stats");
+		if (!shared) {
+			LOG(SoftwareIsp, Error) << "Failed to create shared memory for statistics";
+			return std::unique_ptr<SwStatsCpu>();
+		}
+		if (!shared.fd().isValid()) {
+			LOG(SoftwareIsp, Error) << "Invalid fd of shared statistics";
+			return std::unique_ptr<SwStatsCpu>();
+		}
+
+		ASSERT(shared.fd().get() >= 0);
+		unsigned int bufferId = shared.fd().get();
+		fdStats.emplace_back(shared.fd());
+		sharedStats->emplace(bufferId, std::move(shared));
+	}
+
+	auto stats = std::make_unique<SwStatsCpu>(std::move(sharedStats));
+	stats->statsReady.connect(this, &SoftwareIsp::statsReady);
+
+	return stats;
+}
+
 /**
  * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
  * \brief Load a configuration from a file
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
index b8ee06a0..cb411a18 100644
--- a/src/libcamera/software_isp/swstats_cpu.cpp
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -11,6 +11,8 @@
 
 #include "swstats_cpu.h"
 
+#include <memory>
+
 #include <libcamera/base/log.h>
 
 #include <libcamera/stream.h>
@@ -129,12 +131,14 @@ namespace libcamera {
 
 LOG_DEFINE_CATEGORY(SwStatsCpu)
 
-SwStatsCpu::SwStatsCpu()
-	: sharedStats_("softIsp_stats")
+/**
+ * \brief The constructor of SwStatsCpu
+ * \param [in] sharedStats Mapping of statistics buffer ids to statistics
+ *   instances that are shared with the IPA
+ */
+SwStatsCpu::SwStatsCpu(std::unique_ptr<std::map<uint32_t, SharedMemObject<SwIspStats>>> sharedStats)
+	: sharedStats_(std::move(sharedStats))
 {
-	if (!sharedStats_)
-		LOG(SwStatsCpu, Error)
-			<< "Failed to create shared memory for statistics";
 }
 
 static constexpr unsigned int kRedYMul = 77; /* 0.299 * 256 */
@@ -316,7 +320,7 @@ void SwStatsCpu::startFrame([[maybe_unused]] const uint32_t statsBufferId)
  */
 void SwStatsCpu::finishFrame(uint32_t frame, const uint32_t statsBufferId)
 {
-	*sharedStats_ = stats_;
+	*(sharedStats_->at(statsBufferId)) = stats_;
 	statsReady.emit(frame, statsBufferId);
 }
 
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
index 402eef2d..fbdea4a3 100644
--- a/src/libcamera/software_isp/swstats_cpu.h
+++ b/src/libcamera/software_isp/swstats_cpu.h
@@ -11,6 +11,7 @@
 
 #pragma once
 
+#include <map>
 #include <stdint.h>
 
 #include <libcamera/base/signal.h>
@@ -29,12 +30,12 @@ struct StreamConfiguration;
 class SwStatsCpu
 {
 public:
-	SwStatsCpu();
+	SwStatsCpu(std::unique_ptr<std::map<uint32_t, SharedMemObject<SwIspStats>>> sharedStats);
 	~SwStatsCpu() = default;
 
-	bool isValid() const { return sharedStats_.fd().isValid(); }
+	bool isValid() const { return sharedStats_->begin()->second.fd().isValid(); }
 
-	const SharedFD &getStatsFD() { return sharedStats_.fd(); }
+	const SharedFD &getStatsFD() { return sharedStats_->begin()->second.fd(); }
 
 	const Size &patternSize() { return patternSize_; }
 
@@ -90,7 +91,7 @@ private:
 
 	unsigned int xShift_;
 
-	SharedMemObject<SwIspStats> sharedStats_;
+	std::unique_ptr<std::map<uint32_t, SharedMemObject<SwIspStats>>> sharedStats_;
 	SwIspStats stats_;
 };
 
