diff mbox

[PATCH/WIP] tvp5150: add v4l2 subdev pad ops

Message ID CA+2YH7tqJM+ZghfejvVXwSqY5BkU18Lzq0OoKUKWZFyAEsDpYg@mail.gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Enrico Aug. 29, 2011, 5:28 p.m. 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 mbox

Patch

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 <media/tvp5150.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
 
 #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;
 }