summaryrefslogtreecommitdiffstats
path: root/drivers/staging/sm750fb/ddk750_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/sm750fb/ddk750_display.c')
-rw-r--r--drivers/staging/sm750fb/ddk750_display.c159
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 000000000..172624ff9
--- /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);
+}