]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: dc: Add NVSR driver
authorDaniel Solomon <daniels@nvidia.com>
Tue, 21 Jan 2014 22:32:40 +0000 (14:32 -0800)
committerChao Xu <cxu@nvidia.com>
Thu, 30 Jan 2014 01:40:06 +0000 (17:40 -0800)
This is the initial driver cut for NVSR.
Supports only the following:
- DP interface
- Communication over DPAUX
- Sparse Refresh entry/exit via sysfs

Change-Id: I4095ea0632b82b9b7a897cdf7bc48350a0aba4f5
Signed-off-by: Daniel Solomon <daniels@nvidia.com>
Reviewed-on: http://git-master/r/353412
Reviewed-by: Chao Xu <cxu@nvidia.com>
Tested-by: Chao Xu <cxu@nvidia.com>
arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_priv_defs.h
drivers/video/tegra/dc/dc_sysfs.c
drivers/video/tegra/dc/dp.h
drivers/video/tegra/dc/ext/control.c
drivers/video/tegra/dc/nvsr.c [new file with mode: 0644]
drivers/video/tegra/dc/nvsr.h [new file with mode: 0644]
drivers/video/tegra/dc/nvsr_regs.h [new file with mode: 0644]

index e3750098f475fa9377e4c95099f6e60c9c216085..35c2cfa02b889363cd5cdccc1e3a5a4f6fab33c5 100644 (file)
@@ -389,6 +389,7 @@ enum {
        TEGRA_DC_OUT_DSI,
        TEGRA_DC_OUT_DP,
        TEGRA_DC_OUT_LVDS,
+       TEGRA_DC_OUT_NVSR_DP,
 };
 
 struct tegra_dc_out_pin {
index ac20ce82e18b14659960bd479ababe68fe3fe173..3ae262220a20e4bc0aa5da205136835460b2efc1 100644 (file)
@@ -62,6 +62,7 @@
 #include "dev.h"
 #include "nvsd.h"
 #include "dp.h"
+#include "nvsr.h"
 
 /* HACK! This needs to come from DT */
 #include "../../../../arch/arm/mach-tegra/iomap.h"
@@ -1394,13 +1395,17 @@ static int tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
        case TEGRA_DC_OUT_DP:
                dc->out_ops = &tegra_dc_dp_ops;
                break;
+#ifdef CONFIG_TEGRA_NVSR
+       case TEGRA_DC_OUT_NVSR_DP:
+               dc->out_ops = &tegra_dc_nvsr_ops;
+               break;
+#endif
 #endif
 #ifdef CONFIG_TEGRA_LVDS
        case TEGRA_DC_OUT_LVDS:
                dc->out_ops = &tegra_dc_lvds_ops;
                break;
 #endif
-
        default:
                dc->out_ops = NULL;
                break;
@@ -1836,7 +1841,7 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
                if (!completion_done(&dc->crc_complete))
                        complete(&dc->crc_complete);
 
-               if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE && !dc->nvsr)
                        tegra_dc_put(dc);
        }
 
@@ -1942,6 +1947,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        else
                tegra_dc_continuous_irq(dc, status);
 
+       if (dc->nvsr)
+               tegra_dc_nvsr_irq(dc->nvsr, status);
+
        /* update video mode if it has changed since the last frame */
        if (status & (FRAME_END_INT | V_BLANK_INT))
                if (tegra_dc_update_mode(dc))
index 326adb3819ad48449595c0addc8db19c40b3dce1..741fc3a13915cc94d41bd044188fd0960085baae 100644 (file)
@@ -350,6 +350,9 @@ extern struct tegra_dc_out_ops tegra_dc_dp_ops;
 #ifdef CONFIG_TEGRA_LVDS
 extern struct tegra_dc_out_ops tegra_dc_lvds_ops;
 #endif
+#ifdef CONFIG_TEGRA_NVSR
+extern struct tegra_dc_out_ops tegra_dc_nvsr_ops;
+#endif
 
 /* defined in dc_sysfs.c, used by dc.c */
 void tegra_dc_remove_sysfs(struct device *dev);
index 6947a6f35c0db3c2aa0a411876ab91c3daeeb20b..794e70280f95c129b3b535b477603d9c6b79ef6b 100644 (file)
@@ -38,7 +38,6 @@
 
 #include "dc_reg.h"
 
-
 #define NEED_UPDATE_EMC_ON_EVERY_FRAME (windows_idle_detection_time == 0)
 
 /* 29 bit offset for window clip number */
@@ -121,6 +120,8 @@ struct tegra_dc_shift_clk_div {
        unsigned long div; /* denominator */
 };
 
