From: andy.hayes@displaylink.com
To: "devel@edk2.groups.io" <devel@edk2.groups.io>
Cc: Leif Lindholm <leif.lindholm@linaro.org>,
"michael.d.kinney@intel.com" <michael.d.kinney@intel.com>
Subject: [PATCH v1 0/1] Added GOP driver for DisplayLink-based Universal USB Docks to edk2-platforms
Date: Wed, 14 Aug 2019 14:43:43 +0000 [thread overview]
Message-ID: <DB8PR10MB2684B8E28270DD983695427F95AD0@DB8PR10MB2684.EURPRD10.PROD.OUTLOOK.COM> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 2471 bytes --]
>From 4a42eb997aeb3699217b40bf3bc47dec56458847 Mon Sep 17 00:00:00 2001
From: "Andy Hayes" < andy.hayes@displaylink.com >
Date: Wed, 14 Aug 2019 15:30:20 +0100
Subject: [PATCH v1 0/1] Added GOP graphics driver for DisplayLink-based Universal USB Docks to edk2-platforms
andy.hayes@displaylink.com (1):
Added GOP driver for USB Docks which use DisplayLink chips.
.../DisplayLinkPkg/DisplayLinkPkg.dsc | 61 +
.../DisplayLinkGop/DisplayLinkGopDxe.inf | 63 +
.../DisplayLinkPkg/DisplayLinkGop/Edid.h | 129 ++
.../DisplayLinkGop/UsbDescriptors.h | 109 ++
.../DisplayLinkGop/UsbDisplayLink.h | 284 +++++
.../DisplayLinkGop/CapabilityDescriptor.c | 137 ++
.../DisplayLinkGop/ComponentName.c | 235 ++++
.../DisplayLinkPkg/DisplayLinkGop/Edid.c | 598 +++++++++
.../DisplayLinkPkg/DisplayLinkGop/Gop.c | 587 +++++++++
.../DisplayLinkGop/UsbDescriptors.c | 144 +++
.../DisplayLinkGop/UsbDisplayLink.c | 1109 +++++++++++++++++
.../DisplayLinkGop/UsbTransfer.c | 180 +++
.../DisplayLinkGop/VideoModes.c | 254 ++++
Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md | 77 ++
Maintainers.txt | 5 +
15 files changed, 3972 insertions(+)
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c
create mode 100644 Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md
--
2.17.1
[-- Attachment #1.2: Type: text/html, Size: 6680 bytes --]
[-- Attachment #2: 0001-Added-GOP-driver-for-USB-Docks-using-DisplayLink-chi.patch --]
[-- Type: application/octet-stream, Size: 136837 bytes --]
From 4a42eb997aeb3699217b40bf3bc47dec56458847 Mon Sep 17 00:00:00 2001
From: "andy.hayes@displaylink.com" <ahayes@UKCAML3148.NewnhamResearch.local>
Date: Wed, 14 Aug 2019 15:28:51 +0100
Subject: [PATCH v1 1/1] Added GOP driver for USB Docks using DisplayLink
chips.
---
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc | 61 ++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf | 63 ++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h | 129 +++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h | 109 ++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h | 284 +++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c | 137 +++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c | 235 +++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c | 598 +++++++++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c | 587 +++++++++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c | 144 +++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c | 1109 ++++++++++++++++++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c | 180 ++++
Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c | 254 +++++
Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md | 77 ++
Maintainers.txt | 5 +
15 files changed, 3972 insertions(+)
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc
new file mode 100644
index 000000000000..955331ba6076
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc
@@ -0,0 +1,61 @@
+#/** @file
+#
+# Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ PLATFORM_NAME = DisplayLinkPkg
+ PLATFORM_GUID = ad3b37b0-f798-4f97-9b3f-0c6f43d7c993
+ PLATFORM_VERSION = 0.1
+ DSC_SPECIFICATION = 0x0001001C
+ OUTPUT_DIRECTORY = Build/DisplayLink
+ SUPPORTED_ARCHITECTURES = X64|IA32|AARCH64|ARM
+ BUILD_TARGETS = DEBUG|RELEASE|NOOPT
+ SKUID_IDENTIFIER = DEFAULT
+
+[LibraryClasses]
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
+ DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+ UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+
+[LibraryClasses.AARCH64]
+ NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+ NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+[LibraryClasses.ARM]
+ NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+ NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+[PcdsFixedAtBuild]
+!ifdef $(DEBUG_ENABLE_OUTPUT)
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x3f
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80080043 # Flags to control amount of debug output - see https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Debugging
+ gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x07
+!endif
+
+[Components]
+ Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf
+
+[BuildOptions]
+ *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES -D INF_DRIVER_VERSION=$(INF_DRIVER_VERSION)
+ GCC:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG
+ MSFT:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG
+!ifdef $(COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE)
+ *_*_*_CC_FLAGS = -D COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
+!endif
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf
new file mode 100644
index 000000000000..9f1e1cd7f3b3
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf
@@ -0,0 +1,63 @@
+#/** @file
+# USB DisplayLink driver that implements blt and EDID commands
+#
+# USB DisplayLink driver consumes I/O Protocol and Device Path Protocol, and produces
+# Graphics Output Protocol on DisplayLink devices.
+# 1. DisplayLink reference
+# 2. UEFI Specification, v2.1
+#
+# Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = DisplayLinkGop
+ FILE_GUID = 2D2E62AA-9ECF-43b7-8219-94E7FC713DFF
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = UsbDisplayLinkDriverBindingEntryPoint
+ UNLOAD_IMAGE = UsbDisplayLinkDriverCombinedGopUnload
+ INF_DRIVER_VERSION = 0x00000001
+
+[Sources]
+ CapabilityDescriptor.c
+ ComponentName.c
+ Edid.c
+ Gop.c
+ UsbDescriptors.c
+ UsbDisplayLink.c
+ UsbDisplayLink.h
+ UsbTransfer.c
+ VideoModes.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ ReportStatusCodeLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ UefiUsbLib
+
+[Protocols]
+ gEfiUsbIoProtocolGuid ## TO_START
+ gEfiEdidActiveProtocolGuid # PROTOCOL BY_START
+ gEfiEdidDiscoveredProtocolGuid # PROTOCOL BY_START
+ gEfiEdidOverrideProtocolGuid # PROTOCOL TO_START
+ gEfiHiiDatabaseProtocolGuid
+ gEfiHiiFontProtocolGuid
+
+[Guids]
+ gEfiEventExitBootServicesGuid
+ gEfiGlobalVariableGuid
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize ## CONSUMES
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h
new file mode 100644
index 000000000000..80e443ced4a8
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h
@@ -0,0 +1,129 @@
+/** @file Edid.h
+ * @brief Helper routine and corresponding data struct used by USB DisplayLink Driver.
+ * Reads and parses the EDID, checks if a requested video mode is in the supplied EDID
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#ifndef EDID_H
+#define EDID_H
+
+#include "UsbDisplayLink.h"
+
+#define EDID_HEADER_SIZE ((size_t)8)
+#define EDID_NUMBER_OF_ESTABLISHED_TIMINGS_BYTES ((UINTN)3)
+#define EDID_NUMBER_OF_STANDARD_TIMINGS ((UINTN)8)
+#define EDID_NUMBER_OF_DETAILED_TIMINGS ((UINTN)4)
+
+
+typedef struct {
+ UINT16 HRes;
+ UINT16 VRes;
+ UINT16 Refresh;
+} EDID_TIMING;
+
+
+EFI_STATUS
+DlEdidGetSupportedVideoMode (
+ UINT32 ModeNumber,
+ CONST VOID *EDID,
+ UINT32 EdidSize,
+ CONST struct VideoMode **VideoMode
+ );
+
+EFI_STATUS
+DlEdidGetSupportedVideoModeWithFallback (
+ UINT32 ModeNumber,
+ CONST VOID *EDID,
+ UINT32 EdidSize,
+ CONST struct VideoMode **VideoMode
+ );
+
+UINT32
+DlEdidGetNumSupportedModesInEdid (
+ CONST VOID *EDID,
+ UINT32 EdidSize
+ );
+
+EFI_STATUS
+DlReadEdid (
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
+);
+
+// EDID Detailed timings section - Features
+enum EdidDetailedTimingsFeatures {
+ EdidDetailedTimingsFeaturesInterlaced = 0x80,
+ EdidDetailedTimingsFeaturesStereoModeMask = 0x60,
+ EdidDetailedTimingsFeaturesSyncSchemeMask = 0x18,
+ EdidDetailedTimingsFeaturesHorizontalSyncPositive = 0x02,
+ EdidDetailedTimingsFeaturesVerticalSyncPositive = 0x04,
+};
+
+// NR-110497-TC-7ZM Section 4.3.3 0x22 Set Video Mode - Flags
+enum VideoModeFlags {
+ VideoModeFlagsInterlaced = 0x0001,
+ VideoModeFlagsHorizontalSyncInverted = 0x0100,
+ VideoModeFlagsVerticalSyncInverted = 0x0200,
+};
+
+struct StandardTimingIdentification {
+ UINT8 HorizontalActivePixels; // X resolution, from 256->2288 in increments of 8 pixels
+ UINT8 ImageAspectRatioAndrefresh; // Bits 7,6 Aspect ratio - 0=16:10 1=4:3 2=5:4 3=16:9
+ // Bits 5,0 Refresh rate Range 60->1213Hz
+};
+
+struct DetailedTimingIdentification {
+ UINT16 PixelClock; // wPixelClock in VideoMode struct
+ UINT8 HActiveLo; // wHActive
+ UINT8 HBlankingLo; // wHBlanking
+ UINT8 HActiveHiBlankingHi;
+ UINT8 VActiveLo; // wVActive
+ UINT8 VBlankingLo; // wVBlanking
+ UINT8 VActiveHiBlankingHi;
+ UINT8 HSyncOffsetLo; // wHSyncOffset, front porch
+ UINT8 HSyncWidthLo; // wHSyncWidth
+ UINT8 VSyncOffsetLoSyncWidthLo;
+ UINT8 HSyncOffsetHiHSyncWidthHiVSyncOffsetHiSyncWidthHi;
+ UINT8 HImageSizeHi;
+ UINT8 VImageSizeHi;
+ UINT8 HImageSizeLoVImageSizeLo;
+ UINT8 HBorder;
+ UINT8 VBorder;
+ UINT8 Features;
+};
+
+struct Edid {
+ UINT8 Header[EDID_HEADER_SIZE]; //EDID header "00 FF FF FF FF FF FF 00"
+ UINT16 ManufactureName; //EISA 3-character ID
+ UINT16 ProductCode; //Vendor assigned code
+ UINT32 SerialNumber; //32-bit serial number
+ UINT8 WeekOfManufacture; //Week number
+ UINT8 YearOfManufacture; //Year
+ UINT8 EdidVersion; //EDID Structure Version
+ UINT8 EdidRevision; //EDID Structure Revision
+ UINT8 VideoInputDefinition;
+ UINT8 MaxHorizontalImageSize; //cm
+ UINT8 MaxVerticalImageSize; //cm
+ UINT8 DisplayTransferCharacteristic;
+ UINT8 FeatureSupport;
+ UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0
+ UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0
+ UINT8 RedX; //Red-x Bits 9 - 2
+ UINT8 RedY; //Red-y Bits 9 - 2
+ UINT8 GreenX; //Green-x Bits 9 - 2
+ UINT8 GreenY; //Green-y Bits 9 - 2
+ UINT8 BlueX; //Blue-x Bits 9 - 2
+ UINT8 BlueY; //Blue-y Bits 9 - 2
+ UINT8 WhiteX; //White-x Bits 9 - 2
+ UINT8 WhiteY; //White-x Bits 9 - 2
+ UINT8 EstablishedTimings[EDID_NUMBER_OF_ESTABLISHED_TIMINGS_BYTES];
+ struct StandardTimingIdentification standardTimingIdentifications[EDID_NUMBER_OF_STANDARD_TIMINGS];
+ struct DetailedTimingIdentification detailedTimingDescriptions[EDID_NUMBER_OF_DETAILED_TIMINGS];
+ UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow
+ UINT8 Checksum;
+};
+
+#endif // EDID_H
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h
new file mode 100644
index 000000000000..cdc01aad193a
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h
@@ -0,0 +1,109 @@
+/**
+ * @file UsbDescriptors.h
+ * @brief Functions to read USB Interface and Capabilities descriptors
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+ /**
+ Type of the Direct Framebuffer capability descriptor.
+ This is a vendor specific USB descriptor for DisplayLink.
+ @see NR-140082 Section 3.5
+ **/
+#define DESCRIPTOR_TYPE_DIRECTFB_CAPABILITY 0x5e
+
+ /**
+ Identifiers for capabllity keys
+ @see NR-140082 Section 3.2
+ **/
+
+ /**
+ Key for Capabilities 1 - See section 3.2.1
+ **/
+#define CAPABILITIES1_KEY 0x0
+
+ /**
+ Lengths for capabllity fields
+ **/
+#define CAPABILITIES1_LENGTH 0x4
+
+ /**
+ Bits for the capability bitmask Capabilities1
+ **/
+
+ /**
+ This is the first capability defined for the protocol.
+ It represents the mode of operation as of initial release.
+ If a device ever breaks compatibility with this initial release,
+ it will cease
+ to support CapabilityBaseProtocol.
+ **/
+#define CAPABILITIES1_BASE_PROTOCOL (1 << 0)
+
+ /**
+ Idealised VendorDescriptor which is the result
+ of parsing vendor descriptor from device.
+ **/
+ typedef struct {
+ UINT32 Capabilities1;
+ } VendorDescriptor;
+
+#pragma pack(push, 1)
+ typedef struct {
+ UINT16 Key; /** Really of type enum DescrptorKeys */
+ UINT8 Length;
+ UINT8 Value[];
+ } DescriptorKLV;
+
+ typedef struct {
+ UINT8 Length;
+ UINT8 Type;
+ UINT16 CapabilityVersion;
+ UINT8 CapabilityLength;
+ UINT8 Klv[];
+ } VendorDescriptorGeneric;
+#pragma pack(pop)
+
+
+EFI_STATUS UsbDisplayLinkGetInterfaceDescriptor (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ EFI_USB_INTERFACE_DESCRIPTOR* InterfaceDescriptor,
+ UINT8 index
+ );
+
+EFI_STATUS ReadCapabilitiesDescriptor (IN EFI_USB_IO_PROTOCOL *UsbIo, VOID* Buffer, UINT16 Length);
+
+/**
+Parse data in buffer to a VendorDescriptor, if possible.
+
+@param Data Buffer
+@param Length Length of buffer
+
+@retval EFI_SUCCESS The descriptor was parsed successfully
+@retval EFI_UNSUPPORTED Simple Pointer Protocol is not installed on Controller.
+**/
+EFI_STATUS
+UsbDisplayLinkParseCapabilitiesDescriptor (
+ CONST IN VOID* Data,
+ IN UINTN Length,
+ OUT VendorDescriptor* Descriptor
+);
+
+/**
+Decide if binding may proceed, given capabilities
+
+@retval TRUE Binding may proceed
+@retval FALSE Binding is not possible
+**/
+BOOLEAN
+UsbDisplayLinkCapabilitiesSufficientToBind (
+ CONST IN VendorDescriptor* Descriptor
+);
+
+#endif // USB_DESCRIPTORS_H_
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h
new file mode 100644
index 000000000000..7c2722c9870a
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h
@@ -0,0 +1,284 @@
+/**
+ * @file UsbDisplayLink.h
+ * @brief Helper routine and corresponding data struct used by USB DisplayLink Driver.
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#ifndef _EFI_USB_DISPLAYLINK_H_
+#define _EFI_USB_DISPLAYLINK_H_
+
+#undef _DEBUG
+
+#undef NULL
+#undef _ASSERT
+
+#include <wchar.h>
+
+#include <Uefi/UefiBaseType.h>
+#include <Uefi/UefiSpec.h>
+
+#include <Protocol/EdidActive.h>
+#include <Protocol/EdidDiscovered.h>
+#include <Protocol/EdidOverride.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/UsbIo.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#define VENDOR_DISPLAYLINK 0x17e9
+#define CLASS_VENDOR 0xFF
+
+#define DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO ((UINTN)0)
+
+#define INTERFACE_PROTOCOL_DIRECT_FB 4
+
+#define USB_DISPLAYLINK_DEV_SIGNATURE SIGNATURE_32 ('d', 'l', 'i', 'n')
+
+#define USB_TRANSFER_LENGTH (64 * 1024)
+#define DISPLAYLINK_MODE_DATA_LENGTH (146)
+#define DISPLAYLINK_USB_CTRL_TIMEOUT (1000)
+#define DISPLAYLINK_USB_BULK_TIMEOUT (1)
+
+#define DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD ((UINTN)1000000) // 0.1s in us
+#define DISPLAYLINK_FULL_SCREEN_UPDATE_PERIOD ((UINTN)30000) // 3s in ticks
+
+#define DISPLAYLINK_FIXED_VERTICAL_REFRESH_RATE ((UINT16)60)
+
+// Requests to read values from the firmware
+#define EDID_BLOCK_SIZE 128
+#define EDID_DETAILED_TIMING_INVALID_PIXEL_CLOCK ((UINT16)(0x64))
+
+/** Structures ported from firmware - protocol.h */
+enum ID {
+ // VideoCommands
+ GET_OUTPUT_EDID = 0,
+ SET_VIDEO_MODE = 1
+};
+
+typedef struct {
+ UINT32 HorizontalResolution;
+ UINT32 VerticalResolution;
+ UINT32 ColorDepth;
+ UINT32 RefreshRate;
+ UINT8 Commands[DISPLAYLINK_MODE_DATA_LENGTH];
+} DISPLAYLINK_MODE_DATA;
+
+#define GRAPHICS_OUTPUT_INVALID_MODE_NUMBER 0xffff
+
+/**
+ * Device instance of USB display.
+ */
+typedef struct {
+ UINT64 Signature;
+ EFI_HANDLE Handle;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
+ EFI_USB_ENDPOINT_DESCRIPTOR BulkOutEndpointDescriptor;
+ EFI_USB_ENDPOINT_DESCRIPTOR BulkInEndpointDescriptor;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutputProtocol;
+ EFI_EDID_DISCOVERED_PROTOCOL EdidDiscovered;
+ EFI_EDID_ACTIVE_PROTOCOL EdidActive;
+ EFI_UNICODE_STRING_TABLE *ControllerNameTable;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Screen;
+ UINTN DataSent; /** Debug - used to track the bandwidth */
+ EFI_EVENT TimerEvent;
+ EFI_EVENT DriverExitBootServicesEvent;
+ BOOLEAN ShowBandwidth; /** Debugging - show the bandwidth on the screen */
+ BOOLEAN ShowTestPattern; /** Show a colourbar pattern instead of the BLTd contents of the framebuffer */
+ UINTN LastY1; /** Used to track if we can do a partial screen update */
+ UINTN LastY2;
+ UINTN LastWidth;
+ UINTN TimeSinceLastScreenUpdate; /** Do a full screen update every (x) seconds */
+} USB_DISPLAYLINK_DEV;
+
+struct VideoMode {
+ UINT8 Reserved1; /* Reserved - must be 0. */
+ UINT8 Reserved2; /* Reserved - must be 2. */
+
+ // Values matching the EDID Detailed Timing Descriptor spec
+ UINT16 PixelClock;
+ UINT16 HActive;
+ UINT16 HBlanking;
+ UINT16 HSyncOffset; // Horizontal Front Porch
+ UINT16 HSyncWidth;
+ UINT16 VActive;
+ UINT16 VBlanking;
+ UINT16 VSyncOffset; // Vertical Front Porch
+ UINT16 VSyncWidth;
+ // End of Edid Detailed Timing Descriptor
+
+ UINT16 Flags /*ModeFlags*/;
+ UINT16 Accumulate;
+ UINT16 Reserved3; /* Reserved - must be 0. */
+ UINT16 Reserved4; /* Reserved - must be 0. */
+ UINT16 InsetLeft;
+ UINT16 InsetTop;
+ UINT16 InsetRight;
+ UINT16 InsetBottom;
+ UINT32 FillValue;
+ UINT32 Reserved5; /* Reserved - must be 0. */
+ UINT8 Vic;
+ UINT8 ActiveFormat;
+ UINT16 Reserved6;
+};
+
+#define USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(a) \
+ CR(a, USB_DISPLAYLINK_DEV, GraphicsOutputProtocol, USB_DISPLAYLINK_DEV_SIGNATURE)
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gUsbDisplayLinkDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL mUsbDisplayLinkComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL mUsbDisplayLinkComponentName2;
+
+
+/* ******************************************* */
+/* ******** GOP interface functions ******** */
+/* ******************************************* */
+
+/**
+ * Implementation of the GOP protocol QueryMode API function
+ * @param This Instance of the GOP protocol
+ * @param ModeNumber
+ * @param SizeOfInfo
+ * @param Info
+ * @return
+ */
+EFI_STATUS
+ EFIAPI
+ DisplayLinkQueryMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber,
+ OUT UINTN *SizeOfInfo,
+ OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
+ );
+
+
+/**
+ * Implementation of the GOP protocol SetMode API function
+ * @param This
+ * @param ModeNumber
+ * @return
+ */
+EFI_STATUS
+ EFIAPI
+ DisplayLinkSetMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber
+ );
+
+/**
+ * Implementation of the GOP protocol Blt API function
+ * @param This
+ * @param BltBuffer
+ * @param BltOperation
+ * @param SourceX
+ * @param SourceY
+ * @param DestinationX
+ * @param DestinationY
+ * @param Width
+ * @param Height
+ * @param Delta
+ * @return
+ */
+EFI_STATUS
+ EFIAPI
+ DisplayLinkBlt (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta OPTIONAL
+ );
+
+/* *************************** */
+/* ** GOP helper functions ** */
+/* *************************** */
+
+VOID
+DlGopPrintTextToScreen (
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
+ UINTN X,
+ UINTN Y,
+ IN CONST CHAR16 *Format,
+ ...
+);
+
+EFI_STATUS
+DlGopSendTestPattern (
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ UINTN PatternNumber
+);
+
+EFI_STATUS
+DlGopSendScreenUpdate (
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
+);
+
+
+/* ******************************************* */
+/* ******** USB interface functions ******** */
+/* ******************************************* */
+
+EFI_STATUS
+DlUsbSendControlWriteMessage (
+ IN USB_DISPLAYLINK_DEV *Device,
+ IN UINT8 Request,
+ IN UINT16 Value,
+ IN CONST VOID *ControlMsg,
+ IN UINT16 ControlMsgLen
+);
+
+EFI_STATUS
+DlUsbSendControlReadMessage (
+ IN USB_DISPLAYLINK_DEV *Device,
+ IN UINT8 Request,
+ IN UINT16 Value,
+ OUT VOID *ControlMsg,
+ IN UINT16 ControlMsgLen
+);
+
+EFI_STATUS
+DlUsbBulkWrite (
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ CONST UINT8* Buffer,
+ UINTN DataLen,
+ UINT32 *USBStatus
+);
+
+UINTN
+DlUsbBulkRead (
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ UINT8* Buffer,
+ UINTN BufferLen
+);
+
+/* ******************************************* */
+/* ******** Video Mode functions ******** */
+/* ******************************************* */
+
+// Pre-calculated video modes
+UINT32
+DlVideoModeGetNumSupportedVideoModes ();
+
+CONST struct VideoMode *
+DlVideoModeGetSupportedVideoMode (
+ UINT32 index
+);
+
+#endif
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c
new file mode 100644
index 000000000000..4bfadd770b81
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c
@@ -0,0 +1,137 @@
+/** @file CapabilityDescriptor.c
+ * @brief Definitions for reading USB capability descriptor DisplayLink dock
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+#include "UsbDescriptors.h"
+
+/**
+ * Check that the Capability Descriptor is valid and hasn't been tampered with.
+ * @param Data Binary data of the Capability Descriptor received from the DisplayLink device
+ * @param Length
+ * @param out
+ * @return
+ */
+STATIC EFI_STATUS
+ValidateHeader (
+ CONST IN VOID* Data,
+ IN UINTN Length,
+ OUT CONST VendorDescriptorGeneric** Out
+ )
+{
+ if (Length < sizeof (VendorDescriptorGeneric)) {
+ DEBUG ((DEBUG_ERROR, "Data too short (%d bytes) for capability descriptor header section\n", Length));
+ return EFI_INVALID_PARAMETER;
+ }
+ CONST VendorDescriptorGeneric* Desc = (VendorDescriptorGeneric*)Data;
+ if (Desc->Length > Length) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: Descriptor (%d bytes) exceeds buffer (%d bytes)\n",
+ Length, Desc->Length));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ if (Desc->Type != DESCRIPTOR_TYPE_DIRECTFB_CAPABILITY) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid type (0x%08x)\n", Desc->Type));
+ return EFI_UNSUPPORTED;
+ }
+ if (Desc->CapabilityVersion != 1) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid version (%d)\n", Desc->CapabilityVersion));
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+ // Capability length must fit within remaining space
+ if (Desc->CapabilityLength > (Desc->Length - (sizeof (Desc->Length) + sizeof (Desc->Type)))) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid CapabilityLength (%d)\n", Desc->CapabilityLength));
+ return EFI_INVALID_PARAMETER;
+ }
+ *Out = Desc;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ * Check that the connected DisplayLink device supports the functionality that this driver requires, e.g. video formats
+ * @param Data Binary data of the Capability Descriptor received from the DisplayLink device
+ * @param Length
+ * @param Output
+ * @return
+ */
+EFI_STATUS
+UsbDisplayLinkParseCapabilitiesDescriptor (
+ CONST IN VOID* Data,
+ IN UINTN Length,
+ OUT VendorDescriptor* Output
+ )
+{
+ CONST VendorDescriptorGeneric* Desc;
+ EFI_STATUS Status;
+
+ Desc = NULL;
+ Status = ValidateHeader (Data, Length, &Desc);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Default capabilities
+ Output->Capabilities1 = 0;
+
+ CONST UINTN CapsHeaderLength = sizeof (Desc->CapabilityVersion) + sizeof (Desc->CapabilityLength);
+ ASSERT (CapsHeaderLength < MAX_UINT8);
+
+ UINTN DataRemaining;
+ UINTN Offset;
+
+ DataRemaining = Desc->CapabilityLength - CapsHeaderLength;
+ Offset = 0;
+
+ while (DataRemaining >= sizeof (DescriptorKLV)) {
+ CONST DescriptorKLV* KeyHeader = (CONST DescriptorKLV*)(Desc->Klv + Offset);
+ CONST UINTN KeyValueSize = sizeof (DescriptorKLV) + KeyHeader->Length;
+ if (KeyValueSize > DataRemaining) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid value Length (%d)\n", Desc->CapabilityLength));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (KeyHeader->Key) {
+ case CAPABILITIES1_KEY: {
+ if (KeyHeader->Length != CAPABILITIES1_LENGTH) {
+ DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid length (%d) for Capabilities 1\n", KeyHeader->Length));
+ return EFI_INVALID_PARAMETER;
+ }
+ Output->Capabilities1 = *(UINT32*)KeyHeader->Value;
+ break;
+ default:
+ // Ignore unknown types
+ break;
+ }
+ }
+ DataRemaining -= KeyValueSize;
+ Offset += KeyValueSize;
+ }
+ return EFI_SUCCESS;
+}
+
+
+/**
+ * Check that the DisplayLink device supports the basic level of functionality to display GOP pixels.
+ * @param Descriptor The USB descriptor received from the DisplayLink device
+ * @return True we can bind, False we can't
+ */
+BOOLEAN
+UsbDisplayLinkCapabilitiesSufficientToBind (
+ CONST IN VendorDescriptor* Descriptor
+ )
+{
+ BOOLEAN Sufficient;
+ Sufficient = (BOOLEAN)(Descriptor->Capabilities1 & CAPABILITIES1_BASE_PROTOCOL);
+
+ if (Sufficient == FALSE) {
+ DEBUG ((DEBUG_ERROR, "DisplayLink device does not report support for base capabilites - reports x%x, required x%x\n", Descriptor->Capabilities1 & CAPABILITIES1_BASE_PROTOCOL));
+ }
+ return Sufficient;
+}
+
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c
new file mode 100644
index 000000000000..74498f339eb7
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c
@@ -0,0 +1,235 @@
+/**
+ * @file ComponentName.c
+ * @brief UEFI Component Name protocol implementation for USB DisplayLink driver.
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+);
+
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+);
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL mUsbDisplayLinkComponentName = {
+ UsbDisplayLinkComponentNameGetDriverName,
+ UsbDisplayLinkComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mUsbDisplayLinkComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UsbDisplayLinkComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UsbDisplayLinkComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbDisplayLinkDriverNameTable[] = {
+ { (CHAR8*)"eng;en", (CHAR16*)L"DisplayLink USB GOP Driver" },
+ { (CHAR8*)NULL , (CHAR16*)NULL }
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+ @param DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUsbDisplayLinkDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &mUsbDisplayLinkComponentName));
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+ @param ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+ @param Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+ @param ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutputProtocol;
+ EFI_USB_IO_PROTOCOL *UsbIoProtocol;
+
+ //
+ // This is a device driver, so ChildHandle must be NULL.
+ //
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check Controller's handle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIoProtocol,
+ gUsbDisplayLinkDriverBinding.DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+ if (!EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiUsbIoProtocolGuid,
+ gUsbDisplayLinkDriverBinding.DriverBindingHandle,
+ ControllerHandle);
+
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Status != EFI_ALREADY_STARTED) {
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Get the device context
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiGraphicsOutputProtocolGuid,
+ (VOID**)&GraphicsOutputProtocol,
+ gUsbDisplayLinkDriverBinding.DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(GraphicsOutputProtocol);
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ UsbDisplayLinkDev->ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &mUsbDisplayLinkComponentName));
+}
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c
new file mode 100644
index 000000000000..21f4b7d9c736
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c
@@ -0,0 +1,598 @@
+/** @file Edid.c
+ * @brief Reads and parses the EDID, checks if a requested video mode is in the supplied EDID
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+#include "Edid.h"
+
+CONST UINT8 ExpectedEdidHeader[EDID_HEADER_SIZE] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
+
+//
+// Standard timing defined by VESA EDID
+//
+CONST EDID_TIMING EstablishedTimings[EDID_NUMBER_OF_ESTABLISHED_TIMINGS_BYTES][8] = {
+ //
+ // Established Timing I
+ //
+ {
+ { 800, 600, 60 },
+ { 800, 600, 56 },
+ { 640, 480, 75 },
+ { 640, 480, 72 },
+ { 640, 480, 67 },
+ { 640, 480, 60 },
+ { 720, 400, 88 },
+ { 720, 400, 70 },
+ },
+ {
+ //
+ // Established Timing II
+ //
+ { 1280, 1024, 75 },
+ { 1024, 768, 75 },
+ { 1024, 768, 70 },
+ { 1024, 768, 60 },
+ { 1024, 768, 87 },
+ { 832, 624, 75 },
+ { 800, 600, 75 },
+ { 800, 600, 72 },
+ },
+ //
+ // Established Timing III
+ //
+ {
+ { 1152, 870, 75 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ }
+};
+
+/**
+ * Requests the monitor EDID data from the connected DisplayLink device
+ * @param UsbDisplayLinkDev
+ * @param EdidDataBlock
+ * @param EdidSize
+ * @retval EFI_DEVICE_ERROR - No EDID received, or EDID is corrupted
+ * @retval EFI_OUT_OF_RESOURCES - Cannot allocate memory
+ * @retval EFI_SUCCESS
+ *
+ */
+STATIC EFI_STATUS
+ReadEdidData (
+ IN USB_DISPLAYLINK_DEV *UsbDisplayLinkDev,
+ OUT UINT8 **EdidDataBlock,
+ OUT UINTN *EdidSize
+)
+{
+ EFI_STATUS Status;
+
+ UINT8 EdidDataRead[EDID_BLOCK_SIZE];
+ UINT8 *EdidData = EdidDataRead;
+ UINT8* ValidEdid;
+
+ Status = DlUsbSendControlReadMessage (UsbDisplayLinkDev, GET_OUTPUT_EDID, 0, EdidDataRead, sizeof (EdidDataRead));
+
+ if (EFI_ERROR (Status) || (EdidData[0] != 0)) {
+ DEBUG ((DEBUG_ERROR, "No monitor EDID received from DisplayLink device - System error %r, EDID error %d. Monitor connected correctly?\n", Status, EdidData[0]));
+ return EFI_DEVICE_ERROR;
+ } else {
+ //
+ // Search for the EDID signature
+ //
+ ValidEdid = &EdidData[0];
+ CONST UINT64 Signature = 0x00ffffffffffff00ull;
+ if (CompareMem (ValidEdid, &Signature, 8) != 0) {
+ //
+ // No EDID signature found
+ //
+ DEBUG ((DEBUG_ERROR, "Monitor EDID received from DisplayLink device did not have a valid signature - corrupted?\n"));
+ Status = EFI_DEVICE_ERROR;
+ return Status;
+ }
+ }
+
+ *EdidDataBlock = (UINT8*)AllocateCopyPool (
+ EDID_BLOCK_SIZE,
+ ValidEdid);
+
+ if (*EdidDataBlock == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Currently only support EDID 1.x
+ //
+ *EdidSize = EDID_BLOCK_SIZE;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+Calculates the mod256 checksum of the EDID and compares it with the one supplied at the end of the EDID
+@param EDID Pointer to the 128-byte EDID
+@retval TRUE The EDID checksum is correct
+**/
+BOOLEAN
+IsEdidChecksumCorrect (
+ CONST VOID *EDID
+ )
+{
+ CONST UINT8 EdidChecksum = ((UINT8 *)EDID)[EDID_BLOCK_SIZE - 1];
+ UINT8 CalculatedChecksum;
+
+ CalculatedChecksum = 0;
+
+ UINTN i;
+ for (i = 0; i < EDID_BLOCK_SIZE - 1; i++) {
+ CalculatedChecksum += ((UINT8 *)EDID)[i];
+ }
+ CalculatedChecksum = 0 - CalculatedChecksum;
+
+ return (CalculatedChecksum == EdidChecksum);
+}
+
+/**
+Check if a particular video mode is in the Established Timings section of the EDID.
+
+@param EDID Pointer to the 128-byte EDID
+@param hRes Horizontal resolution
+@param vRes Vertical resolution
+@param refresh Refresh rate
+@retval TRUE The requested mode is present in the Established Timings section
+
+**/
+STATIC BOOLEAN
+IsModeInEstablishedTimings (
+ IN CONST VOID *EDID,
+ IN UINT16 HRes,
+ IN UINT16 VRes,
+ IN UINT16 Refresh
+ )
+{
+ CONST struct Edid *pEDID = (CONST struct Edid *)EDID;
+ BOOLEAN ModeSupported;
+
+ ModeSupported = FALSE;
+
+ int EstByteNum;
+ int BitNum;
+ for (EstByteNum = 0; EstByteNum < EDID_NUMBER_OF_ESTABLISHED_TIMINGS_BYTES; EstByteNum++) {
+ for (BitNum = 0; BitNum < 8; BitNum++) {
+ if (pEDID->EstablishedTimings[EstByteNum] & (1 << BitNum)) { // The bit is set in the established timings of the EDID
+
+ if ((EstablishedTimings[EstByteNum][BitNum].HRes == HRes) && // The passed-in resolution matches the resolution represented by the set bit
+ (EstablishedTimings[EstByteNum][BitNum].VRes == VRes) &&
+ (EstablishedTimings[EstByteNum][BitNum].Refresh == Refresh)) {
+
+ ModeSupported = TRUE;
+ break;
+ }
+ }
+ }
+ if (ModeSupported == TRUE) {
+ break;
+ }
+ }
+ return ModeSupported;
+}
+
+/**
+Extract the resolutions and refresh rate from one of the entries in the Standard Timings section of the EDID.
+
+@param EDID Pointer to the 128-byte EDID
+@param timingNumber The entry that we want to extract
+@param hRes Output - Horizontal resolution
+@param vRes Output - Vertical resolution
+@param refresh Output - Refresh rate
+@retval TRUE The requested Standard Timings entry contains valid data
+
+**/
+STATIC BOOLEAN ReadStandardTiming (
+ CONST VOID *EDID,
+ IN UINT8 TimingNumber,
+ OUT UINT16 *HRes,
+ OUT UINT16 *VRes,
+ OUT UINT8 *Refresh)
+{
+ CONST struct Edid *pEDID = (CONST struct Edid *)EDID;
+
+ // See section 3.9.1 of the VESA EDID spec
+
+ if (((pEDID->standardTimingIdentifications[TimingNumber].HorizontalActivePixels) == 0x01) &&
+ (pEDID->standardTimingIdentifications[TimingNumber].ImageAspectRatioAndrefresh) == 0x01) {
+ *HRes = 0;
+ *VRes = 0;
+ *Refresh = 0;
+ return FALSE;
+ }
+ *HRes = (pEDID->standardTimingIdentifications[TimingNumber].HorizontalActivePixels + 31) * 8;
+
+ UINT8 AspectRatio;
+ AspectRatio = (pEDID->standardTimingIdentifications[TimingNumber].ImageAspectRatioAndrefresh >> 6) & 0x3;
+
+ switch (AspectRatio) {
+ case 0: *VRes = (*HRes * 10) / 16;
+ break;
+ case 1: *VRes = (*HRes * 3) / 4;
+ break;
+ case 2: *VRes = (*HRes * 4) / 5;
+ break;
+ case 3: *VRes = (*HRes * 9) / 16;
+ break;
+ default: break;
+ }
+
+ // WORKAROUND - 1360x768 is not a perfect aspect ratio
+ if ((*HRes == 1360) && (*VRes == 765)) {
+ *VRes = 768;
+ }
+
+ *Refresh = (pEDID->standardTimingIdentifications[TimingNumber].ImageAspectRatioAndrefresh & 0x1F) + 60;
+
+ return TRUE;
+}
+
+/**
+Extract the resolutions and refresh rate from one of the entries in the Detailed Timings section of the EDID.
+
+@param EDID Pointer to the 128-byte EDID
+@param timingNumber The entry that we want to extract
+@param videoMode Output - Filled in with details from the detailed timing
+@retval TRUE The requested Detailed Timings entry contains valid data
+
+**/
+STATIC BOOLEAN
+ReadDetailedTiming (
+ IN CONST VOID *EDID,
+ IN UINT8 TimingNumber,
+ OUT struct VideoMode *VideoMode
+ )
+{
+ if (TimingNumber >= EDID_NUMBER_OF_DETAILED_TIMINGS) {
+ return FALSE;
+ }
+
+ UINT16 NumValidDetailedTimingsFound;
+ NumValidDetailedTimingsFound = 0;
+
+ // Spin through the detailed timings until we find a valid one - then check if this has the index that we want
+ int BlockNumber;
+ for (BlockNumber = 0; BlockNumber < EDID_NUMBER_OF_DETAILED_TIMINGS; BlockNumber++) {
+ CONST struct Edid *pEDID = (CONST struct Edid *)EDID;
+ CONST struct DetailedTimingIdentification *pTiming = &pEDID->detailedTimingDescriptions[BlockNumber];
+
+ if (((BlockNumber == 0) && (pTiming->PixelClock == EDID_DETAILED_TIMING_INVALID_PIXEL_CLOCK)) ||
+ (pTiming->PixelClock == 0)) {
+ // This is not a valid detailed timing descriptor
+ continue;
+ }
+
+ if ((pTiming->Features & EdidDetailedTimingsFeaturesSyncSchemeMask) != EdidDetailedTimingsFeaturesSyncSchemeMask) {
+ DEBUG ((DEBUG_INFO, "EDID detailed timing with unsupported sync scheme found - not processing.\n"));
+ continue;
+ }
+
+ if ((pTiming->Features & EdidDetailedTimingsFeaturesStereoModeMask) != 0) {
+ DEBUG ((DEBUG_INFO, "EDID detailed timing with unsupported stereo mode found - not processing.\n"));
+ continue;
+ }
+
+ // We've found a supported detailed timing - now see if this is the requested one
+ if (TimingNumber != NumValidDetailedTimingsFound) {
+ NumValidDetailedTimingsFound++;
+ continue;
+ }
+
+ ZeroMem ((VOID *)VideoMode, sizeof (struct VideoMode));
+
+ // Bit manipulations copied from host software class EDIDTimingDescriptor
+
+ VideoMode->PixelClock = (UINT16)pTiming->PixelClock;
+ VideoMode->HActive = pTiming->HActiveLo + ((pTiming->HActiveHiBlankingHi & 0xF0) << 4);
+ VideoMode->VActive = pTiming->VActiveLo + ((pTiming->VActiveHiBlankingHi & 0xF0) << 4);
+
+ VideoMode->HBlanking = pTiming->HBlankingLo + ((pTiming->HActiveHiBlankingHi & 0x0F) << 8);
+ VideoMode->VBlanking = pTiming->VBlankingLo + ((pTiming->VActiveHiBlankingHi & 0x0F) << 8);
+
+ VideoMode->HSyncOffset = pTiming->HSyncOffsetLo + ((pTiming->HSyncOffsetHiHSyncWidthHiVSyncOffsetHiSyncWidthHi & 0xC0) << 2); // Horizontal Front Porch
+ VideoMode->HSyncWidth = pTiming->HSyncWidthLo + ((pTiming->HSyncOffsetHiHSyncWidthHiVSyncOffsetHiSyncWidthHi & 0x30) << 4);
+
+ VideoMode->VSyncOffset = ((pTiming->VSyncOffsetLoSyncWidthLo & 0xF0) >> 4) + ((pTiming->HSyncOffsetHiHSyncWidthHiVSyncOffsetHiSyncWidthHi & 0x0C) << 2); // Vertical Front Porch
+ VideoMode->VSyncWidth = (pTiming->VSyncOffsetLoSyncWidthLo & 0x0F) + ((pTiming->HSyncOffsetHiHSyncWidthHiVSyncOffsetHiSyncWidthHi & 0x03) << 4);
+
+ VideoMode->Reserved2 = 2;
+ VideoMode->Accumulate = 1;
+
+ // Horizontal and vertical sync inversions - positive if bit set in descriptor (EDID spec)
+ // In the VideoMode, they are negative if the bit is set (NR-110497-TC 4.3.3 0x22 Set Video Mode)
+
+ // Horizontal sync
+ if ((pTiming->Features & EdidDetailedTimingsFeaturesHorizontalSyncPositive) == 0) {
+ VideoMode->Flags |= VideoModeFlagsHorizontalSyncInverted;
+ }
+ // Vertical sync
+ if ((pTiming->Features & EdidDetailedTimingsFeaturesVerticalSyncPositive) == 0) {
+ VideoMode->Flags |= VideoModeFlagsVerticalSyncInverted;
+ }
+ // Interlace
+ if ((pTiming->Features & EdidDetailedTimingsFeaturesInterlaced) == EdidDetailedTimingsFeaturesInterlaced) {
+ VideoMode->Flags |= VideoModeFlagsInterlaced;
+ }
+
+ DEBUG ((DEBUG_INFO, "Read mode %dx%d from detailed timings\n", VideoMode->HActive, VideoMode->VActive));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+Check if a particular video mode is in either the Established or Standard Timings section of the EDID.
+
+@param EDID Pointer to the 128-byte EDID
+@param hRes Horizontal resolution
+@param vRes Vertical resolution
+@param refresh Refresh rate
+@retval TRUE The requested mode is present in the EDID
+
+**/
+STATIC BOOLEAN
+IsModeInEdid (
+ IN CONST VOID *EDID,
+ IN UINT16 HRes,
+ IN UINT16 VRes,
+ IN UINT16 Refresh
+ )
+{
+ UINT16 EdidHRes;
+ UINT16 EdidVRes;
+ UINT8 EdidRefresh;
+ BOOLEAN ModeSupported;
+
+ ModeSupported = IsModeInEstablishedTimings (EDID, HRes, VRes, Refresh);
+
+ if (ModeSupported == FALSE) {
+ // Check if the mode is in the Standard Timings section of the EDID
+ UINT8 i;
+ for (i = 0; i < EDID_NUMBER_OF_STANDARD_TIMINGS; i++) {
+ if (TRUE == ReadStandardTiming (EDID, i, &EdidHRes, &EdidVRes, &EdidRefresh)) {
+ if ((HRes == EdidHRes) && (VRes == EdidVRes) && (Refresh == EdidRefresh)) {
+ ModeSupported = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ return ModeSupported;
+}
+
+/**
+Returns the (index)'th entry from the list of pre-calculated video timings that is also present in the EDID,
+or, video mode data corresponding to any detailed timings present in the EDID.
+
+Like QueryVideoMode, finds the number (and contents) of video modes available by repeatedly calling this function
+with an increasing index value, until it returns FALSE
+@param index The caller wants the _index_'th video mode
+@param EDID Pointer to the 128-byte EDID
+@param edidSize Size in bytes of the EDID
+@param videoMode Video timings extracted from the modeData structure
+@retval EFI_SUCCESS The requested mode is present in the EDID
+@retval EFI_INVALID_PARAMETER The requested mode is not present in the EDID
+**/
+EFI_STATUS
+DlEdidGetSupportedVideoMode (
+ IN UINT32 Index,
+ IN CONST VOID *EDID,
+ IN UINT32 EdidSize,
+ OUT CONST struct VideoMode **VideoMode
+ )
+{
+ UINTN SupportedVideoModesFoundInEdid;
+ EFI_STATUS Status;
+
+ SupportedVideoModesFoundInEdid = 0;
+ Status = EFI_INVALID_PARAMETER;
+
+ // If we didn't manage to find an EDID earlier, just use one of the hard-coded video modes
+ if ((EDID == NULL) || (EdidSize != EDID_BLOCK_SIZE)) {
+ if (Index >= DlVideoModeGetNumSupportedVideoModes ()) {
+ return EFI_INVALID_PARAMETER;
+ }
+ else {
+ *VideoMode = DlVideoModeGetSupportedVideoMode (Index);
+ DEBUG ((DEBUG_WARN, "No monitor EDID loaded - returning mode from default list (%dx%d)\n", (*VideoMode)->HActive, (*VideoMode)->VActive));
+ return EFI_SUCCESS;
+ }
+ }
+
+ UINT16 ModeNumber;
+ for (ModeNumber = 0; ModeNumber < DlVideoModeGetNumSupportedVideoModes (); ModeNumber++) {
+
+ CONST struct VideoMode *SupportedVideoMode = DlVideoModeGetSupportedVideoMode (ModeNumber);
+ ASSERT (SupportedVideoMode);
+
+ if (IsModeInEdid (EDID, SupportedVideoMode->HActive, SupportedVideoMode->VActive, DISPLAYLINK_FIXED_VERTICAL_REFRESH_RATE)) {
+ if (Index == SupportedVideoModesFoundInEdid) {
+ *VideoMode = SupportedVideoMode;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ SupportedVideoModesFoundInEdid++;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ // Have a look in the detailed timings
+ UINTN DetailedTimingNumber;
+ STATIC struct VideoMode TmpVideoMode;
+ DetailedTimingNumber = Index - SupportedVideoModesFoundInEdid;
+
+ if (DetailedTimingNumber < EDID_NUMBER_OF_DETAILED_TIMINGS) {
+ if (ReadDetailedTiming (EDID, (UINT8)DetailedTimingNumber, &TmpVideoMode)) {
+ *VideoMode = &TmpVideoMode;
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ * Like GetSupportedEdidVideoMode, but will return a fallback fixed mode of 640x480@60Hz
+ * for index 0 if no suitable modes found in EDID.
+ * @param index
+ * @param EDID
+ * @param edidSize
+ * @param videoMode
+ * @return EFI_SUCCESS
+ */
+EFI_STATUS
+DlEdidGetSupportedVideoModeWithFallback (
+ IN UINT32 Index,
+ IN CONST VOID *EDID,
+ IN UINT32 EdidSize,
+ OUT CONST struct VideoMode **VideoMode
+ )
+{
+ EFI_STATUS Status;
+ Status = DlEdidGetSupportedVideoMode (Index, EDID, EdidSize, VideoMode);
+
+ if (EFI_ERROR (Status)) {
+ // Special case - if we didn't find any matching video modes in the EDID, fall back to 640x480@60Hz
+ if (Index == 0) {
+ *VideoMode = DlVideoModeGetSupportedVideoMode (0);
+ DEBUG ((DEBUG_WARN, "No video modes supported by driver found in monitor EDID received from DL device - falling back to %dx%d\n", (*VideoMode)->HActive, (*VideoMode)->VActive));
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+/**
+Count the number of video modes that we have timing information for that are present in the EDID
+@param EDID Pointer to the 128-byte EDID
+@param edidSize
+@retval The number of modes in the EDID
+
+**/
+UINT32
+DlEdidGetNumSupportedModesInEdid (
+ IN CONST VOID *EDID,
+ IN UINT32 EdidSize
+ )
+{
+ UINT32 MaxMode;
+
+ if ((EDID == NULL) || (EdidSize != EDID_BLOCK_SIZE)) {
+ return DlVideoModeGetNumSupportedVideoModes ();
+ }
+
+ for (MaxMode = 0; ; MaxMode++) {
+ CONST struct VideoMode *videoMode;
+ if (EFI_ERROR (DlEdidGetSupportedVideoMode (MaxMode, EDID, EdidSize, &videoMode))) {
+ break;
+ }
+ }
+ DEBUG ((DEBUG_INFO, "Found %d video modes supported by driver in monitor EDID.\n", MaxMode));
+ return MaxMode;
+}
+
+
+
+/**
+ * Read the EDID from the connected monitor, store it in the local data structure
+ * @param UsbDisplayLinkDev
+ * @retval EFI_OUT_OF_RESOURCES - Could not allocate memory
+ * @retval EFI_SUCCESS
+ */
+EFI_STATUS
+DlReadEdid (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN EdidFound;
+ EFI_EDID_OVERRIDE_PROTOCOL* EdidOverride;
+
+ //
+ // setup EDID information
+ //
+ UsbDisplayLinkDev->EdidDiscovered.Edid = (UINT8 *)NULL;
+ UsbDisplayLinkDev->EdidDiscovered.SizeOfEdid = 0;
+
+ EdidFound = FALSE;
+
+ //
+ // Find EDID Override protocol firstly, this protocol is installed by platform if needed.
+ //
+ Status = gBS->LocateProtocol (&gEfiEdidOverrideProtocolGuid, NULL, (VOID**)&EdidOverride);
+
+ if (!EFI_ERROR (Status)) {
+ UINT32 EdidAttributes = 0xff;
+ UINTN EdidDataSize = 0;
+ UINT8* EdidDataBlock = (UINT8*)NULL;
+
+ // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow
+ EdidDataBlock = (UINT8*)AllocatePool (EDID_BLOCK_SIZE * 2);
+
+ if (NULL == EdidDataBlock) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EdidOverride->GetEdid (
+ EdidOverride,
+ &UsbDisplayLinkDev->Handle,
+ &EdidAttributes,
+ &EdidDataSize,
+ &EdidDataBlock);
+
+ if (!EFI_ERROR (Status) && EdidAttributes == 0 && EdidDataSize != 0) {
+ UsbDisplayLinkDev->EdidDiscovered.SizeOfEdid = (UINT32)EdidDataSize;
+ UsbDisplayLinkDev->EdidDiscovered.Edid = EdidDataBlock;
+ EdidFound = TRUE;
+ }
+ else {
+ FreePool (EdidDataBlock);
+ EdidDataBlock = NULL;
+ }
+ }
+
+ if (EdidFound != TRUE) {
+ UINTN EdidDataSize = 0;
+ UINT8* EdidDataBlock = (UINT8*)NULL;
+
+ if (ReadEdidData (UsbDisplayLinkDev, &EdidDataBlock, &EdidDataSize) == EFI_SUCCESS) {
+
+ if (IsEdidChecksumCorrect (EdidDataBlock)) {
+ UsbDisplayLinkDev->EdidDiscovered.SizeOfEdid = (UINT32)EdidDataSize;
+ UsbDisplayLinkDev->EdidDiscovered.Edid = EdidDataBlock;
+ EdidFound = TRUE;
+ } else {
+ DEBUG ((DEBUG_WARN, "Monitor EDID received from DisplayLink device had an invalid checksum. Corrupted?\n"));
+ }
+ }
+ }
+
+ if (EdidFound == FALSE) {
+ DEBUG ((DEBUG_WARN, "No valid monitor EDID received from DisplayLink device. Cannot detect resolutions supported by monitor.\n"));
+ }
+
+ // Set the EDID active.
+ // In an error case this will be set 0/NULL, which flags to the parsing code that there is no EDID.
+ UsbDisplayLinkDev->EdidActive.SizeOfEdid = UsbDisplayLinkDev->EdidDiscovered.SizeOfEdid;
+ UsbDisplayLinkDev->EdidActive.Edid = UsbDisplayLinkDev->EdidDiscovered.Edid;
+
+ return EFI_SUCCESS;
+}
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c
new file mode 100644
index 000000000000..db1b8952a470
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c
@@ -0,0 +1,587 @@
+/**
+ * @file Gop.c
+ * @brief UEFI GOP protocol API implementation for USB DisplayLink driver.
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+#include "Edid.h"
+
+
+/**
+ *
+ * @param This Pointer to the instance of the GOP protocol
+ * @param BltOperation
+ * @param SourceX
+ * @param SourceY
+ * @param Width
+ * @param Height
+ * @param DestinationX
+ * @param DestinationY
+ * @return
+ */
+STATIC EFI_STATUS
+CheckBounds (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL* This,
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY
+ )
+{
+ USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL* Gop;
+
+ UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(This);
+ Gop = &UsbDisplayLinkDev->GraphicsOutputProtocol;
+
+ CONST EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* ScreenMode = Gop->Mode->Info;
+
+ if (BltOperation == EfiBltVideoToBltBuffer || BltOperation == EfiBltVideoToVideo) {
+ if (SourceY + Height > ScreenMode->VerticalResolution) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (SourceX + Width > ScreenMode->HorizontalResolution) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (BltOperation == EfiBltBufferToVideo
+ || BltOperation == EfiBltVideoToVideo
+ || BltOperation == EfiBltVideoFill) {
+ if (DestinationY + Height > ScreenMode->VerticalResolution) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DestinationX + Width > ScreenMode->HorizontalResolution) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+#if 0
+ // Check if the buffer areas overlap when copying to and from the same buffer
+ if (BltOperation == EfiBltVideoToVideo) {
+ if ((SourceX < DestinationX + Width) &&
+ (SourceX + Width > DestinationX) &&
+ (SourceY < DestinationY + Height) &&
+ (SourceY + Height > DestinationY)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+#endif
+ return EFI_SUCCESS;
+}
+
+/**
+ * Update the local copy of the Frame Buffer. This local copy is periodically transmitted to the
+ * DisplayLink device (via DlGopSendScreenUpdate)
+ * @param UsbDisplayLinkDev
+ * @param BltBuffer
+ * @param BltOperation
+ * @param SourceX
+ * @param SourceY
+ * @param DestinationX
+ * @param DestinationY
+ * @param Width
+ * @param Height
+ * @param BltBufferStride
+ * @param PixelsPerScanLine
+ */
+STATIC VOID
+BuildBackBuffer (
+ IN USB_DISPLAYLINK_DEV *UsbDisplayLinkDev,
+ IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN BltBufferStride,
+ IN UINTN PixelsPerScanLine
+)
+{
+ UINTN H;
+ UINTN W;
+ switch (BltOperation) {
+ case EfiBltVideoToBltBuffer:
+ {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Blt;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcB;
+ Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)((UINT8 *)BltBuffer + (DestinationY * BltBufferStride) + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+ SrcB = UsbDisplayLinkDev->Screen + SourceY * PixelsPerScanLine + SourceX;
+
+ for (H = 0; H < Height; H++) {
+ for (W = 0; W < Width; W++) {
+ Blt[W] = *SrcB++;
+ }
+ Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(((UINT8*)Blt) + BltBufferStride);
+ SrcB += PixelsPerScanLine - Width;
+ }
+ }
+ break;
+
+ case EfiBltBufferToVideo:
+ {
+ // Update the store of the area of the screen that is "dirty" - that we need to send in the next screen update.
+ if (DestinationY < UsbDisplayLinkDev->LastY1) {
+ UsbDisplayLinkDev->LastY1 = DestinationY;
+ }
+ if ((DestinationY + Height) > UsbDisplayLinkDev->LastY2) {
+ UsbDisplayLinkDev->LastY2 = DestinationY + Height;
+ }
+
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Blt;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
+ Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)(((UINT8 *)BltBuffer) + (SourceY * BltBufferStride) + SourceX * sizeof *Blt);
+ DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
+
+ for (H = 0; H < Height; H++) {
+ for (W = 0; W < Width; W++) {
+ *DstB++ = Blt[W];
+ }
+ Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(((UINT8*)Blt) + BltBufferStride);
+ DstB += PixelsPerScanLine - Width;
+ }
+ }
+ break;
+
+ case EfiBltVideoToVideo:
+ {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcB;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
+ SrcB = UsbDisplayLinkDev->Screen + SourceY * PixelsPerScanLine + SourceX;
+ DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
+
+ for (H = 0; H < Height; H++) {
+ for (W = 0; W < Width; W++) {
+ *DstB++ = *SrcB++;
+ }
+ SrcB += PixelsPerScanLine - Width;
+ DstB += PixelsPerScanLine - Width;
+ }
+ }
+ break;
+
+ case EfiBltVideoFill:
+ {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
+ DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
+ for (H = 0; H < Height; H++) {
+ for (W = 0; W < Width; W++) {
+ *DstB++ = *BltBuffer;
+ }
+ DstB += PixelsPerScanLine - Width;
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+/**
+ * Display a colour bar pattern on the DisplayLink device.
+ * @param UsbDisplayLinkDev
+ * @param PatternNumber
+ * @return
+ */
+EFI_STATUS
+DlGopSendTestPattern (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ IN UINTN PatternNumber)
+{
+ EFI_STATUS Status;
+ UINTN DataLen;
+ UINT8 *DstBuf;
+ UINT32 USBStatus;
+
+ Status = EFI_SUCCESS;
+ DataLen = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 3; // Send 1 line @ 24 bits per pixel
+ DstBuf = AllocateZeroPool (DataLen);
+
+ if (DstBuf == NULL) {
+ DEBUG ((DEBUG_ERROR, "SendTestPattern Failed to allocate memory\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //DEBUG ((DEBUG_ERROR, "Called DlGopSendTestPattern %d\n", PatternNumber));
+
+ CONST UINT8 RedPixel[3] = { 0xFF, 0x00, 0x00 };
+ CONST UINT8 GreenPixel[3] = { 0x00, 0xFF, 0x00 };
+ CONST UINT8 BluePixel[3] = { 0x00, 0x00, 0xFF };
+ CONST UINT8 YellowPixel[3] = { 0xFF, 0xFF, 0x00 };
+ CONST UINT8 MagentaPixel[3] = { 0xFF, 0x00, 0xFF };
+ CONST UINT8 CyanPixel[3] = { 0x00, 0xFF, 0xFF };
+
+ UINTN Row;
+ UINTN Column;
+ for (Row = 0; Row < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution; Row++) {
+ for (Column = 0; Column < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution; Column++) {
+
+ if (0 == PatternNumber) {
+ if (Row < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution / 3) {
+ CopyMem (&DstBuf[Column * 3], RedPixel, sizeof (RedPixel));
+ }
+ else if (Row < (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution * 2) / 3)
+ {
+ CopyMem (&DstBuf[Column * 3], GreenPixel, sizeof (GreenPixel));
+ }
+ else {
+ CopyMem (&DstBuf[Column * 3], BluePixel, sizeof (BluePixel));
+ }
+ }
+ else {
+ if (Column < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution / 3) {
+ CopyMem (&DstBuf[Column * 3], YellowPixel, sizeof (RedPixel));
+ }
+ else if (Column < (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 2) / 3)
+ {
+ CopyMem (&DstBuf[Column * 3], MagentaPixel, sizeof (GreenPixel));
+ }
+ else {
+ CopyMem (&DstBuf[Column * 3], CyanPixel, sizeof (BluePixel));
+ }
+ }
+ }
+ DlUsbBulkWrite (UsbDisplayLinkDev, DstBuf, DataLen, &USBStatus);
+ }
+ // Payload with length of 1 to terminate the frame
+ DlUsbBulkWrite (UsbDisplayLinkDev, DstBuf, 1, &USBStatus);
+ FreePool (DstBuf);
+
+ return Status;
+}
+
+
+/**
+ * Transfer the latest copy of the Blt buffer over USB to the DisplayLink device
+ * @param UsbDisplayLinkDev
+ * @return
+ */
+EFI_STATUS
+DlGopSendScreenUpdate (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
+ )
+{
+ EFI_STATUS Status;
+ UINT32 USBStatus;
+ Status = EFI_SUCCESS;
+
+ // If it has been a while since we sent an update, send a full screen.
+ // This allows us to update a hot-plugged monitor quickly.
+ if (UsbDisplayLinkDev->TimeSinceLastScreenUpdate > DISPLAYLINK_FULL_SCREEN_UPDATE_PERIOD) {
+ UsbDisplayLinkDev->LastY1 = 0;
+ UsbDisplayLinkDev->LastY2 = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution - 1;
+ }
+
+ // If there has been no BLT since the last update/poll, drop out quietly.
+ if (UsbDisplayLinkDev->LastY2 < UsbDisplayLinkDev->LastY1) {
+ UsbDisplayLinkDev->TimeSinceLastScreenUpdate += (DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD / 1000); // Convert us to ms
+ return EFI_SUCCESS;
+ }
+
+ UsbDisplayLinkDev->TimeSinceLastScreenUpdate = 0;
+
+ EFI_TPL OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+ UINTN DataLen;
+ UINTN Width;
+ UINTN Height;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcPtr;
+ UINT8* DstPtr;
+ UINT8 DstBuffer[1920 * 3]; // TODO - Get rid of the magic numbers
+ // ALSO TODO - Use a buffer allocated at runtime to store the line, stored in the USB_DISPLAYLINK_DEV structure.
+ UINTN H;
+
+ DataLen = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 3; // Send 1 line @ 24 bits per pixel - TODO - Get rid of the magic number
+ Width = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution;
+ Height = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution;
+ SrcPtr = UsbDisplayLinkDev->Screen;
+ DstPtr = DstBuffer;
+
+ for (H = 0; H < Height; H++) {
+ DstPtr = DstBuffer;
+
+ UINTN W;
+ for (W = 0; W < Width; W++) {
+ // Need to swap round the RGB values
+ DstPtr[0] = ((UINT8 *)SrcPtr)[2];
+ DstPtr[1] = ((UINT8 *)SrcPtr)[1];
+ DstPtr[2] = ((UINT8 *)SrcPtr)[0];
+ SrcPtr++;
+ DstPtr += 3;
+ }
+
+ Status = DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, DataLen, &USBStatus);
+
+ // USBStatus values defined in usbio.h, e.g. EFI_USB_ERR_TIMEOUT 0x40
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Screen update - USB bulk transfer of pixel data failed. Line %d len %d, failure code %r USB status x%x\n", H, DataLen, Status, USBStatus));
+ break;
+ }
+ // Need an extra DlUsbBulkWrite if the data length is divisible by USB MaxPacketSize. This spare data will just get written into the (invisible) stride area.
+ // Note that the API doesn't let us do a bulk write of 0.
+ if ((DataLen & (UsbDisplayLinkDev->BulkOutEndpointDescriptor.MaxPacketSize - 1)) == 0) {
+ Status = DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, 2, &USBStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Screen update - USB bulk transfer of pixel data failed. Line %d len %d, failure code %r USB status x%x\n", H, DataLen, Status, USBStatus));
+ break;
+ }
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ // If we've successfully transmitted the frame, reset the values that store which area of the screen has been BLTted to.
+ // If we haven't succeeded, this will mean we'll try to resend it after the next poll period.
+ UsbDisplayLinkDev->LastY2 = 0;
+ UsbDisplayLinkDev->LastY1 = (UINTN)-1;
+ }
+
+ // Payload with length of 1 to terminate the frame
+ // We need to do this even if we had an error, to indicate to the DL device that it should now expect a new frame.
+ DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, 1, &USBStatus);
+
+ gBS->RestoreTPL (OriginalTPL);
+
+ return Status;
+}
+
+/**
+ * Calculate the video refresh rate from the video timing parameters (pixel clock etc)
+ * @param videoMode
+ * @return
+ */
+STATIC inline UINT16
+CalculateRefreshRate (
+ IN CONST struct VideoMode *VideoMode)
+{
+ UINT16 RefreshRate;
+ UINT16 Rmod;
+ UINT16 Rate10Hz;
+
+ RefreshRate = (VideoMode->PixelClock * 10000) / ((VideoMode->HActive + VideoMode->HBlanking) * (VideoMode->VActive + VideoMode->VBlanking));
+ Rmod = RefreshRate % 10;
+ Rate10Hz = RefreshRate - Rmod;
+
+ if (Rmod >= 5) {
+ Rate10Hz += 10;
+ }
+ return Rate10Hz;
+}
+
+/* ***************************************************************************************************** */
+/* ***************************************************************************************************** */
+/* ****************** START OF FUNCTIONS WHICH IMPLEMENT GOP INTERFACE ****************** */
+/* ***************************************************************************************************** */
+/* ***************************************************************************************************** */
+
+
+/**
+ *
+ * @param Gop Pointer to the instance of the GOP protocol
+ * @param ModeNumber
+ * @param SizeOfInfo
+ * @param Info
+ * @return
+ */
+EFI_STATUS
+EFIAPI
+DisplayLinkQueryMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
+ IN UINT32 ModeNumber,
+ OUT UINTN *SizeOfInfo,
+ OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
+)
+{
+ USB_DISPLAYLINK_DEV *Dev;
+ CONST struct VideoMode *VideoMode;
+ EFI_STATUS Status;
+
+ Dev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(Gop);
+ Status = EFI_INVALID_PARAMETER;
+
+ if ((SizeOfInfo == NULL) || (Info == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get a video mode from the EDID
+ Status = DlEdidGetSupportedVideoModeWithFallback (ModeNumber, Dev->EdidActive.Edid, Dev->EdidActive.SizeOfEdid, &VideoMode);
+
+ if (!EFI_ERROR (Status)) {
+
+ *Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
+ if (*Info == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DEBUG ((DEBUG_INFO, "BIOS querying mode number %d - returning %dx%d @ %dHz\n", ModeNumber, VideoMode->HActive, VideoMode->VActive, CalculateRefreshRate (VideoMode)));
+
+ *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+
+ (*Info)->Version = 0;
+ (*Info)->HorizontalResolution = VideoMode->HActive;
+ (*Info)->VerticalResolution = VideoMode->VActive;
+ (*Info)->PixelFormat = PixelBltOnly;
+ (*Info)->PixelsPerScanLine = (*Info)->HorizontalResolution;
+ (*Info)->PixelInformation.RedMask = 0;
+ (*Info)->PixelInformation.GreenMask = 0;
+ (*Info)->PixelInformation.BlueMask = 0;
+ (*Info)->PixelInformation.ReservedMask = 0;
+ }
+ return Status;
+}
+
+/**
+ *
+ * @param Gop Pointer to the instance of the GOP protocol
+ * @param ModeNumber
+ * @return
+ */
+EFI_STATUS
+EFIAPI
+DisplayLinkSetMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
+ IN UINT32 ModeNumber
+)
+{
+ USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
+ EFI_STATUS Status;
+ CONST struct VideoMode *VideoMode;
+
+ UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(Gop);
+
+ // Prevent the DisplayLinkPeriodicTimer from interrupting us (bug 28877).
+ // When the driver is manually loaded, the TPL is TPL_NOTIFY (16) which prevents the interrupt from the timer.
+ // When the GOP driver is sideloaded, the TPL of this call is TPL_APPLICATION (4) and the timer can interrupt us.
+ Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
+
+ // Get a video mode from the EDID
+ Status = DlEdidGetSupportedVideoModeWithFallback (ModeNumber, UsbDisplayLinkDev->EdidActive.Edid, UsbDisplayLinkDev->EdidActive.SizeOfEdid, &VideoMode);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Gop->Mode->Info->Version = 0;
+ Gop->Mode->Info->HorizontalResolution = VideoMode->HActive;
+ Gop->Mode->Info->VerticalResolution = VideoMode->VActive;
+ Gop->Mode->Info->PixelFormat = PixelBltOnly;
+ Gop->Mode->Info->PixelsPerScanLine = Gop->Mode->Info->HorizontalResolution;
+ Gop->Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+ Gop->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS)(UINTN)NULL;
+ Gop->Mode->FrameBufferSize = 0;
+
+ //
+ // Allocate the back buffer
+ //
+ if (UsbDisplayLinkDev->Screen != NULL) {
+ FreePool (UsbDisplayLinkDev->Screen);
+ }
+
+ UsbDisplayLinkDev->Screen = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)AllocateZeroPool (
+ Gop->Mode->Info->HorizontalResolution *
+ Gop->Mode->Info->VerticalResolution *
+ sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+
+ if (UsbDisplayLinkDev->Screen == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DEBUG ((DEBUG_INFO, "Video mode %d selected by BIOS - %d x %d.\n", ModeNumber, VideoMode->HActive, VideoMode->VActive));
+ // Wait until we are sure that we can set the video mode before we tell the firmware
+ Status = DlUsbSendControlWriteMessage (UsbDisplayLinkDev, SET_VIDEO_MODE, 0, VideoMode, sizeof (struct VideoMode));
+
+ if (Status != EFI_SUCCESS) {
+ // Flag up that we haven't set the video mode correctly yet.
+ DEBUG ((DEBUG_ERROR, "Failed to send USB message to DisplayLink device to set monitor video mode. Monitor connected correctly?\n"));
+ Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
+ FreePool (UsbDisplayLinkDev->Screen);
+ UsbDisplayLinkDev->Screen = NULL;
+ } else {
+ BuildBackBuffer (
+ UsbDisplayLinkDev,
+ UsbDisplayLinkDev->Screen,
+ EfiBltBufferToVideo,
+ 0, 0,
+ 0, 0,
+ Gop->Mode->Info->HorizontalResolution,
+ Gop->Mode->Info->VerticalResolution,
+ Gop->Mode->Info->HorizontalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
+ Gop->Mode->Info->HorizontalResolution);
+ // unlock the DisplayLinkPeriodicTimer
+ Gop->Mode->Mode = ModeNumber;
+ }
+
+ return Status;
+}
+
+/**
+ * Implementation of the GOP protocol Blt API function
+ * @param This Pointer to the instance of the GOP protocol
+ * @param BltBuffer
+ * @param BltOperation
+ * @param SourceX
+ * @param SourceY
+ * @param DestinationX
+ * @param DestinationY
+ * @param Width
+ * @param Height
+ * @param Delta
+ * @return
+ */
+EFI_STATUS
+EFIAPI
+DisplayLinkBlt (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta OPTIONAL
+)
+{
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
+ UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(This);
+
+ // Drop out if we haven't set the video mode up yet
+ if (This->Mode->Mode == GRAPHICS_OUTPUT_INVALID_MODE_NUMBER) {
+ return EFI_SUCCESS;
+ }
+
+ if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Width == 0 || Height == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Lock so we make an atomic write the frame buffer.
+ // We would not want a timer based event (Cursor, ...) to come in while we are doing this operation.
+ EFI_TPL OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+ CONST UINTN BltBufferStride = (Delta == 0) ? Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) : Delta;
+ CONST EFI_STATUS boundsCheckStatus = CheckBounds (This, BltOperation, SourceX, SourceY, Width, Height, DestinationX, DestinationY);
+ if (EFI_ERROR (boundsCheckStatus)) {
+ gBS->RestoreTPL (OriginalTPL);
+ return boundsCheckStatus;
+ }
+
+ BuildBackBuffer (UsbDisplayLinkDev, BltBuffer, BltOperation, SourceX, SourceY, DestinationX, DestinationY, Width, Height, BltBufferStride, This->Mode->Info->PixelsPerScanLine);
+
+ gBS->RestoreTPL (OriginalTPL);
+ return EFI_SUCCESS;
+}
+
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c
new file mode 100644
index 000000000000..2a7886ed6523
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c
@@ -0,0 +1,144 @@
+/**
+ * @file UsbDescriptors.c
+ * @brief Functions to read USB Interface and Capabilities descriptors
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+#include "UsbDescriptors.h"
+
+/**
+ *
+ * @param UsbIo
+ * @param descriptorType
+ * @param index
+ * @param Buffer
+ * @param Length
+ * @param UsbStatus
+ * @return
+ */
+STATIC EFI_STATUS
+ReadDescriptor (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ UINT8 DescriptorType,
+ UINT8 Index,
+ VOID* Buffer,
+ UINT16 Length,
+ UINT32* UsbStatus)
+{
+ EFI_STATUS Status;
+
+ UINT8 Header[2] = {0, 0};
+
+ EFI_USB_DEVICE_REQUEST Request;
+ ZeroMem (&Request, sizeof (Request));
+ Request.RequestType = USB_ENDPOINT_DIR_IN | USB_REQ_TYPE_STANDARD | USB_TARGET_DEVICE;
+ Request.Request = USB_REQ_GET_DESCRIPTOR;
+ Request.Index = 0;
+ Request.Value = DescriptorType << 8 | Index;
+ Request.Length = sizeof (Header);
+
+ // Read the descriptor header to see how many bytes it contains
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &Request,
+ EfiUsbDataIn,
+ DISPLAYLINK_USB_CTRL_TIMEOUT,
+ Header,
+ sizeof (Header),
+ UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to read length of descriptor type x%x, index %u (code %r, USB status x%x)\n",
+ DescriptorType, Index, Status, *UsbStatus));
+ return Status;
+ }
+ CONST UINT16 TotalLength = Header[0];
+
+ // Now we know the size of it, we can read the entire descriptor
+ Request.Length = TotalLength;
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &Request,
+ EfiUsbDataIn,
+ DISPLAYLINK_USB_CTRL_TIMEOUT,
+ Buffer,
+ TotalLength,
+ UsbStatus);
+
+ return Status;
+}
+
+/**
+ Perform a USB control transfer to read the DisplayLink vendor descriptor.
+
+ @param UsbIo Pointer to the instance of the USBIO protocol
+ @param Buffer Pointer to the buffer where descriptor should be written
+ @param Length Length of buffer (and the maximum amount of descriptor data that shall be read)
+
+ @retval EFI_SUCCESS The descriptor has been copied into Buffer
+ @retval Other The descriptor could not be read
+**/
+EFI_STATUS
+ReadCapabilitiesDescriptor (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ OUT VOID* Buffer,
+ IN UINT16 Length)
+{
+ UINT32 UsbStatus;
+ EFI_STATUS Status;
+
+ Status = ReadDescriptor (
+ UsbIo,
+ DESCRIPTOR_TYPE_DIRECTFB_CAPABILITY,
+ 0,
+ Buffer,
+ Length,
+ &UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Could not read capabilities descriptor from DL device (code %r, USB status x%x). Unrecognised firmware version?\n", Status, UsbStatus));
+ }
+
+ return Status;
+}
+
+
+/**
+ An alternative to the UBSIO protocol function EFI_USB_IO_GET_INTERFACE_DESCRIPTOR.
+ This version allows you to specify an index.
+ * @param UsbIo Pointer to the instance of the USBIO protocol
+ * @param interfaceDescriptor Where the descriptor should be written
+ * @param index The index of the descriptor required (the standard USBIO function doesn't let you do this)
+ * @return
+ */
+EFI_STATUS
+UsbDisplayLinkGetInterfaceDescriptor (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ OUT EFI_USB_INTERFACE_DESCRIPTOR* InterfaceDescriptor,
+ UINT8 Index
+ )
+{
+ UINT32 UsbStatus;
+ EFI_STATUS Status;
+
+ Status = ReadDescriptor (
+ UsbIo,
+ USB_DESC_TYPE_INTERFACE,
+ Index,
+ InterfaceDescriptor,
+ sizeof (EFI_USB_INTERFACE_DESCRIPTOR),
+ &UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "USB control transfer failed while reading interface descriptor (code %r, USB status x%x)\n", Status, UsbStatus));
+ }
+
+ return Status;
+}
+
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c
new file mode 100644
index 000000000000..99be66a9bd2d
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c
@@ -0,0 +1,1109 @@
+/**
+ * @file UsbDisplayLink.c
+ * @brief USB DisplayLink Driver that manages USB DisplayLink device and produces Graphics Output Protocol
+ * This file implements the functions of the Driver Binding / Start / Stop / Unload interface
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/PrintLib.h>
+#include <Protocol/HiiFont.h>
+
+#include "Edid.h"
+#include "UsbDescriptors.h"
+
+//
+// Functions of Driver Binding Protocol
+//
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+);
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+);
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+);
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+);
+
+
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverCombinedGopBindingEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable,
+ IN EFI_HANDLE DriverBindingHandle
+);
+
+
+// Generated with https://www.guidgen.com/ - "B70E5A79-C6D6-4267-B02E-9108C989E287"
+EFI_GUID gEfiDlGopVariableGuid = { 0xB70E5A79, 0xC6D6, 0x4267,{ 0xB0, 0x2E, 0x91, 0x08, 0xC9, 0x89, 0xE2, 0x87 } };
+
+
+EFI_DRIVER_BINDING_PROTOCOL gUsbDisplayLinkDriverBinding = {
+ UsbDisplayLinkDriverBindingSupported,
+ UsbDisplayLinkDriverBindingStart,
+ UsbDisplayLinkDriverBindingStop,
+ INF_DRIVER_VERSION,
+ NULL,
+ NULL
+};
+
+
+/**
+ * Reads integer environment variable with default fallback.
+ * @param variableName variable name to read
+ * @param defaultValue default value to return if requested not found
+ */
+STATIC UINT32
+ReadEnvironmentInt (
+ CONST wchar_t* VariableName,
+ UINT32 DefaultValue
+ )
+{
+ UINT32 Result;
+ UINTN DataSize;
+ DataSize = sizeof (Result);
+ CONST EFI_STATUS Status = gRT->GetVariable ((CHAR16*)VariableName, &gEfiDlGopVariableGuid, (UINT32*)NULL, &DataSize, &Result);
+ if (!EFI_ERROR (Status) && (sizeof (Result) == DataSize)) {
+ return Result;
+ }
+ return DefaultValue;
+}
+
+/**
+* Reads boolean environment variable with default fallback.
+* @param variableName variable name to read
+* @param defaultValue default value to return if requested not found
+*/
+STATIC BOOLEAN
+ReadEnvironmentBool (
+ CONST wchar_t* VariableName,
+ BOOLEAN DefaultValue
+ )
+{
+ return ReadEnvironmentInt (VariableName, DefaultValue ? 1 : 0) == 1;
+}
+
+
+/**
+*
+* @param UsbDisplayLinkDev
+* @return
+*/
+STATIC EFI_STATUS
+InitializeUsbDisplayLinkDevice (
+ IN OUT USB_DISPLAYLINK_DEV *UsbDisplayLinkDev
+)
+{
+ EFI_GRAPHICS_OUTPUT_PROTOCOL* Gop;
+ Gop = &UsbDisplayLinkDev->GraphicsOutputProtocol;
+ Gop->QueryMode = DisplayLinkQueryMode;
+ Gop->SetMode = DisplayLinkSetMode;
+ Gop->Blt = DisplayLinkBlt;
+
+ //
+ // Allocate buffer for Graphics Output Protocol mode information
+ //
+ Gop->Mode = (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE));
+ if (Gop->Mode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Gop->Mode->Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
+ if (Gop->Mode->Info == NULL) {
+ FreePool (Gop->Mode);
+ Gop->Mode = NULL;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Gop->Mode->MaxMode = MAX(1, DlEdidGetNumSupportedModesInEdid (UsbDisplayLinkDev->EdidActive.Edid, UsbDisplayLinkDev->EdidActive.SizeOfEdid));
+
+ Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
+ Gop->Mode->Info->Version = 0;
+ // TODO - Initialising the horizontal resolution prevents certain BIOSs from hanging on boot, but
+ // it is not yet clear why. See bug 28194.
+ Gop->Mode->Info->HorizontalResolution = DlVideoModeGetSupportedVideoMode (0)->HActive;
+ Gop->Mode->Info->VerticalResolution = 0;
+ Gop->Mode->Info->PixelFormat = PixelBltOnly;
+ Gop->Mode->Info->PixelsPerScanLine = 0;
+ Gop->Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+ Gop->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS)(UINTN)NULL;
+ Gop->Mode->FrameBufferSize = 0;
+
+ // Prevent DlGopSendScreenUpdate from running until we are sure that the video mode is set
+ UsbDisplayLinkDev->LastY2 = 0;
+ UsbDisplayLinkDev->LastY1 = (UINTN)-1;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Look for alternate settings for the UsbIo handle's interface
+ which offer protocol DL_PROTOCOL_DIRECT_FB.
+
+ @retval -1 Not found
+ @retval Other The alternate setting
+**/
+STATIC INTN
+GetDirectFbAltSetting (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINT8 ParentInterfaceNumber
+ )
+{
+ EFI_STATUS Status;
+ INTN AltSettingIndex;
+ UINT16 InterfaceIndex;
+
+ AltSettingIndex = -1;
+
+ for (InterfaceIndex = 0; InterfaceIndex <= 0xFF; InterfaceIndex++) {
+ EFI_USB_INTERFACE_DESCRIPTOR interfaceDescriptor;
+ Status = UsbDisplayLinkGetInterfaceDescriptor (UsbIo, &interfaceDescriptor, (UINT8)InterfaceIndex);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (interfaceDescriptor.InterfaceNumber == ParentInterfaceNumber &&
+ (interfaceDescriptor.InterfaceClass == CLASS_VENDOR) &&
+ interfaceDescriptor.InterfaceProtocol == INTERFACE_PROTOCOL_DIRECT_FB) {
+ AltSettingIndex = interfaceDescriptor.AlternateSetting;
+ break;
+ }
+ }
+ return AltSettingIndex;
+}
+
+/**
+ *
+ * @param UsbIo
+ * @param altSettingIndex
+ * @return
+ */
+STATIC EFI_STATUS
+SelectAltSetting (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINTN AltSettingIndex)
+{
+ // Set alternate setting 1 on the interface
+ EFI_STATUS Status;
+ UINT32 UsbStatus;
+ EFI_USB_DEVICE_REQUEST Request;
+ ZeroMem (&Request, sizeof (Request));
+ Request.RequestType = USB_REQ_TYPE_STANDARD | USB_TARGET_INTERFACE;
+ Request.Request = USB_REQ_SET_INTERFACE;
+ Request.Index = DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO;
+ Request.Value = (UINT16)AltSettingIndex;
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &Request,
+ EfiUsbNoData,
+ DISPLAYLINK_USB_CTRL_TIMEOUT,
+ NULL,
+ 0,
+ &UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ DEBUG ((DEBUG_ERROR, "USB control transfer failed while attempting to select alt setting %d on interface (code %r, USB status x%x). DisplayLink device has unsupported firmware version?\n", AltSettingIndex, Status, UsbStatus));
+ }
+ return Status;
+}
+
+
+/**
+ Report whether the driver can support the device attached via UsbIo
+ by seeing what if any capabilities it reports.
+
+ @retval TRUE Device has sufficient capabilities for this driver.
+ @retval FALSE Device lacks sufficient capabilities.
+**/
+STATIC BOOLEAN
+CapabilitiesSupported (
+ IN EFI_USB_IO_PROTOCOL *UsbIo
+ )
+{
+ UINT8 Buffer[256];
+ EFI_STATUS Status;
+
+ Status = ReadCapabilitiesDescriptor (UsbIo, Buffer, sizeof (Buffer));
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ VendorDescriptor descriptor;
+ Status = UsbDisplayLinkParseCapabilitiesDescriptor (Buffer, sizeof (Buffer), &descriptor);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to parse capabilities descriptor (code %r)\n", Status));
+ return FALSE;
+ }
+
+ return UsbDisplayLinkCapabilitiesSufficientToBind (&descriptor);
+}
+
+
+/**
+ *
+ * @param UsbIo
+ * @param InterfaceDescriptor
+ * @param altSettingIndex
+ * @return
+ */
+STATIC BOOLEAN
+IsDLDirectFbCapableInterface (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor,
+ IN INTN *AltSettingIndex)
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
+
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DeviceDescriptor);
+
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ if (DeviceDescriptor.IdVendor != VENDOR_DISPLAYLINK) {
+ return FALSE;
+ }
+
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, InterfaceDescriptor);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ // We can assume that the interface that we want to talk to - the NIVO interface - is number 0
+ if (InterfaceDescriptor->InterfaceNumber != DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO) {
+ return FALSE;
+ }
+
+ // Check if we have an interface (alt setting) descriptor with the correct interface protocol
+ *AltSettingIndex = GetDirectFbAltSetting (UsbIo, InterfaceDescriptor->InterfaceNumber);
+
+ if (*AltSettingIndex == -1) {
+ DEBUG ((DEBUG_ERROR, "DisplayLink GOP: Failed to find setting on device which supports GOP functionality. Check firmware / device version?\n"));
+ return FALSE;
+ }
+
+ // Now check that the capabilities that we need are properly supported
+ if (CapabilitiesSupported (UsbIo) == FALSE) {
+ DEBUG ((DEBUG_ERROR, "DisplayLink GOP: DL device detected, but it doesn't support the required GOP features. Check firmware / device version?\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * Prints a block of text in the framebuffer (helper function).
+ * @param X x coordinate
+ * @param Y y coordinate
+ */
+STATIC VOID
+DisplayLinkPrintTextToScreenInternal (
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
+ UINTN X,
+ UINTN Y,
+ IN CHAR16 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_FONT_PROTOCOL *HiiFont;
+ EFI_IMAGE_OUTPUT *Blt;
+ EFI_FONT_DISPLAY_INFO FontInfo;
+ EFI_HII_OUT_FLAGS Flags;
+
+ Blt = (EFI_IMAGE_OUTPUT*)NULL;
+
+ Status = gBS->LocateProtocol (&gEfiHiiFontProtocolGuid, NULL, (VOID **)&HiiFont);
+ if (!EFI_ERROR (Status)) {
+ Blt = (EFI_IMAGE_OUTPUT*)AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT));
+ Blt->Width = (UINT16)GraphicsOutput->Mode->Info->HorizontalResolution;
+ Blt->Height = (UINT16)GraphicsOutput->Mode->Info->VerticalResolution;
+ Blt->Image.Screen = GraphicsOutput;
+
+ ZeroMem (&FontInfo, sizeof (EFI_FONT_DISPLAY_INFO));
+ FontInfo.ForegroundColor.Red = 0;
+ FontInfo.ForegroundColor.Green = 0;
+ FontInfo.ForegroundColor.Blue = 0;
+ FontInfo.BackgroundColor.Red = 0xff;
+ FontInfo.BackgroundColor.Green = 0xff;
+ FontInfo.BackgroundColor.Blue = 0xff;
+
+ Flags = EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_OUT_FLAG_CLIP |
+ EFI_HII_OUT_FLAG_CLIP_CLEAN_X | EFI_HII_OUT_FLAG_CLIP_CLEAN_Y |
+ EFI_HII_IGNORE_LINE_BREAK | EFI_HII_DIRECT_TO_SCREEN;
+
+ Status = HiiFont->StringToImage (
+ HiiFont,
+ Flags,
+ Buffer,
+ &FontInfo,
+ &Blt,
+ X,
+ Y,
+ (EFI_HII_ROW_INFO**)NULL,
+ (UINTN*)NULL,
+ (UINTN*)NULL);
+ }
+
+ if (Blt != NULL) {
+ FreePool (Blt);
+ }
+}
+
+
+/**
+* Prints a block of text in the framebuffer.
+* @param X x coordinate
+* @param Y y coordinate
+* @param Format string format similar to stdlib's vsnprintf
+* @param ... arguments
+*/
+VOID
+DlGopPrintTextToScreen (
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
+ UINTN X,
+ UINTN Y,
+ IN CONST CHAR16 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ CHAR16 *Buffer;
+ UINTN BufferSize;
+
+ ASSERT (Format != NULL);
+ ASSERT (((UINTN) Format & BIT0) == 0);
+
+ VA_START(Marker, Format);
+
+ BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);
+
+ Buffer = (CHAR16*)AllocatePool (BufferSize);
+ ASSERT (Buffer != NULL);
+
+ UnicodeVSPrint (Buffer, BufferSize, Format, Marker);
+
+ VA_END(Marker);
+
+ DisplayLinkPrintTextToScreenInternal (GraphicsOutput, X, Y, Buffer);
+
+ FreePool (Buffer);
+}
+
+
+/**
+ * Sometimes platforms only write to the first GOP device that they find. Enabling this function allows us to copy the pixels from this device.
+ * @param UsbDisplayLinkDev
+ */
+#ifdef COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
+STATIC VOID
+DisplayLinkCopyFromPrimaryGopDevice (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
+ )
+{
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleIndex;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
+
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiGraphicsOutputProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer);
+
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+ gBS->HandleProtocol (HandleBuffer[HandleIndex], &gEfiGraphicsOutputProtocolGuid, (VOID**)&Gop);
+ if (Gop != &UsbDisplayLinkDev->GraphicsOutputProtocol && Gop->Mode->FrameBufferBase != (EFI_PHYSICAL_ADDRESS)(UINTN)NULL) {
+
+#if 0
+ if (UsbDisplayLinkDev->UseFixedMode) {
+ if ((Gop->Mode->Info->HorizontalResolution != UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution)
+ || (Gop->Mode->Info->VerticalResolution != UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution))
+ {
+ DisplayLinkSetMode (&UsbDisplayLinkDev->GraphicsOutputProtocol, GetNearestMode (Gop->Mode->Info->HorizontalResolution, Gop->Mode->Info->VerticalResolution));
+ }
+ }
+
+ // Check to see if we should skip this copy because there is corruption on the screen
+ if (Gop->Mode->Info->HorizontalResolution == 1024 && Gop->Mode->Info->VerticalResolution == 768) {
+ CONST UINT32 corruptionCrc = 0x6BB19C15;
+ UINT32 lineCrc = corruptionCrc;
+ UINTN line;
+ for (line = 15; line < 25 && lineCrc == corruptionCrc; line++) {
+ lineCrc = 0;
+ gBS->CalculateCrc32 ((char*)Gop->Mode->FrameBufferBase + (line * 4 * 1024), 1024, &lineCrc);
+ }
+ if (lineCrc == corruptionCrc) {
+ if (UsbDisplayLinkDev->ShowDebug) {
+ DlGopPrintTextToScreen (&UsbDisplayLinkDev->GraphicsOutputProtocol, 32, 196, (CONST CHAR16*)L"Skipping Copy");
+ }
+ break;
+ }
+ }
+#endif
+
+ // See if we need to do a screen update - calculate a really noddy hash to see if any screen updates have happened.
+ STATIC UINT32 prevframeBufferHash = 0; // 4 bytes per pixel
+ UINT32 currFrameBufferHash = 0;
+ UINTN i;
+ for (i = 0; i < (Gop->Mode->Info->HorizontalResolution * Gop->Mode->Info->VerticalResolution); i++) {
+ currFrameBufferHash += ((UINT32*)(UINTN)Gop->Mode->FrameBufferBase)[i];
+ }
+
+ if (currFrameBufferHash != prevframeBufferHash) {
+ prevframeBufferHash = currFrameBufferHash;
+
+ DisplayLinkBlt (
+ &UsbDisplayLinkDev->GraphicsOutputProtocol,
+ (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(UINTN)Gop->Mode->FrameBufferBase,
+ EfiBltBufferToVideo,
+ 0,
+ 0,
+ 0,
+ 0,
+ Gop->Mode->Info->HorizontalResolution,
+ Gop->Mode->Info->VerticalResolution,
+ 0);
+ }
+ break;
+ }
+ }
+
+ FreePool (HandleBuffer);
+}
+#endif // COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
+
+
+/**
+ * TODO: Verify if it's true!
+ * Exit from boot services: signal handler.
+ */
+STATIC VOID
+EFIAPI
+DisplayLinkDriverExitBootServices (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
+ UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)Context;
+
+ gBS->CloseEvent (UsbDisplayLinkDev->TimerEvent);
+}
+
+/**
+ * Periodic screen update: timer callback.
+ */
+VOID
+EFIAPI
+DisplayLinkPeriodicTimer (
+ IN EFI_EVENT Event,
+ IN VOID* Context
+ )
+{
+ EFI_STATUS Status;
+ USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
+
+ Status = EFI_SUCCESS;
+ UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)Context;
+
+ // Drop out if we haven't set the video mode up yet
+ if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Mode == GRAPHICS_OUTPUT_INVALID_MODE_NUMBER) {
+ // Restart the one-shot timer to poll the status again.
+ Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to create timer.\n"));
+ }
+ return;
+ }
+
+#ifdef COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
+ DisplayLinkCopyFromPrimaryGopDevice (UsbDisplayLinkDev);
+#endif // COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
+
+ if (UsbDisplayLinkDev->ShowBandwidth) {
+ STATIC UINTN Count = 0;
+
+ if (Count++ % 50 == 0) {
+ DlGopPrintTextToScreen (&UsbDisplayLinkDev->GraphicsOutputProtocol, 32, 48, (CONST CHAR16*)L" Bandwidth: %d MB/s ", UsbDisplayLinkDev->DataSent * 10000000 / DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD / 50 / 1024 / 1024);
+ UsbDisplayLinkDev->DataSent = 0;
+ }
+ }
+
+ if (UsbDisplayLinkDev->ShowTestPattern)
+ {
+ if (UsbDisplayLinkDev->ShowTestPattern == 5) {
+ DlGopSendTestPattern (UsbDisplayLinkDev, 0);
+ } else if (UsbDisplayLinkDev->ShowTestPattern >= 10) {
+ DlGopSendTestPattern (UsbDisplayLinkDev, 1);
+ UsbDisplayLinkDev->ShowTestPattern = 0;
+ }
+ UsbDisplayLinkDev->ShowTestPattern++;
+
+ }
+
+ // Send the latest version of the frame buffer to the DL device over USB
+ DlGopSendScreenUpdate (UsbDisplayLinkDev);
+
+ // Restart the timer now we've finished
+ Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to create timer.\n"));
+ }
+}
+
+/* ****************************************************************************************************** */
+/* ****************************************************************************************************** */
+/* ****************** START OF FUNCTIONS WHICH IMPLEMENT DRIVER BINDING INTERFACE ****************** */
+/* ****************************************************************************************************** */
+/* ****************************************************************************************************** */
+
+/**
+ Check whether USB DisplayLink driver supports this device.
+
+ @param This The USB DisplayLink driver binding protocol.
+ @param Controller The controller handle to check.
+ @param RemainingDevicePath The remaining device path.
+
+ @retval EFI_SUCCESS The driver supports this controller.
+ @retval other This device isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Use the USB I/O Protocol interface to check whether Controller is
+ // a DisplayLink device that can be managed by this driver.
+ //
+ Status = EFI_UNSUPPORTED;
+ EFI_USB_INTERFACE_DESCRIPTOR DummyInterfaceDescriptor;
+ INTN DummyAltSettingIndex;
+
+ if (IsDLDirectFbCapableInterface (UsbIo, &DummyInterfaceDescriptor, &DummyAltSettingIndex)){
+ Status = EFI_SUCCESS;
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller);
+
+ return Status;
+}
+
+
+/**
+ Starts the DisplayLink device with this driver.
+
+ This function consumes USB I/O Protocol, intializes USB DisplayLink device,
+ installs Graphics Output Protocol
+ Transfer to manage the USB DisplayLink device.
+
+ @param This The USB DisplayLink driver binding instance.
+ @param Controller Handle of device to bind driver to.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_UNSUPPORTED This driver does not support this device.
+ @retval EFI_DEVICE_ERROR This driver cannot be started due to device Error.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ALREADY_STARTED This driver has been started.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
+ UINT8 EndpointNumber;
+ EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
+ UINT8 Index;
+ BOOLEAN FoundOut;
+ BOOLEAN FoundIn;
+ EFI_TPL OriginalTPL;
+ INTN altSettingIndex;
+
+ OriginalTPL = gBS->RaiseTPL (TPL_CALLBACK);
+
+ UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)AllocateZeroPool (sizeof (USB_DISPLAYLINK_DEV));
+ if (UsbDisplayLinkDev == NULL) {
+ DEBUG ((DEBUG_ERROR, "Device initialialisation - Failed to allocate memory for device.\n"));
+ gBS->RestoreTPL (OriginalTPL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbDisplayLinkDev->Signature = USB_DISPLAYLINK_DEV_SIGNATURE;
+
+ UsbDisplayLinkDev->ShowBandwidth = ReadEnvironmentBool (L"DisplayLinkShowBandwidth", FALSE);
+ UsbDisplayLinkDev->ShowTestPattern = ReadEnvironmentBool (L"DisplayLinkShowTestPatterns", FALSE);
+
+ //
+ // Open USB I/O Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbDisplayLinkDev->UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to open usbio protocol. Is USB correctly supported on this platform?.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto ErrorExit2;
+ }
+
+ if (!IsDLDirectFbCapableInterface (UsbDisplayLinkDev->UsbIo, &UsbDisplayLinkDev->InterfaceDescriptor, &altSettingIndex)) {
+ Status = EFI_UNSUPPORTED;
+ goto ErrorExit4;
+ }
+
+ Status = SelectAltSetting (UsbDisplayLinkDev->UsbIo, altSettingIndex);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DisplayLink GOP: Failed to select alternate setting.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto ErrorExit4;
+ }
+
+ //
+ // Parse endpoint descriptor
+ //
+ EndpointNumber = UsbDisplayLinkDev->InterfaceDescriptor.NumEndpoints;
+
+ //
+ // Traverse endpoints to find bulk endpoint
+ //
+ FoundOut = FALSE;
+ FoundIn = FALSE;
+ for (Index = 0; Index < EndpointNumber; Index++) {
+ UsbDisplayLinkDev->UsbIo->UsbGetEndpointDescriptor (
+ UsbDisplayLinkDev->UsbIo,
+ Index,
+ &EndpointDescriptor);
+
+ if ((EndpointDescriptor.Attributes & (BIT0 | BIT1)) == USB_ENDPOINT_BULK) {
+ if (!FoundOut && (EndpointDescriptor.EndpointAddress & BIT7) == 0) {
+ CopyMem (&UsbDisplayLinkDev->BulkOutEndpointDescriptor, &EndpointDescriptor, sizeof (EndpointDescriptor));
+ FoundOut = TRUE;
+ } else if (!FoundIn && (EndpointDescriptor.EndpointAddress & BIT7) == BIT7) {
+ CopyMem (&UsbDisplayLinkDev->BulkInEndpointDescriptor, &EndpointDescriptor, sizeof (EndpointDescriptor));
+ FoundIn = TRUE;
+ }
+ }
+ }
+
+ if (FoundOut == FALSE) {
+ Status = EFI_UNSUPPORTED;
+ DEBUG ((DEBUG_ERROR, "No endpoints found. Num endpoints searched = %d.\n", EndpointNumber));
+ goto ErrorExit4;
+ }
+
+ Status = DlReadEdid (UsbDisplayLinkDev);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to read monitor EDID from DisplayLink device (code %r)\n", Status));
+ goto ErrorExit7;
+ }
+
+ Status = InitializeUsbDisplayLinkDevice (UsbDisplayLinkDev);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to initialise DisplayLink device (code %r)\n", Status));
+ goto ErrorExit7;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiGraphicsOutputProtocolGuid,
+ &UsbDisplayLinkDev->GraphicsOutputProtocol,
+ &gEfiEdidDiscoveredProtocolGuid,
+ &UsbDisplayLinkDev->EdidDiscovered,
+ &gEfiEdidActiveProtocolGuid,
+ &UsbDisplayLinkDev->EdidActive,
+ NULL);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to install Graphics Output and EDID protocol interfaces - driver not installed correctly - %r\n", Status));
+ goto ErrorExit8;
+ }
+
+ UsbDisplayLinkDev->ControllerNameTable = (EFI_UNICODE_STRING_TABLE*)NULL;
+
+ AddUnicodeString2 (
+ "eng",
+ mUsbDisplayLinkComponentName.SupportedLanguages,
+ &UsbDisplayLinkDev->ControllerNameTable,
+ (CONST CHAR16*)L"Generic Usb DisplayLink",
+ TRUE);
+
+ AddUnicodeString2 (
+ "en",
+ mUsbDisplayLinkComponentName2.SupportedLanguages,
+ &UsbDisplayLinkDev->ControllerNameTable,
+ (CONST CHAR16*)L"Generic Usb DisplayLink",
+ FALSE);
+
+ //
+ // Setup a periodic timer
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ DisplayLinkPeriodicTimer,
+ UsbDisplayLinkDev,
+ &UsbDisplayLinkDev->TimerEvent);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "Failed to create screeen update polling event.\n"));
+ goto ErrorExit8;
+ }
+
+ // Start one-shot timer. The rendering operations can take quite a long time, so we
+ // don't want another timer event to happen until we have finished; so we'll restart
+ // the timer from DisplayLinkPeriodicTimer, the event handler function.
+ Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "Failed to create screen update polling timer.\n"));
+ goto ErrorExit8;
+ }
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ DisplayLinkDriverExitBootServices,
+ UsbDisplayLinkDev,
+ &gEfiEventExitBootServicesGuid,
+ &UsbDisplayLinkDev->DriverExitBootServicesEvent);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "Failed to create event for bootexit.\n"));
+ goto ErrorExit8;
+ }
+
+ gBS->RestoreTPL (OriginalTPL);
+
+ DEBUG ((DEBUG_INFO, "DisplayLink GOP driver successfully bound to device.\n"));
+
+ return EFI_SUCCESS;
+
+ //
+ // Error handler
+ //
+ ErrorExit8:
+ ErrorExit7:
+ ErrorExit4:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller);
+
+ ErrorExit2:
+ if (UsbDisplayLinkDev != NULL) {
+ FreePool (UsbDisplayLinkDev);
+ UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)NULL;
+ }
+
+ DEBUG ((DEBUG_ERROR, "Exiting - Failed to initialise driver.\n"));
+
+ gBS->RestoreTPL (OriginalTPL);
+ return Status;
+}
+
+/**
+Entrypoint of USB DisplayLink Driver.
+
+This function is the entrypoint of a combined USB DisplayLink GOP Driver. It installs Driver Binding
+Protocols together with Component Name Protocols.
+
+@param ImageHandle The firmware allocated handle for the EFI image.
+@param SystemTable A pointer to the EFI System Table.
+@param DriverBindingHandle The Driver binding handle
+
+@retval EFI_SUCCESS The entry point is executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverCombinedGopBindingEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable,
+ IN EFI_HANDLE DriverBindingHandle
+)
+{
+ EFI_STATUS Status;
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gUsbDisplayLinkDriverBinding,
+ DriverBindingHandle,
+ &mUsbDisplayLinkComponentName,
+ &mUsbDisplayLinkComponentName2);
+
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+Entrypoint of USB DisplayLink Driver.
+
+This function is the entrypoint of USB DisplayLink Driver. It installs Driver Binding
+Protocols together with Component Name Protocols.
+
+@param ImageHandle The firmware allocated handle for the EFI image.
+@param SystemTable A pointer to the EFI System Table.
+
+@retval EFI_SUCCESS The entry point is executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+)
+{
+ return UsbDisplayLinkDriverCombinedGopBindingEntryPoint (ImageHandle, SystemTable, ImageHandle);
+}
+
+
+/**
+Unloads an image.
+@param ImageHandle Handle that identifies the image to be unloaded.
+@retval EFI_SUCCESS The image has been unloaded.
+@retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverCombinedGopUnload (
+ IN EFI_HANDLE ImageHandle
+)
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ EFI_STATUS handleDisconnectStatus;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+
+ //
+ // Retrieve array of all handles in the handle database
+ //
+ handleDisconnectStatus = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (! EFI_ERROR (handleDisconnectStatus)) {
+ //
+ // Disconnect the current driver from handles in the handle database
+ //
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->DisconnectController (HandleBuffer[Index], gImageHandle, NULL);
+ }
+ //
+ // Free the array of handles
+ //
+ if (HandleBuffer != NULL)
+ {
+ FreePool (HandleBuffer);
+ }
+ }
+
+ // Even if we didn't manage to disconnect the handles, try to uninstall the protocols
+ //
+ // Uninstall protocols installed in the driver entry point
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gUsbDisplayLinkDriverBinding,
+ &gEfiComponentNameProtocolGuid,
+ &mUsbDisplayLinkComponentName,
+ &gEfiComponentName2ProtocolGuid,
+ &mUsbDisplayLinkComponentName2,
+ NULL
+ );
+
+ if (EFI_ERROR (handleDisconnectStatus))
+ {
+ return handleDisconnectStatus;
+ }
+ return Status;
+}
+
+
+/**
+ Stop the USB DisplayLink device handled by this driver.
+
+ @param This The USB DisplayLink driver binding protocol.
+ @param Controller The controller to release.
+ @param NumberOfChildren The number of handles in ChildHandleBuffer.
+ @param ChildHandleBuffer The array of child handle.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_UNSUPPORTED Simple Pointer Protocol is not installed on Controller.
+ @retval Others Fail to uninstall protocols attached on the device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbDisplayLinkDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutputProtocol;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiGraphicsOutputProtocolGuid,
+ (VOID **) &GraphicsOutputProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(GraphicsOutputProtocol);
+
+ // Reset the video mode to clear the display. Don't drop out if there is a problem, just press on.
+ // Note that this will also clear the frame buffer, as the screen buffer will be re-allocated with AllocateZeroPool.
+ if ((GraphicsOutputProtocol->Mode != NULL) &&
+ (GraphicsOutputProtocol->Mode->Mode != GRAPHICS_OUTPUT_INVALID_MODE_NUMBER)) {
+ Status = DisplayLinkSetMode (GraphicsOutputProtocol, GraphicsOutputProtocol->Mode->Mode);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "Driver stop - Problem resetting video mode - %r.\n", Status));
+ }
+ }
+
+ // Reset the alt setting on the interface (to the DL3 alt setting)
+ Status = SelectAltSetting (UsbDisplayLinkDev->UsbIo, 0);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "Error resetting USB interface alternate setting - %r.\n", Status));
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiGraphicsOutputProtocolGuid,
+ &UsbDisplayLinkDev->GraphicsOutputProtocol,
+ &gEfiEdidDiscoveredProtocolGuid,
+ &UsbDisplayLinkDev->EdidDiscovered,
+ &gEfiEdidActiveProtocolGuid,
+ &UsbDisplayLinkDev->EdidActive,
+ NULL);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "Error uninstalling Graphics Output and EDID protocol interfaces - %r.\n", Status));
+ return Status;
+ }
+
+ gBS->CloseEvent (UsbDisplayLinkDev->TimerEvent);
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller);
+
+ //
+ // Free all resources.
+ //
+ if (UsbDisplayLinkDev->ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (UsbDisplayLinkDev->ControllerNameTable);
+ }
+
+ if (UsbDisplayLinkDev->Screen != NULL) {
+ FreePool (UsbDisplayLinkDev->Screen);
+ UsbDisplayLinkDev->Screen = NULL;
+ }
+
+ if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode) {
+ if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info) {
+ FreePool (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info);
+ UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info = NULL;
+ }
+ FreePool (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode);
+ UsbDisplayLinkDev->GraphicsOutputProtocol.Mode = NULL;
+ }
+
+ FreePool (UsbDisplayLinkDev);
+
+ return EFI_SUCCESS;
+
+}
+
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c
new file mode 100644
index 000000000000..252293da39d4
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c
@@ -0,0 +1,180 @@
+/**
+ * @file UsbTransfer.c
+ * @brief Wrapper of UEFI USB bulk and control transfer interface for USB DisplayLink driver.
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+
+/**
+ * Write the data to the DisplayLink device using the USBIO protocol.
+ * @param UsbDisplayLinkDev
+ * @param Buffer
+ * @param DataLen
+ * @param USBStatus
+ * @return
+ * EFI_SUCCESS The bulk transfer has been successfully executed.
+ * EFI_INVALID_PARAMETER If DeviceEndpoint is not valid.
+ * EFI_INVALID_PARAMETER Data is NULL.
+ * EFI_INVALID_PARAMETER DataLength is NULL.
+ * EFI_INVALID_PARAMETER Status is NULL.
+ * EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ * EFI_TIMEOUT The bulk transfer cannot be completed within Timeout timeframe.
+ * EFI_DEVICE_ERROR The transfer failed other than timeout, and the transfer status is returned in Status.
+ */
+EFI_STATUS
+DlUsbBulkWrite (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ IN CONST UINT8* Buffer,
+ IN UINTN DataLen,
+ OUT UINT32 *USBStatus
+ )
+{
+ EFI_STATUS Status;
+ Status = UsbDisplayLinkDev->UsbIo->UsbBulkTransfer (
+ UsbDisplayLinkDev->UsbIo,
+ UsbDisplayLinkDev->BulkOutEndpointDescriptor.EndpointAddress,
+ (VOID*)Buffer,
+ &DataLen,
+ DISPLAYLINK_USB_BULK_TIMEOUT,
+ USBStatus);
+
+ return Status;
+}
+
+/**
+* Read data from the DisplayLink device using the USBIO protocol.
+* @param UsbDisplayLinkDev
+* @param Buffer
+* @param BufferLen
+* @return 0 if an error occurred or 0 bytes were read, otherwise the number of bytes read
+ */
+UINTN
+DlUsbBulkRead (
+ IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
+ IN UINT8* Buffer,
+ IN UINTN BufferLen
+ )
+{
+ UINT32 Result;
+ UINTN ReadLen;
+ EFI_STATUS Status;
+
+ ReadLen = BufferLen;
+
+ Status = UsbDisplayLinkDev->UsbIo->UsbBulkTransfer (
+ UsbDisplayLinkDev->UsbIo,
+ UsbDisplayLinkDev->BulkInEndpointDescriptor.EndpointAddress,
+ Buffer,
+ &ReadLen,
+ DISPLAYLINK_USB_BULK_TIMEOUT,
+ &Result);
+
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ return ReadLen;
+}
+
+
+/**
+Send a control message (e.g set video mode) message to the DisplayLink device.
+
+@param Device USB device handle.
+@param request Request type, e.g. SET_VIDEO_MODE
+@param value
+@param controlMsg Pointer to the message to send.
+@param controlMsgLen Length of the message.
+
+@retval EFI_SUCCESS Successfully sent message.
+
+**/
+EFI_STATUS
+DlUsbSendControlWriteMessage (
+ IN USB_DISPLAYLINK_DEV *Device,
+ IN UINT8 Request,
+ IN UINT16 Value,
+ IN CONST VOID *ControlMsg,
+ IN UINT16 ControlMsgLen
+ )
+{
+ EFI_STATUS Status;
+ UINT32 UsbStatus;
+ EFI_USB_DEVICE_REQUEST UsbRequest;
+
+ ZeroMem (&Request, sizeof (Request));
+ UsbRequest.RequestType = USB_REQ_TYPE_VENDOR | USB_TARGET_INTERFACE;
+ UsbRequest.Index = Device->InterfaceDescriptor.InterfaceNumber;
+ UsbRequest.Request = Request;
+ UsbRequest.Value = Value;
+ UsbRequest.Length = ControlMsgLen;
+
+ Status = Device->UsbIo->UsbControlTransfer (
+ Device->UsbIo,
+ &UsbRequest,
+ EfiUsbDataOut,
+ DISPLAYLINK_USB_CTRL_TIMEOUT,
+ (VOID *)ControlMsg,
+ ControlMsgLen,
+ &UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "USB write control transfer failed - %r (USB status x%x).\n", Status, UsbStatus));
+ Status = EFI_DEVICE_ERROR;
+ }
+ return Status;
+}
+
+
+/**
+Request data from the DisplayLink device (e.g. the monitor EDID)
+
+@param Device USB device handle.
+@param request Request type, e.g. GET_OUTPUT_EDID
+@param value
+@param controlMsg Pointer to the message to send.
+@param controlMsgLen Length of the message.
+
+@retval EFI_SUCCESS Successfully sent message.
+
+**/
+EFI_STATUS
+DlUsbSendControlReadMessage (
+ IN USB_DISPLAYLINK_DEV *Device,
+ IN UINT8 Request,
+ IN UINT16 Value,
+ OUT VOID *ControlMsg,
+ IN UINT16 ControlMsgLen
+ )
+{
+ EFI_STATUS Status;
+ UINT32 UsbStatus;
+ EFI_USB_DEVICE_REQUEST UsbRequest;
+
+ ZeroMem (&UsbRequest, sizeof (UsbRequest));
+ UsbRequest.RequestType = USB_REQ_TYPE_VENDOR | USB_TARGET_INTERFACE | USB_ENDPOINT_DIR_IN;
+ UsbRequest.Request = Request;
+ UsbRequest.Value = Value;
+ UsbRequest.Index = Device->InterfaceDescriptor.InterfaceNumber;
+ UsbRequest.Length = ControlMsgLen;
+
+ Status = Device->UsbIo->UsbControlTransfer (
+ Device->UsbIo,
+ &UsbRequest,
+ EfiUsbDataIn,
+ DISPLAYLINK_USB_CTRL_TIMEOUT,
+ (VOID *)ControlMsg,
+ ControlMsgLen,
+ &UsbStatus);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "USB read control transfer failed - %r (USB status x%x).\n", Status, UsbStatus));
+ Status = EFI_DEVICE_ERROR;
+ }
+ return Status;
+}
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c
new file mode 100644
index 000000000000..6218c093147c
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c
@@ -0,0 +1,254 @@
+/**
+ * @file VideoModes.c
+ * @brief Pre-calculated video timings sent to the DisplayLink device when a video mode is selected
+ *
+ * Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+**/
+
+#include "UsbDisplayLink.h"
+
+
+// Supported video modes - must be in order of pixel count (i.e. hres x vres)
+
+STATIC CONST struct VideoMode ModeData[] = {
+ {
+ // 640 x 480 @ 60Hz
+ .Reserved2 = 2,
+ .PixelClock = 2517,
+ .HActive = 640,
+ .HBlanking = 160,
+ .HSyncOffset = 16,
+ .HSyncWidth = 96,
+ .VActive = 480,
+ .VBlanking = 45,
+ .VSyncOffset = 10,
+ .VSyncWidth = 2,
+ .Flags = 0x00000300,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 800 x 600 @ 60Hz
+ .Reserved2 = 2,
+ .PixelClock = 4000,
+ .HActive = 800,
+ .HBlanking = 256,
+ .HSyncOffset = 40,
+ .HSyncWidth = 128,
+ .VActive = 600,
+ .VBlanking = 28,
+ .VSyncOffset = 1,
+ .VSyncWidth = 3,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1024x768 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 6500,
+ .HActive = 1024,
+ .HBlanking = 320,
+ .HSyncOffset = 24,
+ .HSyncWidth = 136,
+ .VActive = 768,
+ .VBlanking = 38,
+ .VSyncOffset = 3,
+ .VSyncWidth = 6,
+ .Flags = 0x00000300,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1360x768 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 8550,
+ .HActive = 1360,
+ .HBlanking = 432,
+ .HSyncOffset = 64,
+ .HSyncWidth = 112,
+ .VActive = 768,
+ .VBlanking = 27,
+ .VSyncOffset = 3,
+ .VSyncWidth = 6,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1280x960 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 10800,
+ .HActive = 1280,
+ .HBlanking = 520,
+ .HSyncOffset = 96,
+ .HSyncWidth = 112,
+ .VActive = 960,
+ .VBlanking = 40,
+ .VSyncOffset = 1,
+ .VSyncWidth = 3,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1280x1024 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 10800,
+ .HActive = 1280,
+ .HBlanking = 408,
+ .HSyncOffset = 48,
+ .HSyncWidth = 112,
+ .VActive = 1024,
+ .VBlanking = 42,
+ .VSyncOffset = 1,
+ .VSyncWidth = 3,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1600x900 @ 60Hz
+ .Reserved2 = 2,
+ .PixelClock = 11825,
+ .HActive = 1600,
+ .HBlanking = 512,
+ .HSyncOffset = 88,
+ .HSyncWidth = 168,
+ .VActive = 900,
+ .VBlanking = 34,
+ .VSyncOffset = 3,
+ .VSyncWidth = 5,
+ .Flags = 0x00000500,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1400x1050 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 12175,
+ .HActive = 1400,
+ .HBlanking = 464,
+ .HSyncOffset = 88,
+ .HSyncWidth = 144,
+ .VActive = 1050,
+ .VBlanking = 39,
+ .VSyncOffset = 3,
+ .VSyncWidth = 4,
+ .Flags = 0x00000100,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1600x1200 @ 60Hz
+ .Reserved1 = 0,
+ .Reserved2 = 2,
+ .PixelClock = 16200,
+ .HActive = 1600,
+ .HBlanking = 560,
+ .HSyncOffset = 64,
+ .HSyncWidth = 192,
+ .VActive = 1200,
+ .VBlanking = 50,
+ .VSyncOffset = 1,
+ .VSyncWidth = 3,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ },
+ {
+ // 1920 x 1080
+ .Reserved2 = 2,
+ .PixelClock = 14850,
+ .HActive = 1920,
+ .HBlanking = 280,
+ .HSyncOffset = 88,
+ .HSyncWidth = 44,
+ .VActive = 1080,
+ .VBlanking = 45,
+ .VSyncOffset = 4,
+ .VSyncWidth = 5,
+ .Flags = 0x00000000,
+ .Accumulate = 1,
+ .Reserved3 = 0,
+ .Reserved4 = 0,
+ .Reserved5 = 0x00000000,
+ .Vic = 0,
+ .ActiveFormat = 0,
+ }
+};
+
+STATIC CONST UINT32 NumSupportedVideoModes = (sizeof (ModeData) / sizeof (struct VideoMode));
+
+/**
+Find the number of pre-calculated video modes that we support.
+
+@retval Number of modes.
+
+**/
+UINT32 DlVideoModeGetNumSupportedVideoModes ()
+{
+ return NumSupportedVideoModes;
+}
+
+/**
+Get one of the pre-calculated video modes
+
+@param index The video mode that we want.
+
+@retval NULL The index was out of range.
+
+**/
+CONST struct VideoMode *DlVideoModeGetSupportedVideoMode (
+ UINT32 Index
+ )
+{
+ if (Index >= NumSupportedVideoModes) {
+ return NULL;
+ }
+ return &ModeData[Index];
+}
diff --git a/Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md b/Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md
new file mode 100644
index 000000000000..40061f1eb46d
--- /dev/null
+++ b/Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md
@@ -0,0 +1,77 @@
+# DISPLAYLINK DRIVERS
+This package contains a GOP driver for Universal USB-connected docks containing the
+DisplayLink DL-6xxx chip or newer.
+
+[DisplayLink Website](http://www.displaylink.com)
+
+[Products](https://www.displaylink.com/products/universal-docking-stations)
+
+# INDEX
+
+* [Resolutions Supported](#resolutions-supported)
+* [Frame rates](#frame-rates)
+* [Multiple monitor outputs](#multiple-monitor-outputs)
+* [Multiple DisplayLink devices](#multiple-displaylink-devices)
+* [Behaviour with no monitor connected](#behaviour-with-no-monitor-connected)
+
+# Resolutions supported
+
+The driver supports the following resolutions:
+
+640 x 480 @ 60Hz
+
+800 x 600 @ 60Hz
+
+1024x768 @ 60Hz
+
+1360x768 @ 60Hz
+
+1280x960 @ 60Hz
+
+1280x1024 @ 60Hz
+
+1600x900 @ 60Hz
+
+1400x1050 @ 60Hz
+
+1600x1200 @ 60Hz
+
+1920x1080 @ 60Hz
+
+
+Note that the list of resolutions advertised by the driver may be smaller than
+this if a connected monitor does not support a particular resolution. The driver
+interrogates connected monitors to see which modes can be supported.It is the
+responsibility of the BIOS to select the video mode from this list which most
+closely matches its requirements. In some cases this may lead to the BIOS
+scaling its display.
+
+# Frame rates
+
+The driver is limited to a maximum of ten frames per second. Some slower systems
+at higher screen resolutions may perform at a lower rate than this.
+
+# Multiple monitor outputs
+
+If multiple monitors are connected to the DisplayLinkdevice, the display will be
+duplicated (cloned) across all outputs at the same resolution. The resolution
+used will be limited by the capability of the monitor with the
+lowest specification.
+
+# Multiple DisplayLink devices
+
+The driver will support the connection of multiple DisplayLink devices. The
+exact behaviourof the system with multiple devices connected is defined by the
+rest of the BIOS; usually, the BIOS causes the displays to be duplicated
+(cloned) across all devices. Note that the system performance and frame rate
+will be affected by the number of DisplayLink devices connected.
+
+# Behaviour with no monitor connected
+
+The driver uses the EDID (Extended Display Identification Data) protocol to
+detect the list of resolutions that a monitor will support.In some monitors this
+may take some time, and occasionally no EDID information will be returned at
+all. In this case the driver will not be able to detect that there is a monitor
+connected. To improve the user experience in these cases, the driver will behave
+as if there is a monitor connected, and will fall back to presenting the full
+range of supported resolutions to the BIOS.
diff --git a/Maintainers.txt b/Maintainers.txt
index 876ae5612ad8..4093375acffe 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -47,6 +47,11 @@ Drivers/OptionRomPkg
W: https://github.com/tianocore/tianocore.github.io/wiki/OptionRomPkg
M: Ray Ni <ray.ni@intel.com>
+Drivers/DisplayLink
+M: Leif Lindholm <leif.lindholm@linaro.org>
+M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
+R: Andy Hayes <andy.hayes@displaylink.com>
+
Platform
M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
M: Leif Lindholm <leif.lindholm@linaro.org>
--
2.17.1
next reply other threads:[~2019-08-14 14:43 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-14 14:43 andy.hayes [this message]
2019-08-14 15:22 ` [PATCH v1 0/1] Added GOP driver for DisplayLink-based Universal USB Docks to edk2-platforms Michael D Kinney
2019-08-14 17:03 ` Leif Lindholm
2019-08-14 17:50 ` Leif Lindholm
2021-01-12 13:49 ` [edk2-devel] " sebastian.olney
-- strict thread matches above, loose matches on Subject: below --
2019-08-15 12:50 Andy Hayes
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DB8PR10MB2684B8E28270DD983695427F95AD0@DB8PR10MB2684.EURPRD10.PROD.OUTLOOK.COM \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox