Loading drivers/gpu/drm/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ msm-y := \ sde/sde_encoder_phys_vid.o \ sde/sde_encoder_phys_cmd.o \ sde/sde_irq.o \ sde/sde_kms_utils.o \ sde/sde_kms.o \ sde/sde_plane.o \ msm_atomic.o \ Loading drivers/gpu/drm/msm/sde/sde_crtc.c +174 −108 Original line number Diff line number Diff line Loading @@ -19,40 +19,11 @@ #include "sde_kms.h" #include "sde_hw_lm.h" #include "sde_hw_mdp_ctl.h" #include "sde_crtc.h" #define CRTC_DUAL_MIXERS 2 #define PENDING_FLIP 2 #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; u32 flush_mask; }; struct sde_crtc { struct drm_crtc base; char name[8]; struct drm_plane *plane; struct drm_plane *planes[8]; struct drm_encoder *encoder; int id; bool enabled; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) #define CTL(i) (CTL_0 + (i)) #define LM(i) (LM_0 + (i)) #define INTF(i) (INTF_0 + (i)) static struct sde_kms *get_kms(struct drm_crtc *crtc) { Loading @@ -60,89 +31,91 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static inline struct sde_hw_ctl *sde_crtc_rm_get_ctl_path(enum sde_ctl idx, void __iomem *addr, struct sde_mdss_cfg *m) { /* * This module keeps track of the requested hw resources state, * if the requested resource is being used it returns NULL, * otherwise it returns the hw driver struct */ return sde_hw_ctl_init(idx, addr, m); } static inline struct sde_hw_mixer *sde_crtc_rm_get_mixer(enum sde_lm idx, void __iomem *addr, struct sde_mdss_cfg *m) { /* * This module keeps track of the requested hw resources state, * if the requested resource is being used it returns NULL, * otherwise it returns the hw driver struct */ return sde_hw_lm_init(idx, addr, m); } static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, struct drm_encoder *encoder) { /* * Assign CRTC resources * num_ctls; * num_mixers; * sde_lm mixer[CRTC_MAX_PIPES]; * sde_ctl ctl[CRTC_MAX_PIPES]; */ struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_kms *kms = get_kms(crtc); enum sde_lm lm_id[CRTC_DUAL_MIXERS]; enum sde_ctl ctl_id[CRTC_DUAL_MIXERS]; int i; if (!kms) { DBG("[%s] invalid kms\n", __func__); struct sde_kms *sde_kms = get_kms(crtc); struct sde_encoder_hw_resources enc_hw_res; const struct sde_hw_res_map *plat_hw_res_map; enum sde_lm unused_lm_id[CRTC_DUAL_MIXERS] = {0}; enum sde_lm lm_idx; int i, count = 0; if (!sde_kms) { DBG("[%s] invalid kms", __func__); return -EINVAL; } if (!kms->mmio) if (!sde_kms->mmio) return -EINVAL; /* * simple check validate against catalog */ sde_crtc->num_ctls = 1; sde_crtc->num_mixers = 1; ctl_id[0] = CTL_0; lm_id[0] = LM_0; /* * need to also enable MDP core clock and AHB CLK * before touching HW driver */ DBG("%s Enable clocks\n", __func__); sde_enable(kms); for (i = 0; i < sde_crtc->num_ctls; i++) { sde_crtc->mixer[i].hw_ctl = sde_crtc_rm_get_ctl_path(ctl_id[i], kms->mmio, kms->catalog); if (!sde_crtc->mixer[i].hw_ctl) { DBG("[%s], Invalid ctl_path", __func__); return -EACCES; /* Get unused LMs */ for (i = 0; i < sde_kms->catalog->mixer_count; i++) { if (!sde_rm_get_mixer(sde_kms, LM(i))) { unused_lm_id[count++] = LM(i); if (count == CRTC_DUAL_MIXERS) break; } } for (i = 0; i < sde_crtc->num_mixers; i++) { sde_crtc->mixer[i].hw_lm = sde_crtc_rm_get_mixer(lm_id[i], kms->mmio, kms->catalog); if (!sde_crtc->mixer[i].hw_lm) { /* query encoder resources */ sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res); /* parse encoder hw resources, find CTL paths */ for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) { WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS); if (enc_hw_res.ctls[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_ctls]; mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i); if (IS_ERR_OR_NULL(mixer->hw_ctl)) { DBG("[%s], Invalid ctl_path", __func__); return -EACCES; } sde_crtc->num_ctls++; } } /* shortcut this process if encoder has no ctl paths */ if (!sde_crtc->num_ctls) return 0; /* * need to disable MDP core clock and AHB CLK * Get default LMs if specified in platform config * other wise acquire the free LMs */ sde_disable(kms); for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) { if (enc_hw_res.intfs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, i); lm_idx = plat_hw_res_map->lm; if (!lm_idx) lm_idx = unused_lm_id[sde_crtc->num_mixers]; DBG("Acquiring LM %d", lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DBG("[%s], Invalid mixer", __func__); return -EACCES; } /* interface info */ mixer->intf_idx = i; mixer->mode = enc_hw_res.intfs[i]; sde_crtc->num_mixers++; } } DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ", sde_crtc->num_ctls, sde_crtc->num_mixers, sde_crtc->mixer[0].hw_lm->idx, sde_crtc->mixer[0].hw_ctl->idx); if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) DBG("lm[1] %d, ctl[1], %d", sde_crtc->mixer[1].hw_lm->idx, sde_crtc->mixer[1].hw_ctl->idx); return 0; } Loading Loading @@ -278,6 +251,7 @@ static void blend_setup(struct drm_crtc *crtc) unsigned long flags; int i, j, plane_cnt = 0; DBG(""); spin_lock_irqsave(&sde_crtc->lm_lock, flags); /* ctl could be reserved already */ Loading Loading @@ -353,10 +327,104 @@ out: spin_unlock_irqrestore(&sde_crtc->lm_lock, flags); } /* if file!=NULL, this is preclose potential cancel-flip path */ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_device *dev = crtc->dev; struct drm_pending_vblank_event *event; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); event = sde_crtc->event; if (event) { /* if regular vblank case (!file) or if cancel-flip from * preclose on file that requested flip, then send the * event: */ if (!file || (event->base.file_priv == file)) { sde_crtc->event = NULL; DBG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->id, event); } } spin_unlock_irqrestore(&dev->event_lock, flags); } static void sde_crtc_vblank_cb(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); unsigned pending; /* unregister callback */ sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL); pending = atomic_xchg(&sde_crtc->pending, 0); if (pending & PENDING_FLIP) complete_flip(crtc, NULL); } static int frame_flushed(struct sde_crtc *sde_crtc) { struct vsync_info vsync; /* encoder get vsync_info */ /* if frame_count does not match frame is flushed */ sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync); return (vsync.frame_count & sde_crtc->vsync_count); } void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); u32 pending; int i, ret; /* ref count the vblank event */ ret = drm_crtc_vblank_get(crtc); if (ret) return; /* register callback */ sde_encoder_register_vblank_callback(sde_crtc->encoder, sde_crtc_vblank_cb, (void *)crtc); /* wait */ pending = atomic_read(&sde_crtc->pending); if (pending & PENDING_FLIP) { wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue, (frame_flushed(sde_crtc) != 0), msecs_to_jiffies(CRTC_MAX_WAIT_ONE_FRAME)); if (ret <= 0) dev_warn(dev->dev, "vblank time out, crtc=%d\n", sde_crtc->id); } for (i = 0; i < sde_crtc->num_ctls; i++) sde_crtc->mixer[i].flush_mask = 0; /* release */ drm_crtc_vblank_put(crtc); } static void request_pending(struct drm_crtc *crtc, u32 pending) { DBG(""); struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct vsync_info vsync; /* request vsync info, cache the current frame count */ sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync); sde_crtc->vsync_count = vsync.frame_count; atomic_or(pending, &sde_crtc->pending); } /** * Flush the CTL PATH */ Loading @@ -369,14 +437,12 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) DBG(""); for (i = 0; i < sde_crtc->num_ctls; i++) { /* * Query flush_mask from encoder * and append to the ctl_path flush_mask */ ctl = sde_crtc->mixer[i].hw_ctl; ctl->ops.get_bitmask_intf(ctl, &(sde_crtc->mixer[i].flush_mask), INTF_1); sde_crtc->mixer[i].intf_idx); DBG("Flushing CTL_ID %d, flush_mask %x", ctl->idx, sde_crtc->mixer[i].flush_mask); ctl->ops.setup_flush(ctl, sde_crtc->mixer[i].flush_mask); } Loading Loading @@ -425,7 +491,7 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; unsigned long flags; DBG(""); DBG("%s: event: %pK", sde_crtc->name, crtc->state->event); WARN_ON(sde_crtc->event); Loading Loading @@ -605,6 +671,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, return ERR_PTR(-EINVAL); } DBG("%s: Successfully initialized crtc\n", __func__); DBG("%s: Successfully initialized crtc", __func__); return crtc; } drivers/gpu/drm/msm/sde/sde_crtc.h 0 → 100644 +79 −0 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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. */ #ifndef _SDE_CRTC_H_ #define _SDE_CRTC_H_ #include "drm_crtc.h" #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define CRTC_DUAL_MIXERS 2 #define PENDING_FLIP 2 /* worst case one frame wait time based on 30 FPS : 33.33ms*/ #define CRTC_MAX_WAIT_ONE_FRAME 34 #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) /** * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC * @hw_dspp : DSPP HW Driver context * @hw_lm : LM HW Driver context * @hw_ctl : CTL Path HW driver context * @intf_idx : Interface idx * @mode : Interface mode Active/CMD * @flush_mask : Flush mask value for this commit */ struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; enum sde_intf intf_idx; enum sde_intf_mode mode; u32 flush_mask; }; /** * struct sde_crtc - virtualized CRTC data structure * @base : Base drm crtc structure * @name : ASCII description of this crtc * @encoder : Associated drm encoder object * @id : Unique crtc identifier * @lm_lock : LM register access spinlock * @num_ctls : Number of ctl paths in use * @num_mixers : Number of mixers in use * @mixer : List of active mixers * @event : Pointer to last received drm vblank event * @pending : Whether or not an update is pending * @vsync_count : Running count of received vsync events */ struct sde_crtc { struct drm_crtc base; char name[8]; struct drm_encoder *encoder; int id; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; atomic_t pending; u32 vsync_count; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) #endif /* _SDE_CRTC_H_ */ drivers/gpu/drm/msm/sde/sde_encoder.c +54 −8 Original line number Diff line number Diff line Loading @@ -201,6 +201,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, { struct sde_encoder_virt *sde_enc = NULL; int i = 0; bool splitmode = false; DBG(""); Loading @@ -211,11 +212,23 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, sde_enc = to_sde_encoder_virt(drm_enc); /* * Panel is driven by two interfaces ,each interface drives half of * the horizontal */ if (sde_enc->num_phys_encs == 2) splitmode = true; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys && phys->phys_ops.mode_set) phys->phys_ops.mode_set(phys, mode, adjusted_mode); if (phys) { phys->phys_ops.mode_set(phys, mode, adjusted_mode, splitmode); if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0) DRM_ERROR("adjusted modes not supported\n"); } } } Loading @@ -223,6 +236,7 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; int i = 0; bool splitmode = false; DBG(""); Loading @@ -235,10 +249,19 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) bs_set(sde_enc, 1); if (sde_enc->num_phys_encs == 2) splitmode = true; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys && phys->phys_ops.enable) /* enable/disable dual interface top config */ if (phys->phys_ops.enable_split_config) phys->phys_ops.enable_split_config(phys, splitmode); phys->phys_ops.enable(phys); } } Loading Loading @@ -380,13 +403,11 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, * h_tile_instance_ids[2] = {0, 1}; DSI0 = left, DSI1 = right * h_tile_instance_ids[2] = {1, 0}; DSI1 = left, DSI0 = right */ const struct sde_hw_res_map *hw_res_map = NULL; enum sde_intf intf_idx = INTF_MAX; enum sde_ctl ctl_idx = CTL_0; enum sde_ctl ctl_idx = CTL_MAX; u32 controller_id = disp_info->h_tile_instance[i]; if (intf_type == INTF_HDMI) ctl_idx = CTL_2; DBG("h_tile_instance %d = %d", i, controller_id); intf_idx = sde_encoder_get_intf(sde_kms->catalog, Loading @@ -396,6 +417,12 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, ret = -EINVAL; } hw_res_map = sde_rm_get_res_map(sde_kms, intf_idx); if (IS_ERR_OR_NULL(hw_res_map)) ret = -EINVAL; else ctl_idx = hw_res_map->ctl; /* Create both VID and CMD Phys Encoders here */ if (!ret) ret = sde_encoder_virt_add_phys_vid_enc( Loading Loading @@ -461,6 +488,25 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); } void sde_encoder_get_vsync_info(struct drm_encoder *drm_enc, struct vsync_info *vsync) { struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); struct sde_encoder_phys *phys; DBG(""); if (!vsync) { DRM_ERROR("Invalid pointer"); return; } /* we get the vsync info from the intf at index 0: master index */ phys = sde_enc->phys_encs[0]; if (phys) phys->phys_ops.get_vsync_info(phys, vsync); } /* encoders init, * initialize encoder based on displays */ Loading drivers/gpu/drm/msm/sde/sde_encoder_phys.h +6 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,8 @@ struct sde_encoder_virt_ops { struct sde_encoder_phys_ops { void (*mode_set)(struct sde_encoder_phys *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode, bool splitmode); bool (*mode_fixup)(struct sde_encoder_phys *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); Loading @@ -39,6 +40,10 @@ struct sde_encoder_phys_ops { void (*destroy)(struct sde_encoder_phys *encoder); void (*get_hw_resources)(struct sde_encoder_phys *encoder, struct sde_encoder_hw_resources *hw_res); void (*get_vsync_info)(struct sde_encoder_phys *enc, struct vsync_info *vsync); void (*enable_split_config)(struct sde_encoder_phys *enc, bool enable); }; struct sde_encoder_phys { Loading Loading
drivers/gpu/drm/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ msm-y := \ sde/sde_encoder_phys_vid.o \ sde/sde_encoder_phys_cmd.o \ sde/sde_irq.o \ sde/sde_kms_utils.o \ sde/sde_kms.o \ sde/sde_plane.o \ msm_atomic.o \ Loading
drivers/gpu/drm/msm/sde/sde_crtc.c +174 −108 Original line number Diff line number Diff line Loading @@ -19,40 +19,11 @@ #include "sde_kms.h" #include "sde_hw_lm.h" #include "sde_hw_mdp_ctl.h" #include "sde_crtc.h" #define CRTC_DUAL_MIXERS 2 #define PENDING_FLIP 2 #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; u32 flush_mask; }; struct sde_crtc { struct drm_crtc base; char name[8]; struct drm_plane *plane; struct drm_plane *planes[8]; struct drm_encoder *encoder; int id; bool enabled; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) #define CTL(i) (CTL_0 + (i)) #define LM(i) (LM_0 + (i)) #define INTF(i) (INTF_0 + (i)) static struct sde_kms *get_kms(struct drm_crtc *crtc) { Loading @@ -60,89 +31,91 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static inline struct sde_hw_ctl *sde_crtc_rm_get_ctl_path(enum sde_ctl idx, void __iomem *addr, struct sde_mdss_cfg *m) { /* * This module keeps track of the requested hw resources state, * if the requested resource is being used it returns NULL, * otherwise it returns the hw driver struct */ return sde_hw_ctl_init(idx, addr, m); } static inline struct sde_hw_mixer *sde_crtc_rm_get_mixer(enum sde_lm idx, void __iomem *addr, struct sde_mdss_cfg *m) { /* * This module keeps track of the requested hw resources state, * if the requested resource is being used it returns NULL, * otherwise it returns the hw driver struct */ return sde_hw_lm_init(idx, addr, m); } static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, struct drm_encoder *encoder) { /* * Assign CRTC resources * num_ctls; * num_mixers; * sde_lm mixer[CRTC_MAX_PIPES]; * sde_ctl ctl[CRTC_MAX_PIPES]; */ struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_kms *kms = get_kms(crtc); enum sde_lm lm_id[CRTC_DUAL_MIXERS]; enum sde_ctl ctl_id[CRTC_DUAL_MIXERS]; int i; if (!kms) { DBG("[%s] invalid kms\n", __func__); struct sde_kms *sde_kms = get_kms(crtc); struct sde_encoder_hw_resources enc_hw_res; const struct sde_hw_res_map *plat_hw_res_map; enum sde_lm unused_lm_id[CRTC_DUAL_MIXERS] = {0}; enum sde_lm lm_idx; int i, count = 0; if (!sde_kms) { DBG("[%s] invalid kms", __func__); return -EINVAL; } if (!kms->mmio) if (!sde_kms->mmio) return -EINVAL; /* * simple check validate against catalog */ sde_crtc->num_ctls = 1; sde_crtc->num_mixers = 1; ctl_id[0] = CTL_0; lm_id[0] = LM_0; /* * need to also enable MDP core clock and AHB CLK * before touching HW driver */ DBG("%s Enable clocks\n", __func__); sde_enable(kms); for (i = 0; i < sde_crtc->num_ctls; i++) { sde_crtc->mixer[i].hw_ctl = sde_crtc_rm_get_ctl_path(ctl_id[i], kms->mmio, kms->catalog); if (!sde_crtc->mixer[i].hw_ctl) { DBG("[%s], Invalid ctl_path", __func__); return -EACCES; /* Get unused LMs */ for (i = 0; i < sde_kms->catalog->mixer_count; i++) { if (!sde_rm_get_mixer(sde_kms, LM(i))) { unused_lm_id[count++] = LM(i); if (count == CRTC_DUAL_MIXERS) break; } } for (i = 0; i < sde_crtc->num_mixers; i++) { sde_crtc->mixer[i].hw_lm = sde_crtc_rm_get_mixer(lm_id[i], kms->mmio, kms->catalog); if (!sde_crtc->mixer[i].hw_lm) { /* query encoder resources */ sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res); /* parse encoder hw resources, find CTL paths */ for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) { WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS); if (enc_hw_res.ctls[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_ctls]; mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i); if (IS_ERR_OR_NULL(mixer->hw_ctl)) { DBG("[%s], Invalid ctl_path", __func__); return -EACCES; } sde_crtc->num_ctls++; } } /* shortcut this process if encoder has no ctl paths */ if (!sde_crtc->num_ctls) return 0; /* * need to disable MDP core clock and AHB CLK * Get default LMs if specified in platform config * other wise acquire the free LMs */ sde_disable(kms); for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) { if (enc_hw_res.intfs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, i); lm_idx = plat_hw_res_map->lm; if (!lm_idx) lm_idx = unused_lm_id[sde_crtc->num_mixers]; DBG("Acquiring LM %d", lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DBG("[%s], Invalid mixer", __func__); return -EACCES; } /* interface info */ mixer->intf_idx = i; mixer->mode = enc_hw_res.intfs[i]; sde_crtc->num_mixers++; } } DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ", sde_crtc->num_ctls, sde_crtc->num_mixers, sde_crtc->mixer[0].hw_lm->idx, sde_crtc->mixer[0].hw_ctl->idx); if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) DBG("lm[1] %d, ctl[1], %d", sde_crtc->mixer[1].hw_lm->idx, sde_crtc->mixer[1].hw_ctl->idx); return 0; } Loading Loading @@ -278,6 +251,7 @@ static void blend_setup(struct drm_crtc *crtc) unsigned long flags; int i, j, plane_cnt = 0; DBG(""); spin_lock_irqsave(&sde_crtc->lm_lock, flags); /* ctl could be reserved already */ Loading Loading @@ -353,10 +327,104 @@ out: spin_unlock_irqrestore(&sde_crtc->lm_lock, flags); } /* if file!=NULL, this is preclose potential cancel-flip path */ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_device *dev = crtc->dev; struct drm_pending_vblank_event *event; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); event = sde_crtc->event; if (event) { /* if regular vblank case (!file) or if cancel-flip from * preclose on file that requested flip, then send the * event: */ if (!file || (event->base.file_priv == file)) { sde_crtc->event = NULL; DBG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->id, event); } } spin_unlock_irqrestore(&dev->event_lock, flags); } static void sde_crtc_vblank_cb(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); unsigned pending; /* unregister callback */ sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL); pending = atomic_xchg(&sde_crtc->pending, 0); if (pending & PENDING_FLIP) complete_flip(crtc, NULL); } static int frame_flushed(struct sde_crtc *sde_crtc) { struct vsync_info vsync; /* encoder get vsync_info */ /* if frame_count does not match frame is flushed */ sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync); return (vsync.frame_count & sde_crtc->vsync_count); } void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); u32 pending; int i, ret; /* ref count the vblank event */ ret = drm_crtc_vblank_get(crtc); if (ret) return; /* register callback */ sde_encoder_register_vblank_callback(sde_crtc->encoder, sde_crtc_vblank_cb, (void *)crtc); /* wait */ pending = atomic_read(&sde_crtc->pending); if (pending & PENDING_FLIP) { wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue, (frame_flushed(sde_crtc) != 0), msecs_to_jiffies(CRTC_MAX_WAIT_ONE_FRAME)); if (ret <= 0) dev_warn(dev->dev, "vblank time out, crtc=%d\n", sde_crtc->id); } for (i = 0; i < sde_crtc->num_ctls; i++) sde_crtc->mixer[i].flush_mask = 0; /* release */ drm_crtc_vblank_put(crtc); } static void request_pending(struct drm_crtc *crtc, u32 pending) { DBG(""); struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct vsync_info vsync; /* request vsync info, cache the current frame count */ sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync); sde_crtc->vsync_count = vsync.frame_count; atomic_or(pending, &sde_crtc->pending); } /** * Flush the CTL PATH */ Loading @@ -369,14 +437,12 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) DBG(""); for (i = 0; i < sde_crtc->num_ctls; i++) { /* * Query flush_mask from encoder * and append to the ctl_path flush_mask */ ctl = sde_crtc->mixer[i].hw_ctl; ctl->ops.get_bitmask_intf(ctl, &(sde_crtc->mixer[i].flush_mask), INTF_1); sde_crtc->mixer[i].intf_idx); DBG("Flushing CTL_ID %d, flush_mask %x", ctl->idx, sde_crtc->mixer[i].flush_mask); ctl->ops.setup_flush(ctl, sde_crtc->mixer[i].flush_mask); } Loading Loading @@ -425,7 +491,7 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; unsigned long flags; DBG(""); DBG("%s: event: %pK", sde_crtc->name, crtc->state->event); WARN_ON(sde_crtc->event); Loading Loading @@ -605,6 +671,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, return ERR_PTR(-EINVAL); } DBG("%s: Successfully initialized crtc\n", __func__); DBG("%s: Successfully initialized crtc", __func__); return crtc; }
drivers/gpu/drm/msm/sde/sde_crtc.h 0 → 100644 +79 −0 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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. */ #ifndef _SDE_CRTC_H_ #define _SDE_CRTC_H_ #include "drm_crtc.h" #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define CRTC_DUAL_MIXERS 2 #define PENDING_FLIP 2 /* worst case one frame wait time based on 30 FPS : 33.33ms*/ #define CRTC_MAX_WAIT_ONE_FRAME 34 #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) /** * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC * @hw_dspp : DSPP HW Driver context * @hw_lm : LM HW Driver context * @hw_ctl : CTL Path HW driver context * @intf_idx : Interface idx * @mode : Interface mode Active/CMD * @flush_mask : Flush mask value for this commit */ struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; enum sde_intf intf_idx; enum sde_intf_mode mode; u32 flush_mask; }; /** * struct sde_crtc - virtualized CRTC data structure * @base : Base drm crtc structure * @name : ASCII description of this crtc * @encoder : Associated drm encoder object * @id : Unique crtc identifier * @lm_lock : LM register access spinlock * @num_ctls : Number of ctl paths in use * @num_mixers : Number of mixers in use * @mixer : List of active mixers * @event : Pointer to last received drm vblank event * @pending : Whether or not an update is pending * @vsync_count : Running count of received vsync events */ struct sde_crtc { struct drm_crtc base; char name[8]; struct drm_encoder *encoder; int id; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; atomic_t pending; u32 vsync_count; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) #endif /* _SDE_CRTC_H_ */
drivers/gpu/drm/msm/sde/sde_encoder.c +54 −8 Original line number Diff line number Diff line Loading @@ -201,6 +201,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, { struct sde_encoder_virt *sde_enc = NULL; int i = 0; bool splitmode = false; DBG(""); Loading @@ -211,11 +212,23 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, sde_enc = to_sde_encoder_virt(drm_enc); /* * Panel is driven by two interfaces ,each interface drives half of * the horizontal */ if (sde_enc->num_phys_encs == 2) splitmode = true; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys && phys->phys_ops.mode_set) phys->phys_ops.mode_set(phys, mode, adjusted_mode); if (phys) { phys->phys_ops.mode_set(phys, mode, adjusted_mode, splitmode); if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0) DRM_ERROR("adjusted modes not supported\n"); } } } Loading @@ -223,6 +236,7 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; int i = 0; bool splitmode = false; DBG(""); Loading @@ -235,10 +249,19 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) bs_set(sde_enc, 1); if (sde_enc->num_phys_encs == 2) splitmode = true; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys && phys->phys_ops.enable) /* enable/disable dual interface top config */ if (phys->phys_ops.enable_split_config) phys->phys_ops.enable_split_config(phys, splitmode); phys->phys_ops.enable(phys); } } Loading Loading @@ -380,13 +403,11 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, * h_tile_instance_ids[2] = {0, 1}; DSI0 = left, DSI1 = right * h_tile_instance_ids[2] = {1, 0}; DSI1 = left, DSI0 = right */ const struct sde_hw_res_map *hw_res_map = NULL; enum sde_intf intf_idx = INTF_MAX; enum sde_ctl ctl_idx = CTL_0; enum sde_ctl ctl_idx = CTL_MAX; u32 controller_id = disp_info->h_tile_instance[i]; if (intf_type == INTF_HDMI) ctl_idx = CTL_2; DBG("h_tile_instance %d = %d", i, controller_id); intf_idx = sde_encoder_get_intf(sde_kms->catalog, Loading @@ -396,6 +417,12 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, ret = -EINVAL; } hw_res_map = sde_rm_get_res_map(sde_kms, intf_idx); if (IS_ERR_OR_NULL(hw_res_map)) ret = -EINVAL; else ctl_idx = hw_res_map->ctl; /* Create both VID and CMD Phys Encoders here */ if (!ret) ret = sde_encoder_virt_add_phys_vid_enc( Loading Loading @@ -461,6 +488,25 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); } void sde_encoder_get_vsync_info(struct drm_encoder *drm_enc, struct vsync_info *vsync) { struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); struct sde_encoder_phys *phys; DBG(""); if (!vsync) { DRM_ERROR("Invalid pointer"); return; } /* we get the vsync info from the intf at index 0: master index */ phys = sde_enc->phys_encs[0]; if (phys) phys->phys_ops.get_vsync_info(phys, vsync); } /* encoders init, * initialize encoder based on displays */ Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys.h +6 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,8 @@ struct sde_encoder_virt_ops { struct sde_encoder_phys_ops { void (*mode_set)(struct sde_encoder_phys *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode, bool splitmode); bool (*mode_fixup)(struct sde_encoder_phys *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); Loading @@ -39,6 +40,10 @@ struct sde_encoder_phys_ops { void (*destroy)(struct sde_encoder_phys *encoder); void (*get_hw_resources)(struct sde_encoder_phys *encoder, struct sde_encoder_hw_resources *hw_res); void (*get_vsync_info)(struct sde_encoder_phys *enc, struct vsync_info *vsync); void (*enable_split_config)(struct sde_encoder_phys *enc, bool enable); }; struct sde_encoder_phys { Loading