drm/atomic: Allow drivers to subclass drm_atomic_state, v3
authorMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Mon, 18 May 2015 08:06:40 +0000 (10:06 +0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 18 May 2015 14:39:41 +0000 (16:39 +0200)
Drivers may need to store the state of shared resources, such as PLLs
or FIFO space, into the atomic state. Allow this by making it possible
to subclass drm_atomic_state.

Changes since v1:
- Change member names for functions to atomic_state_(alloc,clear)
- Change __drm_atomic_state_new to drm_atomic_state_init
- Allow free function to be overridden too, in case extra memory is
  allocated in alloc.
Changes since v2:
- Rename *_default_free to default_release, to make clear it doesn't
  free the state object itself.

Cc: dri-devel@lists.freedesktop.org
Acked-by: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/drm_atomic.c
include/drm/drm_atomic.h
include/drm/drm_crtc.h

index c6277a4a1f2f235e43fcfd569944d46dc7133119..cd1b16b2571660ffb19bacba45bf5817a1b6ee30 100644 (file)
 #include <drm/drm_atomic.h>
 #include <drm/drm_plane_helper.h>
 
-static void kfree_state(struct drm_atomic_state *state)
+/**
+ * drm_atomic_state_default_release -
+ * release memory initialized by drm_atomic_state_init
+ * @state: atomic state
+ *
+ * Free all the memory allocated by drm_atomic_state_init.
+ * This is useful for drivers that subclass the atomic state.
+ */
+void drm_atomic_state_default_release(struct drm_atomic_state *state)
 {
        kfree(state->connectors);
        kfree(state->connector_states);
@@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state)
        kfree(state->crtc_states);
        kfree(state->planes);
        kfree(state->plane_states);
-       kfree(state);
 }
+EXPORT_SYMBOL(drm_atomic_state_default_release);
 
 /**
- * drm_atomic_state_alloc - allocate atomic state
+ * drm_atomic_state_init - init new atomic state
  * @dev: DRM device
+ * @state: atomic state
  *
- * This allocates an empty atomic state to track updates.
+ * Default implementation for filling in a new atomic state.
+ * This is useful for drivers that subclass the atomic state.
  */
-struct drm_atomic_state *
-drm_atomic_state_alloc(struct drm_device *dev)
+int
+drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
 {
-       struct drm_atomic_state *state;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return NULL;
-
        /* TODO legacy paths should maybe do a better job about
         * setting this appropriately?
         */
@@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev)
 
        state->dev = dev;
 
-       DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state);
+       DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state);
 
-       return state;
+       return 0;
 fail:
-       kfree_state(state);
+       drm_atomic_state_default_release(state);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_atomic_state_init);
+
+/**
+ * drm_atomic_state_alloc - allocate atomic state
+ * @dev: DRM device
+ *
+ * This allocates an empty atomic state to track updates.
+ */
+struct drm_atomic_state *
+drm_atomic_state_alloc(struct drm_device *dev)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_atomic_state *state;
+
+       if (!config->funcs->atomic_state_alloc) {
+               state = kzalloc(sizeof(*state), GFP_KERNEL);
+               if (!state)
+                       return NULL;
+               if (drm_atomic_state_init(dev, state) < 0) {
+                       kfree(state);
+                       return NULL;
+               }
+               return state;
+       }
 
-       return NULL;
+       return config->funcs->atomic_state_alloc(dev);
 }
 EXPORT_SYMBOL(drm_atomic_state_alloc);
 
 /**
- * drm_atomic_state_clear - clear state object
+ * drm_atomic_state_default_clear - clear base atomic state
  * @state: atomic state
  *
- * When the w/w mutex algorithm detects a deadlock we need to back off and drop
- * all locks. So someone else could sneak in and change the current modeset
- * configuration. Which means that all the state assembled in @state is no
- * longer an atomic update to the current state, but to some arbitrary earlier
- * state. Which could break assumptions the driver's ->atomic_check likely
- * relies on.
- *
- * Hence we must clear all cached state and completely start over, using this
- * function.
+ * Default implementation for clearing atomic state.
+ * This is useful for drivers that subclass the atomic state.
  */
-void drm_atomic_state_clear(struct drm_atomic_state *state)
+void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
        struct drm_mode_config *config = &dev->mode_config;
@@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
                state->plane_states[i] = NULL;
        }
 }
+EXPORT_SYMBOL(drm_atomic_state_default_clear);
+
+/**
+ * drm_atomic_state_clear - clear state object
+ * @state: atomic state
+ *
+ * When the w/w mutex algorithm detects a deadlock we need to back off and drop
+ * all locks. So someone else could sneak in and change the current modeset
+ * configuration. Which means that all the state assembled in @state is no
+ * longer an atomic update to the current state, but to some arbitrary earlier
+ * state. Which could break assumptions the driver's ->atomic_check likely
+ * relies on.
+ *
+ * Hence we must clear all cached state and completely start over, using this
+ * function.
+ */
+void drm_atomic_state_clear(struct drm_atomic_state *state)
+{
+       struct drm_device *dev = state->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (config->funcs->atomic_state_clear)
+               config->funcs->atomic_state_clear(state);
+       else
+               drm_atomic_state_default_clear(state);
+}
 EXPORT_SYMBOL(drm_atomic_state_clear);
 
 /**
@@ -173,14 +222,25 @@ EXPORT_SYMBOL(drm_atomic_state_clear);
  */
 void drm_atomic_state_free(struct drm_atomic_state *state)
 {
+       struct drm_device *dev;
+       struct drm_mode_config *config;
+
        if (!state)
                return;
 
+       dev = state->dev;
+       config = &dev->mode_config;
+
        drm_atomic_state_clear(state);
 
        DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state);
 
-       kfree_state(state);
+       if (config->funcs->atomic_state_free) {
+               config->funcs->atomic_state_free(state);
+       } else {
+               drm_atomic_state_default_release(state);
+               kfree(state);
+       }
 }
 EXPORT_SYMBOL(drm_atomic_state_free);
 
index d78543067700b86cb3e6e86ca9aa07675a0d5933..f0d3a7387d993bdd604089faa7c4eba5ca14016c 100644 (file)
@@ -35,6 +35,11 @@ drm_atomic_state_alloc(struct drm_device *dev);
 void drm_atomic_state_clear(struct drm_atomic_state *state);
 void drm_atomic_state_free(struct drm_atomic_state *state);
 
+int  __must_check
+drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state);
+void drm_atomic_state_default_clear(struct drm_atomic_state *state);
+void drm_atomic_state_default_release(struct drm_atomic_state *state);
+
 struct drm_crtc_state * __must_check
 drm_atomic_get_crtc_state(struct drm_atomic_state *state,
                          struct drm_crtc *crtc);
index 5626191f3af0ddbea2bce691b2778c4ea64e9feb..37c44f27cb9f00efbd0023f3c6bbfa82445631f2 100644 (file)
@@ -979,6 +979,9 @@ struct drm_mode_set {
  * @atomic_check: check whether a given atomic state update is possible
  * @atomic_commit: commit an atomic state update previously verified with
  *     atomic_check()
+ * @atomic_state_alloc: allocate a new atomic state
+ * @atomic_state_clear: clear the atomic state
+ * @atomic_state_free: free the atomic state
  *
  * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that
  * involve drivers.
@@ -994,6 +997,9 @@ struct drm_mode_config_funcs {
        int (*atomic_commit)(struct drm_device *dev,
                             struct drm_atomic_state *a,
                             bool async);
+       struct drm_atomic_state *(*atomic_state_alloc)(struct drm_device *dev);
+       void (*atomic_state_clear)(struct drm_atomic_state *state);
+       void (*atomic_state_free)(struct drm_atomic_state *state);
 };
 
 /**