const struct drm_panel_funcs *funcs, int connector_type)
 {
        INIT_LIST_HEAD(&panel->list);
+       INIT_LIST_HEAD(&panel->followers);
+       mutex_init(&panel->follower_lock);
        panel->dev = dev;
        panel->funcs = funcs;
        panel->connector_type = connector_type;
  */
 int drm_panel_prepare(struct drm_panel *panel)
 {
+       struct drm_panel_follower *follower;
        int ret;
 
        if (!panel)
                return 0;
        }
 
+       mutex_lock(&panel->follower_lock);
+
        if (panel->funcs && panel->funcs->prepare) {
                ret = panel->funcs->prepare(panel);
                if (ret < 0)
-                       return ret;
+                       goto exit;
        }
        panel->prepared = true;
 
-       return 0;
+       list_for_each_entry(follower, &panel->followers, list) {
+               ret = follower->funcs->panel_prepared(follower);
+               if (ret < 0)
+                       dev_info(panel->dev, "%ps failed: %d\n",
+                                follower->funcs->panel_prepared, ret);
+       }
+
+       ret = 0;
+exit:
+       mutex_unlock(&panel->follower_lock);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_panel_prepare);
 
  */
 int drm_panel_unprepare(struct drm_panel *panel)
 {
+       struct drm_panel_follower *follower;
        int ret;
 
        if (!panel)
                return 0;
        }
 
+       mutex_lock(&panel->follower_lock);
+
+       list_for_each_entry(follower, &panel->followers, list) {
+               ret = follower->funcs->panel_unpreparing(follower);
+               if (ret < 0)
+                       dev_info(panel->dev, "%ps failed: %d\n",
+                                follower->funcs->panel_unpreparing, ret);
+       }
+
        if (panel->funcs && panel->funcs->unprepare) {
                ret = panel->funcs->unprepare(panel);
                if (ret < 0)
-                       return ret;
+                       goto exit;
        }
        panel->prepared = false;
 
-       return 0;
+       ret = 0;
+exit:
+       mutex_unlock(&panel->follower_lock);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_panel_unprepare);
 
 EXPORT_SYMBOL(of_drm_get_panel_orientation);
 #endif
 
