1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs *
4c39f472eSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs *
11c39f472eSBen Skeggs * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs *
14c39f472eSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17c39f472eSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs *
22c39f472eSBen Skeggs * Authors: Ben Skeggs
23c39f472eSBen Skeggs */
2492fba5d3SBen Skeggs #include "priv.h"
25acbe9ecfSBen Skeggs #include "chan.h"
26a1c93078SBen Skeggs #include "head.h"
2778f1ad6fSBen Skeggs #include "ior.h"
2892fba5d3SBen Skeggs #include "outp.h"
29c39f472eSBen Skeggs
30c39f472eSBen Skeggs #include <core/client.h>
31bb3b0a42SBen Skeggs #include <core/ramht.h>
32c39f472eSBen Skeggs #include <subdev/bios.h>
33c39f472eSBen Skeggs #include <subdev/bios/disp.h>
34c39f472eSBen Skeggs #include <subdev/bios/init.h>
35c39f472eSBen Skeggs #include <subdev/bios/pll.h>
36c39f472eSBen Skeggs #include <subdev/devinit.h>
37acbe9ecfSBen Skeggs #include <subdev/i2c.h>
38acbe9ecfSBen Skeggs #include <subdev/mmu.h>
392a4bd8acSBen Skeggs #include <subdev/timer.h>
40c39f472eSBen Skeggs
41168c0299SBen Skeggs #include <nvif/class.h>
42acbe9ecfSBen Skeggs #include <nvif/unpack.h>
43168c0299SBen Skeggs
44acbe9ecfSBen Skeggs static void
nv50_pior_clock(struct nvkm_ior * pior)45acbe9ecfSBen Skeggs nv50_pior_clock(struct nvkm_ior *pior)
4670aa8670SBen Skeggs {
47acbe9ecfSBen Skeggs struct nvkm_device *device = pior->disp->engine.subdev.device;
48acbe9ecfSBen Skeggs const u32 poff = nv50_ior_base(pior);
49bb3b0a42SBen Skeggs
50acbe9ecfSBen Skeggs nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);
51acbe9ecfSBen Skeggs }
52bb3b0a42SBen Skeggs
53acbe9ecfSBen Skeggs static int
nv50_pior_dp_links(struct nvkm_ior * pior,struct nvkm_i2c_aux * aux)54acbe9ecfSBen Skeggs nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)
55acbe9ecfSBen Skeggs {
56acbe9ecfSBen Skeggs int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw, pior->dp.ef);
57acbe9ecfSBen Skeggs if (ret)
58acbe9ecfSBen Skeggs return ret;
59acbe9ecfSBen Skeggs
60acbe9ecfSBen Skeggs return 1;
61acbe9ecfSBen Skeggs }
62acbe9ecfSBen Skeggs
639a4514fbSBen Skeggs static const struct nvkm_ior_func_dp
649a4514fbSBen Skeggs nv50_pior_dp = {
659a4514fbSBen Skeggs .links = nv50_pior_dp_links,
669a4514fbSBen Skeggs };
679a4514fbSBen Skeggs
68acbe9ecfSBen Skeggs static void
nv50_pior_power_wait(struct nvkm_device * device,u32 poff)69acbe9ecfSBen Skeggs nv50_pior_power_wait(struct nvkm_device *device, u32 poff)
70acbe9ecfSBen Skeggs {
71acbe9ecfSBen Skeggs nvkm_msec(device, 2000,
72acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000))
73acbe9ecfSBen Skeggs break;
74acbe9ecfSBen Skeggs );
75acbe9ecfSBen Skeggs }
76acbe9ecfSBen Skeggs
77acbe9ecfSBen Skeggs static void
nv50_pior_power(struct nvkm_ior * pior,bool normal,bool pu,bool data,bool vsync,bool hsync)78acbe9ecfSBen Skeggs nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu, bool data, bool vsync, bool hsync)
79acbe9ecfSBen Skeggs {
80acbe9ecfSBen Skeggs struct nvkm_device *device = pior->disp->engine.subdev.device;
81acbe9ecfSBen Skeggs const u32 poff = nv50_ior_base(pior);
82acbe9ecfSBen Skeggs const u32 shift = normal ? 0 : 16;
83acbe9ecfSBen Skeggs const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
84acbe9ecfSBen Skeggs const u32 field = 0x80000000 | (0x00000101 << shift);
85acbe9ecfSBen Skeggs
86acbe9ecfSBen Skeggs nv50_pior_power_wait(device, poff);
87acbe9ecfSBen Skeggs nvkm_mask(device, 0x61e004 + poff, field, state);
88acbe9ecfSBen Skeggs nv50_pior_power_wait(device, poff);
89acbe9ecfSBen Skeggs }
90acbe9ecfSBen Skeggs
91acbe9ecfSBen Skeggs void
nv50_pior_depth(struct nvkm_ior * ior,struct nvkm_ior_state * state,u32 ctrl)92acbe9ecfSBen Skeggs nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl)
93acbe9ecfSBen Skeggs {
94acbe9ecfSBen Skeggs /* GF119 moves this information to per-head methods, which is
95acbe9ecfSBen Skeggs * a lot more convenient, and where our shared code expect it.
96acbe9ecfSBen Skeggs */
97acbe9ecfSBen Skeggs if (state->head && state == &ior->asy) {
98acbe9ecfSBen Skeggs struct nvkm_head *head = nvkm_head_find(ior->disp, __ffs(state->head));
99acbe9ecfSBen Skeggs
100acbe9ecfSBen Skeggs if (!WARN_ON(!head)) {
101acbe9ecfSBen Skeggs struct nvkm_head_state *state = &head->asy;
102acbe9ecfSBen Skeggs switch ((ctrl & 0x000f0000) >> 16) {
103acbe9ecfSBen Skeggs case 6: state->or.depth = 30; break;
104acbe9ecfSBen Skeggs case 5: state->or.depth = 24; break;
105acbe9ecfSBen Skeggs case 2: state->or.depth = 18; break;
106acbe9ecfSBen Skeggs case 0: state->or.depth = 18; break; /*XXX*/
107acbe9ecfSBen Skeggs default:
108acbe9ecfSBen Skeggs state->or.depth = 18;
109acbe9ecfSBen Skeggs WARN_ON(1);
110acbe9ecfSBen Skeggs break;
111acbe9ecfSBen Skeggs }
112acbe9ecfSBen Skeggs }
113acbe9ecfSBen Skeggs }
114acbe9ecfSBen Skeggs }
115acbe9ecfSBen Skeggs
116acbe9ecfSBen Skeggs static void
nv50_pior_state(struct nvkm_ior * pior,struct nvkm_ior_state * state)117acbe9ecfSBen Skeggs nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state)
118acbe9ecfSBen Skeggs {
119acbe9ecfSBen Skeggs struct nvkm_device *device = pior->disp->engine.subdev.device;
120acbe9ecfSBen Skeggs const u32 coff = pior->id * 8 + (state == &pior->arm) * 4;
121acbe9ecfSBen Skeggs u32 ctrl = nvkm_rd32(device, 0x610b80 + coff);
122acbe9ecfSBen Skeggs
123acbe9ecfSBen Skeggs state->proto_evo = (ctrl & 0x00000f00) >> 8;
124acbe9ecfSBen Skeggs state->rgdiv = 1;
125acbe9ecfSBen Skeggs switch (state->proto_evo) {
126acbe9ecfSBen Skeggs case 0: state->proto = TMDS; break;
127acbe9ecfSBen Skeggs default:
128acbe9ecfSBen Skeggs state->proto = UNKNOWN;
129acbe9ecfSBen Skeggs break;
130acbe9ecfSBen Skeggs }
131acbe9ecfSBen Skeggs
132acbe9ecfSBen Skeggs state->head = ctrl & 0x00000003;
133acbe9ecfSBen Skeggs nv50_pior_depth(pior, state, ctrl);
134acbe9ecfSBen Skeggs }
135acbe9ecfSBen Skeggs
136acbe9ecfSBen Skeggs static const struct nvkm_ior_func
137acbe9ecfSBen Skeggs nv50_pior = {
138acbe9ecfSBen Skeggs .state = nv50_pior_state,
139acbe9ecfSBen Skeggs .power = nv50_pior_power,
140acbe9ecfSBen Skeggs .clock = nv50_pior_clock,
1419a4514fbSBen Skeggs .dp = &nv50_pior_dp,
142acbe9ecfSBen Skeggs };
143acbe9ecfSBen Skeggs
144acbe9ecfSBen Skeggs int
nv50_pior_new(struct nvkm_disp * disp,int id)145acbe9ecfSBen Skeggs nv50_pior_new(struct nvkm_disp *disp, int id)
146acbe9ecfSBen Skeggs {
14779c453afSBen Skeggs return nvkm_ior_new_(&nv50_pior, disp, PIOR, id, false);
14870aa8670SBen Skeggs }
14970aa8670SBen Skeggs
1500407b33fSBen Skeggs int
nv50_pior_cnt(struct nvkm_disp * disp,unsigned long * pmask)151acbe9ecfSBen Skeggs nv50_pior_cnt(struct nvkm_disp *disp, unsigned long *pmask)
15270aa8670SBen Skeggs {
153acbe9ecfSBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
154acbe9ecfSBen Skeggs
155acbe9ecfSBen Skeggs *pmask = (nvkm_rd32(device, 0x610184) & 0x70000000) >> 28;
156acbe9ecfSBen Skeggs return 3;
157acbe9ecfSBen Skeggs }
158acbe9ecfSBen Skeggs
159acbe9ecfSBen Skeggs void
nv50_sor_clock(struct nvkm_ior * sor)160acbe9ecfSBen Skeggs nv50_sor_clock(struct nvkm_ior *sor)
161acbe9ecfSBen Skeggs {
162acbe9ecfSBen Skeggs struct nvkm_device *device = sor->disp->engine.subdev.device;
163acbe9ecfSBen Skeggs const int div = sor->asy.link == 3;
164acbe9ecfSBen Skeggs const u32 soff = nv50_ior_base(sor);
165acbe9ecfSBen Skeggs
166acbe9ecfSBen Skeggs nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);
167acbe9ecfSBen Skeggs }
168acbe9ecfSBen Skeggs
169acbe9ecfSBen Skeggs static void
nv50_sor_power_wait(struct nvkm_device * device,u32 soff)170acbe9ecfSBen Skeggs nv50_sor_power_wait(struct nvkm_device *device, u32 soff)
171acbe9ecfSBen Skeggs {
172acbe9ecfSBen Skeggs nvkm_msec(device, 2000,
173acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))
174acbe9ecfSBen Skeggs break;
175acbe9ecfSBen Skeggs );
176acbe9ecfSBen Skeggs }
177acbe9ecfSBen Skeggs
178acbe9ecfSBen Skeggs void
nv50_sor_power(struct nvkm_ior * sor,bool normal,bool pu,bool data,bool vsync,bool hsync)179acbe9ecfSBen Skeggs nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu, bool data, bool vsync, bool hsync)
180acbe9ecfSBen Skeggs {
181acbe9ecfSBen Skeggs struct nvkm_device *device = sor->disp->engine.subdev.device;
182acbe9ecfSBen Skeggs const u32 soff = nv50_ior_base(sor);
183acbe9ecfSBen Skeggs const u32 shift = normal ? 0 : 16;
184acbe9ecfSBen Skeggs const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
185acbe9ecfSBen Skeggs const u32 field = 0x80000000 | (0x00000001 << shift);
186acbe9ecfSBen Skeggs
187acbe9ecfSBen Skeggs nv50_sor_power_wait(device, soff);
188acbe9ecfSBen Skeggs nvkm_mask(device, 0x61c004 + soff, field, state);
189acbe9ecfSBen Skeggs nv50_sor_power_wait(device, soff);
190acbe9ecfSBen Skeggs
191acbe9ecfSBen Skeggs nvkm_msec(device, 2000,
192acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
193acbe9ecfSBen Skeggs break;
194acbe9ecfSBen Skeggs );
195acbe9ecfSBen Skeggs }
196acbe9ecfSBen Skeggs
197acbe9ecfSBen Skeggs void
nv50_sor_state(struct nvkm_ior * sor,struct nvkm_ior_state * state)198acbe9ecfSBen Skeggs nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
199acbe9ecfSBen Skeggs {
200acbe9ecfSBen Skeggs struct nvkm_device *device = sor->disp->engine.subdev.device;
201acbe9ecfSBen Skeggs const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
202acbe9ecfSBen Skeggs u32 ctrl = nvkm_rd32(device, 0x610b70 + coff);
203acbe9ecfSBen Skeggs
204acbe9ecfSBen Skeggs state->proto_evo = (ctrl & 0x00000f00) >> 8;
205acbe9ecfSBen Skeggs switch (state->proto_evo) {
206acbe9ecfSBen Skeggs case 0: state->proto = LVDS; state->link = 1; break;
207acbe9ecfSBen Skeggs case 1: state->proto = TMDS; state->link = 1; break;
208acbe9ecfSBen Skeggs case 2: state->proto = TMDS; state->link = 2; break;
209acbe9ecfSBen Skeggs case 5: state->proto = TMDS; state->link = 3; break;
210acbe9ecfSBen Skeggs default:
211acbe9ecfSBen Skeggs state->proto = UNKNOWN;
212acbe9ecfSBen Skeggs break;
213acbe9ecfSBen Skeggs }
214acbe9ecfSBen Skeggs
215acbe9ecfSBen Skeggs state->head = ctrl & 0x00000003;
216acbe9ecfSBen Skeggs }
217acbe9ecfSBen Skeggs
218acbe9ecfSBen Skeggs static const struct nvkm_ior_func
219acbe9ecfSBen Skeggs nv50_sor = {
220acbe9ecfSBen Skeggs .state = nv50_sor_state,
221acbe9ecfSBen Skeggs .power = nv50_sor_power,
222acbe9ecfSBen Skeggs .clock = nv50_sor_clock,
223acbe9ecfSBen Skeggs };
224acbe9ecfSBen Skeggs
225acbe9ecfSBen Skeggs static int
nv50_sor_new(struct nvkm_disp * disp,int id)226acbe9ecfSBen Skeggs nv50_sor_new(struct nvkm_disp *disp, int id)
227acbe9ecfSBen Skeggs {
22879c453afSBen Skeggs return nvkm_ior_new_(&nv50_sor, disp, SOR, id, false);
229acbe9ecfSBen Skeggs }
230acbe9ecfSBen Skeggs
231acbe9ecfSBen Skeggs int
nv50_sor_cnt(struct nvkm_disp * disp,unsigned long * pmask)232acbe9ecfSBen Skeggs nv50_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)
233acbe9ecfSBen Skeggs {
234acbe9ecfSBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
235acbe9ecfSBen Skeggs
236acbe9ecfSBen Skeggs *pmask = (nvkm_rd32(device, 0x610184) & 0x03000000) >> 24;
237acbe9ecfSBen Skeggs return 2;
238acbe9ecfSBen Skeggs }
239acbe9ecfSBen Skeggs
240acbe9ecfSBen Skeggs static void
nv50_dac_clock(struct nvkm_ior * dac)241acbe9ecfSBen Skeggs nv50_dac_clock(struct nvkm_ior *dac)
242acbe9ecfSBen Skeggs {
243acbe9ecfSBen Skeggs struct nvkm_device *device = dac->disp->engine.subdev.device;
244acbe9ecfSBen Skeggs const u32 doff = nv50_ior_base(dac);
245acbe9ecfSBen Skeggs
246acbe9ecfSBen Skeggs nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);
247acbe9ecfSBen Skeggs }
248acbe9ecfSBen Skeggs
249acbe9ecfSBen Skeggs int
nv50_dac_sense(struct nvkm_ior * dac,u32 loadval)250acbe9ecfSBen Skeggs nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)
251acbe9ecfSBen Skeggs {
252acbe9ecfSBen Skeggs struct nvkm_device *device = dac->disp->engine.subdev.device;
253acbe9ecfSBen Skeggs const u32 doff = nv50_ior_base(dac);
254acbe9ecfSBen Skeggs
255acbe9ecfSBen Skeggs dac->func->power(dac, false, true, false, false, false);
256acbe9ecfSBen Skeggs
257acbe9ecfSBen Skeggs nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval);
258acbe9ecfSBen Skeggs mdelay(9);
259acbe9ecfSBen Skeggs udelay(500);
260acbe9ecfSBen Skeggs loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000);
261acbe9ecfSBen Skeggs
262acbe9ecfSBen Skeggs dac->func->power(dac, false, false, false, false, false);
263acbe9ecfSBen Skeggs if (!(loadval & 0x80000000))
264acbe9ecfSBen Skeggs return -ETIMEDOUT;
265acbe9ecfSBen Skeggs
266acbe9ecfSBen Skeggs return (loadval & 0x38000000) >> 27;
267acbe9ecfSBen Skeggs }
268acbe9ecfSBen Skeggs
269acbe9ecfSBen Skeggs static void
nv50_dac_power_wait(struct nvkm_device * device,const u32 doff)270acbe9ecfSBen Skeggs nv50_dac_power_wait(struct nvkm_device *device, const u32 doff)
271acbe9ecfSBen Skeggs {
272acbe9ecfSBen Skeggs nvkm_msec(device, 2000,
273acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
274acbe9ecfSBen Skeggs break;
275acbe9ecfSBen Skeggs );
276acbe9ecfSBen Skeggs }
277acbe9ecfSBen Skeggs
278acbe9ecfSBen Skeggs void
nv50_dac_power(struct nvkm_ior * dac,bool normal,bool pu,bool data,bool vsync,bool hsync)279acbe9ecfSBen Skeggs nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu, bool data, bool vsync, bool hsync)
280acbe9ecfSBen Skeggs {
281acbe9ecfSBen Skeggs struct nvkm_device *device = dac->disp->engine.subdev.device;
282acbe9ecfSBen Skeggs const u32 doff = nv50_ior_base(dac);
283acbe9ecfSBen Skeggs const u32 shift = normal ? 0 : 16;
284acbe9ecfSBen Skeggs const u32 state = 0x80000000 | (0x00000040 * ! pu |
285acbe9ecfSBen Skeggs 0x00000010 * ! data |
286acbe9ecfSBen Skeggs 0x00000004 * ! vsync |
287acbe9ecfSBen Skeggs 0x00000001 * ! hsync) << shift;
288acbe9ecfSBen Skeggs const u32 field = 0xc0000000 | (0x00000055 << shift);
289acbe9ecfSBen Skeggs
290acbe9ecfSBen Skeggs nv50_dac_power_wait(device, doff);
291acbe9ecfSBen Skeggs nvkm_mask(device, 0x61a004 + doff, field, state);
292acbe9ecfSBen Skeggs nv50_dac_power_wait(device, doff);
293acbe9ecfSBen Skeggs }
294acbe9ecfSBen Skeggs
295acbe9ecfSBen Skeggs static void
nv50_dac_state(struct nvkm_ior * dac,struct nvkm_ior_state * state)296acbe9ecfSBen Skeggs nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
297acbe9ecfSBen Skeggs {
298acbe9ecfSBen Skeggs struct nvkm_device *device = dac->disp->engine.subdev.device;
299acbe9ecfSBen Skeggs const u32 coff = dac->id * 8 + (state == &dac->arm) * 4;
300acbe9ecfSBen Skeggs u32 ctrl = nvkm_rd32(device, 0x610b58 + coff);
301acbe9ecfSBen Skeggs
302acbe9ecfSBen Skeggs state->proto_evo = (ctrl & 0x00000f00) >> 8;
303acbe9ecfSBen Skeggs switch (state->proto_evo) {
304acbe9ecfSBen Skeggs case 0: state->proto = CRT; break;
305acbe9ecfSBen Skeggs default:
306acbe9ecfSBen Skeggs state->proto = UNKNOWN;
307acbe9ecfSBen Skeggs break;
308acbe9ecfSBen Skeggs }
309acbe9ecfSBen Skeggs
310acbe9ecfSBen Skeggs state->head = ctrl & 0x00000003;
311acbe9ecfSBen Skeggs }
312acbe9ecfSBen Skeggs
313acbe9ecfSBen Skeggs static const struct nvkm_ior_func
314acbe9ecfSBen Skeggs nv50_dac = {
315acbe9ecfSBen Skeggs .state = nv50_dac_state,
316acbe9ecfSBen Skeggs .power = nv50_dac_power,
317acbe9ecfSBen Skeggs .sense = nv50_dac_sense,
318acbe9ecfSBen Skeggs .clock = nv50_dac_clock,
319acbe9ecfSBen Skeggs };
320acbe9ecfSBen Skeggs
321acbe9ecfSBen Skeggs int
nv50_dac_new(struct nvkm_disp * disp,int id)322acbe9ecfSBen Skeggs nv50_dac_new(struct nvkm_disp *disp, int id)
323acbe9ecfSBen Skeggs {
32479c453afSBen Skeggs return nvkm_ior_new_(&nv50_dac, disp, DAC, id, false);
325acbe9ecfSBen Skeggs }
326acbe9ecfSBen Skeggs
327acbe9ecfSBen Skeggs int
nv50_dac_cnt(struct nvkm_disp * disp,unsigned long * pmask)328acbe9ecfSBen Skeggs nv50_dac_cnt(struct nvkm_disp *disp, unsigned long *pmask)
329acbe9ecfSBen Skeggs {
330acbe9ecfSBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
331acbe9ecfSBen Skeggs
332acbe9ecfSBen Skeggs *pmask = (nvkm_rd32(device, 0x610184) & 0x00700000) >> 20;
333acbe9ecfSBen Skeggs return 3;
334acbe9ecfSBen Skeggs }
335acbe9ecfSBen Skeggs
336acbe9ecfSBen Skeggs static void
nv50_head_vblank_put(struct nvkm_head * head)337acbe9ecfSBen Skeggs nv50_head_vblank_put(struct nvkm_head *head)
338acbe9ecfSBen Skeggs {
339acbe9ecfSBen Skeggs struct nvkm_device *device = head->disp->engine.subdev.device;
340acbe9ecfSBen Skeggs
341acbe9ecfSBen Skeggs nvkm_mask(device, 0x61002c, (4 << head->id), 0);
342acbe9ecfSBen Skeggs }
343acbe9ecfSBen Skeggs
344acbe9ecfSBen Skeggs static void
nv50_head_vblank_get(struct nvkm_head * head)345acbe9ecfSBen Skeggs nv50_head_vblank_get(struct nvkm_head *head)
346acbe9ecfSBen Skeggs {
347acbe9ecfSBen Skeggs struct nvkm_device *device = head->disp->engine.subdev.device;
348acbe9ecfSBen Skeggs
349acbe9ecfSBen Skeggs nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));
350acbe9ecfSBen Skeggs }
351acbe9ecfSBen Skeggs
352acbe9ecfSBen Skeggs static void
nv50_head_rgclk(struct nvkm_head * head,int div)353acbe9ecfSBen Skeggs nv50_head_rgclk(struct nvkm_head *head, int div)
354acbe9ecfSBen Skeggs {
355acbe9ecfSBen Skeggs struct nvkm_device *device = head->disp->engine.subdev.device;
356acbe9ecfSBen Skeggs
357acbe9ecfSBen Skeggs nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);
358acbe9ecfSBen Skeggs }
359acbe9ecfSBen Skeggs
360acbe9ecfSBen Skeggs void
nv50_head_rgpos(struct nvkm_head * head,u16 * hline,u16 * vline)361acbe9ecfSBen Skeggs nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
362acbe9ecfSBen Skeggs {
363acbe9ecfSBen Skeggs struct nvkm_device *device = head->disp->engine.subdev.device;
364acbe9ecfSBen Skeggs const u32 hoff = head->id * 0x800;
365acbe9ecfSBen Skeggs
366acbe9ecfSBen Skeggs /* vline read locks hline. */
367acbe9ecfSBen Skeggs *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff;
368acbe9ecfSBen Skeggs *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff;
369acbe9ecfSBen Skeggs }
370acbe9ecfSBen Skeggs
371acbe9ecfSBen Skeggs static void
nv50_head_state(struct nvkm_head * head,struct nvkm_head_state * state)372acbe9ecfSBen Skeggs nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
373acbe9ecfSBen Skeggs {
374acbe9ecfSBen Skeggs struct nvkm_device *device = head->disp->engine.subdev.device;
375acbe9ecfSBen Skeggs const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4;
376acbe9ecfSBen Skeggs u32 data;
377acbe9ecfSBen Skeggs
378acbe9ecfSBen Skeggs data = nvkm_rd32(device, 0x610ae8 + hoff);
379acbe9ecfSBen Skeggs state->vblanke = (data & 0xffff0000) >> 16;
380acbe9ecfSBen Skeggs state->hblanke = (data & 0x0000ffff);
381acbe9ecfSBen Skeggs data = nvkm_rd32(device, 0x610af0 + hoff);
382acbe9ecfSBen Skeggs state->vblanks = (data & 0xffff0000) >> 16;
383acbe9ecfSBen Skeggs state->hblanks = (data & 0x0000ffff);
384acbe9ecfSBen Skeggs data = nvkm_rd32(device, 0x610af8 + hoff);
385acbe9ecfSBen Skeggs state->vtotal = (data & 0xffff0000) >> 16;
386acbe9ecfSBen Skeggs state->htotal = (data & 0x0000ffff);
387acbe9ecfSBen Skeggs data = nvkm_rd32(device, 0x610b00 + hoff);
388acbe9ecfSBen Skeggs state->vsynce = (data & 0xffff0000) >> 16;
389acbe9ecfSBen Skeggs state->hsynce = (data & 0x0000ffff);
390acbe9ecfSBen Skeggs state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000;
391acbe9ecfSBen Skeggs }
392acbe9ecfSBen Skeggs
393acbe9ecfSBen Skeggs static const struct nvkm_head_func
394acbe9ecfSBen Skeggs nv50_head = {
395acbe9ecfSBen Skeggs .state = nv50_head_state,
396acbe9ecfSBen Skeggs .rgpos = nv50_head_rgpos,
397acbe9ecfSBen Skeggs .rgclk = nv50_head_rgclk,
398acbe9ecfSBen Skeggs .vblank_get = nv50_head_vblank_get,
399acbe9ecfSBen Skeggs .vblank_put = nv50_head_vblank_put,
400acbe9ecfSBen Skeggs };
401acbe9ecfSBen Skeggs
402acbe9ecfSBen Skeggs int
nv50_head_new(struct nvkm_disp * disp,int id)403acbe9ecfSBen Skeggs nv50_head_new(struct nvkm_disp *disp, int id)
404acbe9ecfSBen Skeggs {
405acbe9ecfSBen Skeggs return nvkm_head_new_(&nv50_head, disp, id);
406acbe9ecfSBen Skeggs }
407acbe9ecfSBen Skeggs
408acbe9ecfSBen Skeggs int
nv50_head_cnt(struct nvkm_disp * disp,unsigned long * pmask)409acbe9ecfSBen Skeggs nv50_head_cnt(struct nvkm_disp *disp, unsigned long *pmask)
410acbe9ecfSBen Skeggs {
411acbe9ecfSBen Skeggs *pmask = 3;
412acbe9ecfSBen Skeggs return 2;
413acbe9ecfSBen Skeggs }
414acbe9ecfSBen Skeggs
415acbe9ecfSBen Skeggs
416acbe9ecfSBen Skeggs static void
nv50_disp_mthd_list(struct nvkm_disp * disp,int debug,u32 base,int c,const struct nvkm_disp_mthd_list * list,int inst)417acbe9ecfSBen Skeggs nv50_disp_mthd_list(struct nvkm_disp *disp, int debug, u32 base, int c,
418acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list *list, int inst)
419acbe9ecfSBen Skeggs {
42092fba5d3SBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
421bb3b0a42SBen Skeggs struct nvkm_device *device = subdev->device;
422acbe9ecfSBen Skeggs int i;
42370aa8670SBen Skeggs
424acbe9ecfSBen Skeggs for (i = 0; list->data[i].mthd; i++) {
425acbe9ecfSBen Skeggs if (list->data[i].addr) {
426acbe9ecfSBen Skeggs u32 next = nvkm_rd32(device, list->data[i].addr + base + 0);
427acbe9ecfSBen Skeggs u32 prev = nvkm_rd32(device, list->data[i].addr + base + c);
428acbe9ecfSBen Skeggs u32 mthd = list->data[i].mthd + (list->mthd * inst);
429acbe9ecfSBen Skeggs const char *name = list->data[i].name;
430acbe9ecfSBen Skeggs char mods[16];
431acbe9ecfSBen Skeggs
432acbe9ecfSBen Skeggs if (prev != next)
433acbe9ecfSBen Skeggs snprintf(mods, sizeof(mods), "-> %08x", next);
434acbe9ecfSBen Skeggs else
435acbe9ecfSBen Skeggs snprintf(mods, sizeof(mods), "%13c", ' ');
436acbe9ecfSBen Skeggs
437acbe9ecfSBen Skeggs nvkm_printk_(subdev, debug, info,
438acbe9ecfSBen Skeggs "\t%04x: %08x %s%s%s\n",
439acbe9ecfSBen Skeggs mthd, prev, mods, name ? " // " : "",
440acbe9ecfSBen Skeggs name ? name : "");
441290ffeafSBen Skeggs }
44278f1ad6fSBen Skeggs }
443bf5d1a6bSBen Skeggs }
44478f1ad6fSBen Skeggs
445acbe9ecfSBen Skeggs void
nv50_disp_chan_mthd(struct nvkm_disp_chan * chan,int debug)446acbe9ecfSBen Skeggs nv50_disp_chan_mthd(struct nvkm_disp_chan *chan, int debug)
447acbe9ecfSBen Skeggs {
448acbe9ecfSBen Skeggs struct nvkm_disp *disp = chan->disp;
449acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
450acbe9ecfSBen Skeggs const struct nvkm_disp_chan_mthd *mthd = chan->mthd;
451acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list *list;
452acbe9ecfSBen Skeggs int i, j;
453acbe9ecfSBen Skeggs
454acbe9ecfSBen Skeggs if (debug > subdev->debug)
455acbe9ecfSBen Skeggs return;
456acbe9ecfSBen Skeggs if (!mthd)
457acbe9ecfSBen Skeggs return;
458acbe9ecfSBen Skeggs
459acbe9ecfSBen Skeggs for (i = 0; (list = mthd->data[i].mthd) != NULL; i++) {
460acbe9ecfSBen Skeggs u32 base = chan->head * mthd->addr;
461acbe9ecfSBen Skeggs for (j = 0; j < mthd->data[i].nr; j++, base += list->addr) {
462acbe9ecfSBen Skeggs const char *cname = mthd->name;
463acbe9ecfSBen Skeggs const char *sname = "";
464acbe9ecfSBen Skeggs char cname_[16], sname_[16];
465acbe9ecfSBen Skeggs
466acbe9ecfSBen Skeggs if (mthd->addr) {
467acbe9ecfSBen Skeggs snprintf(cname_, sizeof(cname_), "%s %d",
468acbe9ecfSBen Skeggs mthd->name, chan->chid.user);
469acbe9ecfSBen Skeggs cname = cname_;
470acbe9ecfSBen Skeggs }
471acbe9ecfSBen Skeggs
472acbe9ecfSBen Skeggs if (mthd->data[i].nr > 1) {
473acbe9ecfSBen Skeggs snprintf(sname_, sizeof(sname_), " - %s %d",
474acbe9ecfSBen Skeggs mthd->data[i].name, j);
475acbe9ecfSBen Skeggs sname = sname_;
476acbe9ecfSBen Skeggs }
477acbe9ecfSBen Skeggs
478acbe9ecfSBen Skeggs nvkm_printk_(subdev, debug, info, "%s%s:\n", cname, sname);
479acbe9ecfSBen Skeggs nv50_disp_mthd_list(disp, debug, base, mthd->prev,
480acbe9ecfSBen Skeggs list, j);
481acbe9ecfSBen Skeggs }
48278f1ad6fSBen Skeggs }
483f5e088d6SBen Skeggs }
48478f1ad6fSBen Skeggs
485acbe9ecfSBen Skeggs static void
nv50_disp_chan_uevent_fini(struct nvkm_event * event,int type,int index)486acbe9ecfSBen Skeggs nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)
487acbe9ecfSBen Skeggs {
488acbe9ecfSBen Skeggs struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent);
489acbe9ecfSBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
490acbe9ecfSBen Skeggs nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000000 << index);
491acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610020, 0x00000001 << index);
49278f1ad6fSBen Skeggs }
49378f1ad6fSBen Skeggs
494acbe9ecfSBen Skeggs static void
nv50_disp_chan_uevent_init(struct nvkm_event * event,int types,int index)495acbe9ecfSBen Skeggs nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
496acbe9ecfSBen Skeggs {
497acbe9ecfSBen Skeggs struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent);
498acbe9ecfSBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
499acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610020, 0x00000001 << index);
500acbe9ecfSBen Skeggs nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000001 << index);
501acbe9ecfSBen Skeggs }
502bb3b0a42SBen Skeggs
503acbe9ecfSBen Skeggs void
nv50_disp_chan_uevent_send(struct nvkm_disp * disp,int chid)504acbe9ecfSBen Skeggs nv50_disp_chan_uevent_send(struct nvkm_disp *disp, int chid)
505acbe9ecfSBen Skeggs {
506*99d0701aSBen Skeggs nvkm_event_ntfy(&disp->uevent, chid, NVKM_DISP_EVENT_CHAN_AWAKEN);
507acbe9ecfSBen Skeggs }
508acbe9ecfSBen Skeggs
509acbe9ecfSBen Skeggs const struct nvkm_event_func
510acbe9ecfSBen Skeggs nv50_disp_chan_uevent = {
511acbe9ecfSBen Skeggs .init = nv50_disp_chan_uevent_init,
512acbe9ecfSBen Skeggs .fini = nv50_disp_chan_uevent_fini,
513acbe9ecfSBen Skeggs };
514acbe9ecfSBen Skeggs
515acbe9ecfSBen Skeggs u64
nv50_disp_chan_user(struct nvkm_disp_chan * chan,u64 * psize)516acbe9ecfSBen Skeggs nv50_disp_chan_user(struct nvkm_disp_chan *chan, u64 *psize)
517acbe9ecfSBen Skeggs {
518acbe9ecfSBen Skeggs *psize = 0x1000;
519acbe9ecfSBen Skeggs return 0x640000 + (chan->chid.user * 0x1000);
520acbe9ecfSBen Skeggs }
521acbe9ecfSBen Skeggs
522acbe9ecfSBen Skeggs void
nv50_disp_chan_intr(struct nvkm_disp_chan * chan,bool en)523acbe9ecfSBen Skeggs nv50_disp_chan_intr(struct nvkm_disp_chan *chan, bool en)
524acbe9ecfSBen Skeggs {
525acbe9ecfSBen Skeggs struct nvkm_device *device = chan->disp->engine.subdev.device;
526acbe9ecfSBen Skeggs const u32 mask = 0x00010001 << chan->chid.user;
527acbe9ecfSBen Skeggs const u32 data = en ? 0x00010000 << chan->chid.user : 0x00000000;
528acbe9ecfSBen Skeggs nvkm_mask(device, 0x610028, mask, data);
529acbe9ecfSBen Skeggs }
530acbe9ecfSBen Skeggs
531acbe9ecfSBen Skeggs static void
nv50_disp_pioc_fini(struct nvkm_disp_chan * chan)532acbe9ecfSBen Skeggs nv50_disp_pioc_fini(struct nvkm_disp_chan *chan)
533acbe9ecfSBen Skeggs {
534acbe9ecfSBen Skeggs struct nvkm_disp *disp = chan->disp;
535acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
536acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
537acbe9ecfSBen Skeggs int ctrl = chan->chid.ctrl;
538acbe9ecfSBen Skeggs int user = chan->chid.user;
539acbe9ecfSBen Skeggs
540acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200 + (ctrl * 0x10), 0x00000001, 0x00000000);
541acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
542acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000))
543acbe9ecfSBen Skeggs break;
544acbe9ecfSBen Skeggs ) < 0) {
545acbe9ecfSBen Skeggs nvkm_error(subdev, "ch %d timeout: %08x\n", user,
546acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
547acbe9ecfSBen Skeggs }
548acbe9ecfSBen Skeggs }
549acbe9ecfSBen Skeggs
550acbe9ecfSBen Skeggs static int
nv50_disp_pioc_init(struct nvkm_disp_chan * chan)551acbe9ecfSBen Skeggs nv50_disp_pioc_init(struct nvkm_disp_chan *chan)
552acbe9ecfSBen Skeggs {
553acbe9ecfSBen Skeggs struct nvkm_disp *disp = chan->disp;
554acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
555acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
556acbe9ecfSBen Skeggs int ctrl = chan->chid.ctrl;
557acbe9ecfSBen Skeggs int user = chan->chid.user;
558acbe9ecfSBen Skeggs
559acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00002000);
560acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
561acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000))
562acbe9ecfSBen Skeggs break;
563acbe9ecfSBen Skeggs ) < 0) {
564acbe9ecfSBen Skeggs nvkm_error(subdev, "ch %d timeout0: %08x\n", user,
565acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
566acbe9ecfSBen Skeggs return -EBUSY;
567acbe9ecfSBen Skeggs }
568acbe9ecfSBen Skeggs
569acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00000001);
570acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
571acbe9ecfSBen Skeggs u32 tmp = nvkm_rd32(device, 0x610200 + (ctrl * 0x10));
572acbe9ecfSBen Skeggs if ((tmp & 0x00030000) == 0x00010000)
573acbe9ecfSBen Skeggs break;
574acbe9ecfSBen Skeggs ) < 0) {
575acbe9ecfSBen Skeggs nvkm_error(subdev, "ch %d timeout1: %08x\n", user,
576acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
577acbe9ecfSBen Skeggs return -EBUSY;
578acbe9ecfSBen Skeggs }
579acbe9ecfSBen Skeggs
580acbe9ecfSBen Skeggs return 0;
581acbe9ecfSBen Skeggs }
582acbe9ecfSBen Skeggs
583acbe9ecfSBen Skeggs const struct nvkm_disp_chan_func
584acbe9ecfSBen Skeggs nv50_disp_pioc_func = {
585acbe9ecfSBen Skeggs .init = nv50_disp_pioc_init,
586acbe9ecfSBen Skeggs .fini = nv50_disp_pioc_fini,
587acbe9ecfSBen Skeggs .intr = nv50_disp_chan_intr,
588acbe9ecfSBen Skeggs .user = nv50_disp_chan_user,
589acbe9ecfSBen Skeggs };
590acbe9ecfSBen Skeggs
591acbe9ecfSBen Skeggs int
nv50_disp_dmac_bind(struct nvkm_disp_chan * chan,struct nvkm_object * object,u32 handle)592acbe9ecfSBen Skeggs nv50_disp_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle)
593acbe9ecfSBen Skeggs {
594acbe9ecfSBen Skeggs return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -10, handle,
595acbe9ecfSBen Skeggs chan->chid.user << 28 | chan->chid.user);
596acbe9ecfSBen Skeggs }
597acbe9ecfSBen Skeggs
598acbe9ecfSBen Skeggs static void
nv50_disp_dmac_fini(struct nvkm_disp_chan * chan)599acbe9ecfSBen Skeggs nv50_disp_dmac_fini(struct nvkm_disp_chan *chan)
600acbe9ecfSBen Skeggs {
601acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &chan->disp->engine.subdev;
602acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
603acbe9ecfSBen Skeggs int ctrl = chan->chid.ctrl;
604acbe9ecfSBen Skeggs int user = chan->chid.user;
605acbe9ecfSBen Skeggs
606acbe9ecfSBen Skeggs /* deactivate channel */
607acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00001010, 0x00001000);
608acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000003, 0x00000000);
609acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
610acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x001e0000))
611acbe9ecfSBen Skeggs break;
612acbe9ecfSBen Skeggs ) < 0) {
613acbe9ecfSBen Skeggs nvkm_error(subdev, "ch %d fini timeout, %08x\n", user,
614acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
615acbe9ecfSBen Skeggs }
616acbe9ecfSBen Skeggs
617acbe9ecfSBen Skeggs chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));
618acbe9ecfSBen Skeggs }
619acbe9ecfSBen Skeggs
620acbe9ecfSBen Skeggs static int
nv50_disp_dmac_init(struct nvkm_disp_chan * chan)621acbe9ecfSBen Skeggs nv50_disp_dmac_init(struct nvkm_disp_chan *chan)
622acbe9ecfSBen Skeggs {
623acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &chan->disp->engine.subdev;
624acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
625acbe9ecfSBen Skeggs int ctrl = chan->chid.ctrl;
626acbe9ecfSBen Skeggs int user = chan->chid.user;
627acbe9ecfSBen Skeggs
628acbe9ecfSBen Skeggs /* initialise channel for dma command submission */
629acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610204 + (ctrl * 0x0010), chan->push);
630acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000);
631acbe9ecfSBen Skeggs nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl);
632acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000010, 0x00000010);
633acbe9ecfSBen Skeggs nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put);
634acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610200 + (ctrl * 0x0010), 0x00000013);
635acbe9ecfSBen Skeggs
636acbe9ecfSBen Skeggs /* wait for it to go inactive */
637acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
638acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x80000000))
639acbe9ecfSBen Skeggs break;
640acbe9ecfSBen Skeggs ) < 0) {
641acbe9ecfSBen Skeggs nvkm_error(subdev, "ch %d init timeout, %08x\n", user,
642acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
643acbe9ecfSBen Skeggs return -EBUSY;
644acbe9ecfSBen Skeggs }
645acbe9ecfSBen Skeggs
646acbe9ecfSBen Skeggs return 0;
647acbe9ecfSBen Skeggs }
648acbe9ecfSBen Skeggs
649acbe9ecfSBen Skeggs int
nv50_disp_dmac_push(struct nvkm_disp_chan * chan,u64 object)650889fcbe9SBen Skeggs nv50_disp_dmac_push(struct nvkm_disp_chan *chan, u64 object)
651acbe9ecfSBen Skeggs {
652889fcbe9SBen Skeggs chan->memory = nvkm_umem_search(chan->object.client, object);
653acbe9ecfSBen Skeggs if (IS_ERR(chan->memory))
654acbe9ecfSBen Skeggs return PTR_ERR(chan->memory);
6553b9ba66aSBen Skeggs
656acbe9ecfSBen Skeggs if (nvkm_memory_size(chan->memory) < 0x1000)
657acbe9ecfSBen Skeggs return -EINVAL;
6583b9ba66aSBen Skeggs
659acbe9ecfSBen Skeggs switch (nvkm_memory_target(chan->memory)) {
660acbe9ecfSBen Skeggs case NVKM_MEM_TARGET_VRAM: chan->push = 0x00000001; break;
661acbe9ecfSBen Skeggs case NVKM_MEM_TARGET_NCOH: chan->push = 0x00000002; break;
662acbe9ecfSBen Skeggs case NVKM_MEM_TARGET_HOST: chan->push = 0x00000003; break;
663acbe9ecfSBen Skeggs default:
664acbe9ecfSBen Skeggs return -EINVAL;
665acbe9ecfSBen Skeggs }
666acbe9ecfSBen Skeggs
667acbe9ecfSBen Skeggs chan->push |= nvkm_memory_addr(chan->memory) >> 8;
668acbe9ecfSBen Skeggs return 0;
669acbe9ecfSBen Skeggs }
670acbe9ecfSBen Skeggs
671acbe9ecfSBen Skeggs const struct nvkm_disp_chan_func
672acbe9ecfSBen Skeggs nv50_disp_dmac_func = {
673889fcbe9SBen Skeggs .push = nv50_disp_dmac_push,
674acbe9ecfSBen Skeggs .init = nv50_disp_dmac_init,
675acbe9ecfSBen Skeggs .fini = nv50_disp_dmac_fini,
676acbe9ecfSBen Skeggs .intr = nv50_disp_chan_intr,
677acbe9ecfSBen Skeggs .user = nv50_disp_chan_user,
678acbe9ecfSBen Skeggs .bind = nv50_disp_dmac_bind,
679acbe9ecfSBen Skeggs };
680acbe9ecfSBen Skeggs
681889fcbe9SBen Skeggs const struct nvkm_disp_chan_user
682889fcbe9SBen Skeggs nv50_disp_curs = {
683889fcbe9SBen Skeggs .func = &nv50_disp_pioc_func,
684889fcbe9SBen Skeggs .ctrl = 7,
685889fcbe9SBen Skeggs .user = 7,
686889fcbe9SBen Skeggs };
687acbe9ecfSBen Skeggs
688889fcbe9SBen Skeggs const struct nvkm_disp_chan_user
689889fcbe9SBen Skeggs nv50_disp_oimm = {
690889fcbe9SBen Skeggs .func = &nv50_disp_pioc_func,
691889fcbe9SBen Skeggs .ctrl = 5,
692889fcbe9SBen Skeggs .user = 5,
693889fcbe9SBen Skeggs };
694acbe9ecfSBen Skeggs
695acbe9ecfSBen Skeggs static const struct nvkm_disp_mthd_list
696acbe9ecfSBen Skeggs nv50_disp_ovly_mthd_base = {
697acbe9ecfSBen Skeggs .mthd = 0x0000,
698acbe9ecfSBen Skeggs .addr = 0x000000,
699acbe9ecfSBen Skeggs .data = {
700acbe9ecfSBen Skeggs { 0x0080, 0x000000 },
701acbe9ecfSBen Skeggs { 0x0084, 0x0009a0 },
702acbe9ecfSBen Skeggs { 0x0088, 0x0009c0 },
703acbe9ecfSBen Skeggs { 0x008c, 0x0009c8 },
704acbe9ecfSBen Skeggs { 0x0090, 0x6109b4 },
705acbe9ecfSBen Skeggs { 0x0094, 0x610970 },
706acbe9ecfSBen Skeggs { 0x00a0, 0x610998 },
707acbe9ecfSBen Skeggs { 0x00a4, 0x610964 },
708acbe9ecfSBen Skeggs { 0x00c0, 0x610958 },
709acbe9ecfSBen Skeggs { 0x00e0, 0x6109a8 },
710acbe9ecfSBen Skeggs { 0x00e4, 0x6109d0 },
711acbe9ecfSBen Skeggs { 0x00e8, 0x6109d8 },
712acbe9ecfSBen Skeggs { 0x0100, 0x61094c },
713acbe9ecfSBen Skeggs { 0x0104, 0x610984 },
714acbe9ecfSBen Skeggs { 0x0108, 0x61098c },
715acbe9ecfSBen Skeggs { 0x0800, 0x6109f8 },
716acbe9ecfSBen Skeggs { 0x0808, 0x610a08 },
717acbe9ecfSBen Skeggs { 0x080c, 0x610a10 },
718acbe9ecfSBen Skeggs { 0x0810, 0x610a00 },
719acbe9ecfSBen Skeggs {}
720acbe9ecfSBen Skeggs }
721acbe9ecfSBen Skeggs };
722acbe9ecfSBen Skeggs
723acbe9ecfSBen Skeggs static const struct nvkm_disp_chan_mthd
724acbe9ecfSBen Skeggs nv50_disp_ovly_mthd = {
725acbe9ecfSBen Skeggs .name = "Overlay",
726acbe9ecfSBen Skeggs .addr = 0x000540,
727acbe9ecfSBen Skeggs .prev = 0x000004,
728acbe9ecfSBen Skeggs .data = {
729acbe9ecfSBen Skeggs { "Global", 1, &nv50_disp_ovly_mthd_base },
730acbe9ecfSBen Skeggs {}
731acbe9ecfSBen Skeggs }
732acbe9ecfSBen Skeggs };
733acbe9ecfSBen Skeggs
734889fcbe9SBen Skeggs static const struct nvkm_disp_chan_user
735889fcbe9SBen Skeggs nv50_disp_ovly = {
736889fcbe9SBen Skeggs .func = &nv50_disp_dmac_func,
737889fcbe9SBen Skeggs .ctrl = 3,
738889fcbe9SBen Skeggs .user = 3,
739889fcbe9SBen Skeggs .mthd = &nv50_disp_ovly_mthd,
740889fcbe9SBen Skeggs };
741acbe9ecfSBen Skeggs
742acbe9ecfSBen Skeggs static const struct nvkm_disp_mthd_list
743acbe9ecfSBen Skeggs nv50_disp_base_mthd_base = {
744acbe9ecfSBen Skeggs .mthd = 0x0000,
745acbe9ecfSBen Skeggs .addr = 0x000000,
746acbe9ecfSBen Skeggs .data = {
747acbe9ecfSBen Skeggs { 0x0080, 0x000000 },
748acbe9ecfSBen Skeggs { 0x0084, 0x0008c4 },
749acbe9ecfSBen Skeggs { 0x0088, 0x0008d0 },
750acbe9ecfSBen Skeggs { 0x008c, 0x0008dc },
751acbe9ecfSBen Skeggs { 0x0090, 0x0008e4 },
752acbe9ecfSBen Skeggs { 0x0094, 0x610884 },
753acbe9ecfSBen Skeggs { 0x00a0, 0x6108a0 },
754acbe9ecfSBen Skeggs { 0x00a4, 0x610878 },
755acbe9ecfSBen Skeggs { 0x00c0, 0x61086c },
756acbe9ecfSBen Skeggs { 0x00e0, 0x610858 },
757acbe9ecfSBen Skeggs { 0x00e4, 0x610860 },
758acbe9ecfSBen Skeggs { 0x00e8, 0x6108ac },
759acbe9ecfSBen Skeggs { 0x00ec, 0x6108b4 },
760acbe9ecfSBen Skeggs { 0x0100, 0x610894 },
761acbe9ecfSBen Skeggs { 0x0110, 0x6108bc },
762acbe9ecfSBen Skeggs { 0x0114, 0x61088c },
763acbe9ecfSBen Skeggs {}
764acbe9ecfSBen Skeggs }
765acbe9ecfSBen Skeggs };
766acbe9ecfSBen Skeggs
767acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list
768acbe9ecfSBen Skeggs nv50_disp_base_mthd_image = {
769acbe9ecfSBen Skeggs .mthd = 0x0400,
770acbe9ecfSBen Skeggs .addr = 0x000000,
771acbe9ecfSBen Skeggs .data = {
772acbe9ecfSBen Skeggs { 0x0800, 0x6108f0 },
773acbe9ecfSBen Skeggs { 0x0804, 0x6108fc },
774acbe9ecfSBen Skeggs { 0x0808, 0x61090c },
775acbe9ecfSBen Skeggs { 0x080c, 0x610914 },
776acbe9ecfSBen Skeggs { 0x0810, 0x610904 },
777acbe9ecfSBen Skeggs {}
778acbe9ecfSBen Skeggs }
779acbe9ecfSBen Skeggs };
780acbe9ecfSBen Skeggs
781acbe9ecfSBen Skeggs static const struct nvkm_disp_chan_mthd
782acbe9ecfSBen Skeggs nv50_disp_base_mthd = {
783acbe9ecfSBen Skeggs .name = "Base",
784acbe9ecfSBen Skeggs .addr = 0x000540,
785acbe9ecfSBen Skeggs .prev = 0x000004,
786acbe9ecfSBen Skeggs .data = {
787acbe9ecfSBen Skeggs { "Global", 1, &nv50_disp_base_mthd_base },
788acbe9ecfSBen Skeggs { "Image", 2, &nv50_disp_base_mthd_image },
789acbe9ecfSBen Skeggs {}
790acbe9ecfSBen Skeggs }
791acbe9ecfSBen Skeggs };
792acbe9ecfSBen Skeggs
793889fcbe9SBen Skeggs static const struct nvkm_disp_chan_user
794889fcbe9SBen Skeggs nv50_disp_base = {
795889fcbe9SBen Skeggs .func = &nv50_disp_dmac_func,
796889fcbe9SBen Skeggs .ctrl = 1,
797889fcbe9SBen Skeggs .user = 1,
798889fcbe9SBen Skeggs .mthd = &nv50_disp_base_mthd,
799889fcbe9SBen Skeggs };
800acbe9ecfSBen Skeggs
801acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list
802acbe9ecfSBen Skeggs nv50_disp_core_mthd_base = {
803acbe9ecfSBen Skeggs .mthd = 0x0000,
804acbe9ecfSBen Skeggs .addr = 0x000000,
805acbe9ecfSBen Skeggs .data = {
806acbe9ecfSBen Skeggs { 0x0080, 0x000000 },
807acbe9ecfSBen Skeggs { 0x0084, 0x610bb8 },
808acbe9ecfSBen Skeggs { 0x0088, 0x610b9c },
809acbe9ecfSBen Skeggs { 0x008c, 0x000000 },
810acbe9ecfSBen Skeggs {}
811acbe9ecfSBen Skeggs }
812acbe9ecfSBen Skeggs };
813acbe9ecfSBen Skeggs
814acbe9ecfSBen Skeggs static const struct nvkm_disp_mthd_list
815acbe9ecfSBen Skeggs nv50_disp_core_mthd_dac = {
816acbe9ecfSBen Skeggs .mthd = 0x0080,
817acbe9ecfSBen Skeggs .addr = 0x000008,
818acbe9ecfSBen Skeggs .data = {
819acbe9ecfSBen Skeggs { 0x0400, 0x610b58 },
820acbe9ecfSBen Skeggs { 0x0404, 0x610bdc },
821acbe9ecfSBen Skeggs { 0x0420, 0x610828 },
822acbe9ecfSBen Skeggs {}
823acbe9ecfSBen Skeggs }
824acbe9ecfSBen Skeggs };
825acbe9ecfSBen Skeggs
826acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list
827acbe9ecfSBen Skeggs nv50_disp_core_mthd_sor = {
828acbe9ecfSBen Skeggs .mthd = 0x0040,
829acbe9ecfSBen Skeggs .addr = 0x000008,
830acbe9ecfSBen Skeggs .data = {
831acbe9ecfSBen Skeggs { 0x0600, 0x610b70 },
832acbe9ecfSBen Skeggs {}
833acbe9ecfSBen Skeggs }
834acbe9ecfSBen Skeggs };
835acbe9ecfSBen Skeggs
836acbe9ecfSBen Skeggs const struct nvkm_disp_mthd_list
837acbe9ecfSBen Skeggs nv50_disp_core_mthd_pior = {
838acbe9ecfSBen Skeggs .mthd = 0x0040,
839acbe9ecfSBen Skeggs .addr = 0x000008,
840acbe9ecfSBen Skeggs .data = {
841acbe9ecfSBen Skeggs { 0x0700, 0x610b80 },
842acbe9ecfSBen Skeggs {}
843acbe9ecfSBen Skeggs }
844acbe9ecfSBen Skeggs };
845acbe9ecfSBen Skeggs
846acbe9ecfSBen Skeggs static const struct nvkm_disp_mthd_list
847acbe9ecfSBen Skeggs nv50_disp_core_mthd_head = {
848acbe9ecfSBen Skeggs .mthd = 0x0400,
849acbe9ecfSBen Skeggs .addr = 0x000540,
850acbe9ecfSBen Skeggs .data = {
851acbe9ecfSBen Skeggs { 0x0800, 0x610ad8 },
852acbe9ecfSBen Skeggs { 0x0804, 0x610ad0 },
853acbe9ecfSBen Skeggs { 0x0808, 0x610a48 },
854acbe9ecfSBen Skeggs { 0x080c, 0x610a78 },
855acbe9ecfSBen Skeggs { 0x0810, 0x610ac0 },
856acbe9ecfSBen Skeggs { 0x0814, 0x610af8 },
857acbe9ecfSBen Skeggs { 0x0818, 0x610b00 },
858acbe9ecfSBen Skeggs { 0x081c, 0x610ae8 },
859acbe9ecfSBen Skeggs { 0x0820, 0x610af0 },
860acbe9ecfSBen Skeggs { 0x0824, 0x610b08 },
861acbe9ecfSBen Skeggs { 0x0828, 0x610b10 },
862acbe9ecfSBen Skeggs { 0x082c, 0x610a68 },
863acbe9ecfSBen Skeggs { 0x0830, 0x610a60 },
864acbe9ecfSBen Skeggs { 0x0834, 0x000000 },
865acbe9ecfSBen Skeggs { 0x0838, 0x610a40 },
866acbe9ecfSBen Skeggs { 0x0840, 0x610a24 },
867acbe9ecfSBen Skeggs { 0x0844, 0x610a2c },
868acbe9ecfSBen Skeggs { 0x0848, 0x610aa8 },
869acbe9ecfSBen Skeggs { 0x084c, 0x610ab0 },
870acbe9ecfSBen Skeggs { 0x0860, 0x610a84 },
871acbe9ecfSBen Skeggs { 0x0864, 0x610a90 },
872acbe9ecfSBen Skeggs { 0x0868, 0x610b18 },
873acbe9ecfSBen Skeggs { 0x086c, 0x610b20 },
874acbe9ecfSBen Skeggs { 0x0870, 0x610ac8 },
875acbe9ecfSBen Skeggs { 0x0874, 0x610a38 },
876acbe9ecfSBen Skeggs { 0x0880, 0x610a58 },
877acbe9ecfSBen Skeggs { 0x0884, 0x610a9c },
878acbe9ecfSBen Skeggs { 0x08a0, 0x610a70 },
879acbe9ecfSBen Skeggs { 0x08a4, 0x610a50 },
880acbe9ecfSBen Skeggs { 0x08a8, 0x610ae0 },
881acbe9ecfSBen Skeggs { 0x08c0, 0x610b28 },
882acbe9ecfSBen Skeggs { 0x08c4, 0x610b30 },
883acbe9ecfSBen Skeggs { 0x08c8, 0x610b40 },
884acbe9ecfSBen Skeggs { 0x08d4, 0x610b38 },
885acbe9ecfSBen Skeggs { 0x08d8, 0x610b48 },
886acbe9ecfSBen Skeggs { 0x08dc, 0x610b50 },
887acbe9ecfSBen Skeggs { 0x0900, 0x610a18 },
888acbe9ecfSBen Skeggs { 0x0904, 0x610ab8 },
889acbe9ecfSBen Skeggs {}
890acbe9ecfSBen Skeggs }
891acbe9ecfSBen Skeggs };
892acbe9ecfSBen Skeggs
893acbe9ecfSBen Skeggs static const struct nvkm_disp_chan_mthd
894acbe9ecfSBen Skeggs nv50_disp_core_mthd = {
895acbe9ecfSBen Skeggs .name = "Core",
896acbe9ecfSBen Skeggs .addr = 0x000000,
897acbe9ecfSBen Skeggs .prev = 0x000004,
898acbe9ecfSBen Skeggs .data = {
899acbe9ecfSBen Skeggs { "Global", 1, &nv50_disp_core_mthd_base },
900acbe9ecfSBen Skeggs { "DAC", 3, &nv50_disp_core_mthd_dac },
901acbe9ecfSBen Skeggs { "SOR", 2, &nv50_disp_core_mthd_sor },
902acbe9ecfSBen Skeggs { "PIOR", 3, &nv50_disp_core_mthd_pior },
903acbe9ecfSBen Skeggs { "HEAD", 2, &nv50_disp_core_mthd_head },
904acbe9ecfSBen Skeggs {}
905acbe9ecfSBen Skeggs }
906acbe9ecfSBen Skeggs };
907acbe9ecfSBen Skeggs
908acbe9ecfSBen Skeggs static void
nv50_disp_core_fini(struct nvkm_disp_chan * chan)909acbe9ecfSBen Skeggs nv50_disp_core_fini(struct nvkm_disp_chan *chan)
910acbe9ecfSBen Skeggs {
911acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &chan->disp->engine.subdev;
912acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
913acbe9ecfSBen Skeggs
914acbe9ecfSBen Skeggs /* deactivate channel */
915acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200, 0x00000010, 0x00000000);
916acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200, 0x00000003, 0x00000000);
917acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
918acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200) & 0x001e0000))
919acbe9ecfSBen Skeggs break;
920acbe9ecfSBen Skeggs ) < 0) {
921acbe9ecfSBen Skeggs nvkm_error(subdev, "core fini: %08x\n",
922acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200));
923acbe9ecfSBen Skeggs }
924acbe9ecfSBen Skeggs
925acbe9ecfSBen Skeggs chan->suspend_put = nvkm_rd32(device, 0x640000);
926acbe9ecfSBen Skeggs }
927acbe9ecfSBen Skeggs
928acbe9ecfSBen Skeggs static int
nv50_disp_core_init(struct nvkm_disp_chan * chan)929acbe9ecfSBen Skeggs nv50_disp_core_init(struct nvkm_disp_chan *chan)
930acbe9ecfSBen Skeggs {
931acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &chan->disp->engine.subdev;
932acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
933acbe9ecfSBen Skeggs
934acbe9ecfSBen Skeggs /* attempt to unstick channel from some unknown state */
935acbe9ecfSBen Skeggs if ((nvkm_rd32(device, 0x610200) & 0x009f0000) == 0x00020000)
936acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200, 0x00800000, 0x00800000);
937acbe9ecfSBen Skeggs if ((nvkm_rd32(device, 0x610200) & 0x003f0000) == 0x00030000)
938acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200, 0x00600000, 0x00600000);
939acbe9ecfSBen Skeggs
940acbe9ecfSBen Skeggs /* initialise channel for dma command submission */
941acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610204, chan->push);
942acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610208, 0x00010000);
943acbe9ecfSBen Skeggs nvkm_wr32(device, 0x61020c, 0x00000000);
944acbe9ecfSBen Skeggs nvkm_mask(device, 0x610200, 0x00000010, 0x00000010);
945acbe9ecfSBen Skeggs nvkm_wr32(device, 0x640000, chan->suspend_put);
946acbe9ecfSBen Skeggs nvkm_wr32(device, 0x610200, 0x01000013);
947acbe9ecfSBen Skeggs
948acbe9ecfSBen Skeggs /* wait for it to go inactive */
949acbe9ecfSBen Skeggs if (nvkm_msec(device, 2000,
950acbe9ecfSBen Skeggs if (!(nvkm_rd32(device, 0x610200) & 0x80000000))
951acbe9ecfSBen Skeggs break;
952acbe9ecfSBen Skeggs ) < 0) {
953acbe9ecfSBen Skeggs nvkm_error(subdev, "core init: %08x\n",
954acbe9ecfSBen Skeggs nvkm_rd32(device, 0x610200));
955acbe9ecfSBen Skeggs return -EBUSY;
956acbe9ecfSBen Skeggs }
957acbe9ecfSBen Skeggs
958acbe9ecfSBen Skeggs return 0;
959acbe9ecfSBen Skeggs }
960acbe9ecfSBen Skeggs
961acbe9ecfSBen Skeggs const struct nvkm_disp_chan_func
962acbe9ecfSBen Skeggs nv50_disp_core_func = {
963889fcbe9SBen Skeggs .push = nv50_disp_dmac_push,
964acbe9ecfSBen Skeggs .init = nv50_disp_core_init,
965acbe9ecfSBen Skeggs .fini = nv50_disp_core_fini,
966acbe9ecfSBen Skeggs .intr = nv50_disp_chan_intr,
967acbe9ecfSBen Skeggs .user = nv50_disp_chan_user,
968acbe9ecfSBen Skeggs .bind = nv50_disp_dmac_bind,
969acbe9ecfSBen Skeggs };
970acbe9ecfSBen Skeggs
971889fcbe9SBen Skeggs static const struct nvkm_disp_chan_user
972889fcbe9SBen Skeggs nv50_disp_core = {
973889fcbe9SBen Skeggs .func = &nv50_disp_core_func,
974889fcbe9SBen Skeggs .ctrl = 0,
975889fcbe9SBen Skeggs .user = 0,
976889fcbe9SBen Skeggs .mthd = &nv50_disp_core_mthd,
977889fcbe9SBen Skeggs };
97870aa8670SBen Skeggs
979327c5581SBen Skeggs static u32
nv50_disp_super_iedt(struct nvkm_head * head,struct nvkm_outp * outp,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_outp * iedt)980327c5581SBen Skeggs nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,
981327c5581SBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
982327c5581SBen Skeggs struct nvbios_outp *iedt)
983327c5581SBen Skeggs {
984327c5581SBen Skeggs struct nvkm_bios *bios = head->disp->engine.subdev.device->bios;
985327c5581SBen Skeggs const u8 l = ffs(outp->info.link);
986327c5581SBen Skeggs const u16 t = outp->info.hasht;
987327c5581SBen Skeggs const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or;
988327c5581SBen Skeggs u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt);
989327c5581SBen Skeggs if (!data)
990327c5581SBen Skeggs OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m);
991327c5581SBen Skeggs return data;
992327c5581SBen Skeggs }
993327c5581SBen Skeggs
994327c5581SBen Skeggs static void
nv50_disp_super_ied_on(struct nvkm_head * head,struct nvkm_ior * ior,int id,u32 khz)9958d7ef84dSBen Skeggs nv50_disp_super_ied_on(struct nvkm_head *head,
9968d7ef84dSBen Skeggs struct nvkm_ior *ior, int id, u32 khz)
9978d7ef84dSBen Skeggs {
9988d7ef84dSBen Skeggs struct nvkm_subdev *subdev = &head->disp->engine.subdev;
9998d7ef84dSBen Skeggs struct nvkm_bios *bios = subdev->device->bios;
10008d7ef84dSBen Skeggs struct nvkm_outp *outp = ior->asy.outp;
10018d7ef84dSBen Skeggs struct nvbios_ocfg iedtrs;
10028d7ef84dSBen Skeggs struct nvbios_outp iedt;
10038d7ef84dSBen Skeggs u8 ver, hdr, cnt, len, flags = 0x00;
10048d7ef84dSBen Skeggs u32 data;
10058d7ef84dSBen Skeggs
10068d7ef84dSBen Skeggs if (!outp) {
10078d7ef84dSBen Skeggs IOR_DBG(ior, "nothing to attach");
10088d7ef84dSBen Skeggs return;
10098d7ef84dSBen Skeggs }
10108d7ef84dSBen Skeggs
10118d7ef84dSBen Skeggs /* Lookup IED table for the device. */
10128d7ef84dSBen Skeggs data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
10138d7ef84dSBen Skeggs if (!data)
10148d7ef84dSBen Skeggs return;
10158d7ef84dSBen Skeggs
10168d7ef84dSBen Skeggs /* Lookup IEDT runtime settings for the current configuration. */
10178d7ef84dSBen Skeggs if (ior->type == SOR) {
10188d7ef84dSBen Skeggs if (ior->asy.proto == LVDS) {
10198d7ef84dSBen Skeggs if (head->asy.or.depth == 24)
10208d7ef84dSBen Skeggs flags |= 0x02;
10218d7ef84dSBen Skeggs }
10228d7ef84dSBen Skeggs if (ior->asy.link == 3)
10238d7ef84dSBen Skeggs flags |= 0x01;
10248d7ef84dSBen Skeggs }
10258d7ef84dSBen Skeggs
10268d7ef84dSBen Skeggs data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags,
10278d7ef84dSBen Skeggs &ver, &hdr, &cnt, &len, &iedtrs);
10288d7ef84dSBen Skeggs if (!data) {
10298d7ef84dSBen Skeggs OUTP_DBG(outp, "missing IEDT RS for %02x:%02x",
10308d7ef84dSBen Skeggs ior->asy.proto_evo, flags);
10318d7ef84dSBen Skeggs return;
10328d7ef84dSBen Skeggs }
10338d7ef84dSBen Skeggs
10348d7ef84dSBen Skeggs /* Execute the OnInt[23] script for the current frequency. */
10358d7ef84dSBen Skeggs data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz);
10368d7ef84dSBen Skeggs if (!data) {
10378d7ef84dSBen Skeggs OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz",
10388d7ef84dSBen Skeggs id, ior->asy.proto_evo, flags, khz);
10398d7ef84dSBen Skeggs return;
10408d7ef84dSBen Skeggs }
10418d7ef84dSBen Skeggs
10428d7ef84dSBen Skeggs nvbios_init(subdev, data,
10438d7ef84dSBen Skeggs init.outp = &outp->info;
10448d7ef84dSBen Skeggs init.or = ior->id;
10458d7ef84dSBen Skeggs init.link = ior->asy.link;
10468d7ef84dSBen Skeggs init.head = head->id;
10478d7ef84dSBen Skeggs );
10488d7ef84dSBen Skeggs }
10498d7ef84dSBen Skeggs
10508d7ef84dSBen Skeggs static void
nv50_disp_super_ied_off(struct nvkm_head * head,struct nvkm_ior * ior,int id)1051327c5581SBen Skeggs nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
1052327c5581SBen Skeggs {
1053327c5581SBen Skeggs struct nvkm_outp *outp = ior->arm.outp;
1054327c5581SBen Skeggs struct nvbios_outp iedt;
1055327c5581SBen Skeggs u8 ver, hdr, cnt, len;
1056327c5581SBen Skeggs u32 data;
1057327c5581SBen Skeggs
1058327c5581SBen Skeggs if (!outp) {
1059327c5581SBen Skeggs IOR_DBG(ior, "nothing attached");
1060327c5581SBen Skeggs return;
1061327c5581SBen Skeggs }
1062327c5581SBen Skeggs
1063327c5581SBen Skeggs data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
1064327c5581SBen Skeggs if (!data)
1065327c5581SBen Skeggs return;
1066327c5581SBen Skeggs
1067327c5581SBen Skeggs nvbios_init(&head->disp->engine.subdev, iedt.script[id],
1068327c5581SBen Skeggs init.outp = &outp->info;
1069327c5581SBen Skeggs init.or = ior->id;
1070327c5581SBen Skeggs init.link = ior->arm.link;
1071327c5581SBen Skeggs init.head = head->id;
1072327c5581SBen Skeggs );
1073327c5581SBen Skeggs }
1074327c5581SBen Skeggs
1075327c5581SBen Skeggs static struct nvkm_ior *
nv50_disp_super_ior_asy(struct nvkm_head * head)10768d7ef84dSBen Skeggs nv50_disp_super_ior_asy(struct nvkm_head *head)
10778d7ef84dSBen Skeggs {
10788d7ef84dSBen Skeggs struct nvkm_ior *ior;
107992fba5d3SBen Skeggs list_for_each_entry(ior, &head->disp->iors, head) {
10808d7ef84dSBen Skeggs if (ior->asy.head & (1 << head->id)) {
10818d7ef84dSBen Skeggs HEAD_DBG(head, "to %s", ior->name);
10828d7ef84dSBen Skeggs return ior;
10838d7ef84dSBen Skeggs }
10848d7ef84dSBen Skeggs }
10858d7ef84dSBen Skeggs HEAD_DBG(head, "nothing to attach");
10868d7ef84dSBen Skeggs return NULL;
10878d7ef84dSBen Skeggs }
10888d7ef84dSBen Skeggs
10898d7ef84dSBen Skeggs static struct nvkm_ior *
nv50_disp_super_ior_arm(struct nvkm_head * head)1090327c5581SBen Skeggs nv50_disp_super_ior_arm(struct nvkm_head *head)
1091327c5581SBen Skeggs {
1092327c5581SBen Skeggs struct nvkm_ior *ior;
109392fba5d3SBen Skeggs list_for_each_entry(ior, &head->disp->iors, head) {
1094327c5581SBen Skeggs if (ior->arm.head & (1 << head->id)) {
1095327c5581SBen Skeggs HEAD_DBG(head, "on %s", ior->name);
1096327c5581SBen Skeggs return ior;
1097327c5581SBen Skeggs }
1098327c5581SBen Skeggs }
1099327c5581SBen Skeggs HEAD_DBG(head, "nothing attached");
1100327c5581SBen Skeggs return NULL;
1101327c5581SBen Skeggs }
1102327c5581SBen Skeggs
11030d93cd92SBen Skeggs void
nv50_disp_super_3_0(struct nvkm_disp * disp,struct nvkm_head * head)110492fba5d3SBen Skeggs nv50_disp_super_3_0(struct nvkm_disp *disp, struct nvkm_head *head)
1105c39f472eSBen Skeggs {
11060d93cd92SBen Skeggs struct nvkm_ior *ior;
1107c39f472eSBen Skeggs
11080d93cd92SBen Skeggs /* Determine which OR, if any, we're attaching to the head. */
11090d93cd92SBen Skeggs HEAD_DBG(head, "supervisor 3.0");
11100d93cd92SBen Skeggs ior = nv50_disp_super_ior_asy(head);
11110d93cd92SBen Skeggs if (!ior)
1112af85389cSBen Skeggs return;
1113af85389cSBen Skeggs
11140d93cd92SBen Skeggs /* Execute OnInt3 IED script. */
11150d93cd92SBen Skeggs nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000);
11160d93cd92SBen Skeggs
11170d93cd92SBen Skeggs /* OR-specific handling. */
11180d93cd92SBen Skeggs if (ior->func->war_3)
11190d93cd92SBen Skeggs ior->func->war_3(ior);
1120c39f472eSBen Skeggs }
1121c39f472eSBen Skeggs
1122c39f472eSBen Skeggs static void
nv50_disp_super_2_2_dp(struct nvkm_head * head,struct nvkm_ior * ior)11238d7ef84dSBen Skeggs nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior)
1124c39f472eSBen Skeggs {
11258d7ef84dSBen Skeggs struct nvkm_subdev *subdev = &head->disp->engine.subdev;
11268d7ef84dSBen Skeggs const u32 khz = head->asy.hz / 1000;
11278d7ef84dSBen Skeggs const u32 linkKBps = ior->dp.bw * 27000;
1128c39f472eSBen Skeggs const u32 symbol = 100000;
1129c39f472eSBen Skeggs int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
1130c39f472eSBen Skeggs int TU, VTUi, VTUf, VTUa;
1131c39f472eSBen Skeggs u64 link_data_rate, link_ratio, unk;
1132c39f472eSBen Skeggs u32 best_diff = 64 * symbol;
11338d7ef84dSBen Skeggs u64 h, v;
1134c39f472eSBen Skeggs
1135c39f472eSBen Skeggs /* symbols/hblank - algorithm taken from comments in tegra driver */
11368d7ef84dSBen Skeggs h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7;
11378d7ef84dSBen Skeggs h = h * linkKBps;
11388d7ef84dSBen Skeggs do_div(h, khz);
11398d7ef84dSBen Skeggs h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr);
1140c39f472eSBen Skeggs
1141c39f472eSBen Skeggs /* symbols/vblank - algorithm taken from comments in tegra driver */
11428d7ef84dSBen Skeggs v = head->asy.vblanks - head->asy.vblanke - 25;
11438d7ef84dSBen Skeggs v = v * linkKBps;
11448d7ef84dSBen Skeggs do_div(v, khz);
11458d7ef84dSBen Skeggs v = v - ((36 / ior->dp.nr) + 3) - 1;
11468d7ef84dSBen Skeggs
11479a4514fbSBen Skeggs ior->func->dp->audio_sym(ior, head->id, h, v);
1148c39f472eSBen Skeggs
1149c39f472eSBen Skeggs /* watermark / activesym */
11508d7ef84dSBen Skeggs link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr;
1151c39f472eSBen Skeggs
1152c39f472eSBen Skeggs /* calculate ratio of packed data rate to link symbol rate */
1153c39f472eSBen Skeggs link_ratio = link_data_rate * symbol;
11548d7ef84dSBen Skeggs do_div(link_ratio, linkKBps);
1155c39f472eSBen Skeggs
11569a4514fbSBen Skeggs for (TU = 64; ior->func->dp->activesym && TU >= 32; TU--) {
1157c39f472eSBen Skeggs /* calculate average number of valid symbols in each TU */
1158c39f472eSBen Skeggs u32 tu_valid = link_ratio * TU;
1159c39f472eSBen Skeggs u32 calc, diff;
1160c39f472eSBen Skeggs
1161c39f472eSBen Skeggs /* find a hw representation for the fraction.. */
1162c39f472eSBen Skeggs VTUi = tu_valid / symbol;
1163c39f472eSBen Skeggs calc = VTUi * symbol;
1164c39f472eSBen Skeggs diff = tu_valid - calc;
1165c39f472eSBen Skeggs if (diff) {
1166c39f472eSBen Skeggs if (diff >= (symbol / 2)) {
1167c39f472eSBen Skeggs VTUf = symbol / (symbol - diff);
1168c39f472eSBen Skeggs if (symbol - (VTUf * diff))
1169c39f472eSBen Skeggs VTUf++;
1170c39f472eSBen Skeggs
1171c39f472eSBen Skeggs if (VTUf <= 15) {
1172c39f472eSBen Skeggs VTUa = 1;
1173c39f472eSBen Skeggs calc += symbol - (symbol / VTUf);
1174c39f472eSBen Skeggs } else {
1175c39f472eSBen Skeggs VTUa = 0;
1176c39f472eSBen Skeggs VTUf = 1;
1177c39f472eSBen Skeggs calc += symbol;
1178c39f472eSBen Skeggs }
1179c39f472eSBen Skeggs } else {
1180c39f472eSBen Skeggs VTUa = 0;
1181c39f472eSBen Skeggs VTUf = min((int)(symbol / diff), 15);
1182c39f472eSBen Skeggs calc += symbol / VTUf;
1183c39f472eSBen Skeggs }
1184c39f472eSBen Skeggs
1185c39f472eSBen Skeggs diff = calc - tu_valid;
1186c39f472eSBen Skeggs } else {
1187c39f472eSBen Skeggs /* no remainder, but the hw doesn't like the fractional
1188c39f472eSBen Skeggs * part to be zero. decrement the integer part and
1189c39f472eSBen Skeggs * have the fraction add a whole symbol back
1190c39f472eSBen Skeggs */
1191c39f472eSBen Skeggs VTUa = 0;
1192c39f472eSBen Skeggs VTUf = 1;
1193c39f472eSBen Skeggs VTUi--;
1194c39f472eSBen Skeggs }
1195c39f472eSBen Skeggs
1196c39f472eSBen Skeggs if (diff < best_diff) {
1197c39f472eSBen Skeggs best_diff = diff;
1198c39f472eSBen Skeggs bestTU = TU;
1199c39f472eSBen Skeggs bestVTUa = VTUa;
1200c39f472eSBen Skeggs bestVTUf = VTUf;
1201c39f472eSBen Skeggs bestVTUi = VTUi;
1202c39f472eSBen Skeggs if (diff == 0)
1203c39f472eSBen Skeggs break;
1204c39f472eSBen Skeggs }
1205c39f472eSBen Skeggs }
1206c39f472eSBen Skeggs
12079a4514fbSBen Skeggs if (ior->func->dp->activesym) {
1208c39f472eSBen Skeggs if (!bestTU) {
12098d7ef84dSBen Skeggs nvkm_error(subdev, "unable to determine dp config\n");
1210c39f472eSBen Skeggs return;
1211c39f472eSBen Skeggs }
12129a4514fbSBen Skeggs
12139a4514fbSBen Skeggs ior->func->dp->activesym(ior, head->id, bestTU, bestVTUa, bestVTUf, bestVTUi);
12148d7ef84dSBen Skeggs } else {
12158d7ef84dSBen Skeggs bestTU = 64;
12168d7ef84dSBen Skeggs }
1217c39f472eSBen Skeggs
1218c39f472eSBen Skeggs /* XXX close to vbios numbers, but not right */
1219c39f472eSBen Skeggs unk = (symbol - link_ratio) * bestTU;
1220c39f472eSBen Skeggs unk *= link_ratio;
1221c39f472eSBen Skeggs do_div(unk, symbol);
1222c39f472eSBen Skeggs do_div(unk, symbol);
1223c39f472eSBen Skeggs unk += 6;
1224c39f472eSBen Skeggs
12259a4514fbSBen Skeggs ior->func->dp->watermark(ior, head->id, unk);
1226c39f472eSBen Skeggs }
1227c39f472eSBen Skeggs
12288d7ef84dSBen Skeggs void
nv50_disp_super_2_2(struct nvkm_disp * disp,struct nvkm_head * head)122992fba5d3SBen Skeggs nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head)
1230c39f472eSBen Skeggs {
12318d7ef84dSBen Skeggs const u32 khz = head->asy.hz / 1000;
12328d7ef84dSBen Skeggs struct nvkm_outp *outp;
12338d7ef84dSBen Skeggs struct nvkm_ior *ior;
1234c39f472eSBen Skeggs
12358d7ef84dSBen Skeggs /* Determine which OR, if any, we're attaching from the head. */
12368d7ef84dSBen Skeggs HEAD_DBG(head, "supervisor 2.2");
12378d7ef84dSBen Skeggs ior = nv50_disp_super_ior_asy(head);
12388d7ef84dSBen Skeggs if (!ior)
1239c39f472eSBen Skeggs return;
1240c39f472eSBen Skeggs
12419793083fSBen Skeggs outp = ior->asy.outp;
12429793083fSBen Skeggs
12438d7ef84dSBen Skeggs /* For some reason, NVIDIA decided not to:
1244c39f472eSBen Skeggs *
12458d7ef84dSBen Skeggs * A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
12468d7ef84dSBen Skeggs * and
12478d7ef84dSBen Skeggs * B) Use SetControlOutputResource.PixelDepth on LVDS.
1248c39f472eSBen Skeggs *
12498d7ef84dSBen Skeggs * Override the values we usually read from HW with the same
12508d7ef84dSBen Skeggs * data we pass though an ioctl instead.
1251c39f472eSBen Skeggs */
12529793083fSBen Skeggs if (outp && ior->type == SOR && ior->asy.proto == LVDS) {
12539793083fSBen Skeggs head->asy.or.depth = outp->lvds.bpc8 ? 24 : 18;
12549793083fSBen Skeggs ior->asy.link = outp->lvds.dual ? 3 : 1;
1255c39f472eSBen Skeggs }
1256c39f472eSBen Skeggs
12578d7ef84dSBen Skeggs /* Handle any link training, etc. */
12589793083fSBen Skeggs if (outp && outp->func->acquire)
12598d7ef84dSBen Skeggs outp->func->acquire(outp);
1260c39f472eSBen Skeggs
12618d7ef84dSBen Skeggs /* Execute OnInt2 IED script. */
12628d7ef84dSBen Skeggs nv50_disp_super_ied_on(head, ior, 0, khz);
1263c39f472eSBen Skeggs
12648d7ef84dSBen Skeggs /* Program RG clock divider. */
12658d7ef84dSBen Skeggs head->func->rgclk(head, ior->asy.rgdiv);
1266c39f472eSBen Skeggs
12678d7ef84dSBen Skeggs /* Mode-specific internal DP configuration. */
12688d7ef84dSBen Skeggs if (ior->type == SOR && ior->asy.proto == DP)
12698d7ef84dSBen Skeggs nv50_disp_super_2_2_dp(head, ior);
1270c39f472eSBen Skeggs
12718d7ef84dSBen Skeggs /* OR-specific handling. */
12728d7ef84dSBen Skeggs ior->func->clock(ior);
12738d7ef84dSBen Skeggs if (ior->func->war_2)
12748d7ef84dSBen Skeggs ior->func->war_2(ior);
1275c39f472eSBen Skeggs }
1276c39f472eSBen Skeggs
12771f0c9eafSBen Skeggs void
nv50_disp_super_2_1(struct nvkm_disp * disp,struct nvkm_head * head)127892fba5d3SBen Skeggs nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head)
1279c39f472eSBen Skeggs {
128092fba5d3SBen Skeggs struct nvkm_devinit *devinit = disp->engine.subdev.device->devinit;
12818d7ef84dSBen Skeggs const u32 khz = head->asy.hz / 1000;
12821f0c9eafSBen Skeggs HEAD_DBG(head, "supervisor 2.1 - %d khz", khz);
12831f0c9eafSBen Skeggs if (khz)
12841f0c9eafSBen Skeggs nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz);
1285c39f472eSBen Skeggs }
1286c39f472eSBen Skeggs
1287d52e948cSBen Skeggs void
nv50_disp_super_2_0(struct nvkm_disp * disp,struct nvkm_head * head)128892fba5d3SBen Skeggs nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head)
1289c39f472eSBen Skeggs {
1290d52e948cSBen Skeggs struct nvkm_outp *outp;
1291d52e948cSBen Skeggs struct nvkm_ior *ior;
1292c39f472eSBen Skeggs
1293d52e948cSBen Skeggs /* Determine which OR, if any, we're detaching from the head. */
1294d52e948cSBen Skeggs HEAD_DBG(head, "supervisor 2.0");
1295d52e948cSBen Skeggs ior = nv50_disp_super_ior_arm(head);
1296d52e948cSBen Skeggs if (!ior)
1297d52e948cSBen Skeggs return;
1298d52e948cSBen Skeggs
1299d52e948cSBen Skeggs /* Execute OffInt2 IED script. */
1300d52e948cSBen Skeggs nv50_disp_super_ied_off(head, ior, 2);
1301d52e948cSBen Skeggs
1302d52e948cSBen Skeggs /* If we're shutting down the OR's only active head, execute
1303e04cfdc9SBen Skeggs * the output path's disable function.
1304af85389cSBen Skeggs */
1305d52e948cSBen Skeggs if (ior->arm.head == (1 << head->id)) {
1306e04cfdc9SBen Skeggs if ((outp = ior->arm.outp) && outp->func->disable)
1307e04cfdc9SBen Skeggs outp->func->disable(outp, ior);
1308af85389cSBen Skeggs }
1309af85389cSBen Skeggs }
1310af85389cSBen Skeggs
1311327c5581SBen Skeggs void
nv50_disp_super_1_0(struct nvkm_disp * disp,struct nvkm_head * head)131292fba5d3SBen Skeggs nv50_disp_super_1_0(struct nvkm_disp *disp, struct nvkm_head *head)
1313af85389cSBen Skeggs {
1314327c5581SBen Skeggs struct nvkm_ior *ior;
1315327c5581SBen Skeggs
1316327c5581SBen Skeggs /* Determine which OR, if any, we're detaching from the head. */
1317327c5581SBen Skeggs HEAD_DBG(head, "supervisor 1.0");
1318327c5581SBen Skeggs ior = nv50_disp_super_ior_arm(head);
1319327c5581SBen Skeggs if (!ior)
1320327c5581SBen Skeggs return;
1321327c5581SBen Skeggs
1322327c5581SBen Skeggs /* Execute OffInt1 IED script. */
1323327c5581SBen Skeggs nv50_disp_super_ied_off(head, ior, 1);
1324c39f472eSBen Skeggs }
1325c39f472eSBen Skeggs
1326c39f472eSBen Skeggs void
nv50_disp_super_1(struct nvkm_disp * disp)132792fba5d3SBen Skeggs nv50_disp_super_1(struct nvkm_disp *disp)
132829c0ca73SBen Skeggs {
132929c0ca73SBen Skeggs struct nvkm_head *head;
133029c0ca73SBen Skeggs struct nvkm_ior *ior;
133129c0ca73SBen Skeggs
133292fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
133329c0ca73SBen Skeggs head->func->state(head, &head->arm);
133429c0ca73SBen Skeggs head->func->state(head, &head->asy);
133529c0ca73SBen Skeggs }
133629c0ca73SBen Skeggs
133792fba5d3SBen Skeggs list_for_each_entry(ior, &disp->iors, head) {
133829c0ca73SBen Skeggs ior->func->state(ior, &ior->arm);
133929c0ca73SBen Skeggs ior->func->state(ior, &ior->asy);
134029c0ca73SBen Skeggs }
134129c0ca73SBen Skeggs }
134229c0ca73SBen Skeggs
134329c0ca73SBen Skeggs void
nv50_disp_super(struct work_struct * work)1344af85389cSBen Skeggs nv50_disp_super(struct work_struct *work)
1345c39f472eSBen Skeggs {
13463517e6b6SBen Skeggs struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work);
134792fba5d3SBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
134884407824SBen Skeggs struct nvkm_device *device = subdev->device;
1349a1c93078SBen Skeggs struct nvkm_head *head;
1350a6fd8f93SBen Skeggs u32 super;
1351a6fd8f93SBen Skeggs
1352a6fd8f93SBen Skeggs mutex_lock(&disp->super.mutex);
1353a6fd8f93SBen Skeggs super = nvkm_rd32(device, 0x610030);
1354c39f472eSBen Skeggs
13553517e6b6SBen Skeggs nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super.pending, super);
1356c39f472eSBen Skeggs
13573517e6b6SBen Skeggs if (disp->super.pending & 0x00000010) {
13580ce41e3cSBen Skeggs nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);
135929c0ca73SBen Skeggs nv50_disp_super_1(disp);
136092fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1361a1c93078SBen Skeggs if (!(super & (0x00000020 << head->id)))
1362c39f472eSBen Skeggs continue;
1363a1c93078SBen Skeggs if (!(super & (0x00000080 << head->id)))
1364c39f472eSBen Skeggs continue;
1365327c5581SBen Skeggs nv50_disp_super_1_0(disp, head);
1366c39f472eSBen Skeggs }
1367c39f472eSBen Skeggs } else
13683517e6b6SBen Skeggs if (disp->super.pending & 0x00000020) {
136992fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1370a1c93078SBen Skeggs if (!(super & (0x00000080 << head->id)))
1371c39f472eSBen Skeggs continue;
1372d52e948cSBen Skeggs nv50_disp_super_2_0(disp, head);
1373c39f472eSBen Skeggs }
137492fba5d3SBen Skeggs nvkm_outp_route(disp);
137592fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1376a1c93078SBen Skeggs if (!(super & (0x00000200 << head->id)))
1377c39f472eSBen Skeggs continue;
13781f0c9eafSBen Skeggs nv50_disp_super_2_1(disp, head);
1379c39f472eSBen Skeggs }
138092fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1381a1c93078SBen Skeggs if (!(super & (0x00000080 << head->id)))
1382c39f472eSBen Skeggs continue;
13838d7ef84dSBen Skeggs nv50_disp_super_2_2(disp, head);
1384c39f472eSBen Skeggs }
1385c39f472eSBen Skeggs } else
13863517e6b6SBen Skeggs if (disp->super.pending & 0x00000040) {
138792fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1388a1c93078SBen Skeggs if (!(super & (0x00000080 << head->id)))
1389c39f472eSBen Skeggs continue;
13900d93cd92SBen Skeggs nv50_disp_super_3_0(disp, head);
1391c39f472eSBen Skeggs }
1392c39f472eSBen Skeggs }
1393c39f472eSBen Skeggs
13942fde1f1cSBen Skeggs nvkm_wr32(device, 0x610030, 0x80000000);
1395a6fd8f93SBen Skeggs mutex_unlock(&disp->super.mutex);
1396c39f472eSBen Skeggs }
1397c39f472eSBen Skeggs
1398a8ce8b65SBen Skeggs const struct nvkm_enum
1399af85389cSBen Skeggs nv50_disp_intr_error_type[] = {
1400a8ce8b65SBen Skeggs { 0, "NONE" },
1401a8ce8b65SBen Skeggs { 1, "PUSHBUFFER_ERR" },
1402a8ce8b65SBen Skeggs { 2, "TRAP" },
1403a8ce8b65SBen Skeggs { 3, "RESERVED_METHOD" },
1404a8ce8b65SBen Skeggs { 4, "INVALID_ARG" },
1405af85389cSBen Skeggs { 5, "INVALID_STATE" },
1406a8ce8b65SBen Skeggs { 7, "UNRESOLVABLE_HANDLE" },
1407af85389cSBen Skeggs {}
1408af85389cSBen Skeggs };
1409af85389cSBen Skeggs
1410af85389cSBen Skeggs static const struct nvkm_enum
1411af85389cSBen Skeggs nv50_disp_intr_error_code[] = {
1412af85389cSBen Skeggs { 0x00, "" },
1413af85389cSBen Skeggs {}
1414af85389cSBen Skeggs };
1415af85389cSBen Skeggs
1416af85389cSBen Skeggs static void
nv50_disp_intr_error(struct nvkm_disp * disp,int chid)141792fba5d3SBen Skeggs nv50_disp_intr_error(struct nvkm_disp *disp, int chid)
1418af85389cSBen Skeggs {
141992fba5d3SBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
1420af85389cSBen Skeggs struct nvkm_device *device = subdev->device;
1421af85389cSBen Skeggs u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08));
1422af85389cSBen Skeggs u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08));
1423af85389cSBen Skeggs u32 code = (addr & 0x00ff0000) >> 16;
1424af85389cSBen Skeggs u32 type = (addr & 0x00007000) >> 12;
1425af85389cSBen Skeggs u32 mthd = (addr & 0x00000ffc);
1426af85389cSBen Skeggs const struct nvkm_enum *ec, *et;
1427af85389cSBen Skeggs
1428af85389cSBen Skeggs et = nvkm_enum_find(nv50_disp_intr_error_type, type);
1429af85389cSBen Skeggs ec = nvkm_enum_find(nv50_disp_intr_error_code, code);
1430af85389cSBen Skeggs
1431af85389cSBen Skeggs nvkm_error(subdev,
1432af85389cSBen Skeggs "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",
1433af85389cSBen Skeggs type, et ? et->name : "", code, ec ? ec->name : "",
1434af85389cSBen Skeggs chid, mthd, data);
1435af85389cSBen Skeggs
1436af85389cSBen Skeggs if (chid < ARRAY_SIZE(disp->chan)) {
1437af85389cSBen Skeggs switch (mthd) {
1438af85389cSBen Skeggs case 0x0080:
1439af85389cSBen Skeggs nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);
1440af85389cSBen Skeggs break;
1441af85389cSBen Skeggs default:
1442af85389cSBen Skeggs break;
1443af85389cSBen Skeggs }
1444af85389cSBen Skeggs }
1445af85389cSBen Skeggs
1446af85389cSBen Skeggs nvkm_wr32(device, 0x610020, 0x00010000 << chid);
1447af85389cSBen Skeggs nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000);
1448af85389cSBen Skeggs }
1449af85389cSBen Skeggs
1450c39f472eSBen Skeggs void
nv50_disp_intr(struct nvkm_disp * disp)145192fba5d3SBen Skeggs nv50_disp_intr(struct nvkm_disp *disp)
1452c39f472eSBen Skeggs {
145392fba5d3SBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
14542fde1f1cSBen Skeggs u32 intr0 = nvkm_rd32(device, 0x610020);
14552fde1f1cSBen Skeggs u32 intr1 = nvkm_rd32(device, 0x610024);
1456c39f472eSBen Skeggs
1457c39f472eSBen Skeggs while (intr0 & 0x001f0000) {
1458c39f472eSBen Skeggs u32 chid = __ffs(intr0 & 0x001f0000) - 16;
1459fd166a18SBen Skeggs nv50_disp_intr_error(disp, chid);
1460c39f472eSBen Skeggs intr0 &= ~(0x00010000 << chid);
1461c39f472eSBen Skeggs }
1462c39f472eSBen Skeggs
1463c39f472eSBen Skeggs while (intr0 & 0x0000001f) {
1464c39f472eSBen Skeggs u32 chid = __ffs(intr0 & 0x0000001f);
1465fd166a18SBen Skeggs nv50_disp_chan_uevent_send(disp, chid);
1466c39f472eSBen Skeggs intr0 &= ~(0x00000001 << chid);
1467c39f472eSBen Skeggs }
1468c39f472eSBen Skeggs
1469c39f472eSBen Skeggs if (intr1 & 0x00000004) {
147092fba5d3SBen Skeggs nvkm_disp_vblank(disp, 0);
14712fde1f1cSBen Skeggs nvkm_wr32(device, 0x610024, 0x00000004);
1472c39f472eSBen Skeggs }
1473c39f472eSBen Skeggs
1474c39f472eSBen Skeggs if (intr1 & 0x00000008) {
147592fba5d3SBen Skeggs nvkm_disp_vblank(disp, 1);
14762fde1f1cSBen Skeggs nvkm_wr32(device, 0x610024, 0x00000008);
1477c39f472eSBen Skeggs }
1478c39f472eSBen Skeggs
1479c39f472eSBen Skeggs if (intr1 & 0x00000070) {
14803517e6b6SBen Skeggs disp->super.pending = (intr1 & 0x00000070);
14813517e6b6SBen Skeggs queue_work(disp->super.wq, &disp->super.work);
14823517e6b6SBen Skeggs nvkm_wr32(device, 0x610024, disp->super.pending);
1483c39f472eSBen Skeggs }
1484c39f472eSBen Skeggs }
1485c39f472eSBen Skeggs
1486bb3b0a42SBen Skeggs void
nv50_disp_fini(struct nvkm_disp * disp)148792fba5d3SBen Skeggs nv50_disp_fini(struct nvkm_disp *disp)
1488bb3b0a42SBen Skeggs {
148992fba5d3SBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
1490bb3b0a42SBen Skeggs /* disable all interrupts */
1491bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610024, 0x00000000);
1492bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610020, 0x00000000);
1493bb3b0a42SBen Skeggs }
1494bb3b0a42SBen Skeggs
1495bb3b0a42SBen Skeggs int
nv50_disp_init(struct nvkm_disp * disp)149692fba5d3SBen Skeggs nv50_disp_init(struct nvkm_disp *disp)
1497bb3b0a42SBen Skeggs {
149892fba5d3SBen Skeggs struct nvkm_device *device = disp->engine.subdev.device;
1499bb3b0a42SBen Skeggs struct nvkm_head *head;
1500bb3b0a42SBen Skeggs u32 tmp;
1501bb3b0a42SBen Skeggs int i;
1502bb3b0a42SBen Skeggs
1503bb3b0a42SBen Skeggs /* The below segments of code copying values from one register to
1504bb3b0a42SBen Skeggs * another appear to inform EVO of the display capabilities or
1505bb3b0a42SBen Skeggs * something similar. NFI what the 0x614004 caps are for..
1506bb3b0a42SBen Skeggs */
1507bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x614004);
1508bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610184, tmp);
1509bb3b0a42SBen Skeggs
1510bb3b0a42SBen Skeggs /* ... CRTC caps */
151192fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head) {
1512bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800));
1513bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp);
1514bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800));
1515bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp);
1516bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800));
1517bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp);
1518bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800));
1519bb3b0a42SBen Skeggs nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp);
1520bb3b0a42SBen Skeggs }
1521bb3b0a42SBen Skeggs
1522bb3b0a42SBen Skeggs /* ... DAC caps */
1523bb3b0a42SBen Skeggs for (i = 0; i < disp->dac.nr; i++) {
1524bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800));
1525bb3b0a42SBen Skeggs nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp);
1526bb3b0a42SBen Skeggs }
1527bb3b0a42SBen Skeggs
1528bb3b0a42SBen Skeggs /* ... SOR caps */
1529bb3b0a42SBen Skeggs for (i = 0; i < disp->sor.nr; i++) {
1530bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800));
1531bb3b0a42SBen Skeggs nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp);
1532bb3b0a42SBen Skeggs }
1533bb3b0a42SBen Skeggs
1534bb3b0a42SBen Skeggs /* ... PIOR caps */
1535bb3b0a42SBen Skeggs for (i = 0; i < disp->pior.nr; i++) {
1536bb3b0a42SBen Skeggs tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800));
1537bb3b0a42SBen Skeggs nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp);
1538bb3b0a42SBen Skeggs }
1539bb3b0a42SBen Skeggs
1540bb3b0a42SBen Skeggs /* steal display away from vbios, or something like that */
1541bb3b0a42SBen Skeggs if (nvkm_rd32(device, 0x610024) & 0x00000100) {
1542bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610024, 0x00000100);
1543bb3b0a42SBen Skeggs nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000);
1544bb3b0a42SBen Skeggs if (nvkm_msec(device, 2000,
1545bb3b0a42SBen Skeggs if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002))
1546bb3b0a42SBen Skeggs break;
1547bb3b0a42SBen Skeggs ) < 0)
1548bb3b0a42SBen Skeggs return -EBUSY;
1549bb3b0a42SBen Skeggs }
1550bb3b0a42SBen Skeggs
1551bb3b0a42SBen Skeggs /* point at display engine memory area (hash table, objects) */
1552bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9);
1553bb3b0a42SBen Skeggs
1554bb3b0a42SBen Skeggs /* enable supervisor interrupts, disable everything else */
1555bb3b0a42SBen Skeggs nvkm_wr32(device, 0x61002c, 0x00000370);
1556bb3b0a42SBen Skeggs nvkm_wr32(device, 0x610028, 0x00000000);
1557bb3b0a42SBen Skeggs return 0;
1558bb3b0a42SBen Skeggs }
1559bb3b0a42SBen Skeggs
1560acbe9ecfSBen Skeggs int
nv50_disp_oneinit(struct nvkm_disp * disp)1561acbe9ecfSBen Skeggs nv50_disp_oneinit(struct nvkm_disp *disp)
1562acbe9ecfSBen Skeggs {
1563acbe9ecfSBen Skeggs const struct nvkm_disp_func *func = disp->func;
1564acbe9ecfSBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
1565acbe9ecfSBen Skeggs struct nvkm_device *device = subdev->device;
1566acbe9ecfSBen Skeggs int ret, i;
1567acbe9ecfSBen Skeggs
1568acbe9ecfSBen Skeggs if (func->wndw.cnt) {
1569acbe9ecfSBen Skeggs disp->wndw.nr = func->wndw.cnt(disp, &disp->wndw.mask);
1570acbe9ecfSBen Skeggs nvkm_debug(subdev, "Window(s): %d (%08lx)\n", disp->wndw.nr, disp->wndw.mask);
1571acbe9ecfSBen Skeggs }
1572acbe9ecfSBen Skeggs
1573acbe9ecfSBen Skeggs disp->head.nr = func->head.cnt(disp, &disp->head.mask);
1574acbe9ecfSBen Skeggs nvkm_debug(subdev, " Head(s): %d (%02lx)\n", disp->head.nr, disp->head.mask);
1575acbe9ecfSBen Skeggs for_each_set_bit(i, &disp->head.mask, disp->head.nr) {
1576acbe9ecfSBen Skeggs ret = func->head.new(disp, i);
1577acbe9ecfSBen Skeggs if (ret)
1578acbe9ecfSBen Skeggs return ret;
1579acbe9ecfSBen Skeggs }
1580acbe9ecfSBen Skeggs
1581acbe9ecfSBen Skeggs if (func->dac.cnt) {
1582acbe9ecfSBen Skeggs disp->dac.nr = func->dac.cnt(disp, &disp->dac.mask);
1583acbe9ecfSBen Skeggs nvkm_debug(subdev, " DAC(s): %d (%02lx)\n", disp->dac.nr, disp->dac.mask);
1584acbe9ecfSBen Skeggs for_each_set_bit(i, &disp->dac.mask, disp->dac.nr) {
1585acbe9ecfSBen Skeggs ret = func->dac.new(disp, i);
1586acbe9ecfSBen Skeggs if (ret)
1587acbe9ecfSBen Skeggs return ret;
1588acbe9ecfSBen Skeggs }
1589acbe9ecfSBen Skeggs }
1590acbe9ecfSBen Skeggs
1591acbe9ecfSBen Skeggs if (func->pior.cnt) {
1592acbe9ecfSBen Skeggs disp->pior.nr = func->pior.cnt(disp, &disp->pior.mask);
1593acbe9ecfSBen Skeggs nvkm_debug(subdev, " PIOR(s): %d (%02lx)\n", disp->pior.nr, disp->pior.mask);
1594acbe9ecfSBen Skeggs for_each_set_bit(i, &disp->pior.mask, disp->pior.nr) {
1595acbe9ecfSBen Skeggs ret = func->pior.new(disp, i);
1596acbe9ecfSBen Skeggs if (ret)
1597acbe9ecfSBen Skeggs return ret;
1598acbe9ecfSBen Skeggs }
1599acbe9ecfSBen Skeggs }
1600acbe9ecfSBen Skeggs
1601acbe9ecfSBen Skeggs disp->sor.nr = func->sor.cnt(disp, &disp->sor.mask);
1602acbe9ecfSBen Skeggs nvkm_debug(subdev, " SOR(s): %d (%02lx)\n", disp->sor.nr, disp->sor.mask);
1603acbe9ecfSBen Skeggs for_each_set_bit(i, &disp->sor.mask, disp->sor.nr) {
1604acbe9ecfSBen Skeggs ret = func->sor.new(disp, i);
1605acbe9ecfSBen Skeggs if (ret)
1606acbe9ecfSBen Skeggs return ret;
1607acbe9ecfSBen Skeggs }
1608acbe9ecfSBen Skeggs
1609acbe9ecfSBen Skeggs ret = nvkm_gpuobj_new(device, 0x10000, 0x10000, false, NULL, &disp->inst);
1610acbe9ecfSBen Skeggs if (ret)
1611acbe9ecfSBen Skeggs return ret;
1612acbe9ecfSBen Skeggs
1613acbe9ecfSBen Skeggs return nvkm_ramht_new(device, func->ramht_size ? func->ramht_size :
1614acbe9ecfSBen Skeggs 0x1000, 0, disp->inst, &disp->ramht);
1615acbe9ecfSBen Skeggs }
1616acbe9ecfSBen Skeggs
16170407b33fSBen Skeggs static const struct nvkm_disp_func
16180ce41e3cSBen Skeggs nv50_disp = {
1619acbe9ecfSBen Skeggs .oneinit = nv50_disp_oneinit,
162092fba5d3SBen Skeggs .init = nv50_disp_init,
162192fba5d3SBen Skeggs .fini = nv50_disp_fini,
162292fba5d3SBen Skeggs .intr = nv50_disp_intr,
1623af85389cSBen Skeggs .super = nv50_disp_super,
162492fba5d3SBen Skeggs .uevent = &nv50_disp_chan_uevent,
1625f7b2ece3SBen Skeggs .head = { .cnt = nv50_head_cnt, .new = nv50_head_new },
1626bf5d1a6bSBen Skeggs .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new },
16279fe4e177SBen Skeggs .sor = { .cnt = nv50_sor_cnt, .new = nv50_sor_new },
1628f5e088d6SBen Skeggs .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new },
1629168c0299SBen Skeggs .root = { 0, 0, NV50_DISP },
1630168c0299SBen Skeggs .user = {
1631889fcbe9SBen Skeggs {{0,0,NV50_DISP_CURSOR }, nvkm_disp_chan_new, &nv50_disp_curs },
1632889fcbe9SBen Skeggs {{0,0,NV50_DISP_OVERLAY }, nvkm_disp_chan_new, &nv50_disp_oimm },
1633889fcbe9SBen Skeggs {{0,0,NV50_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &nv50_disp_base },
1634889fcbe9SBen Skeggs {{0,0,NV50_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &nv50_disp_core },
1635889fcbe9SBen Skeggs {{0,0,NV50_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, &nv50_disp_ovly },
1636168c0299SBen Skeggs {}
1637168c0299SBen Skeggs }
16380ce41e3cSBen Skeggs };
16390ce41e3cSBen Skeggs
164070aa8670SBen Skeggs int
nv50_disp_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_disp ** pdisp)1641a7f000ecSBen Skeggs nv50_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
1642a7f000ecSBen Skeggs struct nvkm_disp **pdisp)
1643c39f472eSBen Skeggs {
16441c6aab75SBen Skeggs return nvkm_disp_new_(&nv50_disp, device, type, inst, pdisp);
1645c39f472eSBen Skeggs }
1646