outpdp.c 7.8 KB

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