From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=pass header.i=@linaro.org header.s=google header.b=DgoL+Dne; spf=pass (domain: linaro.org, ip: 209.85.221.65, mailfrom: leif.lindholm@linaro.org) Received: from mail-wr1-f65.google.com (mail-wr1-f65.google.com [209.85.221.65]) by groups.io with SMTP; Fri, 30 Aug 2019 08:27:22 -0700 Received: by mail-wr1-f65.google.com with SMTP id z11so7396496wrt.4 for ; Fri, 30 Aug 2019 08:27:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=CwKEdquga+5SHJenK/DXXLIfpw/P+ImgKNzTY+/kyCU=; b=DgoL+DneLTELyCe/7Drd9Eg/bQKz6qr10962s9yJB1fkjnmh2UcUORCoqnExY1Vrgk sEg9EirbnyEHvL7SgWPH6PItjrE4+OGjVLgmwZ3ZgE3b8wgKRhFlKEpcB72L4VZtMDGK OsxGXU00nCP01zLWxHWsiSJI5hvWuPxHinEcyQ5VB9UDlpmyxyaHS5ymfg+kXSG9rckC hEVdBv6eXWKCdnZuBrRgb3Dwo4OQxNfB6y0loMzyHr5/Yx763fZzB+6dZdK8YDiIDAJ2 EnpzIH9WwDt3DMUgczfWc6HywVUExZ80ecZhwzq3kaESTjUrZIqRWSG0X4ResVksDSwZ Opxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=CwKEdquga+5SHJenK/DXXLIfpw/P+ImgKNzTY+/kyCU=; b=XJktHBRU3RSdJbNBnp5fOHIqQbFX7FpEIMI06lBkqJqLHE9SY70rA2NCQ6wpQQe2gF BjBrrvn7S/O733XfQ/EfpbJS/9UXmFo/ObVhzdFmSk2Kk/hkaCjwGb0X6nnbOOTOydPw E3yp7zhJSUWk0usT9Au4F8MCZoMWUQ1gpdX5i0dQYLiQizof6pdbJ4IFxam/LRlSBu4U ELheOLqwmeJMzzZyKd8EXY2A/a8Kxyd99uX2Bfcf/beKh5K/sk1sQvLqgqLHdItajFXL SnbeNEYFWTAK2nWFovAOSfTaXVdg0XvmPXDDddmMmd7uRrSklSxBGEyOMg8mHSR2EDBS 7u6A== X-Gm-Message-State: APjAAAWxhHN1Fz6C6P/v+koU+vKS8JkRJ0nUaM7eRW7CxPRVOxUDHlcJ 6R2GEMAUVKcobF5jqIwOI+kErw== X-Google-Smtp-Source: APXvYqxpABWCTj4Z8C9Eggf+HGZUusvxDaixlvhGMoJczbQk3ZrXiHDB3cskHEi/rbuLnqZVNfQ3sg== X-Received: by 2002:a5d:4fc4:: with SMTP id h4mr19692068wrw.64.1567178839868; Fri, 30 Aug 2019 08:27:19 -0700 (PDT) Return-Path: Received: from bivouac.eciton.net (bivouac.eciton.net. [2a00:1098:0:86:1000:23:0:2]) by smtp.gmail.com with ESMTPSA id v6sm14257036wma.24.2019.08.30.08.27.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 30 Aug 2019 08:27:18 -0700 (PDT) Date: Fri, 30 Aug 2019 16:27:17 +0100 From: "Leif Lindholm" To: Andy Hayes Cc: devel@edk2.groups.io, Michael D Kinney , "Ni, Ray" Subject: Re: [edk2-platforms: PATCH 1/1] DisplayLinkPkg: DisplayLinkGop: Added GOP driver for USB docking stations based on DisplayLink chips Message-ID: <20190830152717.GV29255@bivouac.eciton.net> References: <20190819133200.14577-1-andy.hayes@displaylink.com> <20190819133200.14577-2-andy.hayes@displaylink.com> MIME-Version: 1.0 In-Reply-To: <20190819133200.14577-2-andy.hayes@displaylink.com> User-Agent: Mutt/1.10.1 (2018-07-13) Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi Andy, This looks fine to me - all my feedback has been addressed. Reviewed-by: Leif Lindholm Ray - did you have any comments on this, or can I go ahead and commit? Best Regards, Leif On Mon, Aug 19, 2019 at 02:32:00PM +0100, Andy Hayes wrote: > Cc: Leif Lindholm > Cc: Michael D Kinney > Signed-off-by: Andy Hayes > --- > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/CapabilityDescriptor.c | 137 +++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/ComponentName.c | 235 +++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf | 65 ++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.c | 598 +++++++++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h | 129 +++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c | 578 +++++++++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c | 145 +++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.h | 109 ++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c | 1082 ++++++++++++++++++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h | 278 +++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbTransfer.c | 180 ++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/VideoModes.c | 254 +++++ > Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkPkg.dsc | 61 ++ > Drivers/DisplayLink/DisplayLinkPkg/ReadMe.md | 77 ++ > Maintainers.txt | 5 + > 15 files changed, 3933 insertions(+) > > 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/DisplayLinkGopDxe.inf b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf > new file mode 100644 > index 000000000000..0f458fedcc88 > --- /dev/null > +++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/DisplayLinkGopDxe.inf > @@ -0,0 +1,65 @@ > +#/** @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 > + Edid.h > + Gop.c > + UsbDescriptors.c > + UsbDescriptors.h > + 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.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/Edid.h b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Edid.h > new file mode 100644 > index 000000000000..a1b8a0512d1a > --- /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 ((UINTN)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/Gop.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c > new file mode 100644 > index 000000000000..3e483afdba72 > --- /dev/null > +++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/Gop.c > @@ -0,0 +1,578 @@ > +/** > + * @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; > + } > + } > + > + 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]; // Get rid of the magic numbers at some point > + // Could also 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 > + 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 > + */ > +#ifndef MDEPKG_NDEBUG > +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; > +} > +#endif // MDEPKG_NDEBUG > +/* ***************************************************************************************************** */ > +/* ***************************************************************************************************** */ > +/* ****************** 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..c12a80a6e1ab > --- /dev/null > +++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDescriptors.c > @@ -0,0 +1,145 @@ > +/** > + * @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]; > + ZeroMem (Header, sizeof (Header)); > + > + 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/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.c b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c > new file mode 100644 > index 000000000000..997a3e307f38 > --- /dev/null > +++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.c > @@ -0,0 +1,1082 @@ > +/** > + * @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 > +#include > +#include > + > +#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 ( > + CHAR16 *VariableName, > + UINT32 DefaultValue > + ) > +{ > + UINT32 Result; > + UINTN DataSize; > + DataSize = sizeof (Result); > + CONST EFI_STATUS Status = gRT->GetVariable (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 ( > + CHAR16 *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; > + // 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 > +EFIAPI > +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) { > + > + // 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 > + > + > +/** > + * 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/UsbDisplayLink.h b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h > new file mode 100644 > index 000000000000..497a2621bc2c > --- /dev/null > +++ b/Drivers/DisplayLink/DisplayLinkPkg/DisplayLinkGop/UsbDisplayLink.h > @@ -0,0 +1,278 @@ > +/** > + * @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_ > + > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define VENDOR_DISPLAYLINK 0x17e9 > +#define CLASS_VENDOR 0xFF > + > +#define DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO ((UINTN)0) > + > +#define INTERFACE_PROTOCOL_DIRECT_FB 4 > + > +#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; > + > +#define USB_DISPLAYLINK_DEV_SIGNATURE SIGNATURE_32 ('d', 'l', 'i', 'n') > + > +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 > +EFIAPI > +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/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/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/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..47aac133abb1 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 > > +Drivers/DisplayLink > +M: Leif Lindholm > +M: Ard Bieshuevel > +R: Andy Hayes > + > Platform > M: Ard Biesheuvel > M: Leif Lindholm > -- > 2.20.1 >