drm_panel_orientation_quirks.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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 acer_s1003 = {
  29. .width = 800,
  30. .height = 1280,
  31. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  32. };
  33. static const struct drm_dmi_panel_orientation_data asus_t100ha = {
  34. .width = 800,
  35. .height = 1280,
  36. .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
  37. };
  38. static const struct drm_dmi_panel_orientation_data gpd_micropc = {
  39. .width = 720,
  40. .height = 1280,
  41. .bios_dates = (const char * const []){ "04/26/2019",
  42. NULL },
  43. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  44. };
  45. static const struct drm_dmi_panel_orientation_data gpd_pocket = {
  46. .width = 1200,
  47. .height = 1920,
  48. .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
  49. "07/05/2017", "08/07/2017", NULL },
  50. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  51. };
  52. static const struct drm_dmi_panel_orientation_data gpd_pocket2 = {
  53. .width = 1200,
  54. .height = 1920,
  55. .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018",
  56. "12/07/2018", NULL },
  57. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  58. };
  59. static const struct drm_dmi_panel_orientation_data gpd_win = {
  60. .width = 720,
  61. .height = 1280,
  62. .bios_dates = (const char * const []){
  63. "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
  64. "02/21/2017", "03/20/2017", "05/25/2017", NULL },
  65. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  66. };
  67. static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
  68. .width = 800,
  69. .height = 1280,
  70. .bios_dates = (const char * const []){ "10/16/2015", NULL },
  71. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  72. };
  73. static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = {
  74. .width = 720,
  75. .height = 1280,
  76. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  77. };
  78. static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
  79. .width = 800,
  80. .height = 1280,
  81. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  82. };
  83. static const struct dmi_system_id orientation_data[] = {
  84. { /* Acer One 10 (S1003) */
  85. .matches = {
  86. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
  87. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
  88. },
  89. .driver_data = (void *)&acer_s1003,
  90. }, { /* Asus T100HA */
  91. .matches = {
  92. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
  93. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
  94. },
  95. .driver_data = (void *)&asus_t100ha,
  96. }, { /* GPD MicroPC (generic strings, also match on bios date) */
  97. .matches = {
  98. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
  99. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  100. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
  101. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  102. },
  103. .driver_data = (void *)&gpd_micropc,
  104. }, { /* GPD MicroPC (later BIOS versions with proper DMI strings) */
  105. .matches = {
  106. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
  107. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MicroPC"),
  108. },
  109. .driver_data = (void *)&lcd720x1280_rightside_up,
  110. }, { /*
  111. * GPD Pocket, note that the the DMI data is less generic then
  112. * it seems, devices with a board-vendor of "AMI Corporation"
  113. * are quite rare, as are devices which have both board- *and*
  114. * product-id set to "Default String"
  115. */
  116. .matches = {
  117. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  118. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  119. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  120. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  121. },
  122. .driver_data = (void *)&gpd_pocket,
  123. }, { /* GPD Pocket 2 (generic strings, also match on bios date) */
  124. .matches = {
  125. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
  126. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  127. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
  128. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  129. },
  130. .driver_data = (void *)&gpd_pocket2,
  131. }, { /* GPD Win (same note on DMI match as GPD Pocket) */
  132. .matches = {
  133. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  134. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  135. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  136. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  137. },
  138. .driver_data = (void *)&gpd_win,
  139. }, { /* I.T.Works TW891 */
  140. .matches = {
  141. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
  142. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"),
  143. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
  144. DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
  145. },
  146. .driver_data = (void *)&itworks_tw891,
  147. }, { /*
  148. * Lenovo Ideapad Miix 310 laptop, only some production batches
  149. * have a portrait screen, the resolution checks makes the quirk
  150. * apply only to those batches.
  151. */
  152. .matches = {
  153. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  154. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
  155. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
  156. },
  157. .driver_data = (void *)&lcd800x1280_rightside_up,
  158. }, { /* Lenovo Ideapad Miix 320 */
  159. .matches = {
  160. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
  161. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
  162. DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
  163. },
  164. .driver_data = (void *)&lcd800x1280_rightside_up,
  165. }, { /* VIOS LTH17 */
  166. .matches = {
  167. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
  168. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
  169. },
  170. .driver_data = (void *)&lcd800x1280_rightside_up,
  171. },
  172. {}
  173. };
  174. /**
  175. * drm_get_panel_orientation_quirk - Check for panel orientation quirks
  176. * @width: width in pixels of the panel
  177. * @height: height in pixels of the panel
  178. *
  179. * This function checks for platform specific (e.g. DMI based) quirks
  180. * providing info on panel_orientation for systems where this cannot be
  181. * probed from the hard-/firm-ware. To avoid false-positive this function
  182. * takes the panel resolution as argument and checks that against the
  183. * resolution expected by the quirk-table entry.
  184. *
  185. * Note this function is also used outside of the drm-subsys, by for example
  186. * the efifb code. Because of this this function gets compiled into its own
  187. * kernel-module when built as a module.
  188. *
  189. * Returns:
  190. * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
  191. * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
  192. */
  193. int drm_get_panel_orientation_quirk(int width, int height)
  194. {
  195. const struct dmi_system_id *match;
  196. const struct drm_dmi_panel_orientation_data *data;
  197. const char *bios_date;
  198. int i;
  199. for (match = dmi_first_match(orientation_data);
  200. match;
  201. match = dmi_first_match(match + 1)) {
  202. data = match->driver_data;
  203. if (data->width != width ||
  204. data->height != height)
  205. continue;
  206. if (!data->bios_dates)
  207. return data->orientation;
  208. bios_date = dmi_get_system_info(DMI_BIOS_DATE);
  209. if (!bios_date)
  210. continue;
  211. i = match_string(data->bios_dates, -1, bios_date);
  212. if (i >= 0)
  213. return data->orientation;
  214. }
  215. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  216. }
  217. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  218. #else
  219. /* There are no quirks for non x86 devices yet */
  220. int drm_get_panel_orientation_quirk(int width, int height)
  221. {
  222. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  223. }
  224. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  225. #endif
  226. MODULE_LICENSE("Dual MIT/GPL");