diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2009-08-25 11:50:46 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-19 00:19:17 -0300 |
commit | 6a6c8786725c0b3d143674effa8b772f47b1c189 (patch) | |
tree | 8bb76c5dcbd579f13e876bd1a0bb56bee4bcebdd /drivers | |
parent | 0166b74374cae3fa8bff0caef726a3d960a9a50a (diff) |
V4L/DVB (12534): soc-camera: V4L2 API compliant scaling (S_FMT) and cropping (S_CROP)
The initial soc-camera scaling and cropping implementation turned out to be
incompliant with the V4L2 API, e.g., it expected the user to specify cropping
in output window pixels, instead of input window pixels. This patch converts
the soc-camera core and all drivers to comply with the standard.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/mt9m001.c | 142 | ||||
-rw-r--r-- | drivers/media/video/mt9m111.c | 112 | ||||
-rw-r--r-- | drivers/media/video/mt9t031.c | 220 | ||||
-rw-r--r-- | drivers/media/video/mt9v022.c | 131 | ||||
-rw-r--r-- | drivers/media/video/mx1_camera.c | 10 | ||||
-rw-r--r-- | drivers/media/video/mx3_camera.c | 114 | ||||
-rw-r--r-- | drivers/media/video/ov772x.c | 84 | ||||
-rw-r--r-- | drivers/media/video/pxa_camera.c | 201 | ||||
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 828 | ||||
-rw-r--r-- | drivers/media/video/soc_camera.c | 130 | ||||
-rw-r--r-- | drivers/media/video/soc_camera_platform.c | 4 | ||||
-rw-r--r-- | drivers/media/video/tw9910.c | 120 |
12 files changed, 1465 insertions, 631 deletions
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 775e1a3c98d..e8cf56189ef 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -39,6 +39,13 @@ #define MT9M001_GLOBAL_GAIN 0x35 #define MT9M001_CHIP_ENABLE 0xF1 +#define MT9M001_MAX_WIDTH 1280 +#define MT9M001_MAX_HEIGHT 1024 +#define MT9M001_MIN_WIDTH 48 +#define MT9M001_MIN_HEIGHT 32 +#define MT9M001_COLUMN_SKIP 20 +#define MT9M001_ROW_SKIP 12 + static const struct soc_camera_data_format mt9m001_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -70,6 +77,8 @@ static const struct soc_camera_data_format mt9m001_monochrome_formats[] = { struct mt9m001 { struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ unsigned char autoexposure; }; @@ -196,13 +205,31 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; struct i2c_client *client = sd->priv; struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_rect rect = a->c; struct soc_camera_device *icd = client->dev.platform_data; int ret; const u16 hblank = 9, vblank = 25; + if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16) + /* + * Bayer format - even number of rows for simplicity, + * but let the user play with the top row. + */ + rect.height = ALIGN(rect.height, 2); + + /* Datasheet requirement: see register description */ + rect.width = ALIGN(rect.width, 2); + rect.left = ALIGN(rect.left, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + /* Blanking and start values - default... */ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); if (!ret) @@ -211,46 +238,98 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) /* The caller provides a supported format, as verified per * call to icd->try_fmt() */ if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect->left); + ret = reg_write(client, MT9M001_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect->top); + ret = reg_write(client, MT9M001_ROW_START, rect.top); if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1); + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); if (!ret) ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect->height + icd->y_skip_top - 1); + rect.height + icd->y_skip_top - 1); if (!ret && mt9m001->autoexposure) { ret = reg_write(client, MT9M001_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + vblank); + rect.height + icd->y_skip_top + vblank); if (!ret) { const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (524 + (rect->height + icd->y_skip_top + + icd->exposure = (524 + (rect.height + icd->y_skip_top + vblank - 1) * (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; } } + if (!ret) + mt9m001->rect = rect; + return ret; } +static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + + a->c = mt9m001->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M001_COLUMN_SKIP; + a->bounds.top = MT9M001_ROW_SKIP; + a->bounds.width = MT9M001_MAX_WIDTH; + a->bounds.height = MT9M001_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + pix->pixelformat = mt9m001->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct i2c_client *client = sd->priv; - struct soc_camera_device *icd = client->dev.platform_data; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_crop a = { .c = { - .left = icd->rect_current.left, - .top = icd->rect_current.top, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, + .left = mt9m001->rect.left, + .top = mt9m001->rect.top, + .width = pix->width, + .height = pix->height, }, }; + int ret; /* No support for scaling so far, just crop. TODO: use skipping */ - return mt9m001_s_crop(sd, &a); + ret = mt9m001_s_crop(sd, &a); + if (!ret) { + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + mt9m001->fourcc = pix->pixelformat; + } + + return ret; } static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) @@ -259,9 +338,14 @@ static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; - v4l_bound_align_image(&pix->width, 48, 1280, 1, - &pix->height, 32 + icd->y_skip_top, - 1024 + icd->y_skip_top, 0, 0); + v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH, 1, + &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top, + MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0); + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16) + pix->height = ALIGN(pix->height - 1, 2); return 0; } @@ -472,11 +556,11 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (ctrl->value) { const u16 vblank = 25; if (reg_write(client, MT9M001_SHUTTER_WIDTH, - icd->rect_current.height + + mt9m001->rect.height + icd->y_skip_top + vblank) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (524 + (icd->rect_current.height + + icd->exposure = (524 + (mt9m001->rect.height + icd->y_skip_top + vblank - 1) * (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; @@ -548,6 +632,8 @@ static int mt9m001_video_probe(struct soc_camera_device *icd, if (flags & SOCAM_DATAWIDTH_8) icd->num_formats++; + mt9m001->fourcc = icd->formats->fourcc; + dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, data == 0x8431 ? "C12STM" : "C12ST"); @@ -556,10 +642,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd, static void mt9m001_video_remove(struct soc_camera_device *icd) { - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&client->dev, "Video %x removed: %p, %p\n", client->addr, + dev_dbg(&icd->dev, "Video removed: %p, %p\n", icd->dev.parent, icd->vdev); if (icl->free_bus) icl->free_bus(icl); @@ -578,8 +663,11 @@ static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, .s_fmt = mt9m001_s_fmt, + .g_fmt = mt9m001_g_fmt, .try_fmt = mt9m001_try_fmt, .s_crop = mt9m001_s_crop, + .g_crop = mt9m001_g_crop, + .cropcap = mt9m001_cropcap, }; static struct v4l2_subdev_ops mt9m001_subdev_ops = { @@ -621,15 +709,13 @@ static int mt9m001_probe(struct i2c_client *client, /* Second stage probe - when a capture adapter is there */ icd->ops = &mt9m001_ops; - icd->rect_max.left = 20; - icd->rect_max.top = 12; - icd->rect_max.width = 1280; - icd->rect_max.height = 1024; - icd->rect_current.left = 20; - icd->rect_current.top = 12; - icd->width_min = 48; - icd->height_min = 32; icd->y_skip_top = 1; + + mt9m001->rect.left = MT9M001_COLUMN_SKIP; + mt9m001->rect.top = MT9M001_ROW_SKIP; + mt9m001->rect.width = MT9M001_MAX_WIDTH; + mt9m001->rect.height = MT9M001_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9m001->autoexposure = 1; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index 3637376da75..920dd53c4cf 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -194,7 +194,7 @@ static int mt9m111_reg_read(struct i2c_client *client, const u16 reg) ret = reg_page_map_set(client, reg); if (!ret) - ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); + ret = swab16(i2c_smbus_read_word_data(client, reg & 0xff)); dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); return ret; @@ -257,8 +257,8 @@ static int mt9m111_setup_rect(struct i2c_client *client, int width = rect->width; int height = rect->height; - if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) - || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) is_raw_format = 1; else is_raw_format = 0; @@ -395,23 +395,85 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) return 0; } +static int mt9m111_make_rect(struct i2c_client *client, + struct v4l2_rect *rect) +{ + struct mt9m111 *mt9m111 = to_mt9m111(client); + + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) { + /* Bayer format - even size lengths */ + rect->width = ALIGN(rect->width, 2); + rect->height = ALIGN(rect->height, 2); + /* Let the user play with the starting pixel */ + } + + /* FIXME: the datasheet doesn't specify minimum sizes */ + soc_camera_limit_side(&rect->left, &rect->width, + MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH); + + soc_camera_limit_side(&rect->top, &rect->height, + MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT); + + return mt9m111_setup_rect(client, rect); +} + static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; struct i2c_client *client = sd->priv; struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect->left, rect->top, rect->width, - rect->height); + __func__, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(client, rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) - mt9m111->rect = *rect; + mt9m111->rect = rect; return ret; } +static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + + a->c = mt9m111->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M111_MIN_DARK_COLS; + a->bounds.top = MT9M111_MIN_DARK_ROWS; + a->bounds.width = MT9M111_MAX_WIDTH; + a->bounds.height = MT9M111_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m111_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m111->rect.width; + pix->height = mt9m111->rect.height; + pix->pixelformat = mt9m111->pixfmt; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + static int mt9m111_set_pixfmt(struct i2c_client *client, u32 pixfmt) { struct mt9m111 *mt9m111 = to_mt9m111(client); @@ -478,7 +540,7 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) __func__, pix->pixelformat, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(client, &rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) ret = mt9m111_set_pixfmt(client, pix->pixelformat); if (!ret) @@ -489,11 +551,27 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) static int mt9m111_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; + bool bayer = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16; + + /* + * With Bayer format enforce even side lengths, but let the user play + * with the starting pixel + */ if (pix->height > MT9M111_MAX_HEIGHT) pix->height = MT9M111_MAX_HEIGHT; + else if (pix->height < 2) + pix->height = 2; + else if (bayer) + pix->height = ALIGN(pix->height, 2); + if (pix->width > MT9M111_MAX_WIDTH) pix->width = MT9M111_MAX_WIDTH; + else if (pix->width < 2) + pix->width = 2; + else if (bayer) + pix->width = ALIGN(pix->width, 2); return 0; } @@ -906,8 +984,11 @@ static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { .s_fmt = mt9m111_s_fmt, + .g_fmt = mt9m111_g_fmt, .try_fmt = mt9m111_try_fmt, .s_crop = mt9m111_s_crop, + .g_crop = mt9m111_g_crop, + .cropcap = mt9m111_cropcap, }; static struct v4l2_subdev_ops mt9m111_subdev_ops = { @@ -949,16 +1030,13 @@ static int mt9m111_probe(struct i2c_client *client, /* Second stage probe - when a capture adapter is there */ icd->ops = &mt9m111_ops; - icd->rect_max.left = MT9M111_MIN_DARK_COLS; - icd->rect_max.top = MT9M111_MIN_DARK_ROWS; - icd->rect_max.width = MT9M111_MAX_WIDTH; - icd->rect_max.height = MT9M111_MAX_HEIGHT; - icd->rect_current.left = icd->rect_max.left; - icd->rect_current.top = icd->rect_max.top; - icd->width_min = MT9M111_MIN_DARK_ROWS; - icd->height_min = MT9M111_MIN_DARK_COLS; icd->y_skip_top = 0; + mt9m111->rect.left = MT9M111_MIN_DARK_COLS; + mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; + mt9m111->rect.width = MT9M111_MAX_WIDTH; + mt9m111->rect.height = MT9M111_MAX_HEIGHT; + ret = mt9m111_video_probe(icd, client); if (ret) { icd->ops = NULL; diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index cd3eb7731ac..f234ba60204 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -47,7 +47,7 @@ #define MT9T031_MAX_HEIGHT 1536 #define MT9T031_MAX_WIDTH 2048 #define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 2 +#define MT9T031_MIN_WIDTH 18 #define MT9T031_HORIZONTAL_BLANK 142 #define MT9T031_VERTICAL_BLANK 25 #define MT9T031_COLUMN_SKIP 32 @@ -69,10 +69,11 @@ static const struct soc_camera_data_format mt9t031_colour_formats[] = { struct mt9t031 { struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ - unsigned char autoexposure; u16 xskip; u16 yskip; + unsigned char autoexposure; }; static struct mt9t031 *to_mt9t031(const struct i2c_client *client) @@ -218,56 +219,68 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); } -/* Round up minima and round down maxima */ -static void recalculate_limits(struct soc_camera_device *icd, - u16 xskip, u16 yskip) +/* target must be _even_ */ +static u16 mt9t031_skip(s32 *source, s32 target, s32 max) { - icd->rect_max.left = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip; - icd->rect_max.top = (MT9T031_ROW_SKIP + yskip - 1) / yskip; - icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip; - icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip; - icd->rect_max.width = MT9T031_MAX_WIDTH / xskip; - icd->rect_max.height = MT9T031_MAX_HEIGHT / yskip; + unsigned int skip; + + if (*source < target + target / 2) { + *source = target; + return 1; + } + + skip = min(max, *source + target / 2) / target; + if (skip > 8) + skip = 8; + *source = target * skip; + + return skip; } +/* rect is the sensor rectangle, the caller guarantees parameter validity */ static int mt9t031_set_params(struct soc_camera_device *icd, struct v4l2_rect *rect, u16 xskip, u16 yskip) { struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); struct mt9t031 *mt9t031 = to_mt9t031(client); int ret; - u16 xbin, ybin, width, height, left, top; + u16 xbin, ybin; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - width = rect->width * xskip; - height = rect->height * yskip; - left = rect->left * xskip; - top = rect->top * yskip; - xbin = min(xskip, (u16)3); ybin = min(yskip, (u16)3); - dev_dbg(&client->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n", - xskip, width, rect->width, yskip, height, rect->height); - - /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ + /* + * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. + * There is always a valid suitably aligned value. The worst case is + * xbin = 3, width = 2048. Then we will start at 36, the last read out + * pixel will be 2083, which is < 2085 - first black pixel. + * + * MT9T031 datasheet imposes window left border alignment, depending on + * the selected xskip. Failing to conform to this requirement produces + * dark horizontal stripes in the image. However, even obeying to this + * requirement doesn't eliminate the stripes in all configurations. They + * appear "locally reproducibly," but can differ between tests under + * different lighting conditions. + */ switch (xbin) { - case 2: - left = (left + 3) & ~3; + case 1: + rect->left &= ~1; break; - case 3: - left = roundup(left, 6); - } - - switch (ybin) { case 2: - top = (top + 3) & ~3; + rect->left &= ~3; break; case 3: - top = roundup(top, 6); + rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? + (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); } + rect->top &= ~1; + + dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", + xskip, yskip, rect->width, rect->height, rect->left, rect->top); + /* Disable register update, reconfigure atomically */ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); if (ret < 0) @@ -287,27 +300,29 @@ static int mt9t031_set_params(struct soc_camera_device *icd, ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } - dev_dbg(&client->dev, "new physical left %u, top %u\n", left, top); + dev_dbg(&client->dev, "new physical left %u, top %u\n", + rect->left, rect->top); /* The caller provides a supported format, as guaranteed by * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, left); + ret = reg_write(client, MT9T031_COLUMN_START, rect->left); if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, top); + ret = reg_write(client, MT9T031_ROW_START, rect->top); if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1); + ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); if (ret >= 0) ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - height + icd->y_skip_top - 1); + rect->height + icd->y_skip_top - 1); if (ret >= 0 && mt9t031->autoexposure) { - ret = set_shutter(client, height + icd->y_skip_top + vblank); + ret = set_shutter(client, + rect->height + icd->y_skip_top + vblank); if (ret >= 0) { const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (shutter_max / 2 + (height + + icd->exposure = (shutter_max / 2 + (rect->height + icd->y_skip_top + vblank - 1) * (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; @@ -318,27 +333,72 @@ static int mt9t031_set_params(struct soc_camera_device *icd, if (ret >= 0) ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); + if (ret >= 0) { + mt9t031->rect = *rect; + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + return ret < 0 ? ret : 0; } static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; struct i2c_client *client = sd->priv; struct mt9t031 *mt9t031 = to_mt9t031(client); struct soc_camera_device *icd = client->dev.platform_data; - /* Make sure we don't exceed sensor limits */ - if (rect->left + rect->width > icd->rect_max.left + icd->rect_max.width) - rect->left = icd->rect_max.width + icd->rect_max.left - - rect->width; + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); + + return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031->yskip); +} + +static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + + a->c = mt9t031->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (rect->top + rect->height > icd->rect_max.height + icd->rect_max.top) - rect->top = icd->rect_max.height + icd->rect_max.top - - rect->height; + return 0; +} + +static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9T031_COLUMN_SKIP; + a->bounds.top = MT9T031_ROW_SKIP; + a->bounds.width = MT9T031_MAX_WIDTH; + a->bounds.height = MT9T031_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; - /* CROP - no change in scaling, or in limits */ - return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip); + return 0; +} + +static int mt9t031_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9t031->rect.width / mt9t031->xskip; + pix->height = mt9t031->rect.height / mt9t031->yskip; + pix->pixelformat = V4L2_PIX_FMT_SGRBG10; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; } static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) @@ -346,40 +406,25 @@ static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) struct i2c_client *client = sd->priv; struct mt9t031 *mt9t031 = to_mt9t031(client); struct soc_camera_device *icd = client->dev.platform_data; - int ret; + struct v4l2_pix_format *pix = &f->fmt.pix; u16 xskip, yskip; - struct v4l2_rect rect = { - .left = icd->rect_current.left, - .top = icd->rect_current.top, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, - }; + struct v4l2_rect rect = mt9t031->rect; /* - * try_fmt has put rectangle within limits. - * S_FMT - use binning and skipping for scaling, recalculate - * limits, used for cropping + * try_fmt has put width and height within limits. + * S_FMT: use binning and skipping for scaling */ - /* Is this more optimal than just a division? */ - for (xskip = 8; xskip > 1; xskip--) - if (rect.width * xskip <= MT9T031_MAX_WIDTH) - break; - - for (yskip = 8; yskip > 1; yskip--) - if (rect.height * yskip <= MT9T031_MAX_HEIGHT) - break; - - recalculate_limits(icd, xskip, yskip); - - ret = mt9t031_set_params(icd, &rect, xskip, yskip); - if (!ret) { - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } + xskip = mt9t031_skip(&rect.width, pix->width, MT9T031_MAX_WIDTH); + yskip = mt9t031_skip(&rect.height, pix->height, MT9T031_MAX_HEIGHT); - return ret; + /* mt9t031_set_params() doesn't change width and height */ + return mt9t031_set_params(icd, &rect, xskip, yskip); } +/* + * If a user window larger than sensor window is requested, we'll increase the + * sensor window. + */ static int mt9t031_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; @@ -620,12 +665,12 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (ctrl->value) { const u16 vblank = MT9T031_VERTICAL_BLANK; const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - if (set_shutter(client, icd->rect_current.height + + if (set_shutter(client, mt9t031->rect.height + icd->y_skip_top + vblank) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); icd->exposure = (shutter_max / 2 + - (icd->rect_current.height + + (mt9t031->rect.height + icd->y_skip_top + vblank - 1) * (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; @@ -645,12 +690,6 @@ static int mt9t031_video_probe(struct i2c_client *client) struct mt9t031 *mt9t031 = to_mt9t031(client); s32 data; - /* We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; - /* Enable the chip */ data = reg_write(client, MT9T031_CHIP_ENABLE, 1); dev_dbg(&client->dev, "write: %d\n", data); @@ -688,8 +727,11 @@ static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .s_stream = mt9t031_s_stream, .s_fmt = mt9t031_s_fmt, + .g_fmt = mt9t031_g_fmt, .try_fmt = mt9t031_try_fmt, .s_crop = mt9t031_s_crop, + .g_crop = mt9t031_g_crop, + .cropcap = mt9t031_cropcap, }; static struct v4l2_subdev_ops mt9t031_subdev_ops = { @@ -731,15 +773,13 @@ static int mt9t031_probe(struct i2c_client *client, /* Second stage probe - when a capture adapter is there */ icd->ops = &mt9t031_ops; - icd->rect_max.left = MT9T031_COLUMN_SKIP; - icd->rect_max.top = MT9T031_ROW_SKIP; - icd->rect_current.left = icd->rect_max.left; - icd->rect_current.top = icd->rect_max.top; - icd->width_min = MT9T031_MIN_WIDTH; - icd->rect_max.width = MT9T031_MAX_WIDTH; - icd->height_min = MT9T031_MIN_HEIGHT; - icd->rect_max.height = MT9T031_MAX_HEIGHT; icd->y_skip_top = 0; + + mt9t031->rect.left = MT9T031_COLUMN_SKIP; + mt9t031->rect.top = MT9T031_ROW_SKIP; + mt9t031->rect.width = MT9T031_MAX_WIDTH; + mt9t031->rect.height = MT9T031_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9t031->autoexposure = 1; diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index ab196542528..35ea0ddd071 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -55,6 +55,13 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); /* Progressive scan, master, defaults */ #define MT9V022_CHIP_CONTROL_DEFAULT 0x188 +#define MT9V022_MAX_WIDTH 752 +#define MT9V022_MAX_HEIGHT 480 +#define MT9V022_MIN_WIDTH 48 +#define MT9V022_MIN_HEIGHT 32 +#define MT9V022_COLUMN_SKIP 1 +#define MT9V022_ROW_SKIP 4 + static const struct soc_camera_data_format mt9v022_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -86,6 +93,8 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { struct mt9v022 { struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ u16 chip_control; }; @@ -250,44 +259,101 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_rect rect = a->c; struct soc_camera_device *icd = client->dev.platform_data; int ret; + /* Bayer format - even size lengths */ + if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) { + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + /* Let the user play with the starting pixel */ + } + + soc_camera_limit_side(&rect.left, &rect.width, + MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); + /* Like in example app. Contradicts the datasheet though */ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { if (ret & 1) /* Autoexposure */ ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); else ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect->left); + ret = reg_write(client, MT9V022_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect->top); + ret = reg_write(client, MT9V022_ROW_START, rect.top); if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, - rect->width > 660 - 43 ? 43 : - 660 - rect->width); + rect.width > 660 - 43 ? 43 : + 660 - rect.width); if (!ret) ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width); + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); if (!ret) ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect->height + icd->y_skip_top); + rect.height + icd->y_skip_top); if (ret < 0) return ret; - dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect->width, rect->height); + dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); + + mt9v022->rect = rect; + + return 0; +} + +static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + + a->c = mt9v022->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9V022_COLUMN_SKIP; + a->bounds.top = MT9V022_ROW_SKIP; + a->bounds.width = MT9V022_MAX_WIDTH; + a->bounds.height = MT9V022_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; |