|
@@ -52,6 +52,7 @@ struct bcap_format {
|
|
|
u32 pixelformat;
|
|
|
enum v4l2_mbus_pixelcode mbus_code;
|
|
|
int bpp; /* bits per pixel */
|
|
|
+ int dlen; /* data length for ppi in bits */
|
|
|
};
|
|
|
|
|
|
struct bcap_buffer {
|
|
@@ -76,10 +77,14 @@ struct bcap_device {
|
|
|
unsigned int cur_input;
|
|
|
/* current selected standard */
|
|
|
v4l2_std_id std;
|
|
|
+ /* current selected dv_timings */
|
|
|
+ struct v4l2_dv_timings dv_timings;
|
|
|
/* used to store pixel format */
|
|
|
struct v4l2_pix_format fmt;
|
|
|
/* bits per pixel*/
|
|
|
int bpp;
|
|
|
+ /* data length for ppi in bits */
|
|
|
+ int dlen;
|
|
|
/* used to store sensor supported format */
|
|
|
struct bcap_format *sensor_formats;
|
|
|
/* number of sensor formats array */
|
|
@@ -116,24 +121,35 @@ static const struct bcap_format bcap_formats[] = {
|
|
|
.pixelformat = V4L2_PIX_FMT_UYVY,
|
|
|
.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
|
|
|
.bpp = 16,
|
|
|
+ .dlen = 8,
|
|
|
},
|
|
|
{
|
|
|
.desc = "YCbCr 4:2:2 Interleaved YUYV",
|
|
|
.pixelformat = V4L2_PIX_FMT_YUYV,
|
|
|
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
|
|
|
.bpp = 16,
|
|
|
+ .dlen = 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
|
|
|
+ .pixelformat = V4L2_PIX_FMT_UYVY,
|
|
|
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
|
|
|
+ .bpp = 16,
|
|
|
+ .dlen = 16,
|
|
|
},
|
|
|
{
|
|
|
.desc = "RGB 565",
|
|
|
.pixelformat = V4L2_PIX_FMT_RGB565,
|
|
|
.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
|
|
|
.bpp = 16,
|
|
|
+ .dlen = 8,
|
|
|
},
|
|
|
{
|
|
|
.desc = "RGB 444",
|
|
|
.pixelformat = V4L2_PIX_FMT_RGB444,
|
|
|
.mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
|
|
|
.bpp = 16,
|
|
|
+ .dlen = 8,
|
|
|
},
|
|
|
|
|
|
};
|
|
@@ -366,9 +382,39 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
params.width = bcap_dev->fmt.width;
|
|
|
params.height = bcap_dev->fmt.height;
|
|
|
params.bpp = bcap_dev->bpp;
|
|
|
+ params.dlen = bcap_dev->dlen;
|
|
|
params.ppi_control = bcap_dev->cfg->ppi_control;
|
|
|
params.int_mask = bcap_dev->cfg->int_mask;
|
|
|
- params.blank_clocks = bcap_dev->cfg->blank_clocks;
|
|
|
+ if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
|
|
|
+ & V4L2_IN_CAP_CUSTOM_TIMINGS) {
|
|
|
+ struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt;
|
|
|
+
|
|
|
+ params.hdelay = bt->hsync + bt->hbackporch;
|
|
|
+ params.vdelay = bt->vsync + bt->vbackporch;
|
|
|
+ params.line = bt->hfrontporch + bt->hsync
|
|
|
+ + bt->hbackporch + bt->width;
|
|
|
+ params.frame = bt->vfrontporch + bt->vsync
|
|
|
+ + bt->vbackporch + bt->height;
|
|
|
+ if (bt->interlaced)
|
|
|
+ params.frame += bt->il_vfrontporch + bt->il_vsync
|
|
|
+ + bt->il_vbackporch;
|
|
|
+ } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
|
|
|
+ & V4L2_IN_CAP_STD) {
|
|
|
+ params.hdelay = 0;
|
|
|
+ params.vdelay = 0;
|
|
|
+ if (bcap_dev->std & V4L2_STD_525_60) {
|
|
|
+ params.line = 858;
|
|
|
+ params.frame = 525;
|
|
|
+ } else {
|
|
|
+ params.line = 864;
|
|
|
+ params.frame = 625;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ params.hdelay = 0;
|
|
|
+ params.vdelay = 0;
|
|
|
+ params.line = params.width + bcap_dev->cfg->blank_pixels;
|
|
|
+ params.frame = params.height;
|
|
|
+ }
|
|
|
ret = ppi->ops->set_params(ppi, ¶ms);
|
|
|
if (ret < 0) {
|
|
|
v4l2_err(&bcap_dev->v4l2_dev,
|
|
@@ -600,6 +646,37 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int bcap_g_dv_timings(struct file *file, void *priv,
|
|
|
+ struct v4l2_dv_timings *timings)
|
|
|
+{
|
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video,
|
|
|
+ g_dv_timings, timings);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ bcap_dev->dv_timings = *timings;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int bcap_s_dv_timings(struct file *file, void *priv,
|
|
|
+ struct v4l2_dv_timings *timings)
|
|
|
+{
|
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
|
+ int ret;
|
|
|
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ bcap_dev->dv_timings = *timings;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int bcap_enum_input(struct file *file, void *priv,
|
|
|
struct v4l2_input *input)
|
|
|
{
|
|
@@ -648,13 +725,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index)
|
|
|
return ret;
|
|
|
}
|
|
|
bcap_dev->cur_input = index;
|
|
|
+ /* if this route has specific config, update ppi control */
|
|
|
+ if (route->ppi_control)
|
|
|
+ config->ppi_control = route->ppi_control;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int bcap_try_format(struct bcap_device *bcap,
|
|
|
struct v4l2_pix_format *pixfmt,
|
|
|
- enum v4l2_mbus_pixelcode *mbus_code,
|
|
|
- int *bpp)
|
|
|
+ struct bcap_format *bcap_fmt)
|
|
|
{
|
|
|
struct bcap_format *sf = bcap->sensor_formats;
|
|
|
struct bcap_format *fmt = NULL;
|
|
@@ -669,16 +748,20 @@ static int bcap_try_format(struct bcap_device *bcap,
|
|
|
if (i == bcap->num_sensor_formats)
|
|
|
fmt = &sf[0];
|
|
|
|
|
|
- if (mbus_code)
|
|
|
- *mbus_code = fmt->mbus_code;
|
|
|
- if (bpp)
|
|
|
- *bpp = fmt->bpp;
|
|
|
v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
|
|
|
ret = v4l2_subdev_call(bcap->sd, video,
|
|
|
try_mbus_fmt, &mbus_fmt);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
v4l2_fill_pix_format(pixfmt, &mbus_fmt);
|
|
|
+ if (bcap_fmt) {
|
|
|
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
|
|
|
+ fmt = &sf[i];
|
|
|
+ if (mbus_fmt.code == fmt->mbus_code)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ *bcap_fmt = *fmt;
|
|
|
+ }
|
|
|
pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
|
|
|
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
|
|
|
return 0;
|
|
@@ -707,7 +790,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct bcap_device *bcap_dev = video_drvdata(file);
|
|
|
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
|
|
|
|
|
|
- return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
|
|
|
+ return bcap_try_format(bcap_dev, pixfmt, NULL);
|
|
|
}
|
|
|
|
|
|
static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
|
|
@@ -724,24 +807,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
{
|
|
|
struct bcap_device *bcap_dev = video_drvdata(file);
|
|
|
struct v4l2_mbus_framefmt mbus_fmt;
|
|
|
- enum v4l2_mbus_pixelcode mbus_code;
|
|
|
+ struct bcap_format bcap_fmt;
|
|
|
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
|
|
|
- int ret, bpp;
|
|
|
+ int ret;
|
|
|
|
|
|
if (vb2_is_busy(&bcap_dev->buffer_queue))
|
|
|
return -EBUSY;
|
|
|
|
|
|
/* see if format works */
|
|
|
- ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
|
|
|
+ ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
|
|
|
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code);
|
|
|
ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
bcap_dev->fmt = *pixfmt;
|
|
|
- bcap_dev->bpp = bpp;
|
|
|
+ bcap_dev->bpp = bcap_fmt.bpp;
|
|
|
+ bcap_dev->dlen = bcap_fmt.dlen;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -832,6 +916,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
|
|
|
.vidioc_querystd = bcap_querystd,
|
|
|
.vidioc_s_std = bcap_s_std,
|
|
|
.vidioc_g_std = bcap_g_std,
|
|
|
+ .vidioc_s_dv_timings = bcap_s_dv_timings,
|
|
|
+ .vidioc_g_dv_timings = bcap_g_dv_timings,
|
|
|
.vidioc_reqbufs = bcap_reqbufs,
|
|
|
.vidioc_querybuf = bcap_querybuf,
|
|
|
.vidioc_qbuf = bcap_qbuf,
|
|
@@ -867,6 +953,7 @@ static int __devinit bcap_probe(struct platform_device *pdev)
|
|
|
struct i2c_adapter *i2c_adap;
|
|
|
struct bfin_capture_config *config;
|
|
|
struct vb2_queue *q;
|
|
|
+ struct bcap_route *route;
|
|
|
int ret;
|
|
|
|
|
|
config = pdev->dev.platform_data;
|
|
@@ -976,6 +1063,12 @@ static int __devinit bcap_probe(struct platform_device *pdev)
|
|
|
NULL);
|
|
|
if (bcap_dev->sd) {
|
|
|
int i;
|
|
|
+ if (!config->num_inputs) {
|
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
|
+ "Unable to work without input\n");
|
|
|
+ goto err_unreg_vdev;
|
|
|
+ }
|
|
|
+
|
|
|
/* update tvnorms from the sub devices */
|
|
|
for (i = 0; i < config->num_inputs; i++)
|
|
|
vfd->tvnorms |= config->inputs[i].std;
|
|
@@ -987,8 +1080,24 @@ static int __devinit bcap_probe(struct platform_device *pdev)
|
|
|
|
|
|
v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
|
|
|
|
|
|
+ /*
|
|
|
+ * explicitly set input, otherwise some boards
|
|
|
+ * may not work at the state as we expected
|
|
|
+ */
|
|
|
+ route = &config->routes[0];
|
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
|
|
|
+ route->input, route->output, 0);
|
|
|
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
|
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
|
|
|
+ goto err_unreg_vdev;
|
|
|
+ }
|
|
|
+ bcap_dev->cur_input = 0;
|
|
|
+ /* if this route has specific config, update ppi control */
|
|
|
+ if (route->ppi_control)
|
|
|
+ config->ppi_control = route->ppi_control;
|
|
|
+
|
|
|
/* now we can probe the default state */
|
|
|
- if (vfd->tvnorms) {
|
|
|
+ if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) {
|
|
|
v4l2_std_id std;
|
|
|
ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
|
|
|
if (ret) {
|
|
@@ -998,6 +1107,17 @@ static int __devinit bcap_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
bcap_dev->std = std;
|
|
|
}
|
|
|
+ if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) {
|
|
|
+ struct v4l2_dv_timings dv_timings;
|
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video,
|
|
|
+ g_dv_timings, &dv_timings);
|
|
|
+ if (ret) {
|
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
|
+ "Unable to get dv timings\n");
|
|
|
+ goto err_unreg_vdev;
|
|
|
+ }
|
|
|
+ bcap_dev->dv_timings = dv_timings;
|
|
|
+ }
|
|
|
ret = bcap_init_sensor_formats(bcap_dev);
|
|
|
if (ret) {
|
|
|
v4l2_err(&bcap_dev->v4l2_dev,
|