[2/9,media] tvp5150: add userspace subdev API

Message ID 1447865728-5726-2-git-send-email-l.stach@pengutronix.de (mailing list archive)
State Superseded, archived
Headers

Commit Message

Lucas Stach Nov. 18, 2015, 4:55 p.m. UTC
  From: Philipp Zabel <p.zabel@pengutronix.de>

This patch adds userspace V4L2 subdevice API support.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/media/i2c/tvp5150.c | 259 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 200 insertions(+), 59 deletions(-)
  

Comments

kernel test robot Nov. 18, 2015, 5:06 p.m. UTC | #1
Hi Philipp,

[auto build test ERROR on: v4.4-rc1]
[also build test ERROR on: next-20151118]
[cannot apply to: linuxtv-media/master]

url:    https://github.com/0day-ci/linux/commits/Lucas-Stach/tvp5150-convert-register-access-to-regmap/20151119-005732
config: x86_64-randconfig-x018-11181928 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_format':
>> drivers/media/i2c/tvp5150.c:1062:10: error: implicit declaration of function 'v4l2_subdev_get_try_format' [-Werror=implicit-function-declaration]
      return v4l2_subdev_get_try_format(sd, cfg, pad);
             ^
>> drivers/media/i2c/tvp5150.c:1062:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
   drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_crop':
>> drivers/media/i2c/tvp5150.c:1077:10: error: implicit declaration of function 'v4l2_subdev_get_try_crop' [-Werror=implicit-function-declaration]
      return v4l2_subdev_get_try_crop(sd, cfg, pad);
             ^
   drivers/media/i2c/tvp5150.c:1077:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
   drivers/media/i2c/tvp5150.c: In function 'tvp5150_open':
>> drivers/media/i2c/tvp5150.c:1180:27: warning: passing argument 2 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
     tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
                              ^
   drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_rect *' but argument is of type 'int'
    static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
                ^
   drivers/media/i2c/tvp5150.c:1181:6: warning: passing argument 3 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
         v4l2_subdev_get_try_format(fh, 0));
         ^
   drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_mbus_framefmt *' but argument is of type 'int'
    static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
                ^
   drivers/media/i2c/tvp5150.c: In function 'tvp5150_probe':
>> drivers/media/i2c/tvp5150.c:1340:4: error: 'struct v4l2_subdev' has no member named 'entity'
     sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
       ^
   drivers/media/i2c/tvp5150.c:1342:29: error: 'struct v4l2_subdev' has no member named 'entity'
     res = media_entity_init(&sd->entity, 1, &core->pad, 0);
                                ^
   cc1: some warnings being treated as errors

