1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 * Copyright (C) 2012-2013, The Linux Foundation. All rights reserved.
4 *
5 * Not a Contribution, Apache license notifications and license are
6 * retained for attribution purposes only.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #define DEBUG 0
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <media/IAudioPolicyService.h>
25 #include <media/AudioSystem.h>
26 #include <utils/threads.h>
27 #include <utils/Errors.h>
28 #include <utils/Log.h>
29
30 #include <linux/msm_mdp.h>
31 #include <video/msm_hdmi_modes.h>
32 #include <linux/fb.h>
33 #include <sys/ioctl.h>
34 #include <sys/poll.h>
35 #include <sys/resource.h>
36 #include <cutils/properties.h>
37 #include "hwc_utils.h"
38 #include "external.h"
39
40 using namespace android;
41
42 namespace qhwc {
43
44 #define MAX_FRAME_BUFFER_NAME_SIZE (80)
45 #define MAX_DISPLAY_DEVICES (3)
46 #define MAX_SYSFS_FILE_PATH 255
47 #define UNKNOWN_STRING "unknown"
48 #define SPD_NAME_LENGTH 16
49
50 const char* msmFbDevicePath[] = { "/dev/graphics/fb1",
51 "/dev/graphics/fb2"};
52
53 /*
54 * Updates extDeviceFbIndex Array with the correct frame buffer indices
55 * of avaiable external devices
56 *
57 */
updateExtDispDevFbIndex()58 void ExternalDisplay::updateExtDispDevFbIndex()
59 {
60 FILE *displayDeviceFP = NULL;
61 char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
62 char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
63
64 for(int j = 1; j < MAX_DISPLAY_DEVICES; j++) {
65 snprintf (msmFbTypePath, sizeof(msmFbTypePath),
66 "/sys/class/graphics/fb%d/msm_fb_type", j);
67 displayDeviceFP = fopen(msmFbTypePath, "r");
68 if(displayDeviceFP){
69 fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
70 displayDeviceFP);
71 if(strncmp(fbType, "dtv panel", strlen("dtv panel")) == 0){
72 ALOGD_IF(DEBUG,"hdmi framebuffer index is %d",j);
73 mHdmiFbNum = j;
74 } else if(strncmp(fbType, "writeback panel",
75 strlen("writeback panel")) == 0){
76 ALOGD_IF(DEBUG,"wfd framebuffer index is %d",j);
77 mWfdFbNum = j;
78 }
79 fclose(displayDeviceFP);
80 }
81 }
82 ALOGD_IF(DEBUG,"%s: mHdmiFbNum: %d mWfdFbNum: %d ",__FUNCTION__,
83 mHdmiFbNum, mWfdFbNum);
84 }
85
configureHDMIDisplay()86 int ExternalDisplay::configureHDMIDisplay() {
87 openFrameBuffer(mHdmiFbNum);
88 if(mFd == -1)
89 return -1;
90 readCEUnderscanInfo();
91 readResolution();
92 // TODO: Move this to activate
93 /* Used for changing the resolution
94 * getUserMode will get the preferred
95 * mode set thru adb shell */
96 int mode = getUserMode();
97 if (mode == -1) {
98 //Get the best mode and set
99 mode = getBestMode();
100 }
101 setResolution(mode);
102 setDpyHdmiAttr();
103 setExternalDisplay(true, mHdmiFbNum);
104 return 0;
105 }
106
configureWFDDisplay()107 int ExternalDisplay::configureWFDDisplay() {
108 int ret = 0;
109 if(mConnectedFbNum == mHdmiFbNum) {
110 ALOGE("%s: Cannot process WFD connection while HDMI is active",
111 __FUNCTION__);
112 return -1;
113 }
114 openFrameBuffer(mWfdFbNum);
115 if(mFd == -1)
116 return -1;
117 ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
118 if(ret < 0) {
119 ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
120 strerror(errno));
121 }
122 setDpyWfdAttr();
123 setExternalDisplay(true, mWfdFbNum);
124 return 0;
125 }
126
teardownHDMIDisplay()127 int ExternalDisplay::teardownHDMIDisplay() {
128 if(mConnectedFbNum == mHdmiFbNum) {
129 // hdmi offline event..!
130 closeFrameBuffer();
131 resetInfo();
132 setExternalDisplay(false);
133 }
134 return 0;
135 }
136
teardownWFDDisplay()137 int ExternalDisplay::teardownWFDDisplay() {
138 if(mConnectedFbNum == mWfdFbNum) {
139 // wfd offline event..!
140 closeFrameBuffer();
141 memset(&mVInfo, 0, sizeof(mVInfo));
142 setExternalDisplay(false);
143 }
144 return 0;
145 }
146
processUEventOnline(const char * str)147 void ExternalDisplay::processUEventOnline(const char *str) {
148 const char *s1 = str + strlen("change@/devices/virtual/switch/");
149 if(!strncmp(s1,"hdmi",strlen(s1))) {
150 // hdmi online event..!
151 configureHDMIDisplay();
152 // set system property
153 property_set("hw.hdmiON", "1");
154 }else if(!strncmp(s1,"wfd",strlen(s1))) {
155 // wfd online event..!
156 configureWFDDisplay();
157 }
158 }
159
processUEventOffline(const char * str)160 void ExternalDisplay::processUEventOffline(const char *str) {
161 const char *s1 = str + strlen("change@/devices/virtual/switch/");
162 if(!strncmp(s1,"hdmi",strlen(s1))) {
163 teardownHDMIDisplay();
164 // unset system property
165 property_set("hw.hdmiON", "0");
166 }else if(!strncmp(s1,"wfd",strlen(s1))) {
167 teardownWFDDisplay();
168 }
169 }
170
ExternalDisplay(hwc_context_t * ctx)171 ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
172 mCurrentMode(-1), mConnected(0), mConnectedFbNum(0), mModeCount(0),
173 mUnderscanSupported(false), mHwcContext(ctx), mHdmiFbNum(-1),
174 mWfdFbNum(-1), mExtDpyNum(HWC_DISPLAY_EXTERNAL)
175 {
176 memset(&mVInfo, 0, sizeof(mVInfo));
177 //Determine the fb index for external display devices.
178 updateExtDispDevFbIndex();
179 // disable HPD at start, it will be enabled later
180 // when the display powers on
181 // This helps for framework reboot or adb shell stop/start
182 writeHPDOption(0);
183
184 // for HDMI - retreive all the modes supported by the driver
185 if(mHdmiFbNum != -1) {
186 supported_video_mode_lut =
187 new msm_hdmi_mode_timing_info[HDMI_VFRMT_MAX];
188 // Populate the mode table for supported modes
189 MSM_HDMI_MODES_INIT_TIMINGS(supported_video_mode_lut);
190 MSM_HDMI_MODES_SET_SUPP_TIMINGS(supported_video_mode_lut,
191 MSM_HDMI_MODES_ALL);
192 // Update the Source Product Information
193 // Vendor Name
194 setSPDInfo("vendor_name", "ro.product.manufacturer");
195 // Product Description
196 setSPDInfo("product_description", "ro.product.name");
197 }
198 }
199 /* gets the product manufacturer and product name and writes it
200 * to the sysfs node, so that the driver can get that information
201 * Used to show QCOM 8974 instead of Input 1 for example
202 */
setSPDInfo(const char * node,const char * property)203 void ExternalDisplay::setSPDInfo(const char* node, const char* property) {
204 int err = -1;
205 char info[PROPERTY_VALUE_MAX];
206 char sysFsSPDFilePath[MAX_SYSFS_FILE_PATH];
207 memset(sysFsSPDFilePath, 0, sizeof(sysFsSPDFilePath));
208 snprintf(sysFsSPDFilePath , sizeof(sysFsSPDFilePath),
209 "/sys/devices/virtual/graphics/fb%d/%s",
210 mHdmiFbNum, node);
211 int spdFile = open(sysFsSPDFilePath, O_RDWR, 0);
212 if (spdFile < 0) {
213 ALOGE("%s: file '%s' not found : ret = %d"
214 "err str: %s", __FUNCTION__, sysFsSPDFilePath,
215 spdFile, strerror(errno));
216 } else {
217 memset(info, 0, sizeof(info));
218 property_get(property, info, UNKNOWN_STRING);
219 ALOGD_IF(DEBUG, "In %s: %s = %s", __FUNCTION__, property, info);
220 if (strncmp(info, UNKNOWN_STRING, SPD_NAME_LENGTH)) {
221 err = write(spdFile, info, strlen(info));
222 if (err <= 0) {
223 ALOGE("%s: file write failed for '%s'"
224 "err no = %d", __FUNCTION__, sysFsSPDFilePath, errno);
225 }
226 } else {
227 ALOGD_IF(DEBUG, "%s: property_get failed for SPD %s",
228 __FUNCTION__, node);
229 }
230 close(spdFile);
231 }
232 }
233
setEDIDMode(int resMode)234 void ExternalDisplay::setEDIDMode(int resMode) {
235 ALOGD_IF(DEBUG,"resMode=%d ", resMode);
236 {
237 Mutex::Autolock lock(mExtDispLock);
238 setExternalDisplay(false);
239 openFrameBuffer(mHdmiFbNum);
240 setResolution(resMode);
241 }
242 setExternalDisplay(true, mHdmiFbNum);
243 }
244
setHPD(uint32_t startEnd)245 void ExternalDisplay::setHPD(uint32_t startEnd) {
246 ALOGD_IF(DEBUG,"HPD enabled=%d", startEnd);
247 writeHPDOption(startEnd);
248 }
249
setActionSafeDimension(int w,int h)250 void ExternalDisplay::setActionSafeDimension(int w, int h) {
251 ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
252 Mutex::Autolock lock(mExtDispLock);
253 char actionsafeWidth[PROPERTY_VALUE_MAX];
254 char actionsafeHeight[PROPERTY_VALUE_MAX];
255 snprintf(actionsafeWidth, sizeof(actionsafeWidth), "%d", w);
256 property_set("persist.sys.actionsafe.width", actionsafeWidth);
257 snprintf(actionsafeHeight, sizeof(actionsafeHeight), "%d", h);
258 property_set("persist.sys.actionsafe.height", actionsafeHeight);
259 setExternalDisplay(true, mHdmiFbNum);
260 }
261
getModeCount() const262 int ExternalDisplay::getModeCount() const {
263 ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
264 Mutex::Autolock lock(mExtDispLock);
265 return mModeCount;
266 }
267
getEDIDModes(int * out) const268 void ExternalDisplay::getEDIDModes(int *out) const {
269 Mutex::Autolock lock(mExtDispLock);
270 for(int i = 0;i < mModeCount;i++) {
271 out[i] = mEDIDModes[i];
272 }
273 }
274
readCEUnderscanInfo()275 void ExternalDisplay::readCEUnderscanInfo()
276 {
277 int hdmiScanInfoFile = -1;
278 int len = -1;
279 char scanInfo[17];
280 char *ce_info_str = NULL;
281 const char token[] = ", \n";
282 int ce_info = -1;
283 char sysFsScanInfoFilePath[MAX_SYSFS_FILE_PATH];
284 snprintf(sysFsScanInfoFilePath, sizeof(sysFsScanInfoFilePath),
285 "/sys/devices/virtual/graphics/fb%d/"
286 "scan_info", mHdmiFbNum);
287
288 memset(scanInfo, 0, sizeof(scanInfo));
289 hdmiScanInfoFile = open(sysFsScanInfoFilePath, O_RDONLY, 0);
290 if (hdmiScanInfoFile < 0) {
291 ALOGD_IF(DEBUG, "%s: scan_info file '%s' not found",
292 __FUNCTION__, sysFsScanInfoFilePath);
293 return;
294 } else {
295 len = read(hdmiScanInfoFile, scanInfo, sizeof(scanInfo)-1);
296 ALOGD("%s: Scan Info string: %s length = %d",
297 __FUNCTION__, scanInfo, len);
298 if (len <= 0) {
299 close(hdmiScanInfoFile);
300 ALOGE("%s: Scan Info file empty '%s'",
301 __FUNCTION__, sysFsScanInfoFilePath);
302 return;
303 }
304 scanInfo[len] = '\0'; /* null terminate the string */
305 }
306 close(hdmiScanInfoFile);
307
308 /*
309 * The scan_info contains the three fields
310 * PT - preferred video format
311 * IT - video format
312 * CE video format - containing the underscan support information
313 */
314
315 /* PT */
316 ce_info_str = strtok(scanInfo, token);
317 if (ce_info_str) {
318 /* IT */
319 ce_info_str = strtok(NULL, token);
320 if (ce_info_str) {
321 /* CE */
322 ce_info_str = strtok(NULL, token);
323 if (ce_info_str)
324 ce_info = atoi(ce_info_str);
325 }
326 }
327
328 if (ce_info_str) {
329 // ce_info contains the underscan information
330 if (ce_info == EXT_SCAN_ALWAYS_UNDERSCANED ||
331 ce_info == EXT_SCAN_BOTH_SUPPORTED)
332 // if TV supported underscan, then driver will always underscan
333 // hence no need to apply action safe rectangle
334 mUnderscanSupported = true;
335 } else {
336 ALOGE("%s: scan_info string error", __FUNCTION__);
337 }
338
339 // Store underscan support info in a system property
340 const char* prop = (mUnderscanSupported) ? "1" : "0";
341 property_set("hw.underscan_supported", prop);
342 return;
343 }
344
~ExternalDisplay()345 ExternalDisplay::~ExternalDisplay()
346 {
347 delete [] supported_video_mode_lut;
348 closeFrameBuffer();
349 }
350
351 /*
352 * sets the fb_var_screeninfo from the hdmi_mode_timing_info
353 */
setDisplayTiming(struct fb_var_screeninfo & info,const msm_hdmi_mode_timing_info * mode)354 void setDisplayTiming(struct fb_var_screeninfo &info,
355 const msm_hdmi_mode_timing_info* mode)
356 {
357 info.reserved[0] = 0;
358 info.reserved[1] = 0;
359 info.reserved[2] = 0;
360 #ifndef FB_METADATA_VIDEO_INFO_CODE_SUPPORT
361 info.reserved[3] = (info.reserved[3] & 0xFFFF) |
362 (mode->video_format << 16);
363 #endif
364 info.xoffset = 0;
365 info.yoffset = 0;
366 info.xres = mode->active_h;
367 info.yres = mode->active_v;
368
369 info.pixclock = (mode->pixel_freq)*1000;
370 info.vmode = mode->interlaced ?
371 FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
372
373 info.right_margin = mode->front_porch_h;
374 info.hsync_len = mode->pulse_width_h;
375 info.left_margin = mode->back_porch_h;
376 info.lower_margin = mode->front_porch_v;
377 info.vsync_len = mode->pulse_width_v;
378 info.upper_margin = mode->back_porch_v;
379 }
380
parseResolution(char * edidStr,int * edidModes)381 int ExternalDisplay::parseResolution(char* edidStr, int* edidModes)
382 {
383 char delim = ',';
384 int count = 0;
385 char *start, *end;
386 // EDIDs are string delimited by ','
387 // Ex: 16,4,5,3,32,34,1
388 // Parse this string to get mode(int)
389 start = (char*) edidStr;
390 end = &delim;
391 while(*end == delim) {
392 edidModes[count] = (int) strtol(start, &end, 10);
393 start = end+1;
394 count++;
395 }
396 ALOGD_IF(DEBUG, "In %s: count = %d", __FUNCTION__, count);
397 for (int i = 0; i < count; i++)
398 ALOGD_IF(DEBUG, "Mode[%d] = %d", i, edidModes[i]);
399 return count;
400 }
401
readResolution()402 bool ExternalDisplay::readResolution()
403 {
404 char sysFsEDIDFilePath[MAX_SYSFS_FILE_PATH];
405 snprintf(sysFsEDIDFilePath , sizeof(sysFsEDIDFilePath),
406 "/sys/devices/virtual/graphics/fb%d/edid_modes", mHdmiFbNum);
407
408 int hdmiEDIDFile = open(sysFsEDIDFilePath, O_RDONLY, 0);
409 int len = -1;
410
411 if (hdmiEDIDFile < 0) {
412 ALOGE("%s: edid_modes file '%s' not found",
413 __FUNCTION__, sysFsEDIDFilePath);
414 return false;
415 } else {
416 len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
417 ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
418 __FUNCTION__, mEDIDs, len);
419 if ( len <= 0) {
420 ALOGE("%s: edid_modes file empty '%s'",
421 __FUNCTION__, sysFsEDIDFilePath);
422 }
423 else {
424 while (len > 1 && isspace(mEDIDs[len-1]))
425 --len;
426 mEDIDs[len] = 0;
427 }
428 }
429 close(hdmiEDIDFile);
430 if(len > 0) {
431 // Get EDID modes from the EDID strings
432 mModeCount = parseResolution(mEDIDs, mEDIDModes);
433 ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
434 mModeCount);
435 }
436
437 return (strlen(mEDIDs) > 0);
438 }
439
openFrameBuffer(int fbNum)440 bool ExternalDisplay::openFrameBuffer(int fbNum)
441 {
442 if (mFd == -1) {
443 mFd = open(msmFbDevicePath[fbNum-1], O_RDWR);
444 if (mFd < 0)
445 ALOGE("%s: %s is not available", __FUNCTION__,
446 msmFbDevicePath[fbNum-1]);
447 if(mHwcContext) {
448 mHwcContext->dpyAttr[mExtDpyNum].fd = mFd;
449 }
450 }
451 return (mFd > 0);
452 }
453
closeFrameBuffer()454 bool ExternalDisplay::closeFrameBuffer()
455 {
456 int ret = 0;
457 if(mFd >= 0) {
458 ret = close(mFd);
459 mFd = -1;
460 }
461 if(mHwcContext) {
462 mHwcContext->dpyAttr[mExtDpyNum].fd = mFd;
463 }
464 return (ret == 0);
465 }
466
467 // clears the vinfo, edid, best modes
resetInfo()468 void ExternalDisplay::resetInfo()
469 {
470 memset(&mVInfo, 0, sizeof(mVInfo));
471 memset(mEDIDs, 0, sizeof(mEDIDs));
472 memset(mEDIDModes, 0, sizeof(mEDIDModes));
473 mModeCount = 0;
474 mCurrentMode = -1;
475 mUnderscanSupported = false;
476 // Reset the underscan supported system property
477 const char* prop = "0";
478 property_set("hw.underscan_supported", prop);
479 }
480
getModeOrder(int mode)481 int ExternalDisplay::getModeOrder(int mode)
482 {
483 // XXX: We dont support interlaced modes but having
484 // it here for future
485 switch (mode) {
486 default:
487 case HDMI_VFRMT_1440x480i60_4_3:
488 return 1; // 480i 4:3
489 case HDMI_VFRMT_1440x480i60_16_9:
490 return 2; // 480i 16:9
491 case HDMI_VFRMT_1440x576i50_4_3:
492 return 3; // i576i 4:3
493 case HDMI_VFRMT_1440x576i50_16_9:
494 return 4; // 576i 16:9
495 case HDMI_VFRMT_1920x1080i60_16_9:
496 return 5; // 1080i 16:9
497 case HDMI_VFRMT_640x480p60_4_3:
498 return 6; // 640x480 4:3
499 case HDMI_VFRMT_720x480p60_4_3:
500 return 7; // 480p 4:3
501 case HDMI_VFRMT_720x480p60_16_9:
502 return 8; // 480p 16:9
503 case HDMI_VFRMT_720x576p50_4_3:
504 return 9; // 576p 4:3
505 case HDMI_VFRMT_720x576p50_16_9:
506 return 10; // 576p 16:9
507 case HDMI_VFRMT_1024x768p60_4_3:
508 return 11; // 768p 4:3 Vesa format
509 case HDMI_VFRMT_1280x1024p60_5_4:
510 return 12; // 1024p Vesa format
511 case HDMI_VFRMT_1280x720p50_16_9:
512 return 13; // 720p@50Hz
513 case HDMI_VFRMT_1280x720p60_16_9:
514 return 14; // 720p@60Hz
515 case HDMI_VFRMT_1920x1080p24_16_9:
516 return 15; //1080p@24Hz
517 case HDMI_VFRMT_1920x1080p25_16_9:
518 return 16; //108-p@25Hz
519 case HDMI_VFRMT_1920x1080p30_16_9:
520 return 17; //1080p@30Hz
521 case HDMI_VFRMT_1920x1080p50_16_9:
522 return 18; //1080p@50Hz
523 case HDMI_VFRMT_1920x1080p60_16_9:
524 return 19; //1080p@60Hz
525 case HDMI_VFRMT_2560x1600p60_16_9:
526 return 20; //WQXGA@60Hz541
527 case HDMI_VFRMT_3840x2160p24_16_9:
528 return 21;//2160@24Hz
529 case HDMI_VFRMT_3840x2160p25_16_9:
530 return 22;//2160@25Hz
531 case HDMI_VFRMT_3840x2160p30_16_9:
532 return 23; //2160@30Hz
533 case HDMI_VFRMT_4096x2160p24_16_9:
534 return 24; //4kx2k@24Hz
535 }
536 }
537
538 /// Returns the user mode set(if any) using adb shell
getUserMode()539 int ExternalDisplay::getUserMode() {
540 /* Based on the property set the resolution */
541 char property_value[PROPERTY_VALUE_MAX];
542 property_get("hw.hdmi.resolution", property_value, "-1");
543 int mode = atoi(property_value);
544 // We dont support interlaced modes
545 if(isValidMode(mode) && !isInterlacedMode(mode)) {
546 ALOGD_IF(DEBUG, "%s: setting the HDMI mode = %d", __FUNCTION__, mode);
547 return mode;
548 }
549 return -1;
550 }
551
552 // Get the best mode for the current HD TV
getBestMode()553 int ExternalDisplay::getBestMode() {
554 int bestOrder = 0;
555 int bestMode = HDMI_VFRMT_640x480p60_4_3;
556 Mutex::Autolock lock(mExtDispLock);
557 // for all the edid read, get the best mode
558 for(int i = 0; i < mModeCount; i++) {
559 int mode = mEDIDModes[i];
560 int order = getModeOrder(mode);
561 if (order > bestOrder) {
562 bestOrder = order;
563 bestMode = mode;
564 }
565 }
566 return bestMode;
567 }
568
isValidMode(int ID)569 inline bool ExternalDisplay::isValidMode(int ID)
570 {
571 bool valid = false;
572 for (int i = 0; i < mModeCount; i++) {
573 if(ID == mEDIDModes[i]) {
574 valid = true;
575 break;
576 }
577 }
578 return valid;
579 }
580
581 // returns true if the mode(ID) is interlaced mode format
isInterlacedMode(int ID)582 bool ExternalDisplay::isInterlacedMode(int ID) {
583 bool interlaced = false;
584 switch(ID) {
585 case HDMI_VFRMT_1440x480i60_4_3:
586 case HDMI_VFRMT_1440x480i60_16_9:
587 case HDMI_VFRMT_1440x576i50_4_3:
588 case HDMI_VFRMT_1440x576i50_16_9:
589 case HDMI_VFRMT_1920x1080i60_16_9:
590 interlaced = true;
591 break;
592 default:
593 interlaced = false;
594 break;
595 }
596 return interlaced;
597 }
598
setResolution(int ID)599 void ExternalDisplay::setResolution(int ID)
600 {
601 struct fb_var_screeninfo info;
602 int ret = 0;
603 ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
604 if(ret < 0) {
605 ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
606 strerror(errno));
607 }
608 ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
609 "(%d,%d,%d) %dMHz>", __FUNCTION__,
610 mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
611 mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
612 mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
613 mVInfo.pixclock/1000/1000);
614 //If its a new ID - update var_screeninfo
615 if ((isValidMode(ID)) && mCurrentMode != ID) {
616 const struct msm_hdmi_mode_timing_info *mode =
617 &supported_video_mode_lut[0];
618 for (unsigned int i = 0; i < HDMI_VFRMT_MAX; ++i) {
619 const struct msm_hdmi_mode_timing_info *cur =
620 &supported_video_mode_lut[i];
621 if (cur->video_format == (uint32_t)ID) {
622 mode = cur;
623 break;
624 }
625 }
626 setDisplayTiming(mVInfo, mode);
627 ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
628 "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
629 mode->video_format, mVInfo.xres, mVInfo.yres,
630 mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
631 mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
632 mVInfo.pixclock/1000/1000);
633 #ifdef FB_METADATA_VIDEO_INFO_CODE_SUPPORT
634 struct msmfb_metadata metadata;
635 memset(&metadata, 0 , sizeof(metadata));
636 metadata.op = metadata_op_vic;
637 metadata.data.video_info_code = mode->video_format;
638 if (ioctl(mFd, MSMFB_METADATA_SET, &metadata) == -1) {
639 ALOGD("In %s: MSMFB_METADATA_SET failed Err Str = %s",
640 __FUNCTION__, strerror(errno));
641 }
642 #endif
643 mVInfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
644 ret = ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo);
645 if(ret < 0) {
646 ALOGD("In %s: FBIOPUT_VSCREENINFO failed Err Str = %s",
647 __FUNCTION__, strerror(errno));
648 }
649 mCurrentMode = ID;
650 }
651 }
652
setExternalDisplay(bool connected,int extFbNum)653 void ExternalDisplay::setExternalDisplay(bool connected, int extFbNum)
654 {
655 hwc_context_t* ctx = mHwcContext;
656 if(ctx) {
657 ALOGD_IF(DEBUG, "%s: connected = %d", __FUNCTION__, connected);
658 // Store the external display
659 mConnected = connected;
660 mConnectedFbNum = extFbNum;
661 mHwcContext->dpyAttr[mExtDpyNum].connected = connected;
662 }
663 }
664
getExtFbNum(int & fbNum)665 int ExternalDisplay::getExtFbNum(int &fbNum) {
666 int ret = -1;
667 if(mConnected) {
668 fbNum = mConnectedFbNum;
669 ret = 0;
670 }
671 return ret;
672 }
673
writeHPDOption(int userOption) const674 bool ExternalDisplay::writeHPDOption(int userOption) const
675 {
676 bool ret = true;
677 if(mHdmiFbNum != -1) {
678 char sysFsHPDFilePath[255];
679 snprintf(sysFsHPDFilePath ,sizeof(sysFsHPDFilePath),
680 "/sys/devices/virtual/graphics/fb%d/hpd", mHdmiFbNum);
681 int hdmiHPDFile = open(sysFsHPDFilePath,O_RDWR, 0);
682 if (hdmiHPDFile < 0) {
683 ALOGE("%s: state file '%s' not found : ret%d err str: %s", __FUNCTION__,
684 sysFsHPDFilePath, hdmiHPDFile, strerror(errno));
685 ret = false;
686 } else {
687 int err = -1;
688 ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__, userOption);
689 if(userOption)
690 err = write(hdmiHPDFile, "1", 2);
691 else
692 err = write(hdmiHPDFile, "0" , 2);
693 if (err <= 0) {
694 ALOGE("%s: file write failed '%s'", __FUNCTION__, sysFsHPDFilePath);
695 ret = false;
696 }
697 close(hdmiHPDFile);
698 }
699 }
700 return ret;
701 }
702
setDpyWfdAttr()703 void ExternalDisplay::setDpyWfdAttr() {
704 if(mHwcContext) {
705 mHwcContext->dpyAttr[mExtDpyNum].xres = mVInfo.xres;
706 mHwcContext->dpyAttr[mExtDpyNum].yres = mVInfo.yres;
707 mHwcContext->dpyAttr[mExtDpyNum].vsync_period =
708 1000000000l /60;
709 ALOGD_IF(DEBUG,"%s: wfd...connected..!",__FUNCTION__);
710 }
711 }
712
setDpyHdmiAttr()713 void ExternalDisplay::setDpyHdmiAttr() {
714 int width = 0, height = 0, fps = 0;
715 getAttrForMode(width, height, fps);
716 if(mHwcContext) {
717 ALOGD("ExtDisplay setting xres = %d, yres = %d", width, height);
718 mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = width;
719 mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = height;
720 mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
721 1000000000l / fps;
722 }
723 }
724
getAttrForMode(int & width,int & height,int & fps)725 void ExternalDisplay::getAttrForMode(int& width, int& height, int& fps) {
726 switch (mCurrentMode) {
727 case HDMI_VFRMT_640x480p60_4_3:
728 width = 640;
729 height = 480;
730 fps = 60;
731 break;
732 case HDMI_VFRMT_720x480p60_4_3:
733 case HDMI_VFRMT_720x480p60_16_9:
734 width = 720;
735 height = 480;
736 fps = 60;
737 break;
738 case HDMI_VFRMT_720x576p50_4_3:
739 case HDMI_VFRMT_720x576p50_16_9:
740 width = 720;
741 height = 576;
742 fps = 50;
743 break;
744 case HDMI_VFRMT_1280x720p50_16_9:
745 width = 1280;
746 height = 720;
747 fps = 50;
748 break;
749 case HDMI_VFRMT_1280x720p60_16_9:
750 width = 1280;
751 height = 720;
752 fps = 60;
753 break;
754 case HDMI_VFRMT_1280x1024p60_5_4:
755 width = 1280;
756 height = 1024;
757 fps = 60;
758 break;
759 case HDMI_VFRMT_1024x768p60_4_3:
760 width = 1024;
761 height = 768;
762 fps = 60;
763 break;
764 case HDMI_VFRMT_1920x1080p24_16_9:
765 width = 1920;
766 height = 1080;
767 fps = 24;
768 break;
769 case HDMI_VFRMT_1920x1080p25_16_9:
770 width = 1920;
771 height = 1080;
772 fps = 25;
773 break;
774 case HDMI_VFRMT_1920x1080p30_16_9:
775 width = 1920;
776 height = 1080;
777 fps = 30;
778 break;
779 case HDMI_VFRMT_1920x1080p50_16_9:
780 width = 1920;
781 height = 1080;
782 fps = 50;
783 break;
784 case HDMI_VFRMT_1920x1080p60_16_9:
785 width = 1920;
786 height = 1080;
787 fps = 60;
788 break;
789 case HDMI_VFRMT_2560x1600p60_16_9:
790 width = 2560;
791 height = 1600;
792 fps = 60;
793 break;
794 case HDMI_VFRMT_3840x2160p24_16_9:
795 width = 3840;
796 height = 2160;
797 fps = 24;
798 break;
799 case HDMI_VFRMT_3840x2160p25_16_9:
800 width = 3840;
801 height = 2160;
802 fps = 25;
803 break;
804 case HDMI_VFRMT_3840x2160p30_16_9:
805 width = 3840;
806 height = 2160;
807 fps = 30;
808 break;
809 case HDMI_VFRMT_4096x2160p24_16_9:
810 width = 4096;
811 height = 2160;
812 fps = 24;
813 break;
814
815 }
816 }
817
818 };
819