drm_panel_orientation_quirks.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. #ifdef CONFIG_DMI
  14. /*
  15. * Some x86 clamshell design devices use portrait tablet screens and a display
  16. * engine which cannot rotate in hardware, so we need to rotate the fbcon to
  17. * compensate. Unfortunately these (cheap) devices also typically have quite
  18. * generic DMI data, so we match on a combination of DMI data, screen resolution
  19. * and a list of known BIOS dates to avoid false positives.
  20. */
  21. struct drm_dmi_panel_orientation_data {
  22. int width;
  23. int height;
  24. const char * const *bios_dates;
  25. int orientation;
  26. };
  27. static const struct drm_dmi_panel_orientation_data asus_t100ha = {
  28. .width = 800,
  29. .height = 1280,
  30. .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
  31. };
  32. static const struct drm_dmi_panel_orientation_data gpd_pocket = {
  33. .width = 1200,
  34. .height = 1920,
  35. .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
  36. "07/05/2017", "08/07/2017", NULL },
  37. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  38. };
  39. static const struct drm_dmi_panel_orientation_data gpd_win = {
  40. .width = 720,
  41. .height = 1280,
  42. .bios_dates = (const char * const []){
  43. "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
  44. "02/21/2017", "03/20/2017", "05/25/2017", NULL },
  45. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  46. };
  47. static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
  48. .width = 800,
  49. .height = 1280,
  50. .bios_dates = (const char * const []){ "10/16/2015", NULL },
  51. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  52. };
  53. static const struct drm_dmi_panel_orientation_data vios_lth17 = {
  54. .width = 800,
  55. .height = 1280,
  56. .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  57. };
  58. static const struct dmi_system_id orientation_data[] = {
  59. { /* Asus T100HA */
  60. .matches = {
  61. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
  62. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
  63. },
  64. .driver_data = (void *)&asus_t100ha,
  65. }, { /*
  66. * GPD Pocket, note that the the DMI data is less generic then
  67. * it seems, devices with a board-vendor of "AMI Corporation"
  68. * are quite rare, as are devices which have both board- *and*
  69. * product-id set to "Default String"
  70. */
  71. .matches = {
  72. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  73. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  74. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  75. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  76. },
  77. .driver_data = (void *)&gpd_pocket,
  78. }, { /* GPD Win (same note on DMI match as GPD Pocket) */
  79. .matches = {
  80. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
  81. DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
  82. DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
  83. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
  84. },
  85. .driver_data = (void *)&gpd_win,
  86. }, { /* I.T.Works TW891 */
  87. .matches = {
  88. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
  89. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"),
  90. DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
  91. DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
  92. },
  93. .driver_data = (void *)&itworks_tw891,
  94. }, { /* VIOS LTH17 */
  95. .matches = {
  96. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
  97. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
  98. },
  99. .driver_data = (void *)&vios_lth17,
  100. },
  101. {}
  102. };
  103. /**
  104. * drm_get_panel_orientation_quirk - Check for panel orientation quirks
  105. * @width: width in pixels of the panel
  106. * @height: height in pixels of the panel
  107. *
  108. * This function checks for platform specific (e.g. DMI based) quirks
  109. * providing info on panel_orientation for systems where this cannot be
  110. * probed from the hard-/firm-ware. To avoid false-positive this function
  111. * takes the panel resolution as argument and checks that against the
  112. * resolution expected by the quirk-table entry.
  113. *
  114. * Note this function is also used outside of the drm-subsys, by for example
  115. * the efifb code. Because of this this function gets compiled into its own
  116. * kernel-module when built as a module.
  117. *
  118. * Returns:
  119. * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
  120. * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
  121. */
  122. int drm_get_panel_orientation_quirk(int width, int height)
  123. {
  124. const struct dmi_system_id *match;
  125. const struct drm_dmi_panel_orientation_data *data;
  126. const char *bios_date;
  127. int i;
  128. for (match = dmi_first_match(orientation_data);
  129. match;
  130. match = dmi_first_match(match + 1)) {
  131. data = match->driver_data;
  132. if (data->width != width ||
  133. data->height != height)
  134. continue;
  135. if (!data->bios_dates)
  136. return data->orientation;
  137. bios_date = dmi_get_system_info(DMI_BIOS_DATE);
  138. if (!bios_date)
  139. continue;
  140. for (i = 0; data->bios_dates[i]; i++) {
  141. if (!strcmp(data->bios_dates[i], bios_date))
  142. return data->orientation;
  143. }
  144. }
  145. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  146. }
  147. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  148. #else
  149. /* There are no quirks for non x86 devices yet */
  150. int drm_get_panel_orientation_quirk(int width, int height)
  151. {
  152. return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
  153. }
  154. EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
  155. #endif
  156. MODULE_LICENSE("Dual MIT/GPL");