diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index 966bd2d9f0ccd..ac87a94b7057a 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -176,6 +176,15 @@ Printer
 .. kernel-doc:: drivers/gpu/drm/drm_print.c
    :export:
 
+Tracing
+-------
+
+.. kernel-doc:: drivers/gpu/drm/drm_trace.h
+   :doc: overview
+
+.. kernel-doc:: drivers/gpu/drm/drm_trace.h
+   :internal:
+
 Utilities
 ---------
 
diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h
index 487bc3478efec..19e5f04210586 100644
--- a/drivers/gpu/drm/drm_trace.h
+++ b/drivers/gpu/drm/drm_trace.h
@@ -7,21 +7,109 @@
 #include <linux/tracepoint.h>
 
 struct drm_file;
+/**
+ * DOC: overview
+ *
+ * DRM trace events can be used to trace important moments occurring in the DRM
+ * core or helpers without incurring a significant performance penalty (like you
+ * would with console logging).
+ */
+
+#if !defined(_DRM_TRACE_EVENT_CLASS_ENUM)
+#define _DRM_TRACE_EVENT_CLASS_ENUM
+
+/**
+ * enum drm_trace_event_class - the trace class of an event
+ *
+ * Trace events are broken into different classes (much the same as we do for
+ * console logging) to make sifting through the logs easier. If you want to
+ * isolate one or more classes (or disable a subset of classes), you can write
+ * an expession using the event_class entry field as a tracepoints filter. The
+ * event_class field is not printed as part of the trace log, it's just there
+ * for filtering.
+ *
+ * The classes are bitfields to make filtering easier, but also to allow trace
+ * events to be classified under multiple classes (ie: if you're tracing an
+ * error that occurred with a crtc, you can ensure that these events are
+ * surfaced for filters looking for either crtc or error classes).
+ *
+ * An example filter is:
+ *	Assume you want to view all trace events in failure paths, you would
+ *	write a filter which isolates events with the drm_trace_class_error bit
+ *	set in their event_class field. This allows you to enable all trace
+ *	events in the drm trace system and only consume errors.
+ *
+ *	; echo "event_class & 0x1" > /sys/kernel/tracing/events/drm/filter
+ *	; echo 1 > /sys/kernel/tracing/events/drm/enable
+ *
+ *	Similarly, to view all vblank traces plus any error traces, you select
+ *	both classes in the filter.
+ *
+ *	; echo "event_class & 0x3" > /sys/kernel/tracing/events/drm/filter
+ */
+enum drm_trace_event_class {
+	/**
+	 * @drm_trace_class_error: Denotes that the trace event is coming from
+	 * an error path.
+	 */
+	drm_trace_class_error		= BIT(0),
+
+	/**
+	 * @drm_trace_class_vblank: Use this to track trace events that track
+	 * vblanks and vblank events.
+	 */
+	drm_trace_class_vblank		= BIT(1),
+};
+
+/**
+ * drm_trace - add a drm trace event
+ * @_c: class name (the part that comes after drm_trace_class_)
+ * @_e: event name (the part that comes after drm_$class_)
+ *
+ * Use this macro to simplify adding a trace event for non-failure paths in
+ * drm code. The macro simply assembles the trace event from the class and name,
+ * and sets the correct event_code for this trace.
+ *
+ * It's recommended that this macro is not used in failure paths. For failure
+ * paths, use drm_trace_err().
+ */
+#define drm_trace(_c, _e, ...) \
+	trace_drm_##_c##_##_e(drm_trace_class_##_c, __VA_ARGS__)
+
+/**
+ * drm_trace_err - add a drm trace event in a failure path
+ * @_c: class name (the part that comes after drm_trace_class_)
+ * @_e: event name (the part that comes after drm_$class_)
+ *
+ * This is virtually the same as drm_trace(), however it sets the error class on
+ * the event as well. This is beneficial for those filtering events by
+ * class_error.
+ *
+ * For non-failure paths, please use drm_trace().
+ */
+#define drm_trace_err(_c, _e, ...)					\
+	trace_drm_##_c##_##_e(drm_trace_class_##_c | drm_trace_class_error, \
+			      __VA_ARGS__)
+
+#endif
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM drm
 #define TRACE_INCLUDE_FILE drm_trace
 
 TRACE_EVENT(drm_vblank_event,
-	    TP_PROTO(int crtc, unsigned int seq, ktime_t time, bool high_prec),
-	    TP_ARGS(crtc, seq, time, high_prec),
+	    TP_PROTO(unsigned int event_class, int crtc, unsigned int seq,
+		     ktime_t time, bool high_prec),
+	    TP_ARGS(event_class, crtc, seq, time, high_prec),
 	    TP_STRUCT__entry(
+		    __field(unsigned int, event_class)
 		    __field(int, crtc)
 		    __field(unsigned int, seq)
 		    __field(ktime_t, time)
 		    __field(bool, high_prec)
 		    ),
 	    TP_fast_assign(
+		    __entry->event_class = event_class;
 		    __entry->crtc = crtc;
 		    __entry->seq = seq;
 		    __entry->time = time;
@@ -33,14 +121,17 @@ TRACE_EVENT(drm_vblank_event,
 );
 
 DECLARE_EVENT_CLASS(class_drm_vblank_event,
-	    TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
-	    TP_ARGS(file, crtc, seq),
+	    TP_PROTO(unsigned int event_class, struct drm_file *file,
+		     int crtc, unsigned int seq),
+	    TP_ARGS(event_class, file, crtc, seq),
 	    TP_STRUCT__entry(
+		    __field(unsigned int, event_class)
 		    __field(struct drm_file *, file)
 		    __field(int, crtc)
 		    __field(unsigned int, seq)
 		    ),
 	    TP_fast_assign(
+		    __entry->event_class = event_class;
 		    __entry->file = file;
 		    __entry->crtc = crtc;
 		    __entry->seq = seq;
@@ -49,12 +140,14 @@ DECLARE_EVENT_CLASS(class_drm_vblank_event,
 		      __entry->seq)
 );
 DEFINE_EVENT(class_drm_vblank_event, drm_vblank_event_queued,
-	TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
-	TP_ARGS(file, crtc, seq)
+	TP_PROTO(unsigned int event_class, struct drm_file *file, int crtc,
+		 unsigned int seq),
+	TP_ARGS(event_class, file, crtc, seq)
 );
 DEFINE_EVENT(class_drm_vblank_event, drm_vblank_event_delivered,
-	TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
-	TP_ARGS(file, crtc, seq)
+	TP_PROTO(unsigned int event_class, struct drm_file *file, int crtc,
+		 unsigned int seq),
+	TP_ARGS(event_class, file, crtc, seq)
 );
 
 #endif /* _DRM_TRACE_H_ */
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 1659b13b178c2..19a63a94586ad 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -880,7 +880,7 @@ static void send_vblank_event(struct drm_device *dev,
 		e->event.seq.time_ns = ktime_to_ns(now);
 		break;
 	}
-	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, seq);
+	drm_trace(vblank, event_delivered, e->base.file_priv, e->pipe, seq);
 	drm_send_event_locked(dev, &e->base);
 }
 
@@ -1529,7 +1529,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
 	DRM_DEBUG("event on vblank count %llu, current %llu, crtc %u\n",
 		  req_seq, seq, pipe);
 
-	trace_drm_vblank_event_queued(file_priv, pipe, req_seq);
+	drm_trace(vblank, event_queued, file_priv, pipe, req_seq);
 
 	e->sequence = req_seq;
 	if (vblank_passed(seq, req_seq)) {
@@ -1760,8 +1760,8 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
 		send_vblank_event(dev, e, seq, now);
 	}
 
-	trace_drm_vblank_event(pipe, seq, now,
-			dev->driver->get_vblank_timestamp != NULL);
+	drm_trace(vblank, event, pipe, seq, now,
+		  dev->driver->get_vblank_timestamp != NULL);
 }
 
 /**
