diff --git a/q6voiced.c b/q6voiced.c
index 16f8367bb076325ad750c7ba57d47f094e1396d8..90876622feac22c962266dc38535071db96d0311 100644
--- a/q6voiced.c
+++ b/q6voiced.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: MIT
+#include <stdbool.h>
 #include <stdio.h>
 #include <dbus/dbus.h>
 #include <tinyalsa/asoundlib.h>
@@ -12,16 +13,107 @@ struct pcm_config pcm_config_voice_call = {
 	.format		= PCM_FORMAT_S16_LE,
 };
 
+struct q6voiced {
+	unsigned int card, device;
+	struct pcm *tx, *rx;
+};
+
+static void q6voiced_open(struct q6voiced *v)
+{
+	if (v->tx)
+		return; /* Already active */
+
+	/*
+	 * Opening the PCM devices starts the stream.
+	 * This should be replaced by a codec2codec link probably.
+	 */
+	v->tx = pcm_open(v->card, v->device, PCM_IN, &pcm_config_voice_call);
+	if (!pcm_is_ready(v->tx))
+		perror("Failed to open tx");
+
+	v->rx = pcm_open(v->card, v->device, PCM_OUT, &pcm_config_voice_call);
+	if (!pcm_is_ready(v->rx))
+		perror("Failed to open rx");
+
+	printf("PCM devices were opened.\n");
+}
+
+static void q6voiced_close(struct q6voiced *v)
+{
+	if (!v->tx)
+		return; /* Not active */
+
+	pcm_close(v->rx);
+	pcm_close(v->tx);
+	v->rx = v->tx = NULL;
+
+	printf("PCM devices were closed.\n");
+}
+
+/* See ModemManager-enums.h */
+enum MMCallState {
+	MM_CALL_STATE_DIALING		= 1,
+	MM_CALL_STATE_RINGING_OUT	= 2,
+	MM_CALL_STATE_ACTIVE		= 4,
+};
+
+static bool mm_state_is_active(int state)
+{
+	/*
+	 * Some modems seem to be incapable of reporting DIALING -> ACTIVE.
+	 * Therefore we also consider DIALING/RINGING_OUT as active.
+	 */
+	switch (state) {
+	case MM_CALL_STATE_DIALING:
+	case MM_CALL_STATE_RINGING_OUT:
+	case MM_CALL_STATE_ACTIVE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void handle_signal(struct q6voiced *v, DBusMessage *msg, DBusError *err)
+{
+	// Check if the message is a signal from the correct interface and with the correct name
+	// TODO: Should we also check the call state for oFono?
+	if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallAdded")) {
+		q6voiced_open(v);
+	} else if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallRemoved")) {
+		q6voiced_close(v);
+	} else if (dbus_message_is_signal(msg, "org.freedesktop.ModemManager1.Call", "StateChanged")) {
+		/*
+		 * For ModemManager call objects are created in advance
+		 * and not necessarily immediately started.
+		 * Need to listen for call state changes.
+		 */
+		int old_state, new_state;
+
+		if (!dbus_message_get_args(msg, err,
+					   DBUS_TYPE_INT32, &old_state,
+					   DBUS_TYPE_INT32, &new_state,
+					   DBUS_TYPE_INVALID))
+			return;
+
+		if (old_state == new_state)
+			return; /* No change */
+
+		if (mm_state_is_active(new_state))
+			q6voiced_open(v);
+		else if (mm_state_is_active(old_state) && !mm_state_is_active(new_state))
+			q6voiced_close(v);
+	}
+}
+
 int main(int argc, char **argv)
 {
-	struct pcm *tx = NULL, *rx = NULL;
-	unsigned int card, device;
+	struct q6voiced v = {0};
 
 	DBusMessage *msg;
 	DBusConnection *conn;
 	DBusError err;
 
-	if (argc != 2 || sscanf(argv[1], "hw:%u,%u", &card, &device) != 2) {
+	if (argc != 2 || sscanf(argv[1], "hw:%u,%u", &v.card, &v.device) != 2) {
 		fprintf(stderr, "Usage: q6voiced hw:<card>,<device>\n");
 		return 1;
 	}
@@ -41,6 +133,7 @@ int main(int argc, char **argv)
 		return 1;
 
 	dbus_bus_add_match(conn, "type='signal',interface='org.ofono.VoiceCallManager'", &err);
+	dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.ModemManager1.Call'", &err);
 	dbus_connection_flush(conn);
 	if (dbus_error_is_set(&err)) {
 		fprintf(stderr, "Match error: %s\n", err.message);
@@ -48,40 +141,14 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	printf("Listening for VoiceCallManager signals.\n");
-
 	// Loop listening for signals being emmitted
 	while (dbus_connection_read_write(conn, -1)) {
 		// We need to process all received messages
 		while (msg = dbus_connection_pop_message(conn)) {
-			// Check if the message is a signal from the correct interface and with the correct name
-			if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallAdded")) {
-				if (!tx) {
-					/*
-					 * Opening the PCM devices starts the stream.
-					 * This should be replaced by a codec2codec link probably.
-					 */
-					tx = pcm_open(card, device, PCM_IN, &pcm_config_voice_call);
-					if (!pcm_is_ready(tx))
-						perror("Failed to open tx");
-
-					rx = pcm_open(card, device, PCM_OUT, &pcm_config_voice_call);
-					if (!pcm_is_ready(rx))
-						perror("Failed to open rx");
-
-					printf("PCM devices were opened.\n");
-				} else
-					printf("PCM is already opened!\n");
-
-			} else if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallRemoved")) {
-				if (rx) {
-					pcm_close(rx);
-					pcm_close(tx);
-
-					printf("PCM devices were closed.\n");
-					rx = tx = NULL;
-				} else
-					printf("PCM is already closed!\n");
+			handle_signal(&v, msg, &err);
+			if (dbus_error_is_set(&err)) {
+				fprintf(stderr, "Failed to handle signal: %s\n", err.message);
+				dbus_error_free(&err);
 			}
 
 			dbus_message_unref(msg);