Home | History | Annotate | Download | only in devices
      1 /*
      2 // Copyright(c)2014 IntelCorporation
      3 //
      4 // LicensedundertheApacheLicense,Version2.0(the"License");
      5 // youmaynotusethisfileexceptincompliancewiththeLicense.
      6 // YoumayobtainacopyoftheLicenseat
      7 //
      8 // http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unlessrequiredbyapplicablelaworagreedtoinwriting,software
     11 // distributedundertheLicenseisdistributedonan"ASIS"BASIS,
     12 // WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
     13 // SeetheLicenseforthespecificlanguagegoverningpermissionsand
     14 // limitationsundertheLicense.
     15 */
     16 
     17 #include <HwcTrace.h>
     18 #include <Drm.h>
     19 #include <DrmConfig.h>
     20 #include <Hwcomposer.h>
     21 #include <ExternalDevice.h>
     22 
     23 namespace android {
     24 namespace intel {
     25 
     26 ExternalDevice::ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory)
     27     : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory),
     28       mHdcpControl(NULL),
     29       mAbortModeSettingCond(),
     30       mPendingDrmMode(),
     31       mHotplugEventPending(false),
     32       mExpectedRefreshRate(0)
     33 {
     34     CTRACE();
     35 }
     36 
     37 ExternalDevice::~ExternalDevice()
     38 {
     39     CTRACE();
     40 }
     41 
     42 bool ExternalDevice::initialize()
     43 {
     44     if (!PhysicalDevice::initialize()) {
     45         DEINIT_AND_RETURN_FALSE("failed to initialize physical device");
     46     }
     47 
     48     mHdcpControl = mControlFactory->createHdcpControl();
     49     if (!mHdcpControl) {
     50         DEINIT_AND_RETURN_FALSE("failed to create HDCP control");
     51     }
     52 
     53     mHotplugEventPending = false;
     54     if (mConnected) {
     55         mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
     56     }
     57 
     58     UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver();
     59     if (observer) {
     60         observer->registerListener(
     61             DrmConfig::getHotplugString(),
     62             hotplugEventListener,
     63             this);
     64     } else {
     65         ETRACE("Uevent observer is NULL");
     66     }
     67     return true;
     68 }
     69 
     70 void ExternalDevice::deinitialize()
     71 {
     72     // abort mode settings if it is in the middle
     73     mAbortModeSettingCond.signal();
     74     if (mThread.get()) {
     75         mThread->join();
     76         mThread = NULL;
     77     }
     78 
     79     if (mHdcpControl) {
     80         mHdcpControl->stopHdcp();
     81         delete mHdcpControl;
     82         mHdcpControl = 0;
     83     }
     84 
     85     mHotplugEventPending = false;
     86     PhysicalDevice::deinitialize();
     87 }
     88 
     89 bool ExternalDevice::setDrmMode(drmModeModeInfo& value)
     90 {
     91     if (!mConnected) {
     92         WTRACE("external device is not connected");
     93         return false;
     94     }
     95 
     96     if (mThread.get()) {
     97         mThread->join();
     98         mThread = NULL;
     99     }
    100 
    101     Drm *drm = Hwcomposer::getInstance().getDrm();
    102     drmModeModeInfo mode;
    103     drm->getModeInfo(mType, mode);
    104     if (drm->isSameDrmMode(&value, &mode))
    105         return true;
    106 
    107     // any issue here by faking connection status?
    108     mConnected = false;
    109     mPendingDrmMode = value;
    110 
    111     // setting mode in a working thread
    112     mThread = new ModeSettingThread(this);
    113     if (!mThread.get()) {
    114         ETRACE("failed to create mode settings thread");
    115         return false;
    116     }
    117 
    118     mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY);
    119     return true;
    120 }
    121 
    122 bool ExternalDevice::threadLoop()
    123 {
    124     // one-time execution
    125     setDrmMode();
    126     return false;
    127 }
    128 
    129 void ExternalDevice::setDrmMode()
    130 {
    131     ITRACE("start mode setting...");
    132 
    133     Drm *drm = Hwcomposer::getInstance().getDrm();
    134 
    135     mConnected = false;
    136     mHwc.hotplug(mType, false);
    137 
    138     {
    139         Mutex::Autolock lock(mLock);
    140         // TODO: make timeout value flexible, or wait until surface flinger
    141         // acknowledges hot unplug event.
    142         status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20));
    143         if (err != -ETIMEDOUT) {
    144             ITRACE("Mode settings is interrupted");
    145             mHwc.hotplug(mType, true);
    146             return;
    147         }
    148     }
    149 
    150     // TODO: potential threading issue with onHotplug callback
    151     mHdcpControl->stopHdcp();
    152     if (!drm->setDrmMode(mType, mPendingDrmMode)) {
    153         ETRACE("failed to set Drm mode");
    154         mHwc.hotplug(mType, true);
    155         return;
    156     }
    157 
    158     if (!PhysicalDevice::updateDisplayConfigs()) {
    159         ETRACE("failed to update display configs");
    160         mHwc.hotplug(mType, true);
    161         return;
    162     }
    163     mConnected = true;
    164     mHotplugEventPending = true;
    165     // delay sending hotplug event until HDCP is authenticated
    166     if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) {
    167         ETRACE("startHdcpAsync() failed; HDCP is not enabled");
    168         mHotplugEventPending = false;
    169         mHwc.hotplug(mType, true);
    170     }
    171     mExpectedRefreshRate = 0;
    172 }
    173 
    174 
    175 void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData)
    176 {
    177     if (userData == NULL) {
    178         return;
    179     }
    180 
    181     ExternalDevice *p = (ExternalDevice*)userData;
    182     p->HdcpLinkStatusListener(success);
    183 }
    184 
    185 void ExternalDevice::HdcpLinkStatusListener(bool success)
    186 {
    187     if (!success) {
    188         ETRACE("HDCP is not authenticated, disabling dynamic vsync");
    189         mHwc.getVsyncManager()->enableDynamicVsync(false);
    190     }
    191 
    192     if (mHotplugEventPending) {
    193         DTRACE("HDCP authentication status %d, sending hotplug event...", success);
    194         mHwc.hotplug(mType, mConnected);
    195         mHotplugEventPending = false;
    196     }
    197 
    198     if (success) {
    199         ITRACE("HDCP authenticated, enabling dynamic vsync");
    200         mHwc.getVsyncManager()->enableDynamicVsync(true);
    201     }
    202 }
    203 
    204 void ExternalDevice::hotplugEventListener(void *data)
    205 {
    206     ExternalDevice *pThis = (ExternalDevice*)data;
    207     if (pThis) {
    208         pThis->hotplugListener();
    209     }
    210 }
    211 
    212 void ExternalDevice::hotplugListener()
    213 {
    214     bool ret;
    215 
    216     CTRACE();
    217 
    218     // abort mode settings if it is in the middle
    219     mAbortModeSettingCond.signal();
    220 
    221     // remember the current connection status before detection
    222     bool connected = mConnected;
    223 
    224     // detect display configs
    225     ret = detectDisplayConfigs();
    226     if (ret == false) {
    227         ETRACE("failed to detect display config");
    228         return;
    229     }
    230 
    231     ITRACE("hotpug event: %d", mConnected);
    232 
    233     if (connected == mConnected) {
    234         WTRACE("same connection status detected, hotplug event ignored");
    235         return;
    236     }
    237 
    238     if (mConnected == false) {
    239         mHotplugEventPending = false;
    240         mHwc.getVsyncManager()->resetVsyncSource();
    241         mHdcpControl->stopHdcp();
    242         mHwc.hotplug(mType, mConnected);
    243     } else {
    244         DTRACE("start HDCP asynchronously...");
    245          // delay sending hotplug event till HDCP is authenticated.
    246         mHotplugEventPending = true;
    247         ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
    248         if (ret == false) {
    249             ETRACE("failed to start HDCP");
    250             mHotplugEventPending = false;
    251             mHwc.hotplug(mType, mConnected);
    252         }
    253     }
    254     mActiveDisplayConfig = 0;
    255 }
    256 
    257 int ExternalDevice::getRefreshRate()
    258 {
    259     Drm *drm = Hwcomposer::getInstance().getDrm();
    260     drmModeModeInfo mode;
    261     if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
    262         return 0;
    263     return mode.vrefresh;
    264 }
    265 
    266 void ExternalDevice::setRefreshRate(int hz)
    267 {
    268     RETURN_VOID_IF_NOT_INIT();
    269 
    270     ITRACE("setting refresh rate to %d", hz);
    271 
    272     if (mBlank) {
    273         WTRACE("external device is blank");
    274         return;
    275     }
    276 
    277     Drm *drm = Hwcomposer::getInstance().getDrm();
    278     drmModeModeInfo mode;
    279     if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
    280         return;
    281 
    282     if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED))
    283         return;
    284 
    285     if (hz == (int)mode.vrefresh)
    286         return;
    287 
    288     if (mExpectedRefreshRate != 0 &&
    289             mExpectedRefreshRate == hz && mHotplugEventPending) {
    290         ITRACE("Ignore a new refresh setting event because there is a same event is handling");
    291         return;
    292     }
    293     mExpectedRefreshRate = hz;
    294 
    295     ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz);
    296 
    297     mHwc.getVsyncManager()->enableDynamicVsync(false);
    298 
    299     mHdcpControl->stopHdcp();
    300 
    301     drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz);
    302 
    303     mHotplugEventPending = false;
    304     mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
    305     mHwc.getVsyncManager()->enableDynamicVsync(true);
    306 }
    307 
    308 int ExternalDevice::getActiveConfig()
    309 {
    310     if (!mConnected) {
    311         return 0;
    312     }
    313     return mActiveDisplayConfig;
    314 }
    315 
    316 bool ExternalDevice::setActiveConfig(int index)
    317 {
    318     if (!mConnected) {
    319         if (index == 0)
    320             return true;
    321         else
    322             return false;
    323     }
    324 
    325     // for now we will only permit the frequency change.  In the future
    326     // we may need to set mode as well.
    327     if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) {
    328         DisplayConfig *config = mDisplayConfigs.itemAt(index);
    329         setRefreshRate(config->getRefreshRate());
    330         mActiveDisplayConfig = index;
    331         return true;
    332     } else {
    333         return false;
    334     }
    335     return true;
    336 }
    337 
    338 } // namespace intel
    339 } // namespace android
    340