// 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; }