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