1 /* 2 * Copyright 2018 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 #include <drm/drm_connector.h> 23 #include <drm/drm_mode_config.h> 24 #include <drm/drm_vblank.h> 25 #include "nouveau_drv.h" 26 #include "nouveau_bios.h" 27 #include "nouveau_connector.h" 28 #include "head.h" 29 #include "core.h" 30 #include "crc.h" 31 32 #include <nvif/push507c.h> 33 34 void 35 head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) 36 { 37 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 38 u32 *push; 39 if ((push = evo_wait(core, 3))) { 40 evo_mthd(push, 0x0404 + (head->base.index * 0x300), 2); 41 evo_data(push, asyh->or.depth << 6 | 42 asyh->or.nvsync << 4 | 43 asyh->or.nhsync << 3 | 44 asyh->or.crc_raster); 45 evo_data(push, 0x31ec6000 | head->base.index << 25 | 46 asyh->mode.interlace); 47 evo_kick(push, core); 48 } 49 } 50 51 void 52 head907d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh) 53 { 54 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 55 u32 *push; 56 if ((push = evo_wait(core, 2))) { 57 evo_mthd(push, 0x0498 + (head->base.index * 0x300), 1); 58 evo_data(push, asyh->procamp.sat.sin << 20 | 59 asyh->procamp.sat.cos << 8); 60 evo_kick(push, core); 61 } 62 } 63 64 static void 65 head907d_dither(struct nv50_head *head, struct nv50_head_atom *asyh) 66 { 67 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 68 u32 *push; 69 if ((push = evo_wait(core, 2))) { 70 evo_mthd(push, 0x0490 + (head->base.index * 0x0300), 1); 71 evo_data(push, asyh->dither.mode << 3 | 72 asyh->dither.bits << 1 | 73 asyh->dither.enable); 74 evo_kick(push, core); 75 } 76 } 77 78 void 79 head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh) 80 { 81 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 82 u32 bounds = 0; 83 u32 *push; 84 85 if (asyh->ovly.cpp) { 86 switch (asyh->ovly.cpp) { 87 case 8: bounds |= 0x00000500; break; 88 case 4: bounds |= 0x00000300; break; 89 case 2: bounds |= 0x00000100; break; 90 default: 91 WARN_ON(1); 92 break; 93 } 94 bounds |= 0x00000001; 95 } else { 96 bounds |= 0x00000100; 97 } 98 99 if ((push = evo_wait(core, 2))) { 100 evo_mthd(push, 0x04d4 + head->base.index * 0x300, 1); 101 evo_data(push, bounds); 102 evo_kick(push, core); 103 } 104 } 105 106 static int 107 head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh) 108 { 109 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 110 const int i = head->base.index; 111 u32 bounds = 0; 112 int ret; 113 114 if (asyh->base.cpp) { 115 switch (asyh->base.cpp) { 116 case 8: bounds |= 0x00000500; break; 117 case 4: bounds |= 0x00000300; break; 118 case 2: bounds |= 0x00000100; break; 119 case 1: bounds |= 0x00000000; break; 120 default: 121 WARN_ON(1); 122 break; 123 } 124 bounds |= 0x00000001; 125 } 126 127 if ((ret = PUSH_WAIT(push, 2))) 128 return ret; 129 130 PUSH_NVSQ(push, NV907D, 0x04d0 + (i * 0x300), bounds); 131 return 0; 132 } 133 134 int 135 head907d_curs_clr(struct nv50_head *head) 136 { 137 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 138 const int i = head->base.index; 139 int ret; 140 141 if ((ret = PUSH_WAIT(push, 4))) 142 return ret; 143 144 PUSH_NVSQ(push, NV907D, 0x0480 + (i * 0x300), 0x05000000); 145 PUSH_NVSQ(push, NV907D, 0x048c + (i * 0x300), 0x00000000); 146 return 0; 147 } 148 149 int 150 head907d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh) 151 { 152 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 153 const int i = head->base.index; 154 int ret; 155 156 if ((ret = PUSH_WAIT(push, 5))) 157 return ret; 158 159 PUSH_NVSQ(push, NV907D, 0x0480 + (i * 0x300), 0x80000000 | 160 asyh->curs.layout << 26 | 161 asyh->curs.format << 24, 162 0x0484 + (i * 0x300), asyh->curs.offset >> 8); 163 PUSH_NVSQ(push, NV907D, 0x048c + (i * 0x300), asyh->curs.handle); 164 return 0; 165 } 166 167 int 168 head907d_core_clr(struct nv50_head *head) 169 { 170 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 171 const int i = head->base.index; 172 int ret; 173 174 if ((ret = PUSH_WAIT(push, 2))) 175 return ret; 176 177 PUSH_NVSQ(push, NV907D, 0x0474 + (i * 0x300), 0x00000000); 178 return 0; 179 } 180 181 int 182 head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh) 183 { 184 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 185 const int i = head->base.index; 186 int ret; 187 188 if ((ret = PUSH_WAIT(push, 9))) 189 return ret; 190 191 PUSH_NVSQ(push, NV907D, 0x0460 + (i * 0x300), asyh->core.offset >> 8); 192 PUSH_NVSQ(push, NV907D, 0x0468 + (i * 0x300), asyh->core.h << 16 | asyh->core.w, 193 0x046c + (i * 0x300), asyh->core.layout << 24 | 194 (asyh->core.pitch >> 8) << 8 | 195 asyh->core.blocks << 8 | 196 asyh->core.blockh, 197 0x0470 + (i * 0x300), asyh->core.format << 8, 198 0x0474 + (i * 0x300), asyh->core.handle); 199 PUSH_NVSQ(push, NV907D, 0x04b0 + (i * 0x300), asyh->core.y << 16 | asyh->core.x); 200 return 0; 201 } 202 203 int 204 head907d_olut_clr(struct nv50_head *head) 205 { 206 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 207 const int i = head->base.index; 208 int ret; 209 210 if ((ret = PUSH_WAIT(push, 4))) 211 return ret; 212 213 PUSH_NVSQ(push, NV907D, 0x0448 + (i * 0x300), 0x00000000); 214 PUSH_NVSQ(push, NV907D, 0x045c + (i * 0x300), 0x00000000); 215 return 0; 216 } 217 218 int 219 head907d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh) 220 { 221 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 222 const int i = head->base.index; 223 int ret; 224 225 if ((ret = PUSH_WAIT(push, 5))) 226 return ret; 227 228 PUSH_NVSQ(push, NV907D, 0x0448 + (i * 0x300), 0x80000000 | asyh->olut.mode << 24, 229 0x044c + (i * 0x300), asyh->olut.offset >> 8); 230 PUSH_NVSQ(push, NV907D, 0x045c + (i * 0x300), asyh->olut.handle); 231 return 0; 232 } 233 234 void 235 head907d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem) 236 { 237 for (; size--; in++, mem += 8) { 238 writew(drm_color_lut_extract(in-> red, 14) + 0x6000, mem + 0); 239 writew(drm_color_lut_extract(in->green, 14) + 0x6000, mem + 2); 240 writew(drm_color_lut_extract(in-> blue, 14) + 0x6000, mem + 4); 241 } 242 243 /* INTERPOLATE modes require a "next" entry to interpolate with, 244 * so we replicate the last entry to deal with this for now. 245 */ 246 writew(readw(mem - 8), mem + 0); 247 writew(readw(mem - 6), mem + 2); 248 writew(readw(mem - 4), mem + 4); 249 } 250 251 bool 252 head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size) 253 { 254 if (size != 256 && size != 1024) 255 return false; 256 257 asyh->olut.mode = size == 1024 ? 4 : 7; 258 asyh->olut.load = head907d_olut_load; 259 return true; 260 } 261 262 int 263 head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) 264 { 265 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 266 struct nv50_head_mode *m = &asyh->mode; 267 const int i = head->base.index; 268 int ret; 269 270 if ((ret = PUSH_WAIT(push, 14))) 271 return ret; 272 273 PUSH_NVSQ(push, NV907D, 0x0410 + (i * 0x300), 0x00000000, 274 0x0414 + (i * 0x300), m->v.active << 16 | m->h.active, 275 0x0418 + (i * 0x300), m->v.synce << 16 | m->h.synce, 276 0x041c + (i * 0x300), m->v.blanke << 16 | m->h.blanke, 277 0x0420 + (i * 0x300), m->v.blanks << 16 | m->h.blanks, 278 0x0424 + (i * 0x300), m->v.blank2e << 16 | m->v.blank2s); 279 PUSH_NVSQ(push, NV907D, 0x042c + (i * 0x300), 0x00000000, 280 0x0430 + (i * 0x300), 0xffffff00); 281 PUSH_NVSQ(push, NV907D, 0x0450 + (i * 0x300), m->clock * 1000, 282 0x0454 + (i * 0x300), 0x00200000, 283 0x0458 + (i * 0x300), m->clock * 1000); 284 return 0; 285 } 286 287 int 288 head907d_view(struct nv50_head *head, struct nv50_head_atom *asyh) 289 { 290 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 291 const int i = head->base.index; 292 int ret; 293 294 if ((ret = PUSH_WAIT(push, 8))) 295 return ret; 296 297 PUSH_NVSQ(push, NV907D, 0x0494 + (i * 0x300), 0x00000000); 298 PUSH_NVSQ(push, NV907D, 0x04b8 + (i * 0x300), asyh->view.iH << 16 | asyh->view.iW); 299 PUSH_NVSQ(push, NV907D, 0x04c0 + (i * 0x300), asyh->view.oH << 16 | asyh->view.oW, 300 0x04c4 + (i * 0x300), asyh->view.oH << 16 | asyh->view.oW, 301 0x04c8 + (i * 0x300), asyh->view.oH << 16 | asyh->view.oW); 302 return 0; 303 } 304 305 const struct nv50_head_func 306 head907d = { 307 .view = head907d_view, 308 .mode = head907d_mode, 309 .olut = head907d_olut, 310 .olut_size = 1024, 311 .olut_set = head907d_olut_set, 312 .olut_clr = head907d_olut_clr, 313 .core_calc = head507d_core_calc, 314 .core_set = head907d_core_set, 315 .core_clr = head907d_core_clr, 316 .curs_layout = head507d_curs_layout, 317 .curs_format = head507d_curs_format, 318 .curs_set = head907d_curs_set, 319 .curs_clr = head907d_curs_clr, 320 .base = head907d_base, 321 .ovly = head907d_ovly, 322 .dither = head907d_dither, 323 .procamp = head907d_procamp, 324 .or = head907d_or, 325 }; 326