Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: MIT */
0002 /*
0003  * drm_panel_orientation_quirks.c -- Quirks for non-normal panel orientation
0004  *
0005  * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
0006  *
0007  * Note the quirks in this file are shared with fbdev/efifb and as such
0008  * must not depend on other drm code.
0009  */
0010 
0011 #include <linux/dmi.h>
0012 #include <linux/module.h>
0013 #include <drm/drm_connector.h>
0014 #include <drm/drm_utils.h>
0015 
0016 #ifdef CONFIG_DMI
0017 
0018 /*
0019  * Some x86 clamshell design devices use portrait tablet screens and a display
0020  * engine which cannot rotate in hardware, so we need to rotate the fbcon to
0021  * compensate. Unfortunately these (cheap) devices also typically have quite
0022  * generic DMI data, so we match on a combination of DMI data, screen resolution
0023  * and a list of known BIOS dates to avoid false positives.
0024  */
0025 
0026 struct drm_dmi_panel_orientation_data {
0027     int width;
0028     int height;
0029     const char * const *bios_dates;
0030     int orientation;
0031 };
0032 
0033 static const struct drm_dmi_panel_orientation_data asus_t100ha = {
0034     .width = 800,
0035     .height = 1280,
0036     .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
0037 };
0038 
0039 static const struct drm_dmi_panel_orientation_data gpd_micropc = {
0040     .width = 720,
0041     .height = 1280,
0042     .bios_dates = (const char * const []){ "04/26/2019",
0043         NULL },
0044     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0045 };
0046 
0047 static const struct drm_dmi_panel_orientation_data gpd_pocket = {
0048     .width = 1200,
0049     .height = 1920,
0050     .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
0051         "07/05/2017", "08/07/2017", NULL },
0052     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0053 };
0054 
0055 static const struct drm_dmi_panel_orientation_data gpd_pocket2 = {
0056     .width = 1200,
0057     .height = 1920,
0058     .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018",
0059         "12/07/2018", NULL },
0060     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0061 };
0062 
0063 static const struct drm_dmi_panel_orientation_data gpd_win = {
0064     .width = 720,
0065     .height = 1280,
0066     .bios_dates = (const char * const []){
0067         "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
0068         "02/21/2017", "03/20/2017", "05/25/2017", NULL },
0069     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0070 };
0071 
0072 static const struct drm_dmi_panel_orientation_data gpd_win2 = {
0073     .width = 720,
0074     .height = 1280,
0075     .bios_dates = (const char * const []){
0076         "12/07/2017", "05/24/2018", "06/29/2018", NULL },
0077     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0078 };
0079 
0080 static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
0081     .width = 800,
0082     .height = 1280,
0083     .bios_dates = (const char * const []){ "10/16/2015", NULL },
0084     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0085 };
0086 
0087 static const struct drm_dmi_panel_orientation_data onegx1_pro = {
0088     .width = 1200,
0089     .height = 1920,
0090     .bios_dates = (const char * const []){ "12/17/2020", NULL },
0091     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0092 };
0093 
0094 static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = {
0095     .width = 720,
0096     .height = 1280,
0097     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0098 };
0099 
0100 static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
0101     .width = 800,
0102     .height = 1280,
0103     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0104 };
0105 
0106 static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = {
0107     .width = 1200,
0108     .height = 1920,
0109     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0110 };
0111 
0112 static const struct drm_dmi_panel_orientation_data lcd1280x1920_rightside_up = {
0113     .width = 1280,
0114     .height = 1920,
0115     .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
0116 };
0117 
0118 static const struct drm_dmi_panel_orientation_data lcd1600x2560_leftside_up = {
0119     .width = 1600,
0120     .height = 2560,
0121     .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
0122 };
0123 
0124 static const struct dmi_system_id orientation_data[] = {
0125     {   /* Acer One 10 (S1003) */
0126         .matches = {
0127           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
0128           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
0129         },
0130         .driver_data = (void *)&lcd800x1280_rightside_up,
0131     }, {    /* Asus T100HA */
0132         .matches = {
0133           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
0134           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
0135         },
0136         .driver_data = (void *)&asus_t100ha,
0137     }, {    /* Asus T101HA */
0138         .matches = {
0139           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
0140           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T101HA"),
0141         },
0142         .driver_data = (void *)&lcd800x1280_rightside_up,
0143     }, {    /* Asus T103HAF */
0144         .matches = {
0145           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
0146           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"),
0147         },
0148         .driver_data = (void *)&lcd800x1280_rightside_up,
0149     }, {    /* AYA NEO 2021 */
0150         .matches = {
0151           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"),
0152           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYA NEO 2021"),
0153         },
0154         .driver_data = (void *)&lcd800x1280_rightside_up,
0155     }, {    /* AYA NEO NEXT */
0156         .matches = {
0157           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
0158           DMI_MATCH(DMI_BOARD_NAME, "NEXT"),
0159         },
0160         .driver_data = (void *)&lcd800x1280_rightside_up,
0161     }, {    /* Chuwi HiBook (CWI514) */
0162         .matches = {
0163             DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
0164             DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
0165             /* Above matches are too generic, add bios-date match */
0166             DMI_MATCH(DMI_BIOS_DATE, "05/07/2016"),
0167         },
0168         .driver_data = (void *)&lcd1200x1920_rightside_up,
0169     }, {    /* Chuwi Hi10 Pro (CWI529) */
0170         .matches = {
0171           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
0172           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"),
0173         },
0174         .driver_data = (void *)&lcd1200x1920_rightside_up,
0175     }, {    /* GPD MicroPC (generic strings, also match on bios date) */
0176         .matches = {
0177           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
0178           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
0179           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
0180           DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
0181         },
0182         .driver_data = (void *)&gpd_micropc,
0183     }, {    /* GPD MicroPC (later BIOS versions with proper DMI strings) */
0184         .matches = {
0185           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
0186           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MicroPC"),
0187         },
0188         .driver_data = (void *)&lcd720x1280_rightside_up,
0189     }, {    /* GPD Win Max */
0190         .matches = {
0191           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
0192           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1619-01"),
0193         },
0194         .driver_data = (void *)&lcd800x1280_rightside_up,
0195     }, {    /*
0196          * GPD Pocket, note that the DMI data is less generic then
0197          * it seems, devices with a board-vendor of "AMI Corporation"
0198          * are quite rare, as are devices which have both board- *and*
0199          * product-id set to "Default String"
0200          */
0201         .matches = {
0202           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
0203           DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
0204           DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
0205           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
0206         },
0207         .driver_data = (void *)&gpd_pocket,
0208     }, {    /* GPD Pocket 2 (generic strings, also match on bios date) */
0209         .matches = {
0210           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
0211           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
0212           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
0213           DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
0214         },
0215         .driver_data = (void *)&gpd_pocket2,
0216     }, {    /* GPD Win (same note on DMI match as GPD Pocket) */
0217         .matches = {
0218           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
0219           DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
0220           DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
0221           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
0222         },
0223         .driver_data = (void *)&gpd_win,
0224     }, {    /* GPD Win 2 (too generic strings, also match on bios date) */
0225         .matches = {
0226           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
0227           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
0228           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
0229           DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
0230         },
0231         .driver_data = (void *)&gpd_win2,
0232     }, {    /* GPD Win 3 */
0233         .matches = {
0234           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
0235           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1618-03")
0236         },
0237         .driver_data = (void *)&lcd720x1280_rightside_up,
0238     }, {    /* I.T.Works TW891 */
0239         .matches = {
0240           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
0241           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"),
0242           DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
0243           DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
0244         },
0245         .driver_data = (void *)&itworks_tw891,
0246     }, {    /* KD Kurio Smart C15200 2-in-1 */
0247         .matches = {
0248           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "KD Interactive"),
0249           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Kurio Smart"),
0250           DMI_EXACT_MATCH(DMI_BOARD_NAME, "KDM960BCP"),
0251         },
0252         .driver_data = (void *)&lcd800x1280_rightside_up,
0253     }, {    /*
0254          * Lenovo Ideapad Miix 310 laptop, only some production batches
0255          * have a portrait screen, the resolution checks makes the quirk
0256          * apply only to those batches.
0257          */
0258         .matches = {
0259           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
0260           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
0261           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
0262         },
0263         .driver_data = (void *)&lcd800x1280_rightside_up,
0264     }, {    /* Lenovo Ideapad Miix 320 */
0265         .matches = {
0266           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
0267           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
0268           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
0269         },
0270         .driver_data = (void *)&lcd800x1280_rightside_up,
0271     }, {    /* Lenovo Ideapad D330-10IGM (HD) */
0272         .matches = {
0273           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
0274           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
0275         },
0276         .driver_data = (void *)&lcd800x1280_rightside_up,
0277     }, {    /* Lenovo Ideapad D330-10IGM (FHD) */
0278         .matches = {
0279           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
0280           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
0281         },
0282         .driver_data = (void *)&lcd1200x1920_rightside_up,
0283     }, {    /* Lenovo Yoga Book X90F / X91F / X91L */
0284         .matches = {
0285           /* Non exact match to match all versions */
0286           DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"),
0287         },
0288         .driver_data = (void *)&lcd1200x1920_rightside_up,
0289     }, {    /* Lenovo Yoga Tablet 2 830F / 830L */
0290         .matches = {
0291          /*
0292           * Note this also matches the Lenovo Yoga Tablet 2 1050F/L
0293           * since that uses the same mainboard. The resolution match
0294           * will limit this to only matching on the 830F/L. Neither has
0295           * any external video outputs so those are not a concern.
0296           */
0297          DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
0298          DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
0299          DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
0300          /* Partial match on beginning of BIOS version */
0301          DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
0302         },
0303         .driver_data = (void *)&lcd1200x1920_rightside_up,
0304     }, {    /* OneGX1 Pro */
0305         .matches = {
0306           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SYSTEM_MANUFACTURER"),
0307           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SYSTEM_PRODUCT_NAME"),
0308           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"),
0309         },
0310         .driver_data = (void *)&onegx1_pro,
0311     }, {    /* OneXPlayer */
0312         .matches = {
0313           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK TECHNOLOGY CO., LTD."),
0314           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONE XPLAYER"),
0315         },
0316         .driver_data = (void *)&lcd1600x2560_leftside_up,
0317     }, {    /* Samsung GalaxyBook 10.6 */
0318         .matches = {
0319           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
0320           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galaxy Book 10.6"),
0321         },
0322         .driver_data = (void *)&lcd1280x1920_rightside_up,
0323     }, {    /* Valve Steam Deck */
0324         .matches = {
0325           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
0326           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
0327           DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
0328         },
0329         .driver_data = (void *)&lcd800x1280_rightside_up,
0330     }, {    /* VIOS LTH17 */
0331         .matches = {
0332           DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
0333           DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
0334         },
0335         .driver_data = (void *)&lcd800x1280_rightside_up,
0336     },
0337     {}
0338 };
0339 
0340 /**
0341  * drm_get_panel_orientation_quirk - Check for panel orientation quirks
0342  * @width: width in pixels of the panel
0343  * @height: height in pixels of the panel
0344  *
0345  * This function checks for platform specific (e.g. DMI based) quirks
0346  * providing info on panel_orientation for systems where this cannot be
0347  * probed from the hard-/firm-ware. To avoid false-positive this function
0348  * takes the panel resolution as argument and checks that against the
0349  * resolution expected by the quirk-table entry.
0350  *
0351  * Note this function is also used outside of the drm-subsys, by for example
0352  * the efifb code. Because of this this function gets compiled into its own
0353  * kernel-module when built as a module.
0354  *
0355  * Returns:
0356  * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
0357  * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
0358  */
0359 int drm_get_panel_orientation_quirk(int width, int height)
0360 {
0361     const struct dmi_system_id *match;
0362     const struct drm_dmi_panel_orientation_data *data;
0363     const char *bios_date;
0364     int i;
0365 
0366     for (match = dmi_first_match(orientation_data);
0367          match;
0368          match = dmi_first_match(match + 1)) {
0369         data = match->driver_data;
0370 
0371         if (data->width != width ||
0372             data->height != height)
0373             continue;
0374 
0375         if (!data->bios_dates)
0376             return data->orientation;
0377 
0378         bios_date = dmi_get_system_info(DMI_BIOS_DATE);
0379         if (!bios_date)
0380             continue;
0381 
0382         i = match_string(data->bios_dates, -1, bios_date);
0383         if (i >= 0)
0384             return data->orientation;
0385     }
0386 
0387     return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
0388 }
0389 EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
0390 
0391 #else
0392 
0393 /* There are no quirks for non x86 devices yet */
0394 int drm_get_panel_orientation_quirk(int width, int height)
0395 {
0396     return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
0397 }
0398 EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
0399 
0400 #endif
0401 
0402 MODULE_LICENSE("Dual MIT/GPL");