external.cpp revision 76443245d153635b512539fbc68fdf7a904fdf6f
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
41using namespace android;
42
43namespace 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
53ExternalDisplay::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
61void 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
73void ExternalDisplay::setHPD(uint32_t startEnd) {
74    ALOGD_IF(DEBUG,"HPD enabled=%d", startEnd);
75    writeHPDOption(startEnd);
76}
77
78void 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
85int ExternalDisplay::getModeCount() const {
86    ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
87    Mutex::Autolock lock(mExtDispLock);
88    return mModeCount;
89}
90
91void 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
98ExternalDisplay::~ExternalDisplay()
99{
100    closeFrameBuffer();
101}
102
103struct 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
123void 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
166static 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
186int 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
207bool 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
241bool 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
254bool 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
268void 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
277int 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
319int 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
335inline bool ExternalDisplay::isValidMode(int ID)
336{
337    return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
338}
339
340void 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
387void 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        ALOGD("%s sending hotplug: connected = %d", __FUNCTION__,connected);
412        //Inform SF. Disabled until SF calls unblank
413        ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
414        ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = connected;
415
416        //TODO ideally should be done on "connected" not "online"
417        ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL, connected);
418    }
419    return;
420}
421
422bool ExternalDisplay::writeHPDOption(int userOption) const
423{
424    bool ret = true;
425    int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
426    if (hdmiHPDFile < 0) {
427        ALOGE("%s: state file '%s' not found : ret%d"
428                           "err str: %s",  __FUNCTION__, SYSFS_HPD, hdmiHPDFile,
429                           strerror(errno));
430        ret = false;
431    } else {
432        int err = -1;
433        ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__,
434                 userOption);
435        if(userOption)
436            err = write(hdmiHPDFile, "1", 2);
437        else
438            err = write(hdmiHPDFile, "0" , 2);
439        if (err <= 0) {
440            ALOGE("%s: file write failed '%s'",
441                     __FUNCTION__, SYSFS_HPD);
442            ret = false;
443        }
444        close(hdmiHPDFile);
445    }
446    return ret;
447}
448
449/*
450 * commits the changes to the external display
451 * mExternalDisplay has the mixer number(1-> HDMI 2-> WFD)
452 */
453bool ExternalDisplay::post()
454{
455    if(mFd == -1) {
456        return false;
457    } else if(ioctl(mFd, MSMFB_OVERLAY_COMMIT, &mExternalDisplay) == -1) {
458         ALOGE("%s: MSMFB_OVERLAY_COMMIT failed, str: %s", __FUNCTION__,
459                                                          strerror(errno));
460         return false;
461    }
462    return true;
463}
464
465void ExternalDisplay::setDpyAttr() {
466    int width = 0, height = 0, fps = 0;
467    getAttrForMode(width, height, fps);
468    if(mHwcContext) {
469        ALOGD("ExtDisplay setting xres = %d, yres = %d", width, height);
470        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = width;
471        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = height;
472        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
473            1000000000l / fps;
474    }
475}
476
477void ExternalDisplay::getAttrForMode(int& width, int& height,
478int& fps) {
479    switch (mCurrentMode) {
480        case m640x480p60_4_3:
481            width = 640;
482            height = 480;
483            fps = 60;
484            break;
485        case m720x480p60_4_3:
486        case m720x480p60_16_9:
487            width = 720;
488            height = 480;
489            fps = 60;
490            break;
491        case m720x576p50_4_3:
492        case m720x576p50_16_9:
493            width = 720;
494            height = 576;
495            fps = 50;
496            break;
497        case m1280x720p50_16_9:
498            width = 1280;
499            height = 720;
500            fps = 50;
501            break;
502        case m1280x720p60_16_9:
503            width = 1280;
504            height = 720;
505            fps = 60;
506            break;
507        case m1920x1080p24_16_9:
508            width = 1920;
509            height = 1080;
510            fps = 24;
511            break;
512        case m1920x1080p25_16_9:
513            width = 1920;
514            height = 1080;
515            fps = 25;
516            break;
517        case m1920x1080p30_16_9:
518            width = 1920;
519            height = 1080;
520            fps = 30;
521            break;
522        case m1920x1080p50_16_9:
523            width = 1920;
524            height = 1080;
525            fps = 50;
526            break;
527        case m1920x1080p60_16_9:
528            width = 1920;
529            height = 1080;
530            fps = 60;
531            break;
532    }
533}
534
535};
536
537