Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / media / platform / vsp1 / vsp1_video.c
index b4687a8..9750a61 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * vsp1_video.c  --  R-Car VSP1 Video Node
  *
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
 #include <media/videobuf2-dma-contig.h>
 
 #include "vsp1.h"
+#include "vsp1_bru.h"
 #include "vsp1_entity.h"
 #include "vsp1_rwpf.h"
+#include "vsp1_uds.h"
 #include "vsp1_video.h"
 
 #define VSP1_VIDEO_DEF_FORMAT          V4L2_PIX_FMT_YUYV
@@ -49,70 +51,85 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
        { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 8, 0, 0 }, false, false, 1, 1 },
-       { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+         1, { 8, 0, 0 }, false, false, 1, 1, false },
+       { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+         VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+         VI6_RPF_DSWAP_P_WDS,
+         1, { 16, 0, 0 }, false, false, 1, 1, true },
+       { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS,
-         1, { 16, 0, 0 }, false, false, 1, 1 },
-       { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+         1, { 16, 0, 0 }, false, false, 1, 1, true },
+       { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+         VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+         VI6_RPF_DSWAP_P_WDS,
+         1, { 16, 0, 0 }, false, false, 1, 1, true },
+       { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS,
-         1, { 16, 0, 0 }, false, false, 1, 1 },
+         1, { 16, 0, 0 }, false, false, 1, 1, false },
        { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS,
-         1, { 16, 0, 0 }, false, false, 1, 1 },
+         1, { 16, 0, 0 }, false, false, 1, 1, false },
        { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 24, 0, 0 }, false, false, 1, 1 },
+         1, { 24, 0, 0 }, false, false, 1, 1, false },
        { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 24, 0, 0 }, false, false, 1, 1 },
-       { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+         1, { 24, 0, 0 }, false, false, 1, 1, false },
+       { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+         VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+         1, { 32, 0, 0 }, false, false, 1, 1, true },
+       { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
-         1, { 32, 0, 0 }, false, false, 1, 1 },
-       { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+         1, { 32, 0, 0 }, false, false, 1, 1, false },
+       { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+         VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+         VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+         1, { 32, 0, 0 }, false, false, 1, 1, true },
+       { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
          VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 32, 0, 0 }, false, false, 1, 1 },
+         1, { 32, 0, 0 }, false, false, 1, 1, false },
        { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 16, 0, 0 }, false, false, 2, 1 },
+         1, { 16, 0, 0 }, false, false, 2, 1, false },
        { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 16, 0, 0 }, false, true, 2, 1 },
+         1, { 16, 0, 0 }, false, true, 2, 1, false },
        { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 16, 0, 0 }, true, false, 2, 1 },
+         1, { 16, 0, 0 }, true, false, 2, 1, false },
        { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         1, { 16, 0, 0 }, true, true, 2, 1 },
+         1, { 16, 0, 0 }, true, true, 2, 1, false },
        { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         2, { 8, 16, 0 }, false, false, 2, 2 },
+         2, { 8, 16, 0 }, false, false, 2, 2, false },
        { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         2, { 8, 16, 0 }, false, true, 2, 2 },
+         2, { 8, 16, 0 }, false, true, 2, 2, false },
        { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         2, { 8, 16, 0 }, false, false, 2, 1 },
+         2, { 8, 16, 0 }, false, false, 2, 1, false },
        { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         2, { 8, 16, 0 }, false, true, 2, 1 },
+         2, { 8, 16, 0 }, false, true, 2, 1, false },
        { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32,
          VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
          VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-         3, { 8, 8, 8 }, false, false, 2, 2 },
+         3, { 8, 8, 8 }, false, false, 2, 2, false },
 };
 
 /*
@@ -180,11 +197,29 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
                                   struct v4l2_pix_format_mplane *pix,
                                   const struct vsp1_format_info **fmtinfo)
 {
+       static const u32 xrgb_formats[][2] = {
+               { V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 },
+               { V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 },
+               { V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 },
+               { V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 },
+       };
+
        const struct vsp1_format_info *info;
        unsigned int width = pix->width;
        unsigned int height = pix->height;
        unsigned int i;
 
+       /* Backward compatibility: replace deprecated RGB formats by their XRGB
+        * equivalent. This selects the format older userspace applications want
+        * while still exposing the new format.
+        */
+       for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) {
+               if (xrgb_formats[i][0] == pix->pixelformat) {
+                       pix->pixelformat = xrgb_formats[i][1];
+                       break;
+               }
+       }
+
        /* Retrieve format information and select the default format if the
         * requested format isn't supported.
         */
@@ -272,13 +307,17 @@ vsp1_video_format_adjust(struct vsp1_video *video,
  * Pipeline Management
  */
 
-static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
+static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
+                                        struct vsp1_rwpf *input,
                                         struct vsp1_rwpf *output)
 {
        struct vsp1_entity *entity;
        unsigned int entities = 0;
        struct media_pad *pad;
-       bool uds_found = false;
+       bool bru_found = false;
+
+       input->location.left = 0;
+       input->location.top = 0;
 
        pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
 
@@ -292,6 +331,22 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
 
                entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
 
+               /* A BRU is present in the pipeline, store the compose rectangle
+                * location in the input RPF for use when configuring the RPF.
+                */
+               if (entity->type == VSP1_ENTITY_BRU) {
+                       struct vsp1_bru *bru = to_bru(&entity->subdev);
+                       struct v4l2_rect *rect =
+                               &bru->inputs[pad->index].compose;
+
+                       bru->inputs[pad->index].rpf = input;
+
+                       input->location.left = rect->left;
+                       input->location.top = rect->top;
+
+                       bru_found = true;
+               }
+
                /* We've reached the WPF, we're done. */
                if (entity->type == VSP1_ENTITY_WPF)
                        break;
@@ -304,9 +359,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
 
                /* UDS can't be chained. */
                if (entity->type == VSP1_ENTITY_UDS) {
-                       if (uds_found)
+                       if (pipe->uds)
                                return -EPIPE;
-                       uds_found = true;
+
+                       pipe->uds = entity;
+                       pipe->uds_input = bru_found ? pipe->bru
+                                       : &input->entity;
                }
 
                /* Follow the source link. The link setup operations ensure
@@ -325,6 +383,27 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
        return 0;
 }
 
+static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
+{
+       if (pipe->bru) {
+               struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
+               unsigned int i;
+
+               for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
+                       bru->inputs[i].rpf = NULL;
+       }
+
+       INIT_LIST_HEAD(&pipe->entities);
+       pipe->state = VSP1_PIPELINE_STOPPED;
+       pipe->buffers_ready = 0;
+       pipe->num_video = 0;
+       pipe->num_inputs = 0;
+       pipe->output = NULL;
+       pipe->bru = NULL;
+       pipe->lif = NULL;
+       pipe->uds = NULL;
+}
+
 static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
                                  struct vsp1_video *video)
 {
@@ -363,6 +442,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
                        rwpf->video.pipe_index = 0;
                } else if (e->type == VSP1_ENTITY_LIF) {
                        pipe->lif = e;
+               } else if (e->type == VSP1_ENTITY_BRU) {
+                       pipe->bru = e;
                }
        }
 
@@ -378,7 +459,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
         * contains no loop and that all branches end at the output WPF.
         */
        for (i = 0; i < pipe->num_inputs; ++i) {
-               ret = vsp1_pipeline_validate_branch(pipe->inputs[i],
+               ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i],
                                                    pipe->output);
                if (ret < 0)
                        goto error;
@@ -387,12 +468,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
        return 0;
 
 error:
-       INIT_LIST_HEAD(&pipe->entities);
-       pipe->buffers_ready = 0;
-       pipe->num_video = 0;
-       pipe->num_inputs = 0;
-       pipe->output = NULL;
-       pipe->lif = NULL;
+       __vsp1_pipeline_cleanup(pipe);
        return ret;
 }
 
@@ -423,15 +499,8 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
        mutex_lock(&pipe->lock);
 
        /* If we're the last user clean up the pipeline. */
-       if (--pipe->use_count == 0) {
-               INIT_LIST_HEAD(&pipe->entities);
-               pipe->state = VSP1_PIPELINE_STOPPED;
-               pipe->buffers_ready = 0;
-               pipe->num_video = 0;
-               pipe->num_inputs = 0;
-               pipe->output = NULL;
-               pipe->lif = NULL;
-       }
+       if (--pipe->use_count == 0)
+               __vsp1_pipeline_cleanup(pipe);
 
        mutex_unlock(&pipe->lock);
 }
@@ -452,7 +521,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
        int ret;
 
        spin_lock_irqsave(&pipe->irqlock, flags);
-       pipe->state = VSP1_PIPELINE_STOPPING;
+       if (pipe->state == VSP1_PIPELINE_RUNNING)
+               pipe->state = VSP1_PIPELINE_STOPPING;
        spin_unlock_irqrestore(&pipe->irqlock, flags);
 
        ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED,
@@ -460,8 +530,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
        ret = ret == 0 ? -ETIMEDOUT : 0;
 
        list_for_each_entry(entity, &pipe->entities, list_pipe) {
-               if (entity->route)
-                       vsp1_write(entity->vsp1, entity->route,
+               if (entity->route && entity->route->reg)
+                       vsp1_write(entity->vsp1, entity->route->reg,
                                   VI6_DPR_NODE_UNUSED);
 
                v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
@@ -557,6 +627,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
 
 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
 {
+       enum vsp1_pipeline_state state;
        unsigned long flags;
        unsigned int i;
 
@@ -572,11 +643,13 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
 
        spin_lock_irqsave(&pipe->irqlock, flags);
 
+       state = pipe->state;
+       pipe->state = VSP1_PIPELINE_STOPPED;
+
        /* If a stop has been requested, mark the pipeline as stopped and
         * return.
         */
-       if (pipe->state == VSP1_PIPELINE_STOPPING) {
-               pipe->state = VSP1_PIPELINE_STOPPED;
+       if (state == VSP1_PIPELINE_STOPPING) {
                wake_up(&pipe->wq);
                goto done;
        }
@@ -589,6 +662,47 @@ done:
        spin_unlock_irqrestore(&pipe->irqlock, flags);
 }
 
+/*
+ * Propagate the alpha value through the pipeline.
+ *
+ * As the UDS has restricted scaling capabilities when the alpha component needs
+ * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
+ * value. The UDS then outputs a fixed alpha value which needs to be programmed
+ * from the input RPF alpha.
+ */
+void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
+                                  struct vsp1_entity *input,
+                                  unsigned int alpha)
+{
+       struct vsp1_entity *entity;
+       struct media_pad *pad;
+
+       pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
+
+       while (pad) {
+               if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+
+               entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+
+               /* The BRU background color has a fixed alpha value set to 255,
+                * the output alpha value is thus always equal to 255.
+                */
+               if (entity->type == VSP1_ENTITY_BRU)
+                       alpha = 255;
+
+               if (entity->type == VSP1_ENTITY_UDS) {
+                       struct vsp1_uds *uds = to_uds(&entity->subdev);
+
+                       vsp1_uds_set_alpha(uds, alpha);
+                       break;
+               }
+
+               pad = &entity->pads[entity->source_pad];
+               pad = media_entity_remote_pad(pad);
+       }
+}
+
 /* -----------------------------------------------------------------------------
  * videobuf2 Queue Operations
  */
@@ -635,8 +749,6 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb)
        if (vb->num_planes < format->num_planes)
                return -EINVAL;
 
-       buf->video = video;
-
        for (i = 0; i < vb->num_planes; ++i) {
                buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
                buf->length[i] = vb2_plane_size(vb, i);
@@ -680,11 +792,12 @@ static void vsp1_entity_route_setup(struct vsp1_entity *source)
 {
        struct vsp1_entity *sink;
 
-       if (source->route == 0)
+       if (source->route->reg == 0)
                return;
 
        sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
-       vsp1_write(source->vsp1, source->route, sink->id);
+       vsp1_write(source->vsp1, source->route->reg,
+                  sink->route->inputs[source->sink_pad]);
 }
 
 static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -697,6 +810,25 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
 
        mutex_lock(&pipe->lock);
        if (pipe->stream_count == pipe->num_video - 1) {
+               if (pipe->uds) {
+                       struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
+
+                       /* If a BRU is present in the pipeline before the UDS,
+                        * the alpha component doesn't need to be scaled as the
+                        * BRU output alpha value is fixed to 255. Otherwise we
+                        * need to scale the alpha component only when available
+                        * at the input RPF.
+                        */
+                       if (pipe->uds_input->type == VSP1_ENTITY_BRU) {
+                               uds->scale_alpha = false;
+                       } else {
+                               struct vsp1_rwpf *rpf =
+                                       to_rwpf(&pipe->uds_input->subdev);
+
+                               uds->scale_alpha = rpf->video.fmtinfo->alpha;
+                       }
+               }
+
                list_for_each_entry(entity, &pipe->entities, list_pipe) {
                        vsp1_entity_route_setup(entity);
 
@@ -724,6 +856,7 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq)
 {
        struct vsp1_video *video = vb2_get_drv_priv(vq);
        struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+       struct vsp1_video_buffer *buffer;
        unsigned long flags;
        int ret;
 
@@ -741,6 +874,8 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq)
 
        /* Remove all buffers from the IRQ queue. */
        spin_lock_irqsave(&video->irqlock, flags);
+       list_for_each_entry(buffer, &video->irqqueue, queue)
+               vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR);
        INIT_LIST_HEAD(&video->irqqueue);
        spin_unlock_irqrestore(&video->irqlock, flags);
 
@@ -932,8 +1067,8 @@ static int vsp1_video_open(struct file *file)
 
        file->private_data = vfh;
 
-       if (!vsp1_device_get(video->vsp1)) {
-               ret = -EBUSY;
+       ret = vsp1_device_get(video->vsp1);
+       if (ret < 0) {
                v4l2_fh_del(vfh);
                kfree(vfh);
        }