summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorLaurence Harrison <lharrison@nvidia.com>2011-04-06 18:26:21 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:45:02 -0800
commit7e048e8d4a82d01a81bd026fb3801d43730fa82c (patch)
treec621ce85fd22bcb744af69e14721c07dc0cbec82 /drivers/video
parentc4461ce50b032d6ece330bd096bd6725395a28a0 (diff)
video: tegra: Added sysfs for dc/smartdimmer
Includes: 1.) Added basic DC sysfs objects. 2.) Sysfs objects and functions for smartdimmer settings. 3.) Register dump access for smartdimmer. 4.) Improvements to the behavior of smartdimmer (now updates at the end of vblank instead of the beginning). 5.) Rename v_blank_complete to vblank_complete to keep in same effective style as the rest of the code. Original-Change-Id: I59addcc479880322d49b24d1206009def3c4b392 Reviewed-on: http://git-master/r/29893 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com> Rebase-Id: R3a65726e3644d01b374f9774e966d635234567b4
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/tegra/dc/Makefile1
-rw-r--r--drivers/video/tegra/dc/dc.c119
-rw-r--r--drivers/video/tegra/dc/dc_priv.h7
-rw-r--r--drivers/video/tegra/dc/dc_sysfs.c121
-rwxr-xr-xdrivers/video/tegra/dc/dsi.c2
-rw-r--r--drivers/video/tegra/dc/nvsd.c397
-rw-r--r--drivers/video/tegra/dc/nvsd.h2
7 files changed, 588 insertions, 61 deletions
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
index 64ab35feaa86..63044cafd638 100644
--- a/drivers/video/tegra/dc/Makefile
+++ b/drivers/video/tegra/dc/Makefile
@@ -5,4 +5,5 @@ obj-y += nvhdcp.o
obj-y += edid.o
obj-y += nvsd.o
obj-y += dsi.o
+obj-y += dc_sysfs.o
obj-$(CONFIG_TEGRA_OVERLAY) += overlay.o
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 1ffa1686b3bf..55a93beaa725 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -486,7 +486,6 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
unsigned long update_mask = GENERAL_ACT_REQ;
unsigned long val;
bool update_blend = false;
- bool nvsd_updated = false;
int i;
dc = windows[0]->dc;
@@ -649,21 +648,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
- /* Update the SD brightness */
- nvsd_updated = nvsd_update_brightness(dc);
-
mutex_unlock(&dc->lock);
- /* Do the actual brightness update outside of the mutex */
- if (nvsd_updated && dc->out->sd_settings &&
- dc->out->sd_settings->bl_device) {
-
- struct platform_device *pdev = dc->out->sd_settings->bl_device;
- struct backlight_device *bl = platform_get_drvdata(pdev);
- if (bl)
- backlight_update_status(bl);
- }
-
return 0;
}
EXPORT_SYMBOL(tegra_dc_update_windows);
@@ -1190,6 +1176,29 @@ unsigned tegra_dc_get_out_width(struct tegra_dc *dc)
}
EXPORT_SYMBOL(tegra_dc_get_out_width);
+static void tegra_dc_vblank(struct work_struct *work)
+{
+ struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work);
+ bool nvsd_updated = false;
+
+ mutex_lock(&dc->lock);
+
+ /* Update the SD brightness */
+ nvsd_updated = nvsd_update_brightness(dc);
+
+ mutex_unlock(&dc->lock);
+
+ /* Do the actual brightness update outside of the mutex */
+ if (nvsd_updated && dc->out->sd_settings &&
+ dc->out->sd_settings->bl_device) {
+
+ struct platform_device *pdev = dc->out->sd_settings->bl_device;
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ if (bl)
+ backlight_update_status(bl);
+ }
+}
+
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
{
#ifndef CONFIG_TEGRA_FPGA_PLATFORM
@@ -1202,34 +1211,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
- if (status & V_BLANK_INT)
- complete(&dc->v_blank_complete);
-
- if (status & FRAME_END_INT) {
- int completed = 0;
- int dirty = 0;
-
- val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
- for (i = 0; i < DC_N_WINDOWS; i++) {
- if (!(val & (WIN_A_UPDATE << i))) {
- dc->windows[i].dirty = 0;
- completed = 1;
- } else {
- dirty = 1;
- }
- }
-
- if (!dirty) {
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
- val &= ~FRAME_END_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
- }
-
- if (completed)
- wake_up(&dc->wq);
- }
-
-
/*
* Overlays can get thier internal state corrupted during and underflow
* condition. The only way to fix this state is to reset the DC.
@@ -1247,6 +1228,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
if (status & V_BLANK_INT) {
int i;
+ /* Check for any underflow reset conditions */
for (i = 0; i< DC_N_WINDOWS; i++) {
if (dc->underflow_mask & (WIN_A_UF_INT <<i)) {
dc->windows[i].underflows++;
@@ -1259,14 +1241,60 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
}
if (!dc->underflow_mask) {
+ /* If we have no underflow to check, go ahead
+ and disable the interrupt */
val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
val &= ~V_BLANK_INT;
tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
}
+ /* Clear the underflow mask now that we've checked it. */
dc->underflow_mask = 0;
+
+ /* Schedule any additional bottom-half vblank actvities. */
+ schedule_work(&dc->vblank_work);
+
+ /* Mark the vblank as complete. */
+ complete(&dc->vblank_complete);
}
+ if (status & FRAME_END_INT) {
+ int completed = 0;
+ int dirty = 0;
+
+ val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+ for (i = 0; i < DC_N_WINDOWS; i++) {
+ if (!(val & (WIN_A_UPDATE << i))) {
+ dc->windows[i].dirty = 0;
+ completed = 1;
+ } else {
+ dirty = 1;
+ }
+ }
+
+ if (!dirty) {
+ val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+ val &= ~FRAME_END_INT;
+ tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+ }
+
+ if (completed) {
+ if (!dirty) {
+ /* With the last completed window, go ahead
+ and enable the vblank interrupt for nvsd. */
+ val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+ val |= V_BLANK_INT;
+ tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ val |= V_BLANK_INT;
+ tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+ }
+
+ /* Wake up the workqueue regardless. */
+ wake_up(&dc->wq);
+ }
+ }
return IRQ_HANDLED;
#else
@@ -1665,9 +1693,10 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dc->enabled = true;
mutex_init(&dc->lock);
- init_completion(&dc->v_blank_complete);
+ init_completion(&dc->vblank_complete);
init_waitqueue_head(&dc->wq);
INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
+ INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
dc->n_windows = DC_N_WINDOWS;
for (i = 0; i < dc->n_windows; i++) {
@@ -1736,6 +1765,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
if (dc->out_ops && dc->out_ops->detect)
dc->out_ops->detect(dc);
+ tegra_dc_create_sysfs(&dc->ndev->dev);
+
return 0;
err_free_irq:
@@ -1760,6 +1791,8 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
{
struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ tegra_dc_remove_sysfs(&dc->ndev->dev);
+
if (dc->overlay) {
tegra_overlay_unregister(dc->overlay);
}
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index b01f569a31ee..f5a5f24a5d4b 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -93,7 +93,9 @@ struct tegra_dc {
unsigned long underflow_mask;
struct work_struct reset_work;
- struct completion v_blank_complete;
+ struct completion vblank_complete;
+
+ struct work_struct vblank_work;
};
static inline void tegra_dc_io_start(struct tegra_dc *dc)
@@ -148,4 +150,7 @@ extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
extern struct tegra_dc_out_ops tegra_dc_hdmi_ops;
extern struct tegra_dc_out_ops tegra_dc_dsi_ops;
+void __devexit tegra_dc_remove_sysfs(struct device *dev);
+void tegra_dc_create_sysfs(struct device *dev);
#endif
+
diff --git a/drivers/video/tegra/dc/dc_sysfs.c b/drivers/video/tegra/dc/dc_sysfs.c
new file mode 100644
index 000000000000..4afc8642f173
--- /dev/null
+++ b/drivers/video/tegra/dc/dc_sysfs.c
@@ -0,0 +1,121 @@
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "dc_reg.h"
+#include "dc_priv.h"
+#include "nvsd.h"
+
+/****************
+ * Current mode *
+ ****************/
+static ssize_t mode_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvhost_device *ndev = to_nvhost_device(device);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ struct tegra_dc_mode *m;
+ ssize_t res;
+
+ mutex_lock(&dc->lock);
+ m = &dc->mode;
+ res = snprintf(buf, PAGE_SIZE,
+ "pclk: %d\n"
+ "h_ref_to_sync: %d\n"
+ "v_ref_to_sync: %d\n"
+ "h_sync_width: %d\n"
+ "v_sync_width: %d\n"
+ "h_back_porch: %d\n"
+ "v_back_porch: %d\n"
+ "h_active: %d\n"
+ "v_active: %d\n"
+ "h_front_porch: %d\n"
+ "v_front_porch: %d\n"
+ "stereo_mode: %d\n",
+ m->pclk, m->h_ref_to_sync, m->v_ref_to_sync,
+ m->h_sync_width, m->v_sync_width,
+ m->h_back_porch, m->v_back_porch,
+ m->h_active, m->v_active,
+ m->h_front_porch, m->v_front_porch,
+ m->stereo_mode);
+ mutex_unlock(&dc->lock);
+
+ return res;
+}
+
+static DEVICE_ATTR(mode, S_IRUGO, mode_show, NULL);
+
+/**************
+ * DC Enabled *
+ **************/
+static ssize_t enable_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvhost_device *ndev = to_nvhost_device(device);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ ssize_t res;
+
+ mutex_lock(&dc->lock);
+ res = snprintf(buf, PAGE_SIZE, "%d\n", dc->enabled);
+ mutex_unlock(&dc->lock);
+ return res;
+}
+
+static ssize_t enable_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 enabled;
+
+ enabled = simple_strtoul(buf, NULL, 10);
+
+ if (enabled) {
+ tegra_dc_enable(dc);
+ } else {
+ tegra_dc_disable(dc);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, enable_show, enable_store);
+
+/********
+ * Init *
+ ********/
+void __devexit tegra_dc_remove_sysfs(struct device *dev)
+{
+ struct nvhost_device *ndev = to_nvhost_device(dev);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+
+ device_remove_file(dev, &dev_attr_mode);
+ device_remove_file(dev, &dev_attr_enable);
+
+ if(sd_settings) {
+ nvsd_remove_sysfs(dev);
+ }
+}
+
+void tegra_dc_create_sysfs(struct device *dev)
+{
+ struct nvhost_device *ndev = to_nvhost_device(dev);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+ int error = 0;
+
+ error |= device_create_file(dev, &dev_attr_mode);
+ error |= device_create_file(dev, &dev_attr_enable);
+
+ if(sd_settings) {
+ error |= nvsd_create_sysfs(dev);
+ }
+
+ if(error) {
+ printk("Failed to create sysfs attributes!\n");
+ }
+}
+
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index a98ef9966613..37940581232d 100755
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -729,7 +729,7 @@ void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc, struct tegra_dc_
/* wait for vblank completion */
timeout = wait_for_completion_interruptible_timeout(
- &dc->v_blank_complete, DSI_STOP_DC_DURATION_MSEC);
+ &dc->vblank_complete, DSI_STOP_DC_DURATION_MSEC);
/* disable vblank interrupt */
val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c
index 40ab9c1c1702..75b9bf940559 100644
--- a/drivers/video/tegra/dc/nvsd.c
+++ b/drivers/video/tegra/dc/nvsd.c
@@ -17,13 +17,73 @@
#include <linux/kernel.h>
#include <mach/dc.h>
#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/backlight.h>
#include "dc_reg.h"
#include "dc_priv.h"
#include "nvsd.h"
+/* Elements for sysfs access */
+#define NVSD_ATTR(__name) static struct kobj_attribute nvsd_attr_##__name = \
+ __ATTR(__name, S_IRUGO|S_IWUSR|S_IWGRP, nvsd_settings_show, nvsd_settings_store)
+#define NVSD_ATTRS_ENTRY(__name) (&nvsd_attr_##__name.attr)
+#define IS_NVSD_ATTR(__name) (attr == &nvsd_attr_##__name)
+
+static ssize_t nvsd_settings_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+static ssize_t nvsd_settings_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+static ssize_t nvsd_registers_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+NVSD_ATTR(enable);
+NVSD_ATTR(aggressiveness);
+NVSD_ATTR(bin_width);
+NVSD_ATTR(hw_update_delay);
+NVSD_ATTR(use_vid_luma);
+NVSD_ATTR(coeff);
+NVSD_ATTR(blp_time_constant);
+NVSD_ATTR(blp_step);
+NVSD_ATTR(fc_time_limit);
+NVSD_ATTR(fc_threshold);
+NVSD_ATTR(lut);
+NVSD_ATTR(bltf);
+static struct kobj_attribute nvsd_attr_registers =
+ __ATTR(registers, S_IRUGO, nvsd_registers_show, NULL);
+
+static struct attribute *nvsd_attrs[] = {
+ NVSD_ATTRS_ENTRY(enable),
+ NVSD_ATTRS_ENTRY(aggressiveness),
+ NVSD_ATTRS_ENTRY(bin_width),
+ NVSD_ATTRS_ENTRY(hw_update_delay),
+ NVSD_ATTRS_ENTRY(use_vid_luma),
+ NVSD_ATTRS_ENTRY(coeff),
+ NVSD_ATTRS_ENTRY(blp_time_constant),
+ NVSD_ATTRS_ENTRY(blp_step),
+ NVSD_ATTRS_ENTRY(fc_time_limit),
+ NVSD_ATTRS_ENTRY(fc_threshold),
+ NVSD_ATTRS_ENTRY(lut),
+ NVSD_ATTRS_ENTRY(bltf),
+ NVSD_ATTRS_ENTRY(registers),
+ NULL,
+};
+
+static struct attribute_group nvsd_attr_group = {
+ .attrs = nvsd_attrs,
+};
+
+static struct kobject *nvsd_kobj;
+
+/* shared brightness variable */
static atomic_t *sd_brightness = NULL;
+/* shared boolean for manual K workaround */
+static atomic_t man_k_until_blank = ATOMIC_INIT(0);
+/* Functional initialization */
void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) {
u32 i = 0, val = 0;
/* TODO: check if HW says SD's available */
@@ -39,20 +99,39 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) {
return;
}
- dev_dbg(&dc->ndev->dev, "===================\n");
- dev_dbg(&dc->ndev->dev, "**** NVSD_INIT ****\n");
+ dev_dbg(&dc->ndev->dev, "NVSD Init:\n");
+
+ /* WAR: Settings will not be valid until the next flip.
+ Thus, set manual K to either HW's current value (if
+ we're already enabled) or a non-effective value (if
+ we're about to enable). */
+ val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL);
+ if (val & SD_ENABLE_NORMAL) {
+ i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES);
+ }
+ else {
+ /* 0 values for RGB = 1.0, i.e. non-affected */
+ i = 0;
+ }
+ tegra_dc_writel(dc, i, DC_DISP_SD_MAN_K_VALUES);
+ /* Enable manual correction mode here so that changing the
+ settings won't immediately impact display dehavior. */
+ val |= SD_CORRECTION_MODE_MAN;
+ tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
/* Write LUT */
+ dev_dbg(&dc->ndev->dev, " LUT:\n");
for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) {
val = SD_LUT_R(settings->lut[i].r) |
SD_LUT_G(settings->lut[i].g) |
SD_LUT_B(settings->lut[i].b);
tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i));
- dev_dbg(&dc->ndev->dev, "LUT(%d): 0x%08x\n", i, val);
+ dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val);
}
/* Write BL TF */
+ dev_dbg(&dc->ndev->dev, " BL_TF:\n");
for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
val = SD_BL_TF_POINT_0(settings->bltf[i][0]) |
SD_BL_TF_POINT_1(settings->bltf[i][1]) |
@@ -60,7 +139,7 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) {
SD_BL_TF_POINT_3(settings->bltf[i][3]);
tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i));
- dev_dbg(&dc->ndev->dev, "BL_TF(%d): 0x%08x\n", i, val);
+ dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val);
}
/* Write Coeff */
@@ -68,27 +147,29 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) {
SD_CSC_COEFF_G(settings->coeff.g) |
SD_CSC_COEFF_B(settings->coeff.b);
tegra_dc_writel(dc, val, DC_DISP_SD_CSC_COEFF);
- dev_dbg(&dc->ndev->dev, "COEFF: 0x%08x\n", val);
+ dev_dbg(&dc->ndev->dev, " COEFF: 0x%08x\n", val);
/* Write BL Params */
val = SD_BLP_TIME_CONSTANT(settings->blp.time_constant) |
SD_BLP_STEP(settings->blp.step);
tegra_dc_writel(dc, val, DC_DISP_SD_BL_PARAMETERS);
- dev_dbg(&dc->ndev->dev, "BLP: 0x%08x\n", val);
+ dev_dbg(&dc->ndev->dev, " BLP: 0x%08x\n", val);
/* Write Auto/Manual PWM */
val = (settings->use_auto_pwm) ? SD_BLC_MODE_AUTO : SD_BLC_MODE_MAN;
tegra_dc_writel(dc, val, DC_DISP_SD_BL_CONTROL);
- dev_dbg(&dc->ndev->dev, "BL_CONTROL: 0x%08x\n", val);
+ dev_dbg(&dc->ndev->dev, " BL_CONTROL: 0x%08x\n", val);
/* Write Flicker Control */
val = SD_FC_TIME_LIMIT(settings->fc.time_limit) |
SD_FC_THRESHOLD(settings->fc.threshold);
tegra_dc_writel(dc, val, DC_DISP_SD_FLICKER_CONTROL);
- dev_dbg(&dc->ndev->dev, "FLICKER_CONTROL: 0x%08x\n", val);
+ dev_dbg(&dc->ndev->dev, " FLICKER_CONTROL: 0x%08x\n", val);
/* Manage SD Control */
val = 0;
+ /* Stay in manual correction mode until the next flip. */
+ val |= SD_CORRECTION_MODE_MAN;
/* Enable / One-Shot */
val |= (settings->enable == 2) ?
(SD_ENABLE_ONESHOT | SD_ONESHOT_ENABLE) :
@@ -122,26 +203,29 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) {
case 8: val |= SD_BIN_WIDTH_EIGHT; break;
}
- /* TODO: histogram reset WAR? */
-
/* Finally, Write SD Control */
tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
- dev_dbg(&dc->ndev->dev, "SD_CONTROL: 0x%08x\n", val);
+ dev_dbg(&dc->ndev->dev, " SD_CONTROL: 0x%08x\n", val);
/* set the brightness pointer */
sd_brightness = settings->sd_brightness;
- dev_dbg(&dc->ndev->dev, "sd_brightness: 0x%08x\n", (u32)sd_brightness);
- dev_dbg(&dc->ndev->dev, "*******************\n");
- dev_dbg(&dc->ndev->dev, "===================\n");
+ /* note that we're in manual K until the next flip */
+ atomic_set(&man_k_until_blank, 1);
}
+/* Periodic update */
bool nvsd_update_brightness(struct tegra_dc *dc) {
u32 val = 0;
int cur_sd_brightness;
if (sd_brightness) {
- /* TODO: histogram reset WAR? */
+ if (atomic_read(&man_k_until_blank)) {
+ val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL);
+ val &= ~SD_CORRECTION_MODE_MAN;
+ tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
+ atomic_set(&man_k_until_blank, 0);
+ }
cur_sd_brightness = atomic_read(sd_brightness);
@@ -155,9 +239,290 @@ bool nvsd_update_brightness(struct tegra_dc *dc) {
atomic_set(sd_brightness, (int)val);
return true;
}
- /* TODO: log? */
}
/* No update needed. */
return false;
}
+
+/* Sysfs accessors */
+static ssize_t nvsd_settings_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct device *dev = container_of((kobj->parent), struct device, kobj);
+ struct nvhost_device *ndev = to_nvhost_device(dev);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+ ssize_t res = 0;
+
+ if(sd_settings) {
+ if(IS_NVSD_ATTR(enable)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->enable);
+ }
+ else if(IS_NVSD_ATTR(aggressiveness)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->aggressiveness);
+ }
+ else if(IS_NVSD_ATTR(bin_width)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->bin_width);
+ }
+ else if(IS_NVSD_ATTR(hw_update_delay)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->hw_update_delay);
+ }
+ else if(IS_NVSD_ATTR(use_vid_luma)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->use_vid_luma);
+ }
+ else if(IS_NVSD_ATTR(coeff)) {
+ res = snprintf(buf, PAGE_SIZE, "R: %d / G: %d / B: %d\n",
+ sd_settings->coeff.r,
+ sd_settings->coeff.g,
+ sd_settings->coeff.b);
+ }
+ else if(IS_NVSD_ATTR(blp_time_constant)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->blp.time_constant);
+ }
+ else if(IS_NVSD_ATTR(blp_step)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->blp.step);
+ }
+ else if(IS_NVSD_ATTR(fc_time_limit)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->fc.time_limit);
+ }
+ else if(IS_NVSD_ATTR(fc_threshold)) {
+ res = snprintf(buf, PAGE_SIZE, "%d\n",
+ sd_settings->fc.threshold);
+ }
+ else if(IS_NVSD_ATTR(lut)) {
+ u32 i = 0;
+ for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) {
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ "%d: R: %3d / G: %3d / B: %3d\n",
+ i,
+ sd_settings->lut[i].r,
+ sd_settings->lut[i].g,
+ sd_settings->lut[i].b);
+ }
+ }
+ else if(IS_NVSD_ATTR(bltf)) {
+ u32 i = 0;
+ for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ "%d: 0: %3d / 1: %3d / 2: %3d / 3: %3d\n",
+ i,
+ sd_settings->bltf[i][0],
+ sd_settings->bltf[i][1],
+ sd_settings->bltf[i][2],
+ sd_settings->bltf[i][3]);
+ }
+ }
+ else {
+ res = -EINVAL;
+ }
+ }
+ else {
+ /* This shouldn't be reachable. But just in case... */
+ res = -EINVAL;
+ }
+
+ return res;
+}
+
+#define NVSD_CHECK_AND_UPDATE(_min, _max, _varname) { \
+ int val = simple_strtol(buf, NULL, 10); \
+ if (val >= _min && val <= _max) { \
+ sd_settings->_varname = val; \
+ settings_updated = true; \
+ } }
+#define NVSD_GET_MULTI(_ele, _num, _act, _min, _max) { \
+ char *b, *c, *orig_b; \
+ b = orig_b = kstrdup(buf, GFP_KERNEL); \
+ for (_act = 0; _act < _num; _act++) { \
+ if (!b) \
+ break; \
+ b = strim(b); \
+ c = strsep(&b, " "); \
+ if (!strlen(c)) \
+ break; \
+ _ele[_act] = simple_strtol(c, NULL, 10); \
+ if (_ele[_act] < _min || _ele[_act] > _max) \
+ break; \
+ } \
+ if (orig_b) \
+ kfree(orig_b); \
+}
+static ssize_t nvsd_settings_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 nvhost_device *ndev = to_nvhost_device(dev);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
+ ssize_t res = count;
+ bool settings_updated = false;
+
+ if(sd_settings) {
+ if(IS_NVSD_ATTR(enable)) {
+ NVSD_CHECK_AND_UPDATE(0, 1, enable);
+ }
+ else if(IS_NVSD_ATTR(aggressiveness)) {
+ NVSD_CHECK_AND_UPDATE(1, 5, aggressiveness);
+ }
+ else if(IS_NVSD_ATTR(bin_width)) {
+ NVSD_CHECK_AND_UPDATE(0, 8, bin_width);
+ }
+ else if(IS_NVSD_ATTR(hw_update_delay)) {
+ NVSD_CHECK_AND_UPDATE(0, 2, hw_update_delay);
+ }
+ else if(IS_NVSD_ATTR(use_vid_luma)) {
+ NVSD_CHECK_AND_UPDATE(0, 1, use_vid_luma);
+ }
+ else if(IS_NVSD_ATTR(coeff)) {
+ int ele[3], i = 0, num = 3;
+ NVSD_GET_MULTI(ele, num, i, 0, 15);
+ if (i == num) {
+ sd_settings->coeff.r = ele[0];
+ sd_settings->coeff.g = ele[1];
+ sd_settings->coeff.b = ele[2];
+ settings_updated = true;
+ }
+ else {
+ res = -EINVAL;
+ }
+ }
+ else if(IS_NVSD_ATTR(blp_time_constant)) {
+ NVSD_CHECK_AND_UPDATE(0, 1024, blp.time_constant);
+ }
+ else if(IS_NVSD_ATTR(blp_step)) {
+ NVSD_CHECK_AND_UPDATE(0, 255, blp.step);
+ }
+ else if(IS_NVSD_ATTR(fc_time_limit)) {
+ NVSD_CHECK_AND_UPDATE(0, 255, fc.time_limit);
+ }
+ else if(IS_NVSD_ATTR(fc_threshold)) {
+ NVSD_CHECK_AND_UPDATE(0, 255, fc.threshold);
+ }
+ else if(IS_NVSD_ATTR(lut)) {
+ int ele[3 * DC_DISP_SD_LUT_NUM];
+ int i = 0, num = 3 * DC_DISP_SD_LUT_NUM;
+ NVSD_GET_MULTI(ele, num, i, 0, 255);
+ if (i == num) {
+ for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) {
+ sd_settings->lut[i].r = ele[i * 3 + 0];
+ sd_settings->lut[i].g = ele[i * 3 + 1];
+ sd_settings->lut[i].b = ele[i * 3 + 2];
+ }
+ settings_updated = true;
+ }
+ else {
+ res = -EINVAL;
+ }
+ }
+ else if(IS_NVSD_ATTR(bltf)) {
+ int ele[4 * DC_DISP_SD_BL_TF_NUM];
+ int i = 0, num = 4 * DC_DISP_SD_BL_TF_NUM;
+ NVSD_GET_MULTI(ele, num, i, 0, 255);
+ if (i == num) {
+ for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
+ sd_settings->bltf[i][0] = ele[i * 4 + 0];
+ sd_settings->bltf[i][1] = ele[i * 4 + 1];
+ sd_settings->bltf[i][2] = ele[i * 4 + 2];
+ sd_settings->bltf[i][3] = ele[i * 4 + 3];
+ }
+ settings_updated = true;
+ }
+ else {
+ res = -EINVAL;
+ }
+ }
+ else {
+ res = -EINVAL;
+ }
+
+ /* Re-init if our settings were updated. */
+ if (settings_updated) {
+ nvsd_init(dc, sd_settings);
+ /* Update backlight state IFF we're disabling! */
+ if (!sd_settings->enable && sd_settings->bl_device) {
+ /* Do the actual brightness update outside of the mutex */
+ struct platform_device *pdev = sd_settings->bl_device;
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ if (bl)
+ backlight_update_status(bl);
+ }
+ }
+ }
+ else {
+ /* This shouldn't be reachable. But just in case... */
+ res = -EINVAL;
+ }
+
+ return res;
+}
+
+#define NVSD_PRINT_REG(__name) { \
+ u32 val = tegra_dc_readl(dc, __name); \
+ res += snprintf(buf + res, PAGE_SIZE - res, #__name ": 0x%08x\n", val); \
+}
+#define NVSD_PRINT_REG_ARRAY(__name) { \
+ u32 val = 0, i = 0; \
+ res += snprintf(buf + res, PAGE_SIZE - res, #__name ":\n"); \
+ for (i = 0; i < __name##_NUM; i++) { \
+ val = tegra_dc_readl(dc, __name(i)); \
+ res += snprintf(buf + res, PAGE_SIZE - res, " %d: 0x%08x\n", i, val); \
+ } \
+}
+static ssize_t nvsd_registers_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct device *dev = container_of((kobj->parent), struct device, kobj);
+ struct nvhost_device *ndev = to_nvhost_device(dev);
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ ssize_t res = 0;
+
+ NVSD_PRINT_REG(DC_DISP_SD_CONTROL);
+ NVSD_PRINT_REG(DC_DISP_SD_CSC_COEFF);
+ NVSD_PRINT_REG_ARRAY(DC_DISP_SD_LUT);
+ NVSD_PRINT_REG(DC_DISP_SD_FLICKER_CONTROL);
+ NVSD_PRINT_REG(DC_DISP_SD_PIXEL_COUNT);
+ NVSD_PRINT_REG_ARRAY(DC_DISP_SD_HISTOGRAM);
+ NVSD_PRINT_REG(DC_DISP_SD_BL_PARAMETERS);
+ NVSD_PRINT_REG_ARRAY(DC_DISP_SD_BL_TF);
+ NVSD_PRINT_REG(DC_DISP_SD_BL_CONTROL);
+ NVSD_PRINT_REG(DC_DISP_SD_HW_K_VALUES);
+ NVSD_PRINT_REG(DC_DISP_SD_MAN_K_VALUES);
+
+ return res;
+}
+
+/* Sysfs initializer */
+int nvsd_create_sysfs(struct device *dev)
+{
+ int retval = 0;
+
+ nvsd_kobj = kobject_create_and_add("smartdimmer", &dev->kobj);
+ if (!nvsd_kobj)
+ return -ENOMEM;
+
+ retval = sysfs_create_group(nvsd_kobj, &nvsd_attr_group);
+ if (retval) {
+ kobject_put(nvsd_kobj);
+ dev_err(dev, "%s: failed to create attributes\n", __func__);
+ }
+
+ return retval;
+}
+
+/* Sysfs destructor */
+void __devexit nvsd_remove_sysfs(struct device *dev)
+{
+ if (nvsd_kobj) {
+ sysfs_remove_group(nvsd_kobj, &nvsd_attr_group);
+ kobject_put(nvsd_kobj);
+ }
+}
diff --git a/drivers/video/tegra/dc/nvsd.h b/drivers/video/tegra/dc/nvsd.h
index 6d082265d781..f7fc4a1ead6e 100644
--- a/drivers/video/tegra/dc/nvsd.h
+++ b/drivers/video/tegra/dc/nvsd.h
@@ -19,5 +19,7 @@
void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings);
bool nvsd_update_brightness(struct tegra_dc *dc);
+int nvsd_create_sysfs(struct device *dev);
+void __devexit nvsd_remove_sysfs(struct device *dev);
#endif