[media] omap3isp: preview: Shorten shadow update delay
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 26 Mar 2012 13:24:50 +0000 (10:24 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 8 May 2012 17:08:53 +0000 (14:08 -0300)
When applications modify preview engine parameters, the new values are
applied to the hardware by the preview engine interrupt handler during
vertical blanking. If the parameters are being changed when the
interrupt handler is called, it just delays applying the parameters
until the next frame.

If an application modifies the parameters for every frame, and the
preview engine interrupt is triggerred synchronously, the parameters are
never applied to the hardware.

Fix this by storing new parameters in a shadow copy, and switch the
active parameters with the shadow values atomically.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/omap3isp/isppreview.c
drivers/media/video/omap3isp/isppreview.h

index fd1807d36d40e38776895b6c15bdedabd98cf444..5bdf9e742c8e91ba5999c613e17fd431e599a035 100644 (file)
@@ -649,12 +649,18 @@ preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc)
 static void
 preview_update_contrast(struct isp_prev_device *prev, u8 contrast)
 {
-       struct prev_params *params = &prev->params;
+       struct prev_params *params;
+       unsigned long flags;
+
+       spin_lock_irqsave(&prev->params.lock, flags);
+       params = (prev->params.active & OMAP3ISP_PREV_CONTRAST)
+              ? &prev->params.params[0] : &prev->params.params[1];
 
        if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) {
                params->contrast = contrast * ISPPRV_CONTRAST_UNITS;
-               prev->update |= OMAP3ISP_PREV_CONTRAST;
+               params->update |= OMAP3ISP_PREV_CONTRAST;
        }
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 }
 
 /*
@@ -681,12 +687,18 @@ preview_config_contrast(struct isp_prev_device *prev, const void *params)
 static void
 preview_update_brightness(struct isp_prev_device *prev, u8 brightness)
 {
-       struct prev_params *params = &prev->params;
+       struct prev_params *params;
+       unsigned long flags;
+
+       spin_lock_irqsave(&prev->params.lock, flags);
+       params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS)
+              ? &prev->params.params[0] : &prev->params.params[1];
 
        if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) {
                params->brightness = brightness * ISPPRV_BRIGHT_UNITS;
-               prev->update |= OMAP3ISP_PREV_BRIGHTNESS;
+               params->update |= OMAP3ISP_PREV_BRIGHTNESS;
        }
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 }
 
 /*
@@ -721,6 +733,75 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit)
                       OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC);
 }
 
+static u32
+preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+       u32 active = prev->params.active;
+
+       if (shadow) {
+               /* Mark all shadow parameters we are going to touch as busy. */
+               prev->params.params[0].busy |= ~active & update;
+               prev->params.params[1].busy |= active & update;
+       } else {
+               /* Mark all active parameters we are going to touch as busy. */
+               update = (prev->params.params[0].update & active)
+                      | (prev->params.params[1].update & ~active);
+
+               prev->params.params[0].busy |= active & update;
+               prev->params.params[1].busy |= ~active & update;
+       }
+
+       return update;
+}
+
+static void
+preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+       u32 active = prev->params.active;
+
+       if (shadow) {
+               /* Set the update flag for shadow parameters that have been
+                * updated and clear the busy flag for all shadow parameters.
+                */
+               prev->params.params[0].update |= (~active & update);
+               prev->params.params[1].update |= (active & update);
+               prev->params.params[0].busy &= active;
+               prev->params.params[1].busy &= ~active;
+       } else {
+               /* Clear the update flag for active parameters that have been
+                * applied and the busy flag for all active parameters.
+                */
+               prev->params.params[0].update &= ~(active & update);
+               prev->params.params[1].update &= ~(~active & update);
+               prev->params.params[0].busy &= ~active;
+               prev->params.params[1].busy &= active;
+       }
+}
+
+static void preview_params_switch(struct isp_prev_device *prev)
+{
+       u32 to_switch;
+
+       /* Switch active parameters with updated shadow parameters when the
+        * shadow parameter has been updated and neither the active not the
+        * shadow parameter is busy.
+        */
+       to_switch = (prev->params.params[0].update & ~prev->params.active)
+                 | (prev->params.params[1].update & prev->params.active);
+       to_switch &= ~(prev->params.params[0].busy |
+                      prev->params.params[1].busy);
+       if (to_switch == 0)
+               return;
+
+       prev->params.active ^= to_switch;
+
+       /* Remove the update flag for the shadow copy of parameters we have
+        * switched.
+        */
+       prev->params.params[0].update &= ~(~prev->params.active & to_switch);
+       prev->params.params[1].update &= ~(prev->params.active & to_switch);
+}
+
 /* preview parameters update structure */
 struct preview_update {
        void (*config)(struct isp_prev_device *, const void *);
@@ -833,37 +914,6 @@ static const struct preview_update update_attrs[] = {
        },
 };
 
