diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 46384d88dddd..0130a83973ca 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -5,6 +5,7 @@
  * ipu3.cpp - Pipeline handler for Intel IPU3
  */
 
+#include <algorithm>
 #include <iomanip>
 #include <memory>
 #include <vector>
@@ -221,34 +222,105 @@ CameraConfiguration
 PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 					 const std::vector<StreamUsage> &usages)
 {
-	CameraConfiguration configs;
 	IPU3CameraData *data = cameraData(camera);
-	StreamConfiguration config = {};
+	CameraConfiguration cameraConfig = {};
+	std::set<IPU3Stream *> streams = {
+		&data->outStream_,
+		&data->vfStream_,
+	};
 
-	/*
-	 * FIXME: Soraka: the maximum resolution reported by both sensors
-	 * (2592x1944 for ov5670 and 4224x3136 for ov13858) are returned as
-	 * default configurations but they're not correctly processed by the
-	 * ImgU. Resolutions up tp 2560x1920 have been validated.
-	 *
-	 * \todo Clarify ImgU alignement requirements.
-	 */
-	config.width = 2560;
-	config.height = 1920;
-	config.pixelFormat = V4L2_PIX_FMT_NV12;
-	config.bufferCount = IPU3_BUFFER_COUNT;
-
-	configs[&data->outStream_] = config;
-	LOG(IPU3, Debug)
-		<< "Stream '" << data->outStream_.name_ << "' format set to "
-		<< config.toString();
-
-	configs[&data->vfStream_] = config;
-	LOG(IPU3, Debug)
-		<< "Stream '" << data->vfStream_.name_ << "' format set to "
-		<< config.toString();
-
-	return configs;
+	for (const StreamUsage &usage : usages) {
+		StreamConfiguration streamConfig = {};
+		StreamUsage::Role role = usage.role();
+		IPU3Stream *stream = nullptr;
+
+		switch (role) {
+		case StreamUsage::Role::StillCapture:
+			/*
+			 * Don't allow viewfinder or video capture on the
+			 * 'output' stream. This is an artificial limitation
+			 * until we figure out the capabilities of the
+			 * hardware.
+			 */
+			if (streams.find(&data->outStream_) != streams.end()) {
+				stream = &data->outStream_;
+			} else if (streams.find(&data->vfStream_) != streams.end()) {
+				stream = &data->vfStream_;
+			} else {
+				LOG(IPU3, Error)
+					<< "No stream available for requested role "
+					<< role;
+				break;
+			}
+
+			/*
+			 * FIXME: Soraka: the maximum resolution reported by
+			 * both sensors (2592x1944 for ov5670 and 4224x3136 for
+			 * ov13858) are returned as default configurations but
+			 * they're not correctly processed by the ImgU.
+			 * Resolutions up tp 2560x1920 have been validated.
+			 *
+			 * \todo Clarify ImgU alignment requirements.
+			 */
+			streamConfig.width = 2560;
+			streamConfig.height = 1920;
+
+			break;
+		case StreamUsage::Role::Viewfinder:
+		case StreamUsage::Role::VideoRecording: {
+			/*
+			 * We can't use the 'output' stream for viewfinder or
+			 * video capture usages.
+			 *
+			 * \todo This is an artificial limitation until we
+			 * figure out the exact capabilities of the hardware.
+			 */
+			if (streams.find(&data->vfStream_) == streams.end()) {
+				LOG(IPU3, Error)
+					<< "No stream available for requested role "
+					<< role;
+				break;
+			}
+
+			stream = &data->vfStream_;
+
+			/*
+			 * Align the requested viewfinder size to the
+			 * maximum available sensor resolution and to the
+			 * IPU3 alignment constraints.
+			 */
+			const Size &res = data->cio2_.sensor_->resolution();
+			unsigned int width = std::min(usage.size().width,
+						      res.width);
+			unsigned int height = std::min(usage.size().height,
+						       res.height);
+			streamConfig.width = width & ~7;
+			streamConfig.height = height & ~3;
+
+			break;
+		}
+		default:
+			LOG(IPU3, Error)
+				<< "Requested stream role not supported: " << role;
+			break;
+		}
+
+		if (!stream)
+			return cameraConfig;
+
+		streams.erase(stream);
+
+		streamConfig.pixelFormat = V4L2_PIX_FMT_NV12;
+		streamConfig.bufferCount = IPU3_BUFFER_COUNT;
+
+		cameraConfig[stream] = streamConfig;
+
+		LOG(IPU3, Debug)
+			<< "Stream '" << stream->name_ << "' format set to "
+			<< streamConfig.toString();
+	}
+
+	return cameraConfig;
 }
 
 int PipelineHandlerIPU3::configureStreams(Camera *camera,
