summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/disp/mdp5/mdp5_mixer.c
blob: 113e6b569562cc2c2a1ae70bf07a205e559277c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * Copyright (C) 2017 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mdp5_kms.h"

/*
 * As of now, there are only 2 combinations possible for source split:
 *
 * Left | Right
 * -----|------
 *  LM0 | LM1
 *  LM2 | LM5
 *
 */
static int lm_right_pair[] = { 1, -1, 5, -1, -1, -1 };

static int get_right_pair_idx(struct mdp5_kms *mdp5_kms, int lm)
{
	int i;
	int pair_lm;

	pair_lm = lm_right_pair[lm];
	if (pair_lm < 0)
		return -EINVAL;

	for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
		struct mdp5_hw_mixer *mixer = mdp5_kms->hwmixers[i];

		if (mixer->lm == pair_lm)
			return mixer->idx;
	}

	return -1;
}

int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc,
		      uint32_t caps, struct mdp5_hw_mixer **mixer,
		      struct mdp5_hw_mixer **r_mixer)
{
	struct msm_drm_private *priv = s->dev->dev_private;
	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
	struct mdp5_global_state *global_state = mdp5_get_global_state(s);
	struct mdp5_hw_mixer_state *new_state;
	int i;

	if (IS_ERR(global_state))
		return PTR_ERR(global_state);

	new_state = &global_state->hwmixer;

	for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
		struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i];

		/*
		 * skip if already in-use by a different CRTC. If there is a
		 * mixer already assigned to this CRTC, it means this call is
		 * a request to get an additional right mixer. Assume that the
		 * existing mixer is the 'left' one, and try to see if we can
		 * get its corresponding 'right' pair.
		 */
		if (new_state->hwmixer_to_crtc[cur->idx] &&
		    new_state->hwmixer_to_crtc[cur->idx] != crtc)
			continue;

		/* skip if doesn't support some required caps: */
		if (caps & ~cur->caps)
			continue;

		if (r_mixer) {
			int pair_idx;

			pair_idx = get_right_pair_idx(mdp5_kms, cur->lm);
			if (pair_idx < 0)
				return -EINVAL;

			if (new_state->hwmixer_to_crtc[pair_idx])
				continue;

			*r_mixer = mdp5_kms->hwmixers[pair_idx];
		}

		/*
		 * prefer a pair-able LM over an unpairable one. We can
		 * switch the CRTC from Normal mode to Source Split mode
		 * without requiring a full modeset if we had already
		 * assigned this CRTC a pair-able LM.
		 *
		 * TODO: There will be assignment sequences which would
		 * result in the CRTC requiring a full modeset, even
		 * if we have the LM resources to prevent it. For a platform
		 * with a few displays, we don't run out of pair-able LMs
		 * so easily. For now, ignore the possibility of requiring
		 * a full modeset.
		 */
		if (!(*mixer) || cur->caps & MDP_LM_CAP_PAIR)
			*mixer = cur;
	}

	if (!(*mixer))
		return -ENOMEM;

	if (r_mixer && !(*r_mixer))
		return -ENOMEM;

	DBG("assigning Layer Mixer %d to crtc %s", (*mixer)->lm, crtc->name);

	new_state->hwmixer_to_crtc[(*mixer)->idx] = crtc;
	if (r_mixer) {
		DBG("assigning Right Layer Mixer %d to crtc %s", (*r_mixer)->lm,
		    crtc->name);
		new_state->hwmixer_to_crtc[(*r_mixer)->idx] = crtc;
	}

	return 0;
}

void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
{
	struct mdp5_global_state *global_state = mdp5_get_global_state(s);
	struct mdp5_hw_mixer_state *new_state = &global_state->hwmixer;

	if (!mixer)
		return;

	if (WARN_ON(!new_state->hwmixer_to_crtc[mixer->idx]))
		return;

	DBG("%s: release from crtc %s", mixer->name,
	    new_state->hwmixer_to_crtc[mixer->idx]->name);

	new_state->hwmixer_to_crtc[mixer->idx] = NULL;
}

void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer)
{
	kfree(mixer);
}

static const char * const mixer_names[] = {
	"LM0", "LM1", "LM2", "LM3", "LM4", "LM5",
};

struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm)
{
	struct mdp5_hw_mixer *mixer;

	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
	if (!mixer)
		return ERR_PTR(-ENOMEM);

	mixer->name = mixer_names[lm->id];
	mixer->lm = lm->id;
	mixer->caps = lm->caps;
	mixer->pp = lm->pp;
	mixer->dspp = lm->dspp;
	mixer->flush_mask = mdp_ctl_flush_mask_lm(lm->id);

	return mixer;
}