diff --git a/utils/v4l2-tracer/libv4l2tracer.cpp b/utils/v4l2-tracer/libv4l2tracer.cpp
index 58a02656..7286f321 100644
--- a/utils/v4l2-tracer/libv4l2tracer.cpp
+++ b/utils/v4l2-tracer/libv4l2tracer.cpp
@@ -44,6 +44,9 @@ const std::list<unsigned long> ioctls = {
 	VIDIOC_ENUM_FRAMEINTERVALS,
 	VIDIOC_ENCODER_CMD,
 	VIDIOC_TRY_ENCODER_CMD,
+	VIDIOC_DQEVENT,
+	VIDIOC_SUBSCRIBE_EVENT,
+	VIDIOC_UNSUBSCRIBE_EVENT,
 	VIDIOC_CREATE_BUFS,
 	VIDIOC_PREPARE_BUF,
 	VIDIOC_G_SELECTION,
diff --git a/utils/v4l2-tracer/retrace.cpp b/utils/v4l2-tracer/retrace.cpp
index 28dc6afe..88e70ea9 100644
--- a/utils/v4l2-tracer/retrace.cpp
+++ b/utils/v4l2-tracer/retrace.cpp
@@ -1120,6 +1120,61 @@ void retrace_vidioc_try_decoder_cmd(int fd_retrace, json_object *ioctl_args)
 	free (ptr);
 }
 
