dvo_ns2501.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /*
  2. *
  3. * Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
  4. *
  5. * All Rights Reserved.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a
  8. * copy of this software and associated documentation files (the
  9. * "Software"), to deal in the Software without restriction, including
  10. * without limitation the rights to use, copy, modify, merge, publish,
  11. * distribute, sub license, and/or sell copies of the Software, and to
  12. * permit persons to whom the Software is furnished to do so, subject to
  13. * the following conditions:
  14. *
  15. * The above copyright notice and this permission notice (including the
  16. * next paragraph) shall be included in all copies or substantial portions
  17. * of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
  22. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  23. * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  24. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  25. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. *
  27. */
  28. #include "dvo.h"
  29. #include "i915_reg.h"
  30. #include "i915_drv.h"
  31. #define NS2501_VID 0x1305
  32. #define NS2501_DID 0x6726
  33. #define NS2501_VID_LO 0x00
  34. #define NS2501_VID_HI 0x01
  35. #define NS2501_DID_LO 0x02
  36. #define NS2501_DID_HI 0x03
  37. #define NS2501_REV 0x04
  38. #define NS2501_RSVD 0x05
  39. #define NS2501_FREQ_LO 0x06
  40. #define NS2501_FREQ_HI 0x07
  41. #define NS2501_REG8 0x08
  42. #define NS2501_8_VEN (1<<5)
  43. #define NS2501_8_HEN (1<<4)
  44. #define NS2501_8_DSEL (1<<3)
  45. #define NS2501_8_BPAS (1<<2)
  46. #define NS2501_8_RSVD (1<<1)
  47. #define NS2501_8_PD (1<<0)
  48. #define NS2501_REG9 0x09
  49. #define NS2501_9_VLOW (1<<7)
  50. #define NS2501_9_MSEL_MASK (0x7<<4)
  51. #define NS2501_9_TSEL (1<<3)
  52. #define NS2501_9_RSEN (1<<2)
  53. #define NS2501_9_RSVD (1<<1)
  54. #define NS2501_9_MDI (1<<0)
  55. #define NS2501_REGC 0x0c
  56. enum {
  57. MODE_640x480,
  58. MODE_800x600,
  59. MODE_1024x768,
  60. };
  61. struct ns2501_reg {
  62. uint8_t offset;
  63. uint8_t value;
  64. };
  65. /*
  66. * Magic values based on what the BIOS on
  67. * Fujitsu-Siemens Lifebook S6010 programs (1024x768 panel).
  68. */
  69. static const struct ns2501_reg regs_1024x768[][86] = {
  70. [MODE_640x480] = {
  71. [0] = { .offset = 0x0a, .value = 0x81, },
  72. [1] = { .offset = 0x18, .value = 0x07, },
  73. [2] = { .offset = 0x19, .value = 0x00, },
  74. [3] = { .offset = 0x1a, .value = 0x00, },
  75. [4] = { .offset = 0x1b, .value = 0x11, },
  76. [5] = { .offset = 0x1c, .value = 0x54, },
  77. [6] = { .offset = 0x1d, .value = 0x03, },
  78. [7] = { .offset = 0x1e, .value = 0x02, },
  79. [8] = { .offset = 0xf3, .value = 0x90, },
  80. [9] = { .offset = 0xf9, .value = 0x00, },
  81. [10] = { .offset = 0xc1, .value = 0x90, },
  82. [11] = { .offset = 0xc2, .value = 0x00, },
  83. [12] = { .offset = 0xc3, .value = 0x0f, },
  84. [13] = { .offset = 0xc4, .value = 0x03, },
  85. [14] = { .offset = 0xc5, .value = 0x16, },
  86. [15] = { .offset = 0xc6, .value = 0x00, },
  87. [16] = { .offset = 0xc7, .value = 0x02, },
  88. [17] = { .offset = 0xc8, .value = 0x02, },
  89. [18] = { .offset = 0xf4, .value = 0x00, },
  90. [19] = { .offset = 0x80, .value = 0xff, },
  91. [20] = { .offset = 0x81, .value = 0x07, },
  92. [21] = { .offset = 0x82, .value = 0x3d, },
  93. [22] = { .offset = 0x83, .value = 0x05, },
  94. [23] = { .offset = 0x94, .value = 0x00, },
  95. [24] = { .offset = 0x95, .value = 0x00, },
  96. [25] = { .offset = 0x96, .value = 0x05, },
  97. [26] = { .offset = 0x97, .value = 0x00, },
  98. [27] = { .offset = 0x9a, .value = 0x88, },
  99. [28] = { .offset = 0x9b, .value = 0x00, },
  100. [29] = { .offset = 0x98, .value = 0x00, },
  101. [30] = { .offset = 0x99, .value = 0x00, },
  102. [31] = { .offset = 0xf7, .value = 0x88, },
  103. [32] = { .offset = 0xf8, .value = 0x0a, },
  104. [33] = { .offset = 0x9c, .value = 0x24, },
  105. [34] = { .offset = 0x9d, .value = 0x00, },
  106. [35] = { .offset = 0x9e, .value = 0x25, },
  107. [36] = { .offset = 0x9f, .value = 0x03, },
  108. [37] = { .offset = 0xa0, .value = 0x28, },
  109. [38] = { .offset = 0xa1, .value = 0x01, },
  110. [39] = { .offset = 0xa2, .value = 0x28, },
  111. [40] = { .offset = 0xa3, .value = 0x05, },
  112. [41] = { .offset = 0xb6, .value = 0x09, },
  113. [42] = { .offset = 0xb8, .value = 0x00, },
  114. [43] = { .offset = 0xb9, .value = 0xa0, },
  115. [44] = { .offset = 0xba, .value = 0x00, },
  116. [45] = { .offset = 0xbb, .value = 0x20, },
  117. [46] = { .offset = 0x10, .value = 0x00, },
  118. [47] = { .offset = 0x11, .value = 0xa0, },
  119. [48] = { .offset = 0x12, .value = 0x02, },
  120. [49] = { .offset = 0x20, .value = 0x00, },
  121. [50] = { .offset = 0x22, .value = 0x00, },
  122. [51] = { .offset = 0x23, .value = 0x00, },
  123. [52] = { .offset = 0x24, .value = 0x00, },
  124. [53] = { .offset = 0x25, .value = 0x00, },
  125. [54] = { .offset = 0x8c, .value = 0x10, },
  126. [55] = { .offset = 0x8d, .value = 0x02, },
  127. [56] = { .offset = 0x8e, .value = 0x10, },
  128. [57] = { .offset = 0x8f, .value = 0x00, },
  129. [58] = { .offset = 0x90, .value = 0xff, },
  130. [59] = { .offset = 0x91, .value = 0x07, },
  131. [60] = { .offset = 0x92, .value = 0xa0, },
  132. [61] = { .offset = 0x93, .value = 0x02, },
  133. [62] = { .offset = 0xa5, .value = 0x00, },
  134. [63] = { .offset = 0xa6, .value = 0x00, },
  135. [64] = { .offset = 0xa7, .value = 0x00, },
  136. [65] = { .offset = 0xa8, .value = 0x00, },
  137. [66] = { .offset = 0xa9, .value = 0x04, },
  138. [67] = { .offset = 0xaa, .value = 0x70, },
  139. [68] = { .offset = 0xab, .value = 0x4f, },
  140. [69] = { .offset = 0xac, .value = 0x00, },
  141. [70] = { .offset = 0xa4, .value = 0x84, },
  142. [71] = { .offset = 0x7e, .value = 0x18, },
  143. [72] = { .offset = 0x84, .value = 0x00, },
  144. [73] = { .offset = 0x85, .value = 0x00, },
  145. [74] = { .offset = 0x86, .value = 0x00, },
  146. [75] = { .offset = 0x87, .value = 0x00, },
  147. [76] = { .offset = 0x88, .value = 0x00, },
  148. [77] = { .offset = 0x89, .value = 0x00, },
  149. [78] = { .offset = 0x8a, .value = 0x00, },
  150. [79] = { .offset = 0x8b, .value = 0x00, },
  151. [80] = { .offset = 0x26, .value = 0x00, },
  152. [81] = { .offset = 0x27, .value = 0x00, },
  153. [82] = { .offset = 0xad, .value = 0x00, },
  154. [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
  155. [84] = { .offset = 0x41, .value = 0x00, },
  156. [85] = { .offset = 0xc0, .value = 0x05, },
  157. },
  158. [MODE_800x600] = {
  159. [0] = { .offset = 0x0a, .value = 0x81, },
  160. [1] = { .offset = 0x18, .value = 0x07, },
  161. [2] = { .offset = 0x19, .value = 0x00, },
  162. [3] = { .offset = 0x1a, .value = 0x00, },
  163. [4] = { .offset = 0x1b, .value = 0x19, },
  164. [5] = { .offset = 0x1c, .value = 0x64, },
  165. [6] = { .offset = 0x1d, .value = 0x02, },
  166. [7] = { .offset = 0x1e, .value = 0x02, },
  167. [8] = { .offset = 0xf3, .value = 0x90, },
  168. [9] = { .offset = 0xf9, .value = 0x00, },
  169. [10] = { .offset = 0xc1, .value = 0xd7, },
  170. [11] = { .offset = 0xc2, .value = 0x00, },
  171. [12] = { .offset = 0xc3, .value = 0xf8, },
  172. [13] = { .offset = 0xc4, .value = 0x03, },
  173. [14] = { .offset = 0xc5, .value = 0x1a, },
  174. [15] = { .offset = 0xc6, .value = 0x00, },
  175. [16] = { .offset = 0xc7, .value = 0x73, },
  176. [17] = { .offset = 0xc8, .value = 0x02, },
  177. [18] = { .offset = 0xf4, .value = 0x00, },
  178. [19] = { .offset = 0x80, .value = 0x27, },
  179. [20] = { .offset = 0x81, .value = 0x03, },
  180. [21] = { .offset = 0x82, .value = 0x41, },
  181. [22] = { .offset = 0x83, .value = 0x05, },
  182. [23] = { .offset = 0x94, .value = 0x00, },
  183. [24] = { .offset = 0x95, .value = 0x00, },
  184. [25] = { .offset = 0x96, .value = 0x05, },
  185. [26] = { .offset = 0x97, .value = 0x00, },
  186. [27] = { .offset = 0x9a, .value = 0x88, },
  187. [28] = { .offset = 0x9b, .value = 0x00, },
  188. [29] = { .offset = 0x98, .value = 0x00, },
  189. [30] = { .offset = 0x99, .value = 0x00, },
  190. [31] = { .offset = 0xf7, .value = 0x88, },
  191. [32] = { .offset = 0xf8, .value = 0x06, },
  192. [33] = { .offset = 0x9c, .value = 0x23, },
  193. [34] = { .offset = 0x9d, .value = 0x00, },
  194. [35] = { .offset = 0x9e, .value = 0x25, },
  195. [36] = { .offset = 0x9f, .value = 0x03, },
  196. [37] = { .offset = 0xa0, .value = 0x28, },
  197. [38] = { .offset = 0xa1, .value = 0x01, },
  198. [39] = { .offset = 0xa2, .value = 0x28, },
  199. [40] = { .offset = 0xa3, .value = 0x05, },
  200. [41] = { .offset = 0xb6, .value = 0x09, },
  201. [42] = { .offset = 0xb8, .value = 0x30, },
  202. [43] = { .offset = 0xb9, .value = 0xc8, },
  203. [44] = { .offset = 0xba, .value = 0x00, },
  204. [45] = { .offset = 0xbb, .value = 0x20, },
  205. [46] = { .offset = 0x10, .value = 0x20, },
  206. [47] = { .offset = 0x11, .value = 0xc8, },
  207. [48] = { .offset = 0x12, .value = 0x02, },
  208. [49] = { .offset = 0x20, .value = 0x00, },
  209. [50] = { .offset = 0x22, .value = 0x00, },
  210. [51] = { .offset = 0x23, .value = 0x00, },
  211. [52] = { .offset = 0x24, .value = 0x00, },
  212. [53] = { .offset = 0x25, .value = 0x00, },
  213. [54] = { .offset = 0x8c, .value = 0x10, },
  214. [55] = { .offset = 0x8d, .value = 0x02, },
  215. [56] = { .offset = 0x8e, .value = 0x04, },
  216. [57] = { .offset = 0x8f, .value = 0x00, },
  217. [58] = { .offset = 0x90, .value = 0xff, },
  218. [59] = { .offset = 0x91, .value = 0x07, },
  219. [60] = { .offset = 0x92, .value = 0xa0, },
  220. [61] = { .offset = 0x93, .value = 0x02, },
  221. [62] = { .offset = 0xa5, .value = 0x00, },
  222. [63] = { .offset = 0xa6, .value = 0x00, },
  223. [64] = { .offset = 0xa7, .value = 0x00, },
  224. [65] = { .offset = 0xa8, .value = 0x00, },
  225. [66] = { .offset = 0xa9, .value = 0x83, },
  226. [67] = { .offset = 0xaa, .value = 0x40, },
  227. [68] = { .offset = 0xab, .value = 0x32, },
  228. [69] = { .offset = 0xac, .value = 0x00, },
  229. [70] = { .offset = 0xa4, .value = 0x80, },
  230. [71] = { .offset = 0x7e, .value = 0x18, },
  231. [72] = { .offset = 0x84, .value = 0x00, },
  232. [73] = { .offset = 0x85, .value = 0x00, },
  233. [74] = { .offset = 0x86, .value = 0x00, },
  234. [75] = { .offset = 0x87, .value = 0x00, },
  235. [76] = { .offset = 0x88, .value = 0x00, },
  236. [77] = { .offset = 0x89, .value = 0x00, },
  237. [78] = { .offset = 0x8a, .value = 0x00, },
  238. [79] = { .offset = 0x8b, .value = 0x00, },
  239. [80] = { .offset = 0x26, .value = 0x00, },
  240. [81] = { .offset = 0x27, .value = 0x00, },
  241. [82] = { .offset = 0xad, .value = 0x00, },
  242. [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
  243. [84] = { .offset = 0x41, .value = 0x00, },
  244. [85] = { .offset = 0xc0, .value = 0x07, },
  245. },
  246. [MODE_1024x768] = {
  247. [0] = { .offset = 0x0a, .value = 0x81, },
  248. [1] = { .offset = 0x18, .value = 0x07, },
  249. [2] = { .offset = 0x19, .value = 0x00, },
  250. [3] = { .offset = 0x1a, .value = 0x00, },
  251. [4] = { .offset = 0x1b, .value = 0x11, },
  252. [5] = { .offset = 0x1c, .value = 0x54, },
  253. [6] = { .offset = 0x1d, .value = 0x03, },
  254. [7] = { .offset = 0x1e, .value = 0x02, },
  255. [8] = { .offset = 0xf3, .value = 0x90, },
  256. [9] = { .offset = 0xf9, .value = 0x00, },
  257. [10] = { .offset = 0xc1, .value = 0x90, },
  258. [11] = { .offset = 0xc2, .value = 0x00, },
  259. [12] = { .offset = 0xc3, .value = 0x0f, },
  260. [13] = { .offset = 0xc4, .value = 0x03, },
  261. [14] = { .offset = 0xc5, .value = 0x16, },
  262. [15] = { .offset = 0xc6, .value = 0x00, },
  263. [16] = { .offset = 0xc7, .value = 0x02, },
  264. [17] = { .offset = 0xc8, .value = 0x02, },
  265. [18] = { .offset = 0xf4, .value = 0x00, },
  266. [19] = { .offset = 0x80, .value = 0xff, },
  267. [20] = { .offset = 0x81, .value = 0x07, },
  268. [21] = { .offset = 0x82, .value = 0x3d, },
  269. [22] = { .offset = 0x83, .value = 0x05, },
  270. [23] = { .offset = 0x94, .value = 0x00, },
  271. [24] = { .offset = 0x95, .value = 0x00, },
  272. [25] = { .offset = 0x96, .value = 0x05, },
  273. [26] = { .offset = 0x97, .value = 0x00, },
  274. [27] = { .offset = 0x9a, .value = 0x88, },
  275. [28] = { .offset = 0x9b, .value = 0x00, },
  276. [29] = { .offset = 0x98, .value = 0x00, },
  277. [30] = { .offset = 0x99, .value = 0x00, },
  278. [31] = { .offset = 0xf7, .value = 0x88, },
  279. [32] = { .offset = 0xf8, .value = 0x0a, },
  280. [33] = { .offset = 0x9c, .value = 0x24, },
  281. [34] = { .offset = 0x9d, .value = 0x00, },
  282. [35] = { .offset = 0x9e, .value = 0x25, },
  283. [36] = { .offset = 0x9f, .value = 0x03, },
  284. [37] = { .offset = 0xa0, .value = 0x28, },
  285. [38] = { .offset = 0xa1, .value = 0x01, },
  286. [39] = { .offset = 0xa2, .value = 0x28, },
  287. [40] = { .offset = 0xa3, .value = 0x05, },
  288. [41] = { .offset = 0xb6, .value = 0x09, },
  289. [42] = { .offset = 0xb8, .value = 0x00, },
  290. [43] = { .offset = 0xb9, .value = 0xa0, },
  291. [44] = { .offset = 0xba, .value = 0x00, },
  292. [45] = { .offset = 0xbb, .value = 0x20, },
  293. [46] = { .offset = 0x10, .value = 0x00, },
  294. [47] = { .offset = 0x11, .value = 0xa0, },
  295. [48] = { .offset = 0x12, .value = 0x02, },
  296. [49] = { .offset = 0x20, .value = 0x00, },
  297. [50] = { .offset = 0x22, .value = 0x00, },
  298. [51] = { .offset = 0x23, .value = 0x00, },
  299. [52] = { .offset = 0x24, .value = 0x00, },
  300. [53] = { .offset = 0x25, .value = 0x00, },
  301. [54] = { .offset = 0x8c, .value = 0x10, },
  302. [55] = { .offset = 0x8d, .value = 0x02, },
  303. [56] = { .offset = 0x8e, .value = 0x10, },
  304. [57] = { .offset = 0x8f, .value = 0x00, },
  305. [58] = { .offset = 0x90, .value = 0xff, },
  306. [59] = { .offset = 0x91, .value = 0x07, },
  307. [60] = { .offset = 0x92, .value = 0xa0, },
  308. [61] = { .offset = 0x93, .value = 0x02, },
  309. [62] = { .offset = 0xa5, .value = 0x00, },
  310. [63] = { .offset = 0xa6, .value = 0x00, },
  311. [64] = { .offset = 0xa7, .value = 0x00, },
  312. [65] = { .offset = 0xa8, .value = 0x00, },
  313. [66] = { .offset = 0xa9, .value = 0x04, },
  314. [67] = { .offset = 0xaa, .value = 0x70, },
  315. [68] = { .offset = 0xab, .value = 0x4f, },
  316. [69] = { .offset = 0xac, .value = 0x00, },
  317. [70] = { .offset = 0xa4, .value = 0x84, },
  318. [71] = { .offset = 0x7e, .value = 0x18, },
  319. [72] = { .offset = 0x84, .value = 0x00, },
  320. [73] = { .offset = 0x85, .value = 0x00, },
  321. [74] = { .offset = 0x86, .value = 0x00, },
  322. [75] = { .offset = 0x87, .value = 0x00, },
  323. [76] = { .offset = 0x88, .value = 0x00, },
  324. [77] = { .offset = 0x89, .value = 0x00, },
  325. [78] = { .offset = 0x8a, .value = 0x00, },
  326. [79] = { .offset = 0x8b, .value = 0x00, },
  327. [80] = { .offset = 0x26, .value = 0x00, },
  328. [81] = { .offset = 0x27, .value = 0x00, },
  329. [82] = { .offset = 0xad, .value = 0x00, },
  330. [83] = { .offset = 0x08, .value = 0x34, }, /* 0x35 */
  331. [84] = { .offset = 0x41, .value = 0x00, },
  332. [85] = { .offset = 0xc0, .value = 0x01, },
  333. },
  334. };
  335. static const struct ns2501_reg regs_init[] = {
  336. [0] = { .offset = 0x35, .value = 0xff, },
  337. [1] = { .offset = 0x34, .value = 0x00, },
  338. [2] = { .offset = 0x08, .value = 0x30, },
  339. };
  340. struct ns2501_priv {
  341. bool quiet;
  342. const struct ns2501_reg *regs;
  343. };
  344. #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
  345. /*
  346. * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens
  347. * laptops does not react on the i2c bus unless
  348. * both the PLL is running and the display is configured in its native
  349. * resolution.
  350. * This function forces the DVO on, and stores the registers it touches.
  351. * Afterwards, registers are restored to regular values.
  352. *
  353. * This is pretty much a hack, though it works.
  354. * Without that, ns2501_readb and ns2501_writeb fail
  355. * when switching the resolution.
  356. */
  357. /*
  358. ** Read a register from the ns2501.
  359. ** Returns true if successful, false otherwise.
  360. ** If it returns false, it might be wise to enable the
  361. ** DVO with the above function.
  362. */
  363. static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
  364. {
  365. struct ns2501_priv *ns = dvo->dev_priv;
  366. struct i2c_adapter *adapter = dvo->i2c_bus;
  367. u8 out_buf[2];
  368. u8 in_buf[2];
  369. struct i2c_msg msgs[] = {
  370. {
  371. .addr = dvo->slave_addr,
  372. .flags = 0,
  373. .len = 1,
  374. .buf = out_buf,
  375. },
  376. {
  377. .addr = dvo->slave_addr,
  378. .flags = I2C_M_RD,
  379. .len = 1,
  380. .buf = in_buf,
  381. }
  382. };
  383. out_buf[0] = addr;
  384. out_buf[1] = 0;
  385. if (i2c_transfer(adapter, msgs, 2) == 2) {
  386. *ch = in_buf[0];
  387. return true;
  388. }
  389. if (!ns->quiet) {
  390. DRM_DEBUG_KMS
  391. ("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
  392. adapter->name, dvo->slave_addr);
  393. }
  394. return false;
  395. }
  396. /*
  397. ** Write a register to the ns2501.
  398. ** Returns true if successful, false otherwise.
  399. ** If it returns false, it might be wise to enable the
  400. ** DVO with the above function.
  401. */
  402. static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
  403. {
  404. struct ns2501_priv *ns = dvo->dev_priv;
  405. struct i2c_adapter *adapter = dvo->i2c_bus;
  406. uint8_t out_buf[2];
  407. struct i2c_msg msg = {
  408. .addr = dvo->slave_addr,
  409. .flags = 0,
  410. .len = 2,
  411. .buf = out_buf,
  412. };
  413. out_buf[0] = addr;
  414. out_buf[1] = ch;
  415. if (i2c_transfer(adapter, &msg, 1) == 1) {
  416. return true;
  417. }
  418. if (!ns->quiet) {
  419. DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
  420. addr, adapter->name, dvo->slave_addr);
  421. }
  422. return false;
  423. }
  424. /* National Semiconductor 2501 driver for chip on i2c bus
  425. * scan for the chip on the bus.
  426. * Hope the VBIOS initialized the PLL correctly so we can
  427. * talk to it. If not, it will not be seen and not detected.
  428. * Bummer!
  429. */
  430. static bool ns2501_init(struct intel_dvo_device *dvo,
  431. struct i2c_adapter *adapter)
  432. {
  433. /* this will detect the NS2501 chip on the specified i2c bus */
  434. struct ns2501_priv *ns;
  435. unsigned char ch;
  436. ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL);
  437. if (ns == NULL)
  438. return false;
  439. dvo->i2c_bus = adapter;
  440. dvo->dev_priv = ns;
  441. ns->quiet = true;
  442. if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
  443. goto out;
  444. if (ch != (NS2501_VID & 0xff)) {
  445. DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
  446. ch, adapter->name, dvo->slave_addr);
  447. goto out;
  448. }
  449. if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
  450. goto out;
  451. if (ch != (NS2501_DID & 0xff)) {
  452. DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
  453. ch, adapter->name, dvo->slave_addr);
  454. goto out;
  455. }
  456. ns->quiet = false;
  457. DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
  458. return true;
  459. out:
  460. kfree(ns);
  461. return false;
  462. }
  463. static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
  464. {
  465. /*
  466. * This is a Laptop display, it doesn't have hotplugging.
  467. * Even if not, the detection bit of the 2501 is unreliable as
  468. * it only works for some display types.
  469. * It is even more unreliable as the PLL must be active for
  470. * allowing reading from the chiop.
  471. */
  472. return connector_status_connected;
  473. }
  474. static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
  475. struct drm_display_mode *mode)
  476. {
  477. DRM_DEBUG_KMS
  478. ("is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
  479. mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
  480. /*
  481. * Currently, these are all the modes I have data from.
  482. * More might exist. Unclear how to find the native resolution
  483. * of the panel in here so we could always accept it
  484. * by disabling the scaler.
  485. */
  486. if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) ||
  487. (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) ||
  488. (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) {
  489. return MODE_OK;
  490. } else {
  491. return MODE_ONE_SIZE; /* Is this a reasonable error? */
  492. }
  493. }
  494. static void ns2501_mode_set(struct intel_dvo_device *dvo,
  495. struct drm_display_mode *mode,
  496. struct drm_display_mode *adjusted_mode)
  497. {
  498. struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
  499. int mode_idx, i;
  500. DRM_DEBUG_KMS
  501. ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
  502. mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
  503. if (mode->hdisplay == 640 && mode->vdisplay == 480)
  504. mode_idx = MODE_640x480;
  505. else if (mode->hdisplay == 800 && mode->vdisplay == 600)
  506. mode_idx = MODE_800x600;
  507. else if (mode->hdisplay == 1024 && mode->vdisplay == 768)
  508. mode_idx = MODE_1024x768;
  509. else
  510. return;
  511. /* Hopefully doing it every time won't hurt... */
  512. for (i = 0; i < ARRAY_SIZE(regs_init); i++)
  513. ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value);
  514. ns->regs = regs_1024x768[mode_idx];
  515. for (i = 0; i < 84; i++)
  516. ns2501_writeb(dvo, ns->regs[i].offset, ns->regs[i].value);
  517. }
  518. /* set the NS2501 power state */
  519. static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
  520. {
  521. unsigned char ch;
  522. if (!ns2501_readb(dvo, NS2501_REG8, &ch))
  523. return false;
  524. return ch & NS2501_8_PD;
  525. }
  526. /* set the NS2501 power state */
  527. static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
  528. {
  529. struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
  530. DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
  531. if (enable) {
  532. if (WARN_ON(ns->regs[83].offset != 0x08 ||
  533. ns->regs[84].offset != 0x41 ||
  534. ns->regs[85].offset != 0xc0))
  535. return;
  536. ns2501_writeb(dvo, 0xc0, ns->regs[85].value | 0x08);
  537. ns2501_writeb(dvo, 0x41, ns->regs[84].value);
  538. ns2501_writeb(dvo, 0x34, 0x01);
  539. msleep(15);
  540. ns2501_writeb(dvo, 0x08, 0x35);
  541. if (!(ns->regs[83].value & NS2501_8_BPAS))
  542. ns2501_writeb(dvo, 0x08, 0x31);
  543. msleep(200);
  544. ns2501_writeb(dvo, 0x34, 0x03);
  545. ns2501_writeb(dvo, 0xc0, ns->regs[85].value);
  546. } else {
  547. ns2501_writeb(dvo, 0x34, 0x01);
  548. msleep(200);
  549. ns2501_writeb(dvo, 0x08, 0x34);
  550. msleep(15);
  551. ns2501_writeb(dvo, 0x34, 0x00);
  552. }
  553. }
  554. static void ns2501_destroy(struct intel_dvo_device *dvo)
  555. {
  556. struct ns2501_priv *ns = dvo->dev_priv;
  557. if (ns) {
  558. kfree(ns);
  559. dvo->dev_priv = NULL;
  560. }
  561. }
  562. struct intel_dvo_dev_ops ns2501_ops = {
  563. .init = ns2501_init,
  564. .detect = ns2501_detect,
  565. .mode_valid = ns2501_mode_valid,
  566. .mode_set = ns2501_mode_set,
  567. .dpms = ns2501_dpms,
  568. .get_hw_state = ns2501_get_hw_state,
  569. .destroy = ns2501_destroy,
  570. };