]> git.sur5r.net Git - u-boot/blobdiff - drivers/video/tegra124/sor.c
Merge git://www.denx.de/git/u-boot-imx
[u-boot] / drivers / video / tegra124 / sor.c
index faeda4cb7b37822038218b69f5d59ceef06596a8..700ab25d4678ee3fee668f5424656478bca3dd42 100644 (file)
@@ -5,9 +5,12 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <errno.h>
-#include <fdtdec.h>
 #include <malloc.h>
+#include <panel.h>
+#include <syscon.h>
+#include <video_bridge.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch-tegra/dc.h>
@@ -37,6 +40,14 @@ DECLARE_GLOBAL_DATA_PTR;
 #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF             (0 << 25)
 #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON              (1 << 25)
 
+struct tegra_dc_sor_data {
+       void *base;
+       void *pmc_base;
+       u8 portnum;     /* 0 or 1 */
+       int power_is_up;
+       struct udevice *panel;
+};
+
 static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
 {
        return readl((u32 *)sor->base + reg);
@@ -57,6 +68,27 @@ static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
        tegra_sor_writel(sor, reg, reg_val);
 }
 
+void tegra_dp_disable_tx_pu(struct udevice *dev)
+{
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
+       tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+                             DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
+}
+
+void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
+                          u32 vs_reg, u32 pc_reg, u8 pc_supported)
+{
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
+       tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
+       tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
+       if (pc_supported) {
+               tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
+                                     pc_reg);
+       }
+}
+
 static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
                                      u32 mask, u32 exp_val,
                                      int poll_interval_us, int timeout_ms)
@@ -78,8 +110,9 @@ static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
        return -ETIMEDOUT;
 }
 
-int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
+int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
        u32 orig_val;
 
@@ -106,10 +139,11 @@ int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
        return 0;
 }
 
-void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
                                 u8 training_pattern,
                                 const struct tegra_dp_link_config *link_cfg)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -177,9 +211,10 @@ static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
        return 0;
 }
 
-static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
+static int tegra_dc_sor_power_dplanes(struct udevice *dev,
                                      u32 lane_count, int pu)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -201,15 +236,15 @@ static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
                }
 
                tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
-               tegra_dc_sor_set_lane_count(sor, lane_count);
+               tegra_dc_sor_set_lane_count(dev, lane_count);
        }
 
        return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
 }
 
-void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
-                                 int power_up)
+void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -238,14 +273,15 @@ static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
        }
 }
 
-static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
+static void tegra_dc_sor_set_dp_mode(struct udevice *dev,
                                const struct tegra_dp_link_config *link_cfg)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
-       tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+       tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
 
-       tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
+       tegra_dc_sor_set_dp_linkctl(dev, 1, training_pattern_none, link_cfg);
        reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
        reg_val &= ~DP_CONFIG_WATERMARK_MASK;
        reg_val |= link_cfg->watermark;
@@ -334,8 +370,9 @@ static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
        return 0;
 }
 
-void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
+void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
@@ -349,9 +386,10 @@ void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
        tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
 }
 
-void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
                                   u8 *lane_count)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, CLK_CNTRL);
@@ -378,15 +416,18 @@ void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
        }
 }
 
-void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
+void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
        tegra_sor_write_field(sor, CLK_CNTRL,
                              CLK_CNTRL_DP_LINK_SPEED_MASK,
                              link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
 }
 
-void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
+void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 reg_val;
 
        reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -422,15 +463,25 @@ void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
  * 4   1       0       0       0       0       0       1
  * 5   0       0       0       0       0       0       1
  */
-static int tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, int is_lvds)
+static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+       u32 reg;
        int ret;
 
        if (sor->power_is_up)
                return 0;
 
+       /*
+        * If for some reason it is already powered up, don't do it again.
+        * This can happen if U-Boot is the secondary boot loader.
+        */
+       reg = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
+       if (reg & DP_PADCTL_PD_TXD_0_NO)
+               return 0;
+
        /* Set link bw */
-       tegra_dc_sor_set_link_bandwidth(sor, is_lvds ?
+       tegra_dc_sor_set_link_bandwidth(dev, is_lvds ?
                                        CLK_CNTRL_DP_LINK_SPEED_LVDS :
                                        CLK_CNTRL_DP_LINK_SPEED_G1_62);
 
@@ -638,9 +689,10 @@ static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
        writel(reg_val, &disp_ctrl->cmd.state_access);
 }
 
