Skip to content

Commit

Permalink
[media] v4l2: blackfin: add EPPI3 support
Browse files Browse the repository at this point in the history
Bf60x soc has a new PPI called Enhanced PPI version 3.
HD video is supported now. To achieve this, we redesign
ppi params and add dv timings feature.

Signed-off-by: Scott Jiang <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
  • Loading branch information
Scott Jiang authored and Mauro Carvalho Chehab committed Dec 26, 2012
1 parent fab0e8f commit 45b8259
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 36 deletions.
148 changes: 134 additions & 14 deletions drivers/media/platform/blackfin/bfin_capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 */
Expand Down Expand Up @@ -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,
},

};
Expand Down Expand Up @@ -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, &params);
if (ret < 0) {
v4l2_err(&bcap_dev->v4l2_dev,
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 45b8259

Please sign in to comment.