]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: dc: Add TEGRA_DC_EXT_SET_CMU_ALIGNED
authorDaniel Solomon <daniels@nvidia.com>
Mon, 28 Jul 2014 21:14:01 +0000 (14:14 -0700)
committerJon Mayo <jmayo@nvidia.com>
Sat, 8 Nov 2014 00:09:51 +0000 (16:09 -0800)
This ioctl is intended to be used for the purpose
of changing the panel gamma and saturation  while the
panel is on, without the visual artefacts that would
result from disabling CMU during the update, and while
minimizing risk of CPU/DC CMU access collisions.

The ioctl TEGRA_DC_EXT_SET_CMU_ALIGNED updates only
the entries in CMU CSC and LUT2 that have changed since
the last CMU update, keeps CMU enabled while doing
so, and aligns the update with the next FRAME_END_INT.

Bug 1535044

Change-Id: Ibe758f94eccfff72c85e299107b3053378127805
Signed-off-by: Daniel Solomon <daniels@nvidia.com>
Reviewed-on: http://git-master/r/556485
(cherry picked from commit 8c8cfd08c0a4c7473bf13f7e1b3cc672b5dbb1fc)
Reviewed-on: http://git-master/r/594597
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Jon Mayo <jmayo@nvidia.com>
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/ext/dev.c
include/video/tegra_dc_ext.h

index be8f73a92f8092701918b1f65cea811c8e3b4cb0..8dd9a710f64039b913ddffbac10091c3048d306d 100644 (file)
@@ -1640,6 +1640,37 @@ int tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 }
 EXPORT_SYMBOL(tegra_dc_update_cmu);
 
