root/drivers/staging/sm750fb/ddk750_mode.c
// SPDX-License-Identifier: GPL-2.0

#include "ddk750_reg.h"
#include "ddk750_mode.h"
#include "ddk750_chip.h"

/*
 * SM750LE only:
 * This function takes care extra registers and bit fields required to set
 * up a mode in SM750LE
 *
 * Explanation about Display Control register:
 * HW only supports 7 predefined pixel clocks, and clock select is
 * in bit 29:27 of Display Control register.
 */
static unsigned long
display_control_adjust_SM750LE(struct mode_parameter *mode_param,
                               unsigned long disp_control)
{
        unsigned long x, y;

        x = mode_param->horizontal_display_end;
        y = mode_param->vertical_display_end;

        /*
         * SM750LE has to set up the top-left and bottom-right
         * registers as well.
         * Note that normal SM750/SM718 only use those two register for
         * auto-centering mode.
         */
        poke32(CRT_AUTO_CENTERING_TL, 0);

        poke32(CRT_AUTO_CENTERING_BR,
               (((y - 1) << CRT_AUTO_CENTERING_BR_BOTTOM_SHIFT) &
                CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
               ((x - 1) & CRT_AUTO_CENTERING_BR_RIGHT_MASK));

        /*
         * Assume common fields in disp_control have been properly set before
         * calling this function.
         * This function only sets the extra fields in disp_control.
         */

        /* Clear bit 29:27 of display control register */
        disp_control &= ~CRT_DISPLAY_CTRL_CLK_MASK;

        /* Set bit 29:27 of display control register for the right clock */
        /* Note that SM750LE only need to supported 7 resolutions. */
        if (x == 800 && y == 600)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL41;
        else if (x == 1024 && y == 768)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL65;
        else if (x == 1152 && y == 864)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL80;
        else if (x == 1280 && y == 768)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL80;
        else if (x == 1280 && y == 720)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL74;
        else if (x == 1280 && y == 960)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL108;
        else if (x == 1280 && y == 1024)
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL108;
        else /* default to VGA clock */
                disp_control |= CRT_DISPLAY_CTRL_CLK_PLL25;

        /* Set bit 25:24 of display controller */
        disp_control |= (CRT_DISPLAY_CTRL_CRTSELECT | CRT_DISPLAY_CTRL_RGBBIT);

        /* Set bit 14 of display controller */
        disp_control |= DISPLAY_CTRL_CLOCK_PHASE;

        poke32(CRT_DISPLAY_CTRL, disp_control);

        return disp_control;
}

/* only timing related registers will be  programed */
static void program_mode_registers(struct mode_parameter *mode_param,
                                   struct pll_value *pll)
{
        int cnt = 0;
        unsigned int tmp, reg;

        if (pll->clock_type == SECONDARY_PLL) {
                /* programe secondary pixel clock */
                poke32(CRT_PLL_CTRL, sm750_format_pll_reg(pll));

                tmp = ((mode_param->horizontal_total - 1) <<
                       CRT_HORIZONTAL_TOTAL_TOTAL_SHIFT) &
                     CRT_HORIZONTAL_TOTAL_TOTAL_MASK;
                tmp |= (mode_param->horizontal_display_end - 1) &
                      CRT_HORIZONTAL_TOTAL_DISPLAY_END_MASK;

                poke32(CRT_HORIZONTAL_TOTAL, tmp);

                tmp = (mode_param->horizontal_sync_width <<
                       CRT_HORIZONTAL_SYNC_WIDTH_SHIFT) &
                     CRT_HORIZONTAL_SYNC_WIDTH_MASK;
                tmp |= (mode_param->horizontal_sync_start - 1) &
                      CRT_HORIZONTAL_SYNC_START_MASK;

                poke32(CRT_HORIZONTAL_SYNC, tmp);

                tmp = ((mode_param->vertical_total - 1) <<
                       CRT_VERTICAL_TOTAL_TOTAL_SHIFT) &
                     CRT_VERTICAL_TOTAL_TOTAL_MASK;
                tmp |= (mode_param->vertical_display_end - 1) &
                      CRT_VERTICAL_TOTAL_DISPLAY_END_MASK;

                poke32(CRT_VERTICAL_TOTAL, tmp);

                tmp = ((mode_param->vertical_sync_height <<
                       CRT_VERTICAL_SYNC_HEIGHT_SHIFT)) &
                     CRT_VERTICAL_SYNC_HEIGHT_MASK;
                tmp |= (mode_param->vertical_sync_start - 1) &
                      CRT_VERTICAL_SYNC_START_MASK;

                poke32(CRT_VERTICAL_SYNC, tmp);

                tmp = DISPLAY_CTRL_TIMING | DISPLAY_CTRL_PLANE;
                if (mode_param->vertical_sync_polarity)
                        tmp |= DISPLAY_CTRL_VSYNC_PHASE;
                if (mode_param->horizontal_sync_polarity)
                        tmp |= DISPLAY_CTRL_HSYNC_PHASE;

                if (sm750_get_chip_type() == SM750LE) {
                        display_control_adjust_SM750LE(mode_param, tmp);
                } else {
                        reg = peek32(CRT_DISPLAY_CTRL) &
                                ~(DISPLAY_CTRL_VSYNC_PHASE |
                                  DISPLAY_CTRL_HSYNC_PHASE |
                                  DISPLAY_CTRL_TIMING | DISPLAY_CTRL_PLANE);

                        poke32(CRT_DISPLAY_CTRL, tmp | reg);
                }

        } else if (pll->clock_type == PRIMARY_PLL) {
                unsigned int reserved;

                poke32(PANEL_PLL_CTRL, sm750_format_pll_reg(pll));

                reg = ((mode_param->horizontal_total - 1) <<
                        PANEL_HORIZONTAL_TOTAL_TOTAL_SHIFT) &
                        PANEL_HORIZONTAL_TOTAL_TOTAL_MASK;
                reg |= ((mode_param->horizontal_display_end - 1) &
                        PANEL_HORIZONTAL_TOTAL_DISPLAY_END_MASK);
                poke32(PANEL_HORIZONTAL_TOTAL, reg);

                poke32(PANEL_HORIZONTAL_SYNC,
                       ((mode_param->horizontal_sync_width <<
                         PANEL_HORIZONTAL_SYNC_WIDTH_SHIFT) &
                        PANEL_HORIZONTAL_SYNC_WIDTH_MASK) |
                       ((mode_param->horizontal_sync_start - 1) &
                        PANEL_HORIZONTAL_SYNC_START_MASK));

                poke32(PANEL_VERTICAL_TOTAL,
                       (((mode_param->vertical_total - 1) <<
                         PANEL_VERTICAL_TOTAL_TOTAL_SHIFT) &
                        PANEL_VERTICAL_TOTAL_TOTAL_MASK) |
                       ((mode_param->vertical_display_end - 1) &
                        PANEL_VERTICAL_TOTAL_DISPLAY_END_MASK));

                poke32(PANEL_VERTICAL_SYNC,
                       ((mode_param->vertical_sync_height <<
                         PANEL_VERTICAL_SYNC_HEIGHT_SHIFT) &
                        PANEL_VERTICAL_SYNC_HEIGHT_MASK) |
                       ((mode_param->vertical_sync_start - 1) &
                        PANEL_VERTICAL_SYNC_START_MASK));

                tmp = DISPLAY_CTRL_TIMING | DISPLAY_CTRL_PLANE;
                if (mode_param->vertical_sync_polarity)
                        tmp |= DISPLAY_CTRL_VSYNC_PHASE;
                if (mode_param->horizontal_sync_polarity)
                        tmp |= DISPLAY_CTRL_HSYNC_PHASE;
                if (mode_param->clock_phase_polarity)
                        tmp |= DISPLAY_CTRL_CLOCK_PHASE;

                reserved = PANEL_DISPLAY_CTRL_RESERVED_MASK |
                        PANEL_DISPLAY_CTRL_VSYNC;

                reg = (peek32(PANEL_DISPLAY_CTRL) & ~reserved) &
                        ~(DISPLAY_CTRL_CLOCK_PHASE | DISPLAY_CTRL_VSYNC_PHASE |
                          DISPLAY_CTRL_HSYNC_PHASE | DISPLAY_CTRL_TIMING |
                          DISPLAY_CTRL_PLANE);

                /*
                 * May a hardware bug or just my test chip (not confirmed).
                 * PANEL_DISPLAY_CTRL register seems requiring few writes
                 * before a value can be successfully written in.
                 * Added some masks to mask out the reserved bits.
                 * Note: This problem happens by design. The hardware will wait
                 *       for the next vertical sync to turn on/off the plane.
                 */
                poke32(PANEL_DISPLAY_CTRL, tmp | reg);

                while ((peek32(PANEL_DISPLAY_CTRL) & ~reserved) !=
                        (tmp | reg)) {
                        cnt++;
                        if (cnt > 1000)
                                break;
                        poke32(PANEL_DISPLAY_CTRL, tmp | reg);
                }
        }
}

int ddk750_set_mode_timing(struct mode_parameter *parm, enum clock_type clock)
{
        struct pll_value pll;

        pll.input_freq = DEFAULT_INPUT_CLOCK;
        pll.clock_type = clock;

        sm750_calc_pll_value(parm->pixel_clock, &pll);
        if (sm750_get_chip_type() == SM750LE) {
                /* set graphic mode via IO method */
                outb_p(0x88, 0x3d4);
                outb_p(0x06, 0x3d5);
        }
        program_mode_registers(parm, &pll);
        return 0;
}