| 1 | // SPDX-License-Identifier: MIT | 
|---|
| 2 | /* | 
|---|
| 3 | * Copyright © 2018 Intel Corporation | 
|---|
| 4 | */ | 
|---|
| 5 |  | 
|---|
| 6 | #include <linux/dmi.h> | 
|---|
| 7 |  | 
|---|
| 8 | #include <drm/drm_print.h> | 
|---|
| 9 |  | 
|---|
| 10 | #include "intel_display_core.h" | 
|---|
| 11 | #include "intel_display_types.h" | 
|---|
| 12 | #include "intel_quirks.h" | 
|---|
| 13 |  | 
|---|
| 14 | static void intel_set_quirk(struct intel_display *display, enum intel_quirk_id quirk) | 
|---|
| 15 | { | 
|---|
| 16 | display->quirks.mask |= BIT(quirk); | 
|---|
| 17 | } | 
|---|
| 18 |  | 
|---|
| 19 | static void intel_set_dpcd_quirk(struct intel_dp *intel_dp, enum intel_quirk_id quirk) | 
|---|
| 20 | { | 
|---|
| 21 | intel_dp->quirks.mask |= BIT(quirk); | 
|---|
| 22 | } | 
|---|
| 23 |  | 
|---|
| 24 | /* | 
|---|
| 25 | * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason | 
|---|
| 26 | */ | 
|---|
| 27 | static void quirk_ssc_force_disable(struct intel_display *display) | 
|---|
| 28 | { | 
|---|
| 29 | intel_set_quirk(display, quirk: QUIRK_LVDS_SSC_DISABLE); | 
|---|
| 30 | drm_info(display->drm, "applying lvds SSC disable quirk\n"); | 
|---|
| 31 | } | 
|---|
| 32 |  | 
|---|
| 33 | /* | 
|---|
| 34 | * A machine (e.g. Acer Aspire 5734Z) may need to invert the panel backlight | 
|---|
| 35 | * brightness value | 
|---|
| 36 | */ | 
|---|
| 37 | static void quirk_invert_brightness(struct intel_display *display) | 
|---|
| 38 | { | 
|---|
| 39 | intel_set_quirk(display, quirk: QUIRK_INVERT_BRIGHTNESS); | 
|---|
| 40 | drm_info(display->drm, "applying inverted panel brightness quirk\n"); | 
|---|
| 41 | } | 
|---|
| 42 |  | 
|---|
| 43 | /* Some VBT's incorrectly indicate no backlight is present */ | 
|---|
| 44 | static void quirk_backlight_present(struct intel_display *display) | 
|---|
| 45 | { | 
|---|
| 46 | intel_set_quirk(display, quirk: QUIRK_BACKLIGHT_PRESENT); | 
|---|
| 47 | drm_info(display->drm, "applying backlight present quirk\n"); | 
|---|
| 48 | } | 
|---|
| 49 |  | 
|---|
| 50 | /* Toshiba Satellite P50-C-18C requires T12 delay to be min 800ms | 
|---|
| 51 | * which is 300 ms greater than eDP spec T12 min. | 
|---|
| 52 | */ | 
|---|
| 53 | static void quirk_increase_t12_delay(struct intel_display *display) | 
|---|
| 54 | { | 
|---|
| 55 | intel_set_quirk(display, quirk: QUIRK_INCREASE_T12_DELAY); | 
|---|
| 56 | drm_info(display->drm, "Applying T12 delay quirk\n"); | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | /* | 
|---|
| 60 | * GeminiLake NUC HDMI outputs require additional off time | 
|---|
| 61 | * this allows the onboard retimer to correctly sync to signal | 
|---|
| 62 | */ | 
|---|
| 63 | static void quirk_increase_ddi_disabled_time(struct intel_display *display) | 
|---|
| 64 | { | 
|---|
| 65 | intel_set_quirk(display, quirk: QUIRK_INCREASE_DDI_DISABLED_TIME); | 
|---|
| 66 | drm_info(display->drm, "Applying Increase DDI Disabled quirk\n"); | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | static void quirk_no_pps_backlight_power_hook(struct intel_display *display) | 
|---|
| 70 | { | 
|---|
| 71 | intel_set_quirk(display, quirk: QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK); | 
|---|
| 72 | drm_info(display->drm, "Applying no pps backlight power quirk\n"); | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | static void quirk_fw_sync_len(struct intel_dp *intel_dp) | 
|---|
| 76 | { | 
|---|
| 77 | struct intel_display *display = to_intel_display(intel_dp); | 
|---|
| 78 |  | 
|---|
| 79 | intel_set_dpcd_quirk(intel_dp, quirk: QUIRK_FW_SYNC_LEN); | 
|---|
| 80 | drm_info(display->drm, "Applying Fast Wake sync pulse count quirk\n"); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | static void quirk_edp_limit_rate_hbr2(struct intel_display *display) | 
|---|
| 84 | { | 
|---|
| 85 | intel_set_quirk(display, quirk: QUIRK_EDP_LIMIT_RATE_HBR2); | 
|---|
| 86 | drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); | 
|---|
| 87 | } | 
|---|
| 88 |  | 
|---|
| 89 | struct intel_quirk { | 
|---|
| 90 | int device; | 
|---|
| 91 | int subsystem_vendor; | 
|---|
| 92 | int subsystem_device; | 
|---|
| 93 | void (*hook)(struct intel_display *display); | 
|---|
| 94 | }; | 
|---|
| 95 |  | 
|---|
| 96 | struct intel_dpcd_quirk { | 
|---|
| 97 | int device; | 
|---|
| 98 | int subsystem_vendor; | 
|---|
| 99 | int subsystem_device; | 
|---|
| 100 | u8 sink_oui[3]; | 
|---|
| 101 | u8 sink_device_id[6]; | 
|---|
| 102 | void (*hook)(struct intel_dp *intel_dp); | 
|---|
| 103 | }; | 
|---|
| 104 |  | 
|---|
| 105 | #define SINK_OUI(first, second, third) { (first), (second), (third) } | 
|---|
| 106 | #define SINK_DEVICE_ID(first, second, third, fourth, fifth, sixth) \ | 
|---|
| 107 | { (first), (second), (third), (fourth), (fifth), (sixth) } | 
|---|
| 108 |  | 
|---|
| 109 | #define SINK_DEVICE_ID_ANY	SINK_DEVICE_ID(0, 0, 0, 0, 0, 0) | 
|---|
| 110 |  | 
|---|
| 111 | /* For systems that don't have a meaningful PCI subdevice/subvendor ID */ | 
|---|
| 112 | struct intel_dmi_quirk { | 
|---|
| 113 | void (*hook)(struct intel_display *display); | 
|---|
| 114 | const struct dmi_system_id (*dmi_id_list)[]; | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | static int intel_dmi_reverse_brightness(const struct dmi_system_id *id) | 
|---|
| 118 | { | 
|---|
| 119 | DRM_INFO( "Backlight polarity reversed on %s\n", id->ident); | 
|---|
| 120 | return 1; | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | static int intel_dmi_no_pps_backlight(const struct dmi_system_id *id) | 
|---|
| 124 | { | 
|---|
| 125 | DRM_INFO( "No pps backlight support on %s\n", id->ident); | 
|---|
| 126 | return 1; | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | static const struct intel_dmi_quirk intel_dmi_quirks[] = { | 
|---|
| 130 | { | 
|---|
| 131 | .dmi_id_list = &(const struct dmi_system_id[]) { | 
|---|
| 132 | { | 
|---|
| 133 | .callback = intel_dmi_reverse_brightness, | 
|---|
| 134 | .ident = "NCR Corporation", | 
|---|
| 135 | .matches = {DMI_MATCH(DMI_SYS_VENDOR, "NCR Corporation"), | 
|---|
| 136 | DMI_MATCH(DMI_PRODUCT_NAME, ""), | 
|---|
| 137 | }, | 
|---|
| 138 | }, | 
|---|
| 139 | { | 
|---|
| 140 | .callback = intel_dmi_reverse_brightness, | 
|---|
| 141 | .ident = "Thundersoft TST178 tablet", | 
|---|
| 142 | /* DMI strings are too generic, also match on BIOS date */ | 
|---|
| 143 | .matches = {DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), | 
|---|
| 144 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"), | 
|---|
| 145 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."), | 
|---|
| 146 | DMI_EXACT_MATCH(DMI_BIOS_DATE, "04/15/2014"), | 
|---|
| 147 | }, | 
|---|
| 148 | }, | 
|---|
| 149 | { }  /* terminating entry */ | 
|---|
| 150 | }, | 
|---|
| 151 | .hook = quirk_invert_brightness, | 
|---|
| 152 | }, | 
|---|
| 153 | { | 
|---|
| 154 | .dmi_id_list = &(const struct dmi_system_id[]) { | 
|---|
| 155 | { | 
|---|
| 156 | .callback = intel_dmi_no_pps_backlight, | 
|---|
| 157 | .ident = "Google Lillipup sku524294", | 
|---|
| 158 | .matches = {DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Google"), | 
|---|
| 159 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "Lindar"), | 
|---|
| 160 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "sku524294"), | 
|---|
| 161 | }, | 
|---|
| 162 | }, | 
|---|
| 163 | { | 
|---|
| 164 | .callback = intel_dmi_no_pps_backlight, | 
|---|
| 165 | .ident = "Google Lillipup sku524295", | 
|---|
| 166 | .matches = {DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Google"), | 
|---|
| 167 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "Lindar"), | 
|---|
| 168 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "sku524295"), | 
|---|
| 169 | }, | 
|---|
| 170 | }, | 
|---|
| 171 | { } | 
|---|
| 172 | }, | 
|---|
| 173 | .hook = quirk_no_pps_backlight_power_hook, | 
|---|
| 174 | }, | 
|---|
| 175 | }; | 
|---|
| 176 |  | 
|---|
| 177 | static struct intel_quirk intel_quirks[] = { | 
|---|
| 178 | /* Lenovo U160 cannot use SSC on LVDS */ | 
|---|
| 179 | { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, | 
|---|
| 180 |  | 
|---|
| 181 | /* Sony Vaio Y cannot use SSC on LVDS */ | 
|---|
| 182 | { .device: 0x0046, .subsystem_vendor: 0x104d, .subsystem_device: 0x9076, .hook: quirk_ssc_force_disable }, | 
|---|
| 183 |  | 
|---|
| 184 | /* Acer Aspire 5734Z must invert backlight brightness */ | 
|---|
| 185 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x0459, .hook: quirk_invert_brightness }, | 
|---|
| 186 |  | 
|---|
| 187 | /* Acer/eMachines G725 */ | 
|---|
| 188 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x0210, .hook: quirk_invert_brightness }, | 
|---|
| 189 |  | 
|---|
| 190 | /* Acer/eMachines e725 */ | 
|---|
| 191 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x0212, .hook: quirk_invert_brightness }, | 
|---|
| 192 |  | 
|---|
| 193 | /* Acer/Packard Bell NCL20 */ | 
|---|
| 194 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x034b, .hook: quirk_invert_brightness }, | 
|---|
| 195 |  | 
|---|
| 196 | /* Acer Aspire 4736Z */ | 
|---|
| 197 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x0260, .hook: quirk_invert_brightness }, | 
|---|
| 198 |  | 
|---|
| 199 | /* Acer Aspire 5336 */ | 
|---|
| 200 | { .device: 0x2a42, .subsystem_vendor: 0x1025, .subsystem_device: 0x048a, .hook: quirk_invert_brightness }, | 
|---|
| 201 |  | 
|---|
| 202 | /* Acer C720 and C720P Chromebooks (Celeron 2955U) have backlights */ | 
|---|
| 203 | { .device: 0x0a06, .subsystem_vendor: 0x1025, .subsystem_device: 0x0a11, .hook: quirk_backlight_present }, | 
|---|
| 204 |  | 
|---|
| 205 | /* Acer C720 Chromebook (Core i3 4005U) */ | 
|---|
| 206 | { .device: 0x0a16, .subsystem_vendor: 0x1025, .subsystem_device: 0x0a11, .hook: quirk_backlight_present }, | 
|---|
| 207 |  | 
|---|
| 208 | /* Apple Macbook 2,1 (Core 2 T7400) */ | 
|---|
| 209 | { .device: 0x27a2, .subsystem_vendor: 0x8086, .subsystem_device: 0x7270, .hook: quirk_backlight_present }, | 
|---|
| 210 |  | 
|---|
| 211 | /* Apple Macbook 4,1 */ | 
|---|
| 212 | { .device: 0x2a02, .subsystem_vendor: 0x106b, .subsystem_device: 0x00a1, .hook: quirk_backlight_present }, | 
|---|
| 213 |  | 
|---|
| 214 | /* Toshiba CB35 Chromebook (Celeron 2955U) */ | 
|---|
| 215 | { .device: 0x0a06, .subsystem_vendor: 0x1179, .subsystem_device: 0x0a88, .hook: quirk_backlight_present }, | 
|---|
| 216 |  | 
|---|
| 217 | /* HP Chromebook 14 (Celeron 2955U) */ | 
|---|
| 218 | { .device: 0x0a06, .subsystem_vendor: 0x103c, .subsystem_device: 0x21ed, .hook: quirk_backlight_present }, | 
|---|
| 219 |  | 
|---|
| 220 | /* Dell Chromebook 11 */ | 
|---|
| 221 | { .device: 0x0a06, .subsystem_vendor: 0x1028, .subsystem_device: 0x0a35, .hook: quirk_backlight_present }, | 
|---|
| 222 |  | 
|---|
| 223 | /* Dell Chromebook 11 (2015 version) */ | 
|---|
| 224 | { .device: 0x0a16, .subsystem_vendor: 0x1028, .subsystem_device: 0x0a35, .hook: quirk_backlight_present }, | 
|---|
| 225 |  | 
|---|
| 226 | /* Toshiba Satellite P50-C-18C */ | 
|---|
| 227 | { .device: 0x191B, .subsystem_vendor: 0x1179, .subsystem_device: 0xF840, .hook: quirk_increase_t12_delay }, | 
|---|
| 228 |  | 
|---|
| 229 | /* GeminiLake NUC */ | 
|---|
| 230 | { .device: 0x3185, .subsystem_vendor: 0x8086, .subsystem_device: 0x2072, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 231 | { .device: 0x3184, .subsystem_vendor: 0x8086, .subsystem_device: 0x2072, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 232 | /* ASRock ITX*/ | 
|---|
| 233 | { .device: 0x3185, .subsystem_vendor: 0x1849, .subsystem_device: 0x2212, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 234 | { .device: 0x3184, .subsystem_vendor: 0x1849, .subsystem_device: 0x2212, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 235 | /* ECS Liva Q2 */ | 
|---|
| 236 | { .device: 0x3185, .subsystem_vendor: 0x1019, .subsystem_device: 0xa94d, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 237 | { .device: 0x3184, .subsystem_vendor: 0x1019, .subsystem_device: 0xa94d, .hook: quirk_increase_ddi_disabled_time }, | 
|---|
| 238 | /* HP Notebook - 14-r206nv */ | 
|---|
| 239 | { .device: 0x0f31, .subsystem_vendor: 0x103c, .subsystem_device: 0x220f, .hook: quirk_invert_brightness }, | 
|---|
| 240 |  | 
|---|
| 241 | /* Dell XPS 13 7390 2-in-1 */ | 
|---|
| 242 | { .device: 0x8a12, .subsystem_vendor: 0x1028, .subsystem_device: 0x08b0, .hook: quirk_edp_limit_rate_hbr2 }, | 
|---|
| 243 | }; | 
|---|
| 244 |  | 
|---|
| 245 | static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { | 
|---|
| 246 | /* Dell Precision 5490 */ | 
|---|
| 247 | { | 
|---|
| 248 | .device = 0x7d55, | 
|---|
| 249 | .subsystem_vendor = 0x1028, | 
|---|
| 250 | .subsystem_device = 0x0cc7, | 
|---|
| 251 | .sink_oui = SINK_OUI(0x38, 0xec, 0x11), | 
|---|
| 252 | .hook = quirk_fw_sync_len, | 
|---|
| 253 | }, | 
|---|
| 254 |  | 
|---|
| 255 | }; | 
|---|
| 256 |  | 
|---|
| 257 | void intel_init_quirks(struct intel_display *display) | 
|---|
| 258 | { | 
|---|
| 259 | struct pci_dev *d = to_pci_dev(display->drm->dev); | 
|---|
| 260 | int i; | 
|---|
| 261 |  | 
|---|
| 262 | for (i = 0; i < ARRAY_SIZE(intel_quirks); i++) { | 
|---|
| 263 | struct intel_quirk *q = &intel_quirks[i]; | 
|---|
| 264 |  | 
|---|
| 265 | if (d->device == q->device && | 
|---|
| 266 | (d->subsystem_vendor == q->subsystem_vendor || | 
|---|
| 267 | q->subsystem_vendor == PCI_ANY_ID) && | 
|---|
| 268 | (d->subsystem_device == q->subsystem_device || | 
|---|
| 269 | q->subsystem_device == PCI_ANY_ID)) | 
|---|
| 270 | q->hook(display); | 
|---|
| 271 | } | 
|---|
| 272 | for (i = 0; i < ARRAY_SIZE(intel_dmi_quirks); i++) { | 
|---|
| 273 | if (dmi_check_system(list: *intel_dmi_quirks[i].dmi_id_list) != 0) | 
|---|
| 274 | intel_dmi_quirks[i].hook(display); | 
|---|
| 275 | } | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | void intel_init_dpcd_quirks(struct intel_dp *intel_dp, | 
|---|
| 279 | const struct drm_dp_dpcd_ident *ident) | 
|---|
| 280 | { | 
|---|
| 281 | struct intel_display *display = to_intel_display(intel_dp); | 
|---|
| 282 | struct pci_dev *d = to_pci_dev(display->drm->dev); | 
|---|
| 283 | int i; | 
|---|
| 284 |  | 
|---|
| 285 | for (i = 0; i < ARRAY_SIZE(intel_dpcd_quirks); i++) { | 
|---|
| 286 | const struct intel_dpcd_quirk *q = &intel_dpcd_quirks[i]; | 
|---|
| 287 |  | 
|---|
| 288 | if (d->device == q->device && | 
|---|
| 289 | (d->subsystem_vendor == q->subsystem_vendor || | 
|---|
| 290 | q->subsystem_vendor == PCI_ANY_ID) && | 
|---|
| 291 | (d->subsystem_device == q->subsystem_device || | 
|---|
| 292 | q->subsystem_device == PCI_ANY_ID) && | 
|---|
| 293 | !memcmp(q->sink_oui, ident->oui, sizeof(ident->oui)) && | 
|---|
| 294 | (!memcmp(q->sink_device_id, ident->device_id, | 
|---|
| 295 | sizeof(ident->device_id)) || | 
|---|
| 296 | mem_is_zero(s: q->sink_device_id, n: sizeof(q->sink_device_id)))) | 
|---|
| 297 | q->hook(intel_dp); | 
|---|
| 298 | } | 
|---|
| 299 | } | 
|---|
| 300 |  | 
|---|
| 301 | bool intel_has_quirk(struct intel_display *display, enum intel_quirk_id quirk) | 
|---|
| 302 | { | 
|---|
| 303 | return display->quirks.mask & BIT(quirk); | 
|---|
| 304 | } | 
|---|
| 305 |  | 
|---|
| 306 | bool intel_has_dpcd_quirk(struct intel_dp *intel_dp, enum intel_quirk_id quirk) | 
|---|
| 307 | { | 
|---|
| 308 | return intel_dp->quirks.mask & BIT(quirk); | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|