1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2016 Red Hat 4 * Author: Rob Clark <robdclark@gmail.com> 5 */ 6 7 #include "mdp5_kms.h" 8 9 int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane, 10 uint32_t caps, uint32_t blkcfg, 11 struct mdp5_hw_pipe **hwpipe, 12 struct mdp5_hw_pipe **r_hwpipe) 13 { 14 struct msm_drm_private *priv = s->dev->dev_private; 15 struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); 16 struct mdp5_global_state *new_global_state, *old_global_state; 17 struct mdp5_hw_pipe_state *old_state, *new_state; 18 int i, j; 19 20 new_global_state = mdp5_get_global_state(s); 21 if (IS_ERR(new_global_state)) 22 return PTR_ERR(new_global_state); 23 24 /* grab old_state after mdp5_get_global_state(), since now we hold lock: */ 25 old_global_state = mdp5_get_existing_global_state(mdp5_kms); 26 27 old_state = &old_global_state->hwpipe; 28 new_state = &new_global_state->hwpipe; 29 30 for (i = 0; i < mdp5_kms->num_hwpipes; i++) { 31 struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i]; 32 33 /* skip if already in-use.. check both new and old state, 34 * since we cannot immediately re-use a pipe that is 35 * released in the current update in some cases: 36 * (1) mdp5 can have SMP (non-double-buffered) 37 * (2) hw pipe previously assigned to different CRTC 38 * (vblanks might not be aligned) 39 */ 40 if (new_state->hwpipe_to_plane[cur->idx] || 41 old_state->hwpipe_to_plane[cur->idx]) 42 continue; 43 44 /* skip if doesn't support some required caps: */ 45 if (caps & ~cur->caps) 46 continue; 47 48 /* 49 * don't assign a cursor pipe to a plane that isn't going to 50 * be used as a cursor 51 */ 52 if (cur->caps & MDP_PIPE_CAP_CURSOR && 53 plane->type != DRM_PLANE_TYPE_CURSOR) 54 continue; 55 56 /* possible candidate, take the one with the 57 * fewest unneeded caps bits set: 58 */ 59 if (!(*hwpipe) || (hweight_long(cur->caps & ~caps) < 60 hweight_long((*hwpipe)->caps & ~caps))) { 61 bool r_found = false; 62 63 if (r_hwpipe) { 64 for (j = i + 1; j < mdp5_kms->num_hwpipes; 65 j++) { 66 struct mdp5_hw_pipe *r_cur = 67 mdp5_kms->hwpipes[j]; 68 69 /* reject different types of hwpipes */ 70 if (r_cur->caps != cur->caps) 71 continue; 72 73 /* respect priority, eg. VIG0 > VIG1 */ 74 if (cur->pipe > r_cur->pipe) 75 continue; 76 77 *r_hwpipe = r_cur; 78 r_found = true; 79 break; 80 } 81 } 82 83 if (!r_hwpipe || r_found) 84 *hwpipe = cur; 85 } 86 } 87 88 if (!(*hwpipe)) 89 return -ENOMEM; 90 91 if (r_hwpipe && !(*r_hwpipe)) 92 return -ENOMEM; 93 94 if (mdp5_kms->smp) { 95 int ret; 96 97 /* We don't support SMP and 2 hwpipes/plane together */ 98 WARN_ON(r_hwpipe); 99 100 DBG("%s: alloc SMP blocks", (*hwpipe)->name); 101 ret = mdp5_smp_assign(mdp5_kms->smp, &new_global_state->smp, 102 (*hwpipe)->pipe, blkcfg); 103 if (ret) 104 return -ENOMEM; 105 106 (*hwpipe)->blkcfg = blkcfg; 107 } 108 109 DBG("%s: assign to plane %s for caps %x", 110 (*hwpipe)->name, plane->name, caps); 111 new_state->hwpipe_to_plane[(*hwpipe)->idx] = plane; 112 113 if (r_hwpipe) { 114 DBG("%s: assign to right of plane %s for caps %x", 115 (*r_hwpipe)->name, plane->name, caps); 116 new_state->hwpipe_to_plane[(*r_hwpipe)->idx] = plane; 117 } 118 119 return 0; 120 } 121 122 void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe) 123 { 124 struct msm_drm_private *priv = s->dev->dev_private; 125 struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); 126 struct mdp5_global_state *state = mdp5_get_global_state(s); 127 struct mdp5_hw_pipe_state *new_state = &state->hwpipe; 128 129 if (!hwpipe) 130 return; 131 132 if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx])) 133 return; 134 135 DBG("%s: release from plane %s", hwpipe->name, 136 new_state->hwpipe_to_plane[hwpipe->idx]->name); 137 138 if (mdp5_kms->smp) { 139 DBG("%s: free SMP blocks", hwpipe->name); 140 mdp5_smp_release(mdp5_kms->smp, &state->smp, hwpipe->pipe); 141 } 142 143 new_state->hwpipe_to_plane[hwpipe->idx] = NULL; 144 } 145 146 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe) 147 { 148 kfree(hwpipe); 149 } 150 151 struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe, 152 uint32_t reg_offset, uint32_t caps) 153 { 154 struct mdp5_hw_pipe *hwpipe; 155 156 hwpipe = kzalloc(sizeof(*hwpipe), GFP_KERNEL); 157 if (!hwpipe) 158 return ERR_PTR(-ENOMEM); 159 160 hwpipe->name = pipe2name(pipe); 161 hwpipe->pipe = pipe; 162 hwpipe->reg_offset = reg_offset; 163 hwpipe->caps = caps; 164 hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe); 165 166 return hwpipe; 167 } 168