linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 1/9] [media] tvp5150: convert register access to regmap
@ 2016-03-14 15:23 Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Lucas Stach
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

Regmap provides built in debugging, caching and provides dedicated accessors
for bit manipulations in registers, which make the following changes a lot
simpler.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 6c3769d44b75..6ba93a425640 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -10,6 +10,7 @@
 #include <linux/videodev2.h>
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
 #include <media/i2c/tvp5150.h>
@@ -37,6 +38,7 @@ struct tvp5150 {
 	struct v4l2_subdev sd;
 	struct v4l2_ctrl_handler hdl;
 	struct v4l2_rect rect;
+	struct regmap *regmap;
 
 	v4l2_std_id norm;	/* Current set standard */
 	u32 input;
@@ -56,30 +58,14 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
 
 static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
 {
-	struct i2c_client *c = v4l2_get_subdevdata(sd);
-	int rc;
-
-	rc = i2c_smbus_read_byte_data(c, addr);
-	if (rc < 0) {
-		v4l2_err(sd, "i2c i/o error: rc == %d\n", rc);
-		return rc;
-	}
-
-	v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, rc);
-
-	return rc;
-}
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	int ret, val;
 
-static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
-				 unsigned char value)
-{
-	struct i2c_client *c = v4l2_get_subdevdata(sd);
-	int rc;
+	ret = regmap_read(decoder->regmap, addr, &val);
+	if (ret < 0)
+		return ret;
 
-	v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", addr, value);
-	rc = i2c_smbus_write_byte_data(c, addr, value);
-	if (rc < 0)
-		v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d\n", rc);
+	return val;
 }
 
 static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
@@ -266,8 +252,8 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
 			decoder->input, decoder->output,
 			input, opmode);
 
-	tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
-	tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
+	regmap_write(decoder->regmap, TVP5150_OP_MODE_CTL, opmode);
+	regmap_write(decoder->regmap, TVP5150_VD_IN_SRC_SEL_1, input);
 
 	/* Svideo should enable YCrCb output and disable GPCL output
 	 * For Composite and TV, it should be the reverse
@@ -282,7 +268,7 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
 		val = (val & ~0x40) | 0x10;
 	else
 		val = (val & ~0x10) | 0x40;
-	tvp5150_write(sd, TVP5150_MISC_CTL, val);
+	regmap_write(decoder->regmap, TVP5150_MISC_CTL, val);
 };
 
 struct i2c_reg_value {
@@ -553,8 +539,10 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
 static int tvp5150_write_inittab(struct v4l2_subdev *sd,
 				const struct i2c_reg_value *regs)
 {
+	struct tvp5150 *decoder = to_tvp5150(sd);
+
 	while (regs->reg != 0xff) {
-		tvp5150_write(sd, regs->reg, regs->value);
+		regmap_write(decoder->regmap, regs->reg, regs->value);
 		regs++;
 	}
 	return 0;
@@ -563,22 +551,24 @@ static int tvp5150_write_inittab(struct v4l2_subdev *sd,
 static int tvp5150_vdp_init(struct v4l2_subdev *sd,
 				const struct i2c_vbi_ram_value *regs)
 {
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	struct regmap *map = decoder->regmap;
 	unsigned int i;
 
 	/* Disable Full Field */
-	tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+	regmap_write(map, TVP5150_FULL_FIELD_ENA, 0);
 
 	/* Before programming, Line mode should be at 0xff */
 	for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
-		tvp5150_write(sd, i, 0xff);
+		regmap_write(map, i, 0xff);
 
 	/* Load Ram Table */
 	while (regs->reg != (u16)-1) {
-		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
-		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
+		regmap_write(map, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+		regmap_write(map, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
 
 		for (i = 0; i < 16; i++)
-			tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
+			regmap_write(map, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
 
 		regs++;
 	}
@@ -658,11 +648,11 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd,
 	reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
 
 	if (fields&1) {
-		tvp5150_write(sd, reg, type);
+		regmap_write(decoder->regmap, reg, type);
 	}
 
 	if (fields&2) {
-		tvp5150_write(sd, reg+1, type);
+		regmap_write(decoder->regmap, reg+1, type);
 	}
 
 	return type;
@@ -731,7 +721,7 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
 	}
 
 	v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
-	tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
+	regmap_write(decoder->regmap, TVP5150_VIDEO_STD, fmt);
 	return 0;
 }
 
@@ -777,20 +767,20 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 
 static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct tvp5150 *decoder = to_tvp5150(to_sd(ctrl));
 
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
-		tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
+		regmap_write(decoder->regmap, TVP5150_BRIGHT_CTL, ctrl->val);
 		return 0;
 	case V4L2_CID_CONTRAST:
-		tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
+		regmap_write(decoder->regmap, TVP5150_CONTRAST_CTL, ctrl->val);
 		return 0;
 	case V4L2_CID_SATURATION:
-		tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
+		regmap_write(decoder->regmap, TVP5150_SATURATION_CTL, ctrl->val);
 		return 0;
 	case V4L2_CID_HUE:
-		tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
+		regmap_write(decoder->regmap, TVP5150_HUE_CTL, ctrl->val);
 		return 0;
 	}
 	return -EINVAL;
@@ -890,17 +880,17 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
 			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
 			      hmax - rect.top);
 
-	tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
-	tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
+	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top);
+	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
 		      rect.top + rect.height - hmax);
-	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
+	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
 		      rect.left >> TVP5150_CROP_SHIFT);
-	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
+	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
 		      rect.left | (1 << TVP5150_CROP_SHIFT));
-	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
+	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
 		      (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
 		      TVP5150_CROP_SHIFT);
-	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
+	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
 		      rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
 
 	decoder->rect = rect;
