diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom
index eaf3955e..931ef357 100644
--- a/include/libcamera/ipa/rkisp1.mojom
+++ b/include/libcamera/ipa/rkisp1.mojom
@@ -27,6 +27,7 @@ interface IPARkISP1Interface {
 	[async] fillParamsBuffer(uint32 frame, uint32 bufferId);
 	[async] processStatsBuffer(uint32 frame, uint32 bufferId,
 				   libcamera.ControlList sensorControls);
+	[async] completeRaw(uint32 frame);
 };
 
 interface IPARkISP1EventInterface {
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index 1335e3d1..4fd12333 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -63,6 +63,7 @@ public:
 	void fillParamsBuffer(const uint32_t frame, const uint32_t bufferId) override;
 	void processStatsBuffer(const uint32_t frame, const uint32_t bufferId,
 				const ControlList &sensorControls) override;
+	void completeRaw(const uint32_t frame) override;
 
 protected:
 	std::string logPrefix() const override;
@@ -349,6 +350,15 @@ void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId
 	prepareMetadata(frame, aeState);
 }
 
+void IPARkISP1::completeRaw(const uint32_t frame)
+{
+	unsigned int aeState = 0;
+
+	setControls(frame);
+
+	prepareMetadata(frame, aeState);
+}
+
 void IPARkISP1::setControls(unsigned int frame)
 {
 	/*
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 25fbf9f1..80ca5547 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -182,6 +182,7 @@ private:
 	std::unique_ptr<V4L2Subdevice> csi_;
 
 	bool hasSelfPath_;
+	bool isRaw_;
 
 	RkISP1MainPath mainPath_;
 	RkISP1SelfPath selfPath_;
@@ -363,7 +364,10 @@ void RkISP1CameraData::paramFilled(unsigned int frame)
 		return;
 
 	pipe->param_->queueBuffer(info->paramBuffer);
-	pipe->stat_->queueBuffer(info->statBuffer);
+
+	if (!pipe->isRaw_) {
+		pipe->stat_->queueBuffer(info->statBuffer);
+	}
 
 	if (info->mainPathBuffer)
 		mainPath_->queueBuffer(info->mainPathBuffer);
@@ -413,6 +417,21 @@ bool RkISP1CameraConfiguration::fitsAllPaths(const StreamConfiguration &cfg)
 	return true;
 }
 
+std::map<PixelFormat, uint32_t> rawFormats = {
+	{ formats::SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8 },
+	{ formats::SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8 },
+	{ formats::SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8 },
+	{ formats::SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8 },
+	{ formats::SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10 },
+	{ formats::SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10 },
+	{ formats::SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10 },
+	{ formats::SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10 },
+	{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12 },
+	{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12 },
+	{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12 },
+	{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12 },
+};
+
 CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 {
 	const CameraSensor *sensor = data_->sensor_.get();
@@ -504,8 +523,23 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 
 	/* Select the sensor format. */
 	Size maxSize;
-	for (const StreamConfiguration &cfg : config_)
+	PixelFormat rawFormat;
+	bool hasRawFormat = false;
+	for (StreamConfiguration &cfg : config_) {
+		if (PixelFormatInfo::info(cfg.pixelFormat).colourEncoding ==
+		    PixelFormatInfo::ColourEncodingRAW) {
+			hasRawFormat = true;
+			rawFormat = cfg.pixelFormat;
+
+			/* Raw format cannot be resized by ISP. */
+			if (cfg.size != sensor->resolution()) {
+				cfg.size = sensor->resolution();
+				status = Adjusted;
+			}
+		}
+
 		maxSize = std::max(maxSize, cfg.size);
+	}
 
 	sensorFormat_ = sensor->getFormat({ MEDIA_BUS_FMT_SBGGR12_1X12,
 					    MEDIA_BUS_FMT_SGBRG12_1X12,
@@ -520,6 +554,13 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 					    MEDIA_BUS_FMT_SGRBG8_1X8,
 					    MEDIA_BUS_FMT_SRGGB8_1X8 },
 					  maxSize);
+
+	if (hasRawFormat) {
+		auto mbus = rawFormats.find(rawFormat);
+		if (mbus != rawFormats.end())
+			sensorFormat_ = sensor->getFormat({ mbus->second }, maxSize);
+	}
+
 	if (sensorFormat_.size.isNull())
 		sensorFormat_.size = sensor->resolution();
 
@@ -659,8 +700,14 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
 		<< "ISP input pad configured with " << format
 		<< " crop " << rect;
 
+	const PixelFormat &streamFormat = config->at(0).pixelFormat;
+	const PixelFormatInfo &info = PixelFormatInfo::info(streamFormat);
+	isRaw_ = info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;
+
 	/* YUYV8_2X8 is required on the ISP source path pad for YUV output. */
-	format.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
+	if (!isRaw_)
+		format.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
+
 	LOG(RkISP1, Debug)
 		<< "Configuring ISP output pad with " << format
 		<< " crop " << rect;
@@ -1152,6 +1199,17 @@ void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)
 	request->metadata().set(controls::SensorTimestamp,
 				buffer->metadata().timestamp);
 
