root/drivers/staging/sm750fb/ddk750_display.c
// SPDX-License-Identifier: GPL-2.0
#include "ddk750_reg.h"
#include "ddk750_chip.h"
#include "ddk750_display.h"
#include "ddk750_power.h"

static void set_display_control(int ctrl, int disp_state)
{
        /* state != 0 means turn on both timing & plane en_bit */
        unsigned long reg, val, reserved;
        int cnt = 0;

        if (!ctrl) {
                reg = PANEL_DISPLAY_CTRL;
                reserved = PANEL_DISPLAY_CTRL_RESERVED_MASK;
        } else {
                reg = CRT_DISPLAY_CTRL;
                reserved = CRT_DISPLAY_CTRL_RESERVED_MASK;
        }

        val = peek32(reg);
        if (disp_state) {
                /*
                 * Timing should be enabled first before enabling the
                 * plane because changing at the same time does not
                 * guarantee that the plane will also enabled or
                 * disabled.
                 */
                val |= DISPLAY_CTRL_TIMING;
                poke32(reg, val);

                val |= DISPLAY_CTRL_PLANE;

                /*
                 * Somehow the register value on the plane is not set
                 * until a few delay. Need to write and read it a
                 * couple times
                 */
                do {
                        cnt++;
                        poke32(reg, val);
                } while ((peek32(reg) & ~reserved) != (val & ~reserved));
                pr_debug("Set Plane enbit:after tried %d times\n", cnt);
        } else {
                /*
                 * When turning off, there is no rule on the
                 * programming sequence since whenever the clock is
                 * off, then it does not matter whether the plane is
                 * enabled or disabled.  Note: Modifying the plane bit
                 * will take effect on the next vertical sync. Need to
                 * find out if it is necessary to wait for 1 vsync
                 * before modifying the timing enable bit.
                 */
                val &= ~DISPLAY_CTRL_PLANE;
                poke32(reg, val);

                val &= ~DISPLAY_CTRL_TIMING;
                poke32(reg, val);
        }
}

static void primary_wait_vertical_sync(int delay)
{
        unsigned int status;

        /*
         * Do not wait when the Primary PLL is off or display control is
         * already off. This will prevent the software to wait forever.
         */
        if (!(peek32(PANEL_PLL_CTRL) & PLL_CTRL_POWER) ||
            !(peek32(PANEL_DISPLAY_CTRL) & DISPLAY_CTRL_TIMING))
                return;

        while (delay-- > 0) {
                /* Wait for end of vsync. */
                do {
                        status = peek32(SYSTEM_CTRL);
                } while (status & SYSTEM_CTRL_PANEL_VSYNC_ACTIVE);

                /* Wait for start of vsync. */
                do {
                        status = peek32(SYSTEM_CTRL);
                } while (!(status & SYSTEM_CTRL_PANEL_VSYNC_ACTIVE));
        }
}

static void sw_panel_power_sequence(int disp, int delay)
{
        unsigned int reg;

        /* disp should be 1 to open sequence */
        reg = peek32(PANEL_DISPLAY_CTRL);
        reg |= (disp ? PANEL_DISPLAY_CTRL_FPEN : 0);
        poke32(PANEL_DISPLAY_CTRL, reg);
        primary_wait_vertical_sync(delay);

        reg = peek32(PANEL_DISPLAY_CTRL);
        reg |= (disp ? PANEL_DISPLAY_CTRL_DATA : 0);
        poke32(PANEL_DISPLAY_CTRL, reg);
        primary_wait_vertical_sync(delay);

        reg = peek32(PANEL_DISPLAY_CTRL);
        reg |= (disp ? PANEL_DISPLAY_CTRL_VBIASEN : 0);
        poke32(PANEL_DISPLAY_CTRL, reg);
        primary_wait_vertical_sync(delay);

        reg = peek32(PANEL_DISPLAY_CTRL);
        reg |= (disp ? PANEL_DISPLAY_CTRL_FPEN : 0);
        poke32(PANEL_DISPLAY_CTRL, reg);
        primary_wait_vertical_sync(delay);
}

void ddk750_set_logical_disp_out(enum disp_output output)
{
        unsigned int reg;

        if (output & PNL_2_USAGE) {
                /* set panel path controller select */
                reg = peek32(PANEL_DISPLAY_CTRL);
                reg &= ~PANEL_DISPLAY_CTRL_SELECT_MASK;
                reg |= (((output & PNL_2_MASK) >> PNL_2_OFFSET) <<
                        PANEL_DISPLAY_CTRL_SELECT_SHIFT);
                poke32(PANEL_DISPLAY_CTRL, reg);
        }

        if (output & CRT_2_USAGE) {
                /* set crt path controller select */
                reg = peek32(CRT_DISPLAY_CTRL);
                reg &= ~CRT_DISPLAY_CTRL_SELECT_MASK;
                reg |= (((output & CRT_2_MASK) >> CRT_2_OFFSET) <<
                        CRT_DISPLAY_CTRL_SELECT_SHIFT);
                /*se blank off */
                reg &= ~CRT_DISPLAY_CTRL_BLANK;
                poke32(CRT_DISPLAY_CTRL, reg);
        }

        if (output & PRI_TP_USAGE) {
                /* set primary timing and plane en_bit */
                set_display_control(0, (output & PRI_TP_MASK) >> PRI_TP_OFFSET);
        }

        if (output & SEC_TP_USAGE) {
                /* set secondary timing and plane en_bit*/
                set_display_control(1, (output & SEC_TP_MASK) >> SEC_TP_OFFSET);
        }

        if (output & PNL_SEQ_USAGE) {
                /* set  panel sequence */
                sw_panel_power_sequence((output & PNL_SEQ_MASK) >>
                                        PNL_SEQ_OFFSET, 4);
        }

        if (output & DAC_USAGE)
                set_DAC((output & DAC_MASK) >> DAC_OFFSET);

        if (output & DPMS_USAGE)
                ddk750_set_dpms((output & DPMS_MASK) >> DPMS_OFFSET);
}