diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
index 7fb5be77..f3bb4321 100644
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
@@ -67,6 +67,9 @@ private:
 	void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src);
 	void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src);
 
+	void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src);
+	void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src);
+
 	typedef void (DebayerCpu::*debayerFn)(uint8_t *dst, const uint8_t *src);
 
 	struct DebayerInputConfig {
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
index 0ceb995f..3c43e146 100644
--- a/include/libcamera/internal/software_isp/swstats_cpu.h
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
@@ -29,6 +29,8 @@ public:
 	/* FIXME this should be dropped once AWB has moved to the IPA */
 	SwIspStats getStats() { return *sharedStats_; }
 private:
+	void statsBGGR8Line0(const uint8_t *src, unsigned int stride);
+
 	void statsBGGR10PLine0(const uint8_t *src, unsigned int stride);
 	void statsGBRG10PLine0(const uint8_t *src, unsigned int stride);
 	void statsGRBG10PLine0(const uint8_t *src, unsigned int stride);
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index 3eacdd5d..52910a03 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -111,6 +111,36 @@ void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src)
 	}
 }
 
+void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src)
+{
+	const int width_in_bytes = window_.width;
+	struct ctxt_8bit_src c = {
+		src - inputConfig_.stride, src, src + inputConfig_.stride,
+		red_, green_, blue_ };
+
+	for (int x = 0; x < width_in_bytes; ) {
+		/* Even pixel */
+		bggr8_bgr888(c, dst, x++, 1, 1);
+		/* Odd pixel BGGR -> GBRG */
+		gbrg8_bgr888(c, dst, x++, 1, 1);
+	}
+}
+
+void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src)
+{
+	const int width_in_bytes = window_.width;
+	struct ctxt_8bit_src c = {
+		src - inputConfig_.stride, src, src + inputConfig_.stride,
+		red_, green_, blue_ };
+
+	for (int x = 0; x < width_in_bytes; ) {
+		/* Even pixel */
+		grbg8_bgr888(c, dst, x++, 1, 1);
+		/* Odd pixel GRBG -> RGGB */
+		rggb8_bgr888(c, dst, x++, 1, 1);
+	}
+}
+
 void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src)
 {
 	const int width_in_bytes = window_.width * 5 / 4;
@@ -152,6 +182,22 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 	BayerFormat bayerFormat =
 		BayerFormat::fromPixelFormat(inputFormat);
 
+	if (bayerFormat.bitDepth == 8) {
+	    	config.bpp = 8;
+		config.patternSize.height = 2;
+		config.patternSize.width = 4;
+		config.x_shift = 0;
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
+
+		switch (bayerFormat.order) {
+		case BayerFormat::BGGR:
+			return 0;
+		default:
+			break;
+		}
+	/* } else if (future supported fmts) { ... */
+	}
+
 	if (bayerFormat.bitDepth == 10 &&
 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 	    	config.bpp = 10;
@@ -195,6 +241,17 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
 	BayerFormat bayerFormat =
 		BayerFormat::fromPixelFormat(inputFormat);
 
+	if (bayerFormat.bitDepth == 8) {
+		switch (bayerFormat.order) {
+		case BayerFormat::BGGR:
+			debayer0_ = &DebayerCpu::debayer8_BGBG_BGR888;
+			debayer1_ = &DebayerCpu::debayer8_GRGR_BGR888;
+			return 0;
+		default:
+			break;
+		}
+	}
+
 	if (bayerFormat.bitDepth == 10 &&
 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 		switch (bayerFormat.order) {
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
index 15f23953..e65b1b65 100644
--- a/src/libcamera/software_isp/swstats_cpu.cpp
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -37,9 +37,9 @@ static const unsigned int GREEN_Y_MUL = 150;		/* 0.59 * 256 */
 static const unsigned int BLUE_Y_MUL = 29;		/* 0.11 * 256 */
 
 static inline __attribute__((always_inline)) void
-statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, unsigned int &bright_sum_, unsigned int &too_bright_sum_, SwIspStats &stats_)
+statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, unsigned int &bright_sum_, unsigned int &too_bright_sum_, SwIspStats &stats_, bool is10p)
 {
-	const int width_in_bytes = width * 5 / 4;
+	const int width_in_bytes = width * (4 + is10p) / 4;
 	uint8_t r, g, b, g2;
 	unsigned int y_val;
 	unsigned int sumR = 0;
@@ -49,7 +49,7 @@ statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bg
 	unsigned int bright_sum = 0;
 	unsigned int too_bright_sum = 0;
 
-	for (int x = 0; x < width_in_bytes; x += 5) {
+	for (int x = 0; x < width_in_bytes; x += (4 + is10p)) {
 		if (bggr) {
 			/* BGGR */
 			b  = src0[x];
@@ -84,26 +84,31 @@ statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bg
 	too_bright_sum_ += too_bright_sum;
 }
 
+void SwStatsCpu::statsBGGR8Line0(const uint8_t *src, unsigned int stride)
+{
+	statsBayer10P(window_.width, src, src + stride, true, bright_sum_, too_bright_sum_, stats_, false);
+}
+
 void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src, unsigned int stride)
 {
-	statsBayer10P(window_.width, src, src + stride, true, bright_sum_, too_bright_sum_, stats_);
+	statsBayer10P(window_.width, src, src + stride, true, bright_sum_, too_bright_sum_, stats_, true);
 }
 
 void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src, unsigned int stride)
 {
-	statsBayer10P(window_.width, src, src + stride, false, bright_sum_, too_bright_sum_, stats_);
+	statsBayer10P(window_.width, src, src + stride, false, bright_sum_, too_bright_sum_, stats_, true);
 }
 
 void SwStatsCpu::statsGRBG10PLine0(const uint8_t *src, unsigned int stride)
 {
 	/* GRBG is BGGR with the lines swapped */
-	statsBayer10P(window_.width, src + stride, src, true, bright_sum_, too_bright_sum_, stats_);
+	statsBayer10P(window_.width, src + stride, src, true, bright_sum_, too_bright_sum_, stats_, true);
 }
 
 void SwStatsCpu::statsRGGB10PLine0(const uint8_t *src, unsigned int stride)
 {
 	/* RGGB is GBRG with the lines swapped */
-	statsBayer10P(window_.width, src + stride, src, false, bright_sum_, too_bright_sum_, stats_);
+	statsBayer10P(window_.width, src + stride, src, false, bright_sum_, too_bright_sum_, stats_, true);
 }
 
 void SwStatsCpu::resetStats(void)
@@ -134,6 +139,24 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 	startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
 	finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
 
+	if (bayerFormat.bitDepth == 8) {
+		bpp_ = 8;
+		patternSize_.height = 2;
+		patternSize_.width = 4;
+		y_skip_mask_ = 0x0c; /* Skip every 3th and 4th line */
+		x_shift_ = 0;
+
+		switch (bayerFormat.order) {
+		case BayerFormat::BGGR:
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR8Line0;
+			return 0;
+		default:
+			break;
+		}
+	/* } else if (future supported fmts) { ... */
+	}
+
+
 	if (bayerFormat.bitDepth == 10 &&
 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 		bpp_ = 10;
