diff options
| author | Lloyd Atkinson <latkinso@codeaurora.org> | 2016-07-05 12:23:29 -0400 |
|---|---|---|
| committer | Dhaval Patel <pdhaval@codeaurora.org> | 2016-08-01 11:58:08 -0700 |
| commit | ad84d148e1219ff4d6299847ec29d7ab70616e9f (patch) | |
| tree | 521d04a5f9254250fbb0e74c14602df6ce1ad7f4 | |
| parent | 0527ae787b14f5e9ccd1737b5f998fc6f22742bd (diff) | |
drm/msm/sde: set encoder role at display probe time
Base the encoder's master/slave role on display probe.
Move interface select programming fully to the mdp_top block.
Fix vblank_en race condition in CRTC.
Change-Id: I4a26509a0ac28e0926b8bea90c5aaa5707a37e21
Signed-off-by: Lloyd Atkinson <latkinso@codeaurora.org>
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_crtc.c | 41 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_crtc.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder.c | 167 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys.h | 104 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c | 220 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_intf.c | 40 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_mdp_top.c | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_kms.h | 13 |
8 files changed, 352 insertions, 238 deletions
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index e8d27cfe8b82..786dd63a7a22 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -197,8 +197,8 @@ static void sde_crtc_mode_set_nofb(struct drm_crtc *crtc) static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg, struct sde_plane_state *pstate) { - const struct mdp_format *format; struct drm_plane *plane; + const struct mdp_format *format; format = to_mdp_format( msm_framebuffer_format(pstate->base.fb)); @@ -241,6 +241,16 @@ static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg, cfg->fg.const_alpha = 0xFF; cfg->bg.const_alpha = 0x00; } + + DBG("format 0x%x, alpha_enable %u premultiplied %llu", + format->base.pixel_format, format->alpha_enable, + pstate->property_values[PLANE_PROP_PREMULTIPLIED]); + DBG("fg alpha config %d %d %d %d %d", + cfg->fg.alpha_sel, cfg->fg.const_alpha, cfg->fg.mod_alpha, + cfg->fg.inv_alpha_sel, cfg->fg.inv_mode_alpha); + DBG("bg alpha config %d %d %d %d %d", + cfg->bg.alpha_sel, cfg->bg.const_alpha, cfg->bg.mod_alpha, + cfg->bg.inv_alpha_sel, cfg->bg.inv_mode_alpha); } static void blend_setup(struct drm_crtc *crtc) @@ -312,10 +322,9 @@ static void blend_setup(struct drm_crtc *crtc) * If there is no base layer, enable border color. * currently border color is always black */ - if ((stage_cfg.stage[SDE_STAGE_BASE][0] == SSPP_NONE) && - plane_cnt) { + if ((stage_cfg.stage[SDE_STAGE_BASE][0] == SSPP_NONE) && plane_cnt) { stage_cfg.border_enable = 1; - DBG("Border Color is enabled\n"); + DBG("Border Color is enabled"); } /* Program ctl_paths */ @@ -371,7 +380,7 @@ static void sde_crtc_vblank_cb(void *data) if (pending & PENDING_FLIP) complete_flip(crtc, NULL); - if (sde_crtc->vblank_enable) { + if (sde_crtc->drm_requested_vblank) { drm_handle_vblank(dev, sde_crtc->id); DBG_IRQ(""); } @@ -381,9 +390,13 @@ static bool 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); + /* + * encoder get vsync_info + * if frame_count does not match + * frame is flushed + */ + sde_encoder_get_vblank_status(sde_crtc->encoder, &vsync); + return (vsync.frame_count != sde_crtc->vsync_count) ? true : false; } @@ -422,7 +435,7 @@ static void request_pending(struct drm_crtc *crtc, u32 pending) struct vsync_info vsync; /* request vsync info, cache the current frame count */ - sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync); + sde_encoder_get_vblank_status(sde_crtc->encoder, &vsync); sde_crtc->vsync_count = vsync.frame_count; atomic_or(pending, &sde_crtc->pending); @@ -634,6 +647,15 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) struct sde_crtc *sde_crtc = to_sde_crtc(crtc); DBG("%d", en); + + /* + * Mark that framework requested vblank, + * as opposed to enabling vblank only for our internal purposes + * Currently this variable isn't required, but may be useful for future + * features + */ + sde_crtc->drm_requested_vblank = en; + if (en) sde_encoder_register_vblank_callback(sde_crtc->encoder, sde_crtc_vblank_cb, (void *)crtc); @@ -641,7 +663,6 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL); - sde_crtc->vblank_enable = en; return 0; } diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index fa54d805ddf2..bf4bbc104f88 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -52,7 +52,7 @@ struct sde_crtc_mixer { * @event : Pointer to last received drm vblank event * @pending : Whether or not an update is pending * @vsync_count : Running count of received vsync events - * @vblank_enable : Whether vblanks have been enabled in the encoder + * @drm_requested_vblank : Whether vblanks have been enabled in the encoder */ struct sde_crtc { struct drm_crtc base; @@ -71,7 +71,7 @@ struct sde_crtc { struct drm_pending_vblank_event *event; atomic_t pending; u32 vsync_count; - bool vblank_enable; + bool drm_requested_vblank; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 642c668fc01e..bea9a30cb1dd 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -24,6 +24,40 @@ #include "sde_encoder_phys.h" #include "display_manager.h" +#define NUM_PHYS_ENCODER_TYPES 2 +#define MAX_PHYS_ENCODERS_PER_VIRTUAL \ + (MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES) + +/** + * struct sde_encoder_virt - virtual encoder. Container of one or more physical + * encoders. Virtual encoder manages one "logical" display. Physical + * encoders manage one intf block, tied to a specific panel/sub-panel. + * Virtual encoder defers as much as possible to the physical encoders. + * Virtual encoder registers itself with the DRM Framework as the encoder. + * @base: drm_encoder base class for registration with DRM + * @spin_lock: Lock for IRQ purposes + * @bus_scaling_client: Client handle to the bus scaling interface + * @num_phys_encs: Actual number of physical encoders contained. + * @phys_encs: Container of physical encoders managed. + * @cur_master: Pointer to the current master in this mode. Optimization + * Only valid after enable. Cleared as disable. + * @kms_vblank_callback: Callback into the upper layer / CRTC for + * notification of the VBLANK + * @kms_vblank_callback_data: Data from upper layer for VBLANK notification + */ +struct sde_encoder_virt { + struct drm_encoder base; + spinlock_t spin_lock; + uint32_t bus_scaling_client; + + int num_phys_encs; + struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *cur_master; + + void (*kms_vblank_callback)(void *); + void *kms_vblank_callback_data; +}; + #define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base) #ifdef CONFIG_QCOM_BUS_SCALING @@ -117,8 +151,8 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc, 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.get_hw_resources) - phys->phys_ops.get_hw_resources(phys, hw_res); + if (phys && phys->ops.get_hw_resources) + phys->ops.get_hw_resources(phys, hw_res); } } @@ -139,8 +173,8 @@ static void sde_encoder_destroy(struct drm_encoder *drm_enc) for (i = 0; i < ARRAY_SIZE(sde_enc->phys_encs); i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; - if (phys && phys->phys_ops.destroy) { - phys->phys_ops.destroy(phys); + if (phys && phys->ops.destroy) { + phys->ops.destroy(phys); --sde_enc->num_phys_encs; sde_enc->phys_encs[i] = NULL; } @@ -176,12 +210,11 @@ static bool sde_encoder_virt_mode_fixup(struct drm_encoder *drm_enc, 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_fixup) { - ret = - phys->phys_ops.mode_fixup(phys, mode, - adjusted_mode); + if (phys && phys->ops.mode_fixup) { + ret = phys->ops.mode_fixup(phys, mode, + adjusted_mode); if (!ret) { - DBG("Mode unsupported by phys_enc %d", i); + DRM_ERROR("Mode unsupported, phys_enc %d\n", i); break; } @@ -201,7 +234,6 @@ 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(""); @@ -212,23 +244,10 @@ 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, - mode, - adjusted_mode, - splitmode); - if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0) - DRM_ERROR("adjusted modes not supported\n"); - } + if (phys && phys->ops.mode_set) + phys->ops.mode_set(phys, mode, adjusted_mode); } } @@ -236,7 +255,6 @@ 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(""); @@ -249,21 +267,22 @@ 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); + + if (phys) { + if (phys->ops.enable) + phys->ops.enable(phys); + /* - * enable interrupts on master only + * Master can switch at enable time. + * It is based on the current mode (CMD/VID) and + * the encoder role found at panel probe time */ - phys->phys_ops.enable(phys, (i == 0) ? true : false); + if (phys->ops.is_master && phys->ops.is_master(phys)) { + DBG("phys enc master is now idx %d", i); + sde_enc->cur_master = phys; + } } } } @@ -285,10 +304,13 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) 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.disable) - phys->phys_ops.disable(phys); + if (phys && phys->ops.disable) + phys->ops.disable(phys); } + sde_enc->cur_master = NULL; + DBG("clear phys enc master"); + bs_set(sde_enc, 0); } @@ -338,10 +360,12 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); } -static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder_virt *sde_enc, - struct sde_kms *sde_kms, - enum sde_intf intf_idx, - enum sde_ctl ctl_idx) +static int sde_encoder_virt_add_phys_vid_enc( + struct sde_encoder_virt *sde_enc, + struct sde_kms *sde_kms, + enum sde_intf intf_idx, + enum sde_ctl ctl_idx, + enum sde_enc_split_role split_role) { int ret = 0; @@ -357,12 +381,12 @@ static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder_virt *sde_enc, }; struct sde_encoder_phys *enc = sde_encoder_phys_vid_init(sde_kms, intf_idx, ctl_idx, - &sde_enc->base, - parent_ops); - if (IS_ERR(enc)) - ret = PTR_ERR(enc); - - if (!ret) { + split_role, &sde_enc->base, parent_ops); + if (IS_ERR_OR_NULL(enc)) { + DRM_ERROR("Failed to initialize phys enc: %ld\n", + PTR_ERR(enc)); + ret = enc == 0 ? -EINVAL : PTR_ERR(enc); + } else { sde_enc->phys_encs[sde_enc->num_phys_encs] = enc; ++sde_enc->num_phys_encs; } @@ -407,26 +431,36 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, enum sde_intf intf_idx = INTF_MAX; enum sde_ctl ctl_idx = CTL_MAX; u32 controller_id = disp_info->h_tile_instance[i]; + enum sde_enc_split_role split_role = ENC_ROLE_SOLO; + + if (disp_info->num_of_h_tiles > 1) { + if (i == 0) + split_role = ENC_ROLE_MASTER; + else + split_role = ENC_ROLE_SLAVE; + } - DBG("h_tile_instance %d = %d", i, controller_id); + DBG("h_tile_instance %d = %d, split_role %d", + i, controller_id, split_role); intf_idx = sde_encoder_get_intf(sde_kms->catalog, intf_type, controller_id); if (intf_idx == INTF_MAX) { - DBG("Error: could not get the interface id"); + DRM_ERROR("Error: could not get the interface id\n"); ret = -EINVAL; } hw_res_map = sde_rm_get_res_map(sde_kms, intf_idx); - if (IS_ERR_OR_NULL(hw_res_map)) + if (IS_ERR_OR_NULL(hw_res_map)) { ret = -EINVAL; - else + } 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( - sde_enc, sde_kms, intf_idx, ctl_idx); + sde_enc, sde_kms, intf_idx, ctl_idx, + split_role); + if (ret) + DRM_ERROR("Failed to add phys enc\n"); + } } @@ -456,6 +490,7 @@ static struct drm_encoder *sde_encoder_virt_init( if (ret) goto fail; + sde_enc->cur_master = NULL; spin_lock_init(&sde_enc->spin_lock); drm_enc = &sde_enc->base; drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode); @@ -488,23 +523,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, +void sde_encoder_get_vblank_status(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; + struct sde_encoder_virt *sde_enc = NULL; + struct sde_encoder_phys *master = NULL; DBG(""); - if (!vsync) { + if (!vsync || !drm_enc) { DRM_ERROR("Invalid pointer"); return; } + sde_enc = to_sde_encoder_virt(drm_enc); + + memset(vsync, 0, sizeof(*vsync)); - /* 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); + master = sde_enc->cur_master; + if (master && master->ops.get_vblank_status) + master->ops.get_vblank_status(master, vsync); } /* encoders init, diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 45935bb858bd..e6ee7284fcf1 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -19,42 +19,95 @@ #include "sde_hw_intf.h" #include "sde_hw_mdp_ctl.h" -#define MAX_PHYS_ENCODERS_PER_VIRTUAL 4 +/** + * enum sde_enc_split_role - Role this physical encoder will play in a + * split-panel configuration, where one panel is master, and others slaves. + * Masters have extra responsibilities, like managing the VBLANK IRQ. + * @ENC_ROLE_SOLO: This is the one and only panel. This encoder is master. + * @ENC_ROLE_MASTER: This encoder is the master of a split panel config. + * @ENC_ROLE_SLAVE: This encoder is not the master of a split panel config. + */ +enum sde_enc_split_role { + ENC_ROLE_SOLO, + ENC_ROLE_MASTER, + ENC_ROLE_SLAVE +}; struct sde_encoder_phys; +/** + * struct sde_encoder_virt_ops - Interface the containing virtual encoder + * provides for the physical encoders to use to callback. + * @handle_vblank_virt: Notify virtual encoder of vblank IRQ reception + * Note: This is called from IRQ handler context. + */ struct sde_encoder_virt_ops { void (*handle_vblank_virt)(struct drm_encoder *); }; +/** + * struct sde_encoder_phys_ops - Interface the physical encoders provide to + * the containing virtual encoder. + * @is_master: Whether this phys_enc is the current master + * encoder. Can be switched at enable time. Based + * on split_role and current mode (CMD/VID). + * @mode_fixup: DRM Call. Fixup a DRM mode. + * @mode_set: DRM Call. Set a DRM mode. + * This likely caches the mode, for use at enable. + * @enable: DRM Call. Enable a DRM mode. + * @disable: DRM Call. Disable mode. + * @destroy: DRM Call. Destroy and release resources. + * @get_hw_resources: Populate the structure with the hardware + * resources that this phys_enc is using. + * Expect no overlap between phys_encs. + * @get_vblank_status: Query hardware for the vblank info + * appropriate for this phys_enc (vsync/pprdptr). + * Only appropriate for master phys_enc. + */ struct sde_encoder_phys_ops { - void (*mode_set)(struct sde_encoder_phys *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - bool splitmode); + bool (*is_master)(struct sde_encoder_phys *encoder); bool (*mode_fixup)(struct sde_encoder_phys *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); - void (*enable)(struct sde_encoder_phys *encoder, bool intr_en); + void (*mode_set)(struct sde_encoder_phys *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*enable)(struct sde_encoder_phys *encoder); void (*disable)(struct sde_encoder_phys *encoder); 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, + void (*get_vblank_status)(struct sde_encoder_phys *enc, struct vsync_info *vsync); - void (*enable_split_config)(struct sde_encoder_phys *enc, - bool enable); }; +/** + * struct sde_encoder_phys - physical encoder that drives a single INTF block + * tied to a specific panel / sub-panel. Abstract type, sub-classed by + * phys_vid or phys_cmd for video mode or command mode encs respectively. + * @parent: Pointer to the containing virtual encoder + * @ops: Operations exposed to the virtual encoder + * @parent_ops: Callbacks exposed by the parent to the phys_enc + * @hw_mdptop: Hardware interface to the top registers + * @hw_intf: Hardware interface to the intf registers + * @hw_ctl: Hardware interface to the ctl registers + * @sde_kms: Pointer to the sde_kms top level + * @cached_mode: DRM mode cached at mode_set time, acted on in enable + * @enabled: Whether the encoder has enabled and running a mode + * @split_role: Role to play in a split-panel configuration + * @spin_lock: Lock for IRQ purposes + */ struct sde_encoder_phys { struct drm_encoder *parent; + struct sde_encoder_phys_ops ops; struct sde_encoder_virt_ops parent_ops; - struct sde_encoder_phys_ops phys_ops; + struct sde_hw_mdp *hw_mdptop; struct sde_hw_intf *hw_intf; struct sde_hw_ctl *hw_ctl; struct sde_kms *sde_kms; struct drm_display_mode cached_mode; bool enabled; + enum sde_enc_split_role split_role; spinlock_t spin_lock; }; @@ -69,26 +122,25 @@ struct sde_encoder_phys_vid { struct sde_encoder_phys base; int irq_idx; struct completion vblank_complete; - bool intr_en; }; -struct sde_encoder_virt { - struct drm_encoder base; - spinlock_t spin_lock; - uint32_t bus_scaling_client; - - int num_phys_encs; - struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; - - void (*kms_vblank_callback)(void *); - void *kms_vblank_callback_data; -}; - -struct sde_encoder_phys *sde_encoder_phys_vid_init(struct sde_kms *sde_kms, +/** + * sde_encoder_phys_vid_init - Construct a new video mode physical encoder + * @sde_kms: Pointer to the sde_kms top level + * @intf_idx: Interface index this phys_enc will control + * @ctl_idx: Control index this phys_enc requires + * @split_role: Role to play in a split-panel configuration + * @parent: Pointer to the containing virtual encoder + * @parent_ops: Callbacks exposed by the parent to the phys_enc + * + * Return: Error code or newly allocated encoder + */ +struct sde_encoder_phys *sde_encoder_phys_vid_init( + struct sde_kms *sde_kms, enum sde_intf intf_idx, enum sde_ctl ctl_idx, + enum sde_enc_split_role split_role, struct drm_encoder *parent, - struct sde_encoder_virt_ops - parent_ops); + struct sde_encoder_virt_ops parent_ops); #endif /* __sde_encoder_phys_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 4d38fd326b6e..8c220b99ceb4 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -27,7 +27,10 @@ static bool sde_encoder_phys_vid_is_master( struct sde_encoder_phys *phys_enc) { - bool ret = true; + bool ret = false; + + if (phys_enc->split_role != ENC_ROLE_SLAVE) + ret = true; return ret; } @@ -145,7 +148,7 @@ static u32 programmable_fetch_get_num_lines( } else if (timing->v_front_porch < needed_vfp_lines) { /* Warn fetch needed, but not enough porch in panel config */ pr_warn_once - ("low vbp+vfp may lead to perf issues in some cases\n"); + ("low vbp+vfp may lead to perf issues in some cases\n"); DBG("Less vfp than fetch requires, using entire vfp"); actual_vfp_lines = timing->v_front_porch; } else { @@ -216,49 +219,23 @@ static bool sde_encoder_phys_vid_mode_fixup( return true; } -static void sde_encoder_phys_vid_flush_intf(struct sde_encoder_phys *phys_enc) -{ - struct sde_hw_intf *intf = phys_enc->hw_intf; - struct sde_hw_ctl *ctl = phys_enc->hw_ctl; - u32 flush_mask = 0; - - DBG(""); - - ctl->ops.get_bitmask_intf(ctl, &flush_mask, intf->idx); - ctl->ops.setup_flush(ctl, flush_mask); - - DBG("Flushing CTL_ID %d, flush_mask %x, INTF %d", - ctl->idx, flush_mask, intf->idx); -} - -static void sde_encoder_phys_vid_mode_set(struct sde_encoder_phys *phys_enc, - struct drm_display_mode *mode, - struct drm_display_mode - *adjusted_mode, - bool splitmode) +static void sde_encoder_phys_vid_mode_set( + struct sde_encoder_phys *phys_enc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { mode = adjusted_mode; phys_enc->cached_mode = *adjusted_mode; - if (splitmode) { - phys_enc->cached_mode.hdisplay >>= 1; - phys_enc->cached_mode.htotal >>= 1; - phys_enc->cached_mode.hsync_start >>= 1; - phys_enc->cached_mode.hsync_end >>= 1; - } - - DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, - mode->type, mode->flags); + DBG("intf %d, caching mode:", phys_enc->hw_intf->idx); + drm_mode_debug_printmodeline(mode); } static void sde_encoder_phys_vid_setup_timing_engine( struct sde_encoder_phys *phys_enc) { struct drm_display_mode *mode = &phys_enc->cached_mode; - struct intf_timing_params p = { 0 }; - struct sde_mdp_format_params *sde_fmt_params = NULL; + struct intf_timing_params timing_params = { 0 }; + struct sde_mdp_format_params *fmt_params = NULL; u32 fmt_fourcc = DRM_FORMAT_RGB888; u32 fmt_mod = 0; unsigned long lock_flags; @@ -270,26 +247,37 @@ static void sde_encoder_phys_vid_setup_timing_engine( if (WARN_ON(!phys_enc->hw_ctl->ops.setup_intf_cfg)) return; - DBG("enable mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, - mode->type, mode->flags); - drm_mode_to_intf_timing_params(phys_enc, mode, &p); + DBG("intf %d, enabling mode:", phys_enc->hw_intf->idx); + drm_mode_debug_printmodeline(mode); + + if (phys_enc->split_role != ENC_ROLE_SOLO) { + mode->hdisplay >>= 1; + mode->htotal >>= 1; + mode->hsync_start >>= 1; + mode->hsync_end >>= 1; + + DBG("split_role %d, halve horizontal: %d %d %d %d", + phys_enc->split_role, + mode->hdisplay, mode->htotal, + mode->hsync_start, mode->hsync_end); + } + + drm_mode_to_intf_timing_params(phys_enc, mode, &timing_params); - sde_fmt_params = sde_mdp_get_format_params(fmt_fourcc, fmt_mod); + fmt_params = sde_mdp_get_format_params(fmt_fourcc, fmt_mod); + DBG("fmt_fourcc %d, fmt_mod %d", fmt_fourcc, fmt_mod); intf_cfg.intf = phys_enc->hw_intf->idx; intf_cfg.wb = SDE_NONE; spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); - phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, &p, - sde_fmt_params); + phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, + &timing_params, fmt_params); phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); - programmable_fetch_config(phys_enc, &p); + programmable_fetch_config(phys_enc, &timing_params); } static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) @@ -306,7 +294,7 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_vid *vid_enc = - to_sde_encoder_phys_vid(phys_enc); + to_sde_encoder_phys_vid(phys_enc); struct sde_irq_callback irq_cb; int ret = 0; @@ -365,11 +353,31 @@ static int sde_encoder_phys_vid_unregister_irq( return 0; } -static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc, - bool intr_en) +static void sde_encoder_phys_vid_split_config( + struct sde_encoder_phys *phys_enc, bool enable) +{ + struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop; + struct split_pipe_cfg cfg = { 0 }; + + DBG("enable %d", enable); + + cfg.en = enable; + cfg.mode = INTF_MODE_VIDEO; + cfg.intf = phys_enc->hw_intf->idx; + cfg.pp_split = false; + cfg.split_flush_en = enable; + + if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) { + unsigned long lock_flags; + + spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); + hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg); + spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); + } +} + +static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) { - struct sde_encoder_phys_vid *vid_enc = - to_sde_encoder_phys_vid(phys_enc); int ret = 0; DBG(""); @@ -377,18 +385,18 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc, if (WARN_ON(!phys_enc->hw_intf->ops.enable_timing)) return; - sde_encoder_phys_vid_setup_timing_engine(phys_enc); + if (phys_enc->split_role == ENC_ROLE_MASTER) + sde_encoder_phys_vid_split_config(phys_enc, true); + else if (phys_enc->split_role == ENC_ROLE_SOLO) + sde_encoder_phys_vid_split_config(phys_enc, false); - sde_encoder_phys_vid_flush_intf(phys_enc); + sde_encoder_phys_vid_setup_timing_engine(phys_enc); /* Register for interrupt unless we're the slave encoder */ - if (sde_encoder_phys_vid_is_master(phys_enc) && intr_en) { + if (phys_enc->split_role != ENC_ROLE_SLAVE) ret = sde_encoder_phys_vid_register_irq(phys_enc); - if (!ret) - vid_enc->intr_en = true; - } - if (!ret && !phys_enc->enabled) { + if (!ret) { unsigned long lock_flags = 0; /* Now enable timing engine */ @@ -428,8 +436,7 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) * scanout buffer) don't latch properly.. */ sde_encoder_phys_vid_wait_for_vblank(vid_enc); - if (vid_enc->intr_en) - sde_encoder_phys_vid_unregister_irq(phys_enc); + sde_encoder_phys_vid_unregister_irq(phys_enc); phys_enc->enabled = false; } @@ -438,7 +445,9 @@ static void sde_encoder_phys_vid_destroy(struct sde_encoder_phys *phys_enc) struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); DBG(""); - kfree(phys_enc->hw_intf); + sde_rm_release_ctl_path(phys_enc->sde_kms, phys_enc->hw_ctl->idx); + sde_hw_intf_deinit(phys_enc->hw_intf); + sde_hw_mdp_destroy(phys_enc->hw_mdptop); kfree(vid_enc); } @@ -446,8 +455,6 @@ static void sde_encoder_phys_vid_get_hw_resources( struct sde_encoder_phys *phys_enc, struct sde_encoder_hw_resources *hw_res) { - struct msm_drm_private *priv = phys_enc->parent->dev->dev_private; - struct sde_kms *sde_kms = to_sde_kms(priv->kms); const struct sde_hw_res_map *hw_res_map; DBG("Intf %d\n", phys_enc->hw_intf->idx); @@ -457,12 +464,22 @@ static void sde_encoder_phys_vid_get_hw_resources( * defaults should not be in use, * otherwise signal/return failure */ - hw_res_map = sde_rm_get_res_map(sde_kms, phys_enc->hw_intf->idx); + hw_res_map = sde_rm_get_res_map(phys_enc->sde_kms, + phys_enc->hw_intf->idx); + if (IS_ERR_OR_NULL(hw_res_map)) { + DRM_ERROR("Failed to get hw_res_map: %ld\n", + PTR_ERR(hw_res_map)); + return; + } - /* This is video mode panel so PINGPONG will be in by-pass mode + /* + * This is video mode panel so PINGPONG will be in by-pass mode * only assign ctl path.For cmd panel check if pp_split is * enabled, override default map */ + /* + * phys_enc->hw_ctl->idx + */ hw_res->ctls[hw_res_map->ctl] = true; } @@ -471,54 +488,54 @@ static void sde_encoder_phys_vid_get_hw_resources( * cmd mode will use the pingpong (get_vsync_info) * to get this information */ -static void sde_encoder_intf_get_vsync_info(struct sde_encoder_phys *phys_enc, +static void sde_encoder_intf_get_vblank_status( + struct sde_encoder_phys *phys_enc, struct vsync_info *vsync) { - struct intf_status status; - DBG(""); - phys_enc->hw_intf->ops.get_status(phys_enc->hw_intf, &status); - vsync->frame_count = status.frame_count; - vsync->line_count = status.line_count; - DBG(" sde_encoder_intf_get_vsync_info, count %d", vsync->frame_count); -} -static void sde_encoder_intf_split_config(struct sde_encoder_phys *phys_enc, - bool enable) -{ - struct msm_drm_private *priv = phys_enc->parent->dev->dev_private; - struct sde_kms *sde_kms = to_sde_kms(priv->kms); - struct sde_hw_mdp *mdp = sde_hw_mdptop_init(MDP_TOP, sde_kms->mmio, - sde_kms->catalog); - struct split_pipe_cfg cfg; + /* Slave encoders don't drive VBLANK interrupts, so don't respond */ + if (phys_enc->split_role == ENC_ROLE_SLAVE) + return; - cfg.en = enable; + /* + * Report VBlank Status as the VSYNC Frame/Line Counts from hardware + * Video encoders report from the VSYNC INTF (get_status) + * CMD Encoders report from the PingPong block (get_vsync_info) + */ + if (phys_enc->hw_intf->ops.get_status) { + unsigned long lock_flags = 0; + struct intf_status status; - cfg.mode = INTF_MODE_VIDEO; - cfg.intf = INTF_1; - cfg.pp_split = false; - cfg.split_flush_en = true; + spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); + phys_enc->hw_intf->ops.get_status(phys_enc->hw_intf, &status); + spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); - if (!IS_ERR_OR_NULL(mdp)) - mdp->ops.setup_split_pipe(mdp, &cfg); + vsync->frame_count = status.frame_count; + vsync->line_count = status.line_count; + } + + DBG("frame_count %d line_count %d", vsync->frame_count, + vsync->line_count); } -static void sde_encoder_phys_vid_init_cbs(struct sde_encoder_phys_ops *ops) +static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) { + ops->is_master = sde_encoder_phys_vid_is_master; ops->mode_set = sde_encoder_phys_vid_mode_set; ops->mode_fixup = sde_encoder_phys_vid_mode_fixup; ops->enable = sde_encoder_phys_vid_enable; ops->disable = sde_encoder_phys_vid_disable; ops->destroy = sde_encoder_phys_vid_destroy; ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources; - ops->get_vsync_info = sde_encoder_intf_get_vsync_info; - ops->enable_split_config = sde_encoder_intf_split_config; + ops->get_vblank_status = sde_encoder_intf_get_vblank_status; } struct sde_encoder_phys *sde_encoder_phys_vid_init( struct sde_kms *sde_kms, enum sde_intf intf_idx, enum sde_ctl ctl_idx, + enum sde_enc_split_role split_role, struct drm_encoder *parent, struct sde_encoder_virt_ops parent_ops) { @@ -538,23 +555,34 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( phys_enc = &vid_enc->base; + phys_enc->hw_mdptop = sde_hw_mdptop_init(MDP_TOP, sde_kms->mmio, + sde_kms->catalog); + if (IS_ERR_OR_NULL(phys_enc->hw_mdptop)) { + ret = PTR_ERR(phys_enc->hw_mdptop); + DRM_ERROR("Failed init hw_top: %d\n", ret); + goto fail; + } + phys_enc->hw_intf = sde_hw_intf_init(intf_idx, sde_kms->mmio, sde_kms->catalog); - if (!phys_enc->hw_intf) { - ret = -ENOMEM; + if (IS_ERR_OR_NULL(phys_enc->hw_intf)) { + ret = PTR_ERR(phys_enc->hw_intf); + DRM_ERROR("Failed init hw_intf: %d\n", ret); goto fail; } phys_enc->hw_ctl = sde_rm_acquire_ctl_path(sde_kms, ctl_idx); - if (!phys_enc->hw_ctl) { - ret = -ENOMEM; + if (IS_ERR_OR_NULL(phys_enc->hw_ctl)) { + ret = PTR_ERR(phys_enc->hw_ctl); + DRM_ERROR("Failed init hw_ctl: %d\n", ret); goto fail; } - sde_encoder_phys_vid_init_cbs(&phys_enc->phys_ops); + sde_encoder_phys_vid_init_ops(&phys_enc->ops); phys_enc->parent = parent; phys_enc->parent_ops = parent_ops; phys_enc->sde_kms = sde_kms; + phys_enc->split_role = split_role; spin_lock_init(&phys_enc->spin_lock); DBG("Created sde_encoder_phys_vid for intf %d", phys_enc->hw_intf->idx); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c index a9cd20122275..101e3248c08a 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c @@ -13,7 +13,6 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_intf.h" -#include "sde_hw_mdp_top.h" #define INTF_TIMING_ENGINE_EN 0x000 #define INTF_CONFIG 0x004 @@ -173,24 +172,17 @@ static void sde_hw_intf_setup_timing_engine(struct sde_hw_intf *ctx, (0x21 << 8)); SDE_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl); - SDE_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, - vsync_period * hsync_period); + SDE_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period); SDE_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0, p->vsync_pulse_width * hsync_period); SDE_REG_WRITE(c, INTF_DISPLAY_HCTL, display_hctl); - SDE_REG_WRITE(c, INTF_DISPLAY_V_START_F0, - display_v_start); - SDE_REG_WRITE(c, INTF_DISPLAY_V_END_F0, - display_v_end); + SDE_REG_WRITE(c, INTF_DISPLAY_V_START_F0, display_v_start); + SDE_REG_WRITE(c, INTF_DISPLAY_V_END_F0, display_v_end); SDE_REG_WRITE(c, INTF_ACTIVE_HCTL, active_hctl); - SDE_REG_WRITE(c, INTF_ACTIVE_V_START_F0, - active_v_start); - SDE_REG_WRITE(c, INTF_ACTIVE_V_END_F0, - active_v_end); - + SDE_REG_WRITE(c, INTF_ACTIVE_V_START_F0, active_v_start); + SDE_REG_WRITE(c, INTF_ACTIVE_V_END_F0, active_v_end); SDE_REG_WRITE(c, INTF_BORDER_COLOR, p->border_clr); - SDE_REG_WRITE(c, INTF_UNDERFLOW_COLOR, - p->underflow_clr); + SDE_REG_WRITE(c, INTF_UNDERFLOW_COLOR, p->underflow_clr); SDE_REG_WRITE(c, INTF_HSYNC_SKEW, p->hsync_skew); SDE_REG_WRITE(c, INTF_POLARITY_CTL, polarity_ctl); SDE_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3); @@ -203,24 +195,8 @@ static void sde_hw_intf_enable_timing_engine( u8 enable) { struct sde_hw_blk_reg_map *c = &intf->hw; - u32 intf_sel; - - /* Display interface select */ - if (enable) { - /* top block */ - struct sde_hw_mdp *mdp = sde_hw_mdptop_init(MDP_TOP, - c->base_off, - intf->mdss); - struct sde_hw_blk_reg_map *top = &mdp->hw; - - intf_sel = SDE_REG_READ(top, DISP_INTF_SEL); - - intf_sel |= (intf->cap->type << ((intf->idx - INTF_0) * 8)); - SDE_REG_WRITE(top, DISP_INTF_SEL, intf_sel); - } - - SDE_REG_WRITE(c, INTF_TIMING_ENGINE_EN, - enable & 0x1); + /* Note: Display interface select is handled in top block hw layer */ + SDE_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0); } static void sde_hw_intf_setup_prg_fetch( diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_top.c b/drivers/gpu/drm/msm/sde/sde_hw_mdp_top.c index 9b1d0d5f309e..9c9731215c3a 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_top.c @@ -126,5 +126,6 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx, void sde_hw_mdp_destroy(struct sde_hw_mdp *mdp) { + kfree(mdp); } diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h index bb043dfb46b9..51e6e538305b 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.h +++ b/drivers/gpu/drm/msm/sde/sde_kms.h @@ -46,8 +46,8 @@ struct sde_irq { /** * struct sde_hw_res_map : Default resource table identifying default * hw resource map. Primarily used for forcing DSI to use CTL_0/1 - * and Pingpong 0/1, if the field is set to SDE_NONE means any HW - * intstance for that tpye is allowed as long as it is unused. + * and PingPong 0/1, if the field is set to SDE_NONE means any HW + * instance for that type is allowed as long as it is unused. */ struct sde_hw_res_map { enum sde_intf intf; @@ -56,7 +56,7 @@ struct sde_hw_res_map { enum sde_ctl ctl; }; -/* struct sde_hw_resource_manager : Resource mananger maintains the current +/* struct sde_hw_resource_manager : Resource manager maintains the current * default platform config and manages shared * hw resources ex:ctl_path hw driver context * is needed by CRTCs/PLANEs/ENCODERs @@ -65,7 +65,7 @@ struct sde_hw_res_map { * @intr : pointer to hw interrupt context * @res_table : pointer to default hw_res table for this platform * @feature_map :BIT map for default enabled features ex:specifies if PP_SPLIT - * is enabled/disabled by defalt for this platform + * is enabled/disabled by default for this platform */ struct sde_hw_resource_manager { struct sde_hw_ctl *ctl[CTL_MAX]; @@ -374,10 +374,9 @@ void sde_encoder_get_hw_resources(struct drm_encoder *encoder, struct sde_encoder_hw_resources *hw_res); void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, void (*cb)(void *), void *data); -void sde_encoders_init(struct drm_device *dev); -void sde_encoder_get_vsync_info(struct drm_encoder *encoder, +void sde_encoder_get_vblank_status(struct drm_encoder *encoder, struct vsync_info *vsync); - +void sde_encoders_init(struct drm_device *dev); #endif /* __sde_kms_H__ */ |