+/**
+ * drm_is_panel_follower() - Check if the device is a panel follower
+ * @dev: The 'struct device' to check
+ *
+ * This checks to see if a device needs to be power sequenced together with
+ * a panel using the panel follower API.
+ * At the moment panels can only be followed on device tree enabled systems.
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: true if we should be power sequenced with a panel; false otherwise.
+ */
+bool drm_is_panel_follower(struct device *dev)
+{
+       /*
+        * The "panel" property is actually a phandle, but for simplicity we
+        * don't bother trying to parse it here. We just need to know if the
+        * property is there.
+        */
+       return of_property_read_bool(dev->of_node, "panel");
+}
+EXPORT_SYMBOL(drm_is_panel_follower);
+
+/**
+ * drm_panel_add_follower() - Register something to follow panel state.
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower:     The panel follower descriptor for the follower.
+ *
+ * A panel follower is called right after preparing the panel and right before
+ * unpreparing the panel. It's primary intention is to power on an associated
+ * touchscreen, though it could be used for any similar devices. Multiple
+ * devices are allowed the follow the same panel.
+ *
+ * If a follower is added to a panel that's already been turned on, the
+ * follower's prepare callback is called right away.
+ *
+ * At the moment panels can only be followed on device tree enabled systems.
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: 0 or an error code. Note that -ENODEV means that we detected that
+ *         follower_dev is not actually following a panel. The caller may
+ *         choose to ignore this return value if following a panel is optional.
+ */
+int drm_panel_add_follower(struct device *follower_dev,
+                          struct drm_panel_follower *follower)
+{
+       struct device_node *panel_np;
+       struct drm_panel *panel;
+       int ret;
+
+       panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0);
+       if (!panel_np)
+               return -ENODEV;
+
+       panel = of_drm_find_panel(panel_np);
+       of_node_put(panel_np);
+       if (IS_ERR(panel))
+               return PTR_ERR(panel);
+
+       get_device(panel->dev);
+       follower->panel = panel;
+
+       mutex_lock(&panel->follower_lock);
+
+       list_add_tail(&follower->list, &panel->followers);
+       if (panel->prepared) {
+               ret = follower->funcs->panel_prepared(follower);
+               if (ret < 0)
+                       dev_info(panel->dev, "%ps failed: %d\n",
+                                follower->funcs->panel_prepared, ret);
+       }
+
+       mutex_unlock(&panel->follower_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_panel_add_follower);
+
+/**
+ * drm_panel_remove_follower() - Reverse drm_panel_add_follower().
+ * @follower:     The panel follower descriptor for the follower.
+ *
+ * Undo drm_panel_add_follower(). This includes calling the follower's
+ * unprepare function if we're removed from a panel that's currently prepared.
+ *
+ * Return: 0 or an error code.
+ */
+void drm_panel_remove_follower(struct drm_panel_follower *follower)
+{
+       struct drm_panel *panel = follower->panel;
+       int ret;
+
+       mutex_lock(&panel->follower_lock);
+
+       if (panel->prepared) {
+               ret = follower->funcs->panel_unpreparing(follower);
+               if (ret < 0)
+                       dev_info(panel->dev, "%ps failed: %d\n",
+                                follower->funcs->panel_unpreparing, ret);
+       }
+       list_del_init(&follower->list);
+
+       mutex_unlock(&panel->follower_lock);
+
+       put_device(panel->dev);
+}
+EXPORT_SYMBOL(drm_panel_remove_follower);
+
+static void drm_panel_remove_follower_void(void *follower)
+{
+       drm_panel_remove_follower(follower);
+}
+
+/**
+ * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower:     The panel follower descriptor for the follower.
+ *
+ * Handles calling drm_panel_remove_follower() using devm on the follower_dev.
+ *
+ * Return: 0 or an error code.
+ */
+int devm_drm_panel_add_follower(struct device *follower_dev,
+                               struct drm_panel_follower *follower)
+{
+       int ret;
+
+       ret = drm_panel_add_follower(follower_dev, follower);
+       if (ret)
+               return ret;
+
+       return devm_add_action_or_reset(follower_dev,
+                                       drm_panel_remove_follower_void, follower);
+}
+EXPORT_SYMBOL(devm_drm_panel_add_follower);
+
 #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
 /**
  * drm_panel_of_backlight - use backlight device node for backlight
 
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 
 struct backlight_device;
 struct dentry;
 struct device_node;
 struct drm_connector;
 struct drm_device;
+struct drm_panel_follower;
 struct drm_panel;
 struct display_timing;
 
        void (*debugfs_init)(struct drm_panel *panel, struct dentry *root);
 };
 
+struct drm_panel_follower_funcs {
+       /**
+        * @panel_prepared:
+        *
+        * Called after the panel has been powered on.
+        */
+       int (*panel_prepared)(struct drm_panel_follower *follower);
+
+       /**
+        * @panel_unpreparing:
+        *
+        * Called before the panel is powered off.
+        */
+       int (*panel_unpreparing)(struct drm_panel_follower *follower);
+};
+
+struct drm_panel_follower {
+       /**
+        * @funcs:
+        *
+        * Dependent device callbacks; should be initted by the caller.
+        */
+       const struct drm_panel_follower_funcs *funcs;
+
+       /**
+        * @list
+        *
+        * Used for linking into panel's list; set by drm_panel_add_follower().
+        */
+       struct list_head list;
+
+       /**
+        * @panel
+        *
+        * The panel we're dependent on; set by drm_panel_add_follower().
+        */
+       struct drm_panel *panel;
+};
+
 /**
  * struct drm_panel - DRM panel object
  */
         */
        struct list_head list;
 
+       /**
+        * @followers:
+        *
+        * A list of struct drm_panel_follower dependent on this panel.
+        */
+       struct list_head followers;
+
+       /**
+        * @followers_lock:
+        *
+        * Lock for followers list.
+        */
+       struct mutex follower_lock;
+
        /**
         * @prepare_prev_first:
         *
 }
 #endif
 
+#if defined(CONFIG_DRM_PANEL)
+bool drm_is_panel_follower(struct device *dev);
+int drm_panel_add_follower(struct device *follower_dev,
+                          struct drm_panel_follower *follower);
+void drm_panel_remove_follower(struct drm_panel_follower *follower);
+int devm_drm_panel_add_follower(struct device *follower_dev,
+                               struct drm_panel_follower *follower);
+#else
+static inline bool drm_is_panel_follower(struct device *dev)
+{
+       return false;
+}
+
+static inline int drm_panel_add_follower(struct device *follower_dev,
+                                        struct drm_panel_follower *follower)
+{
+       return -ENODEV;
+}
+
+static inline void drm_panel_remove_follower(struct drm_panel_follower *follower) { }
+static inline int devm_drm_panel_add_follower(struct device *follower_dev,
+                                             struct drm_panel_follower *follower)
+{
+       return -ENODEV;
+}
+#endif
+
 #if IS_ENABLED(CONFIG_DRM_PANEL) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
        (IS_MODULE(CONFIG_DRM) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE)))
 int drm_panel_of_backlight(struct drm_panel *panel);