+static int _tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable)
+{
+       tegra_dc_io_start(dc);
+       if (enable) {
+               atomic_inc(&dc->frame_end_ref);
+               tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
+       } else if (!atomic_dec_return(&dc->frame_end_ref))
+               tegra_dc_mask_interrupt(dc, FRAME_END_INT);
+       tegra_dc_io_end(dc);
+
+       return 0;
+}
+
+int tegra_dc_update_cmu_aligned(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
+{
+       mutex_lock(&dc->lock);
+       if (!dc->enabled) {
+               mutex_unlock(&dc->lock);
+               return 0;
+       }
+
+       memcpy(&dc->cmu_shadow, cmu, sizeof(dc->cmu));
+       dc->cmu_shadow_dirty = true;
+       _tegra_dc_config_frame_end_intr(dc, true);
+
+       mutex_unlock(&dc->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_dc_update_cmu_aligned);
+
 static struct tegra_dc_cmu *tegra_dc_get_cmu(struct tegra_dc *dc)
 {
        if (dc->pdata->cmu)
@@ -1659,6 +1690,7 @@ void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable)
 #define tegra_dc_cache_cmu(dc, src_cmu)
 #define tegra_dc_set_cmu(dc, cmu)
 #define tegra_dc_update_cmu(dc, cmu)
+#define tegra_dc_update_cmu_aligned(dc, cmu)
 #endif
 
 /* disable_irq() blocks until handler completes, calling this function while
@@ -2194,19 +2226,6 @@ out:
        return ret;
 }
 
-static int _tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable)
-{
-       tegra_dc_io_start(dc);
-       if (enable) {
-               atomic_inc(&dc->frame_end_ref);
-               tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
-       } else if (!atomic_dec_return(&dc->frame_end_ref))
-               tegra_dc_mask_interrupt(dc, FRAME_END_INT);
-       tegra_dc_io_end(dc);
-
-       return 0;
-}
-
 int _tegra_dc_wait_for_frame_end(struct tegra_dc *dc,
        u32 timeout_ms)
 {
@@ -2293,6 +2312,61 @@ static void tegra_dc_vblank(struct work_struct *work)
                tegra_dc_prism_update_backlight(dc);
 }
 
+#define CSC_UPDATE_IF_CHANGED(entry, ENTRY) do { \
+               if (cmu_active->csc.entry != cmu_shadow->csc.entry) { \
+                       cmu_active->csc.entry = cmu_shadow->csc.entry; \
+                       tegra_dc_writel(dc, \
+                               cmu_active->csc.entry, \
+                               DC_COM_CMU_CSC_##ENTRY); \
+               } \
+       } while (0)
+
+static void tegra_dc_frame_end(struct work_struct *work)
+{
+       struct tegra_dc *dc = container_of(work,
+               struct tegra_dc, frame_end_work);
+       u32 val;
+       u32 i;
+
+       mutex_lock(&dc->lock);
+
+       if (!dc->enabled) {
+               mutex_unlock(&dc->lock);
+               return;
+       }
+
+       tegra_dc_get(dc);
+
+       if (dc->cmu_shadow_dirty) {
+               struct tegra_dc_cmu *cmu_active = &dc->cmu;
+               struct tegra_dc_cmu *cmu_shadow = &dc->cmu_shadow;
+
+               CSC_UPDATE_IF_CHANGED(krr, KRR);
+               CSC_UPDATE_IF_CHANGED(kgr, KGR);
+               CSC_UPDATE_IF_CHANGED(kbr, KBR);
+               CSC_UPDATE_IF_CHANGED(krg, KRG);
+               CSC_UPDATE_IF_CHANGED(kgg, KGG);
+               CSC_UPDATE_IF_CHANGED(kbg, KBG);
+               CSC_UPDATE_IF_CHANGED(krb, KRB);
+               CSC_UPDATE_IF_CHANGED(kgb, KGB);
+               CSC_UPDATE_IF_CHANGED(kbb, KBB);
+
+               for (i = 0; i < 960; i++)
+                       if (cmu_active->lut2[i] != cmu_shadow->lut2[i]) {
+                               cmu_active->lut2[i] = cmu_shadow->lut2[i];
+                               val = LUT2_ADDR(i) |
+                                       LUT2_DATA(cmu_active->lut2[i]);
+                               tegra_dc_writel(dc, val, DC_COM_CMU_LUT2);
+                       }
+
+               dc->cmu_shadow_dirty = false;
+               _tegra_dc_config_frame_end_intr(dc, false);
+       }
+
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
+}
+
 static void tegra_dc_one_shot_worker(struct work_struct *work)
 {
        struct tegra_dc *dc = container_of(
@@ -2503,6 +2577,8 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status,
 
                if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE && !dc->nvsr)
                        tegra_dc_put(dc);
+
+               queue_work(system_freezable_wq, &dc->frame_end_work);
        }
 
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
@@ -2533,6 +2609,8 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status,
                        complete(&dc->crc_complete);
 
                tegra_dc_trigger_windows(dc);
+
+               queue_work(system_freezable_wq, &dc->frame_end_work);
        }
 
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
@@ -3731,6 +3809,7 @@ static int tegra_dc_probe(struct platform_device *ndev)
 #endif
        INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
        dc->vblank_ref_count = 0;
+       INIT_WORK(&dc->frame_end_work, tegra_dc_frame_end);
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
        INIT_WORK(&dc->vpulse2_work, tegra_dc_vpulse2);
 #endif
index fcf433461459c56df4eb69bacdbb802fa91d4597..26bcb7ca98b42a51fb457461f8875933b5c18939 100644 (file)
@@ -531,6 +531,7 @@ void tegra_dc_set_color_control(struct tegra_dc *dc);
 #ifdef CONFIG_TEGRA_DC_CMU
 void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable);
 int tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu);
+int tegra_dc_update_cmu_aligned(struct tegra_dc *dc, struct tegra_dc_cmu *cmu);
 #endif
 
 struct device_node *tegra_get_panel_node_out_type_check
index ea3b4c2568b9786f04e5001334a7c88baf2236cf..ba37daf03812ca25adcc1f40535b956375108e3c 100644 (file)
@@ -194,7 +194,9 @@ struct tegra_dc {
        int                             n_windows;
 #ifdef CONFIG_TEGRA_DC_CMU
        struct tegra_dc_cmu             cmu;
+       struct tegra_dc_cmu             cmu_shadow;
        bool                            cmu_dirty;
+       bool                            cmu_shadow_dirty;
        bool                            cmu_enabled;
 #endif
        wait_queue_head_t               wq;
@@ -227,6 +229,7 @@ struct tegra_dc {
 
        struct work_struct              vblank_work;
        long                            vblank_ref_count;
+       struct work_struct              frame_end_work;
        struct work_struct              vpulse2_work;
        long                            vpulse2_ref_count;
 
index 0e97757a9b33c0298ea7c9eabf5ec57a14c52a10..40cb802bdd3007033e5aa72eb1612aaeb09372f5 100644 (file)
@@ -1236,6 +1236,36 @@ static int tegra_dc_ext_set_cmu(struct tegra_dc_ext_user *user,
        kfree(cmu);
        return 0;
 }
+
+static int tegra_dc_ext_set_cmu_aligned(struct tegra_dc_ext_user *user,
+                               struct tegra_dc_ext_cmu *args)
+{
+       int i;
+       struct tegra_dc_cmu *cmu;
+       struct tegra_dc *dc = user->ext->dc;
+
+       cmu = kzalloc(sizeof(*cmu), GFP_KERNEL);
+       if (!cmu)
+               return -ENOMEM;
+
+       cmu->csc.krr = args->csc[0];
+       cmu->csc.kgr = args->csc[1];
+       cmu->csc.kbr = args->csc[2];
+       cmu->csc.krg = args->csc[3];
+       cmu->csc.kgg = args->csc[4];
+       cmu->csc.kbg = args->csc[5];
+       cmu->csc.krb = args->csc[6];
+       cmu->csc.kgb = args->csc[7];
+       cmu->csc.kbb = args->csc[8];
+
+       for (i = 0; i < 960; i++)
+               cmu->lut2[i] = args->lut2[i];
+
+       tegra_dc_update_cmu_aligned(dc, cmu);
+
+       kfree(cmu);
+       return 0;
+}
 #endif
 
 #ifdef CONFIG_TEGRA_ISOMGR
@@ -1721,6 +1751,33 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
                return tegra_dc_ext_set_vblank(user->ext, args.enable);
        }
 
+       /* Update only modified elements in CSC and LUT2.
+        * Align writes to FRAME_END_INT */
+       case TEGRA_DC_EXT_SET_CMU_ALIGNED:
+       {
+#ifdef CONFIG_TEGRA_DC_CMU
+               int ret;
+               struct tegra_dc_ext_cmu *args;
+
+               args = kzalloc(sizeof(*args), GFP_KERNEL);
+               if (!args)
+                       return -ENOMEM;
+
+               if (copy_from_user(args, user_arg, sizeof(*args))) {
+                       kfree(args);
+                       return -EFAULT;
+               }
+
+               ret = tegra_dc_ext_set_cmu_aligned(user, args);
+
+               kfree(args);
+
+               return ret;
+#else
+               return -EACCES;
+#endif
+       }
+
        default:
                return -EINVAL;
        }
index 96a8ca50408a4445a34faf535ddc8d3628f891de..6dd6ac839d459aa67d7472b065587e6a6608a14e 100644 (file)
@@ -454,6 +454,9 @@ struct tegra_dc_ext_feature {
 #define TEGRA_DC_EXT_SET_VBLANK \
        _IOW('D', 0x15, struct tegra_dc_ext_set_vblank)
 
+#define TEGRA_DC_EXT_SET_CMU_ALIGNED \
+       _IOW('D', 0x16, struct tegra_dc_ext_cmu)
+
 enum tegra_dc_ext_control_output_type {
        TEGRA_DC_EXT_DSI,
        TEGRA_DC_EXT_LVDS,