@@ -965,22 +955,25 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd,
 
 static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
 {
+	struct tvp5150 *decoder = to_tvp5150(sd);
+
 	/* this is for capturing 36 raw vbi lines
 	   if there's a way to cut off the beginning 2 vbi lines
 	   with the tvp5150 then the vbi line count could be lowered
 	   to 17 lines/field again, although I couldn't find a register
 	   which could do that cropping */
 	if (fmt->sample_format == V4L2_PIX_FMT_GREY)
-		tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+		regmap_write(decoder->regmap, TVP5150_LUMA_PROC_CTL_1, 0x70);
 	if (fmt->count[0] == 18 && fmt->count[1] == 18) {
-		tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
-		tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+		regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, 0x00);
+		regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, 0x01);
 	}
 	return 0;
 }
 
 static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
 {
+	struct tvp5150 *decoder = to_tvp5150(sd);
 	int i;
 
 	if (svbi->service_set != 0) {
@@ -991,17 +984,17 @@ static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f
 				       svbi->service_lines[0][i], 0xf0, i, 3);
 		}
 		/* Enables FIFO */
-		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+		regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 1);
 	} else {
 		/* Disables FIFO*/
-		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
+		regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 0);
 
 		/* Disable Full Field */
-		tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+		regmap_write(decoder->regmap, TVP5150_FULL_FIELD_ENA, 0);
 
 		/* Disable Line modes */
 		for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
-			tvp5150_write(sd, i, 0xff);
+			regmap_write(decoder->regmap, i, 0xff);
 	}
 	return 0;
 }
@@ -1039,7 +1032,9 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
 
 static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
 {
-	tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+	struct tvp5150 *decoder = to_tvp5150(sd);
+
+	regmap_write(decoder->regmap, reg->reg & 0xff, reg->val & 0xff);
 	return 0;
 }
 #endif
@@ -1105,13 +1100,87 @@ static const struct v4l2_subdev_ops tvp5150_ops = {
 			I2C Client & Driver
  ****************************************************************************/
 
+static const struct regmap_range tvp5150_readable_ranges[] = {
+	{
+		.range_min = TVP5150_VD_IN_SRC_SEL_1,
+		.range_max = TVP5150_AUTOSW_MSK,
+	}, {
+		.range_min = TVP5150_COLOR_KIL_THSH_CTL,
+		.range_max = TVP5150_CONF_SHARED_PIN,
+	}, {
+		.range_min = TVP5150_ACT_VD_CROP_ST_MSB,
+		.range_max = TVP5150_HORIZ_SYNC_START,
+	}, {
+		.range_min = TVP5150_VERT_BLANKING_START,
+		.range_max = TVP5150_INTT_CONFIG_REG_B,
+	}, {
+		.range_min = TVP5150_VIDEO_STD,
+		.range_max = TVP5150_VIDEO_STD,
+	}, {
+		.range_min = TVP5150_CB_GAIN_FACT,
+		.range_max = TVP5150_REV_SELECT,
+	}, {
+		.range_min = TVP5150_MSB_DEV_ID,
+		.range_max = TVP5150_STATUS_REG_5,
+	}, {
+		.range_min = TVP5150_CC_DATA_INI,
+		.range_max = TVP5150_TELETEXT_FIL_ENA,
+	}, {
+		.range_min = TVP5150_INT_STATUS_REG_A,
+		.range_max = TVP5150_FIFO_OUT_CTRL,
+	}, {
+		.range_min = TVP5150_FULL_FIELD_ENA,
+		.range_max = TVP5150_FULL_FIELD_MODE_REG,
+	},
+};
+
+bool tvp5150_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TVP5150_VERT_LN_COUNT_MSB:
+	case TVP5150_VERT_LN_COUNT_LSB:
+	case TVP5150_INT_STATUS_REG_A:
+	case TVP5150_INT_STATUS_REG_B:
+	case TVP5150_INT_ACTIVE_REG_B:
+	case TVP5150_STATUS_REG_1:
+	case TVP5150_STATUS_REG_2:
+	case TVP5150_STATUS_REG_3:
+	case TVP5150_STATUS_REG_4:
+	case TVP5150_STATUS_REG_5:
+	/* CC, WSS, VPS, VITC data? */
+	case TVP5150_VBI_FIFO_READ_DATA:
+	case TVP5150_VDP_STATUS_REG:
+	case TVP5150_FIFO_WORD_COUNT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_access_table tvp5150_readable_table = {
+	.yes_ranges = tvp5150_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(tvp5150_readable_ranges),
+};
+
+static struct regmap_config tvp5150_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.rd_table = &tvp5150_readable_table,
+	.volatile_reg = tvp5150_volatile_reg,
+};
+
 static int tvp5150_probe(struct i2c_client *c,
 			 const struct i2c_device_id *id)
 {
 	struct tvp5150 *core;
 	struct v4l2_subdev *sd;
-	int tvp5150_id[4];
-	int i, res;
+	struct regmap *map;
+	u8 tvp5150_id[4];
+	int res;
 
 	/* Check if the adapter supports the needed features */
 	if (!i2c_check_functionality(c->adapter,
@@ -1121,6 +1190,10 @@ static int tvp5150_probe(struct i2c_client *c,
 	core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
 	if (!core)
 		return -ENOMEM;
+	map = devm_regmap_init_i2c(c, &tvp5150_config);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+	core->regmap = map;
 	sd = &core->sd;
 	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
 
@@ -1128,11 +1201,10 @@ static int tvp5150_probe(struct i2c_client *c,
 	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
 	 * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER 
 	 */
-	for (i = 0; i < 4; i++) {
-		res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
-		if (res < 0)
-			return res;
-		tvp5150_id[i] = res;
+	res = regmap_bulk_read(map, TVP5150_MSB_DEV_ID, tvp5150_id, 4);
+	if (res < 0) {
+		dev_err(&c->dev, "reading ID registers failed: %d\n", res);
+		return res;
 	}
 
 	v4l_info(c, "chip found @ 0x%02x (%s)\n",
@@ -1143,7 +1215,7 @@ static int tvp5150_probe(struct i2c_client *c,
 			  tvp5150_id[0], tvp5150_id[1]);
 
 		/* ITU-T BT.656.4 timing */
-		tvp5150_write(sd, TVP5150_REV_SELECT, 0);
+		regmap_write(map, TVP5150_REV_SELECT, 0);
 	} else {
 		/* Is TVP5150A */
 		if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 2/9] [media] tvp5150: add userspace subdev API
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 17:34   ` Mauro Carvalho Chehab
  2016-03-14 15:23 ` [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree Lucas Stach
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

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 | 282 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 223 insertions(+), 59 deletions(-)

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 6ba93a425640..67312c9d83c1 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);
+
+	/* Calculate height based on current standard */
+	if (std & V4L2_STD_525_60)
+		hmax = TVP5150_V_MAX_525_60;
+	else
+		hmax = TVP5150_V_MAX_OTHERS;
 
-	tvp5150_reset(sd, 0);
+	rect->height = clamp(rect->height,
+			     hmax - TVP5150_MAX_CROP_TOP - rect->top,
+			     hmax - rect->top);
+}
 
-	f->width = decoder->rect.width;
-	f->height = decoder->rect.height;
+static void tvp5150_set_crop(struct tvp5150 *decoder, struct v4l2_rect *rect,
+			       v4l2_std_id std)
+{
+	struct regmap *map = decoder->regmap;
+	unsigned int hmax;
 
-	f->code = MEDIA_BUS_FMT_UYVY8_2X8;
-	f->field = V4L2_FIELD_SEQ_TB;
-	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	if (std & V4L2_STD_525_60)
+		hmax = TVP5150_V_MAX_525_60;
+	else
+		hmax = TVP5150_V_MAX_OTHERS;
 
-	v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
-			f->height);
-	return 0;
+	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,153 @@ 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) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(sd, cfg, pad);
+#endif
+	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) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(sd, cfg, pad);
+#endif
+	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);
+	struct v4l2_mbus_framefmt *mbus_format;
+
+	mbus_format = tvp5150_get_pad_format(decoder, sd, cfg,
+					     format->pad, format->which);
+	if (!mbus_format)
+		return -ENOTTY;
+
+	format->format = *mbus_format;
+
+	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);
+	if (!crop || !mbus_format)
+		return -ENOTTY;
+
+	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;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+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;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
 static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
 	.s_ctrl = tvp5150_s_ctrl,
 };