+void retrace_vidioc_dqevent(int fd_retrace)
+{
+	const int poll_timeout_ms = 200;
+	struct pollfd *pfds = (struct pollfd *) calloc(1, sizeof(struct pollfd));
+	if (pfds == nullptr)
+		exit(EXIT_FAILURE);
+	pfds[0].fd = fd_retrace;
+	pfds[0].events = POLLIN | POLLPRI;
+	int ret = poll(pfds, 1, poll_timeout_ms);
+
+	if (ret == -1) {
+		line_info("\n\tPoll error.");
+		perror("");
+		free(pfds);
+		exit(EXIT_FAILURE);
+	}
+	if (ret == 0) {
+		line_info("\n\tPoll timed out.");
+		free(pfds);
+		exit(EXIT_FAILURE);
+	}
+
+	struct v4l2_event event = {};
+	ioctl(fd_retrace, VIDIOC_DQEVENT, &event);
+
+	if (is_verbose() || (errno != 0))
+		perror("VIDIOC_DQEVENT");
+
+	free(pfds);
+}
+
+void retrace_vidioc_subscribe_event(int fd_retrace, json_object *ioctl_args)
+{
+	struct v4l2_event_subscription *ptr = retrace_v4l2_event_subscription_gen(ioctl_args);
+
+	ioctl(fd_retrace, VIDIOC_SUBSCRIBE_EVENT, ptr);
+
+	if (is_verbose() || (errno != 0))
+		perror("VIDIOC_SUBSCRIBE_EVENT");
+
+	free (ptr);
+}
+
+void retrace_vidioc_unsubscribe(int fd_retrace, json_object *ioctl_args)
+{
+	struct v4l2_event_subscription *ptr = retrace_v4l2_event_subscription_gen(ioctl_args);
+
+	ioctl(fd_retrace, VIDIOC_UNSUBSCRIBE_EVENT, ptr);
+
+	if (is_verbose() || (errno != 0))
+		perror("VIDIOC_UNSUBSCRIBE_EVENT");
+
+	free (ptr);
+}
+
 void retrace_vidioc_decoder_cmd(int fd_retrace, json_object *ioctl_args)
 {
 	struct v4l2_decoder_cmd *ptr = retrace_v4l2_decoder_cmd(ioctl_args);
@@ -1187,19 +1242,25 @@ void retrace_ioctl(json_object *syscall_obj)
 {
 	__s64 cmd = 0;
 	int fd_retrace = 0;
+	bool ioctl_error = false;
 
 	json_object *fd_trace_obj;
 	json_object_object_get_ex(syscall_obj, "fd", &fd_trace_obj);
 	fd_retrace = get_fd_retrace_from_fd_trace(json_object_get_int(fd_trace_obj));
-	if (fd_retrace < 0) {
-		line_info("\n\tBad file descriptor.");
-		return;
-	}
 
 	json_object *cmd_obj;
 	json_object_object_get_ex(syscall_obj, "ioctl", &cmd_obj);
 	cmd = s2val(json_object_get_string(cmd_obj), ioctl_val_def);
 
+	json_object *errno_obj;
+	if (json_object_object_get_ex(syscall_obj, "errno", &errno_obj))
+		ioctl_error = true;
+
+	if (fd_retrace < 0) {
+		line_info("\n\tBad file descriptor on %s\n", json_object_get_string(cmd_obj));
+		return;
+	}
+
 	json_object *ioctl_args_user;
 	json_object_object_get_ex(syscall_obj, "from_userspace", &ioctl_args_user);
 
@@ -1324,6 +1385,18 @@ void retrace_ioctl(json_object *syscall_obj)
 	case VIDIOC_TRY_DECODER_CMD:
 		retrace_vidioc_try_decoder_cmd(fd_retrace, ioctl_args_user);
 		break;
+	case VIDIOC_DQEVENT:
+		/* Don't retrace a timed-out DQEVENT */
+		if (ioctl_error)
+			break;
+		retrace_vidioc_dqevent(fd_retrace);
+		break;
+	case VIDIOC_SUBSCRIBE_EVENT:
+		retrace_vidioc_subscribe_event(fd_retrace, ioctl_args_user);
+		break;
+	case VIDIOC_UNSUBSCRIBE_EVENT:
+		retrace_vidioc_unsubscribe(fd_retrace, ioctl_args_user);
+		break;
 	case VIDIOC_DECODER_CMD:
 		retrace_vidioc_decoder_cmd(fd_retrace, ioctl_args_user);
 		break;
diff --git a/utils/v4l2-tracer/trace.cpp b/utils/v4l2-tracer/trace.cpp
index 5049a996..0e8531ff 100644
--- a/utils/v4l2-tracer/trace.cpp
+++ b/utils/v4l2-tracer/trace.cpp
@@ -599,6 +599,13 @@ json_object *trace_ioctl_args(unsigned long cmd, void *arg)
 	case VIDIOC_ENCODER_CMD:
 		trace_v4l2_encoder_cmd_gen(arg, ioctl_args);
 		break;
+	case VIDIOC_DQEVENT:
+		trace_v4l2_event_gen(arg, ioctl_args);
+		break;
+	case VIDIOC_SUBSCRIBE_EVENT:
+	case VIDIOC_UNSUBSCRIBE_EVENT:
+		trace_v4l2_event_subscription_gen(arg, ioctl_args);
+		break;
 	case VIDIOC_CREATE_BUFS:
 		trace_v4l2_create_buffers_gen(arg, ioctl_args);
 		break;
diff --git a/utils/v4l2-tracer/v4l2-tracer-gen.pl b/utils/v4l2-tracer/v4l2-tracer-gen.pl
index 4db204e3..cbfdfbe0 100755
--- a/utils/v4l2-tracer/v4l2-tracer-gen.pl
+++ b/utils/v4l2-tracer/v4l2-tracer-gen.pl
@@ -161,7 +161,7 @@ sub get_val_def_name {
 	@structs_that_use_v4l2_buf_type = qw(v4l2_fmtdesc v4l2_requestbuffers v4l2_buffer v4l2_crop
 	                                     v4l2_exportbuffer v4l2_cropcap v4l2_selection
 	                                     v4l2_sliced_vbi_cap v4l2_format v4l2_streamparm);
-	@structs_that_use_v4l2_ctrl_type = qw(v4l2_queryctrl v4l2_query_ext_ctrl);
+	@structs_that_use_v4l2_ctrl_type = qw(v4l2_queryctrl v4l2_query_ext_ctrl v4l2_event_ctrl);
 	@structs_that_use_v4l2_tuner_type = qw(v4l2_tuner v4l2_frequency);
 	if ($member eq "type") {
 		foreach (@structs_that_use_v4l2_buf_type) {
@@ -191,6 +191,9 @@ sub get_val_def_name {
 		if ($struct_name eq "v4l2_output") {
 			return $val_def_name = "output_type_val_def";
 		}
+		if ($struct_name eq "v4l2_event" || $struct_name eq "v4l2_event_subscription") {
+			return $val_def_name = "event_val_def";
+		}
 		return "nullptr"; # will print as hex string
 	}
 	if ($member eq "pixelformat" || $member eq "pixel_format") {
@@ -270,7 +273,10 @@ sub get_flag_def_name {
 		if ($struct_name =~ /.*selection$/) {
 			return "v4l2_sel_flag_def";
 		}
-			return "nullptr";
+		if ($struct_name eq "v4l2_event_subscription") {
+			return "v4l2_event_sub_flag_def";
+		}
+		return "nullptr";
 	}
 
 	if ($member =~ /.*cap.*/) {
@@ -289,6 +295,11 @@ sub get_flag_def_name {
 	if ($member eq "rxsubchans") {
 		return "tuner_rxsub_flag_def";
 	}
+	if ($member eq "changes") {
+		if ($struct_name eq "v4l2_event_ctrl") {
+			return "v4l2_event_ctrl_ch_flag_def";
+		}
+	}
 	return "";
 }
 
@@ -383,6 +394,21 @@ sub handle_union {
 		printf $fh_trace_cpp "\tdefault:\n\t\tbreak;\n\t}\n";
 	}
 
+	if ($struct_name eq "v4l2_event") {
+		printf $fh_trace_cpp "\tswitch (p->type) {\n";
+		printf $fh_trace_cpp "\tcase V4L2_EVENT_VSYNC:\n";
+		printf $fh_trace_cpp "\t\ttrace_v4l2_event_vsync_gen(&p->u, %s_obj);\n\t\tbreak;\n", $struct_name;
+		printf $fh_trace_cpp "\tcase V4L2_EVENT_CTRL:\n";
+		printf $fh_trace_cpp "\t\ttrace_v4l2_event_ctrl_gen(&p->u, %s_obj);\n\t\tbreak;\n", $struct_name;
+		printf $fh_trace_cpp "\tcase V4L2_EVENT_FRAME_SYNC:\n";
+		printf $fh_trace_cpp "\t\ttrace_v4l2_event_frame_sync_gen(&p->u, %s_obj);\n\t\tbreak;\n", $struct_name;
+		printf $fh_trace_cpp "\tcase V4L2_EVENT_SOURCE_CHANGE:\n";
+		printf $fh_trace_cpp "\t\ttrace_v4l2_event_src_change_gen(&p->u, %s_obj);\n\t\tbreak;\n", $struct_name;
+		printf $fh_trace_cpp "\tcase V4L2_EVENT_MOTION_DET:\n";
+		printf $fh_trace_cpp "\t\ttrace_v4l2_event_motion_det_gen(&p->u, %s_obj);\n\t\tbreak;\n", $struct_name;
+		printf $fh_trace_cpp "\tdefault:\n\t\tbreak;\n\t}\n";
+	}
+
 	return $suppress_union;
 }
 
@@ -854,17 +880,15 @@ while (<>) {
 	if (grep {/#define __LINUX_VIDEODEV2_H/} $_) {
 		$in_v4l2_controls = false;
 	}
-
 	if (grep {/^#define.+FWHT_FL_.+/} $_) {
 		flag_gen("fwht");
 	} elsif (grep {/^#define V4L2_VP8_LF.*/} $_) {
 		flag_gen("vp8_loop_filter");
-	} elsif (grep {/^#define.+_FL_.+/} $_) {  #use to get media flags
+	} elsif (grep {/^#define.+_FL_.+/} $_) {
 		flag_gen();
 	} elsif (grep {/^#define.+_FLAG_.+/} $_) {
 		flag_gen();
 	}
-
 	if ($in_v4l2_controls eq true) {
 		if (grep {/^struct/} $_) {
 			struct_gen_ctrl();
@@ -964,7 +988,16 @@ while (<>) {
 		val_def_gen("V4L2_DEC_CMD_FLUSH");
 		next;
 	}
-
+	if (grep {/^#define V4L2_EVENT_ALL\s+/} $_) {
+		printf $fh_common_info_h "constexpr val_def event_val_def[] = {\n";
+		val_def_gen("V4L2_EVENT_PRIVATE_START");
+		next;
+	}
+	if (grep {/^#define V4L2_EVENT_CTRL_CH_VALUE\s+/} $_) {
+		printf $fh_common_info_h "constexpr flag_def v4l2_event_ctrl_ch_flag_def[] = {\n";
+		flag_def_gen("V4L2_EVENT_CTRL_CH_DIMENSIONS");
+		next
+	}
 	if (grep {/^#define\s+(VIDIOC_\w*)\s*.*/} $_) {
 		push (@ioctls, $_);
 	}