+	if (isRaw_) {
+		ASSERT(activeCamera_);
+		RkISP1CameraData *data = cameraData(activeCamera_);
+
+		RkISP1FrameInfo *info = data->frameInfo_.find(buffer);
+		if (!info)
+			return;
+
+		data->ipa_->completeRaw(info->frame);
+	}
+
 	completeBuffer(request, buffer);
 	tryCompleteRequest(request);
 }
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
index 2d38f0fb..0ae006e8 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
@@ -101,6 +101,23 @@ CameraConfiguration::Status RkISP1Path::validate(StreamConfiguration *cfg)
 	return status;
 }
 
+std::map<PixelFormat, uint32_t> formatToMediaBus = {
+	{ formats::NV12, MEDIA_BUS_FMT_YUYV8_1_5X8 },
+	{ formats::NV21, MEDIA_BUS_FMT_YUYV8_1_5X8 },
+	{ formats::SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8 },
+	{ formats::SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8 },
+	{ formats::SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8 },
+	{ formats::SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8 },
+	{ formats::SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10 },
+	{ formats::SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10 },
+	{ formats::SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10 },
+	{ formats::SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10 },
+	{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12 },
+	{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12 },
+	{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12 },
+	{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12 },
+};
+
 int RkISP1Path::configure(const StreamConfiguration &config,
 			  const V4L2SubdeviceFormat &inputFormat)
 {
@@ -127,15 +144,11 @@ int RkISP1Path::configure(const StreamConfiguration &config,
 		<< "Configuring " << name_ << " resizer output pad with "
 		<< ispFormat;
 
-	switch (config.pixelFormat) {
-	case formats::NV12:
-	case formats::NV21:
-		ispFormat.mbus_code = MEDIA_BUS_FMT_YUYV8_1_5X8;
-		break;
-	default:
+	auto mbus = formatToMediaBus.find(config.pixelFormat);
+	if (mbus != formatToMediaBus.end())
+		ispFormat.mbus_code = mbus->second;
+	else
 		ispFormat.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
-		break;
-	}
 
 	ret = resizer_->setFormat(1, &ispFormat);
 	if (ret < 0)
@@ -207,14 +220,25 @@ void RkISP1Path::stop()
 namespace {
 constexpr Size RKISP1_RSZ_MP_SRC_MIN{ 32, 16 };
 constexpr Size RKISP1_RSZ_MP_SRC_MAX{ 4416, 3312 };
-constexpr std::array<PixelFormat, 6> RKISP1_RSZ_MP_FORMATS{
+constexpr std::array<PixelFormat, 18> RKISP1_RSZ_MP_FORMATS{
 	formats::YUYV,
 	formats::NV16,
 	formats::NV61,
 	formats::NV21,
 	formats::NV12,
 	formats::R8,
-	/* \todo Add support for RAW formats. */
+	formats::SRGGB8,
+	formats::SGRBG8,
+	formats::SGBRG8,
+	formats::SBGGR8,
+	formats::SRGGB10,
+	formats::SGRBG10,
+	formats::SGBRG10,
+	formats::SBGGR10,
+	formats::SRGGB12,
+	formats::SGRBG12,
+	formats::SGBRG12,
+	formats::SBGGR12,
 };
 
 constexpr Size RKISP1_RSZ_SP_SRC_MIN{ 32, 16 };
