diff options
-rw-r--r-- | drivers/video/tegra/dc/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 119 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 7 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_sysfs.c | 121 | ||||
-rwxr-xr-x | drivers/video/tegra/dc/dsi.c | 2 | ||||
-rw-r--r-- | drivers/video/tegra/dc/nvsd.c | 397 | ||||
-rw-r--r-- | drivers/video/tegra/dc/nvsd.h | 2 |
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 |