742 lines
23 KiB
C++
742 lines
23 KiB
C++
|
/*
|
||
|
* Copyright (C) 2007 The Android Open Source Project
|
||
|
* Copyright (c) 2010-2011, The Linux Foundation. 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.
|
||
|
*/
|
||
|
|
||
|
#define LOG_TAG "HDMIDaemon"
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <stdint.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <math.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <utils/misc.h>
|
||
|
#include <signal.h>
|
||
|
|
||
|
#include <binder/IPCThreadState.h>
|
||
|
#include <utils/threads.h>
|
||
|
#include <utils/Atomic.h>
|
||
|
#include <utils/Errors.h>
|
||
|
#include <utils/Log.h>
|
||
|
#include <utils/AssetManager.h>
|
||
|
|
||
|
#include <ui/DisplayInfo.h>
|
||
|
#include <ui/FramebufferNativeWindow.h>
|
||
|
#include <linux/msm_mdp.h>
|
||
|
#include <linux/fb.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#include <cutils/properties.h>
|
||
|
|
||
|
#include "HDMIDaemon.h"
|
||
|
|
||
|
namespace android {
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
|
||
|
#define DEVICE_ROOT "/sys/class/graphics"
|
||
|
#define DEVICE_NODE "fb1"
|
||
|
|
||
|
#define HDMI_SOCKET_NAME "hdmid"
|
||
|
|
||
|
#define HDMI_EVT_CONNECTED "hdmi_connected"
|
||
|
#define HDMI_EVT_DISCONNECTED "hdmi_disconnected"
|
||
|
#define HDMI_EVT_AUDIO_ON "hdmi_audio_on"
|
||
|
#define HDMI_EVT_AUDIO_OFF "hdmi_audio_off"
|
||
|
#define HDMI_EVT_NO_BROADCAST_ONLINE "hdmi_no_broadcast_online"
|
||
|
|
||
|
#define HDMI_CMD_ENABLE_HDMI "enable_hdmi"
|
||
|
#define HDMI_CMD_DISABLE_HDMI "disable_hdmi"
|
||
|
#define HDMI_CMD_CHANGE_MODE "change_mode: "
|
||
|
#define HDMI_CMD_SET_ASWIDTH "set_aswidth: "
|
||
|
#define HDMI_CMD_SET_ASHEIGHT "set_asheight: "
|
||
|
#define HDMI_CMD_HPDOPTION "hdmi_hpd: "
|
||
|
|
||
|
#define SYSFS_CONNECTED DEVICE_ROOT "/" DEVICE_NODE "/connected"
|
||
|
#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
|
||
|
#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd"
|
||
|
#define SYSFS_HDCP_PRESENT DEVICE_ROOT "/" DEVICE_NODE "/hdcp_present"
|
||
|
#define HDMI_PANEL '7'
|
||
|
|
||
|
HDMIDaemon::HDMIDaemon() : Thread(false),
|
||
|
mFrameworkSock(-1), mAcceptedConnection(-1), mUeventSock(-1),
|
||
|
mHDMIUeventQueueHead(NULL), fd1(-1), mCurrentID(-1), mNxtMode(-1)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
HDMIDaemon::~HDMIDaemon() {
|
||
|
HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1;
|
||
|
while (tmp != NULL) {
|
||
|
tmp1 = tmp;
|
||
|
tmp = tmp->next;
|
||
|
delete tmp1;
|
||
|
}
|
||
|
mHDMIUeventQueueHead = NULL;
|
||
|
if (fd1 > 0)
|
||
|
close(fd1);
|
||
|
}
|
||
|
|
||
|
void HDMIDaemon::onFirstRef() {
|
||
|
run("HDMIDaemon", PRIORITY_AUDIO);
|
||
|
}
|
||
|
|
||
|
sp<SurfaceComposerClient> HDMIDaemon::session() const {
|
||
|
return mSession;
|
||
|
}
|
||
|
|
||
|
|
||
|
void HDMIDaemon::binderDied(const wp<IBinder>& who)
|
||
|
{
|
||
|
requestExit();
|
||
|
}
|
||
|
|
||
|
status_t HDMIDaemon::readyToRun() {
|
||
|
|
||
|
if ((mFrameworkSock = android_get_control_socket(HDMI_SOCKET_NAME)) < 0) {
|
||
|
LOGE("Obtaining file descriptor socket '%s' failed: %s",
|
||
|
HDMI_SOCKET_NAME, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (listen(mFrameworkSock, 4) < 0) {
|
||
|
LOGE("Unable to listen on fd '%d' for socket '%s': %s",
|
||
|
mFrameworkSock, HDMI_SOCKET_NAME, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
struct sockaddr_nl nladdr;
|
||
|
memset(&nladdr, 0, sizeof(nladdr));
|
||
|
nladdr.nl_family = AF_NETLINK;
|
||
|
nladdr.nl_pid = getpid();
|
||
|
nladdr.nl_groups = 0xffffffff;
|
||
|
|
||
|
if ((mUeventSock = socket(PF_NETLINK,
|
||
|
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
|
||
|
LOGE("Unable to create uevent socket: %s", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int uevent_sz = 64 * 1024;
|
||
|
if (setsockopt(mUeventSock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
|
||
|
sizeof(uevent_sz)) < 0) {
|
||
|
LOGE("Unable to set uevent socket options: %s", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (bind(mUeventSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
|
||
|
LOGE("Unable to bind uevent socket: %s", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
LOGD("readyToRun: success");
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::threadLoop()
|
||
|
{
|
||
|
int max = -1;
|
||
|
fd_set read_fds;
|
||
|
FD_ZERO(&read_fds);
|
||
|
|
||
|
FD_SET(mFrameworkSock, &read_fds);
|
||
|
if (max < mFrameworkSock)
|
||
|
max = mFrameworkSock;
|
||
|
FD_SET(mUeventSock, &read_fds);
|
||
|
if (max < mUeventSock)
|
||
|
max = mUeventSock;
|
||
|
|
||
|
if (mAcceptedConnection != -1) {
|
||
|
FD_SET(mAcceptedConnection, &read_fds);
|
||
|
if (max < mAcceptedConnection)
|
||
|
max = mAcceptedConnection;
|
||
|
}
|
||
|
|
||
|
struct timeval to;
|
||
|
to.tv_sec = (60 * 60);
|
||
|
to.tv_usec = 0;
|
||
|
|
||
|
int ret;
|
||
|
if ((ret = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
|
||
|
LOGE("select() failed (%s)", strerror(errno));
|
||
|
sleep(1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!ret) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (mAcceptedConnection != -1 && FD_ISSET(mAcceptedConnection, &read_fds)) {
|
||
|
if (processFrameworkCommand() == -1)
|
||
|
mAcceptedConnection = -1;
|
||
|
}
|
||
|
|
||
|
if (FD_ISSET(mFrameworkSock, &read_fds)) {
|
||
|
struct sockaddr addr;
|
||
|
socklen_t alen;
|
||
|
alen = sizeof(addr);
|
||
|
|
||
|
if (mAcceptedConnection != -1) {
|
||
|
close(mAcceptedConnection);
|
||
|
mAcceptedConnection = accept(mFrameworkSock, &addr, &alen);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ((mAcceptedConnection = accept(mFrameworkSock, &addr, &alen)) < 0) {
|
||
|
LOGE("Unable to accept framework connection (%s)",
|
||
|
strerror(errno));
|
||
|
}
|
||
|
else {
|
||
|
// Check what is external display connected(HDMI or Analog TV)
|
||
|
// and if HDCP Keys are present
|
||
|
if((!isHDMIPanel()) || checkHDCPPresent()) {
|
||
|
/*
|
||
|
If the external display is Analog TV - do not broadcast
|
||
|
Connected event.
|
||
|
If the HDCP keys are present, delay the broadcast to audio
|
||
|
as a different event will be triggred to notify audio.
|
||
|
*/
|
||
|
LOGD("threadLoop: delay Broadcast");
|
||
|
sendCommandToFramework(action_no_broadcast_online);
|
||
|
}
|
||
|
|
||
|
mSession = new SurfaceComposerClient();
|
||
|
processUeventQueue();
|
||
|
|
||
|
if (!mDriverOnline) {
|
||
|
LOGE("threadLoop: driver not online; use state-file");
|
||
|
sendCommandToFramework(action_offline);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOGD("threadLoop: Accepted connection from framework");
|
||
|
}
|
||
|
|
||
|
if (FD_ISSET(mUeventSock, &read_fds)) {
|
||
|
if (mAcceptedConnection == -1)
|
||
|
queueUevent();
|
||
|
else
|
||
|
processUevent();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::checkHDCPPresent() {
|
||
|
char present = '0';
|
||
|
//Open the hdcp file - to know if HDCP is supported
|
||
|
int hdcpFile = open(SYSFS_HDCP_PRESENT, O_RDONLY, 0);
|
||
|
if (hdcpFile < 0) {
|
||
|
LOGE("%s: hdcp_present file '%s' not found", __func__, SYSFS_HDCP_PRESENT);
|
||
|
} else {
|
||
|
//Read from the hdcp_present file
|
||
|
int r = read(hdcpFile, &present, 1);
|
||
|
if (r <= 0) {
|
||
|
LOGE("%s: hdcp_present file empty '%s'", __func__, SYSFS_HDCP_PRESENT);
|
||
|
}
|
||
|
}
|
||
|
close(hdcpFile);
|
||
|
return (present == '1') ? true : false;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::isHDMIPanel() {
|
||
|
int len = strlen("msmfbXX_");
|
||
|
bool ret = false;
|
||
|
struct fb_fix_screeninfo fb_finfo;
|
||
|
if (!openFramebuffer())
|
||
|
return false;
|
||
|
if (ioctl(fd1, FBIOGET_FSCREENINFO, &fb_finfo) < 0) {
|
||
|
LOGE("%s: Cannot retreive fixed screeninfo...!!", __func__);
|
||
|
} else {
|
||
|
LOGD("%s: fb_finfo.id == %s", __func__, fb_finfo.id);
|
||
|
if(fb_finfo.id[len] == HDMI_PANEL)
|
||
|
ret = true;
|
||
|
}
|
||
|
if (fd1 > 0) {
|
||
|
close(fd1);
|
||
|
fd1 = -1;
|
||
|
}
|
||
|
LOGD("%s: isHDMIPanel returns %d", __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::cableConnected(bool defaultValue) const
|
||
|
{
|
||
|
int hdmiStateFile = open(SYSFS_CONNECTED, O_RDONLY, 0);
|
||
|
if (hdmiStateFile < 0) {
|
||
|
LOGE("cableConnected: state file '%s' not found", SYSFS_CONNECTED);
|
||
|
return defaultValue;
|
||
|
} else {
|
||
|
char buf;
|
||
|
bool ret = defaultValue;
|
||
|
int err = read(hdmiStateFile, &buf, 1);
|
||
|
if (err <= 0) {
|
||
|
LOGE("cableConnected: empty state file '%s'", SYSFS_CONNECTED);
|
||
|
} else {
|
||
|
if (buf == '1') {
|
||
|
LOGD("cableConnected: %s indicates CONNECTED", SYSFS_CONNECTED);
|
||
|
ret = true;
|
||
|
} else {
|
||
|
LOGD("cableConnected: %s indicates DISCONNECTED", SYSFS_CONNECTED);
|
||
|
ret = false;
|
||
|
}
|
||
|
}
|
||
|
close(hdmiStateFile);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::processUeventMessage(uevent& event)
|
||
|
{
|
||
|
char buffer[64 * 1024];
|
||
|
int count;
|
||
|
char *s = buffer;
|
||
|
char *end;
|
||
|
int param_idx = 0;
|
||
|
int i;
|
||
|
bool first = true;
|
||
|
|
||
|
if ((count = recv(mUeventSock, buffer, sizeof(buffer), 0)) < 0) {
|
||
|
LOGE("Error receiving uevent (%s)", strerror(errno));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
end = s + count;
|
||
|
while (s < end) {
|
||
|
if (first) {
|
||
|
char *p;
|
||
|
for (p = s; *p != '@'; p++);
|
||
|
p++;
|
||
|
if (!strcasestr(p, DEVICE_NODE)) {
|
||
|
return false;
|
||
|
}
|
||
|
LOGD("device uevent (%s)", buffer);
|
||
|
event.path = new char[strlen(p) + 1];
|
||
|
strcpy(event.path, p);
|
||
|
first = false;
|
||
|
} else {
|
||
|
if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
|
||
|
char *a = s + strlen("ACTION=");
|
||
|
|
||
|
if (!strcmp(a, "add"))
|
||
|
event.action = action_add;
|
||
|
else if (!strcmp(a, "change"))
|
||
|
event.action = action_change;
|
||
|
else if (!strcmp(a, "remove"))
|
||
|
event.action = action_remove;
|
||
|
else if (!strcmp(a, "online"))
|
||
|
event.action = action_online;
|
||
|
else if (!strcmp(a, "offline"))
|
||
|
event.action = action_offline;
|
||
|
else
|
||
|
LOGD("%s: action (%s) unknown", __func__, a);
|
||
|
} else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) {
|
||
|
event.seqnum = atoi(s + strlen("SEQNUM="));
|
||
|
} else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) {
|
||
|
event.subsystem = new char[strlen(s + strlen("SUBSYSTEM=")) + 1];
|
||
|
strcpy(event.subsystem, (s + strlen("SUBSYSTEM=")));
|
||
|
} else if (!strncmp(s, "HDCP_STATE=", strlen("HDCP_STATE="))) {
|
||
|
if(!strcmp(s+strlen("HDCP_STATE="),"PASS")) {
|
||
|
//Event HDCP_STATE=PASS, send Audio On.
|
||
|
event.action = action_audio_on;
|
||
|
} else if(!strcmp(s+strlen("HDCP_STATE="), "FAIL")) {
|
||
|
//Event HDCP_STATE=FAIL, send Audio Off
|
||
|
event.action = action_audio_off;
|
||
|
}
|
||
|
} else {
|
||
|
event.param[param_idx] = new char[strlen(s) + 1];
|
||
|
strcpy(event.param[param_idx], s);
|
||
|
param_idx++;
|
||
|
}
|
||
|
}
|
||
|
s += strlen(s) + 1;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void HDMIDaemon::queueUevent()
|
||
|
{
|
||
|
HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1;
|
||
|
while (tmp != NULL && tmp->next != NULL)
|
||
|
tmp = tmp->next;
|
||
|
if (!tmp) {
|
||
|
tmp = new HDMIUeventQueue();
|
||
|
tmp->next = NULL;
|
||
|
if(!processUeventMessage(tmp->mEvent))
|
||
|
delete tmp;
|
||
|
else
|
||
|
mHDMIUeventQueueHead = tmp;
|
||
|
}
|
||
|
else {
|
||
|
tmp1 = new HDMIUeventQueue();
|
||
|
tmp1->next = NULL;
|
||
|
if(!processUeventMessage(tmp1->mEvent))
|
||
|
delete tmp1;
|
||
|
else
|
||
|
tmp->next = tmp1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HDMIDaemon::processUeventQueue()
|
||
|
{
|
||
|
HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1;
|
||
|
while (tmp != NULL) {
|
||
|
tmp1 = tmp;
|
||
|
if (tmp->mEvent.action) {
|
||
|
LOGD("processUeventQueue: event.action == %d", tmp->mEvent.action);
|
||
|
mDriverOnline = true;
|
||
|
sendCommandToFramework(tmp->mEvent.action);
|
||
|
}
|
||
|
tmp = tmp->next;
|
||
|
delete tmp1;
|
||
|
}
|
||
|
mHDMIUeventQueueHead = NULL;
|
||
|
}
|
||
|
|
||
|
void HDMIDaemon::processUevent()
|
||
|
{
|
||
|
uevent event;
|
||
|
if(processUeventMessage(event)) {
|
||
|
if (event.action) {
|
||
|
LOGD("processUevent: event.action == %d", event.action);
|
||
|
mDriverOnline = true;
|
||
|
sendCommandToFramework(event.action);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct disp_mode_timing_type {
|
||
|
int video_format;
|
||
|
|
||
|
int active_h;
|
||
|
int active_v;
|
||
|
|
||
|
int front_porch_h;
|
||
|
int pulse_width_h;
|
||
|
int back_porch_h;
|
||
|
|
||
|
int front_porch_v;
|
||
|
int pulse_width_v;
|
||
|
int back_porch_v;
|
||
|
|
||
|
int pixel_freq;
|
||
|
bool interlaced;
|
||
|
|
||
|
void set_info(struct fb_var_screeninfo &info) const;
|
||
|
};
|
||
|
|
||
|
void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const
|
||
|
{
|
||
|
info.reserved[0] = 0;
|
||
|
info.reserved[1] = 0;
|
||
|
info.reserved[2] = 0;
|
||
|
info.reserved[3] = video_format;
|
||
|
|
||
|
info.xoffset = 0;
|
||
|
info.yoffset = 0;
|
||
|
info.xres = active_h;
|
||
|
info.yres = active_v;
|
||
|
|
||
|
info.pixclock = pixel_freq*1000;
|
||
|
info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
|
||
|
|
||
|
info.right_margin = front_porch_h;
|
||
|
info.hsync_len = pulse_width_h;
|
||
|
info.left_margin = back_porch_h;
|
||
|
info.lower_margin = front_porch_v;
|
||
|
info.vsync_len = pulse_width_v;
|
||
|
info.upper_margin = back_porch_v;
|
||
|
}
|
||
|
|
||
|
/* Video formates supported by the HDMI Standard */
|
||
|
/* Indicates the resolution, pix clock and the aspect ratio */
|
||
|
#define m640x480p60_4_3 1
|
||
|
#define m720x480p60_4_3 2
|
||
|
#define m720x480p60_16_9 3
|
||
|
#define m1280x720p60_16_9 4
|
||
|
#define m1920x1080i60_16_9 5
|
||
|
#define m1440x480i60_4_3 6
|
||
|
#define m1440x480i60_16_9 7
|
||
|
#define m1920x1080p60_16_9 16
|
||
|
#define m720x576p50_4_3 17
|
||
|
#define m720x576p50_16_9 18
|
||
|
#define m1280x720p50_16_9 19
|
||
|
#define m1440x576i50_4_3 21
|
||
|
#define m1440x576i50_16_9 22
|
||
|
#define m1920x1080p50_16_9 31
|
||
|
#define m1920x1080p24_16_9 32
|
||
|
#define m1920x1080p25_16_9 33
|
||
|
#define m1920x1080p30_16_9 34
|
||
|
|
||
|
static struct disp_mode_timing_type supported_video_mode_lut[] = {
|
||
|
{m640x480p60_4_3, 640, 480, 16, 96, 48, 10, 2, 33, 25200, false},
|
||
|
{m720x480p60_4_3, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
|
||
|
{m720x480p60_16_9, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
|
||
|
{m1280x720p60_16_9, 1280, 720, 110, 40, 220, 5, 5, 20, 74250, false},
|
||
|
{m1920x1080i60_16_9, 1920, 540, 88, 44, 148, 2, 5, 5, 74250, false},
|
||
|
{m1440x480i60_4_3, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
|
||
|
{m1440x480i60_16_9, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
|
||
|
{m1920x1080p60_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 148500, false},
|
||
|
{m720x576p50_4_3, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
|
||
|
{m720x576p50_16_9, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
|
||
|
{m1280x720p50_16_9, 1280, 720, 440, 40, 220, 5, 5, 20, 74250, false},
|
||
|
{m1440x576i50_4_3, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
|
||
|
{m1440x576i50_16_9, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
|
||
|
{m1920x1080p50_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 148500, false},
|
||
|
{m1920x1080p24_16_9, 1920, 1080, 638, 44, 148, 4, 5, 36, 74250, false},
|
||
|
{m1920x1080p25_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 74250, false},
|
||
|
{m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false},
|
||
|
};
|
||
|
|
||
|
bool HDMIDaemon::readResolution()
|
||
|
{
|
||
|
int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
|
||
|
|
||
|
memset(mEDIDs, 0, sizeof(mEDIDs));
|
||
|
if (hdmiEDIDFile < 0) {
|
||
|
LOGE("%s: edid_modes file '%s' not found", __func__, SYSFS_EDID_MODES);
|
||
|
return false;
|
||
|
} else {
|
||
|
int r = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
|
||
|
if (r <= 0)
|
||
|
LOGE("%s: edid_modes file empty '%s'", __func__, SYSFS_EDID_MODES);
|
||
|
else {
|
||
|
while (r > 1 && isspace(mEDIDs[r-1]))
|
||
|
--r;
|
||
|
mEDIDs[r] = 0;
|
||
|
}
|
||
|
}
|
||
|
close(hdmiEDIDFile);
|
||
|
|
||
|
return (strlen(mEDIDs) > 0);
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::openFramebuffer()
|
||
|
{
|
||
|
if (fd1 == -1) {
|
||
|
fd1 = open("/dev/graphics/fb1", O_RDWR);
|
||
|
if (fd1 < 0)
|
||
|
LOGE("ERROR: /dev/graphics/fb1 not available\n");
|
||
|
}
|
||
|
return (fd1 > 0);
|
||
|
}
|
||
|
|
||
|
inline bool HDMIDaemon::isValidMode(int ID)
|
||
|
{
|
||
|
return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
|
||
|
}
|
||
|
|
||
|
void HDMIDaemon::setResolution(int ID)
|
||
|
{
|
||
|
struct fb_var_screeninfo info;
|
||
|
if (!openFramebuffer())
|
||
|
return;
|
||
|
//If its a valid mode and its a new ID - update var_screeninfo
|
||
|
if ((isValidMode(ID)) && mCurrentID != ID) {
|
||
|
const struct disp_mode_timing_type *mode = &supported_video_mode_lut[0];
|
||
|
for (unsigned int i = 0; i < sizeof(supported_video_mode_lut)/sizeof(*supported_video_mode_lut); ++i) {
|
||
|
const struct disp_mode_timing_type *cur = &supported_video_mode_lut[i];
|
||
|
if (cur->video_format == ID)
|
||
|
mode = cur;
|
||
|
}
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIOUT_DISABLE);
|
||
|
ioctl(fd1, FBIOGET_VSCREENINFO, &info);
|
||
|
LOGD("GET Info<ID=%d %dx%d (%d,%d,%d), (%d,%d,%d) %dMHz>",
|
||
|
info.reserved[3], info.xres, info.yres,
|
||
|
info.right_margin, info.hsync_len, info.left_margin,
|
||
|
info.lower_margin, info.vsync_len, info.upper_margin,
|
||
|
info.pixclock/1000/1000);
|
||
|
mode->set_info(info);
|
||
|
LOGD("SET Info<ID=%d => Info<ID=%d %dx%d (%d,%d,%d), (%d,%d,%d) %dMHz>", ID,
|
||
|
info.reserved[3], info.xres, info.yres,
|
||
|
info.right_margin, info.hsync_len, info.left_margin,
|
||
|
info.lower_margin, info.vsync_len, info.upper_margin,
|
||
|
info.pixclock/1000/1000);
|
||
|
info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
|
||
|
ioctl(fd1, FBIOPUT_VSCREENINFO, &info);
|
||
|
mCurrentID = ID;
|
||
|
}
|
||
|
//Powerup
|
||
|
ioctl(fd1, FBIOBLANK, FB_BLANK_UNBLANK);
|
||
|
ioctl(fd1, FBIOGET_VSCREENINFO, &info);
|
||
|
//Pan_Display
|
||
|
ioctl(fd1, FBIOPAN_DISPLAY, &info);
|
||
|
property_set("hw.hdmiON", "1");
|
||
|
//Inform SF about HDMI
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIOUT_ENABLE);
|
||
|
|
||
|
}
|
||
|
|
||
|
int HDMIDaemon::processFrameworkCommand()
|
||
|
{
|
||
|
char buffer[128];
|
||
|
int ret;
|
||
|
|
||
|
if ((ret = read(mAcceptedConnection, buffer, sizeof(buffer) -1)) < 0) {
|
||
|
LOGE("Unable to read framework command (%s)", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!ret)
|
||
|
return -1;
|
||
|
|
||
|
buffer[ret] = 0;
|
||
|
|
||
|
if (!strcmp(buffer, HDMI_CMD_ENABLE_HDMI)) {
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIFB_OPEN);
|
||
|
if (!openFramebuffer())
|
||
|
return -1;
|
||
|
LOGD(HDMI_CMD_ENABLE_HDMI);
|
||
|
if(mNxtMode != -1) {
|
||
|
LOGD("processFrameworkCommand: setResolution with =%d", mNxtMode);
|
||
|
setResolution(mNxtMode);
|
||
|
}
|
||
|
} else if (!strcmp(buffer, HDMI_CMD_DISABLE_HDMI)) {
|
||
|
LOGD(HDMI_CMD_DISABLE_HDMI);
|
||
|
|
||
|
if (!openFramebuffer())
|
||
|
return -1;
|
||
|
property_set("hw.hdmiON", "0");
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIOUT_DISABLE);
|
||
|
close(fd1);
|
||
|
fd1 = -1;
|
||
|
} else if (!strncmp(buffer, HDMI_CMD_SET_ASWIDTH, strlen(HDMI_CMD_SET_ASWIDTH))) {
|
||
|
float asWidthRatio;
|
||
|
int ret = sscanf(buffer, HDMI_CMD_SET_ASWIDTH "%f", &asWidthRatio);
|
||
|
if(ret==1) {
|
||
|
SurfaceComposerClient::setActionSafeWidthRatio(asWidthRatio);
|
||
|
}
|
||
|
} else if (!strncmp(buffer, HDMI_CMD_SET_ASHEIGHT, strlen(HDMI_CMD_SET_ASHEIGHT))) {
|
||
|
float asHeightRatio;
|
||
|
int ret = sscanf(buffer, HDMI_CMD_SET_ASHEIGHT "%f", &asHeightRatio);
|
||
|
if(ret==1) {
|
||
|
SurfaceComposerClient::setActionSafeHeightRatio(asHeightRatio);
|
||
|
}
|
||
|
} else if (!strncmp(buffer, HDMI_CMD_HPDOPTION, strlen(HDMI_CMD_HPDOPTION))) {
|
||
|
int option;
|
||
|
int ret = sscanf(buffer, HDMI_CMD_HPDOPTION "%d", &option);
|
||
|
if (ret == 1) {
|
||
|
LOGD(HDMI_CMD_HPDOPTION ": %d", option);
|
||
|
if (option)
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIHPD_ON);
|
||
|
else
|
||
|
SurfaceComposerClient::enableHDMIOutput(HDMIHPD_OFF);
|
||
|
writeHPDOption(option);
|
||
|
}
|
||
|
} else {
|
||
|
int mode;
|
||
|
int ret = sscanf(buffer, HDMI_CMD_CHANGE_MODE "%d", &mode);
|
||
|
if (ret == 1) {
|
||
|
LOGD(HDMI_CMD_CHANGE_MODE);
|
||
|
/* To change the resolution */
|
||
|
char prop_val[PROPERTY_VALUE_MAX];
|
||
|
property_get("enable.hdmi.edid", prop_val, "0");
|
||
|
int val = atoi(prop_val);
|
||
|
if(val == 1) {
|
||
|
/* Based on the hw.yRes set the resolution */
|
||
|
char property_value[PROPERTY_VALUE_MAX];
|
||
|
property_get("hdmi.yRes", property_value, "0");
|
||
|
int yres = atoi(property_value);
|
||
|
switch(yres){
|
||
|
case 480:
|
||
|
mode = 3;
|
||
|
break;
|
||
|
case 720:
|
||
|
mode = 4;
|
||
|
break;
|
||
|
case 1080:
|
||
|
mode = 16;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// If we have a valid fd1 - setresolution
|
||
|
if(fd1 > 0) {
|
||
|
setResolution(mode);
|
||
|
} else {
|
||
|
// Store the mode
|
||
|
mNxtMode = mode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::sendCommandToFramework(uevent_action action)
|
||
|
{
|
||
|
char message[512];
|
||
|
|
||
|
switch (action)
|
||
|
{
|
||
|
//Disconnect
|
||
|
case action_offline:
|
||
|
strncpy(message, HDMI_EVT_DISCONNECTED, sizeof(message));
|
||
|
break;
|
||
|
//Connect
|
||
|
case action_online:
|
||
|
readResolution();
|
||
|
snprintf(message, sizeof(message), "%s: %s", HDMI_EVT_CONNECTED, mEDIDs);
|
||
|
break;
|
||
|
//action_audio_on
|
||
|
case action_audio_on:
|
||
|
strncpy(message, HDMI_EVT_AUDIO_ON, sizeof(message));
|
||
|
break;
|
||
|
//action_audio_off
|
||
|
case action_audio_off:
|
||
|
strncpy(message, HDMI_EVT_AUDIO_OFF, sizeof(message));
|
||
|
break;
|
||
|
//action_no_broadcast_online
|
||
|
case action_no_broadcast_online:
|
||
|
strncpy(message, HDMI_EVT_NO_BROADCAST_ONLINE, sizeof(message));
|
||
|
break;
|
||
|
default:
|
||
|
LOGE("sendCommandToFramework: Unknown event received");
|
||
|
break;
|
||
|
}
|
||
|
int result = write(mAcceptedConnection, message, strlen(message) + 1);
|
||
|
LOGD("sendCommandToFramework: '%s' %s", message, result >= 0 ? "successful" : "failed");
|
||
|
return result >= 0;
|
||
|
}
|
||
|
|
||
|
bool HDMIDaemon::writeHPDOption(int userOption) const
|
||
|
{
|
||
|
bool ret = true;
|
||
|
int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
|
||
|
if (hdmiHPDFile < 0) {
|
||
|
LOGE("writeHPDOption: state file '%s' not found", SYSFS_HPD);
|
||
|
ret = false;
|
||
|
} else {
|
||
|
int err = -1;
|
||
|
if(userOption)
|
||
|
err = write(hdmiHPDFile, "1", 2);
|
||
|
else
|
||
|
err = write(hdmiHPDFile, "0" , 2);
|
||
|
if (err <= 0) {
|
||
|
LOGE("writeHPDOption: file write failed '%s'", SYSFS_HPD);
|
||
|
ret = false;
|
||
|
}
|
||
|
close(hdmiHPDFile);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
|
||
|
}
|
||
|
; // namespace android
|