-/*
- * __preview_get_ptrs - helper function which return pointers to members
- *                         of params and config structures.
- * @params - pointer to preview_params structure.
- * @param - return pointer to appropriate structure field.
- * @configs - pointer to update config structure.
- * @config - return pointer to appropriate structure field.
- * @bit - for which feature to return pointers.
- * Return size of corresponding prev_params member
- */
-static u32
-__preview_get_ptrs(struct prev_params *params, void **param,
-                  struct omap3isp_prev_update_config *configs,
-                  void __user **config, unsigned int index)
-{
-       const struct preview_update *info = &update_attrs[index];
-
-       if (index >= ARRAY_SIZE(update_attrs)) {
-               if (config)
-                       *config = NULL;
-               *param = NULL;
-               return 0;
-       }
-
-       if (configs && config)
-               *config = *(void **)((void *)configs + info->config_offset);
-
-       *param = (void *)params + info->param_offset;
-       return info->param_size;
-}
-
 /*
  * preview_config - Copy and update local structure with userspace preview
  *                  configuration.
@@ -876,37 +926,41 @@ __preview_get_ptrs(struct prev_params *params, void **param,
 static int preview_config(struct isp_prev_device *prev,
                          struct omap3isp_prev_update_config *cfg)
 {
-       const struct preview_update *attr;
-       struct prev_params *params;
-       int i, bit, rval = 0;
+       unsigned long flags;
+       unsigned int i;
+       int rval = 0;
+       u32 update;
+       u32 active;
 
        if (cfg->update == 0)
                return 0;
 
-       params = &prev->params;
+       /* Mark the shadow parameters we're going to update as busy. */
+       spin_lock_irqsave(&prev->params.lock, flags);
+       preview_params_lock(prev, cfg->update, true);
+       active = prev->params.active;
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 
-       if (prev->state != ISP_PIPELINE_STREAM_STOPPED) {
-               unsigned long flags;
-
-               spin_lock_irqsave(&prev->lock, flags);
-               prev->shadow_update = 1;
-               spin_unlock_irqrestore(&prev->lock, flags);
-       }
+       update = 0;
 
        for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
-               attr = &update_attrs[i];
-               bit = 1 << i;
+               const struct preview_update *attr = &update_attrs[i];
+               struct prev_params *params;
+               unsigned int bit = 1 << i;
 
                if (attr->skip || !(cfg->update & bit))
                        continue;
 
