diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h
index 5240f69f..0f564d99 100644
--- a/include/libcamera/internal/ipa_proxy.h
+++ b/include/libcamera/internal/ipa_proxy.h
@@ -31,7 +31,8 @@ public:
 
 	bool isValid() const { return valid_; }
 
-	std::string configurationFile(const std::string &name) const;
+	std::string configurationFile(const std::string &name,
+				      const std::string &fallbackName = std::string()) const;
 
 protected:
 	std::string resolvePath(const std::string &file) const;
diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp
index 6c17c456..69975d8f 100644
--- a/src/libcamera/ipa_proxy.cpp
+++ b/src/libcamera/ipa_proxy.cpp
@@ -72,6 +72,7 @@ IPAProxy::~IPAProxy()
 /**
  * \brief Retrieve the absolute path to an IPA configuration file
  * \param[in] name The configuration file name
+ * \param[in] fallbackName The name of a fallback configuration file
  *
  * This function locates the configuration file for an IPA and returns its
  * absolute path. It searches the following directories, in order:
@@ -89,10 +90,14 @@ IPAProxy::~IPAProxy()
  * named after the IPA module name, as reported in IPAModuleInfo::name, and for
  * a file named \a name within that directory. The \a name is IPA-specific.
  *
+ * If the file named \a name is not found and \a fallbackName is non-empty then
+ * the whole search is repeated for \a fallbackName.
+ *
  * \return The full path to the IPA configuration file, or an empty string if
  * no configuration file can be found
  */
-std::string IPAProxy::configurationFile(const std::string &name) const
+std::string IPAProxy::configurationFile(const std::string &name,
+					const std::string &fallbackName) const
 {
 	struct stat statbuf;
 	int ret;
@@ -146,11 +151,18 @@ std::string IPAProxy::configurationFile(const std::string &name) const
 		}
 	}
 
-	LOG(IPAProxy, Error)
-		<< "Configuration file '" << name
-		<< "' not found for IPA module '" << ipaName << "'";
+	if (fallbackName.empty()) {
+		LOG(IPAProxy, Error)
+			<< "Configuration file '" << name
+			<< "' not found for IPA module '" << ipaName << "'";
+		return std::string();
+	}
 
-	return std::string();
+	LOG(IPAProxy, Warning)
+		<< "Configuration file '" << name
+		<< "' not found for IPA module '" << ipaName
+		<< "', falling back to '" << fallbackName << "'";
+	return configurationFile(fallbackName);
 }
 
 /**
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 066fd4a2..2071c338 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -1186,9 +1186,8 @@ int IPU3CameraData::loadIPA()
 	 * The API tuning file is made from the sensor name. If the tuning file
 	 * isn't found, fall back to the 'uncalibrated' file.
 	 */
-	std::string ipaTuningFile = ipa_->configurationFile(sensor->model() + ".yaml");
-	if (ipaTuningFile.empty())
-		ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
+	std::string ipaTuningFile =
+		ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
 
 	ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
 			 sensorInfo, sensor->controls(), &ipaControls_);
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 4cbf105d..eec5bf94 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -351,13 +351,8 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision)
 	std::string ipaTuningFile;
 	char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RKISP1_TUNING_FILE");
 	if (!configFromEnv || *configFromEnv == '\0') {
-		ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml");
-		/*
-		 * If the tuning file isn't found, fall back to the
-		 * 'uncalibrated' configuration file.
-		 */
-		if (ipaTuningFile.empty())
-			ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
+		ipaTuningFile =
+			ipa_->configurationFile(sensor_->model() + ".yaml", "uncalibrated.yaml");
 	} else {
 		ipaTuningFile = std::string(configFromEnv);
 	}
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index c8748d88..1140372c 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -121,9 +121,8 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
 	 * The API tuning file is made from the sensor name. If the tuning file
 	 * isn't found, fall back to the 'uncalibrated' file.
 	 */
-	std::string ipaTuningFile = ipa_->configurationFile(sensor->model() + ".yaml");
-	if (ipaTuningFile.empty())
-		ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
+	std::string ipaTuningFile =
+		ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
 
 	int ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
 			     debayer_->getStatsFD(),
