root/src/add-ons/accelerants/intel_extreme/dpms.cpp
/*
 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 */


#include "accelerant_protos.h"
#include "accelerant.h"


#undef TRACE
//#define TRACE_DPMS
#ifdef TRACE_DPMS
#       define TRACE(x...) _sPrintf("intel_extreme: " x)
#else
#       define TRACE(x...)
#endif

#define ERROR(x...) _sPrintf("intel_extreme: " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)


static void
enable_all_pipes(bool enable)
{
        // Go over each port and enable pipe/plane
        for (uint32 i = 0; i < gInfo->port_count; i++) {
                if (gInfo->ports[i] == NULL)
                        continue;
                if (!gInfo->ports[i]->IsConnected())
                        continue;
                if (gInfo->ports[i]->GetPipe() == NULL)
                        continue;

                gInfo->ports[i]->Power(enable);
        }

        read32(INTEL_DISPLAY_A_BASE);
                // flush the possibly cached PCI bus writes

        set_frame_buffer_base();
}


static void
enable_lvds_panel(bool enable)
{
        bool hasPCH = (gInfo->shared_info->pch_info != INTEL_PCH_NONE);

        int controlRegister = hasPCH ? PCH_PANEL_CONTROL : INTEL_PANEL_CONTROL;
        int statusRegister = hasPCH ? PCH_PANEL_STATUS : INTEL_PANEL_STATUS;

        uint32 control = read32(controlRegister);
        uint32 panelStatus;

        if (enable) {
                if ((control & PANEL_CONTROL_POWER_TARGET_ON) == 0) {
                        write32(controlRegister, control | PANEL_CONTROL_POWER_TARGET_ON
                                /*| (hasPCH ? PANEL_REGISTER_UNLOCK : 0)*/);
                }

                if (!hasPCH) {
                        do {
                                panelStatus = read32(statusRegister);
                        } while ((panelStatus & PANEL_STATUS_POWER_ON) == 0);
                }
        } else {
                if ((control & PANEL_CONTROL_POWER_TARGET_ON) != 0) {
                        write32(controlRegister, (control & ~PANEL_CONTROL_POWER_TARGET_ON)
                                /*| (hasPCH ? PANEL_REGISTER_UNLOCK : 0)*/);
                }

                if (!hasPCH)
                {
                        do {
                                panelStatus = read32(statusRegister);
                        } while ((panelStatus & PANEL_STATUS_POWER_ON) != 0);
                }
        }
}


void
set_display_power_mode(uint32 mode)
{
        uint32 monitorMode = 0;

        if (mode == B_DPMS_ON) {
                uint32 pll = read32(INTEL_DISPLAY_A_PLL);
                if ((pll & DISPLAY_PLL_ENABLED) == 0) {
                        // reactivate PLL
                        write32(INTEL_DISPLAY_A_PLL, pll);
                        read32(INTEL_DISPLAY_A_PLL);
                        spin(150);
                        write32(INTEL_DISPLAY_A_PLL, pll | DISPLAY_PLL_ENABLED);
                        read32(INTEL_DISPLAY_A_PLL);
                        spin(150);
                        write32(INTEL_DISPLAY_A_PLL, pll | DISPLAY_PLL_ENABLED);
                        read32(INTEL_DISPLAY_A_PLL);
                        spin(150);
                }

                pll = read32(INTEL_DISPLAY_B_PLL);
                if ((pll & DISPLAY_PLL_ENABLED) == 0) {
                        // reactivate PLL
                        write32(INTEL_DISPLAY_B_PLL, pll);
                        read32(INTEL_DISPLAY_B_PLL);
                        spin(150);
                        write32(INTEL_DISPLAY_B_PLL, pll | DISPLAY_PLL_ENABLED);
                        read32(INTEL_DISPLAY_B_PLL);
                        spin(150);
                        write32(INTEL_DISPLAY_B_PLL, pll | DISPLAY_PLL_ENABLED);
                        read32(INTEL_DISPLAY_B_PLL);
                        spin(150);
                }

                enable_all_pipes(true);
        }

        wait_for_vblank();

        switch (mode) {
                case B_DPMS_ON:
                        monitorMode = DISPLAY_MONITOR_ON;
                        break;
                case B_DPMS_SUSPEND:
                        monitorMode = DISPLAY_MONITOR_SUSPEND;
                        break;
                case B_DPMS_STAND_BY:
                        monitorMode = DISPLAY_MONITOR_STAND_BY;
                        break;
                case B_DPMS_OFF:
                        monitorMode = DISPLAY_MONITOR_OFF;
                        break;
        }

        //fixme: port DPMS programming should better be in Ports.cpp. Resetup..
        if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
                write32(INTEL_ANALOG_PORT, (read32(INTEL_ANALOG_PORT)
                                & ~(DISPLAY_MONITOR_MODE_MASK | DISPLAY_MONITOR_PORT_ENABLED))
                        | monitorMode
                        | (mode != B_DPMS_OFF ? DISPLAY_MONITOR_PORT_ENABLED : 0));
        }

        //fixme: port DPMS programming should better be in Ports.cpp and is faulty. Resetup..
        if (false) {//gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
                write32(INTEL_DIGITAL_PORT_B, (read32(INTEL_DIGITAL_PORT_B)
                                & ~(/*DISPLAY_MONITOR_MODE_MASK |*/ DISPLAY_MONITOR_PORT_ENABLED))
                        | (mode != B_DPMS_OFF ? DISPLAY_MONITOR_PORT_ENABLED : 0));
                        // TODO: monitorMode?
        }

        if (mode != B_DPMS_ON)
                enable_all_pipes(false);

        if (mode == B_DPMS_OFF) {
                write32(INTEL_DISPLAY_A_PLL, read32(INTEL_DISPLAY_A_PLL)
                        | DISPLAY_PLL_ENABLED);
                write32(INTEL_DISPLAY_B_PLL, read32(INTEL_DISPLAY_B_PLL)
                        | DISPLAY_PLL_ENABLED);

                read32(INTEL_DISPLAY_B_PLL);
                        // flush the possibly cached PCI bus writes

                spin(150);
        }

        if ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0)
                enable_lvds_panel(mode == B_DPMS_ON);

        read32(INTEL_DISPLAY_A_BASE);
                // flush the possibly cached PCI bus writes
}


//      #pragma mark -


uint32
intel_dpms_capabilities(void)
{
        CALLED();
        return B_DPMS_ON | B_DPMS_SUSPEND | B_DPMS_STAND_BY | B_DPMS_OFF;
}


uint32
intel_dpms_mode(void)
{
        CALLED();
        return gInfo->shared_info->dpms_mode;
}


status_t
intel_set_dpms_mode(uint32 mode)
{
        CALLED();
        gInfo->shared_info->dpms_mode = mode;
        set_display_power_mode(mode);

        return B_OK;
}