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