+struct tegra_dc_nvsr_data;
+
 struct tegra_dc {
        struct platform_device          *ndev;
        struct tegra_dc_platform_data   *pdata;
@@ -231,6 +232,8 @@ struct tegra_dc {
        int                             win_blank_saved_flag;
        struct tegra_dc_win             win_blank_saved;
        struct tegra_edid               *edid;
+
+       struct tegra_dc_nvsr_data *nvsr;
 };
 
 #endif
index 2a8992bbf7de77b27f6a764fa5c0fac3dcd47524..029ea220fea2e7ad08d28e0f40cfc54f02f918b7 100644 (file)
@@ -29,6 +29,7 @@
 #include "dc_priv.h"
 #include "nvsd.h"
 #include "hdmi.h"
+#include "nvsr.h"
 
 static ssize_t mode_show(struct device *device,
        struct device_attribute *attr, char *buf)
@@ -711,6 +712,7 @@ void tegra_dc_remove_sysfs(struct device *dev)
        struct platform_device *ndev = to_platform_device(dev);
        struct tegra_dc *dc = platform_get_drvdata(ndev);
        struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
 
        device_remove_file(dev, &dev_attr_mode);
        device_remove_file(dev, &dev_attr_nvdps);
@@ -735,6 +737,9 @@ void tegra_dc_remove_sysfs(struct device *dev)
        if (sd_settings)
                nvsd_remove_sysfs(dev);
 
+       if (nvsr)
+               nvsr_remove_sysfs(dev);
+
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                device_remove_file(dev, &dev_attr_smart_panel);
 
@@ -750,6 +755,7 @@ void tegra_dc_create_sysfs(struct device *dev)
        struct platform_device *ndev = to_platform_device(dev);
        struct tegra_dc *dc = platform_get_drvdata(ndev);
        struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
        int error = 0;
 
        error |= device_create_file(dev, &dev_attr_mode);
@@ -775,6 +781,9 @@ void tegra_dc_create_sysfs(struct device *dev)
        if (sd_settings)
                error |= nvsd_create_sysfs(dev);
 
+       if (nvsr)
+               error |= nvsr_create_sysfs(dev);
+
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                error |= device_create_file(dev, &dev_attr_smart_panel);
        if (dc->out->type != TEGRA_DC_OUT_HDMI)
index ab21ee5075a88dc20310a7947861b4e346c54e87..c28adbf18a87d8fb05b3b4673d470ce595a93652 100644 (file)
@@ -18,6 +18,7 @@
 #define __DRIVER_VIDEO_TEGRA_DC_DP_H__
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include "sor.h"
 #include "dc_priv.h"
 #include "dpaux_regs.h"
index a9d3762389e0a8c2f733b71c2a027608d035b2f6..fbfa45ab1a46e6f59d67fdbc46cf062cacf7a697 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/video/tegra/dc/ext/control.c
  *
- * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION, All rights reserved.
  *
  * Author: Robert Morell <rmorell@nvidia.com>
  *
@@ -66,6 +66,7 @@ get_output_properties(struct tegra_dc_ext_control_output_properties *properties)
                properties->type = TEGRA_DC_EXT_LVDS;
                break;
        case TEGRA_DC_OUT_DP:
+       case TEGRA_DC_OUT_NVSR_DP:
                properties->type = TEGRA_DC_EXT_DP;
                break;
        default:
diff --git a/drivers/video/tegra/dc/nvsr.c b/drivers/video/tegra/dc/nvsr.c
new file mode 100644 (file)
index 0000000..bec99e0
--- /dev/null
@@ -0,0 +1,1547 @@
+/*
+ * drivers/video/tegra/dc/nvsr.c
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION, All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include "nvsr.h"
+#include "nvsr_regs.h"
+#include "dpaux_regs.h"
+
+#define HIMAX 1
+
+#define NVSR_ERR(...) dev_err(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__);
+#define NVSR_WARN(...) dev_warn(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__);
+#define NVSR_INFO(...) dev_info(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__);
+
+#define NVSR_RET(ret, ...) \
+{ \
+       if (ret) { \
+               NVSR_ERR(__VA_ARGS__); \
+               return; \
+       } \
+}
+
+#define NVSR_RETV(ret, ...) \
+{ \
+       if (ret) { \
+               NVSR_ERR(__VA_ARGS__); \
+               return ret; \
+       } \
+}
+
+/* Wait for 3 frames max to enter/exit */
+#define SR_ENTRY_MAX_TRIES 3
+#define SR_EXIT_MAX_TRIES 3
+
+static void tegra_dc_nvsr_get(struct tegra_dc_nvsr_data *nvsr)
+{
+       tegra_dc_get(nvsr->dc);
+       clk_prepare_enable(nvsr->out_clk);
+}
+
+static void tegra_dc_nvsr_put(struct tegra_dc_nvsr_data *nvsr)
+{
+       clk_disable_unprepare(nvsr->out_clk);
+       tegra_dc_put(nvsr->dc);
+}
+
+static int tegra_dc_nvsr_enter_sr(struct tegra_dc_nvsr_data *nvsr)
+{
+       u32 val;
+       int ret;
+       u8 tries = 0;
+       u32 delay = nvsr->sr_timing_frame_time_us / 1000;
+
+       /* Set entry via sideband */
+       val = NVSR_SRC_CTL1_SIDEBAND_ENTRY_SEL_YES;
+       val |= NVSR_SRC_CTL1_SIDEBAND_ENTRY_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL1, 1, val);
+       NVSR_RETV(ret, "Failed to enter SR via sideband\n");
+       /* Enable SR */
+       val = NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED;
+       val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE;
+       /* Request SR entry */
+       val |= NVSR_SRC_CTL0_SR_ENTRY_REQ_YES;
+       val |= NVSR_SRC_CTL0_SR_ENTRY_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, val);
+       NVSR_RETV(ret, "Failed to request SR entry\n");
+
+       /* TODO: Polling is for NVSR bringup only.
+        * Need to implement IRQ */
+       do {
+               ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val);
+               NVSR_RETV(ret, "Failed to read status register.\n");
+
+               if ((val & NVSR_STATUS_SR_STATE_MASK)
+                       == NVSR_STATUS_SR_STATE_SR_ACTIVE) {
+                       nvsr->sr_active = true;
+                       return 0;
+               }
+
+               msleep(delay);
+       } while (++tries < SR_ENTRY_MAX_TRIES);
+
+       NVSR_ERR("Timed out while waiting for SR entry\n");
+       return -ETIMEDOUT;
+}
+
+static int tegra_dc_nvsr_exit_sr(struct tegra_dc_nvsr_data *nvsr)
+{
+       u32 val;
+       int ret;
+       u8 tries = 0;
+       /* Poll every quarter frame in ms */
+       u32 delay = nvsr->sr_timing_frame_time_us / 1000;
+
+       switch (nvsr->resync_method) {
+       case NVSR_RESYNC_CTL_METHOD_FL:
+               val = NVSR_RESYNC_CTL_METHOD_FL;
+               val |= nvsr->resync_delay << NVSR_RESYNC_CTL_DELAY_SHIFT;
+               break;
+       case NVSR_RESYNC_CTL_METHOD_SS:
+       case NVSR_RESYNC_CTL_METHOD_BS:
+               NVSR_WARN("Sliding Sync and Blank Stretching resync "
+                       "methods not supported; defaulting to Immediate\n");
+       case NVSR_RESYNC_CTL_METHOD_IMM:
+       default:
+               val = NVSR_RESYNC_CTL_METHOD_IMM;
+               break;
+       }
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_RESYNC_CTL, 1, val);
+       NVSR_RETV(ret, "Couldn't write resync method, aborting SR exit\n");
+
+       val = NVSR_SRC_CTL1_SIDEBAND_EXIT_SEL_YES;
+       val |= NVSR_SRC_CTL1_SIDEBAND_EXIT_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL1, 1, val);
+       NVSR_RETV(ret, "Couldn't select sideband exit, aborting SR exit\n");
+
+       /* Request SR exit */
+       val = NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE;
+       val |= NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED;
+       val |= NVSR_SRC_CTL0_SR_EXIT_REQ_YES;
+       val |= NVSR_SRC_CTL0_SR_EXIT_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, val);
+       NVSR_RETV(ret, "Couldn't request SR exit, aborting SR exit\n");
+
+       /* TODO: Polling is for NVSR bringup only.
+        * Need to implement IRQ */
+       do {
+               ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val);
+               NVSR_RETV(ret, "Failed to read status register.\n");
+
+               if ((val & NVSR_STATUS_SR_STATE_MASK)
+                       == NVSR_STATUS_SR_STATE_IDLE) {
+                       nvsr->sr_active = false;
+                       return 0;
+               }
+
+               msleep(delay);
+       } while (++tries < SR_EXIT_MAX_TRIES);
+
+       return -ETIMEDOUT;
+}
+
+static int tegra_dc_nvsr_src_power_on(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       u32 reg_val = 0;
+       u8 tries = 0;
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &reg_val);
+       if (ret) {
+               NVSR_ERR("Failed to read status register.\n");
+               return ret;
+       }
+
+       /* If SRC is on, we're done */
+       if ((reg_val & NVSR_STATUS_SR_STATE_MASK)
+               == NVSR_STATUS_SR_STATE_IDLE)
+               return 0;
+
+       reg_val = NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED;
+       reg_val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, reg_val);
+       NVSR_RETV(ret, "Failed to write to control register.\n");
+
+       /* TODO: Polling is for NVSR bringup only.
+        * Need to implement IRQ */
+       do {
+               ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &reg_val);
+               NVSR_RETV(ret, "Failed to read status register.\n");
+
+               if ((reg_val & NVSR_STATUS_SR_STATE_MASK)
+                               == NVSR_STATUS_SR_STATE_IDLE) {
+                       nvsr->src_on = true;
+                       return 0;
+               }
+
+               msleep(nvsr->sr_timing_frame_time_us/1000);
+       } while (tries++ < 3);
+
+       NVSR_ERR("Failed to power on the SRC.\n");
+       return -ETIMEDOUT;
+}
+
+static int tegra_dc_nvsr_src_power_off(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       u32 reg_val = 0;
+       u8 tries = 0;
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &reg_val);
+       if (ret) {
+               NVSR_ERR("Failed to read status register.\n");
+               return ret;
+       }
+
+       if ((reg_val & NVSR_STATUS_SR_STATE_MASK)
+               == NVSR_STATUS_SR_STATE_OFFLINE)
+               return 0;
+
+       reg_val = NVSR_SRC_CTL0_SR_ENABLE_CTL_DISABLED;
+       reg_val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, reg_val);
+       NVSR_RETV(ret, "Failed to write to control register.\n");
+
+       /* TODO: Polling is for NVSR bringup only.
+        * Need to implement IRQ */
+       do {
+               ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &reg_val);
+               NVSR_RETV(ret, "Failed to read status register.\n");
+
+               if ((reg_val & NVSR_STATUS_SR_STATE_MASK)
+                       == NVSR_STATUS_SR_STATE_OFFLINE) {
+                       nvsr->src_on = false;
+                       /* Will need to re-init SRC on next power-on */
+                       nvsr->is_init = false;
+                       nvsr->sr_active = false;
+                       nvsr->idle_active = false;
+                       return 0;
+               }
+
+               msleep(nvsr->sr_timing_frame_time_us/1000);
+       } while (tries++ < 3);
+
+       NVSR_ERR("Failed to power off the SRC.\n");
+       return -ETIMEDOUT;
+}
+
+/* Read SRC capabilities and assess possible refresh modes. */
+static int tegra_dc_nvsr_query_capabilities(struct tegra_dc_nvsr_data *nvsr)
+{
+       u32 reg_val;
+       int ret;
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SR_CAPS0, 1, &reg_val);
+       NVSR_RETV(ret, "Failed to read cap reg 0.\n");
+
+       nvsr->cap.sr =
+               reg_val & NVSR_SR_CAPS0_SR_CAPABLE_MASK >>
+               NVSR_SR_CAPS0_SR_CAPABLE_SHIFT;
+       nvsr->cap.sr_entry_req =
+               reg_val & NVSR_SR_CAPS0_SR_ENTRY_REQ_MASK >>
+               NVSR_SR_CAPS0_SR_ENTRY_REQ_SHIFT;
+       nvsr->cap.sr_exit_req =
+               reg_val & NVSR_SR_CAPS0_SR_EXIT_REQ_MASK >>
+               NVSR_SR_CAPS0_SR_EXIT_REQ_SHIFT;
+
+       if (!nvsr->cap.sr || !nvsr->cap.sr_entry_req ||
+               !nvsr->cap.sr_exit_req) {
+               NVSR_ERR("SRC can't support self-refresh.\n");
+               return 0;
+       }
+
+       nvsr->cap.resync =
+               reg_val & NVSR_SR_CAPS0_RESYNC_CAP_MASK >>
+               NVSR_SR_CAPS0_RESYNC_CAP_SHIFT;
+
+       nvsr->cap.sparse_mode_support = true;
+
+       /* Parse device/vendor ID */
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRC_ID0, 4, &reg_val);
+       NVSR_RETV(ret, "Failed to read device ID.\n");
+       nvsr->src_id.device_id = reg_val & 0xffff;
+       nvsr->src_id.vendor_id = reg_val >> 16;
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SR_CAPS2, 1, &reg_val);
+       NVSR_RETV(ret, "Failed to read capabilities register 2.\n");
+
+       if ((reg_val & NVSR_SR_CAPS2_SEPERATE_PCLK_SUPPORTED) &&
+               (reg_val & NVSR_SR_CAPS2_BUFFERED_REFRESH_SUPPORTED)) {
+               u32 max_pclk = 0;
+               ret = nvsr->reg_ops.read(nvsr, NVSR_SR_MAX_PCLK0, 2, &max_pclk);
+               if (ret)
+                       NVSR_WARN("Failed to read SRC input max pclk\n");
+
+               if (max_pclk > 0) {
+                       /* NVSR stores SRC input max pclk in units of 20KHz */
+                       nvsr->cap.max_pt_pclk = max_pclk * 20000;
+                       nvsr->cap.sep_pclk = true;
+                       nvsr->cap.buffered_mode_support =
+                               reg_val & NVSR_SR_CAPS2_FRAME_LOCK_MASK >>
+                               NVSR_SR_CAPS2_FRAME_LOCK_SHIFT;
+
+                       nvsr->cap.burst_mode_support =
+                               nvsr->cap.resync == NVSR_SR_CAPS0_RESYNC_CAP_FL;
+               } else
+                       NVSR_WARN("SRC input max pclk is 0.\n");
+       }
+
+       nvsr->cap.is_init = true;
+
+       return 0;
+}
+
+/* Write self-refresh timing: SRC to panel */
+static int tegra_dc_nvsr_write_sr_timing(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       struct tegra_dc_mode *mode = &nvsr->sr_timing;
+       u32 val, hblank, vblank, pclk;
+
+       /* NVSR stores pclk in units of 20KHz */
+       val = DIV_ROUND_UP(mode->pclk, 20000);
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_PIXEL_CLOCK0, 2, val);
+       NVSR_RETV(ret, "Failed to write SR pclk\n");
+       pclk = val * 20000;
+
+       /* Horizontal timing */
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING0, 2, mode->h_active);
+       NVSR_RETV(ret, "Failed to write SR Hactive\n");
+
+       val = mode->h_front_porch + mode->h_sync_width + mode->h_back_porch;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING2, 2, val);
+       NVSR_RETV(ret, "Failed to write SR Hblank\n");
+       hblank = val;
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING4, 2,
+               mode->h_front_porch);
+       NVSR_RETV(ret, "Failed to write SR Hfp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING6, 2,
+               mode->h_back_porch);
+       NVSR_RETV(ret, "Failed to write SR Hbp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING8, 1,
+               mode->h_sync_width);
+       NVSR_RETV(ret, "Failed to write SR Hsync width\n");
+
+       val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ?
+               NVSR_SRMODE_TIMING9_HSYNC_POL_NEG :
+               NVSR_SRMODE_TIMING9_HSYNC_POL_POS;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING9, 1, val);
+       NVSR_RETV(ret, "Failed to write SR Hsync pulse polarity\n");
+
+       /* Vertical timing */
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING10, 2,
+               mode->v_active);
+       NVSR_RETV(ret, "Failed to write SR Vactive\n");
+
+       val = mode->v_front_porch + mode->v_sync_width + mode->v_back_porch;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING12, 2, val);
+       NVSR_RETV(ret, "Failed to write SR Vblank\n");
+       vblank = val;
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING14, 2,
+               mode->v_front_porch);
+       NVSR_RETV(ret, "Failed to write SR Vfp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING16, 2,
+               mode->v_back_porch);
+       NVSR_RETV(ret, "Failed to write SR Vbp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING18, 1,
+               mode->v_sync_width);
+       NVSR_RETV(ret, "Failed to write SR Vsync width\n");
+
+       val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ?
+               NVSR_SRMODE_TIMING19_VSYNC_POL_NEG :
+               NVSR_SRMODE_TIMING19_VSYNC_POL_POS;
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING19, 1, val);
+       NVSR_RETV(ret, "Failed to write SR Hsync pulse polarity\n");
+
+       nvsr->sr_timing_frame_time_us =
+               pclk / (mode->h_active + hblank) / (mode->v_active + vblank);
+       nvsr->sr_timing_frame_time_us = 1000000 / nvsr->sr_timing_frame_time_us;
+
+       return 0;
+}
+
+/* Write pass-through timing: DC to SRC */
+static int tegra_dc_nvsr_write_pt_timing(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       struct tegra_dc_mode *mode = &nvsr->pt_timing;
+       u32 val, hblank, vblank, pclk;
+
+
+       if (mode->pclk > nvsr->cap.max_pt_pclk) {
+               NVSR_ERR("DC->SRC pixel clock (%dHz) > max (%dHz)\n",
+                       mode->pclk, nvsr->cap.max_pt_pclk);
+               return -EINVAL;
+       }
+
+       /* NVSR stores pclk in units of 20KHz */
+       val = DIV_ROUND_UP(mode->pclk, 20000);
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_PIXEL_CLOCK0, 2, val);
+       NVSR_RETV(ret, "Failed to write PT pclk\n");
+       pclk = val * 20000;
+
+       /* Horizontal timing */
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING0, 2, mode->h_active);
+       NVSR_RETV(ret, "Failed to write PT Hactive\n");
+
+       val = mode->h_front_porch + mode->h_sync_width + mode->h_back_porch;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING2, 2, val);
+       NVSR_RETV(ret, "Failed to write PT Hblank\n");
+       hblank = val;
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING4, 2,
+               mode->h_front_porch);
+       NVSR_RETV(ret, "Failed to write PT Hfp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING6, 2,
+               mode->h_back_porch);
+       NVSR_RETV(ret, "Failed to write PT Hbp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING8, 1,
+               mode->h_sync_width);
+       NVSR_RETV(ret, "Failed to write PT Hsync width\n");
+
+       val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ?
+               NVSR_PTMODE_TIMING9_HSYNC_POL_NEG :
+               NVSR_PTMODE_TIMING9_HSYNC_POL_POS;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING9, 1, val);
+       NVSR_RETV(ret, "Failed to write PT Hsync pulse polarity\n");
+
+       /* Vertical timing */
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING10, 2,
+               mode->v_active);
+       NVSR_RETV(ret, "Failed to write PT Vactive\n");
+
+       val = mode->v_front_porch + mode->v_sync_width + mode->v_back_porch;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING12, 2, val);
+       NVSR_RETV(ret, "Failed to write PT Vblank\n");
+       vblank = val;
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING14, 2,
+               mode->v_front_porch);
+       NVSR_RETV(ret, "Failed to write PT Vfp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING16, 2,
+               mode->v_back_porch);
+       NVSR_RETV(ret, "Failed to write PT Vbp\n");
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING18, 1,
+               mode->v_sync_width);
+       NVSR_RETV(ret, "Failed to write PT Vsync width\n");
+
+       val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ?
+               NVSR_PTMODE_TIMING19_VSYNC_POL_NEG :
+               NVSR_PTMODE_TIMING19_VSYNC_POL_POS;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING19, 1, val);
+       NVSR_RETV(ret, "Failed to write PT Hsync pulse polarity\n");
+
+       nvsr->pt_timing_frame_time_us =
+               pclk / (mode->h_active + hblank) / (mode->v_active + vblank);
+       nvsr->pt_timing_frame_time_us = 1000000 / nvsr->pt_timing_frame_time_us;
+
+       return 0;
+}
+
+static int tegra_dc_nvsr_update_timings(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret, i;
+       struct tegra_dc_mode *modes = nvsr->dc->out->modes;
+       struct tegra_dc_mode *mode = &nvsr->dc->mode;
+       int n_modes = nvsr->dc->out->n_modes;
+       u32 min_vblank, max_vblank, min_hblank, max_hblank;
+       u32 vblank, hblank;
+
+       /* If SR isn't supported, no need to program timings */
+       if (!nvsr->cap.sparse_mode_support)
+               return 0;
+
+       /* Find min/max blank timings */
+       if (n_modes) {
+               min_vblank = max_vblank =
+                       modes[0].v_front_porch + modes[0].v_sync_width
+                       + modes[0].v_back_porch;
+               min_hblank = max_hblank =
+                       modes[0].h_front_porch + modes[0].h_sync_width
+                       + modes[0].h_back_porch;
+
+               for (i = 1; i < n_modes; i++) {
+                       vblank = modes[i].v_front_porch + modes[i].v_sync_width
+                               + modes[i].v_back_porch;
+                       hblank = modes[i].h_front_porch + modes[i].h_sync_width
+                               + modes[i].h_back_porch;
+
+                       if (vblank > max_vblank)
+                               max_vblank = vblank;
+                       if (vblank < min_vblank)
+                               min_vblank = vblank;
+                       if (hblank > max_hblank)
+                               max_hblank = hblank;
+                       if (hblank < min_hblank)
+                               min_hblank = hblank;
+               }
+       } else {
+               max_vblank = min_vblank =
+                       mode->v_front_porch + mode->v_sync_width +
+                       mode->v_back_porch;
+               max_hblank = max_hblank =
+                       mode->h_front_porch + mode->h_sync_width +
+                       mode->h_back_porch;
+       }
+
+
+       ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING0, 2, min_vblank);
+       NVSR_RETV(ret, "Failed to init timing\n");
+       ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING2, 2, max_vblank);
+       NVSR_RETV(ret, "Failed to init timing\n");
+       ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING4, 2, min_hblank);
+       NVSR_RETV(ret, "Failed to init timing\n");
+       ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING6, 2, max_hblank);
+       NVSR_RETV(ret, "Failed to init timing\n");
+
+       ret = tegra_dc_nvsr_write_pt_timing(nvsr);
+       NVSR_RETV(ret, "Failed to init pass-through timing\n");
+       ret = tegra_dc_nvsr_write_sr_timing(nvsr);
+       NVSR_RETV(ret, "Failed to init self-refresh timing\n");
+
+       return 0;
+}
+
+static int tegra_nvsr_read_dpaux(struct tegra_dc_nvsr_data *nvsr,
+       u32 reg, u32 size, u32 *val)
+{
+       int ret, i;
+       u32 aux_stat;
+       u8 data[DP_AUX_MAX_BYTES] = {0};
+
+       ret = tegra_dc_dpaux_read(nvsr->out_data.dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
+               reg, data, &size, &aux_stat);
+       NVSR_RETV(ret,
+               "DPAUX read failed: reg = 0x%x, size = %d, aux_stat = %d\n",
+               reg, size, aux_stat);
+
+       *val = 0;
+       for (i = 0; i < DP_AUX_MAX_BYTES; i++)
+               *val |= data[i] << (i * 8);
+
+       return 0;
+}
+
+static int tegra_nvsr_write_dpaux(struct tegra_dc_nvsr_data *nvsr,
+       u32 reg, u32 size, u64 val)
+{
+       int i, ret;
+       u32 aux_stat;
+       u8 data[DP_AUX_MAX_BYTES] = {0};
+
+       for (i = 0; i < size; i++) {
+               data[i] = val & 0xffllu;
+               val = val >> 8;
+       }
+
+       /* HACK: Himax tcon always reports failure when writing;
+        * write 1 byte at a time to avoid breaking out of DP
+        * write code too early */
+       ret = tegra_dc_dpaux_write(nvsr->out_data.dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
+               reg, data, &size, &aux_stat);
+
+/* Current HIMAX silicon always NACKs dpaux writes */
+#if !HIMAX
+       if (ret)
+               return ret;
+#endif
+
+       return 0;
+}
+
+/* Enter Sparse and turn off link, or single frame update */
+static int tegra_dc_nvsr_enter_idle(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret = 0;
+       struct tegra_dc *dc = nvsr->dc;
+
+       if (!nvsr->is_init || !nvsr->src_on ||
+               !nvsr->enable || !nvsr->cap.sparse_mode_support)
+               return -ENODEV;
+
+       if (!nvsr->idle_active) {
+               /* Start a new self-refresh state */
+
+               ret = tegra_dc_nvsr_enter_sr(nvsr);
+               NVSR_RETV(ret, "Entering SR failed\n");
+
+               /* TODO: Turn off link here */
+
+               /* set non-continuous mode */
+               tegra_dc_writel(dc, DISP_CTRL_MODE_NC_DISPLAY,
+                               DC_CMD_DISPLAY_COMMAND);
+               tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
+               tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+               dc->out->flags |= TEGRA_DC_OUT_ONE_SHOT_MODE;
+
+               nvsr->idle_active = true;
+       } else {
+               tegra_dc_nvsr_get(nvsr);
+
+               /* Update cached frame */
+               switch (nvsr->sr_mode) {
+               case SR_MODE_BURST:
+                       /* TODO: Implement Burst Refresh update */
+                       NVSR_ERR("Burst mode unimplemented\n");
+                       break;
+               default:
+                       NVSR_ERR("SR re-entry not supported for "
+                               "current refresh mode.\n");
+                       ret = -ENOSYS;
+               }
+
+               tegra_dc_nvsr_put(nvsr);
+       }
+
+       return ret;
+}
+
+inline void tegra_dc_nvsr_irq(struct tegra_dc_nvsr_data *nvsr,
+       unsigned long status)
+{
+       if ((status & MSF_INT) && (nvsr->waiting_on_framelock)) {
+               tegra_dc_mask_interrupt(nvsr->dc, MSF_INT);
+               nvsr->waiting_on_framelock = false;
+               complete(&nvsr->framelock_comp);
+       }
+}
+
+static inline int tegra_dc_nvsr_reset_src(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+
+       ret = tegra_dc_nvsr_src_power_off(nvsr);
+       NVSR_RETV(ret, "Failed to reset SRC.\n");
+       ret = tegra_dc_nvsr_src_power_on(nvsr);
+       NVSR_RETV(ret, "Failed to reset SRC.\n");
+
+       return 0;
+}
+
+static inline void tegra_dc_nvsr_config_resync_method
+       (struct tegra_dc_nvsr_data *nvsr)
+{
+       u32 val;
+       struct tegra_dc *dc = nvsr->dc;
+
+       if (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_FL) {
+               nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_FL;
+
+               /* Configure MSF pin */
+               val = PIN_OUTPUT_LSPI_OUTPUT_DIS;
+               tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_ENABLE3);
+               val = MSF_ENABLE | MSF_LSPI | MSF_POLARITY_LOW;
+               tegra_dc_writel(dc, val, DC_CMD_DISPLAY_COMMAND_OPTION0);
+               /* Enable MSF (framelock) interrupt in DC */
+               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+               val |= MSF_INT;
+               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+       } else if ((nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_BS) ||
+                       (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_SS)) {
+               NVSR_WARN("Sliding Sync and Blank Stretching resync "
+                       "methods not supported; defaulting to Immediate\n");
+               nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_IMM;
+       } else
+               nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_IMM;
+
+       nvsr->resync_delay = 0;
+}
+
+static int tegra_dc_nvsr_init_src(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       u32 val;
+
+       /* Reset SRC if it's not in idle */
+       ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val);
+       NVSR_RETV(ret, "Failed to read status register.\n");
+       if ((val & NVSR_STATUS_SR_STATE_MASK)
+               != NVSR_STATUS_SR_STATE_IDLE) {
+               /* Reset SRC */
+               ret = tegra_dc_nvsr_reset_src(nvsr);
+               NVSR_RETV(ret, "Failed to reset SRC\n");
+       }
+
+       /* Unmask and enable interrupts from SRC */
+       ret = nvsr->reg_ops.write(nvsr, NVSR_INTR0, 1, 0xff);
+       NVSR_RETV(ret, "Failed to write to interrupt mask regiser.\n");
+       val = NVSR_INTR1_EN_YES;
+       val |= NVSR_INTR1_EN_MASK_ENABLE;
+       ret = nvsr->reg_ops.write(nvsr, NVSR_INTR1, 1, val);
+       NVSR_RETV(ret, "Failed to write to interrupt enable regiser.\n");
+
+       /* Can't populate capabilities during init since DP interface
+        * isn't enabled yet at that point. Read capabilities here instead,
+        * just once */
+       if (unlikely(!nvsr->cap.is_init)) {
+               ret = tegra_dc_nvsr_query_capabilities(nvsr);
+               NVSR_RETV(ret, "Can't ready capabilities\n");
+
+               nvsr->pt_timing = nvsr->dc->mode;
+               nvsr->sr_timing = nvsr->dc->mode;
+       }
+
+       ret = tegra_dc_nvsr_update_timings(nvsr);
+       NVSR_RETV(ret, "Failed to write SRC timings\n");
+
+       tegra_dc_nvsr_config_resync_method(nvsr);
+
+       nvsr->is_init = true;
+
+       return 0;
+}
+
+static int tegra_dc_nvsr_exit_idle(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+       struct tegra_dc *dc = nvsr->dc;
+       u32 exit_timeout = nvsr->sr_timing_frame_time_us / 1000 *
+               SR_EXIT_MAX_TRIES;
+
+       if (!nvsr->is_init || !nvsr->src_on ||
+               !nvsr->enable)
+               return -ENODEV;
+
+       if (!nvsr->idle_active)
+               return 0;
+
+       tegra_dc_get(dc);
+
+       /* Program current frame as NC, to be triggered in MSF/FL interrupt */
+       tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG,
+                                       DC_CMD_STATE_CONTROL);
+
+       /* Set up for FL IRQ, request SR-exit from SRC, and wait */
+       nvsr->waiting_on_framelock = true;
+       tegra_dc_unmask_interrupt(dc, MSF_INT);
+       ret = tegra_dc_nvsr_exit_sr(nvsr);
+       if (ret) {
+               NVSR_ERR("Exiting SR failed\n");
+               nvsr->waiting_on_framelock = false;
+               tegra_dc_mask_interrupt(dc, MSF_INT);
+
+               /* Attempt to reset and re-initialize SRC. */
+               BUG_ON(tegra_dc_nvsr_init_src(nvsr));
+
+               tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY,
+                                               DC_CMD_DISPLAY_COMMAND);
+               tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+               dc->out->flags &= ~TEGRA_DC_OUT_ONE_SHOT_MODE;
+
+               return ret;
+       }
+
+       wait_for_completion_interruptible_timeout(&nvsr->framelock_comp,
+               msecs_to_jiffies(exit_timeout));
+
+       /* Switch DC NC-->C during the single NC frame */
+       tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY,
+                                       DC_CMD_DISPLAY_COMMAND);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       dc->out->flags &= ~TEGRA_DC_OUT_ONE_SHOT_MODE;
+
+       init_completion(&nvsr->framelock_comp);
+       tegra_dc_put(dc);
+
+       nvsr->idle_active = false;
+
+       return 0;
+}
+
+static int tegra_dc_nvsr_enable_nvsr(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+
+       if (nvsr->enable)
+               return 0;
+
+       /* tegra_dc_nvsr_init_src will turn
+        * on SRC and do initial config */
+       ret = tegra_dc_nvsr_init_src(nvsr);
+       NVSR_RETV(ret, "Failed to init SRC\n");
+
+       nvsr->enable = true;
+
+       return 0;
+}
+
+static int tegra_dc_nvsr_disable_nvsr(struct tegra_dc_nvsr_data *nvsr)
+{
+       int ret;
+
+       if (!nvsr->enable)
+               return 0;
+
+       if (nvsr->idle_active) {
+               ret = tegra_dc_nvsr_exit_idle(nvsr);
+               NVSR_RETV(ret, "Can't exit idle, leaving NVSR enabled\n");
+       }
+
+       ret = tegra_dc_nvsr_src_power_off(nvsr);
+       NVSR_RETV(ret, "Failed to shut off SRC\n");
+
+       nvsr->enable = false;
+
+       return 0;
+}
+
+
+static void tegra_dc_nvsr_destroy(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.destroy)
+               nvsr->out_ops.destroy(dc);
+}
+
+static bool tegra_dc_nvsr_detect(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.detect)
+               return nvsr->out_ops.detect(dc);
+
+       return -EINVAL;
+}
+
+static void tegra_dc_nvsr_enable(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+
+       if (nvsr->out_ops.enable)
+               nvsr->out_ops.enable(dc);
+
+       tegra_dc_nvsr_init_src(nvsr);
+}
+
+static bool tegra_dc_nvsr_early_enable(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.early_enable)
+               return nvsr->out_ops.early_enable(dc);
+
+       return true;
+}
+
+static void tegra_dc_nvsr_disable(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+
+       tegra_dc_nvsr_src_power_off(nvsr);
+
+       if (nvsr->out_ops.disable)
+               nvsr->out_ops.disable(dc);
+}
+
+static void tegra_dc_nvsr_hold(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.hold)
+               nvsr->out_ops.hold(dc);
+}
+
+static void tegra_dc_nvsr_release(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.release)
+               nvsr->out_ops.release(dc);
+}
+
+static void tegra_dc_nvsr_idle(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.idle)
+               nvsr->out_ops.idle(dc);
+}
+
+static void tegra_dc_nvsr_suspend(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.suspend)
+               nvsr->out_ops.suspend(dc);
+}
+
+static void tegra_dc_nvsr_resume(struct tegra_dc *dc)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.resume)
+               nvsr->out_ops.resume(dc);
+}
+
+static bool tegra_dc_nvsr_mode_filter(const struct tegra_dc *dc,
+               struct fb_videomode *mode)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.mode_filter)
+               return nvsr->out_ops.mode_filter(dc, mode);
+
+       return -EINVAL;
+}
+
+static long tegra_dc_nvsr_setup_clk(struct tegra_dc *dc, struct clk *clk)
+{
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       if (nvsr->out_ops.setup_clk)
+               return nvsr->out_ops.setup_clk(dc, clk);
+
+       return -EINVAL;
+}
+
+static void tegra_dc_nvsr_init_out_ops(struct tegra_dc_nvsr_data *nvsr,
+       struct tegra_dc_out_ops *out_ops)
+{
+       if (out_ops->init)
+               nvsr->out_ops.init = out_ops->init;
+
+       if (out_ops->destroy) {
+               tegra_dc_nvsr_ops.destroy = tegra_dc_nvsr_destroy;
+               nvsr->out_ops.destroy = out_ops->destroy;
+       }
+
+       if (out_ops->detect) {
+               tegra_dc_nvsr_ops.detect = tegra_dc_nvsr_detect;
+               nvsr->out_ops.detect = out_ops->detect;
+       }
+
+       if (out_ops->enable) {
+               tegra_dc_nvsr_ops.enable = tegra_dc_nvsr_enable;
+               nvsr->out_ops.enable = out_ops->enable;
+       }
+
+       if (out_ops->early_enable) {
+               tegra_dc_nvsr_ops.early_enable = tegra_dc_nvsr_early_enable;
+               nvsr->out_ops.early_enable = out_ops->early_enable;
+       }
+
+       if (out_ops->disable) {
+               tegra_dc_nvsr_ops.disable = tegra_dc_nvsr_disable;
+               nvsr->out_ops.disable = out_ops->disable;
+       }
+
+       if (out_ops->hold) {
+               tegra_dc_nvsr_ops.hold = tegra_dc_nvsr_hold;
+               nvsr->out_ops.hold = out_ops->hold;
+       }
+
+       if (out_ops->release) {
+               tegra_dc_nvsr_ops.release = tegra_dc_nvsr_release;
+               nvsr->out_ops.release = out_ops->release;
+       }
+       if (out_ops->idle) {
+               tegra_dc_nvsr_ops.idle = tegra_dc_nvsr_idle;
+               nvsr->out_ops.idle = out_ops->idle;
+       }
+       if (out_ops->suspend) {
+               tegra_dc_nvsr_ops.suspend = tegra_dc_nvsr_suspend;
+               nvsr->out_ops.suspend = out_ops->suspend;
+       }
+       if (out_ops->resume) {
+               tegra_dc_nvsr_ops.resume = tegra_dc_nvsr_resume;
+               nvsr->out_ops.resume = out_ops->resume;
+       }
+       if (out_ops->mode_filter) {
+               tegra_dc_nvsr_ops.mode_filter = tegra_dc_nvsr_mode_filter;
+               nvsr->out_ops.mode_filter = out_ops->mode_filter;
+       }
+       if (out_ops->setup_clk) {
+               tegra_dc_nvsr_ops.setup_clk = tegra_dc_nvsr_setup_clk;
+               nvsr->out_ops.setup_clk = out_ops->setup_clk;
+       }
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int nvsr_dbg_read_read_show(struct seq_file *s, void *unused)
+{ return 0; }
+static int nvsr_dbg_read_write_show(struct seq_file *s, void *unused)
+{ return 0; }
+
+static int nvsr_dbg_read_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, nvsr_dbg_read_read_show, inode->i_private);
+}
+
+static int nvsr_dbg_write_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, nvsr_dbg_read_write_show, inode->i_private);
+}
+
+static const char *tegra_dc_nvsr_state_to_string(u32 val)
+{
+       val &= NVSR_STATUS_SR_STATE_MASK;
+       switch (val) {
+       case NVSR_STATUS_SR_STATE_OFFLINE:
+               return "Offline";
+       case NVSR_STATUS_SR_STATE_IDLE:
+               return "Idle";
+       case NVSR_STATUS_SR_STATE_SR_ENTRY_TRIG:
+               return "SR-entry triggered";
+       case NVSR_STATUS_SR_STATE_SR_ENTRY_CACHING:
+               return "SR-entry caching";
+       case NVSR_STATUS_SR_STATE_SR_ENTRY_RDY:
+               return "SR-entry ready";
+       case NVSR_STATUS_SR_STATE_SR_ACTIVE:
+               return "SR active";
+       case NVSR_STATUS_SR_STATE_SR_EXIT_TRIG:
+               return "SR-exit triggered";
+       case NVSR_STATUS_SR_STATE_SR_EXIT_RESYNC:
+               return "SR-exit resynching";
+       default:
+               return "Unknown";
+       }
+
+       /* Should never get here */
+       return "";
+}
+
+static int nvsr_dbg_status_show(struct seq_file *s, void *unused)
+{
+       struct tegra_dc_nvsr_data *nvsr = s->private;
+       u32 val, val2;
+       int ret;
+
+       if (!nvsr->is_init) {
+               seq_printf(s, "NVSR has not been initialized.\n");
+               return -EINVAL;
+       }
+
+       tegra_dc_nvsr_get(nvsr);
+
+       seq_printf(s, "SRC Info\n");
+       seq_printf(s, "--------\n");
+       seq_printf(s, "Device ID: \t\t\t\t%d\n", nvsr->src_id.device_id);
+       seq_printf(s, "Vendor ID: \t\t\t\t%d\n", nvsr->src_id.vendor_id);
+       seq_printf(s, "Sparse Refresh mode supported: \t\t%d\n",
+               nvsr->cap.sparse_mode_support);
+       seq_printf(s, "\tEntry request band capability: \t%d\n",
+               nvsr->cap.sr_entry_req);
+       seq_printf(s, "\tExit request band capability: \t%d\n",
+               nvsr->cap.sr_exit_req);
+       seq_printf(s, "\tResync capabilities:\n");
+       if (!nvsr->cap.resync)
+               seq_printf(s, "\t\tImmediate\n");
+       else {
+               if (nvsr->cap.resync &
+                       NVSR_SR_CAPS0_RESYNC_CAP_FL)
+                       seq_printf(s, "\t\tFramelock\n");
+               if (nvsr->cap.resync &
+                       NVSR_SR_CAPS0_RESYNC_CAP_BS)
+                       seq_printf(s, "\t\tBlank stretching\n");
+               if (nvsr->cap.resync &
+                       NVSR_SR_CAPS0_RESYNC_CAP_SS)
+                       seq_printf(s, "\t\tSliding sync\n");
+       }
+       seq_printf(s, "Buffered mode supported: \t\t%d\n",
+               nvsr->cap.buffered_mode_support);
+       seq_printf(s, "Burst mode supported: \t\t\t%d\n",
+               nvsr->cap.burst_mode_support);
+       seq_printf(s, "\tSRC max input pclk: \t\t%d\n",
+               nvsr->cap.max_pt_pclk);
+       seq_printf(s, "\n");
+       seq_printf(s, "SRC status\n");
+       seq_printf(s, "----------\n");
+       seq_printf(s, "NVSR functionality enabled: \t%d\n", nvsr->enable);
+       seq_printf(s, "In Sparse Refresh: \t\t%d\n", nvsr->sr_active);
+       seq_printf(s, "Resync method: \t\t\t%s\n",
+               !nvsr->resync_method ?
+               "Immediate" :
+               nvsr->resync_method == NVSR_SR_CAPS0_RESYNC_CAP_FL ?
+               "Framelock" :
+               nvsr->resync_method == NVSR_RESYNC_CTL_METHOD_BS ?
+               "Blank stretching" :
+               nvsr->resync_method == NVSR_SR_CAPS0_RESYNC_CAP_SS ?
+               "Sliding sync" :
+               "unknown");
+       seq_printf(s, "Resync delay (frames): \t\t%d\n", nvsr->resync_delay);
+       seq_printf(s, "SRC powered: \t\t\t%d\n", nvsr->src_on);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val);
+       seq_printf(s, "Current SRC status: \t%s\n",
+               tegra_dc_nvsr_state_to_string(val));
+
+       seq_printf(s, "PT Timing:\n");
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_PIXEL_CLOCK0, 2, &val);
+       seq_printf(s, "\tPixel clock: \t%dHz\n", ret ? -1 : val * 20000);
+
+       /* Horizontal PT timing */
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING0, 2, &val);
+       seq_printf(s, "\tHactive: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING2, 2, &val);
+       seq_printf(s, "\tHblank: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING4, 2, &val);
+       seq_printf(s, "\tHfp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING6, 2, &val);
+       seq_printf(s, "\tHbp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING8, 1, &val);
+       seq_printf(s, "\tHsync width: \t%d\n", val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING9, 1, &val);
+       val2 = val & NVSR_PTMODE_TIMING9_HORZ_BORDER_MASK;
+       val = (val & NVSR_PTMODE_TIMING9_HSYNC_POL_MASK) >>
+               NVSR_PTMODE_TIMING9_HSYNC_POL_SHIFT;
+       seq_printf(s, "\tHborder: \t%d\n", ret ? -1 : val2);
+       seq_printf(s, "\tHsync pulse polarity: \t%d\n", ret ? -1 : val);
+
+       /* Vertical PT timing */
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING10, 2, &val);
+       seq_printf(s, "\tVactive: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING12, 2, &val);
+       seq_printf(s, "\tVblank \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING14, 2, &val);
+       seq_printf(s, "\tVfp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING16, 2, &val);
+       seq_printf(s, "\tVbp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING18, 1, &val);
+       seq_printf(s, "\tVsync width: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING19, 1, &val);
+       val2 = val & NVSR_PTMODE_TIMING19_VERT_BORDER_MASK;
+       val = (val & NVSR_PTMODE_TIMING19_VSYNC_POL_MASK) >>
+               NVSR_PTMODE_TIMING19_VSYNC_POL_SHIFT;
+       seq_printf(s, "\tVborder: \t%d\n", ret ? -1 : val2);
+       seq_printf(s, "\tVsync pulse polarity: \t%d\n", ret ? -1 : val);
+       seq_printf(s, "\tFrame time: \t%dus\n", nvsr->pt_timing_frame_time_us);
+
+       seq_printf(s, "SR Timing:\n");
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_PIXEL_CLOCK0, 2, &val);
+       seq_printf(s, "\tPixel clock: \t%dHz\n", ret ? -1 : val * 20000);
+
+       /* Horizontal SR timing */
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING0, 2, &val);
+       seq_printf(s, "\tHactive: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING2, 2, &val);
+       seq_printf(s, "\tHblank: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING4, 2, &val);
+       seq_printf(s, "\tHfp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING6, 2, &val);
+       seq_printf(s, "\tHbp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING8, 1, &val);
+       seq_printf(s, "\tHsync width: \t%d\n", val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING9, 1, &val);
+       val2 = val & NVSR_SRMODE_TIMING9_HORZ_BORDER_MASK;
+       val = (val & NVSR_SRMODE_TIMING9_HSYNC_POL_MASK) >>
+               NVSR_SRMODE_TIMING9_HSYNC_POL_SHIFT;
+       seq_printf(s, "\tHborder: \t%d\n", ret ? -1 : val2);
+       seq_printf(s, "\tHsync pulse polarity: \t%d\n", ret ? -1 : val);
+
+       /* Vertical SR timing */
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING10, 2, &val);
+       seq_printf(s, "\tVactive: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING12, 2, &val);
+       seq_printf(s, "\tVblank \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING14, 2, &val);
+       seq_printf(s, "\tVfp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING16, 2, &val);
+       seq_printf(s, "\tVbp: \t\t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING18, 1, &val);
+       seq_printf(s, "\tVsync width: \t%d\n", ret ? -1 : val);
+
+       ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING19, 1, &val);
+       val2 = val & NVSR_SRMODE_TIMING19_VERT_BORDER;
+       val = (val & NVSR_SRMODE_TIMING19_VSYNC_POL_MASK) >>
+               NVSR_SRMODE_TIMING19_VSYNC_POL_SHIFT;
+       seq_printf(s, "\tVborder: \t%d\n", ret ? -1 : val2);
+       seq_printf(s, "\tVsync pulse polarity: \t%d\n", ret ? -1 : val);
+       seq_printf(s, "\tFrame time: \t%dus\n", nvsr->sr_timing_frame_time_us);
+
+       tegra_dc_nvsr_put(nvsr);
+
+       return 0;
+}
+
+static int nvsr_dbg_status_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, nvsr_dbg_status_show, inode->i_private);
+}
+
+static int nvsr_dbg_read_write(struct file *file,
+       const char __user *userbuf, size_t count, loff_t *ppos)
+{
+       struct seq_file *m = file->private_data;
+       struct tegra_dc_nvsr_data *nvsr = m ? m->private : NULL;
+       char buf[128], *addr, *size, *pbuf;
+       u32 uaddr, usize, val;
+
+       if (!nvsr)
+               return -ENODEV;
+
+       if (sizeof(buf) <= count)
+               return -EFAULT;
+
+       if (copy_from_user(buf, userbuf, sizeof(buf)))
+               return -EFAULT;
+
+       buf[count] = '\0';
+       strim(buf);
+
+       pbuf = buf;
+       addr = strsep(&pbuf, " ");
+       size = strsep(&pbuf, " ");
+
+       if (!size) {
+               NVSR_INFO("Syntax error: <addr_hex> <num_bytes>\n");
+               return -EINVAL;
+       }
+
+       if (kstrtou32(addr, 16, &uaddr)) {
+               NVSR_INFO("Can't parse address \"%s\"\n", addr);
+               return -EINVAL;
+       }
+
+       if (kstrtou32(size, 10, &usize)) {
+               NVSR_INFO("Can't parse data size\"%s\"\n", size);
+               return -EINVAL;
+       }
+
+       NVSR_INFO("Reading %u bytes from address 0x%x:\n", usize, uaddr);
+
+       tegra_dc_nvsr_get(nvsr);
+
+       if (nvsr->reg_ops.read(nvsr, uaddr, usize, &val))
+               NVSR_INFO("Failed to read register.\n");
+
+       NVSR_INFO("NVSR: Read back 0x%x\n", val);
+
+       tegra_dc_nvsr_put(nvsr);
+
+       return count;
+}
+
+static int nvsr_dbg_write_write(struct file *file,
+       const char __user *userbuf, size_t count, loff_t *ppos)
+{
+       struct seq_file *m = file->private_data;
+       struct tegra_dc_nvsr_data *nvsr = m ? m->private : NULL;
+       char buf[128], *addr, *val, *size, *pbuf;
+       u32 uaddr, usize;
+       u64 uval;
+
+       if (!nvsr)
+               return -ENODEV;
+
+       if (sizeof(buf) <= count)
+               return -EFAULT;
+
+       if (copy_from_user(buf, userbuf, sizeof(buf)))
+               return -EFAULT;
+
+       buf[count] = '\0';
+       strim(buf);
+
+       pbuf = buf;
+       addr = strsep(&pbuf, " ");
+       val = strsep(&pbuf, " ");
+       size = strsep(&pbuf, " ");
+
+       if (!val || !size) {
+               NVSR_INFO("Syntax error: <addr_hex> <data_hex> <num_bytes>\n");
+               return -EINVAL;
+       }
+
+       if (kstrtou32(addr, 16, &uaddr)) {
+               NVSR_INFO("Can't parse address \"%s\"\n", addr);
+               return -EINVAL;
+       }
+
+       if (kstrtou64(val, 16, &uval)) {
+               NVSR_INFO("Can't parse value\"%s\"\n", val);
+               return -EINVAL;
+       }
+
+       if (kstrtou32(size, 10, &usize)) {
+               NVSR_INFO("Can't parse data size\"%s\"\n", size);
+               return -EINVAL;
+       }
+
+       NVSR_INFO("Writing value of 0x%llx (%u bytes) to address 0x%x...\n",
+               uval, usize, uaddr);
+
+       tegra_dc_nvsr_get(nvsr);
+
+       if (nvsr->reg_ops.write(nvsr, uaddr, usize, uval))
+               NVSR_INFO("Failed to write to register.\n");
+
+       tegra_dc_nvsr_put(nvsr);
+
+       return count;
+}
+
+static const struct file_operations nvsr_dbg_read_fops = {
+       .open           = nvsr_dbg_read_open,
+       .read           = seq_read,
+       .write          = nvsr_dbg_read_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations nvsr_dbg_write_fops = {
+       .open           = nvsr_dbg_write_open,
+       .read           = seq_read,
+       .write          = nvsr_dbg_write_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations nvsr_dbg_status_fops = {
+       .open           = nvsr_dbg_status_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void tegra_dc_nvsr_debug_create(struct tegra_dc_nvsr_data *nvsr)
+{
+       struct dentry *retval, *nvsrdir;
+
+       nvsrdir = debugfs_create_dir("tegra_nvsr", NULL);
+       if (!nvsrdir)
+               return;
+
+       retval = debugfs_create_file("read", S_IRUGO | S_IWUGO,
+               nvsrdir, nvsr, &nvsr_dbg_read_fops);
+       if (!retval)
+               goto free_out;
+       retval = debugfs_create_file("write", S_IRUGO | S_IWUGO,
+               nvsrdir, nvsr, &nvsr_dbg_write_fops);
+       if (!retval)
+               goto free_out;
+       retval = debugfs_create_file("status", S_IRUGO,
+               nvsrdir, nvsr, &nvsr_dbg_status_fops);
+       if (!retval)
+               goto free_out;
+
+       return;
+
+free_out:
+       debugfs_remove_recursive(nvsrdir);
+       return;
+}
+
+#else
+static inline void tegra_dc_nvsr_debug_create(struct tegra_dc_nvsr_data *nvsr)
+{ }
+#endif
+
+static int tegra_dc_nvsr_init(struct tegra_dc *dc)
+{
+       int ret;
+
+       struct tegra_dc_nvsr_data *nvsr = kzalloc(sizeof(*nvsr), GFP_KERNEL);
+       if (!nvsr) {
+               ret = -ENOMEM;
+               goto err_nvsr_init_out;
+       }
+
+       nvsr->dc = dc;
+       dc->nvsr = nvsr;
+
+       switch (dc->out->type) {
+       case TEGRA_DC_OUT_NVSR_DP:
+               tegra_dc_nvsr_init_out_ops(nvsr, &tegra_dc_dp_ops);
+               if (nvsr->out_ops.init) {
+                       ret = nvsr->out_ops.init(dc);
+                       NVSR_RETV(ret, "Out ops init failed.\n");
+               }
+               nvsr->out_data.dp = tegra_dc_get_outdata(dc);
+               nvsr->out_clk = nvsr->out_data.dp->clk;
+               nvsr->reg_ops.read = tegra_nvsr_read_dpaux;
+               nvsr->reg_ops.write = tegra_nvsr_write_dpaux;
+               break;
+       default:
+               pr_err("NVSR output interface not found.\n");
+               ret = -EINVAL;
+               goto err_nvsr_init_free;
+       }
+
+       tegra_dc_nvsr_debug_create(nvsr);
+       init_completion(&nvsr->framelock_comp);
+
+       nvsr->enable = true;
+
+       return ret;
+
+err_nvsr_init_free:
+       kfree(nvsr);
+err_nvsr_init_out:
+       return ret;
+}
+
+struct tegra_dc_out_ops tegra_dc_nvsr_ops = {
+       .init = tegra_dc_nvsr_init,
+};
+
+static struct kobject *nvsr_kobj;
+
+static ssize_t enable_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       struct device *dev = container_of((kobj->parent), struct device, kobj);
+       struct platform_device *ndev = to_platform_device(dev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", dc->nvsr->enable);
+}
+
+static ssize_t enable_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       struct device *dev = container_of((kobj->parent), struct device, kobj);
+       struct platform_device *ndev = to_platform_device(dev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       u32 val;
+
+       if (kstrtou32(buf, 10, &val) < 0) {
+               dev_err(dev, "NVSR: bad enable input: \"%s\"\n", buf);
+               return -EINVAL;
+       }
+
+       tegra_dc_nvsr_get(nvsr);
+
+       if (val)
+               NVSR_RETV(tegra_dc_nvsr_enable_nvsr(nvsr),
+                       "Failed to enable NVSR\n")
+       else
+               NVSR_RETV(tegra_dc_nvsr_disable_nvsr(nvsr),
+                       "Failed to disable NVSR\n")
+
+       tegra_dc_nvsr_put(nvsr);
+
+       return count;
+}
+
+static struct kobj_attribute nvsr_attr_enable =
+       __ATTR(enable, S_IRUGO|S_IWUSR, enable_show, enable_store);
+
+static ssize_t idle_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       struct device *dev = container_of((kobj->parent), struct device, kobj);
+       struct platform_device *ndev = to_platform_device(dev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", dc->nvsr->idle_active);
+}
+
+static ssize_t idle_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       struct device *dev = container_of((kobj->parent), struct device, kobj);
+       struct platform_device *ndev = to_platform_device(dev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
+       struct tegra_dc_nvsr_data *nvsr = dc->nvsr;
+       u32 val;
+
+       if (kstrtou32(buf, 10, &val) < 0) {
+               dev_err(dev, "NVSR: bad enable input: \"%s\"\n", buf);
+               return -EINVAL;
+       }
+
+       if (val)
+               NVSR_RETV(tegra_dc_nvsr_enter_idle(nvsr),
+                       "Failed to enter idle\n")
+       else
+               NVSR_RETV(tegra_dc_nvsr_exit_idle(nvsr),
+                       "Failed to exit SR\n")
+
+       return count;
+}
+
+static struct kobj_attribute nvsr_attr_idle =
+       __ATTR(idle, S_IRUGO|S_IWUSR, idle_show, idle_store);
+
+static struct attribute *nvsr_attrs[] = {
+       &nvsr_attr_enable.attr,
+       &nvsr_attr_idle.attr,
+       NULL,
+};
+
+static struct attribute_group nvsr_attr_group = {
+       .attrs = nvsr_attrs,
+};
+
+/* Sysfs initializer */
+int nvsr_create_sysfs(struct device *dev)
+{
+       int retval;
+
+       nvsr_kobj = kobject_create_and_add("nvsr", &dev->kobj);
+
+       if (!nvsr_kobj)
+               return -ENOMEM;
+
+       retval = sysfs_create_group(nvsr_kobj, &nvsr_attr_group);
+
+       if (retval) {
+               kobject_put(nvsr_kobj);
+               dev_err(dev, "%s: failed to create attributes\n", __func__);
+       }
+
+       return retval;
+}
+
+/* Sysfs destructor */
+void nvsr_remove_sysfs(struct device *dev)
+{
+       if (nvsr_kobj) {
+               sysfs_remove_group(nvsr_kobj, &nvsr_attr_group);
+               kobject_put(nvsr_kobj);
+       }
+}
+
diff --git a/drivers/video/tegra/dc/nvsr.h b/drivers/video/tegra/dc/nvsr.h
new file mode 100644 (file)
index 0000000..a0a35ff
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * drivers/video/tegra/dc/nvsr.h
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION, All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DRIVER_VIDEO_TEGRA_DC_NVSR_H__
+#define __DRIVER_VIDEO_TEGRA_DC_NVSR_H__
+
+#include <mach/dc.h>
+#include "dc_priv.h"
+#include "lvds.h"
+#include "dp.h"
+#include "dsi.h"
+
+struct tegra_dc_nvsr_data {
+       struct tegra_dc *dc;
+
+       struct {
+               int (*read) (struct tegra_dc_nvsr_data *nvsr,
+                       u32 reg, u32 size, u32 *val);
+               int (*write) (struct tegra_dc_nvsr_data *nvsr,
+                       u32 reg, u32 size, u64 val);
+       } reg_ops;
+
+       union {
+               struct tegra_dc_dp_data *dp;
+               struct tegra_dc_dsi_data        *dsi;
+               struct tegra_dc_lvds_data       *lvds;
+       } out_data;
+
+       struct {
+               u8 sr;
+               u8 sr_entry_req;
+               u8 sr_exit_req;
+               u8 resync;
+
+               bool sep_pclk;
+               /* Maximum DC->SRC pixel clock */
+               u32 max_pt_pclk;
+
+               bool sparse_mode_support;
+               bool burst_mode_support;
+               bool buffered_mode_support;
+
+               bool is_init;
+       } cap;
+
+       struct {
+               u16 device_id;
+               u16 vendor_id;
+       } src_id;
+
+       bool is_init;
+       bool src_on;
+       enum {
+               SR_MODE_SPARSE,
+               SR_MODE_BUFFERED,
+               SR_MODE_BURST
+       } sr_mode;
+
+       bool enable;
+       bool sr_active;
+       bool idle_active;
+       bool resync_delay;
+       u8 resync_method;
+
+       bool waiting_on_framelock;
+       struct completion framelock_comp;
+
+       struct clk      *out_clk;
+       struct tegra_dc_out_ops out_ops;
+       bool is_nvsr_init;
+
+       struct tegra_dc_mode pt_timing; /* DC to SRC */
+       u32 pt_timing_frame_time_us;
+       struct tegra_dc_mode sr_timing; /* SRC to panel */
+       u32 sr_timing_frame_time_us;
+};
+
+#ifdef CONFIG_TEGRA_NVSR
+void tegra_dc_nvsr_irq(struct tegra_dc_nvsr_data *nvsr, unsigned long status);
+int nvsr_create_sysfs(struct device *dev);
+void nvsr_remove_sysfs(struct device *dev);
+#else
+static inline void tegra_dc_nvsr_irq(struct tegra_dc_nvsr_data *nvsr, unsigned long status) {}
+static inline int nvsr_create_sysfs(struct device *dev) { return -ENOSYS; }
+static inline void nvsr_remove_sysfs(struct device *dev) {}
+#endif
+
+#endif
diff --git a/drivers/video/tegra/dc/nvsr_regs.h b/drivers/video/tegra/dc/nvsr_regs.h
new file mode 100644 (file)
index 0000000..b60e733
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * drivers/video/tegra/dc/nvsr_regs.h
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION, All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DRIVER_VIDEO_TEGRA_DC_NVSR_REGS_H__
+
+/* Macros and address/field defines were
+ * borrowed from big GPU driver */
+
+#define NVSR_SRC_ID0                                          0x3a0
+#define NVSR_SR_CAPS0                                         0x335
+#define NVSR_SR_CAPS0_SR_CAPABLE_MASK                         1
+#define NVSR_SR_CAPS0_SR_CAPABLE_SHIFT                        0
+#define NVSR_SR_CAPS0_SR_ENTRY_REQ_MASK                       (3<<1)
+#define NVSR_SR_CAPS0_SR_ENTRY_REQ_SHIFT                      1
+#define NVSR_SR_CAPS0_SR_EXIT_REQ_MASK                        (3<<3)
+#define NVSR_SR_CAPS0_SR_EXIT_REQ_SHIFT                       3
+#define NVSR_SR_CAPS0_RESYNC_CAP_MASK                         (7<<5)
+#define NVSR_SR_CAPS0_RESYNC_CAP_SHIFT                        5
+#define NVSR_SR_CAPS0_RESYNC_CAP_SS                           1
+#define NVSR_SR_CAPS0_RESYNC_CAP_FL                           2
+#define NVSR_SR_CAPS0_RESYNC_CAP_BS                           4
+#define NVSR_SR_CAPS2                                         0x337
+#define NVSR_SR_CAPS2_SEPERATE_PCLK_SUPPORTED                 1
+#define NVSR_SR_CAPS2_BUFFERED_REFRESH_SUPPORTED              (1<<1)
+#define NVSR_SR_CAPS2_FRAME_LOCK_MASK                         (1<<2)
+#define NVSR_SR_CAPS2_FRAME_LOCK_SHIFT                        2
+#define NVSR_SR_MAX_PCLK0                                     0x339
+#define NVSR_SRC_CTL0                                         0x340
+#define NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED                   1
+#define NVSR_SRC_CTL0_SR_ENABLE_CTL_DISABLED                  0
+#define NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE                   (1<<1)
+#define NVSR_SRC_CTL0_SR_ENTRY_REQ_YES                        (1<<4)
+#define NVSR_SRC_CTL0_SR_ENTRY_MASK_ENABLE                    (1<<5)
+#define NVSR_SRC_CTL0_SR_EXIT_REQ_YES                         (1<<6)
+#define NVSR_SRC_CTL0_SR_EXIT_MASK_ENABLE                     (1<<7)
+#define NVSR_SRC_CTL1                                         0x341
+#define NVSR_SRC_CTL1_SIDEBAND_ENTRY_SEL_YES                  (1<<4)
+#define NVSR_SRC_CTL1_SIDEBAND_ENTRY_MASK_ENABLE              (1<<5)
+#define NVSR_SRC_CTL1_SIDEBAND_EXIT_SEL_YES                   (1<<6)
+#define NVSR_SRC_CTL1_SIDEBAND_EXIT_MASK_ENABLE               (1<<7)
+#define NVSR_INTR0                                            0x343
+#define NVSR_INTR1                                            0x344
+#define NVSR_INTR1_EN_YES                                     1
+#define NVSR_INTR1_EN_MASK_ENABLE                             (1<<1)
+#define NVSR_RESYNC_CTL                                       0x346
+#define NVSR_RESYNC_CTL_METHOD_IMM                            0
+#define NVSR_RESYNC_CTL_METHOD_SS                             1
+#define NVSR_RESYNC_CTL_METHOD_FL                             2
+#define NVSR_RESYNC_CTL_METHOD_BS                             3
+#define NVSR_RESYNC_CTL_DELAY_SHIFT                           6
+#define NVSR_STATUS                                           0x348
+#define NVSR_STATUS_SR_STATE_MASK                             7
+#define NVSR_STATUS_SR_STATE_OFFLINE                          0
+#define NVSR_STATUS_SR_STATE_IDLE                             1
+#define NVSR_STATUS_SR_STATE_SR_ENTRY_TRIG                    2
+#define NVSR_STATUS_SR_STATE_SR_ENTRY_CACHING                 3
+#define NVSR_STATUS_SR_STATE_SR_ENTRY_RDY                     4
+#define NVSR_STATUS_SR_STATE_SR_ACTIVE                        5
+#define NVSR_STATUS_SR_STATE_SR_EXIT_TRIG                     6
+#define NVSR_STATUS_SR_STATE_SR_EXIT_RESYNC                   7
+#define NVSR_SRMODE_PIXEL_CLOCK0                              0x350
+#define NVSR_SRMODE_TIMING0                                   0x352
+#define NVSR_SRMODE_TIMING2                                   0x354
+#define NVSR_SRMODE_TIMING4                                   0x356
+#define NVSR_SRMODE_TIMING6                                   0x358
+#define NVSR_SRMODE_TIMING8                                   0x35a
+#define NVSR_SRMODE_TIMING9                                   0x35b
+#define NVSR_SRMODE_TIMING9_HORZ_BORDER_MASK                  0x7f
+#define NVSR_SRMODE_TIMING9_HSYNC_POL_MASK                    (1<<7)
+#define NVSR_SRMODE_TIMING9_HSYNC_POL_SHIFT                   7
+#define NVSR_SRMODE_TIMING9_HSYNC_POL_POS                     (1<<7)
+#define NVSR_SRMODE_TIMING9_HSYNC_POL_NEG                     (0<<7)
+#define NVSR_SRMODE_TIMING10                                  0x35c
+#define NVSR_SRMODE_TIMING12                                  0x35e
+#define NVSR_SRMODE_TIMING14                                  0x360
+#define NVSR_SRMODE_TIMING16                                  0x362
+#define NVSR_SRMODE_TIMING18                                  0x364
+#define NVSR_SRMODE_TIMING19                                  0x365
+#define NVSR_SRMODE_TIMING19_VERT_BORDER                      0x7f
+#define NVSR_SRMODE_TIMING19_VSYNC_POL_MASK                   (1<<7)
+#define NVSR_SRMODE_TIMING19_VSYNC_POL_SHIFT                  7
+#define NVSR_SRMODE_TIMING19_VSYNC_POL_POS                    (1<<7)
+#define NVSR_SRMODE_TIMING19_VSYNC_POL_NEG                    (0<<7)
+#define NVSR_PTMODE_PIXEL_CLOCK0                              0x368
+#define NVSR_PTMODE_TIMING0                                   0x36a
+#define NVSR_PTMODE_TIMING2                                   0x36c
+#define NVSR_PTMODE_TIMING4                                   0x36e
+#define NVSR_PTMODE_TIMING6                                   0x370
+#define NVSR_PTMODE_TIMING8                                   0x372
+#define NVSR_PTMODE_TIMING9                                   0x373
+#define NVSR_PTMODE_TIMING9_HORZ_BORDER_MASK                  0x7f
+#define NVSR_PTMODE_TIMING9_HSYNC_POL_MASK                    (1<<7)
+#define NVSR_PTMODE_TIMING9_HSYNC_POL_SHIFT                   7
+#define NVSR_PTMODE_TIMING9_HSYNC_POL_POS                     (1<<7)
+#define NVSR_PTMODE_TIMING9_HSYNC_POL_NEG                     (0<<7)
+#define NVSR_PTMODE_TIMING10                                  0x374
+#define NVSR_PTMODE_TIMING12                                  0x376
+#define NVSR_PTMODE_TIMING14                                  0x378
+#define NVSR_PTMODE_TIMING16                                  0x37a
+#define NVSR_PTMODE_TIMING18                                  0x37c
+#define NVSR_PTMODE_TIMING19                                  0x37d
+#define NVSR_PTMODE_TIMING19_VERT_BORDER_MASK                 0x7f
+#define NVSR_PTMODE_TIMING19_VSYNC_POL_MASK                   (1<<7)
+#define NVSR_PTMODE_TIMING19_VSYNC_POL_SHIFT                  7
+#define NVSR_PTMODE_TIMING19_VSYNC_POL_POS                    (1<<7)
+#define NVSR_PTMODE_TIMING19_VSYNC_POL_NEG                    (0<<7)
+#define NVSR_BLANK_TIMING0                                    0x388
+#define NVSR_BLANK_TIMING2                                    0x38a
+#define NVSR_BLANK_TIMING4                                    0x38c
+#define NVSR_BLANK_TIMING6                                    0x38e
+
+#endif