+               params = &prev->params.params[!!(active & bit)];
+
                if (cfg->flag & bit) {
-                       void *to = NULL, __user *from = NULL;
-                       unsigned long sz = 0;
+                       void __user *from = *(void * __user *)
+                               ((void *)cfg + attr->config_offset);
+                       void *to = (void *)params + attr->param_offset;
+                       size_t size = attr->param_size;
 
-                       sz = __preview_get_ptrs(params, &to, cfg, &from, i);
-                       if (to && from && sz) {
-                               if (copy_from_user(to, from, sz)) {
+                       if (to && from && size) {
+                               if (copy_from_user(to, from, size)) {
                                        rval = -EFAULT;
                                        break;
                                }
@@ -916,50 +970,59 @@ static int preview_config(struct isp_prev_device *prev,
                        params->features &= ~bit;
                }
 
-               prev->update |= bit;
+               update |= bit;
        }
 
-       prev->shadow_update = 0;
+       spin_lock_irqsave(&prev->params.lock, flags);
+       preview_params_unlock(prev, update, true);
+       preview_params_switch(prev);
+       spin_unlock_irqrestore(&prev->params.lock, flags);
+
        return rval;
 }
 
 /*
  * preview_setup_hw - Setup preview registers and/or internal memory
  * @prev: pointer to preview private structure
+ * @update: Bitmask of parameters to setup
+ * @active: Bitmask of parameters active in set 0
  * Note: can be called from interrupt context
  * Return none
  */
-static void preview_setup_hw(struct isp_prev_device *prev)
+static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
+                            u32 active)
 {
-       struct prev_params *params = &prev->params;
-       const struct preview_update *attr;
-       unsigned int bit;
        unsigned int i;
-       void *param_ptr;
+       u32 features;
 
-       if (prev->update == 0)
+       if (update == 0)
                return;
 
+       features = (prev->params.params[0].features & active)
+                | (prev->params.params[1].features & ~active);
+
        for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
-               attr = &update_attrs[i];
-               bit = 1 << i;
+               const struct preview_update *attr = &update_attrs[i];
+               struct prev_params *params;
+               unsigned int bit = 1 << i;
+               void *param_ptr;
 
-               if (!(prev->update & bit))
+               if (!(update & bit))
                        continue;
 
+               params = &prev->params.params[!(active & bit)];
+
                if (params->features & bit) {
                        if (attr->config) {
-                               __preview_get_ptrs(params, &param_ptr, NULL,
-                                                  NULL, i);
+                               param_ptr = (void *)params + attr->param_offset;
                                attr->config(prev, param_ptr);
                        }
                        if (attr->enable)
                                attr->enable(prev, 1);
-               } else
+               } else {
                        if (attr->enable)
                                attr->enable(prev, 0);
-
-               prev->update &= ~bit;
+               }
        }
 }
 
@@ -997,13 +1060,17 @@ preview_config_ycpos(struct isp_prev_device *prev,
 static void preview_config_averager(struct isp_prev_device *prev, u8 average)
 {
        struct isp_device *isp = to_isp_device(prev);
+       struct prev_params *params;
        int reg = 0;
 
-       if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER)
+       params = (prev->params.active & OMAP3ISP_PREV_CFA)
+              ? &prev->params.params[0] : &prev->params.params[1];
+
+       if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER)
                reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT |
                      ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT |
                      average;
-       else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
+       else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
                reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT |
                      ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT |
                      average;
@@ -1021,33 +1088,35 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
  *
  * See the explanation at the PREV_MARGIN_* definitions for more details.
  */
