Merge branches 'intel_pstate', 'pm-cpufreq' and 'pm-cpufreq-sched'
[sfrench/cifs-2.6.git] / drivers / gpu / drm / msm / mdp / 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 struct mdp5_hw_pipe *mdp5_pipe_assign(struct drm_atomic_state *s,
21                 struct drm_plane *plane, uint32_t caps, uint32_t blkcfg)
22 {
23         struct msm_drm_private *priv = s->dev->dev_private;
24         struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
25         struct mdp5_state *state;
26         struct mdp5_hw_pipe_state *old_state, *new_state;
27         struct mdp5_hw_pipe *hwpipe = NULL;
28         int i;
29
30         state = mdp5_get_state(s);
31         if (IS_ERR(state))
32                 return ERR_CAST(state);
33
34         /* grab old_state after mdp5_get_state(), since now we hold lock: */
35         old_state = &mdp5_kms->state->hwpipe;
36         new_state = &state->hwpipe;
37
38         for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
39                 struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i];
40
41                 /* skip if already in-use.. check both new and old state,
42                  * since we cannot immediately re-use a pipe that is
43                  * released in the current update in some cases:
44                  *  (1) mdp5 can have SMP (non-double-buffered)
45                  *  (2) hw pipe previously assigned to different CRTC
46                  *      (vblanks might not be aligned)
47                  */
48                 if (new_state->hwpipe_to_plane[cur->idx] ||
49                                 old_state->hwpipe_to_plane[cur->idx])
50                         continue;
51
52                 /* skip if doesn't support some required caps: */
53                 if (caps & ~cur->caps)
54                         continue;
55
56                 /*
57                  * don't assign a cursor pipe to a plane that isn't going to
58                  * be used as a cursor
59                  */
60                 if (cur->caps & MDP_PIPE_CAP_CURSOR &&
61                                 plane->type != DRM_PLANE_TYPE_CURSOR)
62                         continue;
63
64                 /* possible candidate, take the one with the
65                  * fewest unneeded caps bits set:
66                  */
67                 if (!hwpipe || (hweight_long(cur->caps & ~caps) <
68                                 hweight_long(hwpipe->caps & ~caps)))
69                         hwpipe = cur;
70         }
71
72         if (!hwpipe)
73                 return ERR_PTR(-ENOMEM);
74
75         if (mdp5_kms->smp) {
76                 int ret;
77
78                 DBG("%s: alloc SMP blocks", hwpipe->name);
79                 ret = mdp5_smp_assign(mdp5_kms->smp, &state->smp,
80                                 hwpipe->pipe, blkcfg);
81                 if (ret)
82                         return ERR_PTR(-ENOMEM);
83
84                 hwpipe->blkcfg = blkcfg;
85         }
86
87         DBG("%s: assign to plane %s for caps %x",
88                         hwpipe->name, plane->name, caps);
89         new_state->hwpipe_to_plane[hwpipe->idx] = plane;
90
91         return hwpipe;
92 }
93
94 void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
95 {
96         struct msm_drm_private *priv = s->dev->dev_private;
97         struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
98         struct mdp5_state *state = mdp5_get_state(s);
99         struct mdp5_hw_pipe_state *new_state = &state->hwpipe;
100
101         if (!hwpipe)
102                 return;
103
104         if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
105                 return;
106
107         DBG("%s: release from plane %s", hwpipe->name,
108                 new_state->hwpipe_to_plane[hwpipe->idx]->name);
109
110         if (mdp5_kms->smp) {
111                 DBG("%s: free SMP blocks", hwpipe->name);
112                 mdp5_smp_release(mdp5_kms->smp, &state->smp, hwpipe->pipe);
113         }
114
115         new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
116 }
117
118 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
119 {
120         kfree(hwpipe);
121 }
122
123 struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
124                 uint32_t reg_offset, uint32_t caps)
125 {
126         struct mdp5_hw_pipe *hwpipe;
127
128         hwpipe = kzalloc(sizeof(*hwpipe), GFP_KERNEL);
129         if (!hwpipe)
130                 return ERR_PTR(-ENOMEM);
131
132         hwpipe->name = pipe2name(pipe);
133         hwpipe->pipe = pipe;
134         hwpipe->reg_offset = reg_offset;
135         hwpipe->caps = caps;
136         hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
137
138         return hwpipe;
139 }