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