From patchwork Mon Aug 29 17:28:56 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Enrico X-Patchwork-Id: 1109312 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p7THrWSh008158 for ; Mon, 29 Aug 2011 17:53:37 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754199Ab1H2R26 (ORCPT ); Mon, 29 Aug 2011 13:28:58 -0400 Received: from mail-yi0-f46.google.com ([209.85.218.46]:55487 "EHLO mail-yi0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750948Ab1H2R25 (ORCPT ); Mon, 29 Aug 2011 13:28:57 -0400 Received: by yie30 with SMTP id 30so3713675yie.19 for ; Mon, 29 Aug 2011 10:28:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:sender:date:x-google-sender-auth:message-id:subject :from:to:cc:content-type; bh=q4TcHFBUvZwPq7Bop8lS/DKBcVUnhV/UoG24PMinInk=; b=b794GymrTJA6SzRz81NOYfsO7wh93TjLbUzEleZ9m/ytnk4+Y3NCpDmm5Ykaf2DR6u hc21reuk/6hovlBatzIFEIZ/VQMMhcZ7QFgAgOFt3JoR9PKy9FNjszpJGIuMjaBMyHuj zJMG//iWe7jMrPSRjLnTpsjB0MRODRl13ZUpk= MIME-Version: 1.0 Received: by 10.236.197.104 with SMTP id s68mr27206734yhn.20.1314638936635; Mon, 29 Aug 2011 10:28:56 -0700 (PDT) Received: by 10.236.95.46 with HTTP; Mon, 29 Aug 2011 10:28:56 -0700 (PDT) Date: Mon, 29 Aug 2011 19:28:56 +0200 X-Google-Sender-Auth: 3U8QL1JDS-yjmReS-d2m17_wm00 Message-ID: Subject: [PATCH/WIP] tvp5150: add v4l2 subdev pad ops From: Enrico To: linux-media@vger.kernel.org Cc: Enric Balletbo i Serra , Mauro Carvalho Chehab Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Mon, 29 Aug 2011 17:53:37 +0000 (UTC) Hi, attached is a patch (i tested it with kernel 3.1rc3 but applies to 2.6.39 and 3.0 too) that makes the driver register with omap3-isp. Copied mostly from mt9v032.c. /dev/video* and v4l-subdev* are registered correctly. Video capture is still NOT working but i think i'm close to make it work so some help is welcome! I'm testing it on an IGEP board with tvp5151 on expansion board. Issues i see: root@igep0020:~# ./media-ctl2 --set-format '"tvp5150 2-005c":0 [UYVY 720x628]' Setting up format UYVY 720x628 on pad tvp5150 2-005c/0 Format set: UYVY 720x628 Setting up format UYVY 720x628 on pad OMAP3 ISP CCDC/0 Format set: SGRBG10 720x628 and root@igep0020:~# gst-launch -ve v4l2src device=/dev/video2 queue-size=1 num-buffers=250 ! fakesink Setting pipeline to PAUSED ... ERROR: Pipeline doesn't want to pause. ERROR: from element /GstPipeline:pipeline0/GstV4l2Src:v4l2src0: Failed getting controls attributes on device '/dev/video2'. Additional debug info: v4l2_calls.c(267): gst_v4l2_fill_lists (): /GstPipeline:pipeline0/GstV4l2Src:v4l2src0: Failed querying control 9963776 on device '/dev/video2'. (25 - Inappropriate ioctl for device) Setting pipeline to NULL ... Freeing pipeline ... Maybe the gstreamer issue could be caused by not being compiled with the proper kernel headers. media-ctl is compiled from latest ideasonboard git repo, i admit i am not that sure that i compiled it with correct kernel headers. I don't know why but every format i try to set on CCDC is always set to SGRBG10. I'm not sure that some parameters are correct (like V4L2_FILED_NONE, colorspace, pixel format...), so i need someone to take a look. Are the subdev_internal_ops (registered, open, close) needed? I know the patch is not properly formatted for submission, i'll do that when capture will work. Thanks, Enrico diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index e927d25..bf34e11 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -2,6 +2,7 @@ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver * * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2011 Enrico Butera (ebutera@users.berlios.de) * This code is placed under the terms of the GNU General Public License v2 */ @@ -13,9 +14,30 @@ #include #include #include +#include +#include #include "tvp5150_reg.h" +#define TVP5150_PIXEL_ARRAY_HEIGHT 628 +#define TVP5150_PIXEL_ARRAY_WIDTH 720 + +#define TVP5150_WINDOW_WIDTH_MIN 1 +#define TVP5150_WINDOW_WIDTH_DEF 720 +#define TVP5150_WINDOW_WIDTH_MAX 720 + +#define TVP5150_WINDOW_HEIGHT_MIN 1 +#define TVP5150_WINDOW_HEIGHT_DEF 628 +#define TVP5150_WINDOW_HEIGHT_MAX 628 + +#define TVP5150_COLUMN_START_MIN 0 +#define TVP5150_COLUMN_START_DEF 0 +#define TVP5150_COLUMN_START_MAX 720 + +#define TVP5150_ROW_START_MIN 0 +#define TVP5150_ROW_START_DEF 0 +#define TVP5150_ROW_START_MAX 628 + MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); @@ -28,6 +50,9 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); struct tvp5150 { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; v4l2_std_id norm; /* Current set standard */ u32 input; @@ -779,6 +804,203 @@ static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) } /**************************************************************************** + V4L2 subdev pad ops + ****************************************************************************/ + +static struct v4l2_mbus_framefmt * +__tvp5150_get_pad_format(struct tvp5150 *tvp5150, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &tvp5150->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__tvp5150_get_pad_crop(struct tvp5150 *tvp5150, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &tvp5150->crop; + default: + return NULL; + } +} + +static int tvp5150_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_UYVY8_1X16; + return 0; +} + +static int tvp5150_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_UYVY8_1X16) + return -EINVAL; + + fse->min_width = TVP5150_WINDOW_WIDTH_DEF / fse->index; + fse->max_width = fse->min_width; + fse->min_height = TVP5150_WINDOW_HEIGHT_DEF / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int tvp5150_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct tvp5150 *tvp5150 = to_tvp5150(subdev); + + format->format = *__tvp5150_get_pad_format(tvp5150, fh, format->pad, + format->which); + return 0; +} + +static int tvp5150_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct tvp5150 *tvp5150 = to_tvp5150(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __tvp5150_get_pad_crop(tvp5150, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, TVP5150_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, TVP5150_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __tvp5150_get_pad_format(tvp5150, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int tvp5150_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct tvp5150 *tvp5150 = to_tvp5150(subdev); + + crop->rect = *__tvp5150_get_pad_crop(tvp5150, fh, crop->pad, + crop->which); + return 0; +} + +static int tvp5150_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct tvp5150 *tvp5150 = to_tvp5150(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), + TVP5150_COLUMN_START_MIN, + TVP5150_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), + TVP5150_ROW_START_MIN, + TVP5150_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + TVP5150_WINDOW_WIDTH_MIN, + TVP5150_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + TVP5150_WINDOW_HEIGHT_MIN, + TVP5150_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, TVP5150_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, TVP5150_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __tvp5150_get_pad_crop(tvp5150, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __tvp5150_get_pad_format(tvp5150, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/**************************************************************************** + V4L2 subdev internal ops + ****************************************************************************/ + +static int tvp5150_registered(struct v4l2_subdev *subdev) +{ + return 0; +} + +static int tvp5150_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = TVP5150_COLUMN_START_DEF; + crop->top = TVP5150_ROW_START_DEF; + crop->width = TVP5150_WINDOW_WIDTH_DEF; + crop->height = TVP5150_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_UYVY8_1X16; + format->width = TVP5150_WINDOW_WIDTH_DEF; + format->height = TVP5150_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int tvp5150_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +/**************************************************************************** I2C Command ****************************************************************************/ @@ -932,6 +1154,15 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_routing = tvp5150_s_routing, }; +static const struct v4l2_subdev_pad_ops tvp5150_subdev_pad_ops = { + .enum_mbus_code = tvp5150_enum_mbus_code, + .enum_frame_size = tvp5150_enum_frame_size, + .get_fmt = tvp5150_get_format, + .set_fmt = tvp5150_set_format, + .get_crop = tvp5150_get_crop, + .set_crop = tvp5150_set_crop, +}; + static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, .g_sliced_fmt = tvp5150_g_sliced_fmt, @@ -939,11 +1170,18 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .s_raw_fmt = tvp5150_s_raw_fmt, }; +static const struct v4l2_subdev_internal_ops tvp5150_subdev_internal_ops = { + .registered = tvp5150_registered, + .open = tvp5150_open, + .close = tvp5150_close, +}; + static const struct v4l2_subdev_ops tvp5150_ops = { .core = &tvp5150_core_ops, .tuner = &tvp5150_tuner_ops, .video = &tvp5150_video_ops, .vbi = &tvp5150_vbi_ops, + .pad = &tvp5150_subdev_pad_ops, }; @@ -957,6 +1195,7 @@ static int tvp5150_probe(struct i2c_client *c, struct tvp5150 *core; struct v4l2_subdev *sd; u8 msb_id, lsb_id, msb_rom, lsb_rom; + int ret; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, @@ -968,6 +1207,23 @@ static int tvp5150_probe(struct i2c_client *c, return -ENOMEM; } sd = &core->sd; + + core->crop.left = TVP5150_COLUMN_START_DEF; + core->crop.top = TVP5150_ROW_START_DEF; + core->crop.width = TVP5150_WINDOW_WIDTH_DEF; + core->crop.height = TVP5150_WINDOW_HEIGHT_DEF; + + core->format.code = V4L2_MBUS_FMT_UYVY8_1X16; + core->format.width = TVP5150_WINDOW_WIDTH_DEF; + core->format.height = TVP5150_WINDOW_HEIGHT_DEF; + core->format.field = V4L2_FIELD_NONE; + core->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &tvp5150_subdev_internal_ops; + + core->pad.flags = MEDIA_PAD_FL_SOURCE; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); @@ -1015,9 +1271,17 @@ static int tvp5150_probe(struct i2c_client *c, } v4l2_ctrl_handler_setup(&core->hdl); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + core->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_init(&sd->entity, 1, &core->pad, 0); + if (ret < 0) + kfree(core); + if (debug > 1) tvp5150_log_status(sd); - return 0; + return ret; } static int tvp5150_remove(struct i2c_client *c) @@ -1031,6 +1295,7 @@ static int tvp5150_remove(struct i2c_client *c) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); + media_entity_cleanup(&sd->entity); kfree(to_tvp5150(sd)); return 0; }