• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright (C) 2010 The Android Open Source Project
4  * Copyright (C) 2012, The Linux Foundation. All rights reserved.
5  *
6  * Not a Contribution, Apache license notifications and license are
7  * retained for attribution purposes only.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #define DEBUG 0
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <media/IAudioPolicyService.h>
26 #include <media/AudioSystem.h>
27 #include <utils/threads.h>
28 #include <utils/Errors.h>
29 #include <utils/Log.h>
30 
31 #include <linux/msm_mdp.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 #include "overlayUtils.h"
40 
41 using namespace android;
42 
43 namespace qhwc {
44 
45 
46 #define DEVICE_ROOT "/sys/devices/virtual/graphics"
47 #define DEVICE_NODE "fb1"
48 
49 #define SYSFS_EDID_MODES        DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
50 #define SYSFS_HPD               DEVICE_ROOT "/" DEVICE_NODE "/hpd"
51 
52 
ExternalDisplay(hwc_context_t * ctx)53 ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
54     mCurrentMode(-1), mExternalDisplay(0), mModeCount(0), mHwcContext(ctx)
55 {
56     memset(&mVInfo, 0, sizeof(mVInfo));
57     //Enable HPD for HDMI
58     writeHPDOption(1);
59 }
60 
setEDIDMode(int resMode)61 void ExternalDisplay::setEDIDMode(int resMode) {
62     ALOGD_IF(DEBUG,"resMode=%d ", resMode);
63     int extDispType;
64     {
65         Mutex::Autolock lock(mExtDispLock);
66         extDispType = mExternalDisplay;
67         setExternalDisplay(0);
68         setResolution(resMode);
69     }
70     setExternalDisplay(extDispType);
71 }
72 
setHPD(uint32_t startEnd)73 void ExternalDisplay::setHPD(uint32_t startEnd) {
74     ALOGD_IF(DEBUG,"HPD enabled=%d", startEnd);
75     writeHPDOption(startEnd);
76 }
77 
setActionSafeDimension(int w,int h)78 void ExternalDisplay::setActionSafeDimension(int w, int h) {
79     ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
80     Mutex::Autolock lock(mExtDispLock);
81     overlay::utils::ActionSafe::getInstance()->setDimension(w, h);
82     setExternalDisplay(mExternalDisplay);
83 }
84 
getModeCount() const85 int ExternalDisplay::getModeCount() const {
86     ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
87     Mutex::Autolock lock(mExtDispLock);
88     return mModeCount;
89 }
90 
getEDIDModes(int * out) const91 void ExternalDisplay::getEDIDModes(int *out) const {
92     Mutex::Autolock lock(mExtDispLock);
93     for(int i = 0;i < mModeCount;i++) {
94         out[i] = mEDIDModes[i];
95     }
96 }
97 
~ExternalDisplay()98 ExternalDisplay::~ExternalDisplay()
99 {
100     closeFrameBuffer();
101 }
102 
103 struct disp_mode_timing_type {
104     int  video_format;
105 
106     int  active_h;
107     int  active_v;
108 
109     int  front_porch_h;
110     int  pulse_width_h;
111     int  back_porch_h;
112 
113     int  front_porch_v;
114     int  pulse_width_v;
115     int  back_porch_v;
116 
117     int  pixel_freq;
118     bool interlaced;
119 
120     void set_info(struct fb_var_screeninfo &info) const;
121 };
122 
set_info(struct fb_var_screeninfo & info) const123 void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const
124 {
125     info.reserved[0] = 0;
126     info.reserved[1] = 0;
127     info.reserved[2] = 0;
128     info.reserved[3] = (info.reserved[3] & 0xFFFF) | (video_format << 16);
129 
130     info.xoffset = 0;
131     info.yoffset = 0;
132     info.xres = active_h;
133     info.yres = active_v;
134 
135     info.pixclock = pixel_freq*1000;
136     info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
137 
138     info.right_margin = front_porch_h;
139     info.hsync_len = pulse_width_h;
140     info.left_margin = back_porch_h;
141     info.lower_margin = front_porch_v;
142     info.vsync_len = pulse_width_v;
143     info.upper_margin = back_porch_v;
144 }
145 
146 /* Video formates supported by the HDMI Standard */
147 /* Indicates the resolution, pix clock and the aspect ratio */
148 #define m640x480p60_4_3         1
149 #define m720x480p60_4_3         2
150 #define m720x480p60_16_9        3
151 #define m1280x720p60_16_9       4
152 #define m1920x1080i60_16_9      5
153 #define m1440x480i60_4_3        6
154 #define m1440x480i60_16_9       7
155 #define m1920x1080p60_16_9      16
156 #define m720x576p50_4_3         17
157 #define m720x576p50_16_9        18
158 #define m1280x720p50_16_9       19
159 #define m1440x576i50_4_3        21
160 #define m1440x576i50_16_9       22
161 #define m1920x1080p50_16_9      31
162 #define m1920x1080p24_16_9      32
163 #define m1920x1080p25_16_9      33
164 #define m1920x1080p30_16_9      34
165 
166 static struct disp_mode_timing_type supported_video_mode_lut[] = {
167     {m640x480p60_4_3,     640,  480,  16,  96,  48, 10, 2, 33,  25200, false},
168     {m720x480p60_4_3,     720,  480,  16,  62,  60,  9, 6, 30,  27030, false},
169     {m720x480p60_16_9,    720,  480,  16,  62,  60,  9, 6, 30,  27030, false},
170     {m1280x720p60_16_9,  1280,  720, 110,  40, 220,  5, 5, 20,  74250, false},
171     {m1920x1080i60_16_9, 1920,  540,  88,  44, 148,  2, 5,  5,  74250, false},
172     {m1440x480i60_4_3,   1440,  240,  38, 124, 114,  4, 3, 15,  27000, true},
173     {m1440x480i60_16_9,  1440,  240,  38, 124, 114,  4, 3, 15,  27000, true},
174     {m1920x1080p60_16_9, 1920, 1080,  88,  44, 148,  4, 5, 36, 148500, false},
175     {m720x576p50_4_3,     720,  576,  12,  64,  68,  5, 5, 39,  27000, false},
176     {m720x576p50_16_9,    720,  576,  12,  64,  68,  5, 5, 39,  27000, false},
177     {m1280x720p50_16_9,  1280,  720, 440,  40, 220,  5, 5, 20,  74250, false},
178     {m1440x576i50_4_3,   1440,  288,  24, 126, 138,  2, 3, 19,  27000, true},
179     {m1440x576i50_16_9,  1440,  288,  24, 126, 138,  2, 3, 19,  27000, true},
180     {m1920x1080p50_16_9, 1920, 1080, 528,  44, 148,  4, 5, 36, 148500, false},
181     {m1920x1080p24_16_9, 1920, 1080, 638,  44, 148,  4, 5, 36,  74250, false},
182     {m1920x1080p25_16_9, 1920, 1080, 528,  44, 148,  4, 5, 36,  74250, false},
183     {m1920x1080p30_16_9, 1920, 1080,  88,  44, 148,  4, 5, 36,  74250, false},
184 };
185 
parseResolution(char * edidStr,int * edidModes)186 int ExternalDisplay::parseResolution(char* edidStr, int* edidModes)
187 {
188     char delim = ',';
189     int count = 0;
190     char *start, *end;
191     // EDIDs are string delimited by ','
192     // Ex: 16,4,5,3,32,34,1
193     // Parse this string to get mode(int)
194     start = (char*) edidStr;
195     end = &delim;
196     while(*end == delim) {
197         edidModes[count] = (int) strtol(start, &end, 10);
198         start = end+1;
199         count++;
200     }
201     ALOGD_IF(DEBUG, "In %s: count = %d", __FUNCTION__, count);
202     for (int i = 0; i < count; i++)
203         ALOGD_IF(DEBUG, "Mode[%d] = %d", i, edidModes[i]);
204     return count;
205 }
206 
readResolution()207 bool ExternalDisplay::readResolution()
208 {
209     int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
210     int len = -1;
211 
212     if (hdmiEDIDFile < 0) {
213         ALOGE("%s: edid_modes file '%s' not found",
214                  __FUNCTION__, SYSFS_EDID_MODES);
215         return false;
216     } else {
217         len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
218         ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
219                  __FUNCTION__, mEDIDs, len);
220         if ( len <= 0) {
221             ALOGE("%s: edid_modes file empty '%s'",
222                      __FUNCTION__, SYSFS_EDID_MODES);
223         }
224         else {
225             while (len > 1 && isspace(mEDIDs[len-1]))
226                 --len;
227             mEDIDs[len] = 0;
228         }
229     }
230     close(hdmiEDIDFile);
231     if(len > 0) {
232         // GEt EDID modes from the EDID strings
233         mModeCount = parseResolution(mEDIDs, mEDIDModes);
234         ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
235                  mModeCount);
236     }
237 
238     return (strlen(mEDIDs) > 0);
239 }
240 
openFramebuffer()241 bool ExternalDisplay::openFramebuffer()
242 {
243     if (mFd == -1) {
244         mFd = open("/dev/graphics/fb1", O_RDWR);
245         if (mFd < 0)
246             ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
247     }
248     if(mHwcContext) {
249         mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
250     }
251     return (mFd > 0);
252 }
253 
closeFrameBuffer()254 bool ExternalDisplay::closeFrameBuffer()
255 {
256     int ret = 0;
257     if(mFd > 0) {
258         ret = close(mFd);
259         mFd = -1;
260     }
261     if(mHwcContext) {
262         mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
263     }
264     return (ret == 0);
265 }
266 
267 // clears the vinfo, edid, best modes
resetInfo()268 void ExternalDisplay::resetInfo()
269 {
270     memset(&mVInfo, 0, sizeof(mVInfo));
271     memset(mEDIDs, 0, sizeof(mEDIDs));
272     memset(mEDIDModes, 0, sizeof(mEDIDModes));
273     mModeCount = 0;
274     mCurrentMode = -1;
275 }
276 
getModeOrder(int mode)277 int ExternalDisplay::getModeOrder(int mode)
278 {
279     switch (mode) {
280         default:
281         case m1440x480i60_4_3:
282             return 1; // 480i 4:3
283         case m1440x480i60_16_9:
284             return 2; // 480i 16:9
285         case m1440x576i50_4_3:
286             return 3; // i576i 4:3
287         case m1440x576i50_16_9:
288             return 4; // 576i 16:9
289         case m640x480p60_4_3:
290             return 5; // 640x480 4:3
291         case m720x480p60_4_3:
292             return 6; // 480p 4:3
293         case m720x480p60_16_9:
294             return 7; // 480p 16:9
295         case m720x576p50_4_3:
296             return 8; // 576p 4:3
297         case m720x576p50_16_9:
298             return 9; // 576p 16:9
299         case m1920x1080i60_16_9:
300             return 10; // 1080i 16:9
301         case m1280x720p50_16_9:
302             return 11; // 720p@50Hz
303         case m1280x720p60_16_9:
304             return 12; // 720p@60Hz
305         case m1920x1080p24_16_9:
306             return 13; //1080p@24Hz
307         case m1920x1080p25_16_9:
308             return 14; //108-p@25Hz
309         case m1920x1080p30_16_9:
310             return 15; //1080p@30Hz
311         case m1920x1080p50_16_9:
312             return 16; //1080p@50Hz
313         case m1920x1080p60_16_9:
314             return 17; //1080p@60Hz
315     }
316 }
317 
318 // Get the best mode for the current HD TV
getBestMode()319 int ExternalDisplay::getBestMode() {
320     int bestOrder = 0;
321     int bestMode = m640x480p60_4_3;
322     Mutex::Autolock lock(mExtDispLock);
323     // for all the edid read, get the best mode
324     for(int i = 0; i < mModeCount; i++) {
325         int mode = mEDIDModes[i];
326         int order = getModeOrder(mode);
327         if (order > bestOrder) {
328             bestOrder = order;
329             bestMode = mode;
330         }
331     }
332     return bestMode;
333 }
334 
isValidMode(int ID)335 inline bool ExternalDisplay::isValidMode(int ID)
336 {
337     return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
338 }
339 
setResolution(int ID)340 void ExternalDisplay::setResolution(int ID)
341 {
342     struct fb_var_screeninfo info;
343     int ret = 0;
344     if (!openFramebuffer())
345         return;
346     ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
347     if(ret < 0) {
348         ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
349                                                             strerror(errno));
350     }
351 
352     ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
353             "(%d,%d,%d) %dMHz>", __FUNCTION__,
354             mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
355             mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
356             mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
357             mVInfo.pixclock/1000/1000);
358     //If its a valid mode and its a new ID - update var_screeninfo
359     if ((isValidMode(ID)) && mCurrentMode != ID) {
360         const struct disp_mode_timing_type *mode =
361             &supported_video_mode_lut[0];
362         unsigned count =  sizeof(supported_video_mode_lut)/sizeof
363             (*supported_video_mode_lut);
364         for (unsigned int i = 0; i < count; ++i) {
365             const struct disp_mode_timing_type *cur =
366                 &supported_video_mode_lut[i];
367             if (cur->video_format == ID)
368                 mode = cur;
369         }
370         mode->set_info(mVInfo);
371         ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
372                  "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
373                  mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
374                  mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
375                  mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
376                  mVInfo.pixclock/1000/1000);
377         mVInfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
378         ret = ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo);
379         if(ret < 0) {
380             ALOGD("In %s: FBIOPUT_VSCREENINFO failed Err Str = %s",
381                                                  __FUNCTION__, strerror(errno));
382         }
383         mCurrentMode = ID;
384     }
385 }
386 
setExternalDisplay(int connected)387 void ExternalDisplay::setExternalDisplay(int connected)
388 {
389 
390     hwc_context_t* ctx = mHwcContext;
391     if(ctx) {
392         ALOGD_IF(DEBUG, "%s: status = %d", __FUNCTION__,
393                  connected);
394         if(connected) {
395             readResolution();
396             //Get the best mode and set
397             // TODO: Move this to activate
398             setResolution(getBestMode());
399             setDpyAttr();
400             //enable hdmi vsync
401         } else {
402             // Disable the hdmi vsync
403             closeFrameBuffer();
404             resetInfo();
405         }
406         // Store the external display
407         mExternalDisplay = connected;
408         const char* prop = (connected) ? "1" : "0";
409         // set system property
410         property_set("hw.hdmiON", prop);
411     }
412     return;
413 }
414 
writeHPDOption(int userOption) const415 bool ExternalDisplay::writeHPDOption(int userOption) const
416 {
417     bool ret = true;
418     int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
419     if (hdmiHPDFile < 0) {
420         ALOGE("%s: state file '%s' not found : ret%d"
421                            "err str: %s",  __FUNCTION__, SYSFS_HPD, hdmiHPDFile,
422                            strerror(errno));
423         ret = false;
424     } else {
425         int err = -1;
426         ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__,
427                  userOption);
428         if(userOption)
429             err = write(hdmiHPDFile, "1", 2);
430         else
431             err = write(hdmiHPDFile, "0" , 2);
432         if (err <= 0) {
433             ALOGE("%s: file write failed '%s'",
434                      __FUNCTION__, SYSFS_HPD);
435             ret = false;
436         }
437         close(hdmiHPDFile);
438     }
439     return ret;
440 }
441 
442 /*
443  * commits the changes to the external display
444  * mExternalDisplay has the mixer number(1-> HDMI 2-> WFD)
445  */
post()446 bool ExternalDisplay::post()
447 {
448     if(mFd == -1)
449         return false;
450 
451     struct mdp_display_commit ext_commit;
452     memset(&ext_commit, 0, sizeof(ext_commit));
453     ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
454     if (ioctl(mFd, MSMFB_DISPLAY_COMMIT, &ext_commit) == -1) {
455          ALOGE("%s: MSMFB_DISPLAY_COMMIT for external failed, str: %s",
456                                                 __FUNCTION__, strerror(errno));
457          return false;
458     }
459 
460     return true;
461 }
462 
setDpyAttr()463 void ExternalDisplay::setDpyAttr() {
464     int width = 0, height = 0, fps = 0;
465     getAttrForMode(width, height, fps);
466     if(mHwcContext) {
467         ALOGD("ExtDisplay setting xres = %d, yres = %d", width, height);
468         mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = width;
469         mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = height;
470         mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
471             1000000000l / fps;
472     }
473 }
474 
getAttrForMode(int & width,int & height,int & fps)475 void ExternalDisplay::getAttrForMode(int& width, int& height,
476 int& fps) {
477     switch (mCurrentMode) {
478         case m640x480p60_4_3:
479             width = 640;
480             height = 480;
481             fps = 60;
482             break;
483         case m720x480p60_4_3:
484         case m720x480p60_16_9:
485             width = 720;
486             height = 480;
487             fps = 60;
488             break;
489         case m720x576p50_4_3:
490         case m720x576p50_16_9:
491             width = 720;
492             height = 576;
493             fps = 50;
494             break;
495         case m1280x720p50_16_9:
496             width = 1280;
497             height = 720;
498             fps = 50;
499             break;
500         case m1280x720p60_16_9:
501             width = 1280;
502             height = 720;
503             fps = 60;
504             break;
505         case m1920x1080p24_16_9:
506             width = 1920;
507             height = 1080;
508             fps = 24;
509             break;
510         case m1920x1080p25_16_9:
511             width = 1920;
512             height = 1080;
513             fps = 25;
514             break;
515         case m1920x1080p30_16_9:
516             width = 1920;
517             height = 1080;
518             fps = 30;
519             break;
520         case m1920x1080p50_16_9:
521             width = 1920;
522             height = 1080;
523             fps = 50;
524             break;
525         case m1920x1080p60_16_9:
526             width = 1920;
527             height = 1080;
528             fps = 60;
529             break;
530     }
531 }
532 
533 };
534 
535