1 /* $NetBSD: nouveau_dispnv50_head507d.c,v 1.4 2021/12/19 10:49:47 riastradh Exp $ */
2
3 /*
4 * Copyright 2018 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include <sys/cdefs.h>
25 __KERNEL_RCSID(0, "$NetBSD: nouveau_dispnv50_head507d.c,v 1.4 2021/12/19 10:49:47 riastradh Exp $");
26
27 #include "head.h"
28 #include "core.h"
29
30 #include <linux/nbsd-namespace.h>
31
32 void
head507d_procamp(struct nv50_head * head,struct nv50_head_atom * asyh)33 head507d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
34 {
35 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
36 u32 *push;
37 if ((push = evo_wait(core, 2))) {
38 evo_mthd(push, 0x08a8 + (head->base.index * 0x400), 1);
39 evo_data(push, asyh->procamp.sat.sin << 20 |
40 asyh->procamp.sat.cos << 8);
41 evo_kick(push, core);
42 }
43 }
44
45 void
head507d_dither(struct nv50_head * head,struct nv50_head_atom * asyh)46 head507d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
47 {
48 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
49 u32 *push;
50 if ((push = evo_wait(core, 2))) {
51 evo_mthd(push, 0x08a0 + (head->base.index * 0x0400), 1);
52 evo_data(push, asyh->dither.mode << 3 |
53 asyh->dither.bits << 1 |
54 asyh->dither.enable);
55 evo_kick(push, core);
56 }
57 }
58
59 void
head507d_ovly(struct nv50_head * head,struct nv50_head_atom * asyh)60 head507d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
61 {
62 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
63 u32 bounds = 0;
64 u32 *push;
65
66 if (asyh->ovly.cpp) {
67 switch (asyh->ovly.cpp) {
68 case 4: bounds |= 0x00000300; break;
69 case 2: bounds |= 0x00000100; break;
70 default:
71 WARN_ON(1);
72 break;
73 }
74 bounds |= 0x00000001;
75 } else {
76 bounds |= 0x00000100;
77 }
78
79 if ((push = evo_wait(core, 2))) {
80 evo_mthd(push, 0x0904 + head->base.index * 0x400, 1);
81 evo_data(push, bounds);
82 evo_kick(push, core);
83 }
84 }
85
86 void
head507d_base(struct nv50_head * head,struct nv50_head_atom * asyh)87 head507d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
88 {
89 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
90 u32 bounds = 0;
91 u32 *push;
92
93 if (asyh->base.cpp) {
94 switch (asyh->base.cpp) {
95 case 8: bounds |= 0x00000500; break;
96 case 4: bounds |= 0x00000300; break;
97 case 2: bounds |= 0x00000100; break;
98 case 1: bounds |= 0x00000000; break;
99 default:
100 WARN_ON(1);
101 break;
102 }
103 bounds |= 0x00000001;
104 }
105
106 if ((push = evo_wait(core, 2))) {
107 evo_mthd(push, 0x0900 + head->base.index * 0x400, 1);
108 evo_data(push, bounds);
109 evo_kick(push, core);
110 }
111 }
112
113 static void
head507d_curs_clr(struct nv50_head * head)114 head507d_curs_clr(struct nv50_head *head)
115 {
116 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
117 u32 *push;
118 if ((push = evo_wait(core, 2))) {
119 evo_mthd(push, 0x0880 + head->base.index * 0x400, 1);
120 evo_data(push, 0x05000000);
121 evo_kick(push, core);
122 }
123 }
124
125 static void
head507d_curs_set(struct nv50_head * head,struct nv50_head_atom * asyh)126 head507d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
127 {
128 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
129 u32 *push;
130 if ((push = evo_wait(core, 3))) {
131 evo_mthd(push, 0x0880 + head->base.index * 0x400, 2);
132 evo_data(push, 0x80000000 | asyh->curs.layout << 26 |
133 asyh->curs.format << 24);
134 evo_data(push, asyh->curs.offset >> 8);
135 evo_kick(push, core);
136 }
137 }
138
139 int
head507d_curs_format(struct nv50_head * head,struct nv50_wndw_atom * asyw,struct nv50_head_atom * asyh)140 head507d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw,
141 struct nv50_head_atom *asyh)
142 {
143 switch (asyw->image.format) {
144 case 0xcf: asyh->curs.format = 1; break;
145 default:
146 WARN_ON(1);
147 return -EINVAL;
148 }
149 return 0;
150 }
151
152 int
head507d_curs_layout(struct nv50_head * head,struct nv50_wndw_atom * asyw,struct nv50_head_atom * asyh)153 head507d_curs_layout(struct nv50_head *head, struct nv50_wndw_atom *asyw,
154 struct nv50_head_atom *asyh)
155 {
156 switch (asyw->image.w) {
157 case 32: asyh->curs.layout = 0; break;
158 case 64: asyh->curs.layout = 1; break;
159 default:
160 return -EINVAL;
161 }
162 return 0;
163 }
164
165 void
head507d_core_clr(struct nv50_head * head)166 head507d_core_clr(struct nv50_head *head)
167 {
168 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
169 u32 *push;
170 if ((push = evo_wait(core, 2))) {
171 evo_mthd(push, 0x0874 + head->base.index * 0x400, 1);
172 evo_data(push, 0x00000000);
173 evo_kick(push, core);
174 }
175 }
176
177 static void
head507d_core_set(struct nv50_head * head,struct nv50_head_atom * asyh)178 head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
179 {
180 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
181 u32 *push;
182 if ((push = evo_wait(core, 9))) {
183 evo_mthd(push, 0x0860 + head->base.index * 0x400, 1);
184 evo_data(push, asyh->core.offset >> 8);
185 evo_mthd(push, 0x0868 + head->base.index * 0x400, 4);
186 evo_data(push, asyh->core.h << 16 | asyh->core.w);
187 evo_data(push, asyh->core.layout << 20 |
188 (asyh->core.pitch >> 8) << 8 |
189 asyh->core.blocks << 8 |
190 asyh->core.blockh);
191 evo_data(push, asyh->core.kind << 16 |
192 asyh->core.format << 8);
193 evo_data(push, asyh->core.handle);
194 evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1);
195 evo_data(push, asyh->core.y << 16 | asyh->core.x);
196 evo_kick(push, core);
197
198 /* EVO will complain with INVALID_STATE if we have an
199 * active cursor and (re)specify HeadSetContextDmaIso
200 * without also updating HeadSetOffsetCursor.
201 */
202 asyh->set.curs = asyh->curs.visible;
203 asyh->set.olut = asyh->olut.handle != 0;
204 }
205 }
206
207 void
head507d_core_calc(struct nv50_head * head,struct nv50_head_atom * asyh)208 head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
209 {
210 struct nv50_disp *disp = nv50_disp(head->base.base.dev);
211 if ((asyh->core.visible = (asyh->base.cpp != 0))) {
212 asyh->core.x = asyh->base.x;
213 asyh->core.y = asyh->base.y;
214 asyh->core.w = asyh->base.w;
215 asyh->core.h = asyh->base.h;
216 } else
217 if ((asyh->core.visible = (asyh->ovly.cpp != 0)) ||
218 (asyh->core.visible = asyh->curs.visible)) {
219 /*XXX: We need to either find some way of having the
220 * primary base layer appear black, while still
221 * being able to display the other layers, or we
222 * need to allocate a dummy black surface here.
223 */
224 asyh->core.x = 0;
225 asyh->core.y = 0;
226 asyh->core.w = asyh->state.mode.hdisplay;
227 asyh->core.h = asyh->state.mode.vdisplay;
228 }
229 asyh->core.handle = disp->core->chan.vram.handle;
230 asyh->core.offset = 0;
231 asyh->core.format = 0xcf;
232 asyh->core.kind = 0;
233 asyh->core.layout = 1;
234 asyh->core.blockh = 0;
235 asyh->core.blocks = 0;
236 asyh->core.pitch = ALIGN(asyh->core.w, 64) * 4;
237 }
238
239 static void
head507d_olut_clr(struct nv50_head * head)240 head507d_olut_clr(struct nv50_head *head)
241 {
242 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
243 u32 *push;
244 if ((push = evo_wait(core, 2))) {
245 evo_mthd(push, 0x0840 + (head->base.index * 0x400), 1);
246 evo_data(push, 0x00000000);
247 evo_kick(push, core);
248 }
249 }
250
251 static void
head507d_olut_set(struct nv50_head * head,struct nv50_head_atom * asyh)252 head507d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
253 {
254 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
255 u32 *push;
256 if ((push = evo_wait(core, 3))) {
257 evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
258 evo_data(push, 0x80000000 | asyh->olut.mode << 30);
259 evo_data(push, asyh->olut.offset >> 8);
260 evo_kick(push, core);
261 }
262 }
263
264 #ifdef __NetBSD__
265 #define __iomem __lut_iomem
266 #define readw(p) atomic_load_relaxed((const __iomem uint16_t *)(p))
267 #define writew(v,p) atomic_store_relaxed((__iomem uint16_t *)(p), (v))
268 #endif
269
270 static void
head507d_olut_load(struct drm_color_lut * in,int size,void __iomem * mem)271 head507d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
272 {
273 for (; size--; in++, mem += 8) {
274 writew(drm_color_lut_extract(in-> red, 11) << 3, mem + 0);
275 writew(drm_color_lut_extract(in->green, 11) << 3, mem + 2);
276 writew(drm_color_lut_extract(in-> blue, 11) << 3, mem + 4);
277 }
278
279 /* INTERPOLATE modes require a "next" entry to interpolate with,
280 * so we replicate the last entry to deal with this for now.
281 */
282 writew(readw(mem - 8), mem + 0);
283 writew(readw(mem - 6), mem + 2);
284 writew(readw(mem - 4), mem + 4);
285 }
286
287 bool
head507d_olut(struct nv50_head * head,struct nv50_head_atom * asyh,int size)288 head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
289 {
290 if (size != 256)
291 return false;
292
293 if (asyh->base.cpp == 1)
294 asyh->olut.mode = 0;
295 else
296 asyh->olut.mode = 1;
297
298 asyh->olut.load = head507d_olut_load;
299 return true;
300 }
301
302 void
head507d_mode(struct nv50_head * head,struct nv50_head_atom * asyh)303 head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
304 {
305 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
306 struct nv50_head_mode *m = &asyh->mode;
307 u32 *push;
308 if ((push = evo_wait(core, 13))) {
309 evo_mthd(push, 0x0804 + (head->base.index * 0x400), 2);
310 evo_data(push, 0x00800000 | m->clock);
311 evo_data(push, m->interlace ? 0x00000002 : 0x00000000);
312 evo_mthd(push, 0x0810 + (head->base.index * 0x400), 7);
313 evo_data(push, 0x00000000);
314 evo_data(push, m->v.active << 16 | m->h.active );
315 evo_data(push, m->v.synce << 16 | m->h.synce );
316 evo_data(push, m->v.blanke << 16 | m->h.blanke );
317 evo_data(push, m->v.blanks << 16 | m->h.blanks );
318 evo_data(push, m->v.blank2e << 16 | m->v.blank2s);
319 evo_data(push, asyh->mode.v.blankus);
320 evo_mthd(push, 0x082c + (head->base.index * 0x400), 1);
321 evo_data(push, 0x00000000);
322 evo_kick(push, core);
323 }
324 }
325
326 void
head507d_view(struct nv50_head * head,struct nv50_head_atom * asyh)327 head507d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
328 {
329 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
330 u32 *push;
331 if ((push = evo_wait(core, 7))) {
332 evo_mthd(push, 0x08a4 + (head->base.index * 0x400), 1);
333 evo_data(push, 0x00000000);
334 evo_mthd(push, 0x08c8 + (head->base.index * 0x400), 1);
335 evo_data(push, asyh->view.iH << 16 | asyh->view.iW);
336 evo_mthd(push, 0x08d8 + (head->base.index * 0x400), 2);
337 evo_data(push, asyh->view.oH << 16 | asyh->view.oW);
338 evo_data(push, asyh->view.oH << 16 | asyh->view.oW);
339 evo_kick(push, core);
340 }
341 }
342
343 const struct nv50_head_func
344 head507d = {
345 .view = head507d_view,
346 .mode = head507d_mode,
347 .olut = head507d_olut,
348 .olut_size = 256,
349 .olut_set = head507d_olut_set,
350 .olut_clr = head507d_olut_clr,
351 .core_calc = head507d_core_calc,
352 .core_set = head507d_core_set,
353 .core_clr = head507d_core_clr,
354 .curs_layout = head507d_curs_layout,
355 .curs_format = head507d_curs_format,
356 .curs_set = head507d_curs_set,
357 .curs_clr = head507d_curs_clr,
358 .base = head507d_base,
359 .ovly = head507d_ovly,
360 .dither = head507d_dither,
361 .procamp = head507d_procamp,
362 };
363