drm_panel_orientation_quirks.c revision 1.26
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 11#include <linux/dmi.h> 12#include <linux/module.h> 13#include <drm/drm_connector.h> 14#include <drm/drm_utils.h> 15 16#ifdef CONFIG_DMI 17 18/* 19 * Some x86 clamshell design devices use portrait tablet screens and a display 20 * engine which cannot rotate in hardware, so we need to rotate the fbcon to 21 * compensate. Unfortunately these (cheap) devices also typically have quite 22 * generic DMI data, so we match on a combination of DMI data, screen resolution 23 * and a list of known BIOS dates to avoid false positives. 24 */ 25 26struct drm_dmi_panel_orientation_data { 27 int width; 28 int height; 29 const char * const *bios_dates; 30 int orientation; 31}; 32 33static 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 41static const struct drm_dmi_panel_orientation_data gpd_pocket = { 42 .width = 1200, 43 .height = 1920, 44 .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017", 45 "07/05/2017", "08/07/2017", NULL }, 46 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 47}; 48 49static const struct drm_dmi_panel_orientation_data gpd_pocket2 = { 50 .width = 1200, 51 .height = 1920, 52 .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018", 53 "12/07/2018", NULL }, 54 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 55}; 56 57static const struct drm_dmi_panel_orientation_data gpd_win = { 58 .width = 720, 59 .height = 1280, 60 .bios_dates = (const char * const []){ 61 "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016", 62 "02/21/2017", "03/20/2017", "05/25/2017", NULL }, 63 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 64}; 65 66static const struct drm_dmi_panel_orientation_data gpd_win2 = { 67 .width = 720, 68 .height = 1280, 69 .bios_dates = (const char * const []){ 70 "12/07/2017", "05/24/2018", "06/29/2018", NULL }, 71 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 72}; 73 74static const struct drm_dmi_panel_orientation_data itworks_tw891 = { 75 .width = 800, 76 .height = 1280, 77 .bios_dates = (const char * const []){ "10/16/2015", NULL }, 78 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 79}; 80 81static const struct drm_dmi_panel_orientation_data onegx1_pro = { 82 .width = 1200, 83 .height = 1920, 84 .bios_dates = (const char * const []){ "12/17/2020", NULL }, 85 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 86}; 87 88static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = { 89 .width = 720, 90 .height = 1280, 91 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 92}; 93 94static const struct drm_dmi_panel_orientation_data lcd800x1280_leftside_up = { 95 .width = 800, 96 .height = 1280, 97 .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, 98}; 99 100static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = { 101 .width = 800, 102 .height = 1280, 103 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 104}; 105 106static const struct drm_dmi_panel_orientation_data lcd1080x1920_leftside_up = { 107 .width = 1080, 108 .height = 1920, 109 .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, 110}; 111 112static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = { 113 .width = 1200, 114 .height = 1920, 115 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 116}; 117 118static const struct drm_dmi_panel_orientation_data lcd1280x1920_rightside_up = { 119 .width = 1280, 120 .height = 1920, 121 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 122}; 123 124static const struct drm_dmi_panel_orientation_data lcd1600x2560_leftside_up = { 125 .width = 1600, 126 .height = 2560, 127 .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, 128}; 129 130static const struct drm_dmi_panel_orientation_data lcd1600x2560_rightside_up = { 131 .width = 1600, 132 .height = 2560, 133 .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, 134}; 135 136static const struct dmi_system_id orientation_data[] = { 137 { /* Acer One 10 (S1003) */ 138 .matches = { 139 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), 140 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"), 141 }, 142 .driver_data = (void *)&lcd800x1280_rightside_up, 143 }, { /* Acer Switch V 10 (SW5-017) */ 144 .matches = { 145 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), 146 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"), 147 }, 148 .driver_data = (void *)&lcd800x1280_rightside_up, 149 }, { /* Anbernic Win600 */ 150 .matches = { 151 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Anbernic"), 152 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Win600"), 153 }, 154 .driver_data = (void *)&lcd720x1280_rightside_up, 155 }, { /* Asus T100HA */ 156 .matches = { 157 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 158 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), 159 }, 160 .driver_data = (void *)&lcd800x1280_leftside_up, 161 }, { /* Asus T101HA */ 162 .matches = { 163 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 164 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T101HA"), 165 }, 166 .driver_data = (void *)&lcd800x1280_rightside_up, 167 }, { /* Asus T103HAF */ 168 .matches = { 169 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 170 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"), 171 }, 172 .driver_data = (void *)&lcd800x1280_rightside_up, 173 }, { /* AYA NEO 2021 */ 174 .matches = { 175 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"), 176 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYA NEO 2021"), 177 }, 178 .driver_data = (void *)&lcd800x1280_rightside_up, 179 }, { /* AYA NEO AIR */ 180 .matches = { 181 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), 182 DMI_MATCH(DMI_BOARD_NAME, "AIR"), 183 }, 184 .driver_data = (void *)&lcd1080x1920_leftside_up, 185 }, { /* AYA NEO NEXT */ 186 .matches = { 187 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 188 DMI_MATCH(DMI_BOARD_NAME, "NEXT"), 189 }, 190 .driver_data = (void *)&lcd800x1280_rightside_up, 191 }, { /* Chuwi HiBook (CWI514) */ 192 .matches = { 193 DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), 194 DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), 195 /* Above matches are too generic, add bios-date match */ 196 DMI_MATCH(DMI_BIOS_DATE, "05/07/2016"), 197 }, 198 .driver_data = (void *)&lcd1200x1920_rightside_up, 199 }, { /* Chuwi Hi10 Pro (CWI529) */ 200 .matches = { 201 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Hampoo"), 202 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"), 203 }, 204 .driver_data = (void *)&lcd1200x1920_rightside_up, 205 }, { /* Dynabook K50 */ 206 .matches = { 207 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."), 208 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"), 209 }, 210 .driver_data = (void *)&lcd800x1280_leftside_up, 211 }, { /* GPD MicroPC (generic strings, also match on bios date) */ 212 .matches = { 213 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), 214 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 215 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), 216 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 217 }, 218 .driver_data = (void *)&gpd_micropc, 219 }, { /* GPD MicroPC (later BIOS versions with proper DMI strings) */ 220 .matches = { 221 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"), 222 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MicroPC"), 223 }, 224 .driver_data = (void *)&lcd720x1280_rightside_up, 225 }, { /* GPD Win Max */ 226 .matches = { 227 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"), 228 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1619-01"), 229 }, 230 .driver_data = (void *)&lcd800x1280_rightside_up, 231 }, { /* 232 * GPD Pocket, note that the DMI data is less generic then 233 * it seems, devices with a board-vendor of "AMI Corporation" 234 * are quite rare, as are devices which have both board- *and* 235 * product-id set to "Default String" 236 */ 237 .matches = { 238 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 239 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 240 DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), 241 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 242 }, 243 .driver_data = (void *)&gpd_pocket, 244 }, { /* GPD Pocket 2 (generic strings, also match on bios date) */ 245 .matches = { 246 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), 247 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 248 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), 249 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 250 }, 251 .driver_data = (void *)&gpd_pocket2, 252 }, { /* GPD Win (same note on DMI match as GPD Pocket) */ 253 .matches = { 254 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 255 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 256 DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), 257 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 258 }, 259 .driver_data = (void *)&gpd_win, 260 }, { /* GPD Win 2 (too generic strings, also match on bios date) */ 261 .matches = { 262 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), 263 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 264 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), 265 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 266 }, 267 .driver_data = (void *)&gpd_win2, 268 }, { /* GPD Win 3 */ 269 .matches = { 270 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"), 271 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1618-03") 272 }, 273 .driver_data = (void *)&lcd720x1280_rightside_up, 274 }, { /* I.T.Works TW891 */ 275 .matches = { 276 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), 277 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"), 278 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), 279 DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), 280 }, 281 .driver_data = (void *)&itworks_tw891, 282 }, { /* Kogan Atlas 2-in-1 D300 */ 283 .matches = { 284 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Kogan"), 285 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KALAP10D300EA"), 286 }, 287 .driver_data = (void *)&lcd800x1280_rightside_up, 288 }, { /* KD Kurio Smart C15200 2-in-1 */ 289 .matches = { 290 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "KD Interactive"), 291 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Kurio Smart"), 292 DMI_EXACT_MATCH(DMI_BOARD_NAME, "KDM960BCP"), 293 }, 294 .driver_data = (void *)&lcd800x1280_rightside_up, 295 }, { /* 296 * Lenovo Ideapad Miix 310 laptop, only some production batches 297 * have a portrait screen, the resolution checks makes the quirk 298 * apply only to those batches. 299 */ 300 .matches = { 301 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 302 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"), 303 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"), 304 }, 305 .driver_data = (void *)&lcd800x1280_rightside_up, 306 }, { /* Lenovo Ideapad Miix 320 */ 307 .matches = { 308 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 309 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"), 310 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), 311 }, 312 .driver_data = (void *)&lcd800x1280_rightside_up, 313 }, { /* Lenovo Ideapad D330-10IGM (HD) */ 314 .matches = { 315 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 316 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), 317 }, 318 .driver_data = (void *)&lcd800x1280_rightside_up, 319 }, { /* Lenovo Ideapad D330-10IGM (FHD) */ 320 .matches = { 321 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 322 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), 323 }, 324 .driver_data = (void *)&lcd1200x1920_rightside_up, 325 }, { /* Lenovo Ideapad D330-10IGL (HD) */ 326 .matches = { 327 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 328 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGL"), 329 }, 330 .driver_data = (void *)&lcd800x1280_rightside_up, 331 }, { /* Lenovo IdeaPad Duet 3 10IGL5 */ 332 .matches = { 333 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), 334 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "IdeaPad Duet 3 10IGL5"), 335 }, 336 .driver_data = (void *)&lcd1200x1920_rightside_up, 337 }, { /* Lenovo Yoga Book X90F / X90L */ 338 .matches = { 339 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 340 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 341 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), 342 }, 343 .driver_data = (void *)&lcd1200x1920_rightside_up, 344 }, { /* Lenovo Yoga Book X91F / X91L */ 345 .matches = { 346 /* Non exact match to match F + L versions */ 347 DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), 348 }, 349 .driver_data = (void *)&lcd1200x1920_rightside_up, 350 }, { /* Lenovo Yoga Tablet 2 830F / 830L */ 351 .matches = { 352 /* 353 * Note this also matches the Lenovo Yoga Tablet 2 1050F/L 354 * since that uses the same mainboard. The resolution match 355 * will limit this to only matching on the 830F/L. Neither has 356 * any external video outputs so those are not a concern. 357 */ 358 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), 359 DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), 360 DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), 361 /* Partial match on beginning of BIOS version */ 362 DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), 363 }, 364 .driver_data = (void *)&lcd1200x1920_rightside_up, 365 }, { /* Lenovo Yoga Tab 3 X90F */ 366 .matches = { 367 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 368 DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 369 DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), 370 }, 371 .driver_data = (void *)&lcd1600x2560_rightside_up, 372 }, { /* Nanote UMPC-01 */ 373 .matches = { 374 DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"), 375 DMI_MATCH(DMI_PRODUCT_NAME, "UMPC-01"), 376 }, 377 .driver_data = (void *)&lcd1200x1920_rightside_up, 378 }, { /* OneGX1 Pro */ 379 .matches = { 380 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SYSTEM_MANUFACTURER"), 381 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SYSTEM_PRODUCT_NAME"), 382 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"), 383 }, 384 .driver_data = (void *)&onegx1_pro, 385 }, { /* OneXPlayer */ 386 .matches = { 387 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK TECHNOLOGY CO., LTD."), 388 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONE XPLAYER"), 389 }, 390 .driver_data = (void *)&lcd1600x2560_leftside_up, 391 }, { /* Samsung GalaxyBook 10.6 */ 392 .matches = { 393 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 394 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galaxy Book 10.6"), 395 }, 396 .driver_data = (void *)&lcd1280x1920_rightside_up, 397 }, { /* Valve Steam Deck */ 398 .matches = { 399 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"), 400 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"), 401 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"), 402 }, 403 .driver_data = (void *)&lcd800x1280_rightside_up, 404 }, { /* VIOS LTH17 */ 405 .matches = { 406 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), 407 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"), 408 }, 409 .driver_data = (void *)&lcd800x1280_rightside_up, 410 }, 411 {} 412}; 413 414/** 415 * drm_get_panel_orientation_quirk - Check for panel orientation quirks 416 * @width: width in pixels of the panel 417 * @height: height in pixels of the panel 418 * 419 * This function checks for platform specific (e.g. DMI based) quirks 420 * providing info on panel_orientation for systems where this cannot be 421 * probed from the hard-/firm-ware. To avoid false-positive this function 422 * takes the panel resolution as argument and checks that against the 423 * resolution expected by the quirk-table entry. 424 * 425 * Note this function is also used outside of the drm-subsys, by for example 426 * the efifb code. Because of this this function gets compiled into its own 427 * kernel-module when built as a module. 428 * 429 * Returns: 430 * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system, 431 * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk. 432 */ 433int drm_get_panel_orientation_quirk(int width, int height) 434{ 435 const struct dmi_system_id *match; 436 const struct drm_dmi_panel_orientation_data *data; 437 const char *bios_date; 438 int i; 439 440 for (match = dmi_first_match(orientation_data); 441 match; 442 match = dmi_first_match(match + 1)) { 443 data = match->driver_data; 444 445 if (data->width != width || 446 data->height != height) 447 continue; 448 449 if (!data->bios_dates) 450 return data->orientation; 451 452 bios_date = dmi_get_system_info(DMI_BIOS_DATE); 453 if (!bios_date) 454 continue; 455 456 i = match_string(data->bios_dates, -1, bios_date); 457 if (i >= 0) 458 return data->orientation; 459 } 460 461 return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 462} 463EXPORT_SYMBOL(drm_get_panel_orientation_quirk); 464 465#else 466 467/* There are no quirks for non x86 devices yet */ 468int drm_get_panel_orientation_quirk(int width, int height) 469{ 470 return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 471} 472EXPORT_SYMBOL(drm_get_panel_orientation_quirk); 473 474#endif 475 476MODULE_LICENSE("Dual MIT/GPL"); 477