]>
Commit | Line | Data |
---|---|---|
7a014a87 BS |
1 | /* |
2 | * Copyright 2014 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
878da15a | 24 | #include "outp.h" |
01a97637 | 25 | #include "ior.h" |
7a014a87 | 26 | |
7a014a87 | 27 | #include <subdev/bios.h> |
878da15a BS |
28 | #include <subdev/bios/dcb.h> |
29 | #include <subdev/i2c.h> | |
7a014a87 | 30 | |
6c22ea37 BS |
31 | void |
32 | nvkm_outp_route(struct nvkm_disp *disp) | |
33 | { | |
34 | struct nvkm_outp *outp; | |
35 | struct nvkm_ior *ior; | |
36 | ||
37 | list_for_each_entry(ior, &disp->ior, head) { | |
38 | if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) { | |
39 | OUTP_DBG(outp, "release %s", ior->name); | |
40 | if (ior->func->route.set) | |
41 | ior->func->route.set(outp, NULL); | |
42 | ior->arm.outp = NULL; | |
43 | } | |
44 | } | |
45 | ||
46 | list_for_each_entry(ior, &disp->ior, head) { | |
47 | if ((outp = ior->asy.outp)) { | |
48 | OUTP_DBG(outp, "acquire %s", ior->name); | |
49 | if (ior->asy.outp != ior->arm.outp) { | |
50 | if (ior->func->route.set) | |
51 | ior->func->route.set(outp, ior); | |
52 | ior->arm.outp = ior->asy.outp; | |
53 | } | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
01a97637 | 58 | static enum nvkm_ior_proto |
6c22ea37 | 59 | nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) |
01a97637 BS |
60 | { |
61 | switch (outp->info.location) { | |
62 | case 0: | |
63 | switch (outp->info.type) { | |
64 | case DCB_OUTPUT_ANALOG: *type = DAC; return CRT; | |
587f577e | 65 | case DCB_OUTPUT_TV : *type = DAC; return TV; |
01a97637 BS |
66 | case DCB_OUTPUT_TMDS : *type = SOR; return TMDS; |
67 | case DCB_OUTPUT_LVDS : *type = SOR; return LVDS; | |
68 | case DCB_OUTPUT_DP : *type = SOR; return DP; | |
69 | default: | |
70 | break; | |
71 | } | |
72 | break; | |
73 | case 1: | |
74 | switch (outp->info.type) { | |
75 | case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS; | |
76 | case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */ | |
77 | default: | |
78 | break; | |
79 | } | |
80 | break; | |
81 | default: | |
82 | break; | |
83 | } | |
84 | WARN_ON(1); | |
85 | return UNKNOWN; | |
86 | } | |
87 | ||
6c22ea37 BS |
88 | void |
89 | nvkm_outp_release(struct nvkm_outp *outp, u8 user) | |
90 | { | |
91 | struct nvkm_ior *ior = outp->ior; | |
92 | OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); | |
93 | if (ior) { | |
94 | outp->acquired &= ~user; | |
95 | if (!outp->acquired) { | |
96 | outp->ior->asy.outp = NULL; | |
97 | outp->ior = NULL; | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | static inline int | |
103 | nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) | |
104 | { | |
105 | outp->ior = ior; | |
106 | outp->ior->asy.outp = outp; | |
107 | outp->ior->asy.link = outp->info.sorconf.link; | |
108 | outp->acquired |= user; | |
109 | return 0; | |
110 | } | |
111 | ||
112 | int | |
113 | nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) | |
114 | { | |
115 | struct nvkm_ior *ior = outp->ior; | |
116 | enum nvkm_ior_proto proto; | |
117 | enum nvkm_ior_type type; | |
118 | ||
119 | OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior); | |
120 | if (ior) { | |
121 | outp->acquired |= user; | |
122 | return 0; | |
123 | } | |
124 | ||
125 | /* Lookup a compatible, and unused, OR to assign to the device. */ | |
126 | proto = nvkm_outp_xlat(outp, &type); | |
127 | if (proto == UNKNOWN) | |
128 | return -ENOSYS; | |
129 | ||
130 | /* First preference is to reuse the OR that is currently armed | |
131 | * on HW, if any, in order to prevent unnecessary switching. | |
132 | */ | |
133 | list_for_each_entry(ior, &outp->disp->ior, head) { | |
134 | if (!ior->asy.outp && ior->arm.outp == outp) | |
135 | return nvkm_outp_acquire_ior(outp, user, ior); | |
136 | } | |
137 | ||
138 | /* Failing that, a completely unused OR is the next best thing. */ | |
139 | list_for_each_entry(ior, &outp->disp->ior, head) { | |
140 | if (!ior->asy.outp && ior->type == type && !ior->arm.outp && | |
2863204c | 141 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) |
6c22ea37 BS |
142 | return nvkm_outp_acquire_ior(outp, user, ior); |
143 | } | |
144 | ||
145 | /* Last resort is to assign an OR that's already active on HW, | |
146 | * but will be released during the next modeset. | |
147 | */ | |
148 | list_for_each_entry(ior, &outp->disp->ior, head) { | |
149 | if (!ior->asy.outp && ior->type == type && | |
2863204c | 150 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) |
6c22ea37 BS |
151 | return nvkm_outp_acquire_ior(outp, user, ior); |
152 | } | |
153 | ||
154 | return -ENOSPC; | |
155 | } | |
156 | ||
f2c906fc | 157 | void |
d7ce92e2 | 158 | nvkm_outp_fini(struct nvkm_outp *outp) |
7a014a87 | 159 | { |
f2c906fc BS |
160 | if (outp->func->fini) |
161 | outp->func->fini(outp); | |
7a014a87 BS |
162 | } |
163 | ||
01a97637 | 164 | static void |
6c22ea37 | 165 | nvkm_outp_init_route(struct nvkm_outp *outp) |
01a97637 BS |
166 | { |
167 | struct nvkm_disp *disp = outp->disp; | |
168 | enum nvkm_ior_proto proto; | |
169 | enum nvkm_ior_type type; | |
170 | struct nvkm_ior *ior; | |
6c22ea37 | 171 | int id, link; |
01a97637 | 172 | |
6c22ea37 | 173 | /* Find any OR from the class that is able to support this device. */ |
01a97637 BS |
174 | proto = nvkm_outp_xlat(outp, &type); |
175 | if (proto == UNKNOWN) | |
176 | return; | |
177 | ||
6c22ea37 BS |
178 | ior = nvkm_ior_find(disp, type, -1); |
179 | if (!ior) { | |
180 | WARN_ON(1); | |
181 | return; | |
182 | } | |
183 | ||
01a97637 | 184 | /* Determine the specific OR, if any, this device is attached to. */ |
6c22ea37 BS |
185 | if (ior->func->route.get) { |
186 | id = ior->func->route.get(outp, &link); | |
187 | if (id < 0) { | |
188 | OUTP_DBG(outp, "no route"); | |
189 | return; | |
190 | } | |
191 | } else { | |
01a97637 | 192 | /* Prior to DCB 4.1, this is hardwired like so. */ |
6c22ea37 BS |
193 | id = ffs(outp->info.or) - 1; |
194 | link = (ior->type == SOR) ? outp->info.sorconf.link : 0; | |
01a97637 BS |
195 | } |
196 | ||
197 | ior = nvkm_ior_find(disp, type, id); | |
198 | if (!ior) { | |
199 | WARN_ON(1); | |
200 | return; | |
201 | } | |
202 | ||
6c22ea37 BS |
203 | /* Determine if the OR is already configured for this device. */ |
204 | ior->func->state(ior, &ior->arm); | |
205 | if (!ior->arm.head || ior->arm.proto != proto) { | |
206 | OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head, | |
207 | ior->arm.proto, proto); | |
208 | return; | |
209 | } | |
210 | ||
211 | OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link); | |
212 | ior->arm.outp = outp; | |
01a97637 BS |
213 | } |
214 | ||
f2c906fc | 215 | void |
d7ce92e2 | 216 | nvkm_outp_init(struct nvkm_outp *outp) |
7a014a87 | 217 | { |
01a97637 | 218 | nvkm_outp_init_route(outp); |
f2c906fc BS |
219 | if (outp->func->init) |
220 | outp->func->init(outp); | |
7a014a87 BS |
221 | } |
222 | ||
223 | void | |
d7ce92e2 | 224 | nvkm_outp_del(struct nvkm_outp **poutp) |
7a014a87 | 225 | { |
d7ce92e2 | 226 | struct nvkm_outp *outp = *poutp; |
f2c906fc BS |
227 | if (outp && !WARN_ON(!outp->func)) { |
228 | if (outp->func->dtor) | |
229 | *poutp = outp->func->dtor(outp); | |
230 | kfree(*poutp); | |
231 | *poutp = NULL; | |
232 | } | |
7a014a87 BS |
233 | } |
234 | ||
01a97637 | 235 | int |
d7ce92e2 BS |
236 | nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp, |
237 | int index, struct dcb_output *dcbE, struct nvkm_outp *outp) | |
7a014a87 | 238 | { |
f2c906fc | 239 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; |
01a97637 BS |
240 | enum nvkm_ior_proto proto; |
241 | enum nvkm_ior_type type; | |
7a014a87 | 242 | |
f2c906fc BS |
243 | outp->func = func; |
244 | outp->disp = disp; | |
7a014a87 | 245 | outp->index = index; |
f2c906fc BS |
246 | outp->info = *dcbE; |
247 | outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index); | |
bf0eb898 | 248 | outp->or = ffs(outp->info.or) - 1; |
7a014a87 | 249 | |
f2c906fc BS |
250 | OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x " |
251 | "edid %x bus %d head %x", | |
252 | outp->info.type, outp->info.location, outp->info.or, | |
253 | outp->info.type >= 2 ? outp->info.sorconf.link : 0, | |
254 | outp->info.connector, outp->info.i2c_index, | |
255 | outp->info.bus, outp->info.heads); | |
01a97637 BS |
256 | |
257 | /* Cull output paths we can't map to an output resource. */ | |
258 | proto = nvkm_outp_xlat(outp, &type); | |
259 | if (proto == UNKNOWN) | |
260 | return -ENODEV; | |
261 | ||
262 | return 0; | |
7a014a87 BS |
263 | } |
264 | ||
3c66c87d BS |
265 | static const struct nvkm_outp_func |
266 | nvkm_outp = { | |
267 | }; | |
268 | ||
7a014a87 | 269 | int |
3c66c87d BS |
270 | nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, |
271 | struct nvkm_outp **poutp) | |
7a014a87 | 272 | { |
f2c906fc BS |
273 | if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL))) |
274 | return -ENOMEM; | |
3c66c87d | 275 | return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp); |
7a014a87 | 276 | } |