vim +/v4l2_subdev_get_try_format +1062 drivers/media/i2c/tvp5150.c

  1056	tvp5150_get_pad_format(struct tvp5150 *decoder, struct v4l2_subdev *sd,
  1057				 struct v4l2_subdev_pad_config *cfg, unsigned int pad,
  1058				 enum v4l2_subdev_format_whence which)
  1059	{
  1060		switch (which) {
  1061		case V4L2_SUBDEV_FORMAT_TRY:
> 1062			return v4l2_subdev_get_try_format(sd, cfg, pad);
  1063		case V4L2_SUBDEV_FORMAT_ACTIVE:
  1064			return &decoder->format;
  1065		default:
  1066			return NULL;
  1067		}
  1068	}
  1069	
  1070	static struct v4l2_rect *
  1071	tvp5150_get_pad_crop(struct tvp5150 *decoder, struct v4l2_subdev *sd,
  1072			       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
  1073			       enum v4l2_subdev_format_whence which)
  1074	{
  1075		switch (which) {
  1076		case V4L2_SUBDEV_FORMAT_TRY:
> 1077			return v4l2_subdev_get_try_crop(sd, cfg, pad);
  1078		case V4L2_SUBDEV_FORMAT_ACTIVE:
  1079			return &decoder->rect;
  1080		default:
  1081			return NULL;
  1082		}
  1083	}
  1084	
  1085	static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
  1086					   struct v4l2_subdev_pad_config *cfg,
  1087					   struct v4l2_subdev_frame_size_enum *fse)
  1088	{
  1089		struct tvp5150 *decoder = to_tvp5150(sd);
  1090		v4l2_std_id std;
  1091	
  1092		if (fse->index > 0 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
  1093			return -EINVAL;
  1094	
  1095		fse->min_width = TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT;
  1096		fse->max_width = TVP5150_H_MAX;
  1097	
  1098		/* Calculate height based on current standard */
  1099		if (decoder->norm == V4L2_STD_ALL)
  1100			std = tvp5150_read_std(sd);
  1101		else
  1102			std = decoder->norm;
  1103	
  1104		if (std & V4L2_STD_525_60) {
  1105			fse->min_height = TVP5150_V_MAX_525_60 - TVP5150_MAX_CROP_TOP;
  1106			fse->max_height = TVP5150_V_MAX_525_60;
  1107		} else {
  1108			fse->min_height = TVP5150_V_MAX_OTHERS - TVP5150_MAX_CROP_TOP;
  1109			fse->max_height = TVP5150_V_MAX_OTHERS;
  1110		}
  1111	
  1112		return 0;
  1113	}
  1114	
  1115	static int tvp5150_get_format(struct v4l2_subdev *sd,
  1116				      struct v4l2_subdev_pad_config *cfg,
  1117				      struct v4l2_subdev_format *format)
  1118	{
  1119		struct tvp5150 *decoder = to_tvp5150(sd);
  1120	
  1121		format->format = *tvp5150_get_pad_format(decoder, sd, cfg,
  1122							   format->pad, format->which);
  1123		return 0;
  1124	}
  1125	
  1126	static int tvp5150_set_format(struct v4l2_subdev *sd,
  1127				      struct v4l2_subdev_pad_config *cfg,
  1128				      struct v4l2_subdev_format *format)
  1129	{
  1130		struct tvp5150 *decoder = to_tvp5150(sd);
  1131		struct v4l2_mbus_framefmt *mbus_format;
  1132		struct v4l2_rect *crop;
  1133	
  1134		crop = tvp5150_get_pad_crop(decoder, sd, cfg, format->pad,
  1135						format->which);
  1136		mbus_format = tvp5150_get_pad_format(decoder, sd, cfg, format->pad,
  1137						    format->which);
  1138		mbus_format->width = crop->width;
  1139		mbus_format->height = crop->height;
  1140	
  1141		format->format = *mbus_format;
  1142	
  1143		if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
  1144			tvp5150_reset(sd, 0);
  1145	
  1146		v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", mbus_format->width,
  1147				mbus_format->height);
  1148	
  1149		return 0;
  1150	}
  1151	
  1152	static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
  1153					struct v4l2_mbus_framefmt *format)
  1154	{
  1155		crop->left = 0;
  1156		crop->width = TVP5150_H_MAX;
  1157		crop->top = 0;
  1158		if (std & V4L2_STD_525_60)
  1159			crop->height = TVP5150_V_MAX_525_60;
  1160		else
  1161			crop->height = TVP5150_V_MAX_OTHERS;
  1162	
  1163		format->width = crop->width;
  1164		format->height = crop->height;
  1165		format->code = MEDIA_BUS_FMT_UYVY8_2X8;
  1166		format->field = V4L2_FIELD_SEQ_TB;
  1167		format->colorspace = V4L2_COLORSPACE_SMPTE170M;
  1168	}
  1169	
  1170	static int tvp5150_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
  1171	{
  1172		struct tvp5150 *decoder = to_tvp5150(sd);
  1173		v4l2_std_id std;
  1174	
  1175		if (decoder->norm == V4L2_STD_ALL)
  1176			std = tvp5150_read_std(sd);
  1177		else
  1178			std = decoder->norm;
  1179	
> 1180		tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
> 1181					 v4l2_subdev_get_try_format(fh, 0));
  1182		return 0;
  1183	}
  1184	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
  
kernel test robot Nov. 18, 2015, 5:08 p.m. UTC | #2
Hi Philipp,

[auto build test ERROR on: v4.4-rc1]
[also build test ERROR on: next-20151118]
[cannot apply to: linuxtv-media/master]

url:    https://github.com/0day-ci/linux/commits/Lucas-Stach/tvp5150-convert-register-access-to-regmap/20151119-005732
config: x86_64-randconfig-x017-11181928 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

Note: the linux-review/Lucas-Stach/tvp5150-convert-register-access-to-regmap/20151119-005732 HEAD bda9ffd30fce12dfa1b1964094311ae5dd780461 builds fine.
      It only hurts bisectibility.

All error/warnings (new ones prefixed by >>):

   drivers/media/i2c/tvp5150.c: In function 'tvp5150_open':
>> drivers/media/i2c/tvp5150.c:1180:52: warning: passing argument 1 of 'v4l2_subdev_get_try_crop' from incompatible pointer type [-Wincompatible-pointer-types]
     tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
                                                       ^
   In file included from include/media/v4l2-device.h:25:0,
                    from drivers/media/i2c/tvp5150.c:15:
   include/media/v4l2-subdev.h:769:37: note: expected 'struct v4l2_subdev *' but argument is of type 'struct v4l2_subdev_fh *'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
                                        ^
   include/media/v4l2-subdev.h:760:2: note: in definition of macro '__V4L2_SUBDEV_MK_GET_TRY'
     fun_name(struct v4l2_subdev *sd,    \
     ^
>> drivers/media/i2c/tvp5150.c:1180:27: error: too few arguments to function 'v4l2_subdev_get_try_crop'
     tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
                              ^
   In file included from include/media/v4l2-device.h:25:0,
                    from drivers/media/i2c/tvp5150.c:15:
   include/media/v4l2-subdev.h:769:37: note: declared here
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
                                        ^
   include/media/v4l2-subdev.h:760:2: note: in definition of macro '__V4L2_SUBDEV_MK_GET_TRY'
     fun_name(struct v4l2_subdev *sd,    \
     ^
>> drivers/media/i2c/tvp5150.c:1181:33: warning: passing argument 1 of 'v4l2_subdev_get_try_format' from incompatible pointer type [-Wincompatible-pointer-types]
         v4l2_subdev_get_try_format(fh, 0));
                                    ^
   In file included from include/media/v4l2-device.h:25:0,
                    from drivers/media/i2c/tvp5150.c:15:
   include/media/v4l2-subdev.h:768:46: note: expected 'struct v4l2_subdev *' but argument is of type 'struct v4l2_subdev_fh *'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
                                                 ^
   include/media/v4l2-subdev.h:760:2: note: in definition of macro '__V4L2_SUBDEV_MK_GET_TRY'
     fun_name(struct v4l2_subdev *sd,    \
     ^
>> drivers/media/i2c/tvp5150.c:1181:6: error: too few arguments to function 'v4l2_subdev_get_try_format'
         v4l2_subdev_get_try_format(fh, 0));
         ^
   In file included from include/media/v4l2-device.h:25:0,
                    from drivers/media/i2c/tvp5150.c:15:
   include/media/v4l2-subdev.h:768:46: note: declared here
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
                                                 ^
   include/media/v4l2-subdev.h:760:2: note: in definition of macro '__V4L2_SUBDEV_MK_GET_TRY'
     fun_name(struct v4l2_subdev *sd,    \
     ^

vim +/v4l2_subdev_get_try_crop +1180 drivers/media/i2c/tvp5150.c

  1174	
  1175		if (decoder->norm == V4L2_STD_ALL)
  1176			std = tvp5150_read_std(sd);
  1177		else
  1178			std = decoder->norm;
  1179	
> 1180		tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
> 1181					 v4l2_subdev_get_try_format(fh, 0));
  1182		return 0;
  1183	}
  1184	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
  
Lucas Stach Nov. 19, 2015, 8:59 a.m. UTC | #3
Am Donnerstag, den 19.11.2015, 01:06 +0800 schrieb kbuild test robot:
> Hi Philipp,
> 
> [auto build test ERROR on: v4.4-rc1]
> [also build test ERROR on: next-20151118]
> [cannot apply to: linuxtv-media/master]
> 
> url:    https://github.com/0day-ci/linux/commits/Lucas-Stach/tvp5150-convert-register-access-to-regmap/20151119-005732
> config: x86_64-randconfig-x018-11181928 (attached as .config)
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=x86_64 
> 
> All error/warnings (new ones prefixed by >>):
> 
>    drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_format':
> >> drivers/media/i2c/tvp5150.c:1062:10: error: implicit declaration of function 'v4l2_subdev_get_try_format' [-Werror=implicit-function-declaration]
>       return v4l2_subdev_get_try_format(sd, cfg, pad);
>              ^
> >> drivers/media/i2c/tvp5150.c:1062:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
>    drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_crop':
> >> drivers/media/i2c/tvp5150.c:1077:10: error: implicit declaration of function 'v4l2_subdev_get_try_crop' [-Werror=implicit-function-declaration]
>       return v4l2_subdev_get_try_crop(sd, cfg, pad);
>              ^
>    drivers/media/i2c/tvp5150.c:1077:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
>    drivers/media/i2c/tvp5150.c: In function 'tvp5150_open':
> >> drivers/media/i2c/tvp5150.c:1180:27: warning: passing argument 2 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
>      tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
>                               ^
>    drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_rect *' but argument is of type 'int'
>     static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
>                 ^
>    drivers/media/i2c/tvp5150.c:1181:6: warning: passing argument 3 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
>          v4l2_subdev_get_try_format(fh, 0));
>          ^
>    drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_mbus_framefmt *' but argument is of type 'int'
>     static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
>                 ^
>    drivers/media/i2c/tvp5150.c: In function 'tvp5150_probe':
> >> drivers/media/i2c/tvp5150.c:1340:4: error: 'struct v4l2_subdev' has no member named 'entity'
>      sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
>        ^
>    drivers/media/i2c/tvp5150.c:1342:29: error: 'struct v4l2_subdev' has no member named 'entity'
>      res = media_entity_init(&sd->entity, 1, &core->pad, 0);
>                                 ^
>    cc1: some warnings being treated as errors

Ok, this is just a missing depends on VIDEO_V4L2_SUBDEV_API. I'll wait
for other feedback before resending with that fixed.

Regards,
Lucas
  
Mauro Carvalho Chehab Nov. 19, 2015, 9:27 a.m. UTC | #4
Em Thu, 19 Nov 2015 09:59:15 +0100
Lucas Stach <l.stach@pengutronix.de> escreveu:

