Commit 036ef573 authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Daniel Vetter

drm/atomic: Allow drivers to subclass drm_atomic_state, v3

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: default avatarAnder Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 744b0588
...@@ -30,7 +30,15 @@ ...@@ -30,7 +30,15 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_plane_helper.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->connectors);
kfree(state->connector_states); kfree(state->connector_states);
...@@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state) ...@@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state)
kfree(state->crtc_states); kfree(state->crtc_states);
kfree(state->planes); kfree(state->planes);
kfree(state->plane_states); 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 * @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 * int
drm_atomic_state_alloc(struct drm_device *dev) 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 /* TODO legacy paths should maybe do a better job about
* setting this appropriately? * setting this appropriately?
*/ */
...@@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev) ...@@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev)
state->dev = 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: 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); 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 * @state: atomic state
* *
* When the w/w mutex algorithm detects a deadlock we need to back off and drop * Default implementation for clearing atomic state.
* all locks. So someone else could sneak in and change the current modeset * This is useful for drivers that subclass the atomic state.
* 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) void drm_atomic_state_default_clear(struct drm_atomic_state *state)
{ {
struct drm_device *dev = state->dev; struct drm_device *dev = state->dev;
struct drm_mode_config *config = &dev->mode_config; struct drm_mode_config *config = &dev->mode_config;
...@@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state) ...@@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
state->plane_states[i] = NULL; 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); EXPORT_SYMBOL(drm_atomic_state_clear);
/** /**
...@@ -173,14 +222,25 @@ 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) void drm_atomic_state_free(struct drm_atomic_state *state)
{ {
struct drm_device *dev;
struct drm_mode_config *config;
if (!state) if (!state)
return; return;
dev = state->dev;
config = &dev->mode_config;
drm_atomic_state_clear(state); drm_atomic_state_clear(state);
DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", 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); EXPORT_SYMBOL(drm_atomic_state_free);
......
...@@ -35,6 +35,11 @@ drm_atomic_state_alloc(struct drm_device *dev); ...@@ -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_clear(struct drm_atomic_state *state);
void drm_atomic_state_free(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 struct drm_crtc_state * __must_check
drm_atomic_get_crtc_state(struct drm_atomic_state *state, drm_atomic_get_crtc_state(struct drm_atomic_state *state,
struct drm_crtc *crtc); struct drm_crtc *crtc);
......
...@@ -979,6 +979,9 @@ struct drm_mode_set { ...@@ -979,6 +979,9 @@ struct drm_mode_set {
* @atomic_check: check whether a given atomic state update is possible * @atomic_check: check whether a given atomic state update is possible
* @atomic_commit: commit an atomic state update previously verified with * @atomic_commit: commit an atomic state update previously verified with
* atomic_check() * 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 * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that
* involve drivers. * involve drivers.
...@@ -994,6 +997,9 @@ struct drm_mode_config_funcs { ...@@ -994,6 +997,9 @@ struct drm_mode_config_funcs {
int (*atomic_commit)(struct drm_device *dev, int (*atomic_commit)(struct drm_device *dev,
struct drm_atomic_state *a, struct drm_atomic_state *a,
bool async); 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);
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment