[PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Thu Apr 21 06:18:05 EDT 2011
Hi Manjunath,
On Wednesday 20 April 2011 17:30:08 Hadli, Manjunath wrote:
> Hi Laurent,
> Thank you for you very valuable and detailed comments. I have fixed a lot
> of your suggestions and there are a few questions I need more explanation
> for. I will send the fixed and updated patches as a follow-up after your
> clarifications.
OK. Please see below for answsers.
> On Thu, Apr 07, 2011 at 17:28:20, Laurent Pinchart wrote:
> > On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote:
[snip]
> > > +static u32 video2_numbuffers = 3;
> > > +static u32 video3_numbuffers = 3;
> >
> > Why is the number of buffers set by a module parameter ? It should be
> > negotiated dynamically with REQBUFS.
>
> This is the minimum number of buffers to be allocated by the system as
> there is no scatter-gather mechanism in Davinci. To make sure of
> availability of a minimum numbers of buffers for the system which may not
> be available otherwise due to fragmentation, these are used.
But you don't reserve the memory when the driver is probed, so how does this
help ?
[snip]
> > > + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj;
> > > +
> > > + /* Convert time represention from jiffies to timeval */
> > > + jiffies_to_timeval(jiffies_time, &timevalue);
> >
> > Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp.
>
> Fixed. Used do_gettimeofday().
Please use ktime_get_ts() instead. It will return a monotonic clock timestamp.
Otherwise the buffer timestamp will vary when the system clock is modified (as
a result of an NTP time update for instance).
> > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
> > > + layer = disp_dev->dev[i];
> > > + /* If streaming is started in this layer */
> > > + if (!layer->started)
> > > + continue;
> > > + /* Check the field format */
> > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
> > > + (event & OSD_END_OF_FRAME)) {
> > > + /* Progressive mode */
> > > + if (layer_first_int[i]) {
> > > + layer_first_int[i] = 0;
> > > + continue;
> > > + }
> > > + /*
> > > + * Mark status of the cur_frm to
> > > + * done and unlock semaphore on it
> > > + */
> > > +
> > > + if (layer->cur_frm != layer->next_frm) {
> > > + layer->cur_frm->ts = timevalue;
> > > + layer->cur_frm->state = VIDEOBUF_DONE;
> >
> > Please use videobuf2.
>
> I would like to get to videobuf2 as a next set of changes. I want to get
> the Dm6446 driver fisrt, add it with Dm365 and do the videobuf2 later. I
> hope it is okay.
We're trying to get rid of videobuf1, so accepting new drivers that make use
of videobuf1 is a bit problematic. Have you had a look at videobuf2 ? How much
time do you think it would take to convert this driver to videobuf2 ? Let's
first address all the other issues, and then tackle that one.
[snip]
> > > +/* interrupt service routine */
> > > +static irqreturn_t venc_isr(int irq, void *arg)
> > > +{
> > > + static unsigned last_event;
> > > + unsigned event = 0;
> > > +
> > > + if (venc_is_second_field())
> > > + event |= VENC_SECOND_FIELD;
> > > + else
> > > + event |= VENC_FIRST_FIELD;
> > > +
> > > + if (event == (last_event & ~VENC_END_OF_FRAME)) {
> > > + /*
> > > + * If the display is non-interlaced, then we need to flag
> > > the + * end-of-frame event at every interrupt regardless of
> > > the + * value of the FIDST bit. We can conclude that the
> > > display is + * non-interlaced if the value of the FIDST bit
> > > is unchanged + * from the previous interrupt.
> > > + */
> >
> > What about checking pix_fmt.field instead ?
>
> Not sure what you mean here. We get the FIRST or second field event from
> the hardware so we need to check the register value rather than
> pix_fmt.field.
Interlacing is configured by userspace. When configured in interlaced mode, I
expect the device to alternate fields. When configured in progressive mode, I
expect it to always return the same field. If that's correct, the FIDST bit is
only needed to identify the active field when configured in interlaced mode.
pix_fmt.field can be use to check whether we're in interlaced or progressive
mode.
[snip]
> > > +static int vpbe_display_querycap(struct file *file, void *priv,
> > > + struct v4l2_capability *cap)
> > > +{
> > > + struct vpbe_fh *fh = file->private_data;
> > > + struct vpbe_display_obj *layer = fh->layer;
> > > +
> > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
> > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id);
> >
> > Do you really need a debugging call here ?
>
> I am Ok either ways. When debugging is enabled, it is just one of the data
> points since there are multiple windows.
You could leave it, but a debug call in the querycap handler doesn't look very
useful to me.
> > > + *cap = vpbe_display_videocap;
> > > +
> > > + return 0;
> > > +}
[snip]
> > > +static int vpbe_display_g_fmt(struct file *file, void *priv,
> > > + struct v4l2_format *fmt)
> > > +{
> > > + struct vpbe_fh *fh = file->private_data;
> > > + struct vpbe_display_obj *layer = fh->layer;
> > > +
> > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
> > > + "VIDIOC_G_FMT, layer id = %d\n",
> > > + layer->device_id);
> > > +
> > > + /* If buffer type is video output */
> > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) {
> > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> > > + /* Fill in the information about format */
> > > + *pixfmt = layer->pix_fmt;
> >
> > I don't see anything wrong in doing
> >
> > fmt->fmt.pix = layer->pix_fmt;
> >
> > directly.
>
> Wel,. In the past patches we had a large amount of multiple indirections,
> and as part of a suggestion on open source I removed all of them with a
> general rule of not having more than 2 indirections. What is your take on
> this? How many indirection levels do you allow?
There's no hard rule for that. I usually use intermediate pointers when the
number of indirection levels grow too high, except when I only need to perform
the indirection once or twice, like in this case.
> > > + } else {
> > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + return 0;
> > > +}
[snip]
> > > +static int vpbe_display_s_fmt(struct file *file, void *priv,
> > > + struct v4l2_format *fmt)
> > > +{
> > > + int ret = 0;
> > > + struct vpbe_fh *fh = file->private_data;
> > > + struct vpbe_display *disp_dev = video_drvdata(file);
> > > + struct vpbe_display_obj *layer = fh->layer;
> > > + struct osd_layer_config *cfg = &layer->layer_info.config;
> >
> > Variables are often declared in longuest to shortest line order in kernel
> > drivers. It might not be a requirement though, but I find it to make code
> > more readable.
>
> cannot do as suggested since the structure variable assignments have
> dependency on previous structure variables.
Oops, my bad.
> > > +
> > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
> > > + "VIDIOC_S_FMT, layer id = %d\n",
> > > + layer->device_id);
> > > +
> > > + /* If streaming is started, return error */
> > > + if (layer->started) {
> >
> > I'm pretty sure there's a race condition here.
>
> Not sure about this. The entire driver is under V4L2 lock per layer handle
> and it would not allow another call to come here once in.
I missed that. Probably because I dislike that lock :-)
> Can you elaborate how is it a race condition?
>
> > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
> > > + return -EBUSY;
> > > + }
> > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
> > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
> > > + return -EINVAL;
> > > + } else {
> > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> > > + /* Check for valid pixel format */
> > > + ret = vpbe_try_format(disp_dev, pixfmt, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* YUV420 is requested, check availability of the
> > > + other video window */
> > > +
> > > + layer->pix_fmt = *pixfmt;
> > > +
> > > + /* Get osd layer config */
> > > + osd_device->ops.get_layer_config(osd_device,
> > > + layer->layer_info.id, cfg);
> > > + /* Store the pixel format in the layer object */
> > > + cfg->xsize = pixfmt->width;
> > > + cfg->ysize = pixfmt->height;
> > > + cfg->line_length = pixfmt->bytesperline;
> > > + cfg->ypos = 0;
> > > + cfg->xpos = 0;
> > > + cfg->interlaced = vpbe_dev->current_timings.interlaced;
> > > +
> > > + /* Change of the default pixel format for both video
> > > windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
> > > + struct vpbe_display_obj *otherlayer;
> >
> > If the requested format isn't NV12, cfg->pixfmt won't be modified. If it
> > has been set to NV12 by a previous S_FMT call, it won't become YUYV. Is
> > that intentional ?
>
> It is reset to YUYV as part of Open. So it is changed only if it is NV12.
As stated below, you shouldn't reset formats in open().
> > > + cfg->pixfmt = PIXFMT_NV12;
> > > + otherlayer = _vpbe_display_get_other_win(disp_dev,
> > > + layer);
> > > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
> > > + }
> > > +
> > > + /* Set the layer config in the osd window */
> > > + ret = osd_device->ops.set_layer_config(osd_device,
> > > + layer->layer_info.id, cfg);
> > > + if (ret < 0) {
> > > + v4l2_err(&vpbe_dev->v4l2_dev,
> > > + "Error in S_FMT params:\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + /* Readback and fill the local copy of current pix format
> > > */ + osd_device->ops.get_layer_config(osd_device,
> > > + layer->layer_info.id, cfg);
> > > +
> > > + /* verify if readback values are as expected */
> > > + if (layer->pix_fmt.width != cfg->xsize ||
> > > + layer->pix_fmt.height != cfg->ysize ||
> > > + layer->pix_fmt.bytesperline != layer->layer_info.
> > > + config.line_length || (cfg->interlaced &&
> > > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) ||
> > > + (!cfg->interlaced && layer->pix_fmt.field !=
> > > + V4L2_FIELD_NONE)) {
> > > + v4l2_err(&vpbe_dev->v4l2_dev,
> > > + "mismatch:layer conf params:\n");
> > > + return -EINVAL;
> > > + }
> > > + }
> > > +
> > > + return 0;
> > > +}
[snip]
> > > +/**
> > > + * vpbe_display_g_std - Get the standard in the current encoder
> > > + *
> > > + * Get the standard in the current encoder. Return the status. 0 -
> > > success + * -EINVAL on error
> > > + */
> > > +static int vpbe_display_g_std(struct file *file, void *priv,
> > > + v4l2_std_id *std_id)
> > > +{
> > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
> > > +
> > > + /* Get the standard from the current encoder */
> > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
> > > + *std_id = vpbe_dev->current_timings.timings.std_id;
> > > + return 0;
> > > + }
> > > + return -EINVAL;
> >
> > Where do you set timings_type ? When can this return an error ?
>
> It can return an error if the driver is set to a DV_PRESET mode.
> timings_type would tell whether is is an SD standard or a DV_PRESET.
But you don't seem to set it anywhere.
> > > +}
[snip]
> > > +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id
> > > id, + struct vpbe_display *disp_dev)
> > > +{
> > > + int err = 0;
> > > + struct osd_layer_config *layer_config;
> > > + struct vpbe_display_obj *layer = disp_dev->dev[id];
> > > + struct osd_layer_config *cfg = &layer->layer_info.config;
> > > +
> > > + /* First claim the layer for this device */
> > > + err = osd_device->ops.request_layer(osd_device,
> > > + layer->layer_info.id);
> > > + if (err < 0) {
> > > + /* Couldn't get layer */
> > > + v4l2_err(&vpbe_dev->v4l2_dev,
> > > + "Display Manager failed to allocate layer\n");
> > > + return -EBUSY;
> > > + }
> > > +
> > > + layer_config = cfg;
> > > + /* Set the default image and crop values */
> > > + layer_config->pixfmt = PIXFMT_YCbCrI;
> > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY;
> > > + layer->pix_fmt.bytesperline = layer_config->line_length =
> > > + vpbe_dev->current_timings.xres * 2;
> > > +
> > > + layer->pix_fmt.width = layer_config->xsize =
> > > + vpbe_dev->current_timings.xres;
> > > + layer->pix_fmt.height = layer_config->ysize =
> > > + vpbe_dev->current_timings.yres;
> > > + layer->pix_fmt.sizeimage =
> > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height;
> > > + layer_config->xpos = 0;
> > > + layer_config->ypos = 0;
> > > + layer_config->interlaced = vpbe_dev->current_timings.interlaced;
> >
> > You shouldn't reinitialized the format every time the device is opened.
> > The previously set format should be kept.
>
> This strictly followed across drivers? I am Ok if that is the expectation.
It's how V4L2 drivers should behave. Some drivers might not follow that rule,
but that would be a bug :-)
> > > +
> > > + /*
> > > + * turn off ping-pong buffer and field inversion to fix
> > > + * the image shaking problem in 1080I mode
> > > + */
> > > +
> > > + if (cfg->interlaced)
> > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED;
> > > + else
> > > + layer->pix_fmt.field = V4L2_FIELD_NONE;
> > > +
> > > + err = osd_device->ops.set_layer_config(osd_device,
> > > + layer->layer_info.id,
> > > + layer_config);
> > > + if (err < 0) {
> > > + /* Couldn't set layer */
> > > + v4l2_err(&vpbe_dev->v4l2_dev,
> > > + "Display Manager failed to set osd layer\n");
> > > + return -EBUSY;
> > > + }
> > > +
> > > + return 0;
> > > +}
[snip]
> > > +/*
> > > + * vpbe_display_probe()
> > > + * This function creates device entries by register itself to the V4L2
> > > driver + * and initializes fields of each layer objects
> > > + */
> > > +static __devinit int vpbe_display_probe(struct platform_device *pdev)
> > > +{
> > > + int i, j = 0, k, err = 0;
> > > + struct vpbe_display *disp_dev;
> > > + struct video_device *vbd = NULL;
> > > + struct vpbe_display_obj *vpbe_display_layer = NULL;
> > > + struct resource *res;
> > > + int irq;
> > > +
> > > + printk(KERN_DEBUG "vpbe_display_probe\n");
> > > +
> > > + /* Allocate memory for vpbe_display */
> > > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
> > > + if (!disp_dev) {
> > > + printk(KERN_ERR "ran out of memory\n");
> > > + return -ENOMEM;
> > > + }
> > > +
> > > + /* Allocate memory for four plane display objects */
> > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
> > > + disp_dev->dev[i] =
> > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL);
> > > + /* If memory allocation fails, return error */
> > > + if (!disp_dev->dev[i]) {
> > > + printk(KERN_ERR "ran out of memory\n");
> > > + err = -ENOMEM;
> > > + goto probe_out;
> > > + }
> > > + spin_lock_init(&disp_dev->dev[i]->irqlock);
> > > + mutex_init(&disp_dev->dev[i]->opslock);
> > > + }
> > > + spin_lock_init(&disp_dev->dma_queue_lock);
> > > +
> > > + err = init_vpbe_layer_objects(i);
> > > + if (err) {
> > > + printk(KERN_ERR "Error initializing vpbe display\n");
> > > + return err;
> > > + }
> > > +
> > > + /*
> > > + * Scan all the platform devices to find the vpbe
> > > + * controller device and get the vpbe_dev object
> > > + */
> > > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL,
> > > + vpbe_device_get);
> > > + if (err < 0)
> > > + return err;
> > > +
> > > + /* Initialize the vpbe display controller */
> > > + if (NULL != vpbe_dev->ops.initialize) {
> > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev);
> > > + if (err) {
> > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing
> > > vpbe\n"); + err = -ENOMEM;
> > > + goto probe_out;
> > > + }
> > > + }
> > > +
> > > + /* check the name of davinci device */
> > > + if (vpbe_dev->cfg->module_name != NULL)
> > > + strcpy(vpbe_display_videocap.card,
> > > + vpbe_dev->cfg->module_name);
> > > +
> > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
> > > + /* Get the pointer to the layer object */
> > > + vpbe_display_layer = disp_dev->dev[i];
> > > + /* Allocate memory for video device */
> > > + vbd = video_device_alloc();
> > > + if (vbd == NULL) {
> > > + for (j = 0; j < i; j++) {
> > > + video_device_release(
> > > + disp_dev->dev[j]->video_dev);
> > > + }
> > > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of
> > > memory\n"); + err = -ENOMEM;
> > > + goto probe_out;
> > > + }
> > > + /* Initialize field of video device */
> > > + vbd->release = video_device_release;
> > > + vbd->fops = &vpbe_fops;
> > > + vbd->ioctl_ops = &vpbe_ioctl_ops;
> > > + vbd->minor = -1;
> > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev;
> > > + vbd->lock = &vpbe_display_layer->opslock;
> > > +
> > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD)
> > > { + vbd->tvnorms = (V4L2_STD_525_60 |
> > > V4L2_STD_625_50); + vbd->current_norm =
> > > + vpbe_dev->current_timings.timings.std_id;
> > > + } else
> > > + vbd->current_norm = 0;
> > > +
> > > + snprintf(vbd->name, sizeof(vbd->name),
> > > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
> > > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
> > > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
> > > + (VPBE_DISPLAY_VERSION_CODE) & 0xff);
> > > +
> > > + /* Set video_dev to the video device */
> > > + vpbe_display_layer->video_dev = vbd;
> > > + vpbe_display_layer->device_id = i;
> > > +
> > > + vpbe_display_layer->layer_info.id =
> > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
> > > + if (display_buf_config_params.numbuffers[i] == 0)
> > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR;
> > > + else
> > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP;
> > > +
> > > + /* Initialize field of the display layer objects */
> > > + vpbe_display_layer->usrs = 0;
> > > + vpbe_display_layer->io_usrs = 0;
> > > + vpbe_display_layer->started = 0;
> > > +
> > > + /* Initialize prio member of layer object */
> > > + v4l2_prio_init(&vpbe_display_layer->prio);
> > > +
> > > + /* Register video device */
> > > + v4l2_info(&vpbe_dev->v4l2_dev,
> > > + "Trying to register VPBE display device.\n");
> > > + v4l2_info(&vpbe_dev->v4l2_dev,
> > > + "layer=%x,layer->video_dev=%x\n",
> > > + (int)vpbe_display_layer,
> > > + (int)&vpbe_display_layer->video_dev);
> > > +
> > > + err = video_register_device(vpbe_display_layer->
> > > + video_dev,
> > > + VFL_TYPE_GRABBER,
> > > + vpbe_display_nr[i]);
> > > + if (err)
> > > + goto probe_out;
> > > + /* set the driver data in platform device */
> > > + platform_set_drvdata(pdev, disp_dev);
> > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev);
> > > + }
> > > +
> > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > > + if (!res) {
> > > + v4l2_err(&vpbe_dev->v4l2_dev,
> > > + "Unable to get VENC interrupt resource\n");
> > > + err = -ENODEV;
> > > + goto probe_out;
> > > + }
> > > + irq = res->start;
> > > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
> > > + disp_dev)) {
> > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request
> > > interrupt\n"); + err = -ENODEV;
> > > + goto probe_out;
> > > + }
> >
> > You probably want to get the resources and register the interrupt handler
> > before registering the V4L2 devices, otherwise userspace will be able to
> > open devices before you're done with the initialization.
>
> I do not think anyone would attempt to open the device so soon as the boot
> process is not complete yet.
Unless you compile the driver as a module. In that case an application could
open the device as soon as it gets registered. hal is known to do that.
> Also, if the irq is registered before, the interrupts start and the driver
> crashes for lack of initialized structure variables.
Why would an interrupt occur before the device gets opened ?
> > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2
> > > device\n"); + return 0;
> > > +probe_out:
> > > + kfree(disp_dev);
> > > +
> > > + for (k = 0; k < j; k++) {
> > > + /* Get the pointer to the layer object */
> > > + vpbe_display_layer = disp_dev->dev[k];
> > > + /* Unregister video device */
> > > + video_unregister_device(vpbe_display_layer->video_dev);
> > > + /* Release video device */
> > > + video_device_release(vpbe_display_layer->video_dev);
> > > + vpbe_display_layer->video_dev = NULL;
> > > + }
> > > + return err;
> > > +}
> >
> > [snip]
> >
> > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
> >
> > Should this be "TI DM644x" instead ?
>
> This is a common IP for DM644x, Dm355 and Dm365 for which the patches will
> follow after this set. So I think it is OK.
What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports
all DaVinci chips.
--
Regards,
Laurent Pinchart
More information about the linux-arm-kernel
mailing list