Commit 8c7eac58 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-msm-next-2018-06-04' of git://people.freedesktop.org/~robclark/linux into drm-next

A bunch of msm fixes and some atomic work.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGvXe157nd70=GPjre=HjiAH91tGM50+XtM59b2MEChvXQ@mail.gmail.com
parents 568cf2e6 74d3a3a7
...@@ -98,21 +98,6 @@ static const struct drm_plane_funcs mdp4_plane_funcs = { ...@@ -98,21 +98,6 @@ static const struct drm_plane_funcs mdp4_plane_funcs = {
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
}; };
static int mdp4_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
struct mdp4_kms *mdp4_kms = get_kms(plane);
struct msm_kms *kms = &mdp4_kms->base.base;
struct drm_framebuffer *fb = new_state->fb;
if (!fb)
return 0;
DBG("%s: prepare: FB[%u]", mdp4_plane->name, fb->base.id);
return msm_framebuffer_prepare(fb, kms->aspace);
}
static void mdp4_plane_cleanup_fb(struct drm_plane *plane, static void mdp4_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
{ {
...@@ -152,7 +137,7 @@ static void mdp4_plane_atomic_update(struct drm_plane *plane, ...@@ -152,7 +137,7 @@ static void mdp4_plane_atomic_update(struct drm_plane *plane,
} }
static const struct drm_plane_helper_funcs mdp4_plane_helper_funcs = { static const struct drm_plane_helper_funcs mdp4_plane_helper_funcs = {
.prepare_fb = mdp4_plane_prepare_fb, .prepare_fb = msm_atomic_prepare_fb,
.cleanup_fb = mdp4_plane_cleanup_fb, .cleanup_fb = mdp4_plane_cleanup_fb,
.atomic_check = mdp4_plane_atomic_check, .atomic_check = mdp4_plane_atomic_check,
.atomic_update = mdp4_plane_atomic_update, .atomic_update = mdp4_plane_atomic_update,
......
...@@ -430,6 +430,7 @@ static void mdp5_crtc_atomic_disable(struct drm_crtc *crtc, ...@@ -430,6 +430,7 @@ static void mdp5_crtc_atomic_disable(struct drm_crtc *crtc,
struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc);
struct device *dev = &mdp5_kms->pdev->dev; struct device *dev = &mdp5_kms->pdev->dev;
unsigned long flags;
DBG("%s", crtc->name); DBG("%s", crtc->name);
...@@ -445,6 +446,14 @@ static void mdp5_crtc_atomic_disable(struct drm_crtc *crtc, ...@@ -445,6 +446,14 @@ static void mdp5_crtc_atomic_disable(struct drm_crtc *crtc,
mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err); mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err);
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
if (crtc->state->event && !crtc->state->active) {
WARN_ON(mdp5_crtc->event);
spin_lock_irqsave(&mdp5_kms->dev->event_lock, flags);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
spin_unlock_irqrestore(&mdp5_kms->dev->event_lock, flags);
}
mdp5_crtc->enabled = false; mdp5_crtc->enabled = false;
} }
......
...@@ -70,60 +70,110 @@ static int mdp5_hw_init(struct msm_kms *kms) ...@@ -70,60 +70,110 @@ static int mdp5_hw_init(struct msm_kms *kms)
return 0; return 0;
} }
struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s) /* Global/shared object state funcs */
/*
* This is a helper that returns the private state currently in operation.
* Note that this would return the "old_state" if called in the atomic check
* path, and the "new_state" after the atomic swap has been done.
*/
struct mdp5_global_state *
mdp5_get_existing_global_state(struct mdp5_kms *mdp5_kms)
{
return to_mdp5_global_state(mdp5_kms->glob_state.state);
}
/*
* This acquires the modeset lock set aside for global state, creates
* a new duplicated private object state.
*/
struct mdp5_global_state *mdp5_get_global_state(struct drm_atomic_state *s)
{ {
struct msm_drm_private *priv = s->dev->dev_private; struct msm_drm_private *priv = s->dev->dev_private;
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
struct msm_kms_state *state = to_kms_state(s); struct drm_private_state *priv_state;
struct mdp5_state *new_state;
int ret; int ret;
if (state->state) ret = drm_modeset_lock(&mdp5_kms->glob_state_lock, s->acquire_ctx);
return state->state;
ret = drm_modeset_lock(&mdp5_kms->state_lock, s->acquire_ctx);
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
new_state = kmalloc(sizeof(*mdp5_kms->state), GFP_KERNEL); priv_state = drm_atomic_get_private_obj_state(s, &mdp5_kms->glob_state);
if (!new_state) if (IS_ERR(priv_state))
return ERR_PTR(-ENOMEM); return ERR_CAST(priv_state);
/* Copy state: */ return to_mdp5_global_state(priv_state);
new_state->hwpipe = mdp5_kms->state->hwpipe; }
new_state->hwmixer = mdp5_kms->state->hwmixer;
if (mdp5_kms->smp) static struct drm_private_state *
new_state->smp = mdp5_kms->state->smp; mdp5_global_duplicate_state(struct drm_private_obj *obj)
{
struct mdp5_global_state *state;
state->state = new_state; state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
return new_state; return &state->base;
} }
static void mdp5_swap_state(struct msm_kms *kms, struct drm_atomic_state *state) static void mdp5_global_destroy_state(struct drm_private_obj *obj,
struct drm_private_state *state)
{ {
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct mdp5_global_state *mdp5_state = to_mdp5_global_state(state);
swap(to_kms_state(state)->state, mdp5_kms->state);
kfree(mdp5_state);
}
static const struct drm_private_state_funcs mdp5_global_state_funcs = {
.atomic_duplicate_state = mdp5_global_duplicate_state,
.atomic_destroy_state = mdp5_global_destroy_state,
};
static int mdp5_global_obj_init(struct mdp5_kms *mdp5_kms)
{
struct mdp5_global_state *state;
drm_modeset_lock_init(&mdp5_kms->glob_state_lock);
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
state->mdp5_kms = mdp5_kms;
drm_atomic_private_obj_init(&mdp5_kms->glob_state,
&state->base,
&mdp5_global_state_funcs);
return 0;
} }
static void mdp5_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state) static void mdp5_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state)
{ {
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct device *dev = &mdp5_kms->pdev->dev; struct device *dev = &mdp5_kms->pdev->dev;
struct mdp5_global_state *global_state;
global_state = mdp5_get_existing_global_state(mdp5_kms);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
if (mdp5_kms->smp) if (mdp5_kms->smp)
mdp5_smp_prepare_commit(mdp5_kms->smp, &mdp5_kms->state->smp); mdp5_smp_prepare_commit(mdp5_kms->smp, &global_state->smp);
} }
static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state) static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
{ {
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct device *dev = &mdp5_kms->pdev->dev; struct device *dev = &mdp5_kms->pdev->dev;
struct mdp5_global_state *global_state;
global_state = mdp5_get_existing_global_state(mdp5_kms);
if (mdp5_kms->smp) if (mdp5_kms->smp)
mdp5_smp_complete_commit(mdp5_kms->smp, &mdp5_kms->state->smp); mdp5_smp_complete_commit(mdp5_kms->smp, &global_state->smp);
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
} }
...@@ -229,7 +279,6 @@ static const struct mdp_kms_funcs kms_funcs = { ...@@ -229,7 +279,6 @@ static const struct mdp_kms_funcs kms_funcs = {
.irq = mdp5_irq, .irq = mdp5_irq,
.enable_vblank = mdp5_enable_vblank, .enable_vblank = mdp5_enable_vblank,
.disable_vblank = mdp5_disable_vblank, .disable_vblank = mdp5_disable_vblank,
.swap_state = mdp5_swap_state,
.prepare_commit = mdp5_prepare_commit, .prepare_commit = mdp5_prepare_commit,
.complete_commit = mdp5_complete_commit, .complete_commit = mdp5_complete_commit,
.wait_for_crtc_commit_done = mdp5_wait_for_crtc_commit_done, .wait_for_crtc_commit_done = mdp5_wait_for_crtc_commit_done,
...@@ -727,7 +776,8 @@ static void mdp5_destroy(struct platform_device *pdev) ...@@ -727,7 +776,8 @@ static void mdp5_destroy(struct platform_device *pdev)
if (mdp5_kms->rpm_enabled) if (mdp5_kms->rpm_enabled)
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
kfree(mdp5_kms->state); drm_atomic_private_obj_fini(&mdp5_kms->glob_state);
drm_modeset_lock_fini(&mdp5_kms->glob_state_lock);
} }
static int construct_pipes(struct mdp5_kms *mdp5_kms, int cnt, static int construct_pipes(struct mdp5_kms *mdp5_kms, int cnt,
...@@ -880,12 +930,9 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) ...@@ -880,12 +930,9 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
mdp5_kms->dev = dev; mdp5_kms->dev = dev;
mdp5_kms->pdev = pdev; mdp5_kms->pdev = pdev;
drm_modeset_lock_init(&mdp5_kms->state_lock); ret = mdp5_global_obj_init(mdp5_kms);
mdp5_kms->state = kzalloc(sizeof(*mdp5_kms->state), GFP_KERNEL); if (ret)
if (!mdp5_kms->state) {
ret = -ENOMEM;
goto fail; goto fail;
}
mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5");
if (IS_ERR(mdp5_kms->mmio)) { if (IS_ERR(mdp5_kms->mmio)) {
......
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
#include "mdp5_ctl.h" #include "mdp5_ctl.h"
#include "mdp5_smp.h" #include "mdp5_smp.h"
struct mdp5_state;
struct mdp5_kms { struct mdp5_kms {
struct mdp_kms base; struct mdp_kms base;
...@@ -49,11 +47,12 @@ struct mdp5_kms { ...@@ -49,11 +47,12 @@ struct mdp5_kms {
struct mdp5_cfg_handler *cfg; struct mdp5_cfg_handler *cfg;
uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */ uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */
/** /*
* Global atomic state. Do not access directly, use mdp5_get_state() * Global private object state, Do not access directly, use
* mdp5_global_get_state()
*/ */
struct mdp5_state *state; struct drm_modeset_lock glob_state_lock;
struct drm_modeset_lock state_lock; struct drm_private_obj glob_state;
struct mdp5_smp *smp; struct mdp5_smp *smp;
struct mdp5_ctl_manager *ctlm; struct mdp5_ctl_manager *ctlm;
...@@ -81,19 +80,23 @@ struct mdp5_kms { ...@@ -81,19 +80,23 @@ struct mdp5_kms {
}; };
#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base) #define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base)
/* Global atomic state for tracking resources that are shared across /* Global private object state for tracking resources that are shared across
* multiple kms objects (planes/crtcs/etc). * multiple kms objects (planes/crtcs/etc).
*
* For atomic updates which require modifying global state,
*/ */
struct mdp5_state { #define to_mdp5_global_state(x) container_of(x, struct mdp5_global_state, base)
struct mdp5_global_state {
struct drm_private_state base;
struct drm_atomic_state *state;
struct mdp5_kms *mdp5_kms;
struct mdp5_hw_pipe_state hwpipe; struct mdp5_hw_pipe_state hwpipe;
struct mdp5_hw_mixer_state hwmixer; struct mdp5_hw_mixer_state hwmixer;
struct mdp5_smp_state smp; struct mdp5_smp_state smp;
}; };
struct mdp5_state *__must_check struct mdp5_global_state * mdp5_get_existing_global_state(struct mdp5_kms *mdp5_kms);
mdp5_get_state(struct drm_atomic_state *s); struct mdp5_global_state *__must_check mdp5_get_global_state(struct drm_atomic_state *s);
/* Atomic plane state. Subclasses the base drm_plane_state in order to /* Atomic plane state. Subclasses the base drm_plane_state in order to
* track assigned hwpipe and hw specific state. * track assigned hwpipe and hw specific state.
......
...@@ -52,14 +52,14 @@ int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc, ...@@ -52,14 +52,14 @@ int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc,
{ {
struct msm_drm_private *priv = s->dev->dev_private; struct msm_drm_private *priv = s->dev->dev_private;
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
struct mdp5_state *state = mdp5_get_state(s); struct mdp5_global_state *global_state = mdp5_get_global_state(s);
struct mdp5_hw_mixer_state *new_state; struct mdp5_hw_mixer_state *new_state;
int i; int i;
if (IS_ERR(state)) if (IS_ERR(global_state))
return PTR_ERR(state); return PTR_ERR(global_state);
new_state = &state->hwmixer; new_state = &global_state->hwmixer;
for (i = 0; i < mdp5_kms->num_hwmixers; i++) { for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i]; struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i];
...@@ -129,8 +129,8 @@ int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc, ...@@ -129,8 +129,8 @@ int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc,
void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer) void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
{ {
struct mdp5_state *state = mdp5_get_state(s); struct mdp5_global_state *global_state = mdp5_get_global_state(s);
struct mdp5_hw_mixer_state *new_state = &state->hwmixer; struct mdp5_hw_mixer_state *new_state = &global_state->hwmixer;
if (!mixer) if (!mixer)
return; return;
......
...@@ -24,17 +24,19 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane, ...@@ -24,17 +24,19 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
{ {
struct msm_drm_private *priv = s->dev->dev_private; struct msm_drm_private *priv = s->dev->dev_private;
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
struct mdp5_state *state; struct mdp5_global_state *new_global_state, *old_global_state;
struct mdp5_hw_pipe_state *old_state, *new_state; struct mdp5_hw_pipe_state *old_state, *new_state;
int i, j; int i, j;
state = mdp5_get_state(s); new_global_state = mdp5_get_global_state(s);
if (IS_ERR(state)) if (IS_ERR(new_global_state))
return PTR_ERR(state); return PTR_ERR(new_global_state);
/* grab old_state after mdp5_get_state(), since now we hold lock: */ /* grab old_state after mdp5_get_global_state(), since now we hold lock: */
old_state = &mdp5_kms->state->hwpipe; old_global_state = mdp5_get_existing_global_state(mdp5_kms);
new_state = &state->hwpipe;
old_state = &old_global_state->hwpipe;
new_state = &new_global_state->hwpipe;
for (i = 0; i < mdp5_kms->num_hwpipes; i++) { for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i]; struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i];
...@@ -107,7 +109,7 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane, ...@@ -107,7 +109,7 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
WARN_ON(r_hwpipe); WARN_ON(r_hwpipe);
DBG("%s: alloc SMP blocks", (*hwpipe)->name); DBG("%s: alloc SMP blocks", (*hwpipe)->name);
ret = mdp5_smp_assign(mdp5_kms->smp, &state->smp, ret = mdp5_smp_assign(mdp5_kms->smp, &new_global_state->smp,
(*hwpipe)->pipe, blkcfg); (*hwpipe)->pipe, blkcfg);
if (ret) if (ret)
return -ENOMEM; return -ENOMEM;
...@@ -132,7 +134,7 @@ void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe) ...@@ -132,7 +134,7 @@ void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
{ {
struct msm_drm_private *priv = s->dev->dev_private; struct msm_drm_private *priv = s->dev->dev_private;
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
struct mdp5_state *state = mdp5_get_state(s); struct mdp5_global_state *state = mdp5_get_global_state(s);
struct mdp5_hw_pipe_state *new_state = &state->hwpipe; struct mdp5_hw_pipe_state *new_state = &state->hwpipe;
if (!hwpipe) if (!hwpipe)
......
...@@ -245,20 +245,6 @@ static const struct drm_plane_funcs mdp5_plane_funcs = { ...@@ -245,20 +245,6 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
.atomic_print_state = mdp5_plane_atomic_print_state, .atomic_print_state = mdp5_plane_atomic_print_state,
}; };
static int mdp5_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
struct mdp5_kms *mdp5_kms = get_kms(plane);
struct msm_kms *kms = &mdp5_kms->base.base;
struct drm_framebuffer *fb = new_state->fb;
if (!new_state->fb)
return 0;
DBG("%s: prepare: FB[%u]", plane->name, fb->base.id);
return msm_framebuffer_prepare(fb, kms->aspace);
}
static void mdp5_plane_cleanup_fb(struct drm_plane *plane, static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
{ {
...@@ -543,7 +529,7 @@ static void mdp5_plane_atomic_async_update(struct drm_plane *plane, ...@@ -543,7 +529,7 @@ static void mdp5_plane_atomic_async_update(struct drm_plane *plane,
} }
static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {
.prepare_fb = mdp5_plane_prepare_fb, .prepare_fb = msm_atomic_prepare_fb,
.cleanup_fb = mdp5_plane_cleanup_fb, .cleanup_fb = mdp5_plane_cleanup_fb,
.atomic_check = mdp5_plane_atomic_check, .atomic_check = mdp5_plane_atomic_check,
.atomic_update = mdp5_plane_atomic_update, .atomic_update = mdp5_plane_atomic_update,
......
...@@ -340,17 +340,20 @@ void mdp5_smp_dump(struct mdp5_smp *smp, struct drm_printer *p) ...@@ -340,17 +340,20 @@ void mdp5_smp_dump(struct mdp5_smp *smp, struct drm_printer *p)
struct mdp5_kms *mdp5_kms = get_kms(smp); struct mdp5_kms *mdp5_kms = get_kms(smp);
struct mdp5_hw_pipe_state *hwpstate; struct mdp5_hw_pipe_state *hwpstate;
struct mdp5_smp_state *state; struct mdp5_smp_state *state;
struct mdp5_global_state *global_state;
int total = 0, i, j; int total = 0, i, j;
drm_printf(p, "name\tinuse\tplane\n"); drm_printf(p, "name\tinuse\tplane\n");
drm_printf(p, "----\t-----\t-----\n"); drm_printf(p, "----\t-----\t-----\n");
if (drm_can_sleep()) if (drm_can_sleep())
drm_modeset_lock(&mdp5_kms->state_lock, NULL); drm_modeset_lock(&mdp5_kms->glob_state_lock, NULL);
global_state = mdp5_get_existing_global_state(mdp5_kms);
/* grab these *after* we hold the state_lock */ /* grab these *after* we hold the state_lock */
hwpstate = &mdp5_kms->state->hwpipe; hwpstate = &global_state->hwpipe;
state = &mdp5_kms->state->smp; state = &global_state->smp;
for (i = 0; i < mdp5_kms->num_hwpipes; i++) { for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
struct mdp5_hw_pipe *hwpipe = mdp5_kms->hwpipes[i]; struct mdp5_hw_pipe *hwpipe = mdp5_kms->hwpipes[i];
...@@ -374,7 +377,7 @@ void mdp5_smp_dump(struct mdp5_smp *smp, struct drm_printer *p) ...@@ -374,7 +377,7 @@ void mdp5_smp_dump(struct mdp5_smp *smp, struct drm_printer *p)
bitmap_weight(state->state, smp->blk_cnt)); bitmap_weight(state->state, smp->blk_cnt));
if (drm_can_sleep()) if (drm_can_sleep())
drm_modeset_unlock(&mdp5_kms->state_lock); drm_modeset_unlock(&mdp5_kms->glob_state_lock);
} }
void mdp5_smp_destroy(struct mdp5_smp *smp) void mdp5_smp_destroy(struct mdp5_smp *smp)
...@@ -384,7 +387,8 @@ void mdp5_smp_destroy(struct mdp5_smp *smp) ...@@ -384,7 +387,8 @@ void mdp5_smp_destroy(struct mdp5_smp *smp)
struct mdp5_smp *mdp5_smp_init(struct mdp5_kms *mdp5_kms, const struct mdp5_smp_block *cfg) struct mdp5_smp *mdp5_smp_init(struct mdp5_kms *mdp5_kms, const struct mdp5_smp_block *cfg)
{ {
struct mdp5_smp_state *state = &mdp5_kms->state->smp; struct mdp5_smp_state *state;
struct mdp5_global_state *global_state;
struct mdp5_smp *smp = NULL; struct mdp5_smp *smp = NULL;
int ret; int ret;
...@@ -398,6 +402,9 @@ struct mdp5_smp *mdp5_smp_init(struct mdp5_kms *mdp5_kms, const struct mdp5_smp_ ...@@ -398,6 +402,9 @@ struct mdp5_smp *mdp5_smp_init(struct mdp5_kms *mdp5_kms, const struct mdp5_smp_
smp->blk_cnt = cfg->mmb_count; smp->blk_cnt = cfg->mmb_count;
smp->blk_size = cfg->mmb_size; smp->blk_size = cfg->mmb_size;
global_state = mdp5_get_existing_global_state(mdp5_kms);
state = &global_state->smp;
/* statically tied MMBs cannot be re-allocated: */ /* statically tied MMBs cannot be re-allocated: */
bitmap_copy(state->state, cfg->reserved_state, smp->blk_cnt); bitmap_copy(state->state, cfg->reserved_state, smp->blk_cnt);
memcpy(smp->reserved, cfg->reserved, sizeof(smp->reserved)); memcpy(smp->reserved, cfg->reserved, sizeof(smp->reserved));
......
...@@ -1036,7 +1036,6 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size) ...@@ -1036,7 +1036,6 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size)
ret = msm_gem_get_iova(msm_host->tx_gem_obj, ret = msm_gem_get_iova(msm_host->tx_gem_obj,
priv->kms->aspace, &iova); priv->kms->aspace, &iova);
mutex_unlock(&dev->struct_mutex);
if (ret) { if (ret) {
pr_err("%s: failed to get iova, %d\n", __func__, ret); pr_err("%s: failed to get iova, %d\n", __func__, ret);
return ret; return ret;
...@@ -1067,9 +1066,20 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size) ...@@ -1067,9 +1066,20 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size)
static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) static void dsi_tx_buf_free(struct msm_dsi_host *msm_host)
{ {
struct drm_device *dev = msm_host->dev; struct drm_device *dev = msm_host->dev;
struct msm_drm_private *priv;
/*
* This is possible if we're tearing down before we've had a chance to
* fully initialize. A very real possibility if our probe is deferred,
* in which case we'll hit msm_dsi_host_destroy() without having run
* through the dsi_tx_buf_alloc().
*/
if (!dev)
return;
priv = dev->dev_private;
if (msm_host->tx_gem_obj) { if (msm_host->tx_gem_obj) {
msm_gem_put_iova(msm_host->tx_gem_obj, 0); msm_gem_put_iova(msm_host->tx_gem_obj, priv->kms->aspace);
drm_gem_object_put_unlocked(msm_host->tx_gem_obj); drm_gem_object_put_unlocked(msm_host->tx_gem_obj);
msm_host->tx_gem_obj = NULL; msm_host->tx_gem_obj = NULL;
} }
......
...@@ -16,69 +16,8 @@ ...@@ -16,69 +16,8 @@
*/ */
#include "msm_drv.h" #include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h" #include "msm_gem.h"
#include "msm_fence.h" #include "msm_kms.h"
struct msm_commit {
struct drm_device *dev;
struct drm_atomic_state *state;
struct work_struct work;
uint32_t crtc_mask;
};
static void commit_worker(struct work_struct *work);
/* block until specified crtcs are no longer pending update, and
* atomically mark them as pending update
*/
static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
int ret;
spin_lock(&priv->pending_crtcs_event.lock);
ret = wait_event_interruptible_locked(priv->pending_crtcs_event,
!(priv->pending_crtcs & crtc_mask));
if (ret == 0) {
DBG("start: %08x", crtc_mask);
priv->pending_crtcs |= crtc_mask;
}
spin_unlock(&priv->pending_crtcs_event.lock);
return ret;
}
/* clear specified crtcs (no longer pending update)
*/
static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
spin_lock(&priv->pending_crtcs_event.lock);
DBG("end: %08x", crtc_mask);
priv->pending_crtcs &= ~crtc_mask;
wake_up_all_locked(&priv->pending_crtcs_event);
spin_unlock(&priv->pending_crtcs_event.lock);
}
static struct msm_commit *commit_init(struct drm_atomic_state *state)
{
struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return NULL;
c->dev = state->dev;
c->state = state;
INIT_WORK(&c->work, commit_worker);
return c;
}
static void commit_destroy(struct msm_commit *c)
{
end_atomic(c->dev->dev_private, c->crtc_mask);
kfree(c);
}
static void msm_atomic_wait_for_commit_done(struct drm_device *dev, static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
struct drm_atomic_state *old_state) struct drm_atomic_state *old_state)
...@@ -97,195 +36,48 @@ static void msm_atomic_wait_for_commit_done(struct drm_device *dev, ...@@ -97,195 +36,48 @@ static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
} }
} }
/* The (potentially) asynchronous part of the commit. At this point int msm_atomic_prepare_fb(struct drm_plane *plane,
* nothing can fail short of armageddon. struct drm_plane_state *new_state)
*/
static void complete_commit(struct msm_commit *c, bool async)
{ {
struct drm_atomic_state *state = c->state; struct msm_drm_private *priv = plane->dev->dev_private;
struct drm_device *dev = state->dev;
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms; struct msm_kms *kms = priv->kms;
struct drm_gem_object *obj;
struct msm_gem_object *msm_obj;
struct dma_fence *fence;
drm_atomic_helper_wait_for_fences(dev, state, false); if (!new_state->fb)
return 0;
kms->funcs->prepare_commit(kms, state);
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_planes(dev, state, 0);
drm_atomic_helper_commit_modeset_enables(dev, state);
/* NOTE: _wait_for_vblanks() only waits for vblank on
* enabled CRTCs. So we end up faulting when disabling
* due to (potentially) unref'ing the outgoing fb's
* before the vblank when the disable has latched.
*
* But if it did wait on disabled (or newly disabled)
* CRTCs, that would be racy (ie. we could have missed
* the irq. We need some way to poll for pipe shut
* down. Or just live with occasionally hitting the
* timeout in the CRTC disable path (which really should
* not be critical path)
*/
msm_atomic_wait_for_commit_done(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
kms->funcs->complete_commit(kms, state);
drm_atomic_state_put(state); obj = msm_framebuffer_bo(new_state->fb, 0);
msm_obj = to_msm_bo(obj);
fence = reservation_object_get_excl_rcu(msm_obj->resv);
commit_destroy(c); drm_atomic_set_fence_for_plane(new_state, fence);
}
static void commit_worker(struct work_struct *work) return msm_framebuffer_prepare(new_state->fb, kms->aspace);
{
complete_commit(container_of(work, struct msm_commit, work), true);
} }
/** void msm_atomic_commit_tail(struct drm_atomic_state *state)
* drm_atomic_helper_commit - commit validated state object
* @dev: DRM device
* @state: the driver state object
* @nonblock: nonblocking commit
*
* This function commits a with drm_atomic_helper_check() pre-validated state
* object. This can still fail when e.g. the framebuffer reservation fails.
*
* RETURNS
* Zero for success or -errno.
*/
int msm_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool nonblock)
{ {
struct drm_device *dev = state->dev;
struct msm_drm_private *priv = dev->dev_private; struct msm_drm_private *priv = dev->dev_private;
struct msm_commit *c; struct msm_kms *kms = priv->kms;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
int i, ret;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/*
* Note that plane->atomic_async_check() should fail if we need
* to re-assign hwpipe or anything that touches global atomic
* state, so we'll never go down the async update path in those
* cases.
*/
if (state->async_update) {
drm_atomic_helper_async_commit(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
return 0;
}
c = commit_init(state);
if (!c) {
ret = -ENOMEM;
goto error;
}
/*
* Figure out what crtcs we have:
*/
for_each_new_crtc_in_state(state, crtc, crtc_state, i)
c->crtc_mask |= drm_crtc_mask(crtc);
/*
* Figure out what fence to wait for:
*/
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
if ((new_plane_state->fb != old_plane_state->fb) && new_plane_state->fb) {
struct drm_gem_object *obj = msm_framebuffer_bo(new_plane_state->fb, 0);
struct msm_gem_object *msm_obj = to_msm_bo(obj);
struct dma_fence *fence = reservation_object_get_excl_rcu(msm_obj->resv);
drm_atomic_set_fence_for_plane(new_plane_state, fence); kms->funcs->prepare_commit(kms, state);
}
}
/* drm_atomic_helper_commit_modeset_disables(dev, state);
* Wait for pending updates on any of the same crtc's and then
* mark our set of crtc's as busy:
*/
ret = start_atomic(dev->dev_private, c->crtc_mask);
if (ret)
goto err_free;
BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); drm_atomic_helper_commit_planes(dev, state, 0);
/* drm_atomic_helper_commit_modeset_enables(dev, state);
* This is the point of no return - everything below never fails except
* when the hw goes bonghits. Which means we can commit the new state on
* the software side now.
*
* swap driver private state while still holding state_lock
*/
if (to_kms_state(state)->state)
priv->kms->funcs->swap_state(priv->kms, state);
/* msm_atomic_wait_for_commit_done(dev, state);
* Everything below can be run asynchronously without the need to grab
* any modeset locks at all under one conditions: It must be guaranteed
* that the asynchronous work has either been cancelled (if the driver
* supports it, which at least requires that the framebuffers get
* cleaned up with drm_atomic_helper_cleanup_planes()) or completed
* before the new state gets committed on the software side with
* drm_atomic_helper_swap_state().
*
* This scheme allows new atomic state updates to be prepared and
* checked in parallel to the asynchronous completion of the previous
* update. Which is important since compositors need to figure out the
* composition of the next frame right after having submitted the
* current layout.
*/
drm_atomic_state_get(state); kms->funcs->complete_commit(kms, state);
if (nonblock) {
queue_work(priv->atomic_wq, &c->work);
return 0;
}
complete_commit(c, false); drm_atomic_helper_wait_for_vblanks(dev, state);
return 0; drm_atomic_helper_commit_hw_done(state);
err_free:
kfree(c);
error:
drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_cleanup_planes(dev, state);
return ret;
}
struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev)
{
struct msm_kms_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state || drm_atomic_state_init(dev, &state->base) < 0) {
kfree(state);
return NULL;
}
return &state->base;
}
void msm_atomic_state_clear(struct drm_atomic_state *s)
{
struct msm_kms_state *state = to_kms_state(s);
drm_atomic_state_default_clear(&state->base);
kfree(state->state);
state->state = NULL;
}
void msm_atomic_state_free(struct drm_atomic_state *state)
{
kfree(to_kms_state(state)->state);
drm_atomic_state_default_release(state);
kfree(state);
} }
...@@ -41,10 +41,11 @@ static const struct drm_mode_config_funcs mode_config_funcs = { ...@@ -41,10 +41,11 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
.fb_create = msm_framebuffer_create, .fb_create = msm_framebuffer_create,
.output_poll_changed = drm_fb_helper_output_poll_changed, .output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check, .atomic_check = drm_atomic_helper_check,
.atomic_commit = msm_atomic_commit, .atomic_commit = drm_atomic_helper_commit,
.atomic_state_alloc = msm_atomic_state_alloc, };
.atomic_state_clear = msm_atomic_state_clear,
.atomic_state_free = msm_atomic_state_free, static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
.atomic_commit_tail = msm_atomic_commit_tail,
}; };
#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING #ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
...@@ -384,7 +385,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) ...@@ -384,7 +385,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
priv->wq = alloc_ordered_workqueue("msm", 0); priv->wq = alloc_ordered_workqueue("msm", 0);
priv->atomic_wq = alloc_ordered_workqueue("msm:atomic", 0); priv->atomic_wq = alloc_ordered_workqueue("msm:atomic", 0);
init_waitqueue_head(&priv->pending_crtcs_event);
INIT_LIST_HEAD(&priv->inactive_list); INIT_LIST_HEAD(&priv->inactive_list);
INIT_LIST_HEAD(&priv->vblank_ctrl.event_list); INIT_LIST_HEAD(&priv->vblank_ctrl.event_list);
...@@ -442,6 +442,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) ...@@ -442,6 +442,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
} }
ddev->mode_config.funcs = &mode_config_funcs; ddev->mode_config.funcs = &mode_config_funcs;
ddev->mode_config.helper_private = &mode_config_helper_funcs;
ret = drm_vblank_init(ddev, priv->num_crtcs); ret = drm_vblank_init(ddev, priv->num_crtcs);
if (ret < 0) { if (ret < 0) {
......
...@@ -117,10 +117,6 @@ struct msm_drm_private { ...@@ -117,10 +117,6 @@ struct msm_drm_private {
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct workqueue_struct *atomic_wq; struct workqueue_struct *atomic_wq;
/* crtcs pending async atomic updates: */
uint32_t pending_crtcs;
wait_queue_head_t pending_crtcs_event;
unsigned int num_planes; unsigned int num_planes;
struct drm_plane *planes[16]; struct drm_plane *planes[16];
...@@ -160,8 +156,9 @@ struct msm_format { ...@@ -160,8 +156,9 @@ struct msm_format {
uint32_t pixel_format; uint32_t pixel_format;
}; };
int msm_atomic_commit(struct drm_device *dev, int msm_atomic_prepare_fb(struct drm_plane *plane,
struct drm_atomic_state *state, bool nonblock); struct drm_plane_state *new_state);
void msm_atomic_commit_tail(struct drm_atomic_state *state);
struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev); struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
void msm_atomic_state_clear(struct drm_atomic_state *state); void msm_atomic_state_clear(struct drm_atomic_state *state);
void msm_atomic_state_free(struct drm_atomic_state *state); void msm_atomic_state_free(struct drm_atomic_state *state);
......
...@@ -40,8 +40,6 @@ struct msm_kms_funcs { ...@@ -40,8 +40,6 @@ struct msm_kms_funcs {
irqreturn_t (*irq)(struct msm_kms *kms); irqreturn_t (*irq)(struct msm_kms *kms);
int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
/* swap global atomic state: */
void (*swap_state)(struct msm_kms *kms, struct drm_atomic_state *state);
/* modeset, bracketing atomic_commit(): */ /* modeset, bracketing atomic_commit(): */
void (*prepare_commit)(struct msm_kms *kms, struct drm_atomic_state *state); void (*prepare_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
void (*complete_commit)(struct msm_kms *kms, struct drm_atomic_state *state); void (*complete_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
...@@ -80,18 +78,6 @@ struct msm_kms { ...@@ -80,18 +78,6 @@ struct msm_kms {
struct msm_gem_address_space *aspace; struct msm_gem_address_space *aspace;
}; };
/**
* Subclass of drm_atomic_state, to allow kms backend to have driver
* private global state. The kms backend can do whatever it wants
* with the ->state ptr. On ->atomic_state_clear() the ->state ptr
* is kfree'd and set back to NULL.
*/
struct msm_kms_state {
struct drm_atomic_state base;
void *state;
};
#define to_kms_state(x) container_of(x, struct msm_kms_state, base)
static inline void msm_kms_init(struct msm_kms *kms, static inline void msm_kms_init(struct msm_kms *kms,
const struct msm_kms_funcs *funcs) const struct msm_kms_funcs *funcs)
{ {
......
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