> Am Donnerstag, den 19.11.2015, 01:06 +0800 schrieb kbuild test robot:
> > Hi Philipp,
> > 
> > [auto build test ERROR on: v4.4-rc1]
> > [also build test ERROR on: next-20151118]
> > [cannot apply to: linuxtv-media/master]
> > 
> > url:    https://github.com/0day-ci/linux/commits/Lucas-Stach/tvp5150-convert-register-access-to-regmap/20151119-005732
> > config: x86_64-randconfig-x018-11181928 (attached as .config)
> > reproduce:
> >         # save the attached .config to linux build tree
> >         make ARCH=x86_64 
> > 
> > All error/warnings (new ones prefixed by >>):
> > 
> >    drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_format':
> > >> drivers/media/i2c/tvp5150.c:1062:10: error: implicit declaration of function 'v4l2_subdev_get_try_format' [-Werror=implicit-function-declaration]
> >       return v4l2_subdev_get_try_format(sd, cfg, pad);
> >              ^
> > >> drivers/media/i2c/tvp5150.c:1062:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
> >    drivers/media/i2c/tvp5150.c: In function 'tvp5150_get_pad_crop':
> > >> drivers/media/i2c/tvp5150.c:1077:10: error: implicit declaration of function 'v4l2_subdev_get_try_crop' [-Werror=implicit-function-declaration]
> >       return v4l2_subdev_get_try_crop(sd, cfg, pad);
> >              ^
> >    drivers/media/i2c/tvp5150.c:1077:10: warning: return makes pointer from integer without a cast [-Wint-conversion]
> >    drivers/media/i2c/tvp5150.c: In function 'tvp5150_open':
> > >> drivers/media/i2c/tvp5150.c:1180:27: warning: passing argument 2 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
> >      tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
> >                               ^
> >    drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_rect *' but argument is of type 'int'
> >     static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
> >                 ^
> >    drivers/media/i2c/tvp5150.c:1181:6: warning: passing argument 3 of 'tvp5150_set_default' makes pointer from integer without a cast [-Wint-conversion]
> >          v4l2_subdev_get_try_format(fh, 0));
> >          ^
> >    drivers/media/i2c/tvp5150.c:1152:13: note: expected 'struct v4l2_mbus_framefmt *' but argument is of type 'int'
> >     static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
> >                 ^
> >    drivers/media/i2c/tvp5150.c: In function 'tvp5150_probe':
> > >> drivers/media/i2c/tvp5150.c:1340:4: error: 'struct v4l2_subdev' has no member named 'entity'
> >      sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
> >        ^
> >    drivers/media/i2c/tvp5150.c:1342:29: error: 'struct v4l2_subdev' has no member named 'entity'
> >      res = media_entity_init(&sd->entity, 1, &core->pad, 0);
> >                                 ^
> >    cc1: some warnings being treated as errors
> 
> Ok, this is just a missing depends on VIDEO_V4L2_SUBDEV_API. I'll wait
> for other feedback before resending with that fixed.

Lucas,

Please notice that tvp5150 is used by drivers that doesn't need nor
want VIDEO_V4L2_SUBDEV_API. So, you should be sure that the driver
will compile fine without subdev API, and will keep working properly
for those devices, no matter if subdev API was compiled or not.

Regards,
Mauro
--
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
  

Patch

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index a7495d2856c3..8670b478dcd6 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -36,7 +36,9 @@  MODULE_PARM_DESC(debug, "Debug level (0-2)");
 
 struct tvp5150 {
 	struct v4l2_subdev sd;
+	struct media_pad pad;
 	struct v4l2_ctrl_handler hdl;
+	struct v4l2_mbus_framefmt format;
 	struct v4l2_rect rect;
 	struct regmap *regmap;
 
@@ -819,38 +821,68 @@  static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
-		struct v4l2_subdev_pad_config *cfg,
-		struct v4l2_subdev_format *format)
+static void tvp5150_try_crop(struct tvp5150 *decoder, struct v4l2_rect *rect,
+			       v4l2_std_id std)
 {
-	struct v4l2_mbus_framefmt *f;
-	struct tvp5150 *decoder = to_tvp5150(sd);
+	unsigned int hmax;
 
-	if (!format || format->pad)
-		return -EINVAL;
+	/* Clamp the crop rectangle boundaries to tvp5150 limits */
+	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
+	rect->width = clamp(rect->width,
+			    TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left,
+			    TVP5150_H_MAX - rect->left);
+	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
 
-	f = &format->format;
+	/* tvp5150 has some special limits */
+	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
+	rect->width = clamp_t(unsigned int, rect->width,
+			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left,
+			      TVP5150_H_MAX - rect->left);
+	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
 
-	tvp5150_reset(sd, 0);
+	/* Calculate height based on current standard */
+	if (std & V4L2_STD_525_60)
+		hmax = TVP5150_V_MAX_525_60;
+	else
+		hmax = TVP5150_V_MAX_OTHERS;
 
-	f->width = decoder->rect.width;
-	f->height = decoder->rect.height;
+	rect->height = clamp(rect->height,
+			     hmax - TVP5150_MAX_CROP_TOP - rect->top,
+			     hmax - rect->top);
+}
 
