diff mbox

[2/7,media] vimc: sen: Integrate the tpg on the sensor

Message ID e3c80eb0aebe828d2df72be9971ad720f439bb71.1438891530.git.helen.fornazier@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Helen Mae Koike Fornazier Aug. 6, 2015, 8:26 p.m. UTC
Initialize the test pattern generator on the sensor
Generate a colored bar image instead of a grey one

Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>
---
 drivers/media/platform/vimc/Kconfig       |  1 +
 drivers/media/platform/vimc/vimc-sensor.c | 44 +++++++++++++++++++++++++++++--
 2 files changed, 43 insertions(+), 2 deletions(-)

Comments

Laurent Pinchart Aug. 13, 2015, 8:29 p.m. UTC | #1
Hi Helen,

Thank you for the patch.

On Thursday 06 August 2015 17:26:09 Helen Fornazier wrote:
> Initialize the test pattern generator on the sensor
> Generate a colored bar image instead of a grey one
> 
> Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>
> ---
>  drivers/media/platform/vimc/Kconfig       |  1 +
>  drivers/media/platform/vimc/vimc-sensor.c | 44 ++++++++++++++++++++++++++--
>  2 files changed, 43 insertions(+), 2 deletions(-)

[snip]

> diff --git a/drivers/media/platform/vimc/vimc-sensor.c
> b/drivers/media/platform/vimc/vimc-sensor.c index d613792..a2879ad 100644
> --- a/drivers/media/platform/vimc/vimc-sensor.c
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -16,15 +16,19 @@
>   */
> 
>  #include <linux/freezer.h>
> +#include <media/tpg.h>
>  #include <linux/vmalloc.h>
>  #include <linux/v4l2-mediabus.h>

media/tpg.h should have been inserted here.

>  #include <media/v4l2-subdev.h>
> 
>  #include "vimc-sensor.h"

[snip]

> +static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
> +{
> +	const struct vimc_pix_map *vpix;
> +
> +	vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +	/* This should never be NULL, as we won't allow any format other then
> +	 * the ones in the vimc_pix_map_list table */
> +	BUG_ON(!vpix);

BUG_ON() is quite harsh, it will stop the machine. If the condition can never 
happen then you can remove the check. If you're worried it might happen I'd 
use a WARN_ON() and return.

> +	tpg_s_bytesperline(&vsen->tpg, 0,
> +			   vsen->mbus_format.width * vpix->bpp);
> +	tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
> +	tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
> +	/* TODO: check why the tpg_s_field need this third argument if
> +	 * it is already receiving the field */
> +	tpg_s_field(&vsen->tpg, vsen->mbus_format.field,
> +		    vsen->mbus_format.field == V4L2_FIELD_ALTERNATE);
> +	tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
> +	tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
> +	tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
> +	tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
> +}
Hans Verkuil Aug. 14, 2015, 12:15 p.m. UTC | #2
On 08/06/2015 10:26 PM, Helen Fornazier wrote:
> Initialize the test pattern generator on the sensor
> Generate a colored bar image instead of a grey one

You don't want to put the tpg in every sensor and have all the blocks in
between process the video. This is all virtual, so all that is necessary
is to put the tpg in every DMA engine (video node) but have the subdevs
modify the tpg setting when you start the pipeline.

So the source would set the width/height to the sensor resolution, and it
will initialize the crop/compose rectangles. Every other entity in the
pipeline will continue modifying according to what they do. E.g. a scaler
will just change the compose rectangle.

When you start streaming the tpg will generate the image based on all those
settings as if all the entities would actually do the work.

Of course, this assumes the processing the entities do map to what the tpg
can do, but that's true for vimc.

An additional advantage is that the entities can use a wide range of
mediabus formats since the tpg can generate basically anything. Implementing
multiplanar is similarly easy. This would be much harder if you had to write
the image processing code for the entities since you'd either have to support
lots of different formats (impractical) or limit yourself to just a few.

> 
> Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>
> ---
>  drivers/media/platform/vimc/Kconfig       |  1 +
>  drivers/media/platform/vimc/vimc-sensor.c | 44 +++++++++++++++++++++++++++++--
>  2 files changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> index 81279f4..7cf7e84 100644
> --- a/drivers/media/platform/vimc/Kconfig
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -1,6 +1,7 @@
>  config VIDEO_VIMC
>  	tristate "Virtual Media Controller Driver (VIMC)"
>  	select VIDEO_V4L2_SUBDEV_API
> +	select VIDEO_TPG
>  	default n
>  	---help---
>  	  Skeleton driver for Virtual Media Controller
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> index d613792..a2879ad 100644
> --- a/drivers/media/platform/vimc/vimc-sensor.c
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -16,15 +16,19 @@
>   */
>  
>  #include <linux/freezer.h>
> +#include <media/tpg.h>
>  #include <linux/vmalloc.h>
>  #include <linux/v4l2-mediabus.h>
>  #include <media/v4l2-subdev.h>
>  
>  #include "vimc-sensor.h"
>  
> +#define VIMC_SEN_FRAME_MAX_WIDTH 4096
> +
>  struct vimc_sen_device {
>  	struct vimc_ent_device ved;
>  	struct v4l2_subdev sd;
> +	struct tpg_data tpg;
>  	struct v4l2_device *v4l2_dev;
>  	struct device *dev;
>  	struct task_struct *kthread_sen;
> @@ -87,6 +91,29 @@ static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
>  	return 0;
>  }
>  
> +static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
> +{
> +	const struct vimc_pix_map *vpix;
> +
> +	vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +	/* This should never be NULL, as we won't allow any format other then
> +	 * the ones in the vimc_pix_map_list table */
> +	BUG_ON(!vpix);
> +
> +	tpg_s_bytesperline(&vsen->tpg, 0,
> +			   vsen->mbus_format.width * vpix->bpp);
> +	tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
> +	tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
> +	/* TODO: check why the tpg_s_field need this third argument if
> +	 * it is already receiving the field */
> +	tpg_s_field(&vsen->tpg, vsen->mbus_format.field,
> +		    vsen->mbus_format.field == V4L2_FIELD_ALTERNATE);

Actually the second argument argument cannot be FIELD_ALTERNATE. If it
is field ALTERNATE, then the second argument must be either FIELD_TOP or
FIELD_BOTTOM: i.e. it tells the generator which field comes first in the
FIELD_ALTERNATE case.

And in case you are wondering: it's always FIELD_TOP except for 60 Hz SDTV
formats where it is FIELD_BOTTOM.

> +	tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
> +	tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
> +	tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
> +	tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
> +}
> +
>  static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
>  	.enum_mbus_code		= vimc_sen_enum_mbus_code,
>  	.enum_frame_size	= vimc_sen_enum_frame_size,
> @@ -112,7 +139,7 @@ static int vimc_thread_sen(void *data)
>  		if (kthread_should_stop())
>  			break;
>  
> -		memset(vsen->frame, 100, vsen->frame_size);
> +		tpg_fill_plane_buffer(&vsen->tpg, V4L2_STD_PAL, 0, vsen->frame);
>  
>  		/* Send the frame to all source pads */
>  		for (i = 0; i < vsen->sd.entity.num_pads; i++)
> @@ -192,6 +219,7 @@ static void vimc_sen_destroy(struct vimc_ent_device *ved)
>  	struct vimc_sen_device *vsen = container_of(ved,
>  						struct vimc_sen_device, ved);
>  
> +	tpg_free(&vsen->tpg);
>  	media_entity_cleanup(ved->ent);
>  	v4l2_device_unregister_subdev(&vsen->sd);
>  	kfree(vsen);
> @@ -242,6 +270,16 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>  	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
>  	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
>  
> +	/* Initialize the test pattern generator */
> +	tpg_init(&vsen->tpg, vsen->mbus_format.width,
> +		 vsen->mbus_format.height);
> +	ret = tpg_alloc(&vsen->tpg, VIMC_SEN_FRAME_MAX_WIDTH);
> +	if (ret)
> +		goto err_clean_m_ent;
> +
> +	/* Configure the tpg */
> +	vimc_sen_tpg_s_format(vsen);
> +
>  	/* Fill the vimc_ent_device struct */
>  	vsen->ved.destroy = vimc_sen_destroy;
>  	vsen->ved.ent = &vsen->sd.entity;
> @@ -261,11 +299,13 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>  	if (ret) {
>  		dev_err(vsen->dev,
>  			"subdev register failed (err=%d)\n", ret);
> -		goto err_clean_m_ent;
> +		goto err_free_tpg;
>  	}
>  
>  	return &vsen->ved;
>  
> +err_free_tpg:
> +	tpg_free(&vsen->tpg);
>  err_clean_m_ent:
>  	media_entity_cleanup(&vsen->sd.entity);
>  err_clean_pads:
> 

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Laurent Pinchart Aug. 14, 2015, 1:05 p.m. UTC | #3
Hi Hans,

On Friday 14 August 2015 14:15:58 Hans Verkuil wrote:
> On 08/06/2015 10:26 PM, Helen Fornazier wrote:
> > Initialize the test pattern generator on the sensor
> > Generate a colored bar image instead of a grey one
> 
> You don't want to put the tpg in every sensor and have all the blocks in
> between process the video. This is all virtual, so all that is necessary
> is to put the tpg in every DMA engine (video node) but have the subdevs
> modify the tpg setting when you start the pipeline.
> 
> So the source would set the width/height to the sensor resolution, and it
> will initialize the crop/compose rectangles. Every other entity in the
> pipeline will continue modifying according to what they do. E.g. a scaler
> will just change the compose rectangle.
> 
> When you start streaming the tpg will generate the image based on all those
> settings as if all the entities would actually do the work.
> 
> Of course, this assumes the processing the entities do map to what the tpg
> can do, but that's true for vimc.

There will be small differences, as generating a YUV image won't be exactly 
the same as debayering a bayer image. It probably doesn't matter much though.

A bigger problem is that the driver aims at supporting output video device 
nodes at some point in mem-to-mem pipelines. For that the debayer, color space 
conversion and scaler subdevs need to perform real image processing.

> An additional advantage is that the entities can use a wide range of
> mediabus formats since the tpg can generate basically anything. Implementing
> multiplanar is similarly easy. This would be much harder if you had to
> write the image processing code for the entities since you'd either have to
> support lots of different formats (impractical) or limit yourself to just a
> few.
Helen Mae Koike Fornazier Sept. 7, 2015, 6:21 p.m. UTC | #4
Hi, thank you all for your review

On Fri, Aug 14, 2015 at 9:15 AM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 08/06/2015 10:26 PM, Helen Fornazier wrote:
>> Initialize the test pattern generator on the sensor
>> Generate a colored bar image instead of a grey one
>
> You don't want to put the tpg in every sensor and have all the blocks in
> between process the video. This is all virtual, so all that is necessary
> is to put the tpg in every DMA engine (video node) but have the subdevs
> modify the tpg setting when you start the pipeline.
>
> So the source would set the width/height to the sensor resolution, and it
> will initialize the crop/compose rectangles. Every other entity in the
> pipeline will continue modifying according to what they do. E.g. a scaler
> will just change the compose rectangle.
>
> When you start streaming the tpg will generate the image based on all those
> settings as if all the entities would actually do the work.
>
> Of course, this assumes the processing the entities do map to what the tpg
> can do, but that's true for vimc.
>
> An additional advantage is that the entities can use a wide range of
> mediabus formats since the tpg can generate basically anything. Implementing
> multiplanar is similarly easy. This would be much harder if you had to write
> the image processing code for the entities since you'd either have to support
> lots of different formats (impractical) or limit yourself to just a few.
>
>>
>> Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>
>> ---
>>  drivers/media/platform/vimc/Kconfig       |  1 +
>>  drivers/media/platform/vimc/vimc-sensor.c | 44 +++++++++++++++++++++++++++++--
>>  2 files changed, 43 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
>> index 81279f4..7cf7e84 100644
>> --- a/drivers/media/platform/vimc/Kconfig
>> +++ b/drivers/media/platform/vimc/Kconfig
>> @@ -1,6 +1,7 @@
>>  config VIDEO_VIMC
>>       tristate "Virtual Media Controller Driver (VIMC)"
>>       select VIDEO_V4L2_SUBDEV_API
>> +     select VIDEO_TPG
>>       default n
>>       ---help---
>>         Skeleton driver for Virtual Media Controller
>> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
>> index d613792..a2879ad 100644
>> --- a/drivers/media/platform/vimc/vimc-sensor.c
>> +++ b/drivers/media/platform/vimc/vimc-sensor.c
>> @@ -16,15 +16,19 @@
>>   */
>>
>>  #include <linux/freezer.h>
>> +#include <media/tpg.h>
>>  #include <linux/vmalloc.h>
>>  #include <linux/v4l2-mediabus.h>
>>  #include <media/v4l2-subdev.h>
>>
>>  #include "vimc-sensor.h"
>>
>> +#define VIMC_SEN_FRAME_MAX_WIDTH 4096
>> +
>>  struct vimc_sen_device {
>>       struct vimc_ent_device ved;
>>       struct v4l2_subdev sd;
>> +     struct tpg_data tpg;
>>       struct v4l2_device *v4l2_dev;
>>       struct device *dev;
>>       struct task_struct *kthread_sen;
>> @@ -87,6 +91,29 @@ static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
>>       return 0;
>>  }
>>
>> +static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
>> +{
>> +     const struct vimc_pix_map *vpix;
>> +
>> +     vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
>> +     /* This should never be NULL, as we won't allow any format other then
>> +      * the ones in the vimc_pix_map_list table */
>> +     BUG_ON(!vpix);
>> +
>> +     tpg_s_bytesperline(&vsen->tpg, 0,
>> +                        vsen->mbus_format.width * vpix->bpp);
>> +     tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
>> +     tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
>> +     /* TODO: check why the tpg_s_field need this third argument if
>> +      * it is already receiving the field */
>> +     tpg_s_field(&vsen->tpg, vsen->mbus_format.field,
>> +                 vsen->mbus_format.field == V4L2_FIELD_ALTERNATE);
>
> Actually the second argument argument cannot be FIELD_ALTERNATE. If it
> is field ALTERNATE, then the second argument must be either FIELD_TOP or
> FIELD_BOTTOM: i.e. it tells the generator which field comes first in the
> FIELD_ALTERNATE case.
>
> And in case you are wondering: it's always FIELD_TOP except for 60 Hz SDTV
> formats where it is FIELD_BOTTOM.

I am not really familiar to SDTV, but it seems to me to be a different
structure API. Thus can I put FIELD_TOP directly in the second
argument?

tpg_s_field(&vsen->tpg, V4L2_FIELD_TOP,
        vsen->mbus_format.field == V4L2_FIELD_ALTERNATE);

Or is there a way to check if the format is SDTV? I didn't find it
defined on V4L2_PIX_FMT_

>
>> +     tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
>> +     tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
>> +     tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
>> +     tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
>> +}
>> +
>>  static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
>>       .enum_mbus_code         = vimc_sen_enum_mbus_code,
>>       .enum_frame_size        = vimc_sen_enum_frame_size,
>> @@ -112,7 +139,7 @@ static int vimc_thread_sen(void *data)
>>               if (kthread_should_stop())
>>                       break;
>>
>> -             memset(vsen->frame, 100, vsen->frame_size);
>> +             tpg_fill_plane_buffer(&vsen->tpg, V4L2_STD_PAL, 0, vsen->frame);
>>
>>               /* Send the frame to all source pads */
>>               for (i = 0; i < vsen->sd.entity.num_pads; i++)
>> @@ -192,6 +219,7 @@ static void vimc_sen_destroy(struct vimc_ent_device *ved)
>>       struct vimc_sen_device *vsen = container_of(ved,
>>                                               struct vimc_sen_device, ved);
>>
>> +     tpg_free(&vsen->tpg);
>>       media_entity_cleanup(ved->ent);
>>       v4l2_device_unregister_subdev(&vsen->sd);
>>       kfree(vsen);
>> @@ -242,6 +270,16 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>>       vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
>>       vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
>>
>> +     /* Initialize the test pattern generator */
>> +     tpg_init(&vsen->tpg, vsen->mbus_format.width,
>> +              vsen->mbus_format.height);
>> +     ret = tpg_alloc(&vsen->tpg, VIMC_SEN_FRAME_MAX_WIDTH);
>> +     if (ret)
>> +             goto err_clean_m_ent;
>> +
>> +     /* Configure the tpg */
>> +     vimc_sen_tpg_s_format(vsen);
>> +
>>       /* Fill the vimc_ent_device struct */
>>       vsen->ved.destroy = vimc_sen_destroy;
>>       vsen->ved.ent = &vsen->sd.entity;
>> @@ -261,11 +299,13 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>>       if (ret) {
>>               dev_err(vsen->dev,
>>                       "subdev register failed (err=%d)\n", ret);
>> -             goto err_clean_m_ent;
>> +             goto err_free_tpg;
>>       }
>>
>>       return &vsen->ved;
>>
>> +err_free_tpg:
>> +     tpg_free(&vsen->tpg);
>>  err_clean_m_ent:
>>       media_entity_cleanup(&vsen->sd.entity);
>>  err_clean_pads:
>>
>
> Regards,
>
>         Hans
diff mbox

Patch

diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
index 81279f4..7cf7e84 100644
--- a/drivers/media/platform/vimc/Kconfig
+++ b/drivers/media/platform/vimc/Kconfig
@@ -1,6 +1,7 @@ 
 config VIDEO_VIMC
 	tristate "Virtual Media Controller Driver (VIMC)"
 	select VIDEO_V4L2_SUBDEV_API
+	select VIDEO_TPG
 	default n
 	---help---
 	  Skeleton driver for Virtual Media Controller
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index d613792..a2879ad 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -16,15 +16,19 @@ 
  */
 
 #include <linux/freezer.h>
+#include <media/tpg.h>
 #include <linux/vmalloc.h>
 #include <linux/v4l2-mediabus.h>
 #include <media/v4l2-subdev.h>
 
 #include "vimc-sensor.h"
 
+#define VIMC_SEN_FRAME_MAX_WIDTH 4096
+
 struct vimc_sen_device {
 	struct vimc_ent_device ved;
 	struct v4l2_subdev sd;
+	struct tpg_data tpg;
 	struct v4l2_device *v4l2_dev;
 	struct device *dev;
 	struct task_struct *kthread_sen;
@@ -87,6 +91,29 @@  static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
+{
+	const struct vimc_pix_map *vpix;
+
+	vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+	/* This should never be NULL, as we won't allow any format other then
+	 * the ones in the vimc_pix_map_list table */
+	BUG_ON(!vpix);
+
+	tpg_s_bytesperline(&vsen->tpg, 0,
+			   vsen->mbus_format.width * vpix->bpp);
+	tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
+	tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
+	/* TODO: check why the tpg_s_field need this third argument if
+	 * it is already receiving the field */
+	tpg_s_field(&vsen->tpg, vsen->mbus_format.field,
+		    vsen->mbus_format.field == V4L2_FIELD_ALTERNATE);
+	tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
+	tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
+	tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
+	tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
+}
+
 static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
 	.enum_mbus_code		= vimc_sen_enum_mbus_code,
 	.enum_frame_size	= vimc_sen_enum_frame_size,
@@ -112,7 +139,7 @@  static int vimc_thread_sen(void *data)
 		if (kthread_should_stop())
 			break;
 
-		memset(vsen->frame, 100, vsen->frame_size);
+		tpg_fill_plane_buffer(&vsen->tpg, V4L2_STD_PAL, 0, vsen->frame);
 
 		/* Send the frame to all source pads */
 		for (i = 0; i < vsen->sd.entity.num_pads; i++)
@@ -192,6 +219,7 @@  static void vimc_sen_destroy(struct vimc_ent_device *ved)
 	struct vimc_sen_device *vsen = container_of(ved,
 						struct vimc_sen_device, ved);
 
+	tpg_free(&vsen->tpg);
 	media_entity_cleanup(ved->ent);
 	v4l2_device_unregister_subdev(&vsen->sd);
 	kfree(vsen);
@@ -242,6 +270,16 @@  struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
 	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
 	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
 
+	/* Initialize the test pattern generator */
+	tpg_init(&vsen->tpg, vsen->mbus_format.width,
+		 vsen->mbus_format.height);
+	ret = tpg_alloc(&vsen->tpg, VIMC_SEN_FRAME_MAX_WIDTH);
+	if (ret)
+		goto err_clean_m_ent;
+
+	/* Configure the tpg */
+	vimc_sen_tpg_s_format(vsen);
+
 	/* Fill the vimc_ent_device struct */
 	vsen->ved.destroy = vimc_sen_destroy;
 	vsen->ved.ent = &vsen->sd.entity;
@@ -261,11 +299,13 @@  struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
 	if (ret) {
 		dev_err(vsen->dev,
 			"subdev register failed (err=%d)\n", ret);
-		goto err_clean_m_ent;
+		goto err_free_tpg;
 	}
 
 	return &vsen->ved;
 
+err_free_tpg:
+	tpg_free(&vsen->tpg);
 err_clean_m_ent:
 	media_entity_cleanup(&vsen->sd.entity);
 err_clean_pads: