diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 1fcf3671..82a3b402 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -274,62 +274,62 @@ SimpleCameraData::SimpleCameraData(SimplePipelineHandler *pipe,
 	int ret;
 
 	/*
-	 * Walk the pipeline towards the video node and store all entities
-	 * along the way.
+	 * Use the uniform-cost search algorithm to find the shortest path to a
+	 * capture device. The shortest path has a good chance to end in a
+	 * CSI capture device, as this is the most direct path for a camera.
+	 * There are other capture devices which ends in encoders or image
+	 * converters which will write the capture image to RAM. These paths
+	 * would be invalid for the purpose of SimplePipeline. These paths are
+	 * also usually longer as they would naturally contain IC- or encoder
+	 * IPU-blocks. Therefore, this algorithm will avoid these paths.
 	 */
-	MediaEntity *source = sensor;
-
-	while (source) {
-		/* If we have reached a video node, we're done. */
-		if (source->function() == MEDIA_ENT_F_IO_V4L)
-			break;
-
-		/*
-		 * Use the first output pad that has links and follow its first
-		 * link.
-		 */
-		MediaPad *sourcePad = nullptr;
-		MediaLink *sourceLink = nullptr;
-		for (MediaPad *pad : source->pads()) {
-			if ((pad->flags() & MEDIA_PAD_FL_SOURCE) &&
-			    !pad->links().empty()) {
-				sourcePad = pad;
-				sourceLink = pad->links().front();
-				break;
+	std::unordered_set<MediaEntity*> visited;
+	std::queue<MediaEntity*> queue;
+
+	/* Remember at each entity where we came from */
+	std::unordered_map<MediaEntity*, std::pair<MediaEntity*, MediaLink*>> parentList;
+	queue.push(sensor);
+
+	while (!queue.empty()) {
+		MediaEntity *entity = queue.back();
+		queue.pop();
+		/* Found the capture device */
+		if (entity->function() == MEDIA_ENT_F_IO_V4L) {
+			video_ = pipe->video(entity);
+			LOG(SimplePipeline, Debug) << "Capture Device: "
+						   << entity->name();
+			/* With the parentList, we can follow back our way from
+			 * the capture device to the sensor. */
+			while (parentList.find(entity) != parentList.end()) {
+				std::pair p = parentList[entity];
+				Entity e { p.first, p.second };
+				entities_.push_front(e);
+				entity = p.first;
+			}
+			/* Finally also remember the sensor */
+			sensor_ = std::make_unique<CameraSensor>(sensor);
+			ret = sensor_->init();
+			if (ret) {
+				sensor_.reset();
 			}
-		}
-
-		if (!sourcePad)
-			return;
-
-		entities_.push_back({ source, sourceLink });
-
-		source = sourceLink->sink()->entity();
-
-		/* Avoid infinite loops. */
-		auto iter = std::find_if(entities_.begin(), entities_.end(),
-					 [&](const Entity &entity) {
-						 return entity.entity == source;
-					 });
-		if (iter != entities_.end()) {
-			LOG(SimplePipeline, Info) << "Loop detected in pipeline";
 			return;
 		}
-	}
-
-	/*
-	 * We have a valid pipeline, get the video device and create the camera
-	 * sensor.
-	 */
-	video_ = pipe->video(source);
-	if (!video_)
-		return;
-
-	sensor_ = std::make_unique<CameraSensor>(sensor);
-	ret = sensor_->init();
-	if (ret) {
-		sensor_.reset();
-		return;
+		/* The actual uniform-cost search algorithm */
+		visited.insert(entity);
+		for (MediaPad *pad : entity->pads()) {
+			if (pad->flags() & MEDIA_PAD_FL_SOURCE) {
+				for (MediaLink *link : pad->links()) {
+					if ((link->flags() & MEDIA_LNK_FL_ENABLED) ||
+					    !(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) {
+						MediaEntity *next = link->sink()->entity();
+						if (visited.find(next) == visited.end()) {
+							queue.push(next);
+							parentList.insert( { next, { entity, link } });
+						}
+					}
+				}
+			}
+		}
 	}
 }
 
