diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 35c29cec..91b149d1 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -30,6 +30,7 @@
 #include <libcamera/control_ids.h>
 #include <libcamera/geometry.h>
 #include <libcamera/pixel_format.h>
+#include <libcamera/property_ids.h>
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/camera.h"
@@ -44,6 +45,7 @@
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/request.h"
+#include "libcamera/internal/software_isp/debayer_geometry.h"
 #include "libcamera/internal/software_isp/software_isp.h"
 #include "libcamera/internal/v4l2_subdevice.h"
 #include "libcamera/internal/v4l2_videodevice.h"
@@ -292,6 +294,7 @@ public:
 			 Transform transform = Transform::Identity);
 	void imageBufferReady(FrameBuffer *buffer);
 	void clearIncompleteRequests();
+	void updateScalerCrop(const Size &inputSize, Transform transform);
 
 	unsigned int streamIndex(const Stream *stream) const
 	{
@@ -983,6 +986,48 @@ void SimpleCameraData::clearIncompleteRequests()
 	}
 }
 
+void SimpleCameraData::updateScalerCrop(const Size &inputSize, Transform transform)
+{
+	IPACameraSensorInfo sensorInfo;
+	if (sensor_->sensorInfo(&sensorInfo))
+		return;
+
+	/*
+	 * The buffer-to-sensor mapping requires a capture path that preserves
+	 * the sensor output. Intermediate cropping or scaling needs a separate
+	 * mapping; do not report the analogue crop as if it included that work.
+	 */
+	if (sensorInfo.outputSize != inputSize)
+		return;
+
+	for (const Entity &entity : entities_) {
+		if (!entity.sink || !entity.source)
+			continue;
+
+		V4L2Subdevice *subdev = pipe()->subdev(entity.entity);
+		for (const MediaPad *pad : { entity.sink, entity.source }) {
+			V4L2SubdeviceFormat format{};
+			if (subdev->getFormat(pad->index(), &format) ||
+			    format.size != inputSize)
+				return;
+
+			for (unsigned int target : { V4L2_SEL_TGT_CROP, V4L2_SEL_TGT_COMPOSE }) {
+				Rectangle selection;
+				int ret = subdev->getSelection(pad->index(), target, &selection, true);
+				if (!ret && selection != Rectangle(inputSize))
+					return;
+				if (ret && ret != -ENOTTY && ret != -EINVAL)
+					return;
+			}
+		}
+	}
+
+	Rectangle crop = debayerCropToSensor(swIsp_->inputCrop(), inputSize,
+					     sensorInfo.analogCrop, sensorInfo.activeArea,
+					     transform);
+	properties_.set(properties::ScalerCropMaximum, crop);
+}
+
 void SimpleCameraData::tryCompleteRequest(Request *request)
 {
 	if (request->hasPendingBuffers())
@@ -1505,6 +1550,8 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 	V4L2VideoDevice *video = data->video_;
 	int ret;
 
+	data->properties_ = data->sensor_->properties();
+
 	/*
 	 * Configure links on the pipeline and propagate formats from the
 	 * sensor to the video node.
@@ -1605,7 +1652,12 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 	} else {
 		ipa::softisp::IPAConfigInfo configInfo;
 		configInfo.sensorControls = data->sensor_->controls();
-		return data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_);
+		ret = data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_);
+		if (ret)
+			return ret;
+
+		data->updateScalerCrop(inputCfg.size, config->combinedTransform());
+		return 0;
 	}
 }
 
