Loading drivers/gpu/drm/msm/sde/sde_core_irq.c +1 −36 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 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 Loading Loading @@ -42,11 +42,6 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) irq_idx); } static void sde_core_irq_intf_error_handler(void *arg, int irq_idx) { SDE_ERROR("INTF underrun detected, irq_idx=%d\n", irq_idx); } int sde_core_irq_idx_lookup(struct sde_kms *sde_kms, enum sde_intr_type intr_type, u32 instance_idx) { Loading Loading @@ -191,36 +186,6 @@ void sde_core_irq_preinstall(struct sde_kms *sde_kms) int sde_core_irq_postinstall(struct sde_kms *sde_kms) { struct msm_drm_private *priv; struct sde_irq_callback irq_cb; int irq_idx; int i; if (!sde_kms) { SDE_ERROR("invalid sde_kms\n"); return -EINVAL; } else if (!sde_kms->dev) { SDE_ERROR("invalid drm device\n"); return -EINVAL; } else if (!sde_kms->dev->dev_private) { SDE_ERROR("invalid device private\n"); return -EINVAL; } priv = sde_kms->dev->dev_private; irq_cb.func = sde_core_irq_intf_error_handler; irq_cb.arg = sde_kms; /* Register interface underrun callback */ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); for (i = 0; i < sde_kms->catalog->intf_count; i++) { irq_idx = sde_core_irq_idx_lookup(sde_kms, SDE_IRQ_TYPE_INTF_UNDER_RUN, i+INTF_0); sde_core_irq_register_callback(sde_kms, irq_idx, &irq_cb); sde_core_irq_enable(sde_kms, &irq_idx, 1); } sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); return 0; } Loading drivers/gpu/drm/msm/sde/sde_encoder.c +23 −3 Original line number Diff line number Diff line Loading @@ -389,6 +389,9 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys) { atomic_set(&phys->vsync_cnt, 0); atomic_set(&phys->underrun_cnt, 0); if (phys->ops.enable) phys->ops.enable(phys); Loading Loading @@ -435,8 +438,12 @@ 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->ops.disable && !phys->ops.is_master(phys)) if (phys && phys->ops.disable && !phys->ops.is_master(phys)) { phys->ops.disable(phys); atomic_set(&phys->vsync_cnt, 0); atomic_set(&phys->underrun_cnt, 0); } } if (sde_enc->cur_master && sde_enc->cur_master->ops.disable) Loading Loading @@ -488,12 +495,13 @@ static enum sde_wb sde_encoder_get_wb(struct sde_mdss_cfg *catalog, return WB_MAX; } static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, struct sde_encoder_phys *phy_enc) { struct sde_encoder_virt *sde_enc = NULL; unsigned long lock_flags; if (!drm_enc) if (!drm_enc || !phy_enc) return; sde_enc = to_sde_encoder_virt(drm_enc); Loading @@ -502,6 +510,17 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) if (sde_enc->crtc_vblank_cb) sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data); spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); atomic_inc(&phy_enc->vsync_cnt); } static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, struct sde_encoder_phys *phy_enc) { if (!phy_enc) return; atomic_inc(&phy_enc->underrun_cnt); } void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, Loading Loading @@ -829,6 +848,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, enum sde_intf_type intf_type; struct sde_encoder_virt_ops parent_ops = { sde_encoder_vblank_callback, sde_encoder_underrun_callback, sde_encoder_handle_phys_enc_ready_for_kickoff }; struct sde_enc_phys_init_params phys_params; Loading drivers/gpu/drm/msm/sde/sde_encoder_phys.h +30 −5 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017 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 Loading Loading @@ -46,11 +46,16 @@ struct sde_encoder_phys; * 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. * @handle_underrun_virt: Notify virtual encoder of underrun IRQ reception * Note: This is called from IRQ handler context. * @handle_ready_for_kickoff: Notify virtual encoder that this phys encoder * is now ready for the next kickoff. */ struct sde_encoder_virt_ops { void (*handle_vblank_virt)(struct drm_encoder *); void (*handle_vblank_virt)(struct drm_encoder *, struct sde_encoder_phys *phys); void (*handle_underrun_virt)(struct drm_encoder *, struct sde_encoder_phys *phys); void (*handle_ready_for_kickoff)(struct drm_encoder *, struct sde_encoder_phys *phys); }; Loading Loading @@ -122,6 +127,21 @@ enum sde_enc_enable_state { SDE_ENC_ENABLED }; /** * enum sde_intr_idx - sde encoder interrupt index * @INTR_IDX_VSYNC: Vsync interrupt for video mode panel * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel */ enum sde_intr_idx { INTR_IDX_VSYNC, INTR_IDX_PINGPONG, INTR_IDX_UNDERRUN, INTR_IDX_RDPTR, INTR_IDX_MAX, }; /** * struct sde_encoder_phys - physical encoder that drives a single INTF block * tied to a specific panel / sub-panel. Abstract type, sub-classed by Loading @@ -138,10 +158,13 @@ enum sde_enc_enable_state { * @enabled: Whether the encoder has enabled and running a mode * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode * @intf_idx: Interface index on sde hardware * @spin_lock: Lock for IRQ purposes * @mode_3d: 3D mux configuration * @enable_state: Enable state tracking * @vblank_refcount: Reference count of vblank request * @vsync_cnt: Vsync count for the physical encoder * @underrun_cnt: Underrun count for the physical encoder */ struct sde_encoder_phys { struct drm_encoder *parent; Loading @@ -155,10 +178,13 @@ struct sde_encoder_phys { struct drm_display_mode cached_mode; enum sde_enc_split_role split_role; enum sde_intf_mode intf_mode; enum sde_intf intf_idx; spinlock_t spin_lock; enum sde_3d_blend_mode mode_3d; enum sde_enc_enable_state enable_state; atomic_t vblank_refcount; atomic_t vsync_cnt; atomic_t underrun_cnt; }; /** Loading @@ -171,7 +197,7 @@ struct sde_encoder_phys { */ struct sde_encoder_phys_vid { struct sde_encoder_phys base; int irq_idx; int irq_idx[INTR_IDX_MAX]; struct sde_hw_intf *hw_intf; struct completion vblank_completion; }; Loading Loading @@ -199,8 +225,7 @@ struct sde_encoder_phys_cmd { int intf_idx; int stream_sel; struct sde_hw_pingpong *hw_pp; int pp_rd_ptr_irq_idx; int pp_tx_done_irq_idx; int irq_idx[INTR_IDX_MAX]; wait_queue_head_t pp_tx_done_wq; atomic_t pending_cnt; }; Loading drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +61 −23 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017 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 Loading Loading @@ -133,6 +133,7 @@ static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) wake_up_all(&cmd_enc->pp_tx_done_wq); /* Trigger a pending flush */ if (phys_enc->parent_ops.handle_ready_for_kickoff) phys_enc->parent_ops.handle_ready_for_kickoff(phys_enc->parent, phys_enc); } Loading @@ -145,15 +146,28 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx) if (!cmd_enc) return; phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent); if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); } static int sde_encoder_phys_cmd_register_pp_irq( struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx) { struct sde_encoder_phys_cmd *cmd_enc = arg; struct sde_encoder_phys *phys_enc; if (!cmd_enc) return; phys_enc = &cmd_enc->base; if (phys_enc->parent_ops.handle_underrun_virt) phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent, phys_enc); } static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); Loading Loading @@ -208,7 +222,7 @@ static int sde_encoder_phys_cmd_register_pp_irq( return ret; } static int sde_encoder_phys_cmd_unregister_pp_irq( static int sde_encoder_phys_cmd_unregister_irq( struct sde_encoder_phys *phys_enc, int irq_idx) { Loading Loading @@ -380,7 +394,7 @@ static int sde_encoder_phys_cmd_control_vblank_irq( /* Slave encoders don't report vblank */ if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; goto end; SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n", __builtin_return_address(0), Loading @@ -390,15 +404,25 @@ static int sde_encoder_phys_cmd_control_vblank_irq( atomic_read(&phys_enc->vblank_refcount)); if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) ret = sde_encoder_phys_cmd_register_pp_irq(phys_enc, ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_RD_PTR, &cmd_enc->pp_rd_ptr_irq_idx, &cmd_enc->irq_idx[INTR_IDX_PINGPONG], sde_encoder_phys_cmd_pp_rd_ptr_irq, "pp_rd_ptr"); else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) ret = sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_rd_ptr_irq_idx); ret = sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); if (enable) ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_RD_PTR, &cmd_enc->irq_idx[INTR_IDX_RDPTR], sde_encoder_phys_cmd_pp_rd_ptr_irq, "pp_rd_ptr"); else ret = sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_RDPTR]); end: if (ret) SDE_ERROR_CMDENC(cmd_enc, "control vblank irq error %d, enable %d\n", Loading Loading @@ -436,19 +460,30 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) sde_encoder_phys_cmd_pingpong_config(phys_enc); /* Both master and slave need to register for pp_tx_done */ ret = sde_encoder_phys_cmd_register_pp_irq(phys_enc, ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_COMP, &cmd_enc->pp_tx_done_irq_idx, &cmd_enc->irq_idx[INTR_IDX_PINGPONG], sde_encoder_phys_cmd_pp_tx_done_irq, "pp_tx_done"); if (ret) return; ret = sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true); if (ret) { sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_tx_done_irq_idx); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); return; } ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_UNDER_RUN, &cmd_enc->irq_idx[INTR_IDX_UNDERRUN], sde_encoder_phys_cmd_underrun_irq, "underrun"); if (ret) { sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_UNDERRUN]); return; } Loading @@ -475,9 +510,11 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc) if (WARN_ON(phys_enc->enable_state == SDE_ENC_DISABLED)) return; sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_tx_done_irq_idx); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_UNDERRUN]); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); atomic_set(&cmd_enc->pending_cnt, 0); wake_up_all(&cmd_enc->pp_tx_done_wq); Loading Loading @@ -603,6 +640,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( phys_enc->hw_mdptop = hw_mdp; cmd_enc->intf_idx = p->intf_idx; phys_enc->intf_idx = p->intf_idx; sde_encoder_phys_cmd_init_ops(&phys_enc->ops); phys_enc->parent = p->parent; Loading drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +69 −29 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 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 Loading Loading @@ -309,12 +309,28 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) return; phys_enc = &vid_enc->base; phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent); if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); /* signal VBLANK completion */ complete_all(&vid_enc->vblank_completion); } static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = arg; struct sde_encoder_phys *phys_enc; if (!vid_enc) return; phys_enc = &vid_enc->base; if (phys_enc->parent_ops.handle_underrun_virt) phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent, phys_enc); } static bool sde_encoder_phys_vid_needs_split_flush( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -347,7 +363,9 @@ static void _sde_encoder_phys_vid_split_config( } } static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc) static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) { struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); Loading @@ -359,59 +377,62 @@ static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc) return -EINVAL; } vid_enc->irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms, SDE_IRQ_TYPE_INTF_VSYNC, vid_enc->hw_intf->idx); if (vid_enc->irq_idx < 0) { *irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms, intr_type, vid_enc->hw_intf->idx); if (*irq_idx < 0) { SDE_ERROR_VIDENC(vid_enc, "failed to lookup IRQ index for INTF_VSYNC\n"); "failed to lookup IRQ index for %s type:%d\n", irq_name, intr_type); return -EINVAL; } irq_cb.func = sde_encoder_phys_vid_vblank_irq; irq_cb.func = irq_func; irq_cb.arg = vid_enc; ret = sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, &irq_cb); ret = sde_core_irq_register_callback(phys_enc->sde_kms, *irq_idx, &irq_cb); if (ret) { SDE_ERROR_VIDENC(vid_enc, "failed to register IRQ callback INTF_VSYNC"); "failed to register IRQ callback for %s\n", irq_name); return ret; } ret = sde_core_irq_enable(phys_enc->sde_kms, &vid_enc->irq_idx, 1); ret = sde_core_irq_enable(phys_enc->sde_kms, irq_idx, true); if (ret) { SDE_ERROR_VIDENC(vid_enc, "enable IRQ for INTF_VSYNC failed, irq_idx %d\n", vid_enc->irq_idx); vid_enc->irq_idx = -EINVAL; "enable IRQ for intr:%s failed, irq_idx %d\n", irq_name, *irq_idx); *irq_idx = -EINVAL; /* Unregister callback on IRQ enable failure */ /* unregister callback on IRQ enable failure */ sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, NULL); *irq_idx, NULL); return ret; } SDE_DEBUG_VIDENC(vid_enc, "registered %d\n", vid_enc->irq_idx); SDE_DEBUG_VIDENC(vid_enc, "registered irq %s idx: %d\n", irq_name, *irq_idx); return ret; } static int sde_encoder_phys_vid_unregister_irq( struct sde_encoder_phys *phys_enc) struct sde_encoder_phys *phys_enc, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); if (!phys_enc) { SDE_ERROR("invalid encoder\n"); return -EINVAL; goto end; } sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, NULL); sde_core_irq_disable(phys_enc->sde_kms, &vid_enc->irq_idx, 1); sde_core_irq_disable(phys_enc->sde_kms, &irq_idx, 1); sde_core_irq_register_callback(phys_enc->sde_kms, irq_idx, NULL); SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", vid_enc->irq_idx); SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", irq_idx); end: return 0; } Loading Loading @@ -478,9 +499,13 @@ static int sde_encoder_phys_vid_control_vblank_irq( atomic_read(&phys_enc->vblank_refcount)); if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) ret = sde_encoder_phys_vid_register_irq(phys_enc); ret = sde_encoder_phys_vid_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_VSYNC, &vid_enc->irq_idx[INTR_IDX_VSYNC], sde_encoder_phys_vid_vblank_irq, "vsync_irq"); else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) ret = sde_encoder_phys_vid_unregister_irq(phys_enc); ret = sde_encoder_phys_vid_unregister_irq(phys_enc, vid_enc->irq_idx[INTR_IDX_VSYNC]); if (ret) SDE_ERROR_VIDENC(vid_enc, Loading @@ -497,6 +522,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) struct sde_hw_intf *intf = vid_enc->hw_intf; struct sde_hw_ctl *ctl = phys_enc->hw_ctl; u32 flush_mask = 0; int ret; if (!phys_enc) { SDE_ERROR("invalid encoder\n"); Loading @@ -518,7 +544,18 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) _sde_encoder_phys_vid_split_config(phys_enc, false); sde_encoder_phys_vid_setup_timing_engine(phys_enc); sde_encoder_phys_vid_control_vblank_irq(phys_enc, true); ret = sde_encoder_phys_vid_control_vblank_irq(phys_enc, true); if (ret) goto end; ret = sde_encoder_phys_vid_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_UNDER_RUN, &vid_enc->irq_idx[INTR_IDX_UNDERRUN], sde_encoder_phys_vid_underrun_irq, "underrun"); if (ret) { sde_encoder_phys_vid_control_vblank_irq(phys_enc, false); goto end; } ctl->ops.get_bitmask_intf(ctl, &flush_mask, intf->idx); ctl->ops.update_pending_flush(ctl, flush_mask); Loading @@ -529,6 +566,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) /* ctl_flush & timing engine enable will be triggered by framework */ if (phys_enc->enable_state == SDE_ENC_DISABLED) phys_enc->enable_state = SDE_ENC_ENABLING; end: return; } static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) Loading Loading @@ -718,7 +758,6 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( ret = -ENOMEM; goto fail; } vid_enc->irq_idx = -EINVAL; init_completion(&vid_enc->vblank_completion); phys_enc = &vid_enc->base; Loading @@ -730,6 +769,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( goto fail; } phys_enc->hw_mdptop = hw_mdp; phys_enc->intf_idx = p->intf_idx; /** * hw_intf resource permanently assigned to this encoder Loading Loading @@ -769,7 +809,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( phys_enc->enable_state = SDE_ENC_DISABLED; SDE_DEBUG_VIDENC(vid_enc, "created\n"); SDE_DEBUG_VIDENC(vid_enc, "created intf idx:%d\n", p->intf_idx); return phys_enc; Loading Loading
drivers/gpu/drm/msm/sde/sde_core_irq.c +1 −36 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 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 Loading Loading @@ -42,11 +42,6 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) irq_idx); } static void sde_core_irq_intf_error_handler(void *arg, int irq_idx) { SDE_ERROR("INTF underrun detected, irq_idx=%d\n", irq_idx); } int sde_core_irq_idx_lookup(struct sde_kms *sde_kms, enum sde_intr_type intr_type, u32 instance_idx) { Loading Loading @@ -191,36 +186,6 @@ void sde_core_irq_preinstall(struct sde_kms *sde_kms) int sde_core_irq_postinstall(struct sde_kms *sde_kms) { struct msm_drm_private *priv; struct sde_irq_callback irq_cb; int irq_idx; int i; if (!sde_kms) { SDE_ERROR("invalid sde_kms\n"); return -EINVAL; } else if (!sde_kms->dev) { SDE_ERROR("invalid drm device\n"); return -EINVAL; } else if (!sde_kms->dev->dev_private) { SDE_ERROR("invalid device private\n"); return -EINVAL; } priv = sde_kms->dev->dev_private; irq_cb.func = sde_core_irq_intf_error_handler; irq_cb.arg = sde_kms; /* Register interface underrun callback */ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); for (i = 0; i < sde_kms->catalog->intf_count; i++) { irq_idx = sde_core_irq_idx_lookup(sde_kms, SDE_IRQ_TYPE_INTF_UNDER_RUN, i+INTF_0); sde_core_irq_register_callback(sde_kms, irq_idx, &irq_cb); sde_core_irq_enable(sde_kms, &irq_idx, 1); } sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); return 0; } Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +23 −3 Original line number Diff line number Diff line Loading @@ -389,6 +389,9 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys) { atomic_set(&phys->vsync_cnt, 0); atomic_set(&phys->underrun_cnt, 0); if (phys->ops.enable) phys->ops.enable(phys); Loading Loading @@ -435,8 +438,12 @@ 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->ops.disable && !phys->ops.is_master(phys)) if (phys && phys->ops.disable && !phys->ops.is_master(phys)) { phys->ops.disable(phys); atomic_set(&phys->vsync_cnt, 0); atomic_set(&phys->underrun_cnt, 0); } } if (sde_enc->cur_master && sde_enc->cur_master->ops.disable) Loading Loading @@ -488,12 +495,13 @@ static enum sde_wb sde_encoder_get_wb(struct sde_mdss_cfg *catalog, return WB_MAX; } static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, struct sde_encoder_phys *phy_enc) { struct sde_encoder_virt *sde_enc = NULL; unsigned long lock_flags; if (!drm_enc) if (!drm_enc || !phy_enc) return; sde_enc = to_sde_encoder_virt(drm_enc); Loading @@ -502,6 +510,17 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) if (sde_enc->crtc_vblank_cb) sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data); spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); atomic_inc(&phy_enc->vsync_cnt); } static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, struct sde_encoder_phys *phy_enc) { if (!phy_enc) return; atomic_inc(&phy_enc->underrun_cnt); } void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, Loading Loading @@ -829,6 +848,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, enum sde_intf_type intf_type; struct sde_encoder_virt_ops parent_ops = { sde_encoder_vblank_callback, sde_encoder_underrun_callback, sde_encoder_handle_phys_enc_ready_for_kickoff }; struct sde_enc_phys_init_params phys_params; Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys.h +30 −5 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017 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 Loading Loading @@ -46,11 +46,16 @@ struct sde_encoder_phys; * 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. * @handle_underrun_virt: Notify virtual encoder of underrun IRQ reception * Note: This is called from IRQ handler context. * @handle_ready_for_kickoff: Notify virtual encoder that this phys encoder * is now ready for the next kickoff. */ struct sde_encoder_virt_ops { void (*handle_vblank_virt)(struct drm_encoder *); void (*handle_vblank_virt)(struct drm_encoder *, struct sde_encoder_phys *phys); void (*handle_underrun_virt)(struct drm_encoder *, struct sde_encoder_phys *phys); void (*handle_ready_for_kickoff)(struct drm_encoder *, struct sde_encoder_phys *phys); }; Loading Loading @@ -122,6 +127,21 @@ enum sde_enc_enable_state { SDE_ENC_ENABLED }; /** * enum sde_intr_idx - sde encoder interrupt index * @INTR_IDX_VSYNC: Vsync interrupt for video mode panel * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel */ enum sde_intr_idx { INTR_IDX_VSYNC, INTR_IDX_PINGPONG, INTR_IDX_UNDERRUN, INTR_IDX_RDPTR, INTR_IDX_MAX, }; /** * struct sde_encoder_phys - physical encoder that drives a single INTF block * tied to a specific panel / sub-panel. Abstract type, sub-classed by Loading @@ -138,10 +158,13 @@ enum sde_enc_enable_state { * @enabled: Whether the encoder has enabled and running a mode * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode * @intf_idx: Interface index on sde hardware * @spin_lock: Lock for IRQ purposes * @mode_3d: 3D mux configuration * @enable_state: Enable state tracking * @vblank_refcount: Reference count of vblank request * @vsync_cnt: Vsync count for the physical encoder * @underrun_cnt: Underrun count for the physical encoder */ struct sde_encoder_phys { struct drm_encoder *parent; Loading @@ -155,10 +178,13 @@ struct sde_encoder_phys { struct drm_display_mode cached_mode; enum sde_enc_split_role split_role; enum sde_intf_mode intf_mode; enum sde_intf intf_idx; spinlock_t spin_lock; enum sde_3d_blend_mode mode_3d; enum sde_enc_enable_state enable_state; atomic_t vblank_refcount; atomic_t vsync_cnt; atomic_t underrun_cnt; }; /** Loading @@ -171,7 +197,7 @@ struct sde_encoder_phys { */ struct sde_encoder_phys_vid { struct sde_encoder_phys base; int irq_idx; int irq_idx[INTR_IDX_MAX]; struct sde_hw_intf *hw_intf; struct completion vblank_completion; }; Loading Loading @@ -199,8 +225,7 @@ struct sde_encoder_phys_cmd { int intf_idx; int stream_sel; struct sde_hw_pingpong *hw_pp; int pp_rd_ptr_irq_idx; int pp_tx_done_irq_idx; int irq_idx[INTR_IDX_MAX]; wait_queue_head_t pp_tx_done_wq; atomic_t pending_cnt; }; Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +61 −23 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017 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 Loading Loading @@ -133,6 +133,7 @@ static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) wake_up_all(&cmd_enc->pp_tx_done_wq); /* Trigger a pending flush */ if (phys_enc->parent_ops.handle_ready_for_kickoff) phys_enc->parent_ops.handle_ready_for_kickoff(phys_enc->parent, phys_enc); } Loading @@ -145,15 +146,28 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx) if (!cmd_enc) return; phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent); if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); } static int sde_encoder_phys_cmd_register_pp_irq( struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx) { struct sde_encoder_phys_cmd *cmd_enc = arg; struct sde_encoder_phys *phys_enc; if (!cmd_enc) return; phys_enc = &cmd_enc->base; if (phys_enc->parent_ops.handle_underrun_virt) phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent, phys_enc); } static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); Loading Loading @@ -208,7 +222,7 @@ static int sde_encoder_phys_cmd_register_pp_irq( return ret; } static int sde_encoder_phys_cmd_unregister_pp_irq( static int sde_encoder_phys_cmd_unregister_irq( struct sde_encoder_phys *phys_enc, int irq_idx) { Loading Loading @@ -380,7 +394,7 @@ static int sde_encoder_phys_cmd_control_vblank_irq( /* Slave encoders don't report vblank */ if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; goto end; SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n", __builtin_return_address(0), Loading @@ -390,15 +404,25 @@ static int sde_encoder_phys_cmd_control_vblank_irq( atomic_read(&phys_enc->vblank_refcount)); if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) ret = sde_encoder_phys_cmd_register_pp_irq(phys_enc, ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_RD_PTR, &cmd_enc->pp_rd_ptr_irq_idx, &cmd_enc->irq_idx[INTR_IDX_PINGPONG], sde_encoder_phys_cmd_pp_rd_ptr_irq, "pp_rd_ptr"); else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) ret = sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_rd_ptr_irq_idx); ret = sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); if (enable) ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_RD_PTR, &cmd_enc->irq_idx[INTR_IDX_RDPTR], sde_encoder_phys_cmd_pp_rd_ptr_irq, "pp_rd_ptr"); else ret = sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_RDPTR]); end: if (ret) SDE_ERROR_CMDENC(cmd_enc, "control vblank irq error %d, enable %d\n", Loading Loading @@ -436,19 +460,30 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) sde_encoder_phys_cmd_pingpong_config(phys_enc); /* Both master and slave need to register for pp_tx_done */ ret = sde_encoder_phys_cmd_register_pp_irq(phys_enc, ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_COMP, &cmd_enc->pp_tx_done_irq_idx, &cmd_enc->irq_idx[INTR_IDX_PINGPONG], sde_encoder_phys_cmd_pp_tx_done_irq, "pp_tx_done"); if (ret) return; ret = sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true); if (ret) { sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_tx_done_irq_idx); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); return; } ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_UNDER_RUN, &cmd_enc->irq_idx[INTR_IDX_UNDERRUN], sde_encoder_phys_cmd_underrun_irq, "underrun"); if (ret) { sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_UNDERRUN]); return; } Loading @@ -475,9 +510,11 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc) if (WARN_ON(phys_enc->enable_state == SDE_ENC_DISABLED)) return; sde_encoder_phys_cmd_unregister_pp_irq(phys_enc, cmd_enc->pp_tx_done_irq_idx); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_UNDERRUN]); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); sde_encoder_phys_cmd_unregister_irq(phys_enc, cmd_enc->irq_idx[INTR_IDX_PINGPONG]); atomic_set(&cmd_enc->pending_cnt, 0); wake_up_all(&cmd_enc->pp_tx_done_wq); Loading Loading @@ -603,6 +640,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( phys_enc->hw_mdptop = hw_mdp; cmd_enc->intf_idx = p->intf_idx; phys_enc->intf_idx = p->intf_idx; sde_encoder_phys_cmd_init_ops(&phys_enc->ops); phys_enc->parent = p->parent; Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +69 −29 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 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 Loading Loading @@ -309,12 +309,28 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) return; phys_enc = &vid_enc->base; phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent); if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); /* signal VBLANK completion */ complete_all(&vid_enc->vblank_completion); } static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = arg; struct sde_encoder_phys *phys_enc; if (!vid_enc) return; phys_enc = &vid_enc->base; if (phys_enc->parent_ops.handle_underrun_virt) phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent, phys_enc); } static bool sde_encoder_phys_vid_needs_split_flush( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -347,7 +363,9 @@ static void _sde_encoder_phys_vid_split_config( } } static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc) static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int *irq_idx, void (*irq_func)(void *, int), const char *irq_name) { struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); Loading @@ -359,59 +377,62 @@ static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc) return -EINVAL; } vid_enc->irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms, SDE_IRQ_TYPE_INTF_VSYNC, vid_enc->hw_intf->idx); if (vid_enc->irq_idx < 0) { *irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms, intr_type, vid_enc->hw_intf->idx); if (*irq_idx < 0) { SDE_ERROR_VIDENC(vid_enc, "failed to lookup IRQ index for INTF_VSYNC\n"); "failed to lookup IRQ index for %s type:%d\n", irq_name, intr_type); return -EINVAL; } irq_cb.func = sde_encoder_phys_vid_vblank_irq; irq_cb.func = irq_func; irq_cb.arg = vid_enc; ret = sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, &irq_cb); ret = sde_core_irq_register_callback(phys_enc->sde_kms, *irq_idx, &irq_cb); if (ret) { SDE_ERROR_VIDENC(vid_enc, "failed to register IRQ callback INTF_VSYNC"); "failed to register IRQ callback for %s\n", irq_name); return ret; } ret = sde_core_irq_enable(phys_enc->sde_kms, &vid_enc->irq_idx, 1); ret = sde_core_irq_enable(phys_enc->sde_kms, irq_idx, true); if (ret) { SDE_ERROR_VIDENC(vid_enc, "enable IRQ for INTF_VSYNC failed, irq_idx %d\n", vid_enc->irq_idx); vid_enc->irq_idx = -EINVAL; "enable IRQ for intr:%s failed, irq_idx %d\n", irq_name, *irq_idx); *irq_idx = -EINVAL; /* Unregister callback on IRQ enable failure */ /* unregister callback on IRQ enable failure */ sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, NULL); *irq_idx, NULL); return ret; } SDE_DEBUG_VIDENC(vid_enc, "registered %d\n", vid_enc->irq_idx); SDE_DEBUG_VIDENC(vid_enc, "registered irq %s idx: %d\n", irq_name, *irq_idx); return ret; } static int sde_encoder_phys_vid_unregister_irq( struct sde_encoder_phys *phys_enc) struct sde_encoder_phys *phys_enc, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); if (!phys_enc) { SDE_ERROR("invalid encoder\n"); return -EINVAL; goto end; } sde_core_irq_register_callback(phys_enc->sde_kms, vid_enc->irq_idx, NULL); sde_core_irq_disable(phys_enc->sde_kms, &vid_enc->irq_idx, 1); sde_core_irq_disable(phys_enc->sde_kms, &irq_idx, 1); sde_core_irq_register_callback(phys_enc->sde_kms, irq_idx, NULL); SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", vid_enc->irq_idx); SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", irq_idx); end: return 0; } Loading Loading @@ -478,9 +499,13 @@ static int sde_encoder_phys_vid_control_vblank_irq( atomic_read(&phys_enc->vblank_refcount)); if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) ret = sde_encoder_phys_vid_register_irq(phys_enc); ret = sde_encoder_phys_vid_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_VSYNC, &vid_enc->irq_idx[INTR_IDX_VSYNC], sde_encoder_phys_vid_vblank_irq, "vsync_irq"); else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) ret = sde_encoder_phys_vid_unregister_irq(phys_enc); ret = sde_encoder_phys_vid_unregister_irq(phys_enc, vid_enc->irq_idx[INTR_IDX_VSYNC]); if (ret) SDE_ERROR_VIDENC(vid_enc, Loading @@ -497,6 +522,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) struct sde_hw_intf *intf = vid_enc->hw_intf; struct sde_hw_ctl *ctl = phys_enc->hw_ctl; u32 flush_mask = 0; int ret; if (!phys_enc) { SDE_ERROR("invalid encoder\n"); Loading @@ -518,7 +544,18 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) _sde_encoder_phys_vid_split_config(phys_enc, false); sde_encoder_phys_vid_setup_timing_engine(phys_enc); sde_encoder_phys_vid_control_vblank_irq(phys_enc, true); ret = sde_encoder_phys_vid_control_vblank_irq(phys_enc, true); if (ret) goto end; ret = sde_encoder_phys_vid_register_irq(phys_enc, SDE_IRQ_TYPE_INTF_UNDER_RUN, &vid_enc->irq_idx[INTR_IDX_UNDERRUN], sde_encoder_phys_vid_underrun_irq, "underrun"); if (ret) { sde_encoder_phys_vid_control_vblank_irq(phys_enc, false); goto end; } ctl->ops.get_bitmask_intf(ctl, &flush_mask, intf->idx); ctl->ops.update_pending_flush(ctl, flush_mask); Loading @@ -529,6 +566,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) /* ctl_flush & timing engine enable will be triggered by framework */ if (phys_enc->enable_state == SDE_ENC_DISABLED) phys_enc->enable_state = SDE_ENC_ENABLING; end: return; } static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) Loading Loading @@ -718,7 +758,6 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( ret = -ENOMEM; goto fail; } vid_enc->irq_idx = -EINVAL; init_completion(&vid_enc->vblank_completion); phys_enc = &vid_enc->base; Loading @@ -730,6 +769,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( goto fail; } phys_enc->hw_mdptop = hw_mdp; phys_enc->intf_idx = p->intf_idx; /** * hw_intf resource permanently assigned to this encoder Loading Loading @@ -769,7 +809,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( phys_enc->enable_state = SDE_ENC_DISABLED; SDE_DEBUG_VIDENC(vid_enc, "created\n"); SDE_DEBUG_VIDENC(vid_enc, "created intf idx:%d\n", p->intf_idx); return phys_enc; Loading