-static void preview_config_input_size(struct isp_prev_device *prev)
+static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
 {
        struct isp_device *isp = to_isp_device(prev);
-       struct prev_params *params = &prev->params;
        unsigned int sph = prev->crop.left;
        unsigned int eph = prev->crop.left + prev->crop.width - 1;
        unsigned int slv = prev->crop.top;
        unsigned int elv = prev->crop.top + prev->crop.height - 1;
+       u32 features;
 
-       if (params->features & OMAP3ISP_PREV_CFA) {
+       features = (prev->params.params[0].features & active)
+                | (prev->params.params[1].features & ~active);
+
+       if (features & OMAP3ISP_PREV_CFA) {
                sph -= 2;
                eph += 2;
                slv -= 2;
                elv += 2;
        }
-       if (params->features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) {
+       if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) {
                sph -= 2;
                eph += 2;
                slv -= 2;
                elv += 2;
        }
-       if (params->features & OMAP3ISP_PREV_HRZ_MED) {
+       if (features & OMAP3ISP_PREV_HRZ_MED) {
                sph -= 2;
                eph += 2;
        }
-       if (params->features & (OMAP3ISP_PREV_CHROMA_SUPP |
-                               OMAP3ISP_PREV_LUMAENH))
+       if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH))
                sph -= 2;
 
        isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
@@ -1182,8 +1251,16 @@ int omap3isp_preview_busy(struct isp_prev_device *prev)
  */
 void omap3isp_preview_restore_context(struct isp_device *isp)
 {
-       isp->isp_prev.update = OMAP3ISP_PREV_FEATURES_END - 1;
-       preview_setup_hw(&isp->isp_prev);
+       struct isp_prev_device *prev = &isp->isp_prev;
+       const u32 update = OMAP3ISP_PREV_FEATURES_END - 1;
+
+       prev->params.params[0].update = prev->params.active & update;
+       prev->params.params[1].update = ~prev->params.active & update;
+
+       preview_setup_hw(prev, update, prev->params.active);
+
+       prev->params.params[0].update = 0;
+       prev->params.params[1].update = 0;
 }
 
 /*
@@ -1242,12 +1319,21 @@ static void preview_print_status(struct isp_prev_device *prev)
 /*
  * preview_init_params - init image processing parameters.
  * @prev: pointer to previewer private structure
- * return none
  */
 static void preview_init_params(struct isp_prev_device *prev)
 {
-       struct prev_params *params = &prev->params;
-       int i = 0;
+       struct prev_params *params;
+       unsigned int i;
+
+       spin_lock_init(&prev->params.lock);
+
+       prev->params.active = ~0;
+       prev->params.params[0].busy = 0;
+       prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1;
+       prev->params.params[1].busy = 0;
+       prev->params.params[1].update = 0;
+
+       params = &prev->params.params[0];
 
        /* Init values */
        params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
@@ -1291,8 +1377,6 @@ static void preview_init_params(struct isp_prev_device *prev)
                         | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV
                         | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS
                         | OMAP3ISP_PREV_CONTRAST;
-
-       prev->update = OMAP3ISP_PREV_FEATURES_END - 1;
 }
 
 /*
@@ -1321,8 +1405,17 @@ static void preview_configure(struct isp_prev_device *prev)
 {
        struct isp_device *isp = to_isp_device(prev);
        struct v4l2_mbus_framefmt *format;
+       unsigned long flags;
+       u32 update;
+       u32 active;
 
-       preview_setup_hw(prev);
+       spin_lock_irqsave(&prev->params.lock, flags);
+       /* Mark all active parameters we are going to touch as busy. */
+       update = preview_params_lock(prev, 0, false);
+       active = prev->params.active;
+       spin_unlock_irqrestore(&prev->params.lock, flags);
+
+       preview_setup_hw(prev, update, active);
 
        if (prev->output & PREVIEW_OUTPUT_MEMORY)
                isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
@@ -1343,7 +1436,7 @@ static void preview_configure(struct isp_prev_device *prev)
 
        preview_adjust_bandwidth(prev);
 
-       preview_config_input_size(prev);
+       preview_config_input_size(prev, active);
 
        if (prev->input == PREVIEW_INPUT_CCDC)
                preview_config_inlineoffset(prev, 0);
@@ -1360,6 +1453,10 @@ static void preview_configure(struct isp_prev_device *prev)
 
        preview_config_averager(prev, 0);
        preview_config_ycpos(prev, format->code);
+
+       spin_lock_irqsave(&prev->params.lock, flags);
+       preview_params_unlock(prev, update, false);
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 }
 
 /* -----------------------------------------------------------------------------
@@ -1448,25 +1545,30 @@ static void preview_isr_buffer(struct isp_prev_device *prev)
 void omap3isp_preview_isr(struct isp_prev_device *prev)
 {
        unsigned long flags;
+       u32 update;
+       u32 active;
 
        if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping))
                return;
 
-       spin_lock_irqsave(&prev->lock, flags);
-       if (prev->shadow_update)
-               goto done;
+       spin_lock_irqsave(&prev->params.lock, flags);
+       preview_params_switch(prev);
+       update = preview_params_lock(prev, 0, false);
+       active = prev->params.active;
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 
-       preview_setup_hw(prev);
-       preview_config_input_size(prev);
-
-done:
-       spin_unlock_irqrestore(&prev->lock, flags);
+       preview_setup_hw(prev, update, active);
+       preview_config_input_size(prev, active);
 
        if (prev->input == PREVIEW_INPUT_MEMORY ||
            prev->output & PREVIEW_OUTPUT_MEMORY)
                preview_isr_buffer(prev);
        else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS)
                preview_enable_oneshot(prev);
+
+       spin_lock_irqsave(&prev->params.lock, flags);
+       preview_params_unlock(prev, update, false);
+       spin_unlock_irqrestore(&prev->params.lock, flags);
 }
 
 /* -----------------------------------------------------------------------------
@@ -1552,7 +1654,6 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable)
        struct isp_video *video_out = &prev->video_out;
        struct isp_device *isp = to_isp_device(prev);
        struct device *dev = to_device(prev);
-       unsigned long flags;
 
        if (prev->state == ISP_PIPELINE_STREAM_STOPPED) {
                if (enable == ISP_PIPELINE_STREAM_STOPPED)
@@ -1589,11 +1690,9 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable)
                if (omap3isp_module_sync_idle(&sd->entity, &prev->wait,
                                              &prev->stopping))
                        dev_dbg(dev, "%s: stop timeout.\n", sd->name);
-               spin_lock_irqsave(&prev->lock, flags);
                omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
                omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
                omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
-               spin_unlock_irqrestore(&prev->lock, flags);
                isp_video_dmaqueue_flags_clr(video_out);
                break;
        }
@@ -2201,7 +2300,7 @@ error_video_in:
 }
 
 /*
- * isp_preview_init - Previewer initialization.
+ * omap3isp_preview_init - Previewer initialization.
  * @dev : Pointer to ISP device
  * return -ENOMEM or zero on success
  */
@@ -2209,8 +2308,8 @@ int omap3isp_preview_init(struct isp_device *isp)
 {
        struct isp_prev_device *prev = &isp->isp_prev;
 
-       spin_lock_init(&prev->lock);
        init_waitqueue_head(&prev->wait);
+
        preview_init_params(prev);
 
        return preview_init_entities(prev);
index 6ee830623d5da1c1ab3b0a5914bd8726a7447679..6663ab64e4b1bbca413e96c77a9868ff7356a663 100644 (file)
@@ -69,6 +69,8 @@ enum preview_ycpos_mode {
 
 /*
  * struct prev_params - Structure for all configuration
+ * @busy: Bitmask of busy parameters (being updated or used)
+ * @update: Bitmask of the parameters to be updated
  * @features: Set of features enabled.
  * @cfa: CFA coefficients.
  * @csup: Chroma suppression coefficients.
@@ -86,6 +88,8 @@ enum preview_ycpos_mode {
  * @brightness: Brightness.
  */
 struct prev_params {
+       u32 busy;
+       u32 update;
        u32 features;
        struct omap3isp_prev_cfa cfa;
        struct omap3isp_prev_csup csup;
@@ -118,12 +122,11 @@ struct prev_params {
  * @output: Bitmask of the active output
  * @video_in: Input video entity
  * @video_out: Output video entity
- * @params: Module configuration data
- * @shadow_update: If set, update the hardware configured in the next interrupt
+ * @params.params : Active and shadow parameters sets
+ * @params.active: Bitmask of parameters active in set 0
+ * @params.lock: Parameters lock, protects params.active and params.shadow
  * @underrun: Whether the preview entity has queued buffers on the output
  * @state: Current preview pipeline state
- * @lock: Shadow update lock
- * @update: Bitmask of the parameters to be updated
  *
  * This structure is used to store the OMAP ISP Preview module Information.
  */
@@ -140,13 +143,15 @@ struct isp_prev_device {
        struct isp_video video_in;
        struct isp_video video_out;
 
-       struct prev_params params;
-       unsigned int shadow_update:1;
+       struct {
+               struct prev_params params[2];
+               u32 active;
+               spinlock_t lock;
+       } params;
+
        enum isp_pipeline_stream_state state;
        wait_queue_head_t wait;
        atomic_t stopping;
-       spinlock_t lock;
-       u32 update;
 };
 
 struct isp_device;