video: tegra: fb: Add interface to nvdps.
Kevin Huang [Tue, 10 Apr 2012 01:03:51 +0000 (18:03 -0700)]
Provide /sys/class/graphics/fb0/device/nvdps to change video mode
on-the-fly without resetting window layout like fb_set_var(). This
allows flicker free changes in refresh rate.

nvdps sysfs file takes an integer, and selects the closest matching mode
with the same or higher refresh rate. Reading the file displays the
current refresh rate.

Bug 560152

Change-Id: Id5c1eafaf338b99fa9742202b38ccbfc238b77d5
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/95473
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Jon Mayo <jmayo@nvidia.com>

arch/arm/mach-tegra/include/mach/fb.h
drivers/video/tegra/dc/dc_sysfs.c
drivers/video/tegra/fb.c

index ced6f9c..5f4a57e 100644 (file)
@@ -28,6 +28,9 @@ struct tegra_fb_data;
 struct tegra_fb_info;
 struct resource;
 
+int tegra_fb_get_mode(struct tegra_dc *dc);
+int tegra_fb_set_mode(struct tegra_dc *dc, int fps);
+
 #ifdef CONFIG_FB_TEGRA
 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                                        struct tegra_dc *dc,
index 6bb1838..0b259c3 100644 (file)
@@ -18,6 +18,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <linux/fb.h>
 #include <linux/platform_device.h>
 #include <linux/kernel.h>
 
@@ -282,6 +283,36 @@ static ssize_t mode_3d_store(struct device *dev,
 static DEVICE_ATTR(stereo_mode,
        S_IRUGO|S_IWUSR, mode_3d_show, mode_3d_store);
 
+static ssize_t nvdps_show(struct device *device,
+       struct device_attribute *attr, char *buf)
+{
+       int refresh_rate;
+       struct nvhost_device *ndev = to_nvhost_device(device);
+       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+
+       refresh_rate = tegra_fb_get_mode(dc);
+       return snprintf(buf, PAGE_SIZE, "%d\n", refresh_rate);
+}
+
+
+static ssize_t nvdps_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvhost_device *ndev = to_nvhost_device(dev);
+       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+       int refresh_rate;
+       int e;
+
+       e = kstrtoint(buf, 10, &refresh_rate);
+       if (e)
+               return e;
+       e = tegra_fb_set_mode(dc, refresh_rate);
+
+       return count;
+}
+
+static DEVICE_ATTR(nvdps, S_IRUGO|S_IWUSR, nvdps_show, nvdps_store);
+
 void __devexit tegra_dc_remove_sysfs(struct device *dev)
 {
        struct nvhost_device *ndev = to_nvhost_device(dev);
@@ -289,6 +320,7 @@ void __devexit tegra_dc_remove_sysfs(struct device *dev)
        struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
 
        device_remove_file(dev, &dev_attr_mode);
+       device_remove_file(dev, &dev_attr_nvdps);
        device_remove_file(dev, &dev_attr_enable);
        device_remove_file(dev, &dev_attr_stats_enable);
        device_remove_file(dev, &dev_attr_crc_checksum_latched);
@@ -310,6 +342,7 @@ void tegra_dc_create_sysfs(struct device *dev)
        int error = 0;
 
        error |= device_create_file(dev, &dev_attr_mode);
+       error |= device_create_file(dev, &dev_attr_nvdps);
        error |= device_create_file(dev, &dev_attr_enable);
        error |= device_create_file(dev, &dev_attr_stats_enable);
        error |= device_create_file(dev, &dev_attr_crc_checksum_latched);
index ac4c84b..e24588b 100644 (file)
@@ -415,6 +415,41 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long
        return 0;
 }
 
+int tegra_fb_get_mode(struct tegra_dc *dc) {
+       return dc->fb->info->mode->refresh;
+}
+
+int tegra_fb_set_mode(struct tegra_dc *dc, int fps) {
+       size_t stereo;
+       struct list_head *pos;
+       struct fb_videomode *best_mode = NULL;
+       int curr_diff = INT_MAX; /* difference of best_mode refresh rate */
+       struct fb_modelist *modelist;
+       struct fb_info *info = dc->fb->info;
+
+       list_for_each(pos, &info->modelist) {
+               struct fb_videomode *mode;
+
+               modelist = list_entry(pos, struct fb_modelist, list);
+               mode = &modelist->mode;
+               if (fps <= mode->refresh && curr_diff > (mode->refresh - fps)) {
+                       curr_diff = mode->refresh - fps;
+                       best_mode = mode;
+               }
+       }
+       if (best_mode) {
+               info->mode = best_mode;
+               stereo = !!(info->var.vmode & info->mode->vmode &
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+                               FB_VMODE_STEREO_FRAME_PACK);
+#else
+                               FB_VMODE_STEREO_LEFT_RIGHT);
+#endif
+               return tegra_dc_set_fb_mode(dc, best_mode, stereo);
+       }
+       return -EIO;
+}
+
 static struct fb_ops tegra_fb_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = tegra_fb_check_var,
@@ -459,6 +494,7 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
 
        memcpy(&fb_info->info->monspecs, specs,
               sizeof(fb_info->info->monspecs));
+       fb_info->info->mode = specs->modedb;
 
        for (i = 0; i < specs->modedb_len; i++) {
                if (mode_filter) {