diff options
Diffstat (limited to 'drivers/staging/sm750fb/ddk750_display.c')
-rw-r--r-- | drivers/staging/sm750fb/ddk750_display.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/staging/sm750fb/ddk750_display.c b/drivers/staging/sm750fb/ddk750_display.c new file mode 100644 index 0000000000..172624ff98 --- /dev/null +++ b/drivers/staging/sm750fb/ddk750_display.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ddk750_reg.h" +#include "ddk750_chip.h" +#include "ddk750_display.h" +#include "ddk750_power.h" +#include "ddk750_dvi.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); +} |