Commit 6a6c8786 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab

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: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 0166b743
......@@ -116,5 +116,45 @@ functionality.
struct soc_camera_device also links to an array of struct soc_camera_data_format,
listing pixel formats, supported by the camera.
VIDIOC_S_CROP and VIDIOC_S_FMT behaviour
----------------------------------------
Above user ioctls modify image geometry as follows:
VIDIOC_S_CROP: sets location and sizes of the sensor window. Unit is one sensor
pixel. Changing sensor window sizes preserves any scaling factors, therefore
user window sizes change as well.
VIDIOC_S_FMT: sets user window. Should preserve previously set sensor window as
much as possible by modifying scaling factors. If the sensor window cannot be
preserved precisely, it may be changed too.
In soc-camera there are two locations, where scaling and cropping can taks
place: in the camera driver and in the host driver. User ioctls are first passed
to the host driver, which then generally passes them down to the camera driver.
It is more efficient to perform scaling and cropping in the camera driver to
save camera bus bandwidth and maximise the framerate. However, if the camera
driver failed to set the required parameters with sufficient precision, the host
driver may decide to also use its own scaling and cropping to fulfill the user's
request.
Camera drivers are interfaced to the soc-camera core and to host drivers over
the v4l2-subdev API, which is completely functional, it doesn't pass any data.
Therefore all camera drivers shall reply to .g_fmt() requests with their current
output geometry. This is necessary to correctly configure the camera bus.
.s_fmt() and .try_fmt() have to be implemented too. Sensor window and scaling
factors have to be maintained by camera drivers internally. According to the
V4L2 API all capture drivers must support the VIDIOC_CROPCAP ioctl, hence we
rely on camera drivers implementing .cropcap(). If the camera driver does not
support cropping, it may choose to not implement .s_crop(), but to enable
cropping support by the camera host driver at least the .g_crop method must be
implemented.
User window geometry is kept in .user_width and .user_height fields in struct
soc_camera_device and used by the soc-camera core and host drivers. The core
updates these fields upon successful completion of a .s_fmt() call, but if these
fields change elsewhere, e.g., during .s_crop() processing, the host driver is
responsible for updating them.
--
Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
......@@ -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;
......
......@@ -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;
......
This diff is collapsed.
......@@ -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;
}
static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
pix->width = mt9v022->rect.width;
pix->height = mt9v022->rect.height;
pix->pixelformat = mt9v022->fourcc;
pix->field = V4L2_FIELD_NONE;
pix->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
......@@ -296,16 +362,16 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct soc_camera_device *icd = client->dev.platform_data;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_crop a = {
.c = {
.left = icd->rect_current.left,
.top = icd->rect_current.top,
.left = mt9v022->rect.left,
.top = mt9v022->rect.top,
.width = pix->width,
.height = pix->height,
},
};
int ret;
/* The caller provides a supported format, as verified per call to
* icd->try_fmt(), datawidth is from our supported format list */
......@@ -328,7 +394,14 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
}
/* No support for scaling on this camera, just crop. */
return mt9v022_s_crop(sd, &a);
ret = mt9v022_s_crop(sd, &a);
if (!ret) {
pix->width = mt9v022->rect.width;
pix->height = mt9v022->rect.height;
mt9v022->fourcc = pix->pixelformat;
}
return ret;
}
static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
......@@ -336,10 +409,13 @@ static int mt9v022_try_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 v4l2_pix_format *pix = &f->fmt.pix;
int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
pix->pixelformat == V4L2_PIX_FMT_SBGGR16;
v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */,
&pix->height, 32 + icd->y_skip_top,
480 + icd->y_skip_top, 0, 0);
v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH,
MT9V022_MAX_WIDTH, align,
&pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top,
MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0);
return 0;
}
......@@ -669,6 +745,8 @@ static int mt9v022_video_probe(struct soc_camera_device *icd,
if (flags & SOCAM_DATAWIDTH_8)
icd->num_formats++;
mt9v022->fourcc = icd->formats->fourcc;
dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
"monochrome" : "colour");
......@@ -679,10 +757,9 @@ static int mt9v022_video_probe(struct soc_camera_device *icd,
static void mt9v022_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);
......@@ -701,8 +778,11 @@ static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.s_stream = mt9v022_s_stream,
.s_fmt = mt9v022_s_fmt,
.g_fmt = mt9v022_g_fmt,
.try_fmt = mt9v022_try_fmt,
.s_crop = mt9v022_s_crop,
.g_crop = mt9v022_g_crop,
.cropcap = mt9v022_cropcap,
};
static struct v4l2_subdev_ops mt9v022_subdev_ops = {
......@@ -745,16 +825,13 @@ static int mt9v022_probe(struct i2c_client *client,
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
icd->ops = &mt9v022_ops;
icd->rect_max.left = 1;
icd->rect_max.top = 4;
icd->rect_max.width = 752;
icd->rect_max.height = 480;
icd->rect_current.left = 1;
icd->rect_current.top = 4;
icd->width_min = 48;
icd->height_min = 32;
icd->y_skip_top = 1;
mt9v022->rect.left = MT9V022_COLUMN_SKIP;
mt9v022->rect.top = MT9V022_ROW_SKIP;
mt9v022->rect.width = MT9V022_MAX_WIDTH;
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
ret = mt9v022_video_probe(icd, client);
if (ret) {
icd->ops = NULL;
......
......@@ -126,7 +126,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
{
struct soc_camera_device *icd = vq->priv_data;
*size = icd->rect_current.width * icd->rect_current.height *
*size = icd->user_width * icd->user_height *
((icd->current_fmt->depth + 7) >> 3);
if (!*count)
......@@ -178,12 +178,12 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
buf->inwork = 1;
if (buf->fmt != icd->current_fmt ||
vb->width != icd->rect_current.width ||
vb->height != icd->rect_current.height ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
vb->width = icd->rect_current.width;
vb->height = icd->rect_current.height;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
......
......@@ -220,7 +220,7 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
*size = icd->rect_current.width * icd->rect_current.height * bpp;
*size = icd->user_width * icd->user_height * bpp;
if (!*count)
*count = 32;
......@@ -241,7 +241,7 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq,
struct mx3_camera_buffer *buf =
container_of(vb, struct mx3_camera_buffer, vb);
/* current_fmt _must_ always be set */
size_t new_size = icd->rect_current.width * icd->rect_current.height *
size_t new_size = icd->user_width * icd->user_height *
((icd->current_fmt->depth + 7) >> 3);
int ret;
......@@ -251,12 +251,12 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq,
*/
if (buf->fmt != icd->current_fmt ||
vb->width != icd->rect_current.width ||
vb->height != icd->rect_current.height ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
vb->width = icd->rect_current.width;
vb->height = icd->rect_current.height;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
if (vb->state != VIDEOBUF_NEEDS_INIT)
free_buffer(vq, buf);
......@@ -354,9 +354,9 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
/* This is the configuration of one sg-element */
video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
video->out_width = icd->rect_current.width;
video->out_height = icd->rect_current.height;
video->out_stride = icd->rect_current.width;
video->out_width = icd->user_width;
video->out_height = icd->user_height;
video->out_stride = icd->user_width;
#ifdef DEBUG
/* helps to see what DMA actually has written */
......@@ -541,7 +541,7 @@ static bool channel_change_requested(struct soc_camera_device *icd,
/* Do buffers have to be re-allocated or channel re-configured? */
return ichan && rect->width * rect->height >
icd->rect_current.width * icd->rect_current.height;
icd->user_width * icd->user_height;
}
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
......@@ -589,8 +589,8 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
*flags |= SOCAM_DATAWIDTH_4;
break;
default:
dev_info(mx3_cam->soc_host.v4l2_dev.dev, "Unsupported bus width %d\n",
buswidth);
dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
"Unsupported bus width %d\n", buswidth);
return -EINVAL;
}
......@@ -605,8 +605,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
unsigned long bus_flags, camera_flags;
int ret = test_platform_param(mx3_cam, depth, &bus_flags);
dev_dbg(icd->dev.parent, "requested bus width %d bit: %d\n",
depth, ret);
dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret);
if (ret < 0)
return ret;
......@@ -727,13 +726,13 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
}
static void configure_geometry(struct mx3_camera_dev *mx3_cam,
struct v4l2_rect *rect)
unsigned int width, unsigned int height)
{
u32 ctrl, width_field, height_field;
/* Setup frame size - this cannot be changed on-the-fly... */
width_field = rect->width - 1;
height_field = rect->height - 1;
width_field = width - 1;
height_field = height - 1;
csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
......@@ -745,11 +744,6 @@ static void configure_geometry(struct mx3_camera_dev *mx3_cam,
ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
/* Sensor does the cropping */
csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
/*
* No need to free resources here if we fail, we'll see if we need to
* do this next time we are called
*/
}
static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
......@@ -786,6 +780,22 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
return 0;
}
/*
* FIXME: learn to use stride != width, then we can keep stride properly aligned
* and support arbitrary (even) widths.
*/
static inline void stride_align(__s32 *width)
{
if (((*width + 7) & ~7) < 4096)
*width = (*width + 7) & ~7;
else
*width = *width & ~7;
}
/*
* As long as we don't implement host-side cropping and scaling, we can use
* default g_crop and cropcap from soc_camera.c
*/
static int mx3_camera_set_crop(struct soc_camera_device *icd,
struct v4l2_crop *a)
{
......@@ -793,20 +803,51 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
struct v4l2_pix_format *pix = &f.fmt.pix;
int ret;
/*
* We now know pixel formats and can decide upon DMA-channel(s)
* So far only direct camera-to-memory is supported
*/
if (channel_change_requested(icd, rect)) {
int ret = acquire_dma_channel(mx3_cam);
soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
ret = v4l2_subdev_call(sd, video, s_crop, a);
if (ret < 0)
return ret;
/* The capture device might have changed its output */
ret = v4l2_subdev_call(sd, video, g_fmt, &f);
if (ret < 0)
return ret;
if (pix->width & 7) {
/* Ouch! We can only handle 8-byte aligned width... */
stride_align(&pix->width);
ret = v4l2_subdev_call(sd, video, s_fmt, &f);
if (ret < 0)
return ret;
}
configure_geometry(mx3_cam, rect);
if (pix->width != icd->user_width || pix->height != icd->user_height) {
/*
* We now know pixel formats and can decide upon DMA-channel(s)
* So far only direct camera-to-memory is supported
*/
if (channel_change_requested(icd, rect)) {
int ret = acquire_dma_channel(mx3_cam);
if (ret < 0)
return ret;
}
return v4l2_subdev_call(sd, video, s_crop, a);
configure_geometry(mx3_cam, pix->width, pix->height);
}
dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
pix->width, pix->height);
icd->user_width = pix->width;
icd->user_height = pix->height;
return ret;
}
static int mx3_camera_set_fmt(struct soc_camera_device *icd,
......@@ -817,12 +858,6 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_rect rect = {
.left = icd->rect_current.left,
.top = icd->rect_current.top,
.width = pix->width,
.height = pix->height,
};
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
......@@ -832,6 +867,9 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
stride_align(&pix->width);
dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height);
ret = acquire_dma_channel(mx3_cam);
if (ret < 0)
return ret;
......@@ -842,7 +880,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
* mxc_v4l2_s_fmt()
*/
configure_geometry(mx3_cam, &rect);
configure_geometry(mx3_cam, pix->width, pix->height);
ret = v4l2_subdev_call(sd, video, s_fmt, f);
if (!ret) {
......@@ -850,6 +888,8 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
icd->current_fmt = xlate->host_fmt;
}
dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
return ret;
}
......
......@@ -382,11 +382,10 @@ struct regval_list {
};
struct ov772x_color_format {
char *name;
__u32 fourcc;
u8 dsp3;
u8 com3;
u8 com7;
const struct soc_camera_data_format *format;
u8 dsp3;
u8 com3;
u8 com7;
};
struct ov772x_win_size {
......@@ -481,43 +480,43 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = {
*/
static const struct ov772x_color_format ov772x_cfmts[] = {
{
SETFOURCC(YUYV),
.format = &ov772x_fmt_lists[0],
.dsp3 = 0x0,
.com3 = SWAP_YUV,
.com7 = OFMT_YUV,
},
{
SETFOURCC(YVYU),
.format = &ov772x_fmt_lists[1],
.dsp3 = UV_ON,
.com3 = SWAP_YUV,
.com7 = OFMT_YUV,
},
{
SETFOURCC(UYVY),
.format = &ov772x_fmt_lists[2],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = OFMT_YUV,
},
{
SETFOURCC(RGB555),
.format = &ov772x_fmt_lists[3],
.dsp3 = 0x0,
.com3 = SWAP_RGB,
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
SETFOURCC(RGB555X),
.format = &ov772x_fmt_lists[4],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
SETFOURCC(RGB565),
.format = &ov772x_fmt_lists[5],
.dsp3 = 0x0,
.com3 = SWAP_RGB,
.com7 = FMT_RGB565 | OFMT_RGB,
},
{
SETFOURCC(RGB565X),
.format = &ov772x_fmt_lists[6],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = FMT_RGB565 | OFMT_RGB,
......@@ -648,8 +647,8 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
dev_dbg(&client->dev,
"format %s, win %s\n", priv->fmt->name, priv->win->name);
dev_dbg(&client->dev, "format %s, win %s\n",
priv->fmt->format->name, priv->win->name);
return 0;
}
......@@ -818,7 +817,7 @@ static int ov772x_set_params(struct i2c_client *client,
*/
priv->fmt = NULL;
for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
if (pixfmt == ov772x_cfmts[i].fourcc) {
if (pixfmt == ov772x_cfmts[i].format->fourcc) {
priv->fmt = ov772x_cfmts + i;
break;
}
......@@ -955,6 +954,56 @@ static int ov772x_set_params(struct i2c_client *client,
return ret;
}
static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
a->c.left = 0;
a->c.top = 0;
a->c.width = VGA_WIDTH;
a->c.height = VGA_HEIGHT;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = VGA_WIDTH;
a->bounds.height = VGA_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
struct ov772x_priv *priv = to_ov772x(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
if (!priv->win || !priv->fmt) {
u32 width = VGA_WIDTH, height = VGA_HEIGHT;
int ret = ov772x_set_params(client, &width, &height,
V4L2_PIX_FMT_YUYV);
if (ret < 0)
return ret;
}
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pix->width = priv->win->width;
pix->height = priv->win->height;
pix->pixelformat = priv->fmt->format->fourcc;
pix->colorspace = priv->fmt->format->colorspace;
pix->field = V4L2_FIELD_NONE;
return 0;
}
static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
......@@ -1060,8 +1109,11 @@ static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.s_stream = ov772x_s_stream,
.g_fmt = ov772x_g_fmt,
.s_fmt = ov772x_s_fmt,
.try_fmt = ov772x_try_fmt,
.cropcap = ov772x_cropcap,
.g_crop = ov772x_g_crop,
};
static struct v4l2_subdev_ops ov772x_subdev_ops = {
......@@ -1110,8 +1162,6 @@ static int ov772x_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
icd->ops = &ov772x_ops;
icd->rect_max.width = MAX_WIDTH;
icd->rect_max.height = MAX_HEIGHT;
ret = ov772x_video_probe(icd, client);
if (ret) {
......
This diff is collapsed.
This diff is collapsed.
......@@ -278,6 +278,9 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
icd->user_formats = NULL;
}
#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
((x) >> 24) & 0xff
/* Called with .vb_lock held */
static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_format *f)
......@@ -287,6 +290,9 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n",
pixfmtstr(pix->pixelformat), pix->width, pix->height);
/* We always call try_fmt() before set_fmt() or set_crop() */
ret = ici->ops->try_fmt(icd, f);
if (ret < 0)
......@@ -302,17 +308,17 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
return -EINVAL;
}
icd->rect_current.width = pix->width;
icd->rect_current.height = pix->height;
icf->vb_vidq.field =
icd->field = pix->field;
icd->user_width = pix->width;
icd->user_height = pix->height;
icf->vb_vidq.field =
icd->field = pix->field;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
f->type);
dev_dbg(&icd->dev, "set width: %d height: %d\n",
icd->rect_current.width, icd->rect_current.height);
icd->user_width, icd->user_height);
/* set physical bus parameters */
return ici->ops->set_bus_param(icd, pix->pixelformat);
......@@ -355,8 +361,8 @@ static int soc_camera_open(struct file *file)
struct v4l2_format f = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.fmt.pix = {
.width = icd->rect_current.width,
.height = icd->rect_current.height,
.width = icd->user_width,
.height = icd->user_height,
.field = icd->field,
.pixelformat = icd->current_fmt->fourcc,
.colorspace = icd->current_fmt->colorspace,
......@@ -557,8 +563,8 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
pix->width = icd->rect_current.width;
pix->height = icd->rect_current.height;
pix->width = icd->user_width;
pix->height = icd->user_height;
pix->field = icf->vb_vidq.field;
pix->pixelformat = icd->current_fmt->fourcc;
pix->bytesperline = pix->width *
......@@ -722,17 +728,9 @@ static int soc_camera_cropcap(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->bounds = icd->rect_max;
a->defrect.left = icd->rect_max.left;
a->defrect.top = icd->rect_max.top;
a->defrect.width = DEFAULT_WIDTH;
a->defrect.height = DEFAULT_HEIGHT;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
return ici->ops->cropcap(icd, a);
}
static int soc_camera_g_crop(struct file *file, void *fh,
......@@ -740,11 +738,14 @@ static int soc_camera_g_crop(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int ret;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->c = icd->rect_current;
mutex_lock(&icf->vb_vidq.vb_lock);
ret = ici->ops->get_crop(icd, a);
mutex_unlock(&icf->vb_vidq.vb_lock);
return 0;
return ret;
}
/*
......@@ -759,49 +760,33 @@ static int soc_camera_s_crop(struct file *file, void *fh,
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct v4l2_rect rect = a->c;
struct v4l2_rect *rect = &a->c;
struct v4l2_crop current_crop;
int ret;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
rect->width, rect->height, rect->left, rect->top);
/* Cropping is allowed during a running capture, guard consistency */
mutex_lock(&icf->vb_vidq.vb_lock);
/* If get_crop fails, we'll let host and / or client drivers decide */
ret = ici->ops->get_crop(icd, &current_crop);
/* Prohibit window size change with initialised buffers */
if (icf->vb_vidq.bufs[0] && (rect.width != icd->rect_current.width ||
rect.height != icd->rect_current.height)) {
if (icf->vb_vidq.bufs[0] && !ret &&
(a->c.width != current_crop.c.width ||
a->c.height != current_crop.c.height)) {
dev_err(&icd->dev,
"S_CROP denied: queue initialised and sizes differ\n");
ret = -EBUSY;
goto unlock;
} else {
ret = ici->ops->set_crop(icd, a);
}
if (rect.width > icd->rect_max.width)
rect.width = icd->rect_max.width;
if (rect.width < icd->width_min)
rect.width = icd->width_min;
if (rect.height > icd->rect_max.height)
rect.height = icd->rect_max.height;
if (rect.height < icd->height_min)
rect.height = icd->height_min;
if (rect.width + rect.left > icd->rect_max.width + icd->rect_max.left)
rect.left = icd->rect_max.width + icd->rect_max.left -
rect.width;
if (rect.height + rect.top > icd->rect_max.height + icd->rect_max.top)
rect.top = icd->rect_max.height + icd->rect_max.top -
rect.height;
ret = ici->ops->set_crop(icd, a);
if (!ret)
icd->rect_current = rect;
unlock:
mutex_unlock(&icf->vb_vidq.vb_lock);
return ret;
......@@ -926,6 +911,8 @@ static int soc_camera_probe(struct device *dev)
struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct device *control = NULL;
struct v4l2_subdev *sd;
struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
int ret;
dev_info(dev, "Probing %s\n", dev_name(dev));
......@@ -982,7 +969,6 @@ static int soc_camera_probe(struct device *dev)
if (ret < 0)
goto eiufmt;
icd->rect_current = icd->rect_max;
icd->field = V4L2_FIELD_ANY;
/* ..._video_start() will create a device node, so we have to protect */
......@@ -992,9 +978,15 @@ static int soc_camera_probe(struct device *dev)
if (ret < 0)
goto evidstart;
/* Try to improve our guess of a reasonable window format */
sd = soc_camera_to_subdev(icd);
if (!v4l2_subdev_call(sd, video, g_fmt, &f)) {
icd->user_width = f.fmt.pix.width;
icd->user_height = f.fmt.pix.height;
}
/* Do we have to sysfs_remove_link() before device_unregister()? */
if (to_soc_camera_control(icd) &&
sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
"control"))
dev_warn(&icd->dev, "Failed creating the control symlink\n");
......@@ -1103,6 +1095,25 @@ static void dummy_release(struct device *dev)
{
}
static int default_cropcap(struct soc_camera_device *icd,
struct v4l2_cropcap *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
return v4l2_subdev_call(sd, video, cropcap, a);
}
static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
return v4l2_subdev_call(sd, video, g_crop, a);
}
static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
return v4l2_subdev_call(sd, video, s_crop, a);
}
int soc_camera_host_register(struct soc_camera_host *ici)
{
struct soc_camera_host *ix;
......@@ -1111,7 +1122,6 @@ int soc_camera_host_register(struct soc_camera_host *ici)
if (!ici || !ici->ops ||
!ici->ops->try_fmt ||
!ici->ops->set_fmt ||
!ici->ops->set_crop ||
!ici->ops->set_bus_param ||
!ici->ops->querycap ||
!ici->ops->init_videobuf ||
......@@ -1122,6 +1132,13 @@ int soc_camera_host_register(struct soc_camera_host *ici)
!ici->v4l2_dev.dev)
return -EINVAL;
if (!ici->ops->set_crop)
ici->ops->set_crop = default_s_crop;
if (!ici->ops->get_crop)
ici->ops->get_crop = default_g_crop;
if (!ici->ops->cropcap)
ici->ops->cropcap = default_cropcap;
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
if (ix->nr == ici->nr) {
......@@ -1321,6 +1338,9 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
if (ret < 0)
goto escdevreg;
icd->user_width = DEFAULT_WIDTH;
icd->user_height = DEFAULT_HEIGHT;
return 0;
escdevreg:
......
......@@ -127,10 +127,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
/* Set the control device reference */
dev_set_drvdata(&icd->dev, &pdev->dev);
icd->width_min = 0;
icd->rect_max.width = p->format.width;
icd->height_min = 0;
icd->rect_max.height = p->format.height;
icd->y_skip_top = 0;
icd->ops = &soc_camera_platform_ops;
......
......@@ -715,8 +715,88 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
return ret;
}
static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
if (!priv->scale) {
int ret;
struct v4l2_crop crop = {
.c = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
};
ret = tw9910_s_crop(sd, &crop);
if (ret < 0)
return ret;
}
a->c.left = 0;
a->c.top = 0;
a->c.width = priv->scale->width;
a->c.height = priv->scale->height;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = 768;
a->bounds.height = 576;
a->defrect.left = 0;
a->defrect.top = 0;
a->defrect.width = 640;
a->defrect.height = 480;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
if (!priv->scale) {
int ret;
struct v4l2_crop crop = {
.c = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
};
ret = tw9910_s_crop(sd, &crop);
if (ret < 0)
return ret;
}
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pix->width = priv->scale->width;
pix->height = priv->scale->height;
pix->pixelformat = V4L2_PIX_FMT_VYUY;
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
pix->field = V4L2_FIELD_INTERLACED;
return 0;
}
static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
/* See tw9910_s_crop() - no proper cropping support */
struct v4l2_crop a = {
......@@ -741,8 +821,8 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
ret = tw9910_s_crop(sd, &a);
if (!ret) {
pix->width = a.c.width;
pix->height = a.c.height;
pix->width = priv->scale->width;
pix->height = priv->scale->height;
}
return ret;
}
......@@ -838,8 +918,11 @@ static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.s_stream = tw9910_s_stream,
.g_fmt = tw9910_g_fmt,
.s_fmt = tw9910_s_fmt,
.try_fmt = tw9910_try_fmt,
.cropcap = tw9910_cropcap,
.g_crop = tw9910_g_crop,
.s_crop = tw9910_s_crop,
};
......@@ -852,20 +935,6 @@ static struct v4l2_subdev_ops tw9910_subdev_ops = {
* i2c_driver function
*/
/* This is called during probe, so, setting rect_max is Ok here: scale == 1 */
static void limit_to_scale(struct soc_camera_device *icd,
const struct tw9910_scale_ctrl *scale)
{
if (scale->width > icd->rect_max.width)
icd->rect_max.width = scale->width;
if (scale->width < icd->width_min)
icd->width_min = scale->width;
if (scale->height > icd->rect_max.height)
icd->rect_max.height = scale->height;
if (scale->height < icd->height_min)
icd->height_min = scale->height;
}
static int tw9910_probe(struct i2c_client *client,
const struct i2c_device_id *did)
......@@ -876,8 +945,7 @@ static int tw9910_probe(struct i2c_client *client,
struct i2c_adapter *adapter =
to_i2c_adapter(client->dev.parent);
struct soc_camera_link *icl;
const struct tw9910_scale_ctrl *scale;
int i, ret;
int ret;
if (!icd) {
dev_err(&client->dev, "TW9910: missing soc-camera data!\n");
......@@ -908,22 +976,6 @@ static int tw9910_probe(struct i2c_client *client,
icd->ops = &tw9910_ops;
icd->iface = info->link.bus_id;
/*
* set width and height
*/
icd->rect_max.width = tw9910_ntsc_scales[0].width; /* set default */
icd->width_min = tw9910_ntsc_scales[0].width;
icd->rect_max.height = tw9910_ntsc_scales[0].height;
icd->height_min = tw9910_ntsc_scales[0].height;
scale = tw9910_ntsc_scales;
for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++)
limit_to_scale(icd, scale + i);
scale = tw9910_pal_scales;
for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++)
limit_to_scale(icd, scale + i);
ret = tw9910_video_probe(icd, client);
if (ret) {
icd->ops = NULL;
......
......@@ -22,8 +22,8 @@ struct soc_camera_device {
struct list_head list;
struct device dev;
struct device *pdev; /* Platform device */
struct v4l2_rect rect_current; /* Current window */
struct v4l2_rect rect_max; /* Maximum window */
s32 user_width;
s32 user_height;
unsigned short width_min;
unsigned short height_min;
unsigned short y_skip_top; /* Lines to skip at the top */
......@@ -76,6 +76,8 @@ struct soc_camera_host_ops {
int (*get_formats)(struct soc_camera_device *, int,
struct soc_camera_format_xlate *);
void (*put_formats)(struct soc_camera_device *);
int (*cropcap)(struct soc_camera_device *, struct v4l2_cropcap *);
int (*get_crop)(struct soc_camera_device *, struct v4l2_crop *);
int (*set_crop)(struct soc_camera_device *, struct v4l2_crop *);
int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
......@@ -277,6 +279,21 @@ static inline unsigned long soc_camera_bus_param_compatible(
common_flags;
}
static inline void soc_camera_limit_side(unsigned int *start,
unsigned int *length, unsigned int start_min,
unsigned int length_min, unsigned int length_max)
{
if (*length < length_min)
*length = length_min;
else if (*length > length_max)
*length = length_max;
if (*start < start_min)
*start = start_min;
else if (*start > start_min + length_max - *length)
*start = start_min + length_max - *length;
}
extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
unsigned long flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment