diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index f1f04d9ef6ed..37bd358462db 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -21,7 +21,7 @@
 
 using namespace libcamera;
 
-class V4L2Camera : public Object
+class V4L2Camera
 {
 public:
 	struct Buffer {
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index e58fd6a0d8b5..622520479be0 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -41,8 +41,7 @@ int V4L2CameraProxy::open(bool nonBlocking)
 {
 	LOG(V4L2Compat, Debug) << "Servicing open";
 
-	int ret = vcam_->invokeMethod(&V4L2Camera::open,
-				      ConnectionTypeBlocking);
+	int ret = vcam_->open();
 	if (ret < 0) {
 		errno = -ret;
 		return -1;
@@ -50,8 +49,7 @@ int V4L2CameraProxy::open(bool nonBlocking)
 
 	nonBlocking_ = nonBlocking;
 
-	vcam_->invokeMethod(&V4L2Camera::getStreamConfig,
-			    ConnectionTypeBlocking, &streamConfig_);
+	vcam_->getStreamConfig(&streamConfig_);
 	setFmtFromConfig(streamConfig_);
 	sizeimage_ = calculateSizeImage(streamConfig_);
 
@@ -72,7 +70,7 @@ void V4L2CameraProxy::close()
 	if (--refcount_ > 0)
 		return;
 
-	vcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking);
+	vcam_->close();
 }
 
 void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,
@@ -284,11 +282,9 @@ int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg)
 	tryFormat(arg);
 
 	Size size(arg->fmt.pix.width, arg->fmt.pix.height);
-	int ret = vcam_->invokeMethod(&V4L2Camera::configure,
-				      ConnectionTypeBlocking,
-				      &streamConfig_, size,
-				      v4l2ToDrm(arg->fmt.pix.pixelformat),
-				      bufferCount_);
+	int ret = vcam_->configure(&streamConfig_, size,
+				   v4l2ToDrm(arg->fmt.pix.pixelformat),
+				   bufferCount_);
 	if (ret < 0)
 		return -EINVAL;
 
@@ -319,13 +315,12 @@ int V4L2CameraProxy::freeBuffers()
 {
 	LOG(V4L2Compat, Debug) << "Freeing libcamera bufs";
 
-	int ret = vcam_->invokeMethod(&V4L2Camera::streamOff,
-				      ConnectionTypeBlocking);
+	int ret = vcam_->streamOff();
 	if (ret < 0) {
 		LOG(V4L2Compat, Error) << "Failed to stop stream";
 		return ret;
 	}
-	vcam_->invokeMethod(&V4L2Camera::freeBuffers, ConnectionTypeBlocking);
+	vcam_->freeBuffers();
 	bufferCount_ = 0;
 
 	return 0;
@@ -349,11 +344,9 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)
 		return freeBuffers();
 
 	Size size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height);
-	ret = vcam_->invokeMethod(&V4L2Camera::configure,
-				  ConnectionTypeBlocking,
-				  &streamConfig_, size,
-				  v4l2ToDrm(curV4L2Format_.fmt.pix.pixelformat),
-				  arg->count);
+	ret = vcam_->configure(&streamConfig_, size,
+			       v4l2ToDrm(curV4L2Format_.fmt.pix.pixelformat),
+			       arg->count);
 	if (ret < 0)
 		return -EINVAL;
 
@@ -366,8 +359,7 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)
 	arg->count = streamConfig_.bufferCount;
 	bufferCount_ = arg->count;
 
-	ret = vcam_->invokeMethod(&V4L2Camera::allocBuffers,
-				  ConnectionTypeBlocking, arg->count);
+	ret = vcam_->allocBuffers(arg->count);
 	if (ret < 0) {
 		arg->count = 0;
 		return ret;
@@ -415,8 +407,7 @@ int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg)
 	    arg->index >= bufferCount_)
 		return -EINVAL;
 
-	int ret = vcam_->invokeMethod(&V4L2Camera::qbuf, ConnectionTypeBlocking,
-				      arg->index);
+	int ret = vcam_->qbuf(arg->index);
 	if (ret < 0)
 		return ret;
 
@@ -459,10 +450,7 @@ int V4L2CameraProxy::vidioc_streamon(int *arg)
 	if (!validateBufferType(*arg))
 		return -EINVAL;
 
-	int ret = vcam_->invokeMethod(&V4L2Camera::streamOn,
-				      ConnectionTypeBlocking);
-
-	return ret;
+	return vcam_->streamOn();
 }
 
 int V4L2CameraProxy::vidioc_streamoff(int *arg)
@@ -472,8 +460,7 @@ int V4L2CameraProxy::vidioc_streamoff(int *arg)
 	if (!validateBufferType(*arg))
 		return -EINVAL;
 
-	int ret = vcam_->invokeMethod(&V4L2Camera::streamOff,
-				      ConnectionTypeBlocking);
+	int ret = vcam_->streamOff();
 
 	for (struct v4l2_buffer &buf : buffers_)
 		buf.flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE);
diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp
index f5a7b2ac4229..961d06b3e39a 100644
--- a/src/v4l2/v4l2_compat_manager.cpp
+++ b/src/v4l2/v4l2_compat_manager.cpp
@@ -37,7 +37,7 @@ void get_symbol(T &func, const char *name)
 } /* namespace */
 
 V4L2CompatManager::V4L2CompatManager()
-	: cm_(nullptr), initialized_(false)
+	: cm_(nullptr)
 {
 	get_symbol(fops_.openat, "openat");
 	get_symbol(fops_.dup, "dup");
@@ -52,24 +52,15 @@ V4L2CompatManager::~V4L2CompatManager()
 	devices_.clear();
 	mmaps_.clear();
 
-	if (isRunning()) {
-		exit(0);
-		/* \todo Wait with a timeout, just in case. */
-		wait();
+	if (cm_) {
+		proxies_.clear();
+		cm_->stop();
+		delete cm_;
+		cm_ = nullptr;
 	}
 }
 
-int V4L2CompatManager::init()
-{
-	start();
-
-	MutexLocker locker(mutex_);
-	cv_.wait(locker, [&] { return initialized_; });
-
-	return 0;
-}
-
-void V4L2CompatManager::run()
+int V4L2CompatManager::start()
 {
 	cm_ = new CameraManager();
 
@@ -77,7 +68,9 @@ void V4L2CompatManager::run()
 	if (ret) {
 		LOG(V4L2Compat, Error) << "Failed to start camera manager: "
 				       << strerror(-ret);
-		return;
+		delete cm_;
+		cm_ = nullptr;
+		return ret;
 	}
 
 	LOG(V4L2Compat, Debug) << "Started camera manager";
@@ -93,22 +86,7 @@ void V4L2CompatManager::run()
 		++index;
 	}
 
-	/*
-	 * libcamera has been initialized. Unlock the init() caller as we're
-	 * now ready to handle calls from the framework.
-	 */
-	mutex_.lock();
-	initialized_ = true;
-	mutex_.unlock();
-	cv_.notify_one();
-
-	/* Now start processing events and messages. */
-	exec();
-
-	proxies_.clear();
-	cm_->stop();
-	delete cm_;
-	cm_ = nullptr;
+	return 0;
 }
 
 V4L2CompatManager *V4L2CompatManager::instance()
@@ -159,8 +137,8 @@ int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mod
 	    major(statbuf.st_rdev) != 81)
 		return fd;
 
-	if (!isRunning())
-		init();
+	if (!cm_)
+		start();
 
 	ret = getCameraIndex(fd);
 	if (ret < 0) {
diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h
index d51b5953d930..872c7c3b10e8 100644
--- a/src/v4l2/v4l2_compat_manager.h
+++ b/src/v4l2/v4l2_compat_manager.h
@@ -8,22 +8,19 @@
 #ifndef __V4L2_COMPAT_MANAGER_H__
 #define __V4L2_COMPAT_MANAGER_H__
 
-#include <condition_variable>
 #include <fcntl.h>
 #include <map>
 #include <memory>
-#include <mutex>
 #include <sys/types.h>
 #include <vector>
 
 #include <libcamera/camera_manager.h>
 
-#include "thread.h"
 #include "v4l2_camera_proxy.h"
 
 using namespace libcamera;
 
-class V4L2CompatManager : public Thread
+class V4L2CompatManager
 {
 public:
 	struct FileOperations {
@@ -46,8 +43,6 @@ public:
 
 	static V4L2CompatManager *instance();
 
-	int init();
-
 	V4L2CameraProxy *getProxy(int fd);
 	const FileOperations &fops() const { return fops_; }
 
@@ -64,17 +59,13 @@ private:
 	V4L2CompatManager();
 	~V4L2CompatManager();
 
-	void run() override;
+	int start();
 	int getCameraIndex(int fd);
 
 	FileOperations fops_;
 
 	CameraManager *cm_;
 
-	std::mutex mutex_;
-	std::condition_variable cv_;
-	bool initialized_;
-
 	std::vector<std::unique_ptr<V4L2CameraProxy>> proxies_;
 	std::map<int, V4L2CameraProxy *> devices_;
 	std::map<void *, V4L2CameraProxy *> mmaps_;
