From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 1387 +++++++++++---------------------------------------------- 1 files changed, 280 insertions(+), 1,107 deletions(-) diff --git a/kernel/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/kernel/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 3c3b7f7..4c64e2d 100644 --- a/kernel/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/kernel/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1,39 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include <linux/sort.h> #include <linux/debugfs.h> #include <linux/ktime.h> -#include <drm/drm_mode.h> +#include <linux/bits.h> + #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_flip_work.h> +#include <drm/drm_mode.h> +#include <drm/drm_probe_helper.h> #include <drm/drm_rect.h> +#include <drm/drm_vblank.h> #include "dpu_kms.h" #include "dpu_hw_lm.h" #include "dpu_hw_ctl.h" +#include "dpu_hw_dspp.h" #include "dpu_crtc.h" #include "dpu_plane.h" #include "dpu_encoder.h" #include "dpu_vbif.h" -#include "dpu_power_handle.h" #include "dpu_core_perf.h" #include "dpu_trace.h" @@ -47,237 +39,17 @@ #define LEFT_MIXER 0 #define RIGHT_MIXER 1 -#define MISR_BUFF_SIZE 256 +/* timeout in ms waiting for frame done */ +#define DPU_CRTC_FRAME_DONE_TIMEOUT_MS 60 -static inline struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc) +#define CONVERT_S3_15(val) \ + (((((u64)val) & ~BIT_ULL(63)) >> 17) & GENMASK_ULL(17, 0)) + +static struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc) { - struct msm_drm_private *priv; - - if (!crtc || !crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid crtc\n"); - return NULL; - } - priv = crtc->dev->dev_private; - if (!priv || !priv->kms) { - DPU_ERROR("invalid kms\n"); - return NULL; - } + struct msm_drm_private *priv = crtc->dev->dev_private; return to_dpu_kms(priv->kms); -} - -static inline int _dpu_crtc_power_enable(struct dpu_crtc *dpu_crtc, bool enable) -{ - struct drm_crtc *crtc; - struct msm_drm_private *priv; - struct dpu_kms *dpu_kms; - - if (!dpu_crtc) { - DPU_ERROR("invalid dpu crtc\n"); - return -EINVAL; - } - - crtc = &dpu_crtc->base; - if (!crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid drm device\n"); - return -EINVAL; - } - - priv = crtc->dev->dev_private; - if (!priv->kms) { - DPU_ERROR("invalid kms\n"); - return -EINVAL; - } - - dpu_kms = to_dpu_kms(priv->kms); - - if (enable) - pm_runtime_get_sync(&dpu_kms->pdev->dev); - else - pm_runtime_put_sync(&dpu_kms->pdev->dev); - - return 0; -} - -/** - * _dpu_crtc_rp_to_crtc - get crtc from resource pool object - * @rp: Pointer to resource pool - * return: Pointer to drm crtc if success; null otherwise - */ -static struct drm_crtc *_dpu_crtc_rp_to_crtc(struct dpu_crtc_respool *rp) -{ - if (!rp) - return NULL; - - return container_of(rp, struct dpu_crtc_state, rp)->base.crtc; -} - -/** - * _dpu_crtc_rp_reclaim - reclaim unused, or all if forced, resources in pool - * @rp: Pointer to resource pool - * @force: True to reclaim all resources; otherwise, reclaim only unused ones - * return: None - */ -static void _dpu_crtc_rp_reclaim(struct dpu_crtc_respool *rp, bool force) -{ - struct dpu_crtc_res *res, *next; - struct drm_crtc *crtc; - - crtc = _dpu_crtc_rp_to_crtc(rp); - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return; - } - - DPU_DEBUG("crtc%d.%u %s\n", crtc->base.id, rp->sequence_id, - force ? "destroy" : "free_unused"); - - list_for_each_entry_safe(res, next, &rp->res_list, list) { - if (!force && !(res->flags & DPU_CRTC_RES_FLAG_FREE)) - continue; - DPU_DEBUG("crtc%d.%u reclaim res:0x%x/0x%llx/%pK/%d\n", - crtc->base.id, rp->sequence_id, - res->type, res->tag, res->val, - atomic_read(&res->refcount)); - list_del(&res->list); - if (res->ops.put) - res->ops.put(res->val); - kfree(res); - } -} - -/** - * _dpu_crtc_rp_free_unused - free unused resource in pool - * @rp: Pointer to resource pool - * return: none - */ -static void _dpu_crtc_rp_free_unused(struct dpu_crtc_respool *rp) -{ - mutex_lock(rp->rp_lock); - _dpu_crtc_rp_reclaim(rp, false); - mutex_unlock(rp->rp_lock); -} - -/** - * _dpu_crtc_rp_destroy - destroy resource pool - * @rp: Pointer to resource pool - * return: None - */ -static void _dpu_crtc_rp_destroy(struct dpu_crtc_respool *rp) -{ - mutex_lock(rp->rp_lock); - list_del_init(&rp->rp_list); - _dpu_crtc_rp_reclaim(rp, true); - mutex_unlock(rp->rp_lock); -} - -/** - * _dpu_crtc_hw_blk_get - get callback for hardware block - * @val: Resource handle - * @type: Resource type - * @tag: Search tag for given resource - * return: Resource handle - */ -static void *_dpu_crtc_hw_blk_get(void *val, u32 type, u64 tag) -{ - DPU_DEBUG("res:%d/0x%llx/%pK\n", type, tag, val); - return dpu_hw_blk_get(val, type, tag); -} - -/** - * _dpu_crtc_hw_blk_put - put callback for hardware block - * @val: Resource handle - * return: None - */ -static void _dpu_crtc_hw_blk_put(void *val) -{ - DPU_DEBUG("res://%pK\n", val); - dpu_hw_blk_put(val); -} - -/** - * _dpu_crtc_rp_duplicate - duplicate resource pool and reset reference count - * @rp: Pointer to original resource pool - * @dup_rp: Pointer to duplicated resource pool - * return: None - */ -static void _dpu_crtc_rp_duplicate(struct dpu_crtc_respool *rp, - struct dpu_crtc_respool *dup_rp) -{ - struct dpu_crtc_res *res, *dup_res; - struct drm_crtc *crtc; - - if (!rp || !dup_rp || !rp->rp_head) { - DPU_ERROR("invalid resource pool\n"); - return; - } - - crtc = _dpu_crtc_rp_to_crtc(rp); - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return; - } - - DPU_DEBUG("crtc%d.%u duplicate\n", crtc->base.id, rp->sequence_id); - - mutex_lock(rp->rp_lock); - dup_rp->sequence_id = rp->sequence_id + 1; - INIT_LIST_HEAD(&dup_rp->res_list); - dup_rp->ops = rp->ops; - list_for_each_entry(res, &rp->res_list, list) { - dup_res = kzalloc(sizeof(struct dpu_crtc_res), GFP_KERNEL); - if (!dup_res) { - mutex_unlock(rp->rp_lock); - return; - } - INIT_LIST_HEAD(&dup_res->list); - atomic_set(&dup_res->refcount, 0); - dup_res->type = res->type; - dup_res->tag = res->tag; - dup_res->val = res->val; - dup_res->ops = res->ops; - dup_res->flags = DPU_CRTC_RES_FLAG_FREE; - DPU_DEBUG("crtc%d.%u dup res:0x%x/0x%llx/%pK/%d\n", - crtc->base.id, dup_rp->sequence_id, - dup_res->type, dup_res->tag, dup_res->val, - atomic_read(&dup_res->refcount)); - list_add_tail(&dup_res->list, &dup_rp->res_list); - if (dup_res->ops.get) - dup_res->ops.get(dup_res->val, 0, -1); - } - - dup_rp->rp_lock = rp->rp_lock; - dup_rp->rp_head = rp->rp_head; - INIT_LIST_HEAD(&dup_rp->rp_list); - list_add_tail(&dup_rp->rp_list, rp->rp_head); - mutex_unlock(rp->rp_lock); -} - -/** - * _dpu_crtc_rp_reset - reset resource pool after allocation - * @rp: Pointer to original resource pool - * @rp_lock: Pointer to serialization resource pool lock - * @rp_head: Pointer to crtc resource pool head - * return: None - */ -static void _dpu_crtc_rp_reset(struct dpu_crtc_respool *rp, - struct mutex *rp_lock, struct list_head *rp_head) -{ - if (!rp || !rp_lock || !rp_head) { - DPU_ERROR("invalid resource pool\n"); - return; - } - - mutex_lock(rp_lock); - rp->rp_lock = rp_lock; - rp->rp_head = rp_head; - INIT_LIST_HEAD(&rp->rp_list); - rp->sequence_id = 0; - INIT_LIST_HEAD(&rp->res_list); - rp->ops.get = _dpu_crtc_hw_blk_get; - rp->ops.put = _dpu_crtc_hw_blk_put; - list_add_tail(&rp->rp_list, rp->rp_head); - mutex_unlock(rp_lock); } static void dpu_crtc_destroy(struct drm_crtc *crtc) @@ -289,37 +61,47 @@ if (!crtc) return; - dpu_crtc->phandle = NULL; - drm_crtc_cleanup(crtc); - mutex_destroy(&dpu_crtc->crtc_lock); kfree(dpu_crtc); } static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, - struct dpu_plane_state *pstate) + struct dpu_plane_state *pstate, struct dpu_format *format) { struct dpu_hw_mixer *lm = mixer->hw_lm; + uint32_t blend_op; + struct drm_format_name_buf format_name; /* default to opaque blending */ - lm->ops.setup_blend_config(lm, pstate->stage, 0XFF, 0, - DPU_BLEND_FG_ALPHA_FG_CONST | - DPU_BLEND_BG_ALPHA_BG_CONST); + blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | + DPU_BLEND_BG_ALPHA_BG_CONST; + + if (format->alpha_enable) { + /* coverage blending */ + blend_op = DPU_BLEND_FG_ALPHA_FG_PIXEL | + DPU_BLEND_BG_ALPHA_FG_PIXEL | + DPU_BLEND_BG_INV_ALPHA; + } + + lm->ops.setup_blend_config(lm, pstate->stage, + 0xFF, 0, blend_op); + + DPU_DEBUG("format:%s, alpha_en:%u blend_op:0x%x\n", + drm_get_format_name(format->base.pixel_format, &format_name), + format->alpha_enable, blend_op); } static void _dpu_crtc_program_lm_output_roi(struct drm_crtc *crtc) { - struct dpu_crtc *dpu_crtc; struct dpu_crtc_state *crtc_state; int lm_idx, lm_horiz_position; - dpu_crtc = to_dpu_crtc(crtc); crtc_state = to_dpu_crtc_state(crtc->state); lm_horiz_position = 0; - for (lm_idx = 0; lm_idx < dpu_crtc->num_mixers; lm_idx++) { + for (lm_idx = 0; lm_idx < crtc_state->num_mixers; lm_idx++) { const struct drm_rect *lm_roi = &crtc_state->lm_bounds[lm_idx]; - struct dpu_hw_mixer *hw_lm = dpu_crtc->mixers[lm_idx].hw_lm; + struct dpu_hw_mixer *hw_lm = crtc_state->mixers[lm_idx].hw_lm; struct dpu_hw_mixer_cfg cfg; if (!lm_roi || !drm_rect_visible(lm_roi)) @@ -339,27 +121,16 @@ struct drm_plane *plane; struct drm_framebuffer *fb; struct drm_plane_state *state; - struct dpu_crtc_state *cstate; + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); struct dpu_plane_state *pstate = NULL; struct dpu_format *format; - struct dpu_hw_ctl *ctl; - struct dpu_hw_mixer *lm; - struct dpu_hw_stage_cfg *stage_cfg; + struct dpu_hw_ctl *ctl = mixer->lm_ctl; + struct dpu_hw_stage_cfg *stage_cfg = &dpu_crtc->stage_cfg; u32 flush_mask; uint32_t stage_idx, lm_idx; int zpos_cnt[DPU_STAGE_MAX + 1] = { 0 }; bool bg_alpha_enable = false; - - if (!dpu_crtc || !mixer) { - DPU_ERROR("invalid dpu_crtc or mixer\n"); - return; - } - - ctl = mixer->hw_ctl; - lm = mixer->hw_lm; - stage_cfg = &dpu_crtc->stage_cfg; - cstate = to_dpu_crtc_state(crtc->state); drm_atomic_crtc_for_each_plane(plane, crtc) { state = plane->state; @@ -379,10 +150,6 @@ state->fb ? state->fb->base.id : -1); format = to_dpu_format(msm_framebuffer_format(pstate->base.fb)); - if (!format) { - DPU_ERROR("invalid format\n"); - return; - } if (pstate->stage == DPU_STAGE_BASE && format->alpha_enable) bg_alpha_enable = true; @@ -400,8 +167,9 @@ fb ? fb->modifier : 0); /* blend config update */ - for (lm_idx = 0; lm_idx < dpu_crtc->num_mixers; lm_idx++) { - _dpu_crtc_setup_blend_cfg(mixer + lm_idx, pstate); + for (lm_idx = 0; lm_idx < cstate->num_mixers; lm_idx++) { + _dpu_crtc_setup_blend_cfg(mixer + lm_idx, + pstate, format); mixer[lm_idx].flush_mask |= flush_mask; @@ -422,38 +190,21 @@ */ static void _dpu_crtc_blend_setup(struct drm_crtc *crtc) { - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *dpu_crtc_state; - struct dpu_crtc_mixer *mixer; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); + struct dpu_crtc_mixer *mixer = cstate->mixers; struct dpu_hw_ctl *ctl; struct dpu_hw_mixer *lm; - int i; - - if (!crtc) - return; - - dpu_crtc = to_dpu_crtc(crtc); - dpu_crtc_state = to_dpu_crtc_state(crtc->state); - mixer = dpu_crtc->mixers; DPU_DEBUG("%s\n", dpu_crtc->name); - if (dpu_crtc->num_mixers > CRTC_DUAL_MIXERS) { - DPU_ERROR("invalid number mixers: %d\n", dpu_crtc->num_mixers); - return; - } - - for (i = 0; i < dpu_crtc->num_mixers; i++) { - if (!mixer[i].hw_lm || !mixer[i].hw_ctl) { - DPU_ERROR("invalid lm or ctl assigned to mixer\n"); - return; - } + for (i = 0; i < cstate->num_mixers; i++) { mixer[i].mixer_op_mode = 0; mixer[i].flush_mask = 0; - if (mixer[i].hw_ctl->ops.clear_all_blendstages) - mixer[i].hw_ctl->ops.clear_all_blendstages( - mixer[i].hw_ctl); + if (mixer[i].lm_ctl->ops.clear_all_blendstages) + mixer[i].lm_ctl->ops.clear_all_blendstages( + mixer[i].lm_ctl); } /* initialize stage cfg */ @@ -461,8 +212,8 @@ _dpu_crtc_blend_setup_mixer(crtc, dpu_crtc, mixer); - for (i = 0; i < dpu_crtc->num_mixers; i++) { - ctl = mixer[i].hw_ctl; + for (i = 0; i < cstate->num_mixers; i++) { + ctl = mixer[i].lm_ctl; lm = mixer[i].hw_lm; lm->ops.setup_alpha_out(lm, mixer[i].mixer_op_mode); @@ -514,21 +265,26 @@ { struct drm_encoder *encoder; - if (!crtc || !crtc->dev) { - DPU_ERROR("invalid crtc\n"); - return INTF_MODE_NONE; - } + /* + * TODO: This function is called from dpu debugfs and as part of atomic + * check. When called from debugfs, the crtc->mutex must be held to + * read crtc->state. However reading crtc->state from atomic check isn't + * allowed (unless you have a good reason, a big comment, and a deep + * understanding of how the atomic/modeset locks work (<- and this is + * probably not possible)). So we'll keep the WARN_ON here for now, but + * really we need to figure out a better way to track our operating mode + */ + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - drm_for_each_encoder(encoder, crtc->dev) - if (encoder->crtc == crtc) - return dpu_encoder_get_intf_mode(encoder); + /* TODO: Returns the first INTF_MODE, could there be multiple values? */ + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) + return dpu_encoder_get_intf_mode(encoder); return INTF_MODE_NONE; } -static void dpu_crtc_vblank_cb(void *data) +void dpu_crtc_vblank_callback(struct drm_crtc *crtc) { - struct drm_crtc *crtc = (struct drm_crtc *)data; struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); /* keep statistics on vblank callback - with auto reset via debugfs */ @@ -536,41 +292,19 @@ dpu_crtc->vblank_cb_time = ktime_get(); else dpu_crtc->vblank_cb_count++; - _dpu_crtc_complete_flip(crtc); drm_crtc_handle_vblank(crtc); trace_dpu_crtc_vblank_cb(DRMID(crtc)); } static void dpu_crtc_frame_event_work(struct kthread_work *work) { - struct msm_drm_private *priv; - struct dpu_crtc_frame_event *fevent; - struct drm_crtc *crtc; - struct dpu_crtc *dpu_crtc; - struct dpu_kms *dpu_kms; + struct dpu_crtc_frame_event *fevent = container_of(work, + struct dpu_crtc_frame_event, work); + struct drm_crtc *crtc = fevent->crtc; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); unsigned long flags; bool frame_done = false; - if (!work) { - DPU_ERROR("invalid work handle\n"); - return; - } - - fevent = container_of(work, struct dpu_crtc_frame_event, work); - if (!fevent->crtc || !fevent->crtc->state) { - DPU_ERROR("invalid crtc\n"); - return; - } - - crtc = fevent->crtc; - dpu_crtc = to_dpu_crtc(crtc); - - dpu_kms = _dpu_crtc_get_kms(crtc); - if (!dpu_kms) { - DPU_ERROR("invalid kms handle\n"); - return; - } - priv = dpu_kms->dev->dev_private; DPU_ATRACE_BEGIN("crtc_frame_event"); DRM_DEBUG_KMS("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event, @@ -581,12 +315,7 @@ | DPU_ENCODER_FRAME_EVENT_PANEL_DEAD)) { if (atomic_read(&dpu_crtc->frame_pending) < 1) { - /* this should not happen */ - DRM_ERROR("crtc%d ev:%u ts:%lld frame_pending:%d\n", - crtc->base.id, - fevent->event, - ktime_to_ns(fevent->ts), - atomic_read(&dpu_crtc->frame_pending)); + /* ignore vblank when not pending */ } else if (atomic_dec_return(&dpu_crtc->frame_pending) == 0) { /* release bandwidth and other resources */ trace_dpu_crtc_frame_event_done(DRMID(crtc), @@ -636,11 +365,6 @@ unsigned long flags; u32 crtc_id; - if (!crtc || !crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid parameters\n"); - return; - } - /* Nothing to do on idle event */ if (event & DPU_ENCODER_FRAME_EVENT_IDLE) return; @@ -666,136 +390,105 @@ fevent->event = event; fevent->crtc = crtc; fevent->ts = ktime_get(); - kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work); + kthread_queue_work(priv->event_thread[crtc_id].worker, &fevent->work); } -void dpu_crtc_complete_commit(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) +void dpu_crtc_complete_commit(struct drm_crtc *crtc) { - if (!crtc || !crtc->state) { - DPU_ERROR("invalid crtc\n"); - return; - } trace_dpu_crtc_complete_commit(DRMID(crtc)); -} - -static void _dpu_crtc_setup_mixer_for_encoder( - struct drm_crtc *crtc, - struct drm_encoder *enc) -{ - struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); - struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); - struct dpu_rm *rm = &dpu_kms->rm; - struct dpu_crtc_mixer *mixer; - struct dpu_hw_ctl *last_valid_ctl = NULL; - int i; - struct dpu_rm_hw_iter lm_iter, ctl_iter; - - dpu_rm_init_hw_iter(&lm_iter, enc->base.id, DPU_HW_BLK_LM); - dpu_rm_init_hw_iter(&ctl_iter, enc->base.id, DPU_HW_BLK_CTL); - - /* Set up all the mixers and ctls reserved by this encoder */ - for (i = dpu_crtc->num_mixers; i < ARRAY_SIZE(dpu_crtc->mixers); i++) { - mixer = &dpu_crtc->mixers[i]; - - if (!dpu_rm_get_hw(rm, &lm_iter)) - break; - mixer->hw_lm = (struct dpu_hw_mixer *)lm_iter.hw; - - /* CTL may be <= LMs, if <, multiple LMs controlled by 1 CTL */ - if (!dpu_rm_get_hw(rm, &ctl_iter)) { - DPU_DEBUG("no ctl assigned to lm %d, using previous\n", - mixer->hw_lm->idx - LM_0); - mixer->hw_ctl = last_valid_ctl; - } else { - mixer->hw_ctl = (struct dpu_hw_ctl *)ctl_iter.hw; - last_valid_ctl = mixer->hw_ctl; - } - - /* Shouldn't happen, mixers are always >= ctls */ - if (!mixer->hw_ctl) { - DPU_ERROR("no valid ctls found for lm %d\n", - mixer->hw_lm->idx - LM_0); - return; - } - - mixer->encoder = enc; - - dpu_crtc->num_mixers++; - DPU_DEBUG("setup mixer %d: lm %d\n", - i, mixer->hw_lm->idx - LM_0); - DPU_DEBUG("setup mixer %d: ctl %d\n", - i, mixer->hw_ctl->idx - CTL_0); - } -} - -static void _dpu_crtc_setup_mixers(struct drm_crtc *crtc) -{ - struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); - struct drm_encoder *enc; - - dpu_crtc->num_mixers = 0; - dpu_crtc->mixers_swapped = false; - memset(dpu_crtc->mixers, 0, sizeof(dpu_crtc->mixers)); - - mutex_lock(&dpu_crtc->crtc_lock); - /* Check for mixers on all encoders attached to this crtc */ - list_for_each_entry(enc, &crtc->dev->mode_config.encoder_list, head) { - if (enc->crtc != crtc) - continue; - - _dpu_crtc_setup_mixer_for_encoder(crtc, enc); - } - - mutex_unlock(&dpu_crtc->crtc_lock); + _dpu_crtc_complete_flip(crtc); } static void _dpu_crtc_setup_lm_bounds(struct drm_crtc *crtc, struct drm_crtc_state *state) { - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *cstate; - struct drm_display_mode *adj_mode; - u32 crtc_split_width; + struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); + struct drm_display_mode *adj_mode = &state->adjusted_mode; + u32 crtc_split_width = adj_mode->hdisplay / cstate->num_mixers; int i; - if (!crtc || !state) { - DPU_ERROR("invalid args\n"); - return; - } - - dpu_crtc = to_dpu_crtc(crtc); - cstate = to_dpu_crtc_state(state); - - adj_mode = &state->adjusted_mode; - crtc_split_width = dpu_crtc_get_mixer_width(dpu_crtc, cstate, adj_mode); - - for (i = 0; i < dpu_crtc->num_mixers; i++) { + for (i = 0; i < cstate->num_mixers; i++) { struct drm_rect *r = &cstate->lm_bounds[i]; r->x1 = crtc_split_width * i; r->y1 = 0; r->x2 = r->x1 + crtc_split_width; - r->y2 = dpu_crtc_get_mixer_height(dpu_crtc, cstate, adj_mode); + r->y2 = adj_mode->vdisplay; trace_dpu_crtc_setup_lm_bounds(DRMID(crtc), i, r); } +} - drm_mode_debug_printmodeline(adj_mode); +static void _dpu_crtc_get_pcc_coeff(struct drm_crtc_state *state, + struct dpu_hw_pcc_cfg *cfg) +{ + struct drm_color_ctm *ctm; + + memset(cfg, 0, sizeof(struct dpu_hw_pcc_cfg)); + + ctm = (struct drm_color_ctm *)state->ctm->data; + + if (!ctm) + return; + + cfg->r.r = CONVERT_S3_15(ctm->matrix[0]); + cfg->g.r = CONVERT_S3_15(ctm->matrix[1]); + cfg->b.r = CONVERT_S3_15(ctm->matrix[2]); + + cfg->r.g = CONVERT_S3_15(ctm->matrix[3]); + cfg->g.g = CONVERT_S3_15(ctm->matrix[4]); + cfg->b.g = CONVERT_S3_15(ctm->matrix[5]); + + cfg->r.b = CONVERT_S3_15(ctm->matrix[6]); + cfg->g.b = CONVERT_S3_15(ctm->matrix[7]); + cfg->b.b = CONVERT_S3_15(ctm->matrix[8]); +} + +static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state = crtc->state; + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); + struct dpu_crtc_mixer *mixer = cstate->mixers; + struct dpu_hw_pcc_cfg cfg; + struct dpu_hw_ctl *ctl; + struct dpu_hw_dspp *dspp; + int i; + + + if (!state->color_mgmt_changed) + return; + + for (i = 0; i < cstate->num_mixers; i++) { + ctl = mixer[i].lm_ctl; + dspp = mixer[i].hw_dspp; + + if (!dspp || !dspp->ops.setup_pcc) + continue; + + if (!state->ctm) { + dspp->ops.setup_pcc(dspp, NULL); + } else { + _dpu_crtc_get_pcc_coeff(state, &cfg); + dspp->ops.setup_pcc(dspp, &cfg); + } + + mixer[i].flush_mask |= ctl->ops.get_bitmask_dspp(ctl, + mixer[i].hw_dspp->idx); + + /* stage config flush mask */ + ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask); + + DPU_DEBUG("lm %d, ctl %d, flush mask 0x%x\n", + mixer[i].hw_lm->idx - DSPP_0, + ctl->idx - CTL_0, + mixer[i].flush_mask); + } } static void dpu_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { - struct dpu_crtc *dpu_crtc; + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); struct drm_encoder *encoder; - struct drm_device *dev; - unsigned long flags; - struct dpu_crtc_smmu_state_data *smmu_state; - - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return; - } if (!crtc->state->enable) { DPU_DEBUG("crtc%d -> enable %d, skip atomic_begin\n", @@ -805,41 +498,23 @@ DPU_DEBUG("crtc%d\n", crtc->base.id); - dpu_crtc = to_dpu_crtc(crtc); - dev = crtc->dev; - smmu_state = &dpu_crtc->smmu_state; + _dpu_crtc_setup_lm_bounds(crtc, crtc->state); - if (!dpu_crtc->num_mixers) { - _dpu_crtc_setup_mixers(crtc); - _dpu_crtc_setup_lm_bounds(crtc, crtc->state); - } - - if (dpu_crtc->event) { - WARN_ON(dpu_crtc->event); - } else { - spin_lock_irqsave(&dev->event_lock, flags); - dpu_crtc->event = crtc->state->event; - crtc->state->event = NULL; - spin_unlock_irqrestore(&dev->event_lock, flags); - } - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; - - /* encoder will trigger pending mask now */ + /* encoder will trigger pending mask now */ + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) dpu_encoder_trigger_kickoff_pending(encoder); - } /* * If no mixers have been allocated in dpu_crtc_atomic_check(), * it means we are trying to flush a CRTC whose state is disabled: * nothing else needs to be done. */ - if (unlikely(!dpu_crtc->num_mixers)) + if (unlikely(!cstate->num_mixers)) return; _dpu_crtc_blend_setup(crtc); + + _dpu_crtc_setup_cp_blocks(crtc); /* * PP_DONE irq is only used by command mode for now. @@ -857,14 +532,8 @@ struct drm_device *dev; struct drm_plane *plane; struct msm_drm_private *priv; - struct msm_drm_thread *event_thread; unsigned long flags; struct dpu_crtc_state *cstate; - - if (!crtc || !crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid crtc\n"); - return; - } if (!crtc->state->enable) { DPU_DEBUG("crtc%d -> enable %d, skip atomic_flush\n", @@ -884,23 +553,18 @@ return; } - event_thread = &priv->event_thread[crtc->index]; - - if (dpu_crtc->event) { - DPU_DEBUG("already received dpu_crtc->event\n"); - } else { - spin_lock_irqsave(&dev->event_lock, flags); - dpu_crtc->event = crtc->state->event; - crtc->state->event = NULL; - spin_unlock_irqrestore(&dev->event_lock, flags); - } + WARN_ON(dpu_crtc->event); + spin_lock_irqsave(&dev->event_lock, flags); + dpu_crtc->event = crtc->state->event; + crtc->state->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); /* * If no mixers has been allocated in dpu_crtc_atomic_check(), * it means we are trying to flush a CRTC whose state is disabled: * nothing else needs to be done. */ - if (unlikely(!dpu_crtc->num_mixers)) + if (unlikely(!cstate->num_mixers)) return; /* @@ -938,20 +602,9 @@ static void dpu_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *cstate; - - if (!crtc || !state) { - DPU_ERROR("invalid argument(s)\n"); - return; - } - - dpu_crtc = to_dpu_crtc(crtc); - cstate = to_dpu_crtc_state(state); + struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); DPU_DEBUG("crtc%d\n", crtc->base.id); - - _dpu_crtc_rp_destroy(&cstate->rp); __drm_atomic_helper_crtc_destroy_state(state); @@ -960,14 +613,8 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) { - struct dpu_crtc *dpu_crtc; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); int ret, rc = 0; - - if (!crtc) { - DPU_ERROR("invalid argument\n"); - return -EINVAL; - } - dpu_crtc = to_dpu_crtc(crtc); if (!atomic_read(&dpu_crtc->frame_pending)) { DPU_DEBUG("no frames pending\n"); @@ -976,7 +623,7 @@ DPU_ATRACE_BEGIN("frame done completion wait"); ret = wait_for_completion_timeout(&dpu_crtc->frame_done_comp, - msecs_to_jiffies(DPU_FRAME_DONE_TIMEOUT)); + msecs_to_jiffies(DPU_CRTC_FRAME_DONE_TIMEOUT_MS)); if (!ret) { DRM_ERROR("frame done wait timed out, ret:%d\n", ret); rc = -ETIMEDOUT; @@ -989,62 +636,27 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) { struct drm_encoder *encoder; - struct drm_device *dev; - struct dpu_crtc *dpu_crtc; - struct msm_drm_private *priv; - struct dpu_kms *dpu_kms; - struct dpu_crtc_state *cstate; - int ret; - - if (!crtc) { - DPU_ERROR("invalid argument\n"); - return; - } - dev = crtc->dev; - dpu_crtc = to_dpu_crtc(crtc); - dpu_kms = _dpu_crtc_get_kms(crtc); - - if (!dpu_kms || !dpu_kms->dev || !dpu_kms->dev->dev_private) { - DPU_ERROR("invalid argument\n"); - return; - } - - priv = dpu_kms->dev->dev_private; - cstate = to_dpu_crtc_state(crtc->state); + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); /* * If no mixers has been allocated in dpu_crtc_atomic_check(), * it means we are trying to start a CRTC whose state is disabled: * nothing else needs to be done. */ - if (unlikely(!dpu_crtc->num_mixers)) + if (unlikely(!cstate->num_mixers)) return; DPU_ATRACE_BEGIN("crtc_commit"); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct dpu_encoder_kickoff_params params = { 0 }; - - if (encoder->crtc != crtc) - continue; - - /* - * Encoder will flush/start now, unless it has a tx pending. - * If so, it may delay and flush at an irq event (e.g. ppdone) - */ - dpu_encoder_prepare_for_kickoff(encoder, ¶ms); - } - - /* wait for frame_event_done completion */ - DPU_ATRACE_BEGIN("wait_for_frame_done_event"); - ret = _dpu_crtc_wait_for_frame_done(crtc); - DPU_ATRACE_END("wait_for_frame_done_event"); - if (ret) { - DPU_ERROR("crtc%d wait for frame done failed;frame_pending%d\n", - crtc->base.id, - atomic_read(&dpu_crtc->frame_pending)); - goto end; - } + /* + * Encoder will flush/start now, unless it has a tx pending. If so, it + * may delay and flush at an irq event (e.g. ppdone) + */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) + dpu_encoder_prepare_for_kickoff(encoder); if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) { /* acquire bandwidth and other resources */ @@ -1056,147 +668,34 @@ dpu_vbif_clear_errors(dpu_kms); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; - + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) dpu_encoder_kickoff(encoder); - } -end: reinit_completion(&dpu_crtc->frame_done_comp); DPU_ATRACE_END("crtc_commit"); } -/** - * _dpu_crtc_vblank_enable_no_lock - update power resource and vblank request - * @dpu_crtc: Pointer to dpu crtc structure - * @enable: Whether to enable/disable vblanks - * - * @Return: error code - */ -static int _dpu_crtc_vblank_enable_no_lock( - struct dpu_crtc *dpu_crtc, bool enable) +static void dpu_crtc_reset(struct drm_crtc *crtc) { - struct drm_device *dev; - struct drm_crtc *crtc; - struct drm_encoder *enc; + struct dpu_crtc_state *cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); - if (!dpu_crtc) { - DPU_ERROR("invalid crtc\n"); - return -EINVAL; - } + if (crtc->state) + dpu_crtc_destroy_state(crtc, crtc->state); - crtc = &dpu_crtc->base; - dev = crtc->dev; - - if (enable) { - int ret; - - /* drop lock since power crtc cb may try to re-acquire lock */ - mutex_unlock(&dpu_crtc->crtc_lock); - ret = _dpu_crtc_power_enable(dpu_crtc, true); - mutex_lock(&dpu_crtc->crtc_lock); - if (ret) - return ret; - - list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { - if (enc->crtc != crtc) - continue; - - trace_dpu_crtc_vblank_enable(DRMID(&dpu_crtc->base), - DRMID(enc), enable, - dpu_crtc); - - dpu_encoder_register_vblank_callback(enc, - dpu_crtc_vblank_cb, (void *)crtc); - } - } else { - list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { - if (enc->crtc != crtc) - continue; - - trace_dpu_crtc_vblank_enable(DRMID(&dpu_crtc->base), - DRMID(enc), enable, - dpu_crtc); - - dpu_encoder_register_vblank_callback(enc, NULL, NULL); - } - - /* drop lock since power crtc cb may try to re-acquire lock */ - mutex_unlock(&dpu_crtc->crtc_lock); - _dpu_crtc_power_enable(dpu_crtc, false); - mutex_lock(&dpu_crtc->crtc_lock); - } - - return 0; -} - -/** - * _dpu_crtc_set_suspend - notify crtc of suspend enable/disable - * @crtc: Pointer to drm crtc object - * @enable: true to enable suspend, false to indicate resume - */ -static void _dpu_crtc_set_suspend(struct drm_crtc *crtc, bool enable) -{ - struct dpu_crtc *dpu_crtc; - struct msm_drm_private *priv; - struct dpu_kms *dpu_kms; - int ret = 0; - - if (!crtc || !crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid crtc\n"); - return; - } - dpu_crtc = to_dpu_crtc(crtc); - priv = crtc->dev->dev_private; - - if (!priv->kms) { - DPU_ERROR("invalid crtc kms\n"); - return; - } - dpu_kms = to_dpu_kms(priv->kms); - - DRM_DEBUG_KMS("crtc%d suspend = %d\n", crtc->base.id, enable); - - mutex_lock(&dpu_crtc->crtc_lock); - - /* - * If the vblank is enabled, release a power reference on suspend - * and take it back during resume (if it is still enabled). - */ - trace_dpu_crtc_set_suspend(DRMID(&dpu_crtc->base), enable, dpu_crtc); - if (dpu_crtc->suspend == enable) - DPU_DEBUG("crtc%d suspend already set to %d, ignoring update\n", - crtc->base.id, enable); - else if (dpu_crtc->enabled && dpu_crtc->vblank_requested) { - ret = _dpu_crtc_vblank_enable_no_lock(dpu_crtc, !enable); - if (ret) - DPU_ERROR("%s vblank enable failed: %d\n", - dpu_crtc->name, ret); - } - - dpu_crtc->suspend = enable; - mutex_unlock(&dpu_crtc->crtc_lock); + if (cstate) + __drm_atomic_helper_crtc_reset(crtc, &cstate->base); + else + __drm_atomic_helper_crtc_reset(crtc, NULL); } /** * dpu_crtc_duplicate_state - state duplicate hook * @crtc: Pointer to drm crtc structure - * @Returns: Pointer to new drm_crtc_state structure */ static struct drm_crtc_state *dpu_crtc_duplicate_state(struct drm_crtc *crtc) { - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *cstate, *old_cstate; + struct dpu_crtc_state *cstate, *old_cstate = to_dpu_crtc_state(crtc->state); - if (!crtc || !crtc->state) { - DPU_ERROR("invalid argument(s)\n"); - return NULL; - } - - dpu_crtc = to_dpu_crtc(crtc); - old_cstate = to_dpu_crtc_state(crtc->state); cstate = kmemdup(old_cstate, sizeof(*old_cstate), GFP_KERNEL); if (!cstate) { DPU_ERROR("failed to allocate state\n"); @@ -1206,143 +705,33 @@ /* duplicate base helper */ __drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base); - _dpu_crtc_rp_duplicate(&old_cstate->rp, &cstate->rp); - return &cstate->base; } -/** - * dpu_crtc_reset - reset hook for CRTCs - * Resets the atomic state for @crtc by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - * @crtc: Pointer to drm crtc structure - */ -static void dpu_crtc_reset(struct drm_crtc *crtc) +static void dpu_crtc_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *cstate; - - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return; - } - - /* revert suspend actions, if necessary */ - if (dpu_kms_is_suspend_state(crtc->dev)) - _dpu_crtc_set_suspend(crtc, false); - - /* remove previous state, if present */ - if (crtc->state) { - dpu_crtc_destroy_state(crtc, crtc->state); - crtc->state = 0; - } - - dpu_crtc = to_dpu_crtc(crtc); - cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); - if (!cstate) { - DPU_ERROR("failed to allocate state\n"); - return; - } - - _dpu_crtc_rp_reset(&cstate->rp, &dpu_crtc->rp_lock, - &dpu_crtc->rp_head); - - cstate->base.crtc = crtc; - crtc->state = &cstate->base; -} - -static void dpu_crtc_handle_power_event(u32 event_type, void *arg) -{ - struct drm_crtc *crtc = arg; - struct dpu_crtc *dpu_crtc; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); struct drm_encoder *encoder; - struct dpu_crtc_mixer *m; - u32 i, misr_status; - - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return; - } - dpu_crtc = to_dpu_crtc(crtc); - - mutex_lock(&dpu_crtc->crtc_lock); - - trace_dpu_crtc_handle_power_event(DRMID(crtc), event_type); - - switch (event_type) { - case DPU_POWER_EVENT_POST_ENABLE: - /* restore encoder; crtc will be programmed during commit */ - drm_for_each_encoder(encoder, crtc->dev) { - if (encoder->crtc != crtc) - continue; - - dpu_encoder_virt_restore(encoder); - } - - for (i = 0; i < dpu_crtc->num_mixers; ++i) { - m = &dpu_crtc->mixers[i]; - if (!m->hw_lm || !m->hw_lm->ops.setup_misr || - !dpu_crtc->misr_enable) - continue; - - m->hw_lm->ops.setup_misr(m->hw_lm, true, - dpu_crtc->misr_frame_count); - } - break; - case DPU_POWER_EVENT_PRE_DISABLE: - for (i = 0; i < dpu_crtc->num_mixers; ++i) { - m = &dpu_crtc->mixers[i]; - if (!m->hw_lm || !m->hw_lm->ops.collect_misr || - !dpu_crtc->misr_enable) - continue; - - misr_status = m->hw_lm->ops.collect_misr(m->hw_lm); - dpu_crtc->misr_data[i] = misr_status ? misr_status : - dpu_crtc->misr_data[i]; - } - break; - case DPU_POWER_EVENT_POST_DISABLE: - /** - * Nothing to do. All the planes on the CRTC will be - * programmed for every frame - */ - break; - default: - DPU_DEBUG("event:%d not handled\n", event_type); - break; - } - - mutex_unlock(&dpu_crtc->crtc_lock); -} - -static void dpu_crtc_disable(struct drm_crtc *crtc) -{ - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_state *cstate; - struct drm_display_mode *mode; - struct drm_encoder *encoder; - struct msm_drm_private *priv; - int ret; unsigned long flags; - - if (!crtc || !crtc->dev || !crtc->dev->dev_private || !crtc->state) { - DPU_ERROR("invalid crtc\n"); - return; - } - dpu_crtc = to_dpu_crtc(crtc); - cstate = to_dpu_crtc_state(crtc->state); - mode = &cstate->base.adjusted_mode; - priv = crtc->dev->dev_private; + bool release_bandwidth = false; DRM_DEBUG_KMS("crtc%d\n", crtc->base.id); - - if (dpu_kms_is_suspend_state(crtc->dev)) - _dpu_crtc_set_suspend(crtc, true); /* Disable/save vblank irq handling */ drm_crtc_vblank_off(crtc); - mutex_lock(&dpu_crtc->crtc_lock); + drm_for_each_encoder_mask(encoder, crtc->dev, + old_crtc_state->encoder_mask) { + /* in video mode, we hold an extra bandwidth reference + * as we cannot drop bandwidth at frame-done if any + * crtc is being used in video mode. + */ + if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) + release_bandwidth = true; + dpu_encoder_assign_crtc(encoder, NULL); + } /* wait for frame_event_done completion */ if (_dpu_crtc_wait_for_frame_done(crtc)) @@ -1351,43 +740,27 @@ atomic_read(&dpu_crtc->frame_pending)); trace_dpu_crtc_disable(DRMID(crtc), false, dpu_crtc); - if (dpu_crtc->enabled && !dpu_crtc->suspend && - dpu_crtc->vblank_requested) { - ret = _dpu_crtc_vblank_enable_no_lock(dpu_crtc, false); - if (ret) - DPU_ERROR("%s vblank enable failed: %d\n", - dpu_crtc->name, ret); - } dpu_crtc->enabled = false; if (atomic_read(&dpu_crtc->frame_pending)) { trace_dpu_crtc_disable_frame_pending(DRMID(crtc), atomic_read(&dpu_crtc->frame_pending)); - dpu_core_perf_crtc_release_bw(crtc); + if (release_bandwidth) + dpu_core_perf_crtc_release_bw(crtc); atomic_set(&dpu_crtc->frame_pending, 0); } dpu_core_perf_crtc_update(crtc, 0, true); - drm_for_each_encoder(encoder, crtc->dev) { - if (encoder->crtc != crtc) - continue; + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) dpu_encoder_register_frame_event_callback(encoder, NULL, NULL); - } - if (dpu_crtc->power_event) - dpu_power_handle_unregister_event(dpu_crtc->phandle, - dpu_crtc->power_event); - - memset(dpu_crtc->mixers, 0, sizeof(dpu_crtc->mixers)); - dpu_crtc->num_mixers = 0; - dpu_crtc->mixers_swapped = false; + memset(cstate->mixers, 0, sizeof(cstate->mixers)); + cstate->num_mixers = 0; /* disable clk & bw control until clk & bw properties are set */ cstate->bw_control = false; cstate->bw_split_vote = false; - - mutex_unlock(&dpu_crtc->crtc_lock); if (crtc->state->event && !crtc->state->active) { spin_lock_irqsave(&crtc->dev->event_lock, flags); @@ -1395,54 +768,43 @@ crtc->state->event = NULL; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } + + pm_runtime_put_sync(crtc->dev->dev); } static void dpu_crtc_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { - struct dpu_crtc *dpu_crtc; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); struct drm_encoder *encoder; - struct msm_drm_private *priv; - int ret; + bool request_bandwidth = false; - if (!crtc || !crtc->dev || !crtc->dev->dev_private) { - DPU_ERROR("invalid crtc\n"); - return; - } - priv = crtc->dev->dev_private; + pm_runtime_get_sync(crtc->dev->dev); DRM_DEBUG_KMS("crtc%d\n", crtc->base.id); - dpu_crtc = to_dpu_crtc(crtc); - drm_for_each_encoder(encoder, crtc->dev) { - if (encoder->crtc != crtc) - continue; + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { + /* in video mode, we hold an extra bandwidth reference + * as we cannot drop bandwidth at frame-done if any + * crtc is being used in video mode. + */ + if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) + request_bandwidth = true; dpu_encoder_register_frame_event_callback(encoder, dpu_crtc_frame_event_cb, (void *)crtc); } - mutex_lock(&dpu_crtc->crtc_lock); + if (request_bandwidth) + atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref); + trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc); - if (!dpu_crtc->enabled && !dpu_crtc->suspend && - dpu_crtc->vblank_requested) { - ret = _dpu_crtc_vblank_enable_no_lock(dpu_crtc, true); - if (ret) - DPU_ERROR("%s vblank enable failed: %d\n", - dpu_crtc->name, ret); - } dpu_crtc->enabled = true; - mutex_unlock(&dpu_crtc->crtc_lock); + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) + dpu_encoder_assign_crtc(encoder, crtc); /* Enable/restore vblank irq handling */ drm_crtc_vblank_on(crtc); - - dpu_crtc->power_event = dpu_power_handle_register_event( - dpu_crtc->phandle, - DPU_POWER_EVENT_POST_ENABLE | DPU_POWER_EVENT_POST_DISABLE | - DPU_POWER_EVENT_PRE_DISABLE, - dpu_crtc_handle_power_event, crtc, dpu_crtc->name); - } struct plane_state { @@ -1455,15 +817,15 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { - struct dpu_crtc *dpu_crtc; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); struct plane_state *pstates; - struct dpu_crtc_state *cstate; const struct drm_plane_state *pstate; struct drm_plane *plane; struct drm_display_mode *mode; - int cnt = 0, rc = 0, mixer_width, i, z_pos; + int cnt = 0, rc = 0, mixer_width = 0, i, z_pos; struct dpu_multirect_plane_states multirect_plane[DPU_STAGE_MAX * 2]; int multirect_count = 0; @@ -1471,15 +833,9 @@ int left_zpos_cnt = 0, right_zpos_cnt = 0; struct drm_rect crtc_rect = { 0 }; - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return -EINVAL; - } - pstates = kzalloc(sizeof(*pstates) * DPU_STAGE_MAX * 4, GFP_KERNEL); - - dpu_crtc = to_dpu_crtc(crtc); - cstate = to_dpu_crtc_state(state); + if (!pstates) + return -ENOMEM; if (!state->enable || !state->active) { DPU_DEBUG("crtc%d -> enable %d, active %d, skip atomic_check\n", @@ -1496,9 +852,11 @@ memset(pipe_staged, 0, sizeof(pipe_staged)); - mixer_width = dpu_crtc_get_mixer_width(dpu_crtc, cstate, mode); + if (cstate->num_mixers) { + mixer_width = mode->hdisplay / cstate->num_mixers; - _dpu_crtc_setup_lm_bounds(crtc, state); + _dpu_crtc_setup_lm_bounds(crtc, state); + } crtc_rect.x2 = mode->hdisplay; crtc_rect.y2 = mode->vdisplay; @@ -1608,6 +966,8 @@ } } + atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref); + rc = dpu_core_perf_crtc_check(crtc, state); if (rc) { DPU_ERROR("crtc%d failed performance check %d\n", @@ -1678,32 +1038,38 @@ } end: - _dpu_crtc_rp_free_unused(&cstate->rp); kfree(pstates); return rc; } int dpu_crtc_vblank(struct drm_crtc *crtc, bool en) { - struct dpu_crtc *dpu_crtc; - int ret; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + struct drm_encoder *enc; - if (!crtc) { - DPU_ERROR("invalid crtc\n"); - return -EINVAL; - } - dpu_crtc = to_dpu_crtc(crtc); - - mutex_lock(&dpu_crtc->crtc_lock); trace_dpu_crtc_vblank(DRMID(&dpu_crtc->base), en, dpu_crtc); - if (dpu_crtc->enabled && !dpu_crtc->suspend) { - ret = _dpu_crtc_vblank_enable_no_lock(dpu_crtc, en); - if (ret) - DPU_ERROR("%s vblank enable failed: %d\n", - dpu_crtc->name, ret); + + /* + * Normally we would iterate through encoder_mask in crtc state to find + * attached encoders. In this case, we might be disabling vblank _after_ + * encoder_mask has been cleared. + * + * Instead, we "assign" a crtc to the encoder in enable and clear it in + * disable (which is also after encoder_mask is cleared). So instead of + * using encoder mask, we'll ask the encoder to toggle itself iff it's + * currently assigned to our crtc. + * + * Note also that this function cannot be called while crtc is disabled + * since we use drm_crtc_vblank_on/off. So we don't need to worry + * about the assigned crtcs being inconsistent with the current state + * (which means no need to worry about modeset locks). + */ + list_for_each_entry(enc, &crtc->dev->mode_config.encoder_list, head) { + trace_dpu_crtc_vblank_enable(DRMID(crtc), DRMID(enc), en, + dpu_crtc); + + dpu_encoder_toggle_vblank_for_crtc(enc, crtc, en); } - dpu_crtc->vblank_requested = en; - mutex_unlock(&dpu_crtc->crtc_lock); return 0; } @@ -1724,32 +1090,25 @@ int i, out_width; - if (!s || !s->private) - return -EINVAL; - dpu_crtc = s->private; crtc = &dpu_crtc->base; + + drm_modeset_lock_all(crtc->dev); cstate = to_dpu_crtc_state(crtc->state); - mutex_lock(&dpu_crtc->crtc_lock); mode = &crtc->state->adjusted_mode; - out_width = dpu_crtc_get_mixer_width(dpu_crtc, cstate, mode); + out_width = mode->hdisplay / cstate->num_mixers; seq_printf(s, "crtc:%d width:%d height:%d\n", crtc->base.id, mode->hdisplay, mode->vdisplay); seq_puts(s, "\n"); - for (i = 0; i < dpu_crtc->num_mixers; ++i) { - m = &dpu_crtc->mixers[i]; - if (!m->hw_lm) - seq_printf(s, "\tmixer[%d] has no lm\n", i); - else if (!m->hw_ctl) - seq_printf(s, "\tmixer[%d] has no ctl\n", i); - else - seq_printf(s, "\tmixer:%d ctl:%d width:%d height:%d\n", - m->hw_lm->idx - LM_0, m->hw_ctl->idx - CTL_0, - out_width, mode->vdisplay); + for (i = 0; i < cstate->num_mixers; ++i) { + m = &cstate->mixers[i]; + seq_printf(s, "\tmixer:%d ctl:%d width:%d height:%d\n", + m->hw_lm->idx - LM_0, m->lm_ctl->idx - CTL_0, + out_width, mode->vdisplay); } seq_puts(s, "\n"); @@ -1818,236 +1177,51 @@ dpu_crtc->vblank_cb_time = ktime_set(0, 0); } - seq_printf(s, "vblank_enable:%d\n", dpu_crtc->vblank_requested); - - mutex_unlock(&dpu_crtc->crtc_lock); + drm_modeset_unlock_all(crtc->dev); return 0; } -static int _dpu_debugfs_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, _dpu_debugfs_status_show, inode->i_private); -} - -static ssize_t _dpu_crtc_misr_setup(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_mixer *m; - int i = 0, rc; - char buf[MISR_BUFF_SIZE + 1]; - u32 frame_count, enable; - size_t buff_copy; - - if (!file || !file->private_data) - return -EINVAL; - - dpu_crtc = file->private_data; - buff_copy = min_t(size_t, count, MISR_BUFF_SIZE); - if (copy_from_user(buf, user_buf, buff_copy)) { - DPU_ERROR("buffer copy failed\n"); - return -EINVAL; - } - - buf[buff_copy] = 0; /* end of string */ - - if (sscanf(buf, "%u %u", &enable, &frame_count) != 2) - return -EINVAL; - - rc = _dpu_crtc_power_enable(dpu_crtc, true); - if (rc) - return rc; - - mutex_lock(&dpu_crtc->crtc_lock); - dpu_crtc->misr_enable = enable; - dpu_crtc->misr_frame_count = frame_count; - for (i = 0; i < dpu_crtc->num_mixers; ++i) { - dpu_crtc->misr_data[i] = 0; - m = &dpu_crtc->mixers[i]; - if (!m->hw_lm || !m->hw_lm->ops.setup_misr) - continue; - - m->hw_lm->ops.setup_misr(m->hw_lm, enable, frame_count); - } - mutex_unlock(&dpu_crtc->crtc_lock); - _dpu_crtc_power_enable(dpu_crtc, false); - - return count; -} - -static ssize_t _dpu_crtc_misr_read(struct file *file, - char __user *user_buff, size_t count, loff_t *ppos) -{ - struct dpu_crtc *dpu_crtc; - struct dpu_crtc_mixer *m; - int i = 0, rc; - u32 misr_status; - ssize_t len = 0; - char buf[MISR_BUFF_SIZE + 1] = {'\0'}; - - if (*ppos) - return 0; - - if (!file || !file->private_data) - return -EINVAL; - - dpu_crtc = file->private_data; - rc = _dpu_crtc_power_enable(dpu_crtc, true); - if (rc) - return rc; - - mutex_lock(&dpu_crtc->crtc_lock); - if (!dpu_crtc->misr_enable) { - len += snprintf(buf + len, MISR_BUFF_SIZE - len, - "disabled\n"); - goto buff_check; - } - - for (i = 0; i < dpu_crtc->num_mixers; ++i) { - m = &dpu_crtc->mixers[i]; - if (!m->hw_lm || !m->hw_lm->ops.collect_misr) - continue; - - misr_status = m->hw_lm->ops.collect_misr(m->hw_lm); - dpu_crtc->misr_data[i] = misr_status ? misr_status : - dpu_crtc->misr_data[i]; - len += snprintf(buf + len, MISR_BUFF_SIZE - len, "lm idx:%d\n", - m->hw_lm->idx - LM_0); - len += snprintf(buf + len, MISR_BUFF_SIZE - len, "0x%x\n", - dpu_crtc->misr_data[i]); - } - -buff_check: - if (count <= len) { - len = 0; - goto end; - } - - if (copy_to_user(user_buff, buf, len)) { - len = -EFAULT; - goto end; - } - - *ppos += len; /* increase offset */ - -end: - mutex_unlock(&dpu_crtc->crtc_lock); - _dpu_crtc_power_enable(dpu_crtc, false); - return len; -} - -#define DEFINE_DPU_DEBUGFS_SEQ_FOPS(__prefix) \ -static int __prefix ## _open(struct inode *inode, struct file *file) \ -{ \ - return single_open(file, __prefix ## _show, inode->i_private); \ -} \ -static const struct file_operations __prefix ## _fops = { \ - .owner = THIS_MODULE, \ - .open = __prefix ## _open, \ - .release = single_release, \ - .read = seq_read, \ - .llseek = seq_lseek, \ -} +DEFINE_SHOW_ATTRIBUTE(_dpu_debugfs_status); static int dpu_crtc_debugfs_state_show(struct seq_file *s, void *v) { struct drm_crtc *crtc = (struct drm_crtc *) s->private; struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); - struct dpu_crtc_res *res; - struct dpu_crtc_respool *rp; - int i; seq_printf(s, "client type: %d\n", dpu_crtc_get_client_type(crtc)); seq_printf(s, "intf_mode: %d\n", dpu_crtc_get_intf_mode(crtc)); seq_printf(s, "core_clk_rate: %llu\n", dpu_crtc->cur_perf.core_clk_rate); - for (i = DPU_POWER_HANDLE_DBUS_ID_MNOC; - i < DPU_POWER_HANDLE_DBUS_ID_MAX; i++) { - seq_printf(s, "bw_ctl[%s]: %llu\n", - dpu_power_handle_get_dbus_name(i), - dpu_crtc->cur_perf.bw_ctl[i]); - seq_printf(s, "max_per_pipe_ib[%s]: %llu\n", - dpu_power_handle_get_dbus_name(i), - dpu_crtc->cur_perf.max_per_pipe_ib[i]); - } - - mutex_lock(&dpu_crtc->rp_lock); - list_for_each_entry(rp, &dpu_crtc->rp_head, rp_list) { - seq_printf(s, "rp.%d: ", rp->sequence_id); - list_for_each_entry(res, &rp->res_list, list) - seq_printf(s, "0x%x/0x%llx/%pK/%d ", - res->type, res->tag, res->val, - atomic_read(&res->refcount)); - seq_puts(s, "\n"); - } - mutex_unlock(&dpu_crtc->rp_lock); + seq_printf(s, "bw_ctl: %llu\n", dpu_crtc->cur_perf.bw_ctl); + seq_printf(s, "max_per_pipe_ib: %llu\n", + dpu_crtc->cur_perf.max_per_pipe_ib); return 0; } -DEFINE_DPU_DEBUGFS_SEQ_FOPS(dpu_crtc_debugfs_state); +DEFINE_SHOW_ATTRIBUTE(dpu_crtc_debugfs_state); static int _dpu_crtc_init_debugfs(struct drm_crtc *crtc) { - struct dpu_crtc *dpu_crtc; - struct dpu_kms *dpu_kms; - - static const struct file_operations debugfs_status_fops = { - .open = _dpu_debugfs_status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - }; - static const struct file_operations debugfs_misr_fops = { - .open = simple_open, - .read = _dpu_crtc_misr_read, - .write = _dpu_crtc_misr_setup, - }; - - if (!crtc) - return -EINVAL; - dpu_crtc = to_dpu_crtc(crtc); - - dpu_kms = _dpu_crtc_get_kms(crtc); - if (!dpu_kms) - return -EINVAL; + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); dpu_crtc->debugfs_root = debugfs_create_dir(dpu_crtc->name, crtc->dev->primary->debugfs_root); - if (!dpu_crtc->debugfs_root) - return -ENOMEM; - /* don't error check these */ debugfs_create_file("status", 0400, dpu_crtc->debugfs_root, - dpu_crtc, &debugfs_status_fops); + dpu_crtc, &_dpu_debugfs_status_fops); debugfs_create_file("state", 0600, dpu_crtc->debugfs_root, &dpu_crtc->base, &dpu_crtc_debugfs_state_fops); - debugfs_create_file("misr_data", 0600, dpu_crtc->debugfs_root, - dpu_crtc, &debugfs_misr_fops); return 0; -} - -static void _dpu_crtc_destroy_debugfs(struct drm_crtc *crtc) -{ - struct dpu_crtc *dpu_crtc; - - if (!crtc) - return; - dpu_crtc = to_dpu_crtc(crtc); - debugfs_remove_recursive(dpu_crtc->debugfs_root); } #else static int _dpu_crtc_init_debugfs(struct drm_crtc *crtc) { return 0; -} - -static void _dpu_crtc_destroy_debugfs(struct drm_crtc *crtc) -{ } #endif /* CONFIG_DEBUG_FS */ @@ -2058,7 +1232,9 @@ static void dpu_crtc_early_unregister(struct drm_crtc *crtc) { - _dpu_crtc_destroy_debugfs(crtc); + struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); + + debugfs_remove_recursive(dpu_crtc->debugfs_root); } static const struct drm_crtc_funcs dpu_crtc_funcs = { @@ -2070,10 +1246,12 @@ .atomic_destroy_state = dpu_crtc_destroy_state, .late_register = dpu_crtc_late_register, .early_unregister = dpu_crtc_early_unregister, + .enable_vblank = msm_crtc_enable_vblank, + .disable_vblank = msm_crtc_disable_vblank, }; static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = { - .disable = dpu_crtc_disable, + .atomic_disable = dpu_crtc_disable, .atomic_enable = dpu_crtc_enable, .atomic_check = dpu_crtc_atomic_check, .atomic_begin = dpu_crtc_atomic_begin, @@ -2081,16 +1259,14 @@ }; /* initialize crtc */ -struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane) +struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane, + struct drm_plane *cursor) { + struct msm_drm_private *priv = dev->dev_private; + struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); struct drm_crtc *crtc = NULL; struct dpu_crtc *dpu_crtc = NULL; - struct msm_drm_private *priv = NULL; - struct dpu_kms *kms = NULL; int i; - - priv = dev->dev_private; - kms = to_dpu_kms(priv->kms); dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL); if (!dpu_crtc) @@ -2099,12 +1275,8 @@ crtc = &dpu_crtc->base; crtc->dev = dev; - mutex_init(&dpu_crtc->crtc_lock); spin_lock_init(&dpu_crtc->spin_lock); atomic_set(&dpu_crtc->frame_pending, 0); - - mutex_init(&dpu_crtc->rp_lock); - INIT_LIST_HEAD(&dpu_crtc->rp_head); init_completion(&dpu_crtc->frame_done_comp); @@ -2118,18 +1290,19 @@ dpu_crtc_frame_event_work); } - drm_crtc_init_with_planes(dev, crtc, plane, NULL, &dpu_crtc_funcs, + drm_crtc_init_with_planes(dev, crtc, plane, cursor, &dpu_crtc_funcs, NULL); drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs); + + if (dpu_kms->catalog->dspp_count) + drm_crtc_enable_color_mgmt(crtc, 0, true, 0); /* save user friendly CRTC name for later */ snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id); /* initialize event handling */ spin_lock_init(&dpu_crtc->event_lock); - - dpu_crtc->phandle = &kms->phandle; DPU_DEBUG("%s: successfully initialized crtc\n", dpu_crtc->name); return crtc; -- Gitblit v1.6.2