From patchwork Mon Mar 14 15:23:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 8580861 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 23E02C0553 for ; Mon, 14 Mar 2016 15:23:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C730A20274 for ; Mon, 14 Mar 2016 15:23:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4B968203B7 for ; Mon, 14 Mar 2016 15:23:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934555AbcCNPXs (ORCPT ); Mon, 14 Mar 2016 11:23:48 -0400 Received: from metis.ext.4.pengutronix.de ([92.198.50.35]:33071 "EHLO metis.ext.4.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934547AbcCNPXj (ORCPT ); Mon, 14 Mar 2016 11:23:39 -0400 Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7] helo=dude.pengutronix.de.) by metis.ext.pengutronix.de with esmtp (Exim 4.80) (envelope-from ) id 1afULJ-0003LL-Ry; Mon, 14 Mar 2016 16:23:37 +0100 From: Lucas Stach To: Mauro Carvalho Chehab , linux-media@vger.kernel.org Cc: kernel@pengutronix.de, patchwork-lst@pengutronix.de Subject: [PATCH v3 2/9] [media] tvp5150: add userspace subdev API Date: Mon, 14 Mar 2016 16:23:30 +0100 Message-Id: <1457969017-4088-2-git-send-email-l.stach@pengutronix.de> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1457969017-4088-1-git-send-email-l.stach@pengutronix.de> References: <1457969017-4088-1-git-send-email-l.stach@pengutronix.de> X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: l.stach@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-media@vger.kernel.org Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Philipp Zabel This patch adds userspace V4L2 subdevice API support. Signed-off-by: Philipp Zabel Signed-off-by: Lucas Stach --- 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;