head.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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 "base.h"
  24. #include "core.h"
  25. #include "curs.h"
  26. #include "ovly.h"
  27. #include <nvif/class.h>
  28. #include <drm/drm_atomic_helper.h>
  29. #include <drm/drm_crtc_helper.h>
  30. #include "nouveau_connector.h"
  31. void
  32. nv50_head_flush_clr(struct nv50_head *head,
  33. struct nv50_head_atom *asyh, bool flush)
  34. {
  35. union nv50_head_atom_mask clr = {
  36. .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask),
  37. };
  38. if (clr.olut) head->func->olut_clr(head);
  39. if (clr.core) head->func->core_clr(head);
  40. if (clr.curs) head->func->curs_clr(head);
  41. }
  42. void
  43. nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
  44. {
  45. if (asyh->set.view ) head->func->view (head, asyh);
  46. if (asyh->set.mode ) head->func->mode (head, asyh);
  47. if (asyh->set.core ) head->func->core_set(head, asyh);
  48. if (asyh->set.olut ) {
  49. asyh->olut.offset = nv50_lut_load(&head->olut,
  50. asyh->olut.mode <= 1,
  51. asyh->olut.buffer,
  52. asyh->state.gamma_lut);
  53. head->func->olut_set(head, asyh);
  54. }
  55. if (asyh->set.curs ) head->func->curs_set(head, asyh);
  56. if (asyh->set.base ) head->func->base (head, asyh);
  57. if (asyh->set.ovly ) head->func->ovly (head, asyh);
  58. if (asyh->set.dither ) head->func->dither (head, asyh);
  59. if (asyh->set.procamp) head->func->procamp (head, asyh);
  60. if (asyh->set.or ) head->func->or (head, asyh);
  61. }
  62. static void
  63. nv50_head_atomic_check_procamp(struct nv50_head_atom *armh,
  64. struct nv50_head_atom *asyh,
  65. struct nouveau_conn_atom *asyc)
  66. {
  67. const int vib = asyc->procamp.color_vibrance - 100;
  68. const int hue = asyc->procamp.vibrant_hue - 90;
  69. const int adj = (vib > 0) ? 50 : 0;
  70. asyh->procamp.sat.cos = ((vib * 2047 + adj) / 100) & 0xfff;
  71. asyh->procamp.sat.sin = ((hue * 2047) / 100) & 0xfff;
  72. asyh->set.procamp = true;
  73. }
  74. static void
  75. nv50_head_atomic_check_dither(struct nv50_head_atom *armh,
  76. struct nv50_head_atom *asyh,
  77. struct nouveau_conn_atom *asyc)
  78. {
  79. struct drm_connector *connector = asyc->state.connector;
  80. u32 mode = 0x00;
  81. if (asyc->dither.mode == DITHERING_MODE_AUTO) {
  82. if (asyh->base.depth > connector->display_info.bpc * 3)
  83. mode = DITHERING_MODE_DYNAMIC2X2;
  84. } else {
  85. mode = asyc->dither.mode;
  86. }
  87. if (asyc->dither.depth == DITHERING_DEPTH_AUTO) {
  88. if (connector->display_info.bpc >= 8)
  89. mode |= DITHERING_DEPTH_8BPC;
  90. } else {
  91. mode |= asyc->dither.depth;
  92. }
  93. asyh->dither.enable = mode;
  94. asyh->dither.bits = mode >> 1;
  95. asyh->dither.mode = mode >> 3;
  96. asyh->set.dither = true;
  97. }
  98. static void
  99. nv50_head_atomic_check_view(struct nv50_head_atom *armh,
  100. struct nv50_head_atom *asyh,
  101. struct nouveau_conn_atom *asyc)
  102. {
  103. struct drm_connector *connector = asyc->state.connector;
  104. struct drm_display_mode *omode = &asyh->state.adjusted_mode;
  105. struct drm_display_mode *umode = &asyh->state.mode;
  106. int mode = asyc->scaler.mode;
  107. struct edid *edid;
  108. int umode_vdisplay, omode_hdisplay, omode_vdisplay;
  109. if (connector->edid_blob_ptr)
  110. edid = (struct edid *)connector->edid_blob_ptr->data;
  111. else
  112. edid = NULL;
  113. if (!asyc->scaler.full) {
  114. if (mode == DRM_MODE_SCALE_NONE)
  115. omode = umode;
  116. } else {
  117. /* Non-EDID LVDS/eDP mode. */
  118. mode = DRM_MODE_SCALE_FULLSCREEN;
  119. }
  120. /* For the user-specified mode, we must ignore doublescan and
  121. * the like, but honor frame packing.
  122. */
  123. umode_vdisplay = umode->vdisplay;
  124. if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
  125. umode_vdisplay += umode->vtotal;
  126. asyh->view.iW = umode->hdisplay;
  127. asyh->view.iH = umode_vdisplay;
  128. /* For the output mode, we can just use the stock helper. */
  129. drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay);
  130. asyh->view.oW = omode_hdisplay;
  131. asyh->view.oH = omode_vdisplay;
  132. /* Add overscan compensation if necessary, will keep the aspect
  133. * ratio the same as the backend mode unless overridden by the
  134. * user setting both hborder and vborder properties.
  135. */
  136. if ((asyc->scaler.underscan.mode == UNDERSCAN_ON ||
  137. (asyc->scaler.underscan.mode == UNDERSCAN_AUTO &&
  138. drm_detect_hdmi_monitor(edid)))) {
  139. u32 bX = asyc->scaler.underscan.hborder;
  140. u32 bY = asyc->scaler.underscan.vborder;
  141. u32 r = (asyh->view.oH << 19) / asyh->view.oW;
  142. if (bX) {
  143. asyh->view.oW -= (bX * 2);
  144. if (bY) asyh->view.oH -= (bY * 2);
  145. else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
  146. } else {
  147. asyh->view.oW -= (asyh->view.oW >> 4) + 32;
  148. if (bY) asyh->view.oH -= (bY * 2);
  149. else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
  150. }
  151. }
  152. /* Handle CENTER/ASPECT scaling, taking into account the areas
  153. * removed already for overscan compensation.
  154. */
  155. switch (mode) {
  156. case DRM_MODE_SCALE_CENTER:
  157. /* NOTE: This will cause scaling when the input is
  158. * larger than the output.
  159. */
  160. asyh->view.oW = min(asyh->view.iW, asyh->view.oW);
  161. asyh->view.oH = min(asyh->view.iH, asyh->view.oH);
  162. break;
  163. case DRM_MODE_SCALE_ASPECT:
  164. /* Determine whether the scaling should be on width or on
  165. * height. This is done by comparing the aspect ratios of the
  166. * sizes. If the output AR is larger than input AR, that means
  167. * we want to change the width (letterboxed on the
  168. * left/right), otherwise on the height (letterboxed on the
  169. * top/bottom).
  170. *
  171. * E.g. 4:3 (1.333) AR image displayed on a 16:10 (1.6) AR
  172. * screen will have letterboxes on the left/right. However a
  173. * 16:9 (1.777) AR image on that same screen will have
  174. * letterboxes on the top/bottom.
  175. *
  176. * inputAR = iW / iH; outputAR = oW / oH
  177. * outputAR > inputAR is equivalent to oW * iH > iW * oH
  178. */
  179. if (asyh->view.oW * asyh->view.iH > asyh->view.iW * asyh->view.oH) {
  180. /* Recompute output width, i.e. left/right letterbox */
  181. u32 r = (asyh->view.iW << 19) / asyh->view.iH;
  182. asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19;
  183. } else {
  184. /* Recompute output height, i.e. top/bottom letterbox */
  185. u32 r = (asyh->view.iH << 19) / asyh->view.iW;
  186. asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
  187. }
  188. break;
  189. default:
  190. break;
  191. }
  192. asyh->set.view = true;
  193. }
  194. static int
  195. nv50_head_atomic_check_lut(struct nv50_head *head,
  196. struct nv50_head_atom *asyh)
  197. {
  198. struct nv50_disp *disp = nv50_disp(head->base.base.dev);
  199. struct drm_property_blob *olut = asyh->state.gamma_lut;
  200. /* Determine whether core output LUT should be enabled. */
  201. if (olut) {
  202. /* Check if any window(s) have stolen the core output LUT
  203. * to as an input LUT for legacy gamma + I8 colour format.
  204. */
  205. if (asyh->wndw.olut) {
  206. /* If any window has stolen the core output LUT,
  207. * all of them must.
  208. */
  209. if (asyh->wndw.olut != asyh->wndw.mask)
  210. return -EINVAL;
  211. olut = NULL;
  212. }
  213. }
  214. if (!olut) {
  215. asyh->olut.handle = 0;
  216. return 0;
  217. }
  218. asyh->olut.handle = disp->core->chan.vram.handle;
  219. asyh->olut.buffer = !asyh->olut.buffer;
  220. head->func->olut(head, asyh);
  221. return 0;
  222. }
  223. static void
  224. nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
  225. {
  226. struct drm_display_mode *mode = &asyh->state.adjusted_mode;
  227. struct nv50_head_mode *m = &asyh->mode;
  228. u32 blankus;
  229. drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
  230. /*
  231. * DRM modes are defined in terms of a repeating interval
  232. * starting with the active display area. The hardware modes
  233. * are defined in terms of a repeating interval starting one
  234. * unit (pixel or line) into the sync pulse. So, add bias.
  235. */
  236. m->h.active = mode->crtc_htotal;
  237. m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1;
  238. m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1;
  239. m->h.blanks = m->h.blanke + mode->crtc_hdisplay;
  240. m->v.active = mode->crtc_vtotal;
  241. m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1;
  242. m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1;
  243. m->v.blanks = m->v.blanke + mode->crtc_vdisplay;
  244. /*XXX: Safe underestimate, even "0" works */
  245. blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active;
  246. blankus *= 1000;
  247. blankus /= mode->crtc_clock;
  248. m->v.blankus = blankus;
  249. if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
  250. m->v.blank2e = m->v.active + m->v.blanke;
  251. m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay;
  252. m->v.active = (m->v.active * 2) + 1;
  253. m->interlace = true;
  254. } else {
  255. m->v.blank2e = 0;
  256. m->v.blank2s = 1;
  257. m->interlace = false;
  258. }
  259. m->clock = mode->crtc_clock;
  260. asyh->or.nhsync = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
  261. asyh->or.nvsync = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
  262. asyh->set.or = head->func->or != NULL;
  263. asyh->set.mode = true;
  264. }
  265. static int
  266. nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
  267. {
  268. struct nouveau_drm *drm = nouveau_drm(crtc->dev);
  269. struct nv50_head *head = nv50_head(crtc);
  270. struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
  271. struct nv50_head_atom *asyh = nv50_head_atom(state);
  272. struct nouveau_conn_atom *asyc = NULL;
  273. struct drm_connector_state *conns;
  274. struct drm_connector *conn;
  275. int i;
  276. NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active);
  277. if (asyh->state.active) {
  278. for_each_new_connector_in_state(asyh->state.state, conn, conns, i) {
  279. if (conns->crtc == crtc) {
  280. asyc = nouveau_conn_atom(conns);
  281. break;
  282. }
  283. }
  284. if (armh->state.active) {
  285. if (asyc) {
  286. if (asyh->state.mode_changed)
  287. asyc->set.scaler = true;
  288. if (armh->base.depth != asyh->base.depth)
  289. asyc->set.dither = true;
  290. }
  291. } else {
  292. if (asyc)
  293. asyc->set.mask = ~0;
  294. asyh->set.mask = ~0;
  295. asyh->set.or = head->func->or != NULL;
  296. }
  297. if (asyh->state.mode_changed || asyh->state.connectors_changed)
  298. nv50_head_atomic_check_mode(head, asyh);
  299. if (asyh->state.color_mgmt_changed ||
  300. memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw))) {
  301. int ret = nv50_head_atomic_check_lut(head, asyh);
  302. if (ret)
  303. return ret;
  304. asyh->olut.visible = asyh->olut.handle != 0;
  305. }
  306. if (asyc) {
  307. if (asyc->set.scaler)
  308. nv50_head_atomic_check_view(armh, asyh, asyc);
  309. if (asyc->set.dither)
  310. nv50_head_atomic_check_dither(armh, asyh, asyc);
  311. if (asyc->set.procamp)
  312. nv50_head_atomic_check_procamp(armh, asyh, asyc);
  313. }
  314. if (head->func->core_calc) {
  315. head->func->core_calc(head, asyh);
  316. if (!asyh->core.visible)
  317. asyh->olut.visible = false;
  318. }
  319. asyh->set.base = armh->base.cpp != asyh->base.cpp;
  320. asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp;
  321. } else {
  322. asyh->olut.visible = false;
  323. asyh->core.visible = false;
  324. asyh->curs.visible = false;
  325. asyh->base.cpp = 0;
  326. asyh->ovly.cpp = 0;
  327. }
  328. if (!drm_atomic_crtc_needs_modeset(&asyh->state)) {
  329. if (asyh->core.visible) {
  330. if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core)))
  331. asyh->set.core = true;
  332. } else
  333. if (armh->core.visible) {
  334. asyh->clr.core = true;
  335. }
  336. if (asyh->curs.visible) {
  337. if (memcmp(&armh->curs, &asyh->curs, sizeof(asyh->curs)))
  338. asyh->set.curs = true;
  339. } else
  340. if (armh->curs.visible) {
  341. asyh->clr.curs = true;
  342. }
  343. if (asyh->olut.visible) {
  344. if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut)))
  345. asyh->set.olut = true;
  346. } else
  347. if (armh->olut.visible) {
  348. asyh->clr.olut = true;
  349. }
  350. } else {
  351. asyh->clr.olut = armh->olut.visible;
  352. asyh->clr.core = armh->core.visible;
  353. asyh->clr.curs = armh->curs.visible;
  354. asyh->set.olut = asyh->olut.visible;
  355. asyh->set.core = asyh->core.visible;
  356. asyh->set.curs = asyh->curs.visible;
  357. }
  358. if (asyh->clr.mask || asyh->set.mask)
  359. nv50_atom(asyh->state.state)->lock_core = true;
  360. return 0;
  361. }
  362. static const struct drm_crtc_helper_funcs
  363. nv50_head_help = {
  364. .atomic_check = nv50_head_atomic_check,
  365. };
  366. static void
  367. nv50_head_atomic_destroy_state(struct drm_crtc *crtc,
  368. struct drm_crtc_state *state)
  369. {
  370. struct nv50_head_atom *asyh = nv50_head_atom(state);
  371. __drm_atomic_helper_crtc_destroy_state(&asyh->state);
  372. kfree(asyh);
  373. }
  374. static struct drm_crtc_state *
  375. nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
  376. {
  377. struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
  378. struct nv50_head_atom *asyh;
  379. if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL)))
  380. return NULL;
  381. __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state);
  382. asyh->wndw = armh->wndw;
  383. asyh->view = armh->view;
  384. asyh->mode = armh->mode;
  385. asyh->olut = armh->olut;
  386. asyh->core = armh->core;
  387. asyh->curs = armh->curs;
  388. asyh->base = armh->base;
  389. asyh->ovly = armh->ovly;
  390. asyh->dither = armh->dither;
  391. asyh->procamp = armh->procamp;
  392. asyh->clr.mask = 0;
  393. asyh->set.mask = 0;
  394. return &asyh->state;
  395. }
  396. static void
  397. __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc,
  398. struct drm_crtc_state *state)
  399. {
  400. if (crtc->state)
  401. crtc->funcs->atomic_destroy_state(crtc, crtc->state);
  402. crtc->state = state;
  403. crtc->state->crtc = crtc;
  404. }
  405. static void
  406. nv50_head_reset(struct drm_crtc *crtc)
  407. {
  408. struct nv50_head_atom *asyh;
  409. if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL))))
  410. return;
  411. __drm_atomic_helper_crtc_reset(crtc, &asyh->state);
  412. }
  413. static void
  414. nv50_head_destroy(struct drm_crtc *crtc)
  415. {
  416. struct nv50_head *head = nv50_head(crtc);
  417. nv50_lut_fini(&head->olut);
  418. drm_crtc_cleanup(crtc);
  419. kfree(head);
  420. }
  421. static const struct drm_crtc_funcs
  422. nv50_head_func = {
  423. .reset = nv50_head_reset,
  424. .gamma_set = drm_atomic_helper_legacy_gamma_set,
  425. .destroy = nv50_head_destroy,
  426. .set_config = drm_atomic_helper_set_config,
  427. .page_flip = drm_atomic_helper_page_flip,
  428. .atomic_duplicate_state = nv50_head_atomic_duplicate_state,
  429. .atomic_destroy_state = nv50_head_atomic_destroy_state,
  430. };
  431. int
  432. nv50_head_create(struct drm_device *dev, int index)
  433. {
  434. struct nouveau_drm *drm = nouveau_drm(dev);
  435. struct nv50_disp *disp = nv50_disp(dev);
  436. struct nv50_head *head;
  437. struct nv50_wndw *curs, *wndw;
  438. struct drm_crtc *crtc;
  439. int ret;
  440. head = kzalloc(sizeof(*head), GFP_KERNEL);
  441. if (!head)
  442. return -ENOMEM;
  443. head->func = disp->core->func->head;
  444. head->base.index = index;
  445. if (disp->disp->object.oclass < GV100_DISP) {
  446. ret = nv50_ovly_new(drm, head->base.index, &wndw);
  447. ret = nv50_base_new(drm, head->base.index, &wndw);
  448. } else {
  449. ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
  450. head->base.index * 2 + 1, &wndw);
  451. ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY,
  452. head->base.index * 2 + 0, &wndw);
  453. }
  454. if (ret == 0)
  455. ret = nv50_curs_new(drm, head->base.index, &curs);
  456. if (ret) {
  457. kfree(head);
  458. return ret;
  459. }
  460. crtc = &head->base.base;
  461. drm_crtc_init_with_planes(dev, crtc, &wndw->plane, &curs->plane,
  462. &nv50_head_func, "head-%d", head->base.index);
  463. drm_crtc_helper_add(crtc, &nv50_head_help);
  464. drm_mode_crtc_set_gamma_size(crtc, 256);
  465. if (head->func->olut_set) {
  466. ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
  467. if (ret)
  468. goto out;
  469. }
  470. out:
  471. if (ret)
  472. nv50_head_destroy(crtc);
  473. return ret;
  474. }