diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_kms.h')
| -rw-r--r-- | drivers/gpu/drm/msm/msm_kms.h | 230 |
1 files changed, 185 insertions, 45 deletions
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index a8f2ba5e5f07..8a7be7b854de 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -1,18 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MSM_KMS_H__ @@ -23,6 +13,8 @@ #include "msm_drv.h" +#ifdef CONFIG_DRM_MSM_KMS + #define MAX_PLANE 4 /* As there are different display controller blocks depending on the @@ -40,64 +32,212 @@ struct msm_kms_funcs { irqreturn_t (*irq)(struct msm_kms *kms); int (*enable_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(): */ + + /* + * Atomic commit handling: + * + * Note that in the case of async commits, the funcs which take + * a crtc_mask (ie. ->flush_commit(), and ->complete_commit()) + * might not be evenly balanced with ->prepare_commit(), however + * each crtc that effected by a ->prepare_commit() (potentially + * multiple times) will eventually (at end of vsync period) be + * flushed and completed. + * + * This has some implications about tracking of cleanup state, + * for example SMP blocks to release after commit completes. Ie. + * cleanup state should be also duplicated in the various + * duplicate_state() methods, as the current cleanup state at + * ->complete_commit() time may have accumulated cleanup work + * from multiple commits. + */ + + /** + * Enable/disable power/clks needed for hw access done in other + * commit related methods. + * + * If mdp4 is migrated to runpm, we could probably drop these + * and use runpm directly. + */ + void (*enable_commit)(struct msm_kms *kms); + void (*disable_commit)(struct msm_kms *kms); + + /** + * @check_mode_changed: + * + * Verify if the commit requires a full modeset on one of CRTCs. + */ + int (*check_mode_changed)(struct msm_kms *kms, struct drm_atomic_state *state); + + /** + * Prepare for atomic commit. This is called after any previous + * (async or otherwise) commit has completed. + */ void (*prepare_commit)(struct msm_kms *kms, struct drm_atomic_state *state); - void (*complete_commit)(struct msm_kms *kms, struct drm_atomic_state *state); - /* functions to wait for atomic commit completed on each CRTC */ - void (*wait_for_crtc_commit_done)(struct msm_kms *kms, - struct drm_crtc *crtc); + + /** + * Flush an atomic commit. This is called after the hardware + * updates have already been pushed down to effected planes/ + * crtcs/encoders/connectors. + */ + void (*flush_commit)(struct msm_kms *kms, unsigned crtc_mask); + + /** + * Wait for any in-progress flush to complete on the specified + * crtcs. This should not block if there is no in-progress + * commit (ie. don't just wait for a vblank), as it will also + * be called before ->prepare_commit() to ensure any potential + * "async" commit has completed. + */ + void (*wait_flush)(struct msm_kms *kms, unsigned crtc_mask); + + /** + * Clean up after commit is completed. This is called after + * ->wait_flush(), to give the backend a chance to do any + * post-commit cleanup. + */ + void (*complete_commit)(struct msm_kms *kms, unsigned crtc_mask); + + /* + * Format handling: + */ + /* misc: */ - const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format); long (*round_pixclk)(struct msm_kms *kms, unsigned long rate, struct drm_encoder *encoder); - int (*set_split_display)(struct msm_kms *kms, - struct drm_encoder *encoder, - struct drm_encoder *slave_encoder, - bool is_cmd_mode); - void (*set_encoder_mode)(struct msm_kms *kms, - struct drm_encoder *encoder, - bool cmd_mode); /* cleanup: */ void (*destroy)(struct msm_kms *kms); + + /* snapshot: */ + void (*snapshot)(struct msm_disp_state *disp_state, struct msm_kms *kms); + #ifdef CONFIG_DEBUG_FS /* debugfs: */ int (*debugfs_init)(struct msm_kms *kms, struct drm_minor *minor); #endif }; +struct msm_kms; + +/* + * A per-crtc timer for pending async atomic flushes. Scheduled to expire + * shortly before vblank to flush pending async updates. + */ +struct msm_pending_timer { + struct msm_hrtimer_work work; + struct kthread_worker *worker; + struct msm_kms *kms; + unsigned crtc_idx; +}; + +/* Commit/Event thread specific structure */ +struct msm_drm_thread { + struct drm_device *dev; + struct kthread_worker *worker; +}; + struct msm_kms { const struct msm_kms_funcs *funcs; + struct drm_device *dev; + + struct hdmi *hdmi; + + struct msm_dsi *dsi[MSM_DSI_CONTROLLER_COUNT]; - /* irq number to be passed on to drm_irq_install */ + struct msm_dp *dp[MSM_DP_CONTROLLER_COUNT]; + + /* irq number to be passed on to msm_irq_install */ int irq; + bool irq_requested; + + /* rate limit the snapshot capture to once per attach */ + atomic_t fault_snapshot_capture; /* mapper-id used to request GEM buffer mapped for scanout: */ - struct msm_gem_address_space *aspace; -}; + struct drm_gpuvm *vm; -/** - * 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; + /* disp snapshot support */ + struct kthread_worker *dump_worker; + struct kthread_work dump_work; + struct mutex dump_mutex; + + /* + * For async commit, where ->flush_commit() and later happens + * from the crtc's pending_timer close to end of the frame: + */ + struct mutex commit_lock[MAX_CRTCS]; + unsigned pending_crtc_mask; + struct msm_pending_timer pending_timers[MAX_CRTCS]; + + struct workqueue_struct *wq; + struct msm_drm_thread event_thread[MAX_CRTCS]; }; -#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 int msm_kms_init(struct msm_kms *kms, const struct msm_kms_funcs *funcs) { + unsigned i, ret; + + for (i = 0; i < ARRAY_SIZE(kms->commit_lock); i++) + mutex_init(&kms->commit_lock[i]); + kms->funcs = funcs; + + kms->wq = alloc_ordered_workqueue("msm", 0); + if (!kms->wq) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++) { + ret = msm_atomic_init_pending_timer(&kms->pending_timers[i], kms, i); + if (ret) { + return ret; + } + } + + return 0; +} + +static inline void msm_kms_destroy(struct msm_kms *kms) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++) + msm_atomic_destroy_pending_timer(&kms->pending_timers[i]); + + destroy_workqueue(kms->wq); } -struct msm_kms *mdp4_kms_init(struct drm_device *dev); -struct msm_kms *mdp5_kms_init(struct drm_device *dev); -int msm_mdss_init(struct drm_device *dev); -void msm_mdss_destroy(struct drm_device *dev); +#define for_each_crtc_mask(dev, crtc, crtc_mask) \ + drm_for_each_crtc(crtc, dev) \ + for_each_if (drm_crtc_mask(crtc) & (crtc_mask)) + +#define for_each_crtc_mask_reverse(dev, crtc, crtc_mask) \ + drm_for_each_crtc_reverse(crtc, dev) \ + for_each_if (drm_crtc_mask(crtc) & (crtc_mask)) + +int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv); +void msm_drm_kms_post_init(struct device *dev); +void msm_drm_kms_unregister(struct device *dev); +void msm_drm_kms_uninit(struct device *dev); + +#else /* ! CONFIG_DRM_MSM_KMS */ + +static inline int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv) +{ + return -ENODEV; +} + +static inline void msm_drm_kms_post_init(struct device *dev) +{ +} + +static inline void msm_drm_kms_unregister(struct device *dev) +{ +} + +static inline void msm_drm_kms_uninit(struct device *dev) +{ +} + +#endif #endif /* __MSM_KMS_H__ */ |