-	f->code = MEDIA_BUS_FMT_UYVY8_2X8;
-	f->field = V4L2_FIELD_SEQ_TB;
-	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+static void tvp5150_set_crop(struct tvp5150 *decoder, struct v4l2_rect *rect,
+			       v4l2_std_id std)
+{
+	struct regmap *map = decoder->regmap;
+	unsigned int hmax;
 
-	v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
-			f->height);
-	return 0;
+	if (std & V4L2_STD_525_60)
+		hmax = TVP5150_V_MAX_525_60;
+	else
+		hmax = TVP5150_V_MAX_OTHERS;
+
+	regmap_write(map, TVP5150_VERT_BLANKING_START, rect->top);
+	regmap_write(map, TVP5150_VERT_BLANKING_STOP,
+		     rect->top + rect->height - hmax);
+	regmap_write(map, TVP5150_ACT_VD_CROP_ST_MSB,
+		     rect->left >> TVP5150_CROP_SHIFT);
+	regmap_write(map, TVP5150_ACT_VD_CROP_ST_LSB,
+		     rect->left | (1 << TVP5150_CROP_SHIFT));
+	regmap_write(map, TVP5150_ACT_VD_CROP_STP_MSB,
+		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
+		     TVP5150_CROP_SHIFT);
+	regmap_write(map, TVP5150_ACT_VD_CROP_STP_LSB,
+		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
+
+	decoder->rect = *rect;
 }
 
 static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
 {
-	struct v4l2_rect rect = a->c;
 	struct tvp5150 *decoder = to_tvp5150(sd);
+	struct v4l2_rect rect = a->c;
 	v4l2_std_id std;
-	unsigned int hmax;
 
 	v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
 		__func__, rect.left, rect.top, rect.width, rect.height);
@@ -858,42 +890,13 @@  static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
 	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	/* tvp5150 has some special limits */
-	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
-	rect.width = clamp_t(unsigned int, rect.width,
-			     TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
-			     TVP5150_H_MAX - rect.left);
-	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
-
-	/* Calculate height based on current standard */
 	if (decoder->norm == V4L2_STD_ALL)
 		std = tvp5150_read_std(sd);
 	else
 		std = decoder->norm;
 
-	if (std & V4L2_STD_525_60)
-		hmax = TVP5150_V_MAX_525_60;
-	else
-		hmax = TVP5150_V_MAX_OTHERS;
-
-	rect.height = clamp_t(unsigned int, rect.height,
-			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
-			      hmax - rect.top);
-
-	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top);
-	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
-		      rect.top + rect.height - hmax);
-	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
-		      rect.left >> TVP5150_CROP_SHIFT);
-	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
-		      rect.left | (1 << TVP5150_CROP_SHIFT));
-	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
-		      (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
-		      TVP5150_CROP_SHIFT);
-	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
-		      rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
-
-	decoder->rect = rect;
+	tvp5150_try_crop(decoder, &rect, std);
+	tvp5150_set_crop(decoder, &rect, std);
 
 	return 0;
 }
@@ -1049,6 +1052,138 @@  static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 
 /* ----------------------------------------------------------------------- */
 
+static struct v4l2_mbus_framefmt *
+tvp5150_get_pad_format(struct tvp5150 *decoder, struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+			 enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &decoder->format;
+	default:
+		return NULL;
+	}
+}
+
+static struct v4l2_rect *
+tvp5150_get_pad_crop(struct tvp5150 *decoder, struct v4l2_subdev *sd,
+		       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+		       enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &decoder->rect;
+	default:
+		return NULL;
+	}
+}
+
+static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	v4l2_std_id std;
+
+	if (fse->index > 0 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
+		return -EINVAL;
+
+	fse->min_width = TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT;
+	fse->max_width = TVP5150_H_MAX;
+
+	/* Calculate height based on current standard */
+	if (decoder->norm == V4L2_STD_ALL)
+		std = tvp5150_read_std(sd);
+	else
+		std = decoder->norm;
+
+	if (std & V4L2_STD_525_60) {
+		fse->min_height = TVP5150_V_MAX_525_60 - TVP5150_MAX_CROP_TOP;
+		fse->max_height = TVP5150_V_MAX_525_60;
+	} else {
+		fse->min_height = TVP5150_V_MAX_OTHERS - TVP5150_MAX_CROP_TOP;
+		fse->max_height = TVP5150_V_MAX_OTHERS;
+	}
+
+	return 0;
+}
+
+static int tvp5150_get_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
+			      struct v4l2_subdev_format *format)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+
+	format->format = *tvp5150_get_pad_format(decoder, sd, cfg,
+						   format->pad, format->which);
+	return 0;
+}
+
+static int tvp5150_set_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
+			      struct v4l2_subdev_format *format)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	struct v4l2_mbus_framefmt *mbus_format;
+	struct v4l2_rect *crop;
+
+	crop = tvp5150_get_pad_crop(decoder, sd, cfg, format->pad,
+					format->which);
+	mbus_format = tvp5150_get_pad_format(decoder, sd, cfg, format->pad,
+					    format->which);
+	mbus_format->width = crop->width;
+	mbus_format->height = crop->height;
+
+	format->format = *mbus_format;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		tvp5150_reset(sd, 0);
+
+	v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", mbus_format->width,
+			mbus_format->height);
+
+	return 0;
+}
+
+static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop,
+				struct v4l2_mbus_framefmt *format)
+{
+	crop->left = 0;
+	crop->width = TVP5150_H_MAX;
+	crop->top = 0;
+	if (std & V4L2_STD_525_60)
+		crop->height = TVP5150_V_MAX_525_60;
+	else
+		crop->height = TVP5150_V_MAX_OTHERS;
+
+	format->width = crop->width;
+	format->height = crop->height;
+	format->code = MEDIA_BUS_FMT_UYVY8_2X8;
+	format->field = V4L2_FIELD_SEQ_TB;
+	format->colorspace = V4L2_COLORSPACE_SMPTE170M;
+}
+
+static int tvp5150_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	v4l2_std_id std;
+
+	if (decoder->norm == V4L2_STD_ALL)
+		std = tvp5150_read_std(sd);
+	else
+		std = decoder->norm;
+
+	tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
+				 v4l2_subdev_get_try_format(fh, 0));
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
 static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
 	.s_ctrl = tvp5150_s_ctrl,
 };
@@ -1083,8 +1218,9 @@  static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
 
 static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
 	.enum_mbus_code = tvp5150_enum_mbus_code,
-	.set_fmt = tvp5150_fill_fmt,
-	.get_fmt = tvp5150_fill_fmt,
+	.enum_frame_size = tvp5150_enum_frame_size,
+	.get_fmt = tvp5150_get_format,
+	.set_fmt = tvp5150_set_format,
 };
 
 static const struct v4l2_subdev_ops tvp5150_ops = {
@@ -1095,6 +1231,9 @@  static const struct v4l2_subdev_ops tvp5150_ops = {
 	.pad = &tvp5150_pad_ops,
 };
 
+static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
+	.open = tvp5150_open,
+};
 
 /****************************************************************************
 			I2C Client & Driver
@@ -1196,6 +1335,13 @@  static int tvp5150_probe(struct i2c_client *c,
 	core->regmap = map;
 	sd = &core->sd;
 	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+	sd->internal_ops = &tvp5150_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+	core->pad.flags = MEDIA_PAD_FL_SOURCE;
+	res = media_entity_init(&sd->entity, 1, &core->pad, 0);
+	if (res < 0)
+		return res;
 
 	/* 
 	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
@@ -1250,14 +1396,9 @@  static int tvp5150_probe(struct i2c_client *c,
 	v4l2_ctrl_handler_setup(&core->hdl);
 
 	/* Default is no cropping */
-	core->rect.top = 0;
-	if (tvp5150_read_std(sd) & V4L2_STD_525_60)
-		core->rect.height = TVP5150_V_MAX_525_60;
-	else
-		core->rect.height = TVP5150_V_MAX_OTHERS;
-	core->rect.left = 0;
-	core->rect.width = TVP5150_H_MAX;
+	tvp5150_set_default(tvp5150_read_std(sd), &core->rect, &core->format);
 
+	sd->dev = &c->dev;
 	res = v4l2_async_register_subdev(sd);
 	if (res < 0)
 		goto err;