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 "head.h" 23 #include "core.h" 24 25 #include <nvif/push507c.h> 26 27 #include <nvhw/class/cl507d.h> 28 29 int 30 head507d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh) 31 { 32 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 33 const int i = head->base.index; 34 int ret; 35 36 if ((ret = PUSH_WAIT(push, 2))) 37 return ret; 38 39 PUSH_NVSQ(push, NV507D, 0x08a8 + (i * 0x400), asyh->procamp.sat.sin << 20 | 40 asyh->procamp.sat.cos << 8); 41 return 0; 42 } 43 44 int 45 head507d_dither(struct nv50_head *head, struct nv50_head_atom *asyh) 46 { 47 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 48 const int i = head->base.index; 49 int ret; 50 51 if ((ret = PUSH_WAIT(push, 2))) 52 return ret; 53 54 PUSH_NVSQ(push, NV507D, 0x08a0 + (i * 0x400), asyh->dither.mode << 3 | 55 asyh->dither.bits << 1 | 56 asyh->dither.enable); 57 return 0; 58 } 59 60 int 61 head507d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh) 62 { 63 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 64 const int i = head->base.index; 65 u32 bounds = 0; 66 int ret; 67 68 if (asyh->ovly.cpp) { 69 switch (asyh->ovly.cpp) { 70 case 4: bounds |= 0x00000300; break; 71 case 2: bounds |= 0x00000100; break; 72 default: 73 WARN_ON(1); 74 break; 75 } 76 bounds |= 0x00000001; 77 } else { 78 bounds |= 0x00000100; 79 } 80 81 if ((ret = PUSH_WAIT(push, 2))) 82 return ret; 83 84 PUSH_NVSQ(push, NV507D, 0x0904 + (i * 0x400), bounds); 85 return 0; 86 } 87 88 int 89 head507d_base(struct nv50_head *head, struct nv50_head_atom *asyh) 90 { 91 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 92 const int i = head->base.index; 93 u32 bounds = 0; 94 int ret; 95 96 if (asyh->base.cpp) { 97 switch (asyh->base.cpp) { 98 case 8: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break; 99 case 4: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break; 100 case 2: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break; 101 case 1: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_8); break; 102 default: 103 WARN_ON(1); 104 break; 105 } 106 bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, USABLE, TRUE); 107 } 108 109 if ((ret = PUSH_WAIT(push, 2))) 110 return ret; 111 112 PUSH_MTHD(push, NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i), bounds); 113 return 0; 114 } 115 116 static int 117 head507d_curs_clr(struct nv50_head *head) 118 { 119 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 120 const int i = head->base.index; 121 int ret; 122 123 if ((ret = PUSH_WAIT(push, 2))) 124 return ret; 125 126 PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_CURSOR(i), 127 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, ENABLE, DISABLE) | 128 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, FORMAT, A8R8G8B8) | 129 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, SIZE, W64_H64)); 130 return 0; 131 } 132 133 static int 134 head507d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh) 135 { 136 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 137 const int i = head->base.index; 138 int ret; 139 140 if ((ret = PUSH_WAIT(push, 3))) 141 return ret; 142 143 PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_CURSOR(i), 144 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, ENABLE, ENABLE) | 145 NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, FORMAT, asyh->curs.format) | 146 NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, SIZE, asyh->curs.layout) | 147 NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_X, 0) | 148 NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_Y, 0) | 149 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, COMPOSITION, ALPHA_BLEND) | 150 NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, SUB_OWNER, NONE), 151 152 HEAD_SET_OFFSET_CURSOR(i), asyh->curs.offset >> 8); 153 return 0; 154 } 155 156 int 157 head507d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw, 158 struct nv50_head_atom *asyh) 159 { 160 switch (asyw->image.format) { 161 case 0xcf: asyh->curs.format = NV507D_HEAD_SET_CONTROL_CURSOR_FORMAT_A8R8G8B8; break; 162 default: 163 WARN_ON(1); 164 return -EINVAL; 165 } 166 return 0; 167 } 168 169 int 170 head507d_curs_layout(struct nv50_head *head, struct nv50_wndw_atom *asyw, 171 struct nv50_head_atom *asyh) 172 { 173 switch (asyw->image.w) { 174 case 32: asyh->curs.layout = NV507D_HEAD_SET_CONTROL_CURSOR_SIZE_W32_H32; break; 175 case 64: asyh->curs.layout = NV507D_HEAD_SET_CONTROL_CURSOR_SIZE_W64_H64; break; 176 default: 177 return -EINVAL; 178 } 179 return 0; 180 } 181 182 int 183 head507d_core_clr(struct nv50_head *head) 184 { 185 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 186 const int i = head->base.index; 187 int ret; 188 189 if ((ret = PUSH_WAIT(push, 2))) 190 return ret; 191 192 PUSH_MTHD(push, NV507D, HEAD_SET_CONTEXT_DMA_ISO(i), 0x00000000); 193 return 0; 194 } 195 196 static int 197 head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh) 198 { 199 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 200 const int i = head->base.index; 201 int ret; 202 203 if ((ret = PUSH_WAIT(push, 9))) 204 return ret; 205 206 PUSH_MTHD(push, NV507D, HEAD_SET_OFFSET(i, 0), 207 NVVAL(NV507D, HEAD_SET_OFFSET, ORIGIN, asyh->core.offset >> 8)); 208 209 PUSH_MTHD(push, NV507D, HEAD_SET_SIZE(i), 210 NVVAL(NV507D, HEAD_SET_SIZE, WIDTH, asyh->core.w) | 211 NVVAL(NV507D, HEAD_SET_SIZE, HEIGHT, asyh->core.h), 212 213 HEAD_SET_STORAGE(i), 214 NVVAL(NV507D, HEAD_SET_STORAGE, BLOCK_HEIGHT, asyh->core.blockh) | 215 NVVAL(NV507D, HEAD_SET_STORAGE, PITCH, asyh->core.pitch >> 8) | 216 NVVAL(NV507D, HEAD_SET_STORAGE, PITCH, asyh->core.blocks) | 217 NVVAL(NV507D, HEAD_SET_STORAGE, MEMORY_LAYOUT, asyh->core.layout), 218 219 HEAD_SET_PARAMS(i), 220 NVVAL(NV507D, HEAD_SET_PARAMS, FORMAT, asyh->core.format) | 221 NVVAL(NV507D, HEAD_SET_PARAMS, KIND, asyh->core.kind) | 222 NVDEF(NV507D, HEAD_SET_PARAMS, PART_STRIDE, PARTSTRIDE_256), 223 224 HEAD_SET_CONTEXT_DMA_ISO(i), 225 NVVAL(NV507D, HEAD_SET_CONTEXT_DMA_ISO, HANDLE, asyh->core.handle)); 226 227 PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_POINT_IN(i, 0), 228 NVVAL(NV507D, HEAD_SET_VIEWPORT_POINT_IN, X, asyh->core.x) | 229 NVVAL(NV507D, HEAD_SET_VIEWPORT_POINT_IN, Y, asyh->core.y)); 230 231 /* EVO will complain with INVALID_STATE if we have an 232 * active cursor and (re)specify HeadSetContextDmaIso 233 * without also updating HeadSetOffsetCursor. 234 */ 235 asyh->set.curs = asyh->curs.visible; 236 asyh->set.olut = asyh->olut.handle != 0; 237 return 0; 238 } 239 240 void 241 head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh) 242 { 243 struct nv50_disp *disp = nv50_disp(head->base.base.dev); 244 if ((asyh->core.visible = (asyh->base.cpp != 0))) { 245 asyh->core.x = asyh->base.x; 246 asyh->core.y = asyh->base.y; 247 asyh->core.w = asyh->base.w; 248 asyh->core.h = asyh->base.h; 249 } else 250 if ((asyh->core.visible = (asyh->ovly.cpp != 0)) || 251 (asyh->core.visible = asyh->curs.visible)) { 252 /*XXX: We need to either find some way of having the 253 * primary base layer appear black, while still 254 * being able to display the other layers, or we 255 * need to allocate a dummy black surface here. 256 */ 257 asyh->core.x = 0; 258 asyh->core.y = 0; 259 asyh->core.w = asyh->state.mode.hdisplay; 260 asyh->core.h = asyh->state.mode.vdisplay; 261 } 262 asyh->core.handle = disp->core->chan.vram.handle; 263 asyh->core.offset = 0; 264 asyh->core.format = NV507D_HEAD_SET_PARAMS_FORMAT_A8R8G8B8; 265 asyh->core.kind = NV507D_HEAD_SET_PARAMS_KIND_KIND_PITCH; 266 asyh->core.layout = NV507D_HEAD_SET_STORAGE_MEMORY_LAYOUT_PITCH; 267 asyh->core.blockh = NV507D_HEAD_SET_STORAGE_BLOCK_HEIGHT_ONE_GOB; 268 asyh->core.blocks = 0; 269 asyh->core.pitch = ALIGN(asyh->core.w, 64) * 4; 270 } 271 272 static int 273 head507d_olut_clr(struct nv50_head *head) 274 { 275 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 276 const int i = head->base.index; 277 int ret; 278 279 if ((ret = PUSH_WAIT(push, 2))) 280 return ret; 281 282 PUSH_MTHD(push, NV507D, HEAD_SET_BASE_LUT_LO(i), 283 NVDEF(NV507D, HEAD_SET_BASE_LUT_LO, ENABLE, DISABLE)); 284 return 0; 285 } 286 287 static int 288 head507d_olut_set(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, 3))) 295 return ret; 296 297 PUSH_MTHD(push, NV507D, HEAD_SET_BASE_LUT_LO(i), 298 NVDEF(NV507D, HEAD_SET_BASE_LUT_LO, ENABLE, ENABLE) | 299 NVVAL(NV507D, HEAD_SET_BASE_LUT_LO, MODE, asyh->olut.mode) | 300 NVVAL(NV507D, HEAD_SET_BASE_LUT_LO, ORIGIN, 0), 301 302 HEAD_SET_BASE_LUT_HI(i), 303 NVVAL(NV507D, HEAD_SET_BASE_LUT_HI, ORIGIN, asyh->olut.offset >> 8)); 304 return 0; 305 } 306 307 static void 308 head507d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem) 309 { 310 for (; size--; in++, mem += 8) { 311 writew(drm_color_lut_extract(in-> red, 11) << 3, mem + 0); 312 writew(drm_color_lut_extract(in->green, 11) << 3, mem + 2); 313 writew(drm_color_lut_extract(in-> blue, 11) << 3, mem + 4); 314 } 315 316 /* INTERPOLATE modes require a "next" entry to interpolate with, 317 * so we replicate the last entry to deal with this for now. 318 */ 319 writew(readw(mem - 8), mem + 0); 320 writew(readw(mem - 6), mem + 2); 321 writew(readw(mem - 4), mem + 4); 322 } 323 324 bool 325 head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size) 326 { 327 if (size != 256) 328 return false; 329 330 if (asyh->base.cpp == 1) 331 asyh->olut.mode = NV507D_HEAD_SET_BASE_LUT_LO_MODE_LORES; 332 else 333 asyh->olut.mode = NV507D_HEAD_SET_BASE_LUT_LO_MODE_HIRES; 334 335 asyh->olut.load = head507d_olut_load; 336 return true; 337 } 338 339 int 340 head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) 341 { 342 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 343 struct nv50_head_mode *m = &asyh->mode; 344 const int i = head->base.index; 345 int ret; 346 347 if ((ret = PUSH_WAIT(push, 13))) 348 return ret; 349 350 PUSH_MTHD(push, NV507D, HEAD_SET_PIXEL_CLOCK(i), 351 NVVAL(NV507D, HEAD_SET_PIXEL_CLOCK, FREQUENCY, m->clock) | 352 NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, MODE, CLK_CUSTOM) | 353 NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, ADJ1000DIV1001, FALSE) | 354 NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, NOT_DRIVER, FALSE), 355 356 HEAD_SET_CONTROL(i), 357 NVVAL(NV507D, HEAD_SET_CONTROL, STRUCTURE, m->interlace)); 358 359 PUSH_MTHD(push, NV507D, HEAD_SET_OVERSCAN_COLOR(i), 360 NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, RED, 0) | 361 NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, GRN, 0) | 362 NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, BLU, 0), 363 364 HEAD_SET_RASTER_SIZE(i), 365 NVVAL(NV507D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) | 366 NVVAL(NV507D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active), 367 368 HEAD_SET_RASTER_SYNC_END(i), 369 NVVAL(NV507D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) | 370 NVVAL(NV507D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce), 371 372 HEAD_SET_RASTER_BLANK_END(i), 373 NVVAL(NV507D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) | 374 NVVAL(NV507D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke), 375 376 HEAD_SET_RASTER_BLANK_START(i), 377 NVVAL(NV507D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) | 378 NVVAL(NV507D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks), 379 380 HEAD_SET_RASTER_VERT_BLANK2(i), 381 NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK2, YSTART, m->v.blank2s) | 382 NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK2, YEND, m->v.blank2e), 383 384 HEAD_SET_RASTER_VERT_BLANK_DMI(i), 385 NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK_DMI, DURATION, m->v.blankus)); 386 387 PUSH_MTHD(push, NV507D, HEAD_SET_DEFAULT_BASE_COLOR(i), 388 NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) | 389 NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) | 390 NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0)); 391 return 0; 392 } 393 394 int 395 head507d_view(struct nv50_head *head, struct nv50_head_atom *asyh) 396 { 397 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; 398 const int i = head->base.index; 399 int ret; 400 401 if ((ret = PUSH_WAIT(push, 7))) 402 return ret; 403 404 PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER(i), 405 NVDEF(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, VERTICAL_TAPS, TAPS_1) | 406 NVDEF(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, HORIZONTAL_TAPS, TAPS_1) | 407 NVVAL(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, HRESPONSE_BIAS, 0) | 408 NVVAL(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, VRESPONSE_BIAS, 0)); 409 410 PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_SIZE_IN(i), 411 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) | 412 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH)); 413 414 PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_SIZE_OUT(i), 415 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) | 416 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH), 417 418 HEAD_SET_VIEWPORT_SIZE_OUT_MIN(i), 419 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, WIDTH, asyh->view.oW) | 420 NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, HEIGHT, asyh->view.oH)); 421 return 0; 422 } 423 424 const struct nv50_head_func 425 head507d = { 426 .view = head507d_view, 427 .mode = head507d_mode, 428 .olut = head507d_olut, 429 .olut_size = 256, 430 .olut_set = head507d_olut_set, 431 .olut_clr = head507d_olut_clr, 432 .core_calc = head507d_core_calc, 433 .core_set = head507d_core_set, 434 .core_clr = head507d_core_clr, 435 .curs_layout = head507d_curs_layout, 436 .curs_format = head507d_curs_format, 437 .curs_set = head507d_curs_set, 438 .curs_clr = head507d_curs_clr, 439 .base = head507d_base, 440 .ovly = head507d_ovly, 441 .dither = head507d_dither, 442 .procamp = head507d_procamp, 443 }; 444