summaryrefslogtreecommitdiffstats
path: root/drivers/staging/sm750fb/ddk750_mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/sm750fb/ddk750_mode.c')
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/staging/sm750fb/ddk750_mode.c b/drivers/staging/sm750fb/ddk750_mode.c
new file mode 100644
index 0000000000..e00a6cb319
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_mode.c
@@ -0,0 +1,225 @@
+// 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
+displayControlAdjust_SM750LE(struct mode_parameter *pModeParam,
+ unsigned long dispControl)
+{
+ unsigned long x, y;
+
+ x = pModeParam->horizontal_display_end;
+ y = pModeParam->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 dispControl have been properly set before
+ * calling this function.
+ * This function only sets the extra fields in dispControl.
+ */
+
+ /* Clear bit 29:27 of display control register */
+ dispControl &= ~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)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL41;
+ else if (x == 1024 && y == 768)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL65;
+ else if (x == 1152 && y == 864)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL80;
+ else if (x == 1280 && y == 768)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL80;
+ else if (x == 1280 && y == 720)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL74;
+ else if (x == 1280 && y == 960)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL108;
+ else if (x == 1280 && y == 1024)
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL108;
+ else /* default to VGA clock */
+ dispControl |= CRT_DISPLAY_CTRL_CLK_PLL25;
+
+ /* Set bit 25:24 of display controller */
+ dispControl |= (CRT_DISPLAY_CTRL_CRTSELECT | CRT_DISPLAY_CTRL_RGBBIT);
+
+ /* Set bit 14 of display controller */
+ dispControl |= DISPLAY_CTRL_CLOCK_PHASE;
+
+ poke32(CRT_DISPLAY_CTRL, dispControl);
+
+ return dispControl;
+}
+
+/* only timing related registers will be programed */
+static int programModeRegisters(struct mode_parameter *pModeParam,
+ struct pll_value *pll)
+{
+ int ret = 0;
+ 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 = ((pModeParam->horizontal_total - 1) <<
+ CRT_HORIZONTAL_TOTAL_TOTAL_SHIFT) &
+ CRT_HORIZONTAL_TOTAL_TOTAL_MASK;
+ tmp |= (pModeParam->horizontal_display_end - 1) &
+ CRT_HORIZONTAL_TOTAL_DISPLAY_END_MASK;
+
+ poke32(CRT_HORIZONTAL_TOTAL, tmp);
+
+ tmp = (pModeParam->horizontal_sync_width <<
+ CRT_HORIZONTAL_SYNC_WIDTH_SHIFT) &
+ CRT_HORIZONTAL_SYNC_WIDTH_MASK;
+ tmp |= (pModeParam->horizontal_sync_start - 1) &
+ CRT_HORIZONTAL_SYNC_START_MASK;
+
+ poke32(CRT_HORIZONTAL_SYNC, tmp);
+
+ tmp = ((pModeParam->vertical_total - 1) <<
+ CRT_VERTICAL_TOTAL_TOTAL_SHIFT) &
+ CRT_VERTICAL_TOTAL_TOTAL_MASK;
+ tmp |= (pModeParam->vertical_display_end - 1) &
+ CRT_VERTICAL_TOTAL_DISPLAY_END_MASK;
+
+ poke32(CRT_VERTICAL_TOTAL, tmp);
+
+ tmp = ((pModeParam->vertical_sync_height <<
+ CRT_VERTICAL_SYNC_HEIGHT_SHIFT)) &
+ CRT_VERTICAL_SYNC_HEIGHT_MASK;
+ tmp |= (pModeParam->vertical_sync_start - 1) &
+ CRT_VERTICAL_SYNC_START_MASK;
+
+ poke32(CRT_VERTICAL_SYNC, tmp);
+
+ tmp = DISPLAY_CTRL_TIMING | DISPLAY_CTRL_PLANE;
+ if (pModeParam->vertical_sync_polarity)
+ tmp |= DISPLAY_CTRL_VSYNC_PHASE;
+ if (pModeParam->horizontal_sync_polarity)
+ tmp |= DISPLAY_CTRL_HSYNC_PHASE;
+
+ if (sm750_get_chip_type() == SM750LE) {
+ displayControlAdjust_SM750LE(pModeParam, 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 = ((pModeParam->horizontal_total - 1) <<
+ PANEL_HORIZONTAL_TOTAL_TOTAL_SHIFT) &
+ PANEL_HORIZONTAL_TOTAL_TOTAL_MASK;
+ reg |= ((pModeParam->horizontal_display_end - 1) &
+ PANEL_HORIZONTAL_TOTAL_DISPLAY_END_MASK);
+ poke32(PANEL_HORIZONTAL_TOTAL, reg);
+
+ poke32(PANEL_HORIZONTAL_SYNC,
+ ((pModeParam->horizontal_sync_width <<
+ PANEL_HORIZONTAL_SYNC_WIDTH_SHIFT) &
+ PANEL_HORIZONTAL_SYNC_WIDTH_MASK) |
+ ((pModeParam->horizontal_sync_start - 1) &
+ PANEL_HORIZONTAL_SYNC_START_MASK));
+
+ poke32(PANEL_VERTICAL_TOTAL,
+ (((pModeParam->vertical_total - 1) <<
+ PANEL_VERTICAL_TOTAL_TOTAL_SHIFT) &
+ PANEL_VERTICAL_TOTAL_TOTAL_MASK) |
+ ((pModeParam->vertical_display_end - 1) &
+ PANEL_VERTICAL_TOTAL_DISPLAY_END_MASK));
+
+ poke32(PANEL_VERTICAL_SYNC,
+ ((pModeParam->vertical_sync_height <<
+ PANEL_VERTICAL_SYNC_HEIGHT_SHIFT) &
+ PANEL_VERTICAL_SYNC_HEIGHT_MASK) |
+ ((pModeParam->vertical_sync_start - 1) &
+ PANEL_VERTICAL_SYNC_START_MASK));
+
+ tmp = DISPLAY_CTRL_TIMING | DISPLAY_CTRL_PLANE;
+ if (pModeParam->vertical_sync_polarity)
+ tmp |= DISPLAY_CTRL_VSYNC_PHASE;
+ if (pModeParam->horizontal_sync_polarity)
+ tmp |= DISPLAY_CTRL_HSYNC_PHASE;
+ if (pModeParam->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);
+ }
+ } else {
+ ret = -1;
+ }
+ return ret;
+}
+
+int ddk750_setModeTiming(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);
+ }
+ programModeRegisters(parm, &pll);
+ return 0;
+}