summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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