android_hardware_aw/hwc/tulip/hwc_uevent.cpp
2016-08-06 18:02:22 -04:00

461 lines
15 KiB
C++
Executable file

/*
* Copyright (C) Allwinner Tech All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hwc.h"
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
static tv_para_t g_tv_para[]=
{
{8, DISP_TV_MOD_NTSC, 720, 480, 60,0},
{8, DISP_TV_MOD_PAL, 720, 576, 60,0},
{5, DISP_TV_MOD_480I, 720, 480, 60,0},
{5, DISP_TV_MOD_576I, 720, 576, 60,0},
{5, DISP_TV_MOD_480P, 720, 480, 60,0},
{5, DISP_TV_MOD_576P, 720, 576, 60,0},
{5, DISP_TV_MOD_720P_50HZ, 1280, 720, 50,0},
{5, DISP_TV_MOD_720P_60HZ, 1280, 720, 60,0},
{1, DISP_TV_MOD_1080P_24HZ, 1920, 1080, 24,0},
{5, DISP_TV_MOD_1080P_50HZ, 1920, 1080, 50,0},
{5, DISP_TV_MOD_1080P_60HZ, 1920, 1080, 60,0},
{5, DISP_TV_MOD_1080I_50HZ, 1920, 1080, 50,0},
{5, DISP_TV_MOD_1080I_60HZ, 1920, 1080, 60,0},
{5, DISP_TV_MOD_3840_2160P_25HZ, 3840, 2160, 25,0xff},
{5, DISP_TV_MOD_3840_2160P_24HZ, 3840, 2160, 24,0xff},
{5, DISP_TV_MOD_3840_2160P_30HZ, 3840, 2160, 30,0xff},
{1, DISP_TV_MOD_1080P_24HZ_3D_FP, 1920, 1080, 24,0},
{1, DISP_TV_MOD_720P_50HZ_3D_FP, 1280, 720, 50,0},
{1, DISP_TV_MOD_720P_60HZ_3D_FP, 1280, 720, 60,0},
{1, DISP_TV_MODE_NUM, 0, 0, 0,0},
};
int get_info_mode(int mode,MODEINFO info)
{
unsigned int i = 0;
for(i=0; i<sizeof(g_tv_para) / sizeof(tv_para_t); i++)
{
if(g_tv_para[i].mode == mode)
{
return *(((int *)(g_tv_para+i))+info);
}
}
return -1;
}
int hwc_manage_display(DisplayInfo **retDisplayInfo, int DispInfo, ManageDisp mode)
{
DisplayInfo* PsDisplayInfo = NULL;
DisplayInfo* TmpDisplayInfo = NULL;
SUNXI_hwcdev_context_t *Globctx = &gSunxiHwcDevice;
int find = -1;
int disp;
for(disp = 0; disp < (Globctx->NumberofDisp); disp++)
{
PsDisplayInfo = &Globctx->SunxiDisplay[disp];
switch(mode)
{
case FIND_HWDISPNUM:
if(PsDisplayInfo->VirtualToHWDisplay == DispInfo)
{
*retDisplayInfo = PsDisplayInfo;
return disp;
}
break;
case NULL_DISPLAY:
if(PsDisplayInfo->VirtualToHWDisplay != -EINVAL )
{
return disp;
}
break;
case SET_DISP:
if(PsDisplayInfo->VirtualToHWDisplay == DispInfo)
{
*retDisplayInfo = PsDisplayInfo;
return disp;
}
if(disp == HWC_DISPLAY_PRIMARY && !PsDisplayInfo->active)
{
PsDisplayInfo->VirtualToHWDisplay = DispInfo;
*retDisplayInfo = PsDisplayInfo;
PsDisplayInfo->active = 1;
return disp;
}
if(PsDisplayInfo->VirtualToHWDisplay == -EINVAL && TmpDisplayInfo == NULL)
{
TmpDisplayInfo = PsDisplayInfo;
find = disp;
}
if(disp == Globctx->NumberofDisp-1 && TmpDisplayInfo != NULL)
{
TmpDisplayInfo->VirtualToHWDisplay = DispInfo;
*retDisplayInfo = TmpDisplayInfo;
(*retDisplayInfo)->active = 1;
return find;
}
break;
case FREE_DISP:
if(PsDisplayInfo->VirtualToHWDisplay == DispInfo)
{
if(disp != HWC_DISPLAY_PRIMARY)
{
PsDisplayInfo->VirtualToHWDisplay = -EINVAL;
}
PsDisplayInfo->active = 0;
if(PsDisplayInfo->Current3DMode == DISPLAY_3D_LEFT_RIGHT_HDMI
|| PsDisplayInfo->Current3DMode == DISPLAY_3D_TOP_BOTTOM_HDMI)
{
PsDisplayInfo->Current3DMode = DISPLAY_2D_ORIGINAL;
PsDisplayInfo->DisplayMode = DISP_TV_MODE_NUM;
}
return disp;
}
break;
default:
ALOGD("Error usage in ManageDisplay");
}
}
return -1;
}
disp_tv_mode get_suitable_hdmi_mode(int select, disp_tv_mode lastmode)
{
SUNXI_hwcdev_context_t *Globctx = &gSunxiHwcDevice;
unsigned long arg[4]={0};
arg[0] = select;
int ret, i, j = -1;
disp_tv_mode theMostMode = DISP_TV_MODE_NUM;
struct disp_output para;
i = sizeof(g_tv_para) / sizeof(g_tv_para[0]);
if(lastmode < DISP_TV_MODE_NUM)
{
arg[1] = DISP_OUTPUT_TYPE_HDMI;
arg[2] = lastmode;
ret = ioctl(Globctx->DisplayFd, DISP_DEVICE_SWITCH, arg);
if(ret >= 0)
{
return lastmode;
}
}
if(Globctx->SunxiDisplay[0].DisplayType == DISP_OUTPUT_TYPE_HDMI)
{
arg[1] = (unsigned long)&para;
ret = ioctl(Globctx->DisplayFd, DISP_GET_OUTPUT, arg);
if(ret >= 0)
{
theMostMode = (disp_tv_mode)para.mode;
}
}else{
while(i > 0)
{
i--;
if(g_tv_para[i].mode == DISP_TV_MOD_1080P_60HZ)
{
j = i;
}
if(j != -1)
{
arg[1] = DISP_OUTPUT_TYPE_HDMI;
arg[2] = g_tv_para[i].mode;
ret = ioctl(Globctx->DisplayFd, DISP_DEVICE_SWITCH, arg);
if(ret >= 0)
{
if(theMostMode == DISP_TV_MODE_NUM)
{
g_tv_para[sizeof(g_tv_para) / sizeof(g_tv_para[0])-1].support = 1<<select;
theMostMode = g_tv_para[i].mode;
}
g_tv_para[i].support |= 1<<select;
}else{
g_tv_para[i].support &= ~(1<<select);
}
}
}
}
if(theMostMode != DISP_TV_MODE_NUM)
{
return theMostMode;
}else{
return DISP_TV_MOD_1080P_60HZ;
}
}
int hwc_hotplug_switch(int DisplayNum, bool plug, disp_tv_mode set_mode)
{
SUNXI_hwcdev_context_t *Globctx = &gSunxiHwcDevice;
int vir_disp = -1;
DisplayInfo *PsDisplayInfo = NULL;
unsigned long arg[4] = {0};
bool AllreadyPlugin = 0;
vir_disp = hwc_manage_display(&PsDisplayInfo, DisplayNum, FIND_HWDISPNUM);
if (PsDisplayInfo != NULL)
{
ALOGD("###AllreadyPlugin:vir(%d) ture(%d)####", vir_disp, DisplayNum);
AllreadyPlugin = 1;
}
if(plug)
{
if(PsDisplayInfo == NULL)
{
vir_disp = hwc_manage_display(&PsDisplayInfo, DisplayNum, SET_DISP);
}
if(set_mode >= DISP_TV_MODE_NUM)
{
set_mode = get_suitable_hdmi_mode(DisplayNum,PsDisplayInfo->DisplayMode);
}
if(set_mode != DISP_TV_MODE_NUM)
{
PsDisplayInfo->setblank = 1;
PsDisplayInfo->VarDisplayWidth = get_info_mode(set_mode,WIDTH);
PsDisplayInfo->VarDisplayHeight = get_info_mode(set_mode,HEIGHT);
PsDisplayInfo->DisplayType = DISP_OUTPUT_TYPE_HDMI;
PsDisplayInfo->DisplayMode = set_mode;
PsDisplayInfo->DiplayDPI_X = 213000;
PsDisplayInfo->DiplayDPI_Y = 213000;
PsDisplayInfo->DisplayVsyncP = 1000000000/get_info_mode(set_mode, REFRESHRAE);
PsDisplayInfo->HwChannelNum = DisplayNum?2:4;
PsDisplayInfo->LayerNumofCH = NUMLAYEROFCHANNEL;
PsDisplayInfo->VideoCHNum =1;
if(Globctx->SunxiDisplay[0].VirtualToHWDisplay != DisplayNum && !AllreadyPlugin)
{
PsDisplayInfo->InitDisplayHeight = PsDisplayInfo->VarDisplayHeight;
PsDisplayInfo->InitDisplayWidth = PsDisplayInfo->VarDisplayWidth;
}
Globctx->memlimit += PsDisplayInfo->InitDisplayHeight * PsDisplayInfo->InitDisplayWidth * 4;
if(Globctx->SunxiDisplay[0].DisplayType != DISP_OUTPUT_TYPE_HDMI)
{
Globctx->hot_plug = 1;
}
arg[0] = DisplayNum;
arg[1] = DISP_OUTPUT_TYPE_HDMI;
arg[2] = set_mode;
ioctl(Globctx->DisplayFd, DISP_DEVICE_SWITCH, (unsigned long)arg);
PsDisplayInfo->setblank = 0;
Globctx->psHwcProcs->invalidate(Globctx->psHwcProcs);
arg[0] = DisplayNum;
arg[1] = 1;
ioctl(Globctx->DisplayFd, DISP_VSYNC_EVENT_EN,(unsigned long)arg);
}else{
ALOGD("###has no fix HDMI Mode###");
return 0;
}
ALOGD( "###hdmi plug in, Type:%d, Mode:0x%08x###",
PsDisplayInfo->DisplayType, PsDisplayInfo->DisplayMode);
}
else if(Globctx->SunxiDisplay[0].DisplayType != DISP_OUTPUT_TYPE_HDMI){
Globctx->hot_plug = 0;
hwc_manage_display(NULL, DisplayNum ,FREE_DISP);
}
if(Globctx->psHwcProcs && Globctx->psHwcProcs->hotplug)
{
if((vir_disp == HWC_DISPLAY_EXTERNAL)
&& ((!AllreadyPlugin && plug )||(AllreadyPlugin && !plug)))
{
ALOGD("hotplug report plug[%d][%d]",DisplayNum,plug);
Globctx->psHwcProcs->hotplug(Globctx->psHwcProcs, HWC_DISPLAY_EXTERNAL, plug);
}
}else{
ALOGD("###psHwcProcs No register.###");
}
if(!plug && Globctx->SunxiDisplay[0].DisplayType != DISP_OUTPUT_TYPE_HDMI)
{
arg[0] = DisplayNum;
arg[1] = DISP_OUTPUT_TYPE_NONE;
sleep(1);
ioctl(Globctx->DisplayFd, DISP_DEVICE_SWITCH, (unsigned long)arg);
arg[0] = DisplayNum;
arg[1] = 0;
ioctl(Globctx->DisplayFd, DISP_VSYNC_EVENT_EN,(unsigned long)arg);
if(hwc_manage_display(NULL, 0 ,NULL_DISPLAY) == Globctx->NumberofDisp)
{
ALOGD( "###ALL Display has plug out###");
}
ALOGD( "###hdmi plug out###");
}
return 0;
}
static inline bool check_stop()
{
char property[PROPERTY_VALUE_MAX];
int stop_hwc = 0;
if (property_get("debug.hwc.forcegpu", property, NULL) >= 0)
{
stop_hwc = atoi(property);
}
return !!stop_hwc;
}
static int hwc_uevent(void)
{
struct sockaddr_nl snl;
const int buffersize = 32*1024;
char *buf = NULL;
struct pollfd fds;
bool stop_hwc = 0;
int count, IsVsync, IsHdmi, hotplug_sock, retval, display_id = -1;
unsigned int cout = 0, new_hdmi_hpd = 0;
uint64_t timestamp = 0;
const char *s = NULL;
SUNXI_hwcdev_context_t *Globctx = &gSunxiHwcDevice;
buf = (char *)calloc(buffersize, sizeof(char));
if(buf == NULL)
{
ALOGD("calloc err.");
return -1;
}
memset(&snl, 0x0, sizeof(snl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = 0;
snl.nl_groups = 0xffffffff;
hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (hotplug_sock == -1)
{
ALOGE("####socket is failed in %s error:%d %s###",
__FUNCTION__, errno, strerror(errno));
free(buf);
return -1;
}
setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
retval = bind(hotplug_sock, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl));
if (retval < 0)
{
ALOGE("####bind is failed in %s error:%d %s###",
__FUNCTION__, errno, strerror(errno));
close(hotplug_sock);
free(buf);
return -1;
}
timeval tv = { 0, 0 };
double starttime = 0.0;
gettimeofday(&tv, NULL);
starttime = tv.tv_sec * 1000 + tv.tv_usec / 1.0e3;
double fCurrentTime = 0.0;
ALOGD("######hwc uevent Thread(%d)%p.#######", gettid(), &snl);
while(1)
{
fds.fd = hotplug_sock;
fds.events = POLLIN;
fds.revents = 0;
Globctx->stop_hwc = check_stop();
//gettimeofday(&tv, NULL);
//fCurrentTime = tv.tv_sec * 1000 + tv.tv_usec / 1.0e3;
hwc_set_debug(Globctx);
retval = poll(&fds, 1, 500);
if(Globctx->psHwcProcs == NULL || Globctx->psHwcProcs->vsync == NULL)
{
continue;
}
if(retval > 0 && (fds.revents & POLLIN))
{
count = ::recv(hotplug_sock, buf, buffersize, 0);
s = buf;
if(count > 0)
{
IsVsync = !strcmp(s, "change@/devices/platform/disp");
IsHdmi = !strcmp(s, "change@/devices/virtual/switch/hdmi");
s += strlen(s) + 1;
if(IsVsync)
{
timestamp = 0;
while(s)
{
display_id = -1;
if (!strncmp(s, "VSYNC0=", strlen("VSYNC0=")))
{
timestamp = strtoull(s + strlen("VSYNC0="), NULL, 0);
display_id = 0;
}else if(!strncmp(s, "VSYNC1=", strlen("VSYNC1="))){
timestamp = strtoull(s + strlen("VSYNC1="), NULL, 0);
display_id = 1;
}
if(Globctx->SunxiDisplay[0].VirtualToHWDisplay == display_id)
{
Globctx->SunxiDisplay[0].mytimestamp = timestamp;
if(Globctx->SunxiDisplay[0].VsyncEnable == 1)
{
Globctx->psHwcProcs->vsync(Globctx->psHwcProcs, 0, timestamp);
}
}
s += strlen(s) + 1;
if(s - buf >= count || s - buf >= buffersize)
{
break;
}
}
}
if(IsHdmi && Globctx->SunxiDisplay[0].DisplayType != DISP_OUTPUT_TYPE_HDMI)
{
while(s)
{
if (!strncmp(s, "SWITCH_STATE=", strlen("SWITCH_STATE=")))
{
new_hdmi_hpd = strtoull(s + strlen("SWITCH_STATE="), NULL, 0);
ALOGD( "#### disp[%d] hotplug[%d]###", HDMI_USED, !!new_hdmi_hpd);
hwc_hotplug_switch(1, !!new_hdmi_hpd, DISP_TV_MODE_NUM);
}
s += strlen(s) + 1;
if(s - buf >= count || s - buf >= buffersize)
{
break;
}
}
}
}
}
}
return 0;
}
void *vsync_thread_wrapper(void *priv)
{
HWC_UNREFERENCED_PARAMETER(priv);
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
hwc_uevent();
return NULL;
}