drm_panel_orientation_quirks.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /* SPDX-License-Identifier: MIT */
  2. /*
  3. * drm_panel_orientation_quirks.c -- Quirks for non-normal panel orientation
  4. *
  5. * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
  6. *
  7. * Note the quirks in this file are shared with fbdev/efifb and as such
  8. * must not depend on other drm code.
  9. */
  10. #include <linux/dmi.h>
  11. #include <linux/module.h>
  12. #include <drm/drm_connector.h>
  13. #include <drm/drm_utils.h>
  14. #ifdef CONFIG_DMI
  15. /*
  16. * Some x86 clamshell design devices use portrait tablet screens and a display
  17. * engine which cannot rotate in hardware, so we need to rotate the fbcon to
  18. * compensate. Unfortunately these (cheap) devices also typically have quite
  19. * generic DMI data, so we match on a combination of DMI data, screen resolution
  20. * and a list of known BIOS dates to avoid false positives.
  21. */
  22. struct drm_dmi_panel_orientation_data {
  23. int width;
  24. int height;
  25. const char * const *bios_dates;
  26. int orientation;
  27. };
  28. static const struct drm_dmi_panel_orientation_data asus_t100ha = {
  29. .width = 800,
  30. .height = 1280,
  31. .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
  32. };
  33. static const struct drm_dmi_panel_orientation_data gpd_micropc = {
  34. .width = 720,
  35. .height = 1280,
  36. .bios_dates = (const char * const []){ "04/26/2019",
  37. NULL },
  38. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  39. };
  40. static const struct drm_dmi_panel_orientation_data gpd_pocket = {
  41. .width = 1200,
  42. .height = 1920,
  43. .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
  44. "07/05/2017", "08/07/2017", NULL },
  45. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  46. };
  47. static const struct drm_dmi_panel_orientation_data gpd_pocket2 = {
  48. .width = 1200,
  49. .height = 1920,
  50. .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018",
  51. "12/07/2018", NULL },
  52. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  53. };
  54. static const struct drm_dmi_panel_orientation_data gpd_win = {
  55. .width = 720,
  56. .height = 1280,
  57. .bios_dates = (const char * const []){
  58. "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
  59. "02/21/2017", "03/20/2017", "05/25/2017", NULL },
  60. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  61. };
  62. static const struct drm_dmi_panel_orientation_data gpd_win2 = {
  63. .width = 720,
  64. .height = 1280,
  65. .bios_dates = (const char * const []){
  66. "12/07/2017", "05/24/2018", "06/29/2018", NULL },
  67. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  68. };
  69. static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
  70. .width = 800,
  71. .height = 1280,
  72. .bios_dates = (const char * const []){ "10/16/2015", NULL },
  73. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  74. };
  75. static const struct drm_dmi_panel_orientation_data onegx1_pro = {
  76. .width = 1200,
  77. .height = 1920,
  78. .bios_dates = (const char * const []){ "12/17/2020", NULL },
  79. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  80. };
  81. static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = {
  82. .width = 720,
  83. .height = 1280,
  84. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  85. };
  86. static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
  87. .width = 800,
  88. .height = 1280,
  89. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  90. };
  91. static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = {
  92. .width = 1200,
  93. .height = 1920,
  94. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  95. };
  96. static const struct drm_dmi_panel_orientation_data lcd1280x1920_rightside_up = {
  97. .width = 1280,
  98. .height = 1920,
  99. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  100. };
  101. static const struct drm_dmi_panel_orientation_data lcd1600x2560_leftside_up = {
  102. .width = 1600,
  103. .height = 2560,
  104. .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
  105. };
  106. static const struct dmi_system_id orientation_data[] = {
  107. { /* Acer One 10 (S1003) */
  108. .matches = {
  109. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
  110. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
  111. },
  112. .driver_data = (void *)&lcd800x1280_rightside_up,
  113. }, { /* Asus T100HA */
  114. .matches = {
  115. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
  116. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
  117. },
  118. .driver_data = (void *)&asus_t100ha,
  119. }, { /* Asus T101HA */
  120. .matches = {
  121. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
  122. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T101HA"),
  123. },
  124. .driver_data = (void *)&lcd800x1280_rightside_up,
  125. }, { /* Asus T103HAF */
  126. .matches = {
  127. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
  128. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"),
  129. },
  130. .driver_data = (void *)&lcd800x1280_rightside_up,
  131. }, { /* AYA NEO 2021 */
  132. .matches = {
  133. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"),
  134. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYA NEO 2021"),
  135. },
  136. .driver_data = (void *)&lcd800x1280_rightside_up,
  137. }, { /* GPD MicroPC (generic strings, also match on bios date) */
  138. .matches = {
  139. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
  140. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  141. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
  142. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  143. },
  144. .driver_data = (void *)&gpd_micropc,
  145. }, { /* GPD MicroPC (later BIOS versions with proper DMI strings) */
  146. .matches = {
  147. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
  148. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MicroPC"),
  149. },
  150. .driver_data = (void *)&lcd720x1280_rightside_up,
  151. }, { /* GPD Win Max */
  152. .matches = {
  153. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
  154. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1619-01"),
  155. },
  156. .driver_data = (void *)&lcd800x1280_rightside_up,
  157. }, { /*
  158. * GPD Pocket, note that the the DMI data is less generic then
  159. * it seems, devices with a board-vendor of "AMI Corporation"
  160. * are quite rare, as are devices which have both board- *and*
  161. * product-id set to "Default String"
  162. */
  163. .matches = {
  164. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  165. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  166. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  167. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  168. },
  169. .driver_data = (void *)&gpd_pocket,
  170. }, { /* GPD Pocket 2 (generic strings, also match on bios date) */
  171. .matches = {
  172. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
  173. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  174. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
  175. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  176. },
  177. .driver_data = (void *)&gpd_pocket2,
  178. }, { /* GPD Win (same note on DMI match as GPD Pocket) */
  179. .matches = {
  180. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  181. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  182. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  183. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  184. },
  185. .driver_data = (void *)&gpd_win,
  186. }, { /* GPD Win 2 (too generic strings, also match on bios date) */
  187. .matches = {
  188. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
  189. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  190. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
  191. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  192. },
  193. .driver_data = (void *)&gpd_win2,
  194. }, { /* GPD Win 3 */
  195. .matches = {
  196. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
  197. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1618-03")
  198. },
  199. .driver_data = (void *)&lcd720x1280_rightside_up,
  200. }, { /* I.T.Works TW891 */
  201. .matches = {
  202. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
  203. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"),
  204. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
  205. DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
  206. },
  207. .driver_data = (void *)&itworks_tw891,
  208. }, { /* KD Kurio Smart C15200 2-in-1 */
  209. .matches = {
  210. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "KD Interactive"),
  211. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Kurio Smart"),
  212. DMI_EXACT_MATCH(DMI_BOARD_NAME, "KDM960BCP"),
  213. },
  214. .driver_data = (void *)&lcd800x1280_rightside_up,
  215. }, { /*
  216. * Lenovo Ideapad Miix 310 laptop, only some production batches
  217. * have a portrait screen, the resolution checks makes the quirk
  218. * apply only to those batches.
  219. */
  220. .matches = {
  221. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  222. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
  223. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
  224. },
  225. .driver_data = (void *)&lcd800x1280_rightside_up,
  226. }, { /* Lenovo Ideapad Miix 320 */
  227. .matches = {
  228. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  229. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
  230. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
  231. },
  232. .driver_data = (void *)&lcd800x1280_rightside_up,
  233. }, { /* Lenovo Ideapad D330-10IGM (HD) */
  234. .matches = {
  235. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  236. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
  237. },
  238. .driver_data = (void *)&lcd800x1280_rightside_up,
  239. }, { /* Lenovo Ideapad D330-10IGM (FHD) */
  240. .matches = {
  241. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  242. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
  243. },
  244. .driver_data = (void *)&lcd1200x1920_rightside_up,
  245. }, { /* Lenovo Yoga Book X90F / X91F / X91L */
  246. .matches = {
  247. /* Non exact match to match all versions */
  248. DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"),
  249. },
  250. .driver_data = (void *)&lcd1200x1920_rightside_up,
  251. }, { /* OneGX1 Pro */
  252. .matches = {
  253. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SYSTEM_MANUFACTURER"),
  254. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SYSTEM_PRODUCT_NAME"),
  255. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"),
  256. },
  257. .driver_data = (void *)&onegx1_pro,
  258. }, { /* OneXPlayer */
  259. .matches = {
  260. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK TECHNOLOGY CO., LTD."),
  261. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONE XPLAYER"),
  262. },
  263. .driver_data = (void *)&lcd1600x2560_leftside_up,
  264. }, { /* Samsung GalaxyBook 10.6 */
  265. .matches = {
  266. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  267. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galaxy Book 10.6"),
  268. },
  269. .driver_data = (void *)&lcd1280x1920_rightside_up,
  270. }, { /* Valve Steam Deck */
  271. .matches = {
  272. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
  273. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
  274. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
  275. },
  276. .driver_data = (void *)&lcd800x1280_rightside_up,
  277. }, { /* VIOS LTH17 */
  278. .matches = {
  279. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
  280. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
  281. },
  282. .driver_data = (void *)&lcd800x1280_rightside_up,
  283. },
  284. {}
  285. };
  286. /**
  287. * drm_get_panel_orientation_quirk - Check for panel orientation quirks
  288. * @width: width in pixels of the panel
  289. * @height: height in pixels of the panel
  290. *
  291. * This function checks for platform specific (e.g. DMI based) quirks
  292. * providing info on panel_orientation for systems where this cannot be
  293. * probed from the hard-/firm-ware. To avoid false-positive this function
  294. * takes the panel resolution as argument and checks that against the
  295. * resolution expected by the quirk-table entry.
  296. *
  297. * Note this function is also used outside of the drm-subsys, by for example
  298. * the efifb code. Because of this this function gets compiled into its own
  299. * kernel-module when built as a module.
  300. *
  301. * Returns:
  302. * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
  303. * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
  304. */
  305. int drm_get_panel_orientation_quirk(int width, int height)
  306. {
  307. const struct dmi_system_id *match;
  308. const struct drm_dmi_panel_orientation_data *data;
  309. const char *bios_date;
  310. int i;
  311. for (match = dmi_first_match(orientation_data);
  312. match;
  313. match = dmi_first_match(match + 1)) {
  314. data = match->driver_data;
  315. if (data->width != width ||
  316. data->height != height)
  317. continue;
  318. if (!data->bios_dates)
  319. return data->orientation;
  320. bios_date = dmi_get_system_info(DMI_BIOS_DATE);
  321. if (!bios_date)
  322. continue;
  323. i = match_string(data->bios_dates, -1, bios_date);
  324. if (i >= 0)
  325. return data->orientation;
  326. }
  327. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  328. }
  329. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  330. #else
  331. /* There are no quirks for non x86 devices yet */
  332. int drm_get_panel_orientation_quirk(int width, int height)
  333. {
  334. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  335. }
  336. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  337. #endif
  338. MODULE_LICENSE("Dual MIT/GPL");