outpdp.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright 2014 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. * Authors: Ben Skeggs
  23. */
  24. #include "outpdp.h"
  25. #include "conn.h"
  26. #include "dport.h"
  27. #include "priv.h"
  28. #include <subdev/i2c.h>
  29. #include <nvif/event.h>
  30. int
  31. nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
  32. {
  33. struct nvkm_output_dp *outp = nvkm_output_dp(base);
  34. bool retrain = true;
  35. u8 link[2], stat[3];
  36. u32 linkrate;
  37. int ret, i;
  38. /* check that the link is trained at a high enough rate */
  39. ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2);
  40. if (ret) {
  41. OUTP_DBG(&outp->base,
  42. "failed to read link config, assuming no sink");
  43. goto done;
  44. }
  45. linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
  46. linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
  47. datarate = (datarate + 9) / 10; /* -> decakilobits */
  48. if (linkrate < datarate) {
  49. OUTP_DBG(&outp->base, "link not trained at sufficient rate");
  50. goto done;
  51. }
  52. /* check that link is still trained */
  53. ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3);
  54. if (ret) {
  55. OUTP_DBG(&outp->base,
  56. "failed to read link status, assuming no sink");
  57. goto done;
  58. }
  59. if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
  60. for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
  61. u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
  62. if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
  63. !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
  64. !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
  65. OUTP_DBG(&outp->base,
  66. "lane %d not equalised", lane);
  67. goto done;
  68. }
  69. }
  70. retrain = false;
  71. } else {
  72. OUTP_DBG(&outp->base, "no inter-lane alignment");
  73. }
  74. done:
  75. if (retrain || !atomic_read(&outp->lt.done)) {
  76. /* no sink, but still need to configure source */
  77. if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
  78. outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
  79. outp->base.info.dpconf.link_bw;
  80. outp->dpcd[DPCD_RC02] =
  81. outp->base.info.dpconf.link_nr;
  82. }
  83. atomic_set(&outp->lt.done, 0);
  84. schedule_work(&outp->lt.work);
  85. } else {
  86. nvkm_notify_get(&outp->irq);
  87. }
  88. if (wait) {
  89. if (!wait_event_timeout(outp->lt.wait,
  90. atomic_read(&outp->lt.done),
  91. msecs_to_jiffies(2000)))
  92. ret = -ETIMEDOUT;
  93. }
  94. return ret;
  95. }
  96. static void
  97. nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
  98. {
  99. struct nvkm_i2c_aux *aux = outp->aux;
  100. if (enable) {
  101. if (!outp->present) {
  102. OUTP_DBG(&outp->base, "aux power -> always");
  103. nvkm_i2c_aux_monitor(aux, true);
  104. outp->present = true;
  105. }
  106. if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dpcd,
  107. sizeof(outp->dpcd))) {
  108. nvkm_output_dp_train(&outp->base, 0, true);
  109. return;
  110. }
  111. }
  112. if (outp->present) {
  113. OUTP_DBG(&outp->base, "aux power -> demand");
  114. nvkm_i2c_aux_monitor(aux, false);
  115. outp->present = false;
  116. }
  117. atomic_set(&outp->lt.done, 0);
  118. }
  119. static int
  120. nvkm_output_dp_hpd(struct nvkm_notify *notify)
  121. {
  122. const struct nvkm_i2c_ntfy_rep *line = notify->data;
  123. struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd);
  124. struct nvkm_connector *conn = outp->base.conn;
  125. struct nvkm_disp *disp = outp->base.disp;
  126. struct nvif_notify_conn_rep_v0 rep = {};
  127. OUTP_DBG(&outp->base, "HPD: %d", line->mask);
  128. nvkm_output_dp_enable(outp, true);
  129. if (line->mask & NVKM_I2C_UNPLUG)
  130. rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
  131. if (line->mask & NVKM_I2C_PLUG)
  132. rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
  133. nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
  134. return NVKM_NOTIFY_KEEP;
  135. }
  136. static int
  137. nvkm_output_dp_irq(struct nvkm_notify *notify)
  138. {
  139. const struct nvkm_i2c_ntfy_rep *line = notify->data;
  140. struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
  141. struct nvkm_connector *conn = outp->base.conn;
  142. struct nvkm_disp *disp = outp->base.disp;
  143. struct nvif_notify_conn_rep_v0 rep = {
  144. .mask = NVIF_NOTIFY_CONN_V0_IRQ,
  145. };
  146. OUTP_DBG(&outp->base, "IRQ: %d", line->mask);
  147. nvkm_output_dp_train(&outp->base, 0, true);
  148. nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
  149. return NVKM_NOTIFY_DROP;
  150. }
  151. static void
  152. nvkm_output_dp_fini(struct nvkm_output *base)
  153. {
  154. struct nvkm_output_dp *outp = nvkm_output_dp(base);
  155. nvkm_notify_put(&outp->hpd);
  156. nvkm_notify_put(&outp->irq);
  157. flush_work(&outp->lt.work);
  158. nvkm_output_dp_enable(outp, false);
  159. }
  160. static void
  161. nvkm_output_dp_init(struct nvkm_output *base)
  162. {
  163. struct nvkm_output_dp *outp = nvkm_output_dp(base);
  164. nvkm_notify_put(&outp->base.conn->hpd);
  165. nvkm_output_dp_enable(outp, true);
  166. nvkm_notify_get(&outp->hpd);
  167. }
  168. static void *
  169. nvkm_output_dp_dtor(struct nvkm_output *base)
  170. {
  171. struct nvkm_output_dp *outp = nvkm_output_dp(base);
  172. nvkm_notify_fini(&outp->hpd);
  173. nvkm_notify_fini(&outp->irq);
  174. return outp;
  175. }
  176. static const struct nvkm_output_func
  177. nvkm_output_dp_func = {
  178. .dtor = nvkm_output_dp_dtor,
  179. .init = nvkm_output_dp_init,
  180. .fini = nvkm_output_dp_fini,
  181. };
  182. int
  183. nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func,
  184. struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
  185. struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp)
  186. {
  187. struct nvkm_device *device = disp->engine.subdev.device;
  188. struct nvkm_bios *bios = device->bios;
  189. struct nvkm_i2c *i2c = device->i2c;
  190. u8 hdr, cnt, len;
  191. u32 data;
  192. int ret;
  193. nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base);
  194. outp->func = func;
  195. outp->aux = aux;
  196. if (!outp->aux) {
  197. OUTP_ERR(&outp->base, "no aux");
  198. return -ENODEV;
  199. }
  200. /* bios data is not optional */
  201. data = nvbios_dpout_match(bios, outp->base.info.hasht,
  202. outp->base.info.hashm, &outp->version,
  203. &hdr, &cnt, &len, &outp->info);
  204. if (!data) {
  205. OUTP_ERR(&outp->base, "no bios dp data");
  206. return -ENODEV;
  207. }
  208. OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x",
  209. outp->version, hdr, cnt, len);
  210. /* link training */
  211. INIT_WORK(&outp->lt.work, nvkm_dp_train);
  212. init_waitqueue_head(&outp->lt.wait);
  213. atomic_set(&outp->lt.done, 0);
  214. /* link maintenance */
  215. ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
  216. &(struct nvkm_i2c_ntfy_req) {
  217. .mask = NVKM_I2C_IRQ,
  218. .port = outp->aux->id,
  219. },
  220. sizeof(struct nvkm_i2c_ntfy_req),
  221. sizeof(struct nvkm_i2c_ntfy_rep),
  222. &outp->irq);
  223. if (ret) {
  224. OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret);
  225. return ret;
  226. }
  227. /* hotplug detect, replaces gpio-based mechanism with aux events */
  228. ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
  229. &(struct nvkm_i2c_ntfy_req) {
  230. .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
  231. .port = outp->aux->id,
  232. },
  233. sizeof(struct nvkm_i2c_ntfy_req),
  234. sizeof(struct nvkm_i2c_ntfy_rep),
  235. &outp->hpd);
  236. if (ret) {
  237. OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret);
  238. return ret;
  239. }
  240. return 0;
  241. }
  242. int
  243. nvkm_output_dp_new_(const struct nvkm_output_dp_func *func,
  244. struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
  245. struct nvkm_output **poutp)
  246. {
  247. struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
  248. struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index);
  249. struct nvkm_output_dp *outp;
  250. if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
  251. return -ENOMEM;
  252. *poutp = &outp->base;
  253. return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp);
  254. }