diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 01b25fb20..c3db5c068 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -351,6 +351,8 @@ public:
 	};
 	std::queue<RequestOutputs> conversionQueue_;
 	bool useConversion_;
+	bool processedRequested_;
+	bool rawRequested_;
 
 	std::unique_ptr<Converter> converter_;
 	std::unique_ptr<SoftwareIsp> swIsp_;
@@ -384,6 +386,9 @@ public:
 	bool needConversion() const { return needConversion_; }
 	const Transform &combinedTransform() const { return combinedTransform_; }
 
+	bool processedRequested_;
+	bool rawRequested_;
+
 private:
 	/*
 	 * The SimpleCameraData instance is guaranteed to be valid as long as
@@ -867,10 +872,13 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)
 	 * point converting an erroneous buffer.
 	 */
 	if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
-		if (!useConversion_) {
+		if (rawRequested_) {
 			/* No conversion, just complete the request. */
 			Request *request = buffer->request();
 			pipe->completeBuffer(request, buffer);
+			SimpleFrameInfo *info = frameInfo_.find(request->sequence());
+			if (info)
+				info->metadataRequired = false;
 			tryCompleteRequest(request);
 			return;
 		}
@@ -880,7 +888,8 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)
 		 * buffer for capture (unless the stream is being stopped), and
 		 * complete the request with all the user-facing buffers.
 		 */
-		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
+		if (buffer->metadata().status != FrameMetadata::FrameCancelled &&
+		    !rawRequested_)
 			video_->queueBuffer(buffer);
 
 		if (conversionQueue_.empty())
@@ -929,13 +938,14 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)
 	 */
 	if (useConversion_) {
 		if (conversionQueue_.empty()) {
-			video_->queueBuffer(buffer);
+			if (!rawRequested_)
+				video_->queueBuffer(buffer);
 			return;
 		}
 
 		if (converter_)
 			converter_->queueBuffers(buffer, conversionQueue_.front().outputs);
-		else
+		else if (processedRequested_)
 			/*
 			 * request->sequence() cannot be retrieved from `buffer' inside
 			 * queueBuffers because unique_ptr's make buffer->request() invalid
@@ -945,6 +955,8 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)
 					     conversionQueue_.front().outputs);
 
 		conversionQueue_.pop();
+		if (rawRequested_)
+			pipe->completeBuffer(request, buffer);
 		return;
 	}
 
@@ -982,7 +994,8 @@ void SimpleCameraData::tryCompleteRequest(Request *request)
 void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)
 {
 	/* Queue the input buffer back for capture. */
-	video_->queueBuffer(buffer);
+	if (!rawRequested_)
+		video_->queueBuffer(buffer);
 }
 
 void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
@@ -1201,6 +1214,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
 			processedRequested = true;
 		}
 
+	rawRequested_ = rawRequested;
+	processedRequested_ = processedRequested;
+
 	/*
 	 * Adjust the requested streams.
 	 *
@@ -1510,6 +1526,8 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 	/* Configure the converter if needed. */
 	std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
 	data->useConversion_ = config->needConversion();
+	data->processedRequested_ = config->processedRequested_;
+	data->rawRequested_ = config->rawRequested_;
 
 	for (unsigned int i = 0; i < config->size(); ++i) {
 		StreamConfiguration &cfg = config->at(i);
@@ -1548,7 +1566,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
 	 * Export buffers on the converter or capture video node, depending on
 	 * whether the converter is used or not.
 	 */
-	if (data->useConversion_)
+	if (data->useConversion_ && !isFormatRaw(stream->configuration().pixelFormat))
 		return data->converter_
 			       ? data->converter_->exportBuffers(stream, count, buffers)
 			       : data->swIsp_->exportBuffers(stream, count, buffers);
@@ -1571,7 +1589,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 		return -EBUSY;
 	}
 
-	if (data->useConversion_) {
+	if (data->useConversion_ && !data->rawRequested_) {
 		/*
 		 * When using the converter allocate a fixed number of internal
 		 * buffers.
@@ -1579,7 +1597,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 		ret = video->allocateBuffers(kNumInternalBuffers,
 					     &data->conversionBuffers_);
 	} else {
-		/* Otherwise, prepare for using buffers from the only stream. */
+		/* Otherwise, prepare for using buffers from the only or raw stream. */
 		Stream *stream = &data->streams_[0];
 		ret = video->importBuffers(stream->configuration().bufferCount);
 	}
@@ -1621,8 +1639,9 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 		}
 
 		/* Queue all internal buffers for capture. */
-		for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
-			video->queueBuffer(buffer.get());
+		if (!data->rawRequested_)
+			for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
+				video->queueBuffer(buffer.get());
 	}
 
 	return 0;
@@ -1673,7 +1692,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
 		 * queue, it will be handed to the converter in the capture
 		 * completion handler.
 		 */
-		if (data->useConversion_) {
+		if (data->useConversion_ && !isFormatRaw(stream->configuration().pixelFormat)) {
 			buffers.emplace(stream, buffer);
 			metadataRequired = !!data->swIsp_;
 		} else {
