linux/drivers/gpu/drm/drm_panel_orientation_quirks.c
<<
>>
Prefs
   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 asus_t100ha = {
  34        .width = 800,
  35        .height = 1280,
  36        .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
  37};
  38
  39static const struct drm_dmi_panel_orientation_data gpd_micropc = {
  40        .width = 720,
  41        .height = 1280,
  42        .bios_dates = (const char * const []){ "04/26/2019",
  43                NULL },
  44        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  45};
  46
  47static const struct drm_dmi_panel_orientation_data gpd_pocket = {
  48        .width = 1200,
  49        .height = 1920,
  50        .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
  51                "07/05/2017", "08/07/2017", NULL },
  52        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  53};
  54
  55static const struct drm_dmi_panel_orientation_data gpd_pocket2 = {
  56        .width = 1200,
  57        .height = 1920,
  58        .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018",
  59                "12/07/2018", NULL },
  60        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  61};
  62
  63static const struct drm_dmi_panel_orientation_data gpd_win = {
  64        .width = 720,
  65        .height = 1280,
  66        .bios_dates = (const char * const []){
  67                "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
  68                "02/21/2017", "03/20/2017", "05/25/2017", NULL },
  69        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  70};
  71
  72static const struct drm_dmi_panel_orientation_data gpd_win2 = {
  73        .width = 720,
  74        .height = 1280,
  75        .bios_dates = (const char * const []){
  76                "12/07/2017", "05/24/2018", "06/29/2018", NULL },
  77        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  78};
  79
  80static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
  81        .width = 800,
  82        .height = 1280,
  83        .bios_dates = (const char * const []){ "10/16/2015", NULL },
  84        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  85};
  86
  87static const struct drm_dmi_panel_orientation_data onegx1_pro = {
  88        .width = 1200,
  89        .height = 1920,
  90        .bios_dates = (const char * const []){ "12/17/2020", NULL },
  91        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
  92};
  93
  94static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = {
  95        .width = 720,
  96        .height = 1280,
  97        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_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 lcd1200x1920_rightside_up = {
 107        .width = 1200,
 108        .height = 1920,
 109        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
 110};
 111
 112static const struct dmi_system_id orientation_data[] = {
 113        {       /* Acer One 10 (S1003) */
 114                .matches = {
 115                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
 116                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
 117                },
 118                .driver_data = (void *)&lcd800x1280_rightside_up,
 119        }, {    /* Asus T100HA */
 120                .matches = {
 121                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 122                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
 123                },
 124                .driver_data = (void *)&asus_t100ha,
 125        }, {    /* Asus T101HA */
 126                .matches = {
 127                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 128                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T101HA"),
 129                },
 130                .driver_data = (void *)&lcd800x1280_rightside_up,
 131        }, {    /* Asus T103HAF */
 132                .matches = {
 133                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 134                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"),
 135                },
 136                .driver_data = (void *)&lcd800x1280_rightside_up,
 137        }, {    /* AYA NEO 2021 */
 138                .matches = {
 139                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"),
 140                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYA NEO 2021"),
 141                },
 142                .driver_data = (void *)&lcd800x1280_rightside_up,
 143        }, {    /* GPD MicroPC (generic strings, also match on bios date) */
 144                .matches = {
 145                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
 146                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
 147                  DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
 148                  DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
 149                },
 150                .driver_data = (void *)&gpd_micropc,
 151        }, {    /* GPD MicroPC (later BIOS versions with proper DMI strings) */
 152                .matches = {
 153                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"),
 154                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MicroPC"),
 155                },
 156                .driver_data = (void *)&lcd720x1280_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        }, {    /*
 209                 * Lenovo Ideapad Miix 310 laptop, only some production batches
 210                 * have a portrait screen, the resolution checks makes the quirk
 211                 * apply only to those batches.
 212                 */
 213                .matches = {
 214                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 215                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
 216                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
 217                },
 218                .driver_data = (void *)&lcd800x1280_rightside_up,
 219        }, {    /* Lenovo Ideapad Miix 320 */
 220                .matches = {
 221                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 222                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
 223                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
 224                },
 225                .driver_data = (void *)&lcd800x1280_rightside_up,
 226        }, {    /* Lenovo Ideapad D330 */
 227                .matches = {
 228                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 229                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81H3"),
 230                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
 231                },
 232                .driver_data = (void *)&lcd1200x1920_rightside_up,
 233        }, {    /* OneGX1 Pro */
 234                .matches = {
 235                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SYSTEM_MANUFACTURER"),
 236                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SYSTEM_PRODUCT_NAME"),
 237                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"),
 238                },
 239                .driver_data = (void *)&onegx1_pro,
 240        }, {    /* VIOS LTH17 */
 241                .matches = {
 242                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
 243                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
 244                },
 245                .driver_data = (void *)&lcd800x1280_rightside_up,
 246        },
 247        {}
 248};
 249
 250/**
 251 * drm_get_panel_orientation_quirk - Check for panel orientation quirks
 252 * @width: width in pixels of the panel
 253 * @height: height in pixels of the panel
 254 *
 255 * This function checks for platform specific (e.g. DMI based) quirks
 256 * providing info on panel_orientation for systems where this cannot be
 257 * probed from the hard-/firm-ware. To avoid false-positive this function
 258 * takes the panel resolution as argument and checks that against the
 259 * resolution expected by the quirk-table entry.
 260 *
 261 * Note this function is also used outside of the drm-subsys, by for example
 262 * the efifb code. Because of this this function gets compiled into its own
 263 * kernel-module when built as a module.
 264 *
 265 * Returns:
 266 * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
 267 * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
 268 */
 269int drm_get_panel_orientation_quirk(int width, int height)
 270{
 271        const struct dmi_system_id *match;
 272        const struct drm_dmi_panel_orientation_data *data;
 273        const char *bios_date;
 274        int i;
 275
 276        for (match = dmi_first_match(orientation_data);
 277             match;
 278             match = dmi_first_match(match + 1)) {
 279                data = match->driver_data;
 280
 281                if (data->width != width ||
 282                    data->height != height)
 283                        continue;
 284
 285                if (!data->bios_dates)
 286                        return data->orientation;
 287
 288                bios_date = dmi_get_system_info(DMI_BIOS_DATE);
 289                if (!bios_date)
 290                        continue;
 291
 292                i = match_string(data->bios_dates, -1, bios_date);
 293                if (i >= 0)
 294                        return data->orientation;
 295        }
 296
 297        return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
 298}
 299EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
 300
 301#else
 302
 303/* There are no quirks for non x86 devices yet */
 304int drm_get_panel_orientation_quirk(int width, int height)
 305{
 306        return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
 307}
 308EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
 309
 310#endif
 311
 312MODULE_LICENSE("Dual MIT/GPL");
 313