@@ -1083,8 +1233,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 +1246,11 @@ static const struct v4l2_subdev_ops tvp5150_ops = {
 	.pad = &tvp5150_pad_ops,
 };
 
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
+	.open = tvp5150_open,
+};
+#endif
 
 /****************************************************************************
 			I2C Client & Driver
@@ -1197,6 +1353,19 @@ static int tvp5150_probe(struct i2c_client *c,
 	sd = &core->sd;
 	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
 
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &tvp5150_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+#endif
+#ifdef CONFIG_MEDIA_CONTROLLER
+	sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
+	core->pad.flags = MEDIA_PAD_FL_SOURCE;
+	res = media_entity_pads_init(&sd->entity, 1, &core->pad);
+	if (res < 0)
+		return res;
+#endif
+
 	/* 
 	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
 	 * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER 
@@ -1250,14 +1419,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;
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 18:19   ` Javier Martinez Canillas
  2016-03-14 15:23 ` [PATCH v3 4/9] [media] tvp5150: fix standard autodetection Lucas Stach
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

By looking at the endpoint flags, it can be determined whether the link
should be of V4L2_MBUS_PARALLEL or V4L2_MBUS_BT656 type. Disable the
dedicated HSYNC/VSYNC outputs in BT.656 mode.

For devices that are not instantiated through DT the current behavior
is preserved.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 67312c9d83c1..f6720d1d09ea 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -11,10 +11,12 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
+#include <linux/of_graph.h>
 #include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
 #include <media/i2c/tvp5150.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-of.h>
 
 #include "tvp5150_reg.h"
 
@@ -38,6 +40,7 @@ struct tvp5150 {
 	struct v4l2_subdev sd;
 	struct media_pad pad;
 	struct v4l2_ctrl_handler hdl;
+	enum v4l2_mbus_type bus_type;
 	struct v4l2_mbus_framefmt format;
 	struct v4l2_rect rect;
 	struct regmap *regmap;
@@ -424,8 +427,6 @@ static const struct i2c_reg_value tvp5150_init_enable[] = {
 		TVP5150_MISC_CTL, 0x6f
 	},{	/* Activates video std autodetection for all standards */
 		TVP5150_AUTOSW_MSK, 0x0
-	},{	/* Default format: 0x47. For 4:2:2: 0x40 */
-		TVP5150_DATA_RATE_SEL, 0x47
 	},{
 		TVP5150_CHROMA_PROC_CTL_1, 0x0c
 	},{
@@ -760,6 +761,25 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 	/* Initializes TVP5150 to stream enabled values */
 	tvp5150_write_inittab(sd, tvp5150_init_enable);
 
+	switch (decoder->bus_type) {
+	case V4L2_MBUS_BT656:
+		/* 8-bit ITU BT.656 */
+		regmap_update_bits(decoder->regmap, TVP5150_DATA_RATE_SEL,
+				   0x7, 0x7);
+		/* disable HSYNC, VSYNC/PALI, AVID, and FID/GLCO */
+		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0x4, 0x0);
+		break;
+	case V4L2_MBUS_PARALLEL:
+		/* 8-bit YUV 4:2:2 */
+		regmap_update_bits(decoder->regmap, TVP5150_DATA_RATE_SEL,
+				   0x7, 0x0);
+		/* enable HSYNC, VSYNC/PALI, AVID, and FID/GLCO */
+		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0x4, 0x4);
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	/* Initialize image preferences */
 	v4l2_ctrl_handler_setup(&decoder->hdl);
 
@@ -1332,6 +1352,8 @@ static struct regmap_config tvp5150_config = {
 static int tvp5150_probe(struct i2c_client *c,
 			 const struct i2c_device_id *id)
 {
+	struct v4l2_of_endpoint bus_cfg;
+	struct device_node *endpoint;
 	struct tvp5150 *core;
 	struct v4l2_subdev *sd;
 	struct regmap *map;
@@ -1398,6 +1420,14 @@ static int tvp5150_probe(struct i2c_client *c,
 		}
 	}
 
+	endpoint = of_graph_get_next_endpoint(c->dev.of_node, NULL);
+	if (endpoint) {
+		v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+		core->bus_type = bus_cfg.bus_type;
+	} else {
+		core->bus_type = V4L2_MBUS_BT656;
+	}
+
 	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
 	core->input = TVP5150_COMPOSITE1;
 	core->enable = 1;
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 4/9] [media] tvp5150: fix standard autodetection
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 5/9] [media] tvp5150: split reset/enable routine Lucas Stach
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

Make sure to not overwrite decoder->norm when setting the standard
in hardware, but only when instructed by V4L2 API calls.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index f6720d1d09ea..21d044b564ad 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -703,8 +703,6 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
 	struct tvp5150 *decoder = to_tvp5150(sd);
 	int fmt = 0;
 
-	decoder->norm = std;
-
 	/* First tests should be against specific std */
 
 	if (std == V4L2_STD_NTSC_443) {
@@ -741,13 +739,37 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 	else
 		decoder->rect.height = TVP5150_V_MAX_OTHERS;
 
+	decoder->norm = std;
 
 	return tvp5150_set_std(sd, std);
 }
 
+static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
+{
+	int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
+
+	switch (val & 0x0F) {
+	case 0x01:
+		return V4L2_STD_NTSC;
+	case 0x03:
+		return V4L2_STD_PAL;
+	case 0x05:
+		return V4L2_STD_PAL_M;
+	case 0x07:
+		return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
+	case 0x09:
+		return V4L2_STD_NTSC_443;
+	case 0xb:
+		return V4L2_STD_SECAM;
+	default:
+		return V4L2_STD_UNKNOWN;
+	}
+}
+
 static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 {
 	struct tvp5150 *decoder = to_tvp5150(sd);
+	v4l2_std_id std;
 
 	/* Initializes TVP5150 to its default values */
 	tvp5150_write_inittab(sd, tvp5150_init_default);
@@ -783,7 +805,13 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 	/* Initialize image preferences */
 	v4l2_ctrl_handler_setup(&decoder->hdl);
 
-	tvp5150_set_std(sd, decoder->norm);
+	if (decoder->norm == V4L2_STD_ALL)
+		std = tvp5150_read_std(sd);
+	else
+		std = decoder->norm;
+
+	/* Disable autoswitch mode */
+	tvp5150_set_std(sd, std);
 	return 0;
 };
 
@@ -808,28 +836,6 @@ static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
 	return -EINVAL;
 }
 
-static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
-{
-	int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
-
-	switch (val & 0x0F) {
-	case 0x01:
-		return V4L2_STD_NTSC;
-	case 0x03:
-		return V4L2_STD_PAL;
-	case 0x05:
-		return V4L2_STD_PAL_M;
-	case 0x07:
-		return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
-	case 0x09:
-		return V4L2_STD_NTSC_443;
-	case 0xb:
-		return V4L2_STD_SECAM;
-	default:
-		return V4L2_STD_UNKNOWN;
-	}
-}
-
 static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
 		struct v4l2_subdev_pad_config *cfg,
 		struct v4l2_subdev_mbus_code_enum *code)
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 5/9] [media] tvp5150: split reset/enable routine
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
                   ` (2 preceding siblings ...)
  2016-03-14 15:23 ` [PATCH v3 4/9] [media] tvp5150: fix standard autodetection Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 6/9] [media] tvp5150: trigger autodetection on subdev open to reset cropping Lucas Stach
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

To trigger standard autodetection only the reset part of the routine
is necessary. Split this out to make it callable on its own.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 21d044b564ad..f5e6bfa9dd7f 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -768,9 +768,6 @@ static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
 
 static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 {
-	struct tvp5150 *decoder = to_tvp5150(sd);
-	v4l2_std_id std;
-
 	/* Initializes TVP5150 to its default values */
 	tvp5150_write_inittab(sd, tvp5150_init_default);
 
@@ -780,6 +777,14 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 	/* Selects decoder input */
 	tvp5150_selmux(sd);
 
+	return 0;
+}
+
+static int tvp5150_enable(struct v4l2_subdev *sd)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	v4l2_std_id std;
+
 	/* Initializes TVP5150 to stream enabled values */
 	tvp5150_write_inittab(sd, tvp5150_init_enable);
 
@@ -844,6 +849,7 @@ static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
 		return -EINVAL;
 
 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
 	return 0;
 }
 
@@ -1179,8 +1185,10 @@ static int tvp5150_set_format(struct v4l2_subdev *sd,
 
 	format->format = *mbus_format;
 
-	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		tvp5150_reset(sd, 0);
+		tvp5150_enable(sd);
+	}
 
 	v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", mbus_format->width,
 			mbus_format->height);
@@ -1454,6 +1462,7 @@ static int tvp5150_probe(struct i2c_client *c,
 	}
 	v4l2_ctrl_handler_setup(&core->hdl);
 
+	tvp5150_reset(sd, 0);
 	/* Default is no cropping */
 	tvp5150_set_default(tvp5150_read_std(sd), &core->rect, &core->format);
 
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 6/9] [media] tvp5150: trigger autodetection on subdev open to reset cropping
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
                   ` (3 preceding siblings ...)
  2016-03-14 15:23 ` [PATCH v3 5/9] [media] tvp5150: split reset/enable routine Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 7/9] [media] tvp5150: remove pin configuration from initialization tables Lucas Stach
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

If cropping isn't set explicitly by userspace, reset it to the maximum
possible rectangle in subdevice open if a standard change is detected.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index f5e6bfa9dd7f..d0b5e148dcd8 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -46,6 +46,7 @@ struct tvp5150 {
 	struct regmap *regmap;
 
 	v4l2_std_id norm;	/* Current set standard */
+	v4l2_std_id detected_norm;
 	u32 input;
 	u32 output;
 	int enable;
@@ -1220,13 +1221,19 @@ 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)
+	if (decoder->norm == V4L2_STD_ALL) {
 		std = tvp5150_read_std(sd);
-	else
-		std = decoder->norm;
+		if (std != decoder->detected_norm) {
+			decoder->detected_norm = std;
+
+			if (std & V4L2_STD_525_60)
+				decoder->rect.height = TVP5150_V_MAX_525_60;
+			else
+				decoder->rect.height = TVP5150_V_MAX_OTHERS;
+			decoder->format.height = decoder->rect.height;
+		}
+	}
 
-	tvp5150_set_default(std, v4l2_subdev_get_try_crop(fh, 0),
-				 v4l2_subdev_get_try_format(fh, 0));
 	return 0;
 }
 #endif
@@ -1443,6 +1450,7 @@ static int tvp5150_probe(struct i2c_client *c,
 	}
 
 	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
+	core->detected_norm = V4L2_STD_UNKNOWN;
 	core->input = TVP5150_COMPOSITE1;
 	core->enable = 1;
 
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 7/9] [media] tvp5150: remove pin configuration from initialization tables
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
                   ` (4 preceding siblings ...)
  2016-03-14 15:23 ` [PATCH v3 6/9] [media] tvp5150: trigger autodetection on subdev open to reset cropping Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 8/9] [media] tvp5150: Add sync lock interrupt handling Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 9/9] [media] tvp5150: disable output while signal not locked Lucas Stach
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

To allow optional interrupt support, we want to configure the pin settings
dynamically. Move those register accesses out of the static initialization
tables.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/media/i2c/tvp5150.c     | 19 +++++++------------
 drivers/media/i2c/tvp5150_reg.h |  1 +
 2 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index d0b5e148dcd8..e0f5bc219ced 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -323,9 +323,6 @@ static const struct i2c_reg_value tvp5150_init_default[] = {
 	{ /* 0x0e */
 		TVP5150_LUMA_PROC_CTL_3,0x00
 	},
-	{ /* 0x0f */
-		TVP5150_CONF_SHARED_PIN,0x08
-	},
 	{ /* 0x11 */
 		TVP5150_ACT_VD_CROP_ST_MSB,0x00
 	},
@@ -362,9 +359,6 @@ static const struct i2c_reg_value tvp5150_init_default[] = {
 	{ /* 0x1d */
 		TVP5150_INT_ENABLE_REG_B,0x00
 	},
-	{ /* 0x1e */
-		TVP5150_INTT_CONFIG_REG_B,0x00
-	},
 	{ /* 0x28 */
 		TVP5150_VIDEO_STD,0x00
 	},
@@ -383,9 +377,6 @@ static const struct i2c_reg_value tvp5150_init_default[] = {
 	{ /* 0xc1 */
 		TVP5150_INT_ENABLE_REG_A,0x00
 	},
-	{ /* 0xc2 */
-		TVP5150_INT_CONF,0x04
-	},
 	{ /* 0xc8 */
 		TVP5150_FIFO_INT_THRESHOLD,0x80
 	},
@@ -420,9 +411,7 @@ static const struct i2c_reg_value tvp5150_init_default[] = {
 
 /* Default values as sugested at TVP5150AM1 datasheet */
 static const struct i2c_reg_value tvp5150_init_enable[] = {
-	{
-		TVP5150_CONF_SHARED_PIN, 2
-	},{	/* Automatic offset and AGC enabled */
+	{	/* Automatic offset and AGC enabled */
 		TVP5150_ANAL_CHL_CTL, 0x15
 	},{	/* Activate YCrCb output 0x9 or 0xd ? */
 		TVP5150_MISC_CTL, 0x6f
@@ -772,6 +761,12 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 	/* Initializes TVP5150 to its default values */
 	tvp5150_write_inittab(sd, tvp5150_init_default);
 
+	/* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
+	regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
+	/* Keep interrupt polarity active low */
+	regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
+	regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
+
 	/* Initializes VDP registers */
 	tvp5150_vdp_init(sd, vbi_ram_default);
 
diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h
index 25a994944918..fc3bcb26413a 100644
--- a/drivers/media/i2c/tvp5150_reg.h
+++ b/drivers/media/i2c/tvp5150_reg.h
@@ -117,6 +117,7 @@
 #define TVP5150_INT_STATUS_REG_A    0xc0 /* Interrupt status register A */
 #define TVP5150_INT_ENABLE_REG_A    0xc1 /* Interrupt enable register A */
 #define TVP5150_INT_CONF            0xc2 /* Interrupt configuration */
+#define   TVP5150_VDPOE             BIT(2)
 #define TVP5150_VDP_CONF_RAM_DATA   0xc3 /* VDP configuration RAM data */
 #define TVP5150_CONF_RAM_ADDR_LOW   0xc4 /* Configuration RAM address low byte */
 #define TVP5150_CONF_RAM_ADDR_HIGH  0xc5 /* Configuration RAM address high byte */
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 8/9] [media] tvp5150: Add sync lock interrupt handling
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
                   ` (5 preceding siblings ...)
  2016-03-14 15:23 ` [PATCH v3 7/9] [media] tvp5150: remove pin configuration from initialization tables Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  2016-03-14 15:23 ` [PATCH v3 9/9] [media] tvp5150: disable output while signal not locked Lucas Stach
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

This patch adds an optional interrupt handler to handle the sync
lock interrupt and sync lock status.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/media/i2c/tvp5150.c     | 103 ++++++++++++++++++++++++++++++++++++++--
 drivers/media/i2c/tvp5150_reg.h |   2 +
 2 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index e0f5bc219ced..b5140253b648 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -9,6 +9,7 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
 #include <linux/of_graph.h>
@@ -44,12 +45,14 @@ struct tvp5150 {
 	struct v4l2_mbus_framefmt format;
 	struct v4l2_rect rect;
 	struct regmap *regmap;
+	int irq;
 
 	v4l2_std_id norm;	/* Current set standard */
 	v4l2_std_id detected_norm;
 	u32 input;
 	u32 output;
 	int enable;
+	bool lock;
 };
 
 static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
@@ -716,6 +719,15 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
 	return 0;
 }
 
+static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct tvp5150 *decoder = to_tvp5150(sd);
+
+	*std = decoder->norm;
+
+	return 0;
+}
+
 static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
 	struct tvp5150 *decoder = to_tvp5150(sd);
@@ -758,14 +770,25 @@ static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
 
 static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 {
+	struct tvp5150 *decoder = to_tvp5150(sd);
+	struct regmap *map = decoder->regmap;
+
 	/* Initializes TVP5150 to its default values */
 	tvp5150_write_inittab(sd, tvp5150_init_default);
 
-	/* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
-	regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
-	/* Keep interrupt polarity active low */
-	regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
-	regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
+	if (decoder->irq) {
+		/* Configure pins: FID, VSYNC, INTREQ, SCLK */
+		regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x0);
+		/* Set interrupt polarity to active high */
+		regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE | 0x1);
+		regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x1);
+	} else {
+		/* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
+		regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
+		/* Keep interrupt polarity active low */
+		regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
+		regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
+	}
 
 	/* Initializes VDP registers */
 	tvp5150_vdp_init(sd, vbi_ram_default);
@@ -776,6 +799,33 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
 	return 0;
 }
 
+static irqreturn_t tvp5150_isr(int irq, void *dev_id)
+{
+	struct tvp5150 *decoder = dev_id;
+	struct regmap *map = decoder->regmap;
+	unsigned int active = 0, status = 0;
+
+	regmap_read(map, TVP5150_INT_STATUS_REG_A, &status);
+	if (status) {
+		regmap_write(map, TVP5150_INT_STATUS_REG_A, status);
+
+		if (status & TVP5150_INT_A_LOCK)
+			decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS);
+
+		return IRQ_HANDLED;
+	}
+
+	regmap_read(map, TVP5150_INT_ACTIVE_REG_B, &active);
+	if (active) {
+		status = 0;
+		regmap_read(map, TVP5150_INT_STATUS_REG_B, &status);
+		if (status)
+			regmap_write(map, TVP5150_INT_RESET_REG_B, status);
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int tvp5150_enable(struct v4l2_subdev *sd)
 {
 	struct tvp5150 *decoder = to_tvp5150(sd);
@@ -939,6 +989,35 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	return 0;
 }
 
+static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+
+	if (enable) {
+		/* Enable YUV(OUT7:0), clock */
+		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0xd,
+			(decoder->bus_type == V4L2_MBUS_BT656) ? 0x9 : 0xd);
+		if (decoder->irq) {
+			/* Enable lock interrupt */
+			regmap_update_bits(decoder->regmap,
+					   TVP5150_INT_ENABLE_REG_A,
+					   TVP5150_INT_A_LOCK,
+					   TVP5150_INT_A_LOCK);
+		}
+	} else {
+		/* Disable YUV(OUT7:0), SYNC, clock */
+		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0xd, 0x0);
+		if (decoder->irq) {
+			/* Disable lock interrupt */
+			regmap_update_bits(decoder->regmap,
+					   TVP5150_INT_ENABLE_REG_A,
+					   TVP5150_INT_A_LOCK, 0);
+		}
+	}
+
+	return 0;
+}
+
 static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
 	struct tvp5150 *decoder = to_tvp5150(sd);
@@ -1254,9 +1333,11 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
 
 static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
 	.s_std = tvp5150_s_std,
+	.g_std = tvp5150_g_std,
 	.s_routing = tvp5150_s_routing,
 	.s_crop = tvp5150_s_crop,
 	.g_crop = tvp5150_g_crop,
+	.s_stream = tvp5150_s_stream,
 	.cropcap = tvp5150_cropcap,
 };
 
@@ -1465,7 +1546,19 @@ static int tvp5150_probe(struct i2c_client *c,
 	}
 	v4l2_ctrl_handler_setup(&core->hdl);
 
+	core->irq = c->irq;
 	tvp5150_reset(sd, 0);
+
+	if (c->irq) {
+		res = devm_request_threaded_irq(&c->dev, c->irq, NULL,
+				tvp5150_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				"tvp5150", core);
+		if (res)
+			return res;
+	} else {
+		core->lock = true;
+	}
+
 	/* Default is no cropping */
 	tvp5150_set_default(tvp5150_read_std(sd), &core->rect, &core->format);
 
diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h
index fc3bcb26413a..282a8a852e45 100644
--- a/drivers/media/i2c/tvp5150_reg.h
+++ b/drivers/media/i2c/tvp5150_reg.h
@@ -115,6 +115,8 @@
 #define TVP5150_TELETEXT_FIL_ENA    0xbb /* Teletext filter enable */
 /* Reserved	BCh-BFh */
 #define TVP5150_INT_STATUS_REG_A    0xc0 /* Interrupt status register A */
+#define   TVP5150_INT_A_LOCK_STATUS BIT(7)
+#define   TVP5150_INT_A_LOCK        BIT(6)
 #define TVP5150_INT_ENABLE_REG_A    0xc1 /* Interrupt enable register A */
 #define TVP5150_INT_CONF            0xc2 /* Interrupt configuration */
 #define   TVP5150_VDPOE             BIT(2)
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 9/9] [media] tvp5150: disable output while signal not locked
  2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
                   ` (6 preceding siblings ...)
  2016-03-14 15:23 ` [PATCH v3 8/9] [media] tvp5150: Add sync lock interrupt handling Lucas Stach
@ 2016-03-14 15:23 ` Lucas Stach
  7 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-14 15:23 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, linux-media; +Cc: kernel, patchwork-lst

From: Philipp Zabel <p.zabel@pengutronix.de>

To avoid short frames on stream start, keep output pins at high impedance
while we are not properly locked onto the input signal.

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

diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index b5140253b648..13ce826a4093 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -51,6 +51,7 @@ struct tvp5150 {
 	v4l2_std_id detected_norm;
 	u32 input;
 	u32 output;
+	u32 oe;
 	int enable;
 	bool lock;
 };
@@ -809,8 +810,11 @@ static irqreturn_t tvp5150_isr(int irq, void *dev_id)
 	if (status) {
 		regmap_write(map, TVP5150_INT_STATUS_REG_A, status);
 
-		if (status & TVP5150_INT_A_LOCK)
+		if (status & TVP5150_INT_A_LOCK) {
 			decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS);
+			regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL,
+					   0xd, decoder->lock ? decoder->oe : 0);
+		}
 
 		return IRQ_HANDLED;
 	}
@@ -841,6 +845,7 @@ static int tvp5150_enable(struct v4l2_subdev *sd)
 				   0x7, 0x7);
 		/* disable HSYNC, VSYNC/PALI, AVID, and FID/GLCO */
 		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0x4, 0x0);
+		decoder->oe = 0x9;
 		break;
 	case V4L2_MBUS_PARALLEL:
 		/* 8-bit YUV 4:2:2 */
@@ -848,6 +853,7 @@ static int tvp5150_enable(struct v4l2_subdev *sd)
 				   0x7, 0x0);
 		/* enable HSYNC, VSYNC/PALI, AVID, and FID/GLCO */
 		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0x4, 0x4);
+		decoder->oe = 0xd;
 		break;
 	default:
 		return -EINVAL;
@@ -994,9 +1000,9 @@ static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
 	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
 
 	if (enable) {
-		/* Enable YUV(OUT7:0), clock */
+		/* Enable YUV(OUT7:0), (SYNC), clock signal, if locked */
 		regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0xd,
-			(decoder->bus_type == V4L2_MBUS_BT656) ? 0x9 : 0xd);
+				   decoder->lock ? decoder->oe : 0);
 		if (decoder->irq) {
 			/* Enable lock interrupt */
 			regmap_update_bits(decoder->regmap,
-- 
2.7.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 2/9] [media] tvp5150: add userspace subdev API
  2016-03-14 15:23 ` [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Lucas Stach
@ 2016-03-14 17:34   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-03-14 17:34 UTC (permalink / raw)
  To: Lucas Stach; +Cc: linux-media, kernel, patchwork-lst

Em Mon, 14 Mar 2016 16:23:30 +0100
Lucas Stach <l.stach@pengutronix.de> escreveu:

> 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 | 282 +++++++++++++++++++++++++++++++++++---------
>  1 file changed, 223 insertions(+), 59 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index 6ba93a425640..67312c9d83c1 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;

Huh? tvp5150 has already pads on it:

struct tvp5150 {
	struct v4l2_subdev sd;
#ifdef CONFIG_MEDIA_CONTROLLER
	struct media_pad pads[DEMOD_NUM_PADS];
	struct media_entity input_ent[TVP5150_INPUT_NUM];
	struct media_pad input_pad[TVP5150_INPUT_NUM];
#endif
	struct v4l2_ctrl_handler hdl;
	struct v4l2_rect rect;

	v4l2_std_id norm;	/* Current set standard */
	u32 input;
	u32 output;
	int enable;

	u16 dev_id;
	u16 rom_ver;

	enum v4l2_mbus_type mbus_type;
};

It seems that your patchset is not based on the latest version of
the driver. Please rebase it on the top of the media tree:

	https://git.linuxtv.org/media_tree.git/

Regards,
Mauro

>  	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);
> +
> +	/* Calculate height based on current standard */
> +	if (std & V4L2_STD_525_60)
> +		hmax = TVP5150_V_MAX_525_60;
> +	else
> +		hmax = TVP5150_V_MAX_OTHERS;
>  
> -	tvp5150_reset(sd, 0);
> +	rect->height = clamp(rect->height,
> +			     hmax - TVP5150_MAX_CROP_TOP - rect->top,
> +			     hmax - rect->top);
> +}
>  
> -	f->width = decoder->rect.width;
> -	f->height = decoder->rect.height;
> +static void tvp5150_set_crop(struct tvp5150 *decoder, struct v4l2_rect *rect,
> +			       v4l2_std_id std)
> +{
> +	struct regmap *map = decoder->regmap;
> +	unsigned int hmax;
>  
> -	f->code = MEDIA_BUS_FMT_UYVY8_2X8;
> -	f->field = V4L2_FIELD_SEQ_TB;
> -	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	if (std & V4L2_STD_525_60)
> +		hmax = TVP5150_V_MAX_525_60;
> +	else
> +		hmax = TVP5150_V_MAX_OTHERS;
>  
> -	v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
> -			f->height);
> -	return 0;
> +	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,153 @@ 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) {
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(sd, cfg, pad);
> +#endif
> +	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) {
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_crop(sd, cfg, pad);
> +#endif
> +	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);
> +	struct v4l2_mbus_framefmt *mbus_format;
> +
> +	mbus_format = tvp5150_get_pad_format(decoder, sd, cfg,
> +					     format->pad, format->which);
> +	if (!mbus_format)
> +		return -ENOTTY;
> +
> +	format->format = *mbus_format;
> +
> +	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);
> +	if (!crop || !mbus_format)
> +		return -ENOTTY;
> +
> +	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;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +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;
> +}
> +#endif
> +
> +/* ----------------------------------------------------------------------- */
> +
>  static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
>  	.s_ctrl = tvp5150_s_ctrl,
>  };
> @@ -1083,8 +1233,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 +1246,11 @@ static const struct v4l2_subdev_ops tvp5150_ops = {
>  	.pad = &tvp5150_pad_ops,
>  };
>  
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
> +	.open = tvp5150_open,
> +};
> +#endif
>  
>  /****************************************************************************
>  			I2C Client & Driver
> @@ -1197,6 +1353,19 @@ static int tvp5150_probe(struct i2c_client *c,
>  	sd = &core->sd;
>  	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
>  
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	sd->internal_ops = &tvp5150_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +#endif
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
> +	core->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	res = media_entity_pads_init(&sd->entity, 1, &core->pad);
> +	if (res < 0)
> +		return res;
> +#endif
> +
>  	/* 
>  	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
>  	 * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER 
> @@ -1250,14 +1419,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;


-- 
Thanks,
Mauro

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree
  2016-03-14 15:23 ` [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree Lucas Stach
@ 2016-03-14 18:19   ` Javier Martinez Canillas
  2016-03-15 10:26     ` Lucas Stach
  0 siblings, 1 reply; 12+ messages in thread
From: Javier Martinez Canillas @ 2016-03-14 18:19 UTC (permalink / raw)
  To: Lucas Stach
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List, Sascha Hauer,
	patchwork-lst

Hello Lucas,

On Mon, Mar 14, 2016 at 12:23 PM, Lucas Stach <l.stach@pengutronix.de> wrote:
> From: Philipp Zabel <p.zabel@pengutronix.de>
>
> By looking at the endpoint flags, it can be determined whether the link
> should be of V4L2_MBUS_PARALLEL or V4L2_MBUS_BT656 type. Disable the
> dedicated HSYNC/VSYNC outputs in BT.656 mode.
>
> For devices that are not instantiated through DT the current behavior
> is preserved.
>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> ---

Similar to Mauro's comment on patch 2/9, the current driver already
supports configuring the interface output format using DT.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree
  2016-03-14 18:19   ` Javier Martinez Canillas
@ 2016-03-15 10:26     ` Lucas Stach
  0 siblings, 0 replies; 12+ messages in thread
From: Lucas Stach @ 2016-03-15 10:26 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Linux Media Mailing List, patchwork-lst, Sascha Hauer,
	Mauro Carvalho Chehab

Am Montag, den 14.03.2016, 15:19 -0300 schrieb Javier Martinez Canillas:
> Hello Lucas,
> 
> On Mon, Mar 14, 2016 at 12:23 PM, Lucas Stach <l.stach@pengutronix.de> wrote:
> > From: Philipp Zabel <p.zabel@pengutronix.de>
> >
> > By looking at the endpoint flags, it can be determined whether the link
> > should be of V4L2_MBUS_PARALLEL or V4L2_MBUS_BT656 type. Disable the
> > dedicated HSYNC/VSYNC outputs in BT.656 mode.
> >
> > For devices that are not instantiated through DT the current behavior
> > is preserved.
> >
> > Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> > Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> > ---
> 
> Similar to Mauro's comment on patch 2/9, the current driver already
> supports configuring the interface output format using DT.

Sorry about that. I've lost touch with the current media tree, will look
into what is already there. Sorry for the noise.



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-03-15 10:26 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-14 15:23 [PATCH v3 1/9] [media] tvp5150: convert register access to regmap Lucas Stach
2016-03-14 15:23 ` [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Lucas Stach
2016-03-14 17:34   ` Mauro Carvalho Chehab
2016-03-14 15:23 ` [PATCH v3 3/9] [media] tvp5150: determine BT.656 or YUV 4:2:2 mode from device tree Lucas Stach
2016-03-14 18:19   ` Javier Martinez Canillas
2016-03-15 10:26     ` Lucas Stach
2016-03-14 15:23 ` [PATCH v3 4/9] [media] tvp5150: fix standard autodetection Lucas Stach
2016-03-14 15:23 ` [PATCH v3 5/9] [media] tvp5150: split reset/enable routine Lucas Stach
2016-03-14 15:23 ` [PATCH v3 6/9] [media] tvp5150: trigger autodetection on subdev open to reset cropping Lucas Stach
2016-03-14 15:23 ` [PATCH v3 7/9] [media] tvp5150: remove pin configuration from initialization tables Lucas Stach
2016-03-14 15:23 ` [PATCH v3 8/9] [media] tvp5150: Add sync lock interrupt handling Lucas Stach
2016-03-14 15:23 ` [PATCH v3 9/9] [media] tvp5150: disable output while signal not locked Lucas Stach

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).