-int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_enable_dp(struct udevice *dev,
                           const struct tegra_dp_link_config *link_cfg)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        int ret;
 
        tegra_sor_write_field(sor, CLK_CNTRL,
@@ -684,7 +736,7 @@ int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
                              PLL2_AUX2_OVERRIDE_POWERDOWN |
                              PLL2_AUX7_PORT_POWERDOWN_DISABLE);
 
-       ret = tegra_dc_sor_power_up(sor, 0);
+       ret = tegra_dc_sor_power_up(dev, 0);
        if (ret) {
                debug("DP failed to power up\n");
                return ret;
@@ -694,29 +746,25 @@ int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
        clock_sor_enable_edp_clock();
 
        /* Power up lanes */
-       tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
+       tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1);
 
-       tegra_dc_sor_set_dp_mode(sor, link_cfg);
+       tegra_dc_sor_set_dp_mode(dev, link_cfg);
        debug("%s ret\n", __func__);
 
        return 0;
 }
 
-int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev,
                        const struct tegra_dp_link_config *link_cfg,
                        const struct display_timing *timing)
 {
-       const void *blob = gd->fdt_blob;
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        struct dc_ctlr *disp_ctrl;
        u32 reg_val;
-       int node;
 
        /* Use the first display controller */
        debug("%s\n", __func__);
-       node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
-       if (node < 0)
-               return -ENOENT;
-       disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+       disp_ctrl = (struct dc_ctlr *)dev_read_addr(dc_dev);
 
        tegra_dc_sor_enable_dc(disp_ctrl);
        tegra_dc_sor_config_panel(sor, 0, link_cfg, timing);
@@ -781,9 +829,11 @@ int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
        return 0;
 }
 
-void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_lane_parm(struct udevice *dev,
                const struct tegra_dp_link_config *link_cfg)
 {
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
        tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
                         link_cfg->drive_current);
        tegra_sor_writel(sor, PR(sor->portnum),
@@ -792,8 +842,8 @@ void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
                         link_cfg->postcursor);
        tegra_sor_writel(sor, LVDS, 0);
 
-       tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
-       tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+       tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
+       tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count);
 
        tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
                              DP_PADCTL_TX_PU_ENABLE |
@@ -808,12 +858,38 @@ void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
        tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
 }
 
-void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
-                       const struct tegra_dp_link_config *link_cfg)
+int tegra_dc_sor_set_voltage_swing(struct udevice *dev,
+                                   const struct tegra_dp_link_config *link_cfg)
 {
-       u32 pad_ctrl = 0;
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
        u32 drive_current = 0;
        u32 pre_emphasis = 0;
+
+       /* Set to a known-good pre-calibrated setting */
+       switch (link_cfg->link_bw) {
+       case SOR_LINK_SPEED_G1_62:
+       case SOR_LINK_SPEED_G2_7:
+               drive_current = 0x13131313;
+               pre_emphasis = 0;
+               break;
+       case SOR_LINK_SPEED_G5_4:
+               debug("T124 does not support 5.4G link clock.\n");
+       default:
+               debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
+               return -ENOLINK;
+       }
+
+       tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
+       tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+
+       return 0;
+}
+
+void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev,
+                       const struct tegra_dp_link_config *link_cfg)
+{
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+       u32 pad_ctrl = 0;
        int err = 0;
 
        switch (link_cfg->lane_count) {
@@ -848,49 +924,159 @@ void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
                debug("Wait for lane power down failed: %d\n", err);
                return;
        }
+}
 
-       /* Set to a known-good pre-calibrated setting */
-       switch (link_cfg->link_bw) {
-       case SOR_LINK_SPEED_G1_62:
-       case SOR_LINK_SPEED_G2_7:
-               drive_current = 0x13131313;
-               pre_emphasis = 0;
+int tegra_sor_precharge_lanes(struct udevice *dev,
+                             const struct tegra_dp_link_config *cfg)
+{
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+       u32 val = 0;
+
+       switch (cfg->lane_count) {
+       case 4:
+               val |= (DP_PADCTL_PD_TXD_3_NO |
+                       DP_PADCTL_PD_TXD_2_NO);
+               /* fall through */
+       case 2:
+               val |= DP_PADCTL_PD_TXD_1_NO;
+               /* fall through */
+       case 1:
+               val |= DP_PADCTL_PD_TXD_0_NO;
                break;
-       case SOR_LINK_SPEED_G5_4:
-               drive_current = 0x19191919;
-               pre_emphasis = 0x09090909;
        default:
-               printf("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
-               return;
+               debug("dp: invalid lane number %d\n", cfg->lane_count);
+               return -EINVAL;
        }
 
-       tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
-                        drive_current);
-       tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+       tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+                             (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+                             (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
+       udelay(100);
+       tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+                             (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+                             0);
+
+       return 0;
 }
 
-int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)
+static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
 {
-       const void *blob = gd->fdt_blob;
-       struct tegra_dc_sor_data *sor;
-       int node;
+       u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
+
+       reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
+       writel(reg_val, &disp_ctrl->disp.disp_win_opt);
+}
 
-       node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
-       if (node < 0)
-               return -ENOENT;
-       sor = calloc(1, sizeof(*sor));
-       if (!sor)
-               return -ENOMEM;
-       sor->base = (void *)fdtdec_get_addr(blob, node, "reg");
+int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev)
+{
+       struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+       int dc_reg_ctx[DC_REG_SAVE_SPACE];
+       struct dc_ctlr *disp_ctrl;
+       unsigned long dc_int_mask;
+       int ret;
 
-       node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC);
-       if (node < 0)
-               return -ENOENT;
-       sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
+       debug("%s\n", __func__);
+       /* Use the first display controller */
+       disp_ctrl = (struct dc_ctlr *)dev_read_addr(dev);
 
-       sor->power_is_up = 0;
-       sor->portnum = 0;
-       *sorp = sor;
+       /* Sleep mode */
+       tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+                        SUPER_STATE1_ASY_ORMODE_SAFE |
+                        SUPER_STATE1_ATTACHED_YES);
+       tegra_dc_sor_super_update(sor);
+
+       tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
+
+       if (tegra_dc_sor_poll_register(sor, TEST,
+                                      TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+                                      TEST_ACT_HEAD_OPMODE_SLEEP, 100,
+                                      TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+               debug("dc timeout waiting for OPMOD = SLEEP\n");
+               ret = -ETIMEDOUT;
+               goto err;
+       }
+
+       tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+                        SUPER_STATE1_ASY_ORMODE_SAFE |
+                        SUPER_STATE1_ATTACHED_NO);
+
+       /* Mask DC interrupts during the 2 dummy frames required for detach */
+       dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
+       writel(0, &disp_ctrl->cmd.int_mask);
+
+       /* Stop DC->SOR path */
+       tegra_dc_sor_enable_sor(disp_ctrl, false);
+       ret = tegra_dc_sor_general_act(disp_ctrl);
+       if (ret)
+               goto err;
+
+       /* Stop DC */
+       writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
+       ret = tegra_dc_sor_general_act(disp_ctrl);
+       if (ret)
+               goto err;
+
+       tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
+
+       writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
+
+       return 0;
+err:
+       debug("%s: ret=%d\n", __func__, ret);
+
+       return ret;
+}
+
+static int tegra_sor_set_backlight(struct udevice *dev, int percent)
+{
+       struct tegra_dc_sor_data *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = panel_enable_backlight(priv->panel);
+       if (ret) {
+               debug("sor: Cannot enable panel backlight\n");
+               return ret;
+       }
 
        return 0;
 }
+
+static int tegra_sor_ofdata_to_platdata(struct udevice *dev)
+{
+       struct tegra_dc_sor_data *priv = dev_get_priv(dev);
+       int ret;
+
+       priv->base = (void *)dev_read_addr(dev);
+
+       priv->pmc_base = (void *)syscon_get_first_range(TEGRA_SYSCON_PMC);
+       if (IS_ERR(priv->pmc_base))
+               return PTR_ERR(priv->pmc_base);
+
+       ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel",
+                                          &priv->panel);
+       if (ret) {
+               debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
+                     dev->name, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct video_bridge_ops tegra_sor_ops = {
+       .set_backlight  = tegra_sor_set_backlight,
+};
+
+static const struct udevice_id tegra_sor_ids[] = {
+       { .compatible = "nvidia,tegra124-sor" },
+       { }
+};
+
+U_BOOT_DRIVER(sor_tegra) = {
+       .name   = "sor_tegra",
+       .id     = UCLASS_VIDEO_BRIDGE,
+       .of_match = tegra_sor_ids,
+       .ofdata_to_platdata = tegra_sor_ofdata_to_platdata,
+       .ops    = &tegra_sor_ops,
+       .priv_auto_alloc_size = sizeof(struct tegra_dc_sor_data),
+};