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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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