GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / gpu / drm / msm / disp / mdp5 / mdp5_pipe.c
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 int 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;
138         struct mdp5_hw_pipe_state *new_state;
139
140         if (!hwpipe)
141                 return 0;
142
143         state = mdp5_get_global_state(s);
144         if (IS_ERR(state))
145                 return PTR_ERR(state);
146
147         new_state = &state->hwpipe;
148
149         if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
150                 return -EINVAL;
151
152         DBG("%s: release from plane %s", hwpipe->name,
153                 new_state->hwpipe_to_plane[hwpipe->idx]->name);
154
155         if (mdp5_kms->smp) {
156                 DBG("%s: free SMP blocks", hwpipe->name);
157                 mdp5_smp_release(mdp5_kms->smp, &state->smp, hwpipe->pipe);
158         }
159
160         new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
161
162         return 0;
163 }
164
165 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
166 {
167         kfree(hwpipe);
168 }
169
170 struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
171                 uint32_t reg_offset, uint32_t caps)
172 {
173         struct mdp5_hw_pipe *hwpipe;
174
175         hwpipe = kzalloc(sizeof(*hwpipe), GFP_KERNEL);
176         if (!hwpipe)
177                 return ERR_PTR(-ENOMEM);
178
179         hwpipe->name = pipe2name(pipe);
180         hwpipe->pipe = pipe;
181         hwpipe->reg_offset = reg_offset;
182         hwpipe->caps = caps;
183         hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
184
185         return hwpipe;
186 }