0
0
mirror of https://github.com/edk2-porting/edk2-msm synced 2025-07-19 17:31:49 +00:00
Files
2022-11-10 18:50:32 +08:00

548 lines
15 KiB
C

#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Protocol/AbsolutePointer.h>
#include <Protocol/EFITlmm.h>
#include <Protocol/EFII2C.h>
#include "SynapticsRmi4.h"
// Instance Template
RMI4_INTERNAL_DATA mInstanceTemplate = {
RMI4_TCH_INSTANCE_SIGNATURE,
0,
0,
{
AbsPReset,
AbsPGetState,
NULL,
NULL,
},
{
0,
0,
0,
0,
0,
0,
0,
},
NULL,
FALSE,
0,
0,
0,
NULL,
FALSE,
NULL,
NULL,
}, *gI2CInstance;;
// Binding
EFI_DRIVER_BINDING_PROTOCOL gRmi4DriverBinding = {
Rmi4AbsolutePointerDriverBindingSupported,
Rmi4AbsolutePointerDriverBindingStart,
Rmi4AbsolutePointerDriverBindingStop,
0x1,
NULL,
NULL,
};
static SYNAPTICS_I2C_DEVICE *gRmi4I2cDeviceIo = NULL;
STATIC EFI_EVENT ExitBootServicesEvent;
EFI_STATUS
EFIAPI
Rmi4AbsolutePointerDriverBindingSupported(
IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
{
EFI_STATUS Status;
SYNAPTICS_I2C_DEVICE *Rmi4I2cDeviceIo;
Status = gBS->OpenProtocol(
Controller, &gSynapticsTouchDeviceProtocolGuid, (VOID **)&Rmi4I2cDeviceIo,
This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR(Status)) {
return Status;
}
Status = EFI_SUCCESS;
gBS->CloseProtocol(
Controller, &gSynapticsTouchDeviceProtocolGuid, This->DriverBindingHandle,
Controller);
return Status;
}
EFI_STATUS
EFIAPI
Rmi4AbsolutePointerDriverBindingStart(
IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
{
EFI_TPL OldTpl;
EFI_STATUS Status;
I2C_STATUS I2CStatus;
SYNAPTICS_I2C_DEVICE *Rmi4I2cDeviceIo;
RMI4_INTERNAL_DATA * Instance;
BOOLEAN bI2COpened = FALSE;
UINT8 InfoData[TOUCH_RMI_PAGE_INFO_BYTES] = {0};
UINT8 Address = TOUCH_RMI_PAGE_INFO_ADDRESS;
UINT8 Page = 0;
UINT8 Function = 0;
OldTpl = gBS->RaiseTPL(TPL_CALLBACK);
Status = gBS->OpenProtocol(
Controller, &gSynapticsTouchDeviceProtocolGuid, (VOID **)&Rmi4I2cDeviceIo,
This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR(Status)) {
goto exit;
}
Instance = AllocateCopyPool(sizeof(RMI4_INTERNAL_DATA), &mInstanceTemplate);
ASSERT(Instance != NULL);
ASSERT(Rmi4I2cDeviceIo != NULL);
Instance->Rmi4Device = Rmi4I2cDeviceIo;
Instance->AbsPointerMode.AbsoluteMaxX = Rmi4I2cDeviceIo->XMax - Rmi4I2cDeviceIo->XMin;
Instance->AbsPointerMode.AbsoluteMaxY = Rmi4I2cDeviceIo->YMax - Rmi4I2cDeviceIo->YMin;
Instance->AbsPointerProtocol.Mode = &Instance->AbsPointerMode;
I2CStatus = Rmi4I2cDeviceIo->I2cQupProtocol->Open(
Instance->Rmi4Device->ControllerI2cDevice,
&Instance->I2cController);
if (I2C_ERROR(I2CStatus)) {
DEBUG((EFI_D_ERROR,
"SynapticsTouchDxe: Failed to open I2C%d: %d \n",
Instance->Rmi4Device->ControllerI2cDevice,
I2CStatus));
Status = EFI_DEVICE_ERROR;
goto exit;
}
bI2COpened = TRUE;
gRmi4I2cDeviceIo = Rmi4I2cDeviceIo;
gI2CInstance = Instance;
Status = SynaPowerUpController(Instance);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Failed to power on controller: %r\n", Status));
goto exit;
}
DEBUG((EFI_D_INFO, "SynapticsTouchDxe: Probe Synaptics RMI4 F12 Function \n"));
// Probe device to locate F12 function
do {
Status =
SynaI2cRead(Instance, Address, &InfoData[0], TOUCH_RMI_PAGE_INFO_BYTES);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: I2C Read failed: %r\n", Status));
goto exit;
}
if (InfoData[5] == TOUCH_RMI_F12_FUNCTION) {
DEBUG((EFI_D_INFO, "SynapticsTouchDxe: RMI4 F12 Function Found. \n"));
Instance->PageF12 = Page;
Instance->TouchDataAddress = InfoData[3];
break;
}
// Last page. Go out
if (InfoData[5] == 0 && Address == TOUCH_RMI_PAGE_INFO_ADDRESS) {
break;
}
// Switch page
else if (InfoData[5] == 0 && Address != TOUCH_RMI_PAGE_INFO_ADDRESS) {
DEBUG((EFI_D_INFO, "SynapticsTouchDxe: Switching to next Synaptics RMI4 Page........"));
Page++;
Address = TOUCH_RMI_PAGE_INFO_ADDRESS;
Status = SynaI2cWrite(Instance, RMI_CHANGE_PAGE_ADDRESS, &Page, 1);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Unable to switch RMI4 page: %r\n", Status));
goto exit;
}
}
else {
Function++;
Address = Address - TOUCH_RMI_PAGE_INFO_BYTES;
}
} while ((Address > 0) && (Function < TOUCH_RMI_MAX_FUNCTIONS));
// Determine final result
if (Function >= TOUCH_RMI_MAX_FUNCTIONS) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Invalid RMI4 function index \n"));
Status = EFI_DEVICE_ERROR;
goto exit;
}
if (Address <= 0) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Invalid RMI4 address \n"));
Status = EFI_DEVICE_ERROR;
goto exit;
}
// Flag device as initialized
Instance->Initialized = TRUE;
// Set touch event
Status = gBS->CreateEvent(
EVT_NOTIFY_WAIT, TPL_NOTIFY, AbsPWaitForInput, Instance,
&Instance->AbsPointerProtocol.WaitForInput);
ASSERT_EFI_ERROR(Status);
// Start polling
Status = AbsStartPolling(Instance);
ASSERT_EFI_ERROR(Status);
// Install protocols
Status = gBS->InstallProtocolInterface(
&Controller, &gEfiAbsolutePointerProtocolGuid, EFI_NATIVE_INTERFACE,
&Instance->AbsPointerProtocol);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Failed to install protocol interface: %r\n", Status));
}
// Name table
Instance->ControllerNameTable = NULL;
AddUnicodeString2(
"eng", gRmi4DriverComponentName.SupportedLanguages,
&Instance->ControllerNameTable, L"Synaptics RMI4 Absolute Pointer", TRUE);
AddUnicodeString2(
"en", gRmi4DriverComponentName.SupportedLanguages,
&Instance->ControllerNameTable, L"Synaptics RMI4 Absolute Pointer",
FALSE);
exit:
if(bI2COpened == TRUE && Status == EFI_DEVICE_ERROR) {
Rmi4I2cDeviceIo->I2cQupProtocol->Close(
Instance->I2cController);
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Error: Closing i2c instance\n"));
}
gBS->RestoreTPL(OldTpl);
return Status;
}
EFI_STATUS
EFIAPI
Rmi4AbsolutePointerDriverBindingStop(
IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer)
{
EFI_STATUS Status;
EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointerProtocol;
RMI4_INTERNAL_DATA * Instance;
SYNAPTICS_I2C_DEVICE *Rmi4I2cDeviceIo;
Status = gBS->OpenProtocol(
Controller, &gEfiAbsolutePointerProtocolGuid,
(VOID **)&AbsolutePointerProtocol, This->DriverBindingHandle, Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
Instance = RMI4_TCH_INSTANCE_FROM_ABSTCH_THIS(AbsolutePointerProtocol);
Status = gBS->UninstallProtocolInterface(
Controller, &gEfiAbsolutePointerProtocolGuid,
&Instance->AbsPointerProtocol);
if (EFI_ERROR(Status)) {
return Status;
}
gBS->CloseEvent(Instance->PollingTimerEvent);
gBS->CloseEvent(Instance->AbsPointerProtocol.WaitForInput);
Status = gBS->OpenProtocol(
Controller, &gSynapticsTouchDeviceProtocolGuid, (VOID **)&Rmi4I2cDeviceIo,
This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER);
Rmi4I2cDeviceIo->I2cQupProtocol->Close(
Instance->I2cController);
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Closing i2c instance\n"));
return EFI_SUCCESS;
}
EFI_STATUS AbsPReset(
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, IN BOOLEAN ExtendedVerification)
{
RMI4_INTERNAL_DATA *Instance;
Instance = RMI4_TCH_INSTANCE_FROM_ABSTCH_THIS(This);
Instance->LastX = 0;
Instance->LastY = 0;
Instance->StateChanged = FALSE;
return EFI_SUCCESS;
}
EFI_STATUS AbsStartPolling(IN RMI4_INTERNAL_DATA *Instance)
{
EFI_STATUS Status = EFI_SUCCESS;
// Set event routines
Status = gBS->CreateEvent(
EVT_NOTIFY_SIGNAL | EVT_TIMER, TPL_CALLBACK, SyncPollCallback, Instance,
&Instance->PollingTimerEvent);
ASSERT_EFI_ERROR(Status);
Status = gBS->SetTimer(Instance->PollingTimerEvent, TimerPeriodic, 200000);
ASSERT_EFI_ERROR(Status);
return Status;
}
EFI_STATUS AbsPGetState(
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This,
IN OUT EFI_ABSOLUTE_POINTER_STATE *State)
{
EFI_STATUS Status = EFI_SUCCESS;
RMI4_INTERNAL_DATA *Instance;
if (This == NULL || State == NULL) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
Instance = RMI4_TCH_INSTANCE_FROM_ABSTCH_THIS(This);
if (!Instance->StateChanged) {
Status = EFI_NOT_READY;
goto exit;
}
State->CurrentX = Instance->LastX;
State->CurrentY = Instance->LastY;
State->CurrentZ = 0;
State->ActiveButtons = 1;
Instance->StateChanged = FALSE;
exit:
return Status;
}
VOID EFIAPI AbsPWaitForInput(IN EFI_EVENT Event, IN VOID *Context)
{
RMI4_INTERNAL_DATA *Instance = (RMI4_INTERNAL_DATA *)Context;
EFI_TPL OldTpl;
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL(TPL_NOTIFY);
SyncPollCallback(NULL, Instance);
if (Instance->StateChanged) {
gBS->SignalEvent(Event);
}
//
// Leave critical section and return
//
gBS->RestoreTPL(OldTpl);
}
EFI_STATUS
EFIAPI
SynaPowerUpController(RMI4_INTERNAL_DATA *Instance)
{
UINT32 Config;
UINT32 ResetLine;
EFI_STATUS Status;
if (Instance == NULL || Instance->Rmi4Device == NULL ||
Instance->Rmi4Device->GpioTlmmProtocol == NULL) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
// Pin Sanity check
ResetLine = Instance->Rmi4Device->ControllerResetPin;
if (ResetLine <= 0) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Invalid GPIO configuration \n"));
Status = EFI_INVALID_PARAMETER;
goto exit;
}
if(Instance->Rmi4Device->ControllerVddPin != 0) {
Config = EFI_GPIO_CFG(Instance->Rmi4Device->ControllerVddPin,
0,
GPIO_OUTPUT,
GPIO_NO_PULL,
GPIO_2MA);
Status = Instance->Rmi4Device->GpioTlmmProtocol->ConfigGpio(Config, TLMM_GPIO_ENABLE);
Instance->Rmi4Device->GpioTlmmProtocol->GpioOut(Config,
Instance->Rmi4Device->ControllerVddCtlActiveLow ? GPIO_LOW_VALUE : GPIO_HIGH_VALUE);
}
if(Instance->Rmi4Device->ControllerVddIoPin != 0) {
Config = EFI_GPIO_CFG(Instance->Rmi4Device->ControllerVddIoPin,
0,
GPIO_OUTPUT,
GPIO_NO_PULL,
GPIO_2MA);
Status = Instance->Rmi4Device->GpioTlmmProtocol->ConfigGpio(Config, TLMM_GPIO_ENABLE);
Instance->Rmi4Device->GpioTlmmProtocol->GpioOut(Config,
Instance->Rmi4Device->ControllerVddCtlActiveLow ? GPIO_LOW_VALUE : GPIO_HIGH_VALUE);
}
// Power Seq
Config = EFI_GPIO_CFG( ResetLine, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA );
Status = Instance->Rmi4Device->GpioTlmmProtocol->ConfigGpio(Config, TLMM_GPIO_ENABLE);
// Configure MSM GPIO RESET line to Low
Instance->Rmi4Device->GpioTlmmProtocol->GpioOut(Config, GPIO_LOW_VALUE);
gBS->Stall(TOUCH_POWER_RAIL_STABLE_TIME);
// configure MSM GPIO RESET line to High
Instance->Rmi4Device->GpioTlmmProtocol->GpioOut(Config, GPIO_HIGH_VALUE);
gBS->Stall(TOUCH_DELAY_TO_COMMUNICATE);
DEBUG((EFI_D_INFO, "SynapticsTouchDxe: Touch controller powered on \n"));
Status = EFI_SUCCESS;
exit:
return Status;
}
EFI_STATUS
EFIAPI
SyncGetTouchData(RMI4_INTERNAL_DATA *Instance, IN PTOUCH_DATA DataBuffer)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT8 TouchCoordinates[TOUCH_DATA_BYTES] = {0};
if (Instance == NULL || DataBuffer == NULL) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
if (!Instance->Initialized) {
Status = EFI_NOT_READY;
goto exit;
}
// Change RMI page to F12
Status =
SynaI2cWrite(Instance, RMI_CHANGE_PAGE_ADDRESS, &Instance->PageF12, 1);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Failed to change RMI4 page address: %r\n", Status));
goto exit;
}
// Read a fingerprint
Status = SynaI2cRead(
Instance, Instance->TouchDataAddress, &TouchCoordinates[0],
TOUCH_DATA_BYTES);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Failed to read RMI4 F12 page data: %r\n", Status));
goto exit;
}
DataBuffer->TouchStatus = TouchCoordinates[0];
DataBuffer->TouchX =
((TouchCoordinates[1] & 0xFF) | ((TouchCoordinates[2] & 0xFF) << 8));
DataBuffer->TouchY =
((TouchCoordinates[3] & 0xFF) | ((TouchCoordinates[4] & 0xFF) << 8));
exit:
return Status;
}
VOID EFIAPI SyncPollCallback(IN EFI_EVENT Event, IN VOID *Context)
{
EFI_STATUS Status;
RMI4_INTERNAL_DATA *Instance = (RMI4_INTERNAL_DATA *)Context;
TOUCH_DATA TouchPointerData;
//EFI_TPL OldTpl;
//
// Enter critical section
//
//OldTpl = gBS->RaiseTPL(TPL_NOTIFY);
Status = SyncGetTouchData(Instance, &TouchPointerData);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "SynapticsTouchDxe: Failed to get Synaptics RMI4 F12 Data: %r\n", Status));
}
else {
if (TouchPointerData.TouchStatus > 0) {
Instance->LastX = TouchPointerData.TouchX + Instance->Rmi4Device->XMin;
Instance->LastY = TouchPointerData.TouchY + Instance->Rmi4Device->YMin;
Instance->StateChanged = TRUE;
if (Instance->Rmi4Device->XInverted) {
Instance->LastX = Instance->AbsPointerMode.AbsoluteMaxX - Instance->LastX;
}
if (Instance->Rmi4Device->YInverted) {
Instance->LastY = Instance->AbsPointerMode.AbsoluteMaxY - Instance->LastY;
}
DEBUG(
(EFI_D_INFO, "SynapticsTouchDxe: Touch: X: %d, Y: %d \n", Instance->LastX,
Instance->LastY));
}
}
//
// Leave critical section and return
//
//gBS->RestoreTPL(OldTpl);
}
STATIC
VOID
SynapticsTouchDxeExitBootService (
EFI_EVENT Event,
VOID *Context
)
{
if(gRmi4I2cDeviceIo != NULL && gI2CInstance != NULL)
gRmi4I2cDeviceIo->I2cQupProtocol->Close(gI2CInstance->I2cController);
DEBUG((EFI_D_VERBOSE, "SynapticsTouchDxe: Closing i2c instance\n"));
}
EFI_STATUS
EFIAPI
SynaInitialize(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS Status;
Status = EfiLibInstallDriverBindingComponentName2(
ImageHandle, SystemTable, &gRmi4DriverBinding, ImageHandle,
&gRmi4DriverComponentName, &gRmi4DriverComponentName2);
ASSERT_EFI_ERROR(Status);
gBS->CreateEventEx(
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SynapticsTouchDxeExitBootService,
NULL,
&gEfiEventExitBootServicesGuid,
&ExitBootServicesEvent
);
return EFI_SUCCESS;
}