// SPDX-License-Identifier: GPL-2.0-only 
 | 
/* 
 | 
 * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ 
 | 
 */ 
 | 
#include <linux/kernel.h> 
 | 
#include <linux/init.h> 
 | 
#include <linux/module.h> 
 | 
#include <linux/errno.h> 
 | 
#include <linux/interrupt.h> 
 | 
#include <linux/string.h> 
 | 
#include <linux/wait.h> 
 | 
#include <linux/time.h> 
 | 
#include <linux/platform_device.h> 
 | 
#include <linux/irq.h> 
 | 
#include <linux/mm.h> 
 | 
#include <linux/mutex.h> 
 | 
#include <linux/videodev2.h> 
 | 
#include <linux/slab.h> 
 | 
  
 | 
  
 | 
#include <media/v4l2-dev.h> 
 | 
#include <media/v4l2-common.h> 
 | 
#include <media/v4l2-ioctl.h> 
 | 
#include <media/v4l2-device.h> 
 | 
#include <media/davinci/vpbe_display.h> 
 | 
#include <media/davinci/vpbe_types.h> 
 | 
#include <media/davinci/vpbe.h> 
 | 
#include <media/davinci/vpbe_venc.h> 
 | 
#include <media/davinci/vpbe_osd.h> 
 | 
#include "vpbe_venc_regs.h" 
 | 
  
 | 
#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" 
 | 
  
 | 
static int debug; 
 | 
  
 | 
#define VPBE_DEFAULT_NUM_BUFS 3 
 | 
  
 | 
module_param(debug, int, 0644); 
 | 
  
 | 
static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, 
 | 
            struct vpbe_layer *layer); 
 | 
  
 | 
static int venc_is_second_field(struct vpbe_display *disp_dev) 
 | 
{ 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    int ret, val; 
 | 
  
 | 
    ret = v4l2_subdev_call(vpbe_dev->venc, 
 | 
                   core, 
 | 
                   ioctl, 
 | 
                   VENC_GET_FLD, 
 | 
                   &val); 
 | 
    if (ret < 0) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
             "Error in getting Field ID 0\n"); 
 | 
        return 1; 
 | 
    } 
 | 
    return val; 
 | 
} 
 | 
  
 | 
static void vpbe_isr_even_field(struct vpbe_display *disp_obj, 
 | 
                struct vpbe_layer *layer) 
 | 
{ 
 | 
    if (layer->cur_frm == layer->next_frm) 
 | 
        return; 
 | 
  
 | 
    layer->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); 
 | 
    vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); 
 | 
    /* Make cur_frm pointing to next_frm */ 
 | 
    layer->cur_frm = layer->next_frm; 
 | 
} 
 | 
  
 | 
static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, 
 | 
                struct vpbe_layer *layer) 
 | 
{ 
 | 
    struct osd_state *osd_device = disp_obj->osd_device; 
 | 
    unsigned long addr; 
 | 
  
 | 
    spin_lock(&disp_obj->dma_queue_lock); 
 | 
    if (list_empty(&layer->dma_queue) || 
 | 
        (layer->cur_frm != layer->next_frm)) { 
 | 
        spin_unlock(&disp_obj->dma_queue_lock); 
 | 
        return; 
 | 
    } 
 | 
    /* 
 | 
     * one field is displayed configure 
 | 
     * the next frame if it is available 
 | 
     * otherwise hold on current frame 
 | 
     * Get next from the buffer queue 
 | 
     */ 
 | 
    layer->next_frm = list_entry(layer->dma_queue.next, 
 | 
              struct  vpbe_disp_buffer, list); 
 | 
    /* Remove that from the buffer queue */ 
 | 
    list_del(&layer->next_frm->list); 
 | 
    spin_unlock(&disp_obj->dma_queue_lock); 
 | 
    /* Mark state of the frame to active */ 
 | 
    layer->next_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 
 | 
    addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb.vb2_buf, 0); 
 | 
    osd_device->ops.start_layer(osd_device, 
 | 
            layer->layer_info.id, 
 | 
            addr, 
 | 
            disp_obj->cbcr_ofst); 
 | 
} 
 | 
  
 | 
/* interrupt service routine */ 
 | 
static irqreturn_t venc_isr(int irq, void *arg) 
 | 
{ 
 | 
    struct vpbe_display *disp_dev = (struct vpbe_display *)arg; 
 | 
    struct vpbe_layer *layer; 
 | 
    static unsigned last_event; 
 | 
    unsigned event = 0; 
 | 
    int fid; 
 | 
    int i; 
 | 
  
 | 
    if (!arg || !disp_dev->dev[0]) 
 | 
        return IRQ_HANDLED; 
 | 
  
 | 
    if (venc_is_second_field(disp_dev)) 
 | 
        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. 
 | 
        */ 
 | 
        event |= VENC_END_OF_FRAME; 
 | 
    } else if (event == VENC_SECOND_FIELD) { 
 | 
        /* end-of-frame for interlaced display */ 
 | 
        event |= VENC_END_OF_FRAME; 
 | 
    } 
 | 
    last_event = event; 
 | 
  
 | 
    for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { 
 | 
        layer = disp_dev->dev[i]; 
 | 
  
 | 
        if (!vb2_start_streaming_called(&layer->buffer_queue)) 
 | 
            continue; 
 | 
  
 | 
        if (layer->layer_first_int) { 
 | 
            layer->layer_first_int = 0; 
 | 
            continue; 
 | 
        } 
 | 
        /* Check the field format */ 
 | 
        if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && 
 | 
            (event & VENC_END_OF_FRAME)) { 
 | 
            /* Progressive mode */ 
 | 
  
 | 
            vpbe_isr_even_field(disp_dev, layer); 
 | 
            vpbe_isr_odd_field(disp_dev, layer); 
 | 
        } else { 
 | 
        /* Interlaced mode */ 
 | 
  
 | 
            layer->field_id ^= 1; 
 | 
            if (event & VENC_FIRST_FIELD) 
 | 
                fid = 0; 
 | 
            else 
 | 
                fid = 1; 
 | 
  
 | 
            /* 
 | 
            * If field id does not match with store 
 | 
            * field id 
 | 
            */ 
 | 
            if (fid != layer->field_id) { 
 | 
                /* Make them in sync */ 
 | 
                layer->field_id = fid; 
 | 
                continue; 
 | 
            } 
 | 
            /* 
 | 
            * device field id and local field id are 
 | 
            * in sync. If this is even field 
 | 
            */ 
 | 
            if (0 == fid) 
 | 
                vpbe_isr_even_field(disp_dev, layer); 
 | 
            else  /* odd field */ 
 | 
                vpbe_isr_odd_field(disp_dev, layer); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return IRQ_HANDLED; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_buffer_prepare() 
 | 
 * This is the callback function called from vb2_qbuf() function 
 | 
 * the buffer is prepared and user space virtual address is converted into 
 | 
 * physical address 
 | 
 */ 
 | 
static int vpbe_buffer_prepare(struct vb2_buffer *vb) 
 | 
{ 
 | 
    struct vb2_queue *q = vb->vb2_queue; 
 | 
    struct vpbe_layer *layer = vb2_get_drv_priv(q); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    unsigned long addr; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
                "vpbe_buffer_prepare\n"); 
 | 
  
 | 
    vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage); 
 | 
    if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) 
 | 
        return -EINVAL; 
 | 
  
 | 
    addr = vb2_dma_contig_plane_dma_addr(vb, 0); 
 | 
    if (!IS_ALIGNED(addr, 8)) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
             "buffer_prepare:offset is not aligned to 32 bytes\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_buffer_setup() 
 | 
 * This function allocates memory for the buffers 
 | 
 */ 
 | 
static int 
 | 
vpbe_buffer_queue_setup(struct vb2_queue *vq, 
 | 
            unsigned int *nbuffers, unsigned int *nplanes, 
 | 
            unsigned int sizes[], struct device *alloc_devs[]) 
 | 
  
 | 
{ 
 | 
    /* Get the file handle object and layer object */ 
 | 
    struct vpbe_layer *layer = vb2_get_drv_priv(vq); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); 
 | 
  
 | 
    /* Store number of buffers allocated in numbuffer member */ 
 | 
    if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS) 
 | 
        *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers; 
 | 
  
 | 
    if (*nplanes) 
 | 
        return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0; 
 | 
  
 | 
    *nplanes = 1; 
 | 
    sizes[0] = layer->pix_fmt.sizeimage; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_buffer_queue() 
 | 
 * This function adds the buffer to DMA queue 
 | 
 */ 
 | 
static void vpbe_buffer_queue(struct vb2_buffer *vb) 
 | 
{ 
 | 
    struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 
 | 
    /* Get the file handle object and layer object */ 
 | 
    struct vpbe_disp_buffer *buf = container_of(vbuf, 
 | 
                struct vpbe_disp_buffer, vb); 
 | 
    struct vpbe_layer *layer = vb2_get_drv_priv(vb->vb2_queue); 
 | 
    struct vpbe_display *disp = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    unsigned long flags; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
            "vpbe_buffer_queue\n"); 
 | 
  
 | 
    /* add the buffer to the DMA queue */ 
 | 
    spin_lock_irqsave(&disp->dma_queue_lock, flags); 
 | 
    list_add_tail(&buf->list, &layer->dma_queue); 
 | 
    spin_unlock_irqrestore(&disp->dma_queue_lock, flags); 
 | 
} 
 | 
  
 | 
static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) 
 | 
{ 
 | 
    struct vpbe_layer *layer = vb2_get_drv_priv(vq); 
 | 
    struct osd_state *osd_device = layer->disp_dev->osd_device; 
 | 
    int ret; 
 | 
  
 | 
    osd_device->ops.disable_layer(osd_device, layer->layer_info.id); 
 | 
  
 | 
    /* Get the next frame from the buffer queue */ 
 | 
    layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, 
 | 
                struct vpbe_disp_buffer, list); 
 | 
    /* Remove buffer from the buffer queue */ 
 | 
    list_del(&layer->cur_frm->list); 
 | 
    /* Mark state of the current frame to active */ 
 | 
    layer->cur_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 
 | 
    /* Initialize field_id and started member */ 
 | 
    layer->field_id = 0; 
 | 
  
 | 
    /* Set parameters in OSD and VENC */ 
 | 
    ret = vpbe_set_osd_display_params(layer->disp_dev, layer); 
 | 
    if (ret < 0) { 
 | 
        struct vpbe_disp_buffer *buf, *tmp; 
 | 
  
 | 
        vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, 
 | 
                VB2_BUF_STATE_QUEUED); 
 | 
        list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) { 
 | 
            list_del(&buf->list); 
 | 
            vb2_buffer_done(&buf->vb.vb2_buf, 
 | 
                    VB2_BUF_STATE_QUEUED); 
 | 
        } 
 | 
  
 | 
        return ret; 
 | 
    } 
 | 
  
 | 
    /* 
 | 
     * if request format is yuv420 semiplanar, need to 
 | 
     * enable both video windows 
 | 
     */ 
 | 
    layer->layer_first_int = 1; 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static void vpbe_stop_streaming(struct vb2_queue *vq) 
 | 
{ 
 | 
    struct vpbe_layer *layer = vb2_get_drv_priv(vq); 
 | 
    struct osd_state *osd_device = layer->disp_dev->osd_device; 
 | 
    struct vpbe_display *disp = layer->disp_dev; 
 | 
    unsigned long flags; 
 | 
  
 | 
    if (!vb2_is_streaming(vq)) 
 | 
        return; 
 | 
  
 | 
    osd_device->ops.disable_layer(osd_device, layer->layer_info.id); 
 | 
  
 | 
    /* release all active buffers */ 
 | 
    spin_lock_irqsave(&disp->dma_queue_lock, flags); 
 | 
    if (layer->cur_frm == layer->next_frm) { 
 | 
        vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, 
 | 
                VB2_BUF_STATE_ERROR); 
 | 
    } else { 
 | 
        if (layer->cur_frm) 
 | 
            vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, 
 | 
                    VB2_BUF_STATE_ERROR); 
 | 
        if (layer->next_frm) 
 | 
            vb2_buffer_done(&layer->next_frm->vb.vb2_buf, 
 | 
                    VB2_BUF_STATE_ERROR); 
 | 
    } 
 | 
  
 | 
    while (!list_empty(&layer->dma_queue)) { 
 | 
        layer->next_frm = list_entry(layer->dma_queue.next, 
 | 
                        struct vpbe_disp_buffer, list); 
 | 
        list_del(&layer->next_frm->list); 
 | 
        vb2_buffer_done(&layer->next_frm->vb.vb2_buf, 
 | 
                VB2_BUF_STATE_ERROR); 
 | 
    } 
 | 
    spin_unlock_irqrestore(&disp->dma_queue_lock, flags); 
 | 
} 
 | 
  
 | 
static const struct vb2_ops video_qops = { 
 | 
    .queue_setup = vpbe_buffer_queue_setup, 
 | 
    .wait_prepare = vb2_ops_wait_prepare, 
 | 
    .wait_finish = vb2_ops_wait_finish, 
 | 
    .buf_prepare = vpbe_buffer_prepare, 
 | 
    .start_streaming = vpbe_start_streaming, 
 | 
    .stop_streaming = vpbe_stop_streaming, 
 | 
    .buf_queue = vpbe_buffer_queue, 
 | 
}; 
 | 
  
 | 
static 
 | 
struct vpbe_layer* 
 | 
_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, 
 | 
            struct vpbe_layer *layer) 
 | 
{ 
 | 
    enum vpbe_display_device_id thiswin, otherwin; 
 | 
    thiswin = layer->device_id; 
 | 
  
 | 
    otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ? 
 | 
    VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0; 
 | 
    return disp_dev->dev[otherwin]; 
 | 
} 
 | 
  
 | 
static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, 
 | 
            struct vpbe_layer *layer) 
 | 
{ 
 | 
    struct osd_layer_config *cfg  = &layer->layer_info.config; 
 | 
    struct osd_state *osd_device = disp_dev->osd_device; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    unsigned long addr; 
 | 
    int ret; 
 | 
  
 | 
    addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb.vb2_buf, 0); 
 | 
    /* Set address in the display registers */ 
 | 
    osd_device->ops.start_layer(osd_device, 
 | 
                    layer->layer_info.id, 
 | 
                    addr, 
 | 
                    disp_dev->cbcr_ofst); 
 | 
  
 | 
    ret = osd_device->ops.enable_layer(osd_device, 
 | 
                layer->layer_info.id, 0); 
 | 
    if (ret < 0) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
            "Error in enabling osd window layer 0\n"); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    /* Enable the window */ 
 | 
    layer->layer_info.enable = 1; 
 | 
    if (cfg->pixfmt == PIXFMT_NV12) { 
 | 
        struct vpbe_layer *otherlayer = 
 | 
            _vpbe_display_get_other_win_layer(disp_dev, layer); 
 | 
  
 | 
        ret = osd_device->ops.enable_layer(osd_device, 
 | 
                otherlayer->layer_info.id, 1); 
 | 
        if (ret < 0) { 
 | 
            v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
                "Error in enabling osd window layer 1\n"); 
 | 
            return -1; 
 | 
        } 
 | 
        otherlayer->layer_info.enable = 1; 
 | 
    } 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static void 
 | 
vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, 
 | 
            struct vpbe_layer *layer, 
 | 
            int expected_xsize, int expected_ysize) 
 | 
{ 
 | 
    struct display_layer_info *layer_info = &layer->layer_info; 
 | 
    struct v4l2_pix_format *pixfmt = &layer->pix_fmt; 
 | 
    struct osd_layer_config *cfg  = &layer->layer_info.config; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    int calculated_xsize; 
 | 
    int h_exp = 0; 
 | 
    int v_exp = 0; 
 | 
    int h_scale; 
 | 
    int v_scale; 
 | 
  
 | 
    v4l2_std_id standard_id = vpbe_dev->current_timings.std_id; 
 | 
  
 | 
    /* 
 | 
     * Application initially set the image format. Current display 
 | 
     * size is obtained from the vpbe display controller. expected_xsize 
 | 
     * and expected_ysize are set through S_SELECTION ioctl. Based on this, 
 | 
     * driver will calculate the scale factors for vertical and 
 | 
     * horizontal direction so that the image is displayed scaled 
 | 
     * and expanded. Application uses expansion to display the image 
 | 
     * in a square pixel. Otherwise it is displayed using displays 
 | 
     * pixel aspect ratio.It is expected that application chooses 
 | 
     * the crop coordinates for cropped or scaled display. if crop 
 | 
     * size is less than the image size, it is displayed cropped or 
 | 
     * it is displayed scaled and/or expanded. 
 | 
     * 
 | 
     * to begin with, set the crop window same as expected. Later we 
 | 
     * will override with scaled window size 
 | 
     */ 
 | 
  
 | 
    cfg->xsize = pixfmt->width; 
 | 
    cfg->ysize = pixfmt->height; 
 | 
    layer_info->h_zoom = ZOOM_X1;    /* no horizontal zoom */ 
 | 
    layer_info->v_zoom = ZOOM_X1;    /* no horizontal zoom */ 
 | 
    layer_info->h_exp = H_EXP_OFF;    /* no horizontal zoom */ 
 | 
    layer_info->v_exp = V_EXP_OFF;    /* no horizontal zoom */ 
 | 
  
 | 
    if (pixfmt->width < expected_xsize) { 
 | 
        h_scale = vpbe_dev->current_timings.xres / pixfmt->width; 
 | 
        if (h_scale < 2) 
 | 
            h_scale = 1; 
 | 
        else if (h_scale >= 4) 
 | 
            h_scale = 4; 
 | 
        else 
 | 
            h_scale = 2; 
 | 
        cfg->xsize *= h_scale; 
 | 
        if (cfg->xsize < expected_xsize) { 
 | 
            if ((standard_id & V4L2_STD_525_60) || 
 | 
            (standard_id & V4L2_STD_625_50)) { 
 | 
                calculated_xsize = (cfg->xsize * 
 | 
                    VPBE_DISPLAY_H_EXP_RATIO_N) / 
 | 
                    VPBE_DISPLAY_H_EXP_RATIO_D; 
 | 
                if (calculated_xsize <= expected_xsize) { 
 | 
                    h_exp = 1; 
 | 
                    cfg->xsize = calculated_xsize; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
        if (h_scale == 2) 
 | 
            layer_info->h_zoom = ZOOM_X2; 
 | 
        else if (h_scale == 4) 
 | 
            layer_info->h_zoom = ZOOM_X4; 
 | 
        if (h_exp) 
 | 
            layer_info->h_exp = H_EXP_9_OVER_8; 
 | 
    } else { 
 | 
        /* no scaling, only cropping. Set display area to crop area */ 
 | 
        cfg->xsize = expected_xsize; 
 | 
    } 
 | 
  
 | 
    if (pixfmt->height < expected_ysize) { 
 | 
        v_scale = expected_ysize / pixfmt->height; 
 | 
        if (v_scale < 2) 
 | 
            v_scale = 1; 
 | 
        else if (v_scale >= 4) 
 | 
            v_scale = 4; 
 | 
        else 
 | 
            v_scale = 2; 
 | 
        cfg->ysize *= v_scale; 
 | 
        if (cfg->ysize < expected_ysize) { 
 | 
            if ((standard_id & V4L2_STD_625_50)) { 
 | 
                calculated_xsize = (cfg->ysize * 
 | 
                    VPBE_DISPLAY_V_EXP_RATIO_N) / 
 | 
                    VPBE_DISPLAY_V_EXP_RATIO_D; 
 | 
                if (calculated_xsize <= expected_ysize) { 
 | 
                    v_exp = 1; 
 | 
                    cfg->ysize = calculated_xsize; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
        if (v_scale == 2) 
 | 
            layer_info->v_zoom = ZOOM_X2; 
 | 
        else if (v_scale == 4) 
 | 
            layer_info->v_zoom = ZOOM_X4; 
 | 
        if (v_exp) 
 | 
            layer_info->v_exp = V_EXP_6_OVER_5; 
 | 
    } else { 
 | 
        /* no scaling, only cropping. Set display area to crop area */ 
 | 
        cfg->ysize = expected_ysize; 
 | 
    } 
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
        "crop display xsize = %d, ysize = %d\n", 
 | 
        cfg->xsize, cfg->ysize); 
 | 
} 
 | 
  
 | 
static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, 
 | 
            struct vpbe_layer *layer, 
 | 
            int top, int left) 
 | 
{ 
 | 
    struct osd_layer_config *cfg = &layer->layer_info.config; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
  
 | 
    cfg->xpos = min((unsigned int)left, 
 | 
            vpbe_dev->current_timings.xres - cfg->xsize); 
 | 
    cfg->ypos = min((unsigned int)top, 
 | 
            vpbe_dev->current_timings.yres - cfg->ysize); 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
        "new xpos = %d, ypos = %d\n", 
 | 
        cfg->xpos, cfg->ypos); 
 | 
} 
 | 
  
 | 
static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, 
 | 
            struct v4l2_rect *c) 
 | 
{ 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
  
 | 
    if ((c->width == 0) || 
 | 
      ((c->width + c->left) > vpbe_dev->current_timings.xres)) 
 | 
        c->width = vpbe_dev->current_timings.xres - c->left; 
 | 
  
 | 
    if ((c->height == 0) || ((c->height + c->top) > 
 | 
      vpbe_dev->current_timings.yres)) 
 | 
        c->height = vpbe_dev->current_timings.yres - c->top; 
 | 
  
 | 
    /* window height must be even for interlaced display */ 
 | 
    if (vpbe_dev->current_timings.interlaced) 
 | 
        c->height &= (~0x01); 
 | 
  
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_try_format() 
 | 
 * If user application provides width and height, and have bytesperline set 
 | 
 * to zero, driver calculates bytesperline and sizeimage based on hardware 
 | 
 * limits. 
 | 
 */ 
 | 
static int vpbe_try_format(struct vpbe_display *disp_dev, 
 | 
            struct v4l2_pix_format *pixfmt, int check) 
 | 
{ 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    int min_height = 1; 
 | 
    int min_width = 32; 
 | 
    int max_height; 
 | 
    int max_width; 
 | 
    int bpp; 
 | 
  
 | 
    if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && 
 | 
        (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) 
 | 
        /* choose default as V4L2_PIX_FMT_UYVY */ 
 | 
        pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; 
 | 
  
 | 
    /* Check the field format */ 
 | 
    if ((pixfmt->field != V4L2_FIELD_INTERLACED) && 
 | 
        (pixfmt->field != V4L2_FIELD_NONE)) { 
 | 
        if (vpbe_dev->current_timings.interlaced) 
 | 
            pixfmt->field = V4L2_FIELD_INTERLACED; 
 | 
        else 
 | 
            pixfmt->field = V4L2_FIELD_NONE; 
 | 
    } 
 | 
  
 | 
    if (pixfmt->field == V4L2_FIELD_INTERLACED) 
 | 
        min_height = 2; 
 | 
  
 | 
    if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) 
 | 
        bpp = 1; 
 | 
    else 
 | 
        bpp = 2; 
 | 
  
 | 
    max_width = vpbe_dev->current_timings.xres; 
 | 
    max_height = vpbe_dev->current_timings.yres; 
 | 
  
 | 
    min_width /= bpp; 
 | 
  
 | 
    if (!pixfmt->width || (pixfmt->width < min_width) || 
 | 
        (pixfmt->width > max_width)) { 
 | 
        pixfmt->width = vpbe_dev->current_timings.xres; 
 | 
    } 
 | 
  
 | 
    if (!pixfmt->height || (pixfmt->height  < min_height) || 
 | 
        (pixfmt->height  > max_height)) { 
 | 
        pixfmt->height = vpbe_dev->current_timings.yres; 
 | 
    } 
 | 
  
 | 
    if (pixfmt->bytesperline < (pixfmt->width * bpp)) 
 | 
        pixfmt->bytesperline = pixfmt->width * bpp; 
 | 
  
 | 
    /* Make the bytesperline 32 byte aligned */ 
 | 
    pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); 
 | 
  
 | 
    if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) 
 | 
        pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + 
 | 
                (pixfmt->bytesperline * pixfmt->height >> 1); 
 | 
    else 
 | 
        pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_querycap(struct file *file, void  *priv, 
 | 
                   struct v4l2_capability *cap) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    snprintf(cap->driver, sizeof(cap->driver), "%s", 
 | 
        dev_name(vpbe_dev->pdev)); 
 | 
    snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 
 | 
         dev_name(vpbe_dev->pdev)); 
 | 
    strscpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_s_selection(struct file *file, void *priv, 
 | 
                 struct v4l2_selection *sel) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_display *disp_dev = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    struct osd_layer_config *cfg = &layer->layer_info.config; 
 | 
    struct osd_state *osd_device = disp_dev->osd_device; 
 | 
    struct v4l2_rect rect = sel->r; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
        "VIDIOC_S_SELECTION, layer id = %d\n", layer->device_id); 
 | 
  
 | 
    if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 
 | 
        sel->target != V4L2_SEL_TGT_CROP) 
 | 
        return -EINVAL; 
 | 
  
 | 
    if (rect.top < 0) 
 | 
        rect.top = 0; 
 | 
    if (rect.left < 0) 
 | 
        rect.left = 0; 
 | 
  
 | 
    vpbe_disp_check_window_params(disp_dev, &rect); 
 | 
  
 | 
    osd_device->ops.get_layer_config(osd_device, 
 | 
            layer->layer_info.id, cfg); 
 | 
  
 | 
    vpbe_disp_calculate_scale_factor(disp_dev, layer, 
 | 
                    rect.width, 
 | 
                    rect.height); 
 | 
    vpbe_disp_adj_position(disp_dev, layer, rect.top, 
 | 
                    rect.left); 
 | 
    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 set layer config:\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    /* apply zooming and h or v expansion */ 
 | 
    osd_device->ops.set_zoom(osd_device, 
 | 
            layer->layer_info.id, 
 | 
            layer->layer_info.h_zoom, 
 | 
            layer->layer_info.v_zoom); 
 | 
    ret = osd_device->ops.set_vid_expansion(osd_device, 
 | 
            layer->layer_info.h_exp, 
 | 
            layer->layer_info.v_exp); 
 | 
    if (ret < 0) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
        "Error in set vid expansion:\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    if ((layer->layer_info.h_zoom != ZOOM_X1) || 
 | 
        (layer->layer_info.v_zoom != ZOOM_X1) || 
 | 
        (layer->layer_info.h_exp != H_EXP_OFF) || 
 | 
        (layer->layer_info.v_exp != V_EXP_OFF)) 
 | 
        /* Enable expansion filter */ 
 | 
        osd_device->ops.set_interpolation_filter(osd_device, 1); 
 | 
    else 
 | 
        osd_device->ops.set_interpolation_filter(osd_device, 0); 
 | 
  
 | 
    sel->r = rect; 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_g_selection(struct file *file, void *priv, 
 | 
                    struct v4l2_selection *sel) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct osd_layer_config *cfg = &layer->layer_info.config; 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    struct osd_state *osd_device = layer->disp_dev->osd_device; 
 | 
    struct v4l2_rect *rect = &sel->r; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
            "VIDIOC_G_SELECTION, layer id = %d\n", 
 | 
            layer->device_id); 
 | 
  
 | 
    if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 
 | 
        return -EINVAL; 
 | 
  
 | 
    switch (sel->target) { 
 | 
    case V4L2_SEL_TGT_CROP: 
 | 
        osd_device->ops.get_layer_config(osd_device, 
 | 
                         layer->layer_info.id, cfg); 
 | 
        rect->top = cfg->ypos; 
 | 
        rect->left = cfg->xpos; 
 | 
        rect->width = cfg->xsize; 
 | 
        rect->height = cfg->ysize; 
 | 
        break; 
 | 
    case V4L2_SEL_TGT_CROP_DEFAULT: 
 | 
    case V4L2_SEL_TGT_CROP_BOUNDS: 
 | 
        rect->left = 0; 
 | 
        rect->top = 0; 
 | 
        rect->width = vpbe_dev->current_timings.xres; 
 | 
        rect->height = vpbe_dev->current_timings.yres; 
 | 
        break; 
 | 
    default: 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_g_pixelaspect(struct file *file, void *priv, 
 | 
                      int type, struct v4l2_fract *f) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); 
 | 
  
 | 
    if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 
 | 
        return -EINVAL; 
 | 
  
 | 
    *f = vpbe_dev->current_timings.aspect; 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_g_fmt(struct file *file, void *priv, 
 | 
                struct v4l2_format *fmt) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    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) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    /* Fill in the information about format */ 
 | 
    fmt->fmt.pix = layer->pix_fmt; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_enum_fmt(struct file *file, void  *priv, 
 | 
                   struct v4l2_fmtdesc *fmt) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
                "VIDIOC_ENUM_FMT, layer id = %d\n", 
 | 
                layer->device_id); 
 | 
    if (fmt->index > 1) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    /* Fill in the information about format */ 
 | 
    if (fmt->index == 0) 
 | 
        fmt->pixelformat = V4L2_PIX_FMT_UYVY; 
 | 
    else 
 | 
        fmt->pixelformat = V4L2_PIX_FMT_NV12; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_s_fmt(struct file *file, void *priv, 
 | 
                struct v4l2_format *fmt) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_display *disp_dev = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    struct osd_layer_config *cfg  = &layer->layer_info.config; 
 | 
    struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; 
 | 
    struct osd_state *osd_device = disp_dev->osd_device; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
            "VIDIOC_S_FMT, layer id = %d\n", 
 | 
            layer->device_id); 
 | 
  
 | 
    if (vb2_is_busy(&layer->buffer_queue)) 
 | 
        return -EBUSY; 
 | 
  
 | 
    if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { 
 | 
        v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    /* 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; 
 | 
    if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { 
 | 
        struct vpbe_layer *otherlayer; 
 | 
  
 | 
        otherlayer = _vpbe_display_get_other_win_layer(disp_dev, layer); 
 | 
        /* if other layer is available, only 
 | 
         * claim it, do not configure it 
 | 
         */ 
 | 
        ret = osd_device->ops.request_layer(osd_device, 
 | 
                            otherlayer->layer_info.id); 
 | 
        if (ret < 0) { 
 | 
            v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
                 "Display Manager failed to allocate layer\n"); 
 | 
            return -EBUSY; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /* 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; 
 | 
  
 | 
    if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) 
 | 
        cfg->pixfmt = PIXFMT_YCBCRI; 
 | 
  
 | 
    /* Change of the default pixel format for both video windows */ 
 | 
    if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { 
 | 
        struct vpbe_layer *otherlayer; 
 | 
        cfg->pixfmt = PIXFMT_NV12; 
 | 
        otherlayer = _vpbe_display_get_other_win_layer(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); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int vpbe_display_try_fmt(struct file *file, void *priv, 
 | 
                  struct v4l2_format *fmt) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_display *disp_dev = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); 
 | 
  
 | 
    if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    /* Check for valid field format */ 
 | 
    return  vpbe_try_format(disp_dev, pixfmt, 0); 
 | 
  
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_s_std - Set the given standard in the encoder 
 | 
 * 
 | 
 * Sets the standard if supported by the current encoder. Return the status. 
 | 
 * 0 - success & -EINVAL on error 
 | 
 */ 
 | 
static int vpbe_display_s_std(struct file *file, void *priv, 
 | 
                v4l2_std_id std_id) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); 
 | 
  
 | 
    if (vb2_is_busy(&layer->buffer_queue)) 
 | 
        return -EBUSY; 
 | 
  
 | 
    if (vpbe_dev->ops.s_std) { 
 | 
        ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); 
 | 
        if (ret) { 
 | 
            v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
            "Failed to set standard for sub devices\n"); 
 | 
            return -EINVAL; 
 | 
        } 
 | 
    } else { 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * 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) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    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.std_id; 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    return -EINVAL; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_enum_output - enumerate outputs 
 | 
 * 
 | 
 * Enumerates the outputs available at the vpbe display 
 | 
 * returns the status, -EINVAL if end of output list 
 | 
 */ 
 | 
static int vpbe_display_enum_output(struct file *file, void *priv, 
 | 
                    struct v4l2_output *output) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,    "VIDIOC_ENUM_OUTPUT\n"); 
 | 
  
 | 
    /* Enumerate outputs */ 
 | 
    if (!vpbe_dev->ops.enum_outputs) 
 | 
        return -EINVAL; 
 | 
  
 | 
    ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); 
 | 
    if (ret) { 
 | 
        v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
            "Failed to enumerate outputs\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_s_output - Set output to 
 | 
 * the output specified by the index 
 | 
 */ 
 | 
static int vpbe_display_s_output(struct file *file, void *priv, 
 | 
                unsigned int i) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,    "VIDIOC_S_OUTPUT\n"); 
 | 
  
 | 
    if (vb2_is_busy(&layer->buffer_queue)) 
 | 
        return -EBUSY; 
 | 
  
 | 
    if (!vpbe_dev->ops.set_output) 
 | 
        return -EINVAL; 
 | 
  
 | 
    ret = vpbe_dev->ops.set_output(vpbe_dev, i); 
 | 
    if (ret) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
            "Failed to set output for sub devices\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_g_output - Get output from subdevice 
 | 
 * for a given by the index 
 | 
 */ 
 | 
static int vpbe_display_g_output(struct file *file, void *priv, 
 | 
                unsigned int *i) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); 
 | 
    /* Get the standard from the current encoder */ 
 | 
    *i = vpbe_dev->current_out_index; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_enum_dv_timings - Enumerate the dv timings 
 | 
 * 
 | 
 * enum the timings in the current encoder. Return the status. 0 - success 
 | 
 * -EINVAL on error 
 | 
 */ 
 | 
static int 
 | 
vpbe_display_enum_dv_timings(struct file *file, void *priv, 
 | 
            struct v4l2_enum_dv_timings *timings) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_TIMINGS\n"); 
 | 
  
 | 
    /* Enumerate outputs */ 
 | 
    if (!vpbe_dev->ops.enum_dv_timings) 
 | 
        return -EINVAL; 
 | 
  
 | 
    ret = vpbe_dev->ops.enum_dv_timings(vpbe_dev, timings); 
 | 
    if (ret) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
            "Failed to enumerate dv timings info\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_s_dv_timings - Set the dv timings 
 | 
 * 
 | 
 * Set the timings in the current encoder. Return the status. 0 - success 
 | 
 * -EINVAL on error 
 | 
 */ 
 | 
static int 
 | 
vpbe_display_s_dv_timings(struct file *file, void *priv, 
 | 
                struct v4l2_dv_timings *timings) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
    int ret; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_TIMINGS\n"); 
 | 
  
 | 
    if (vb2_is_busy(&layer->buffer_queue)) 
 | 
        return -EBUSY; 
 | 
  
 | 
    /* Set the given standard in the encoder */ 
 | 
    if (!vpbe_dev->ops.s_dv_timings) 
 | 
        return -EINVAL; 
 | 
  
 | 
    ret = vpbe_dev->ops.s_dv_timings(vpbe_dev, timings); 
 | 
    if (ret) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
            "Failed to set the dv timings info\n"); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_g_dv_timings - Set the dv timings 
 | 
 * 
 | 
 * Get the timings in the current encoder. Return the status. 0 - success 
 | 
 * -EINVAL on error 
 | 
 */ 
 | 
static int 
 | 
vpbe_display_g_dv_timings(struct file *file, void *priv, 
 | 
                struct v4l2_dv_timings *dv_timings) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_TIMINGS\n"); 
 | 
  
 | 
    /* Get the given standard in the encoder */ 
 | 
  
 | 
    if (vpbe_dev->current_timings.timings_type & 
 | 
                VPBE_ENC_DV_TIMINGS) { 
 | 
        *dv_timings = vpbe_dev->current_timings.dv_timings; 
 | 
    } else { 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_open() 
 | 
 * It creates object of file handle structure and stores it in private_data 
 | 
 * member of filepointer 
 | 
 */ 
 | 
static int vpbe_display_open(struct file *file) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct vpbe_display *disp_dev = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    struct osd_state *osd_device = disp_dev->osd_device; 
 | 
    int err; 
 | 
  
 | 
    /* creating context for file descriptor */ 
 | 
    err = v4l2_fh_open(file); 
 | 
    if (err) { 
 | 
        v4l2_err(&vpbe_dev->v4l2_dev, "v4l2_fh_open failed\n"); 
 | 
        return err; 
 | 
    } 
 | 
  
 | 
    /* leaving if layer is already initialized */ 
 | 
    if (!v4l2_fh_is_singular_file(file)) 
 | 
        return err; 
 | 
  
 | 
    if (!layer->usrs) { 
 | 
        if (mutex_lock_interruptible(&layer->opslock)) 
 | 
            return -ERESTARTSYS; 
 | 
        /* First claim the layer for this device */ 
 | 
        err = osd_device->ops.request_layer(osd_device, 
 | 
                        layer->layer_info.id); 
 | 
        mutex_unlock(&layer->opslock); 
 | 
        if (err < 0) { 
 | 
            /* Couldn't get layer */ 
 | 
            v4l2_err(&vpbe_dev->v4l2_dev, 
 | 
                "Display Manager failed to allocate layer\n"); 
 | 
            v4l2_fh_release(file); 
 | 
            return -EINVAL; 
 | 
        } 
 | 
    } 
 | 
    /* Increment layer usrs counter */ 
 | 
    layer->usrs++; 
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, 
 | 
            "vpbe display device opened successfully\n"); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_release() 
 | 
 * This function deletes buffer queue, frees the buffers and the davinci 
 | 
 * display file * handle 
 | 
 */ 
 | 
static int vpbe_display_release(struct file *file) 
 | 
{ 
 | 
    struct vpbe_layer *layer = video_drvdata(file); 
 | 
    struct osd_layer_config *cfg  = &layer->layer_info.config; 
 | 
    struct vpbe_display *disp_dev = layer->disp_dev; 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    struct osd_state *osd_device = disp_dev->osd_device; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); 
 | 
  
 | 
    mutex_lock(&layer->opslock); 
 | 
  
 | 
    osd_device->ops.disable_layer(osd_device, 
 | 
            layer->layer_info.id); 
 | 
    /* Decrement layer usrs counter */ 
 | 
    layer->usrs--; 
 | 
    /* If this file handle has initialize encoder device, reset it */ 
 | 
    if (!layer->usrs) { 
 | 
        if (cfg->pixfmt == PIXFMT_NV12) { 
 | 
            struct vpbe_layer *otherlayer; 
 | 
            otherlayer = 
 | 
            _vpbe_display_get_other_win_layer(disp_dev, layer); 
 | 
            osd_device->ops.disable_layer(osd_device, 
 | 
                    otherlayer->layer_info.id); 
 | 
            osd_device->ops.release_layer(osd_device, 
 | 
                    otherlayer->layer_info.id); 
 | 
        } 
 | 
        osd_device->ops.disable_layer(osd_device, 
 | 
                layer->layer_info.id); 
 | 
        osd_device->ops.release_layer(osd_device, 
 | 
                layer->layer_info.id); 
 | 
    } 
 | 
  
 | 
    _vb2_fop_release(file, NULL); 
 | 
    mutex_unlock(&layer->opslock); 
 | 
  
 | 
    disp_dev->cbcr_ofst = 0; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* vpbe capture ioctl operations */ 
 | 
static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { 
 | 
    .vidioc_querycap     = vpbe_display_querycap, 
 | 
    .vidioc_g_fmt_vid_out    = vpbe_display_g_fmt, 
 | 
    .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt, 
 | 
    .vidioc_s_fmt_vid_out    = vpbe_display_s_fmt, 
 | 
    .vidioc_try_fmt_vid_out  = vpbe_display_try_fmt, 
 | 
  
 | 
    .vidioc_reqbufs         = vb2_ioctl_reqbufs, 
 | 
    .vidioc_create_bufs     = vb2_ioctl_create_bufs, 
 | 
    .vidioc_querybuf     = vb2_ioctl_querybuf, 
 | 
    .vidioc_qbuf         = vb2_ioctl_qbuf, 
 | 
    .vidioc_dqbuf         = vb2_ioctl_dqbuf, 
 | 
    .vidioc_streamon     = vb2_ioctl_streamon, 
 | 
    .vidioc_streamoff     = vb2_ioctl_streamoff, 
 | 
    .vidioc_expbuf         = vb2_ioctl_expbuf, 
 | 
  
 | 
    .vidioc_g_pixelaspect     = vpbe_display_g_pixelaspect, 
 | 
    .vidioc_g_selection     = vpbe_display_g_selection, 
 | 
    .vidioc_s_selection     = vpbe_display_s_selection, 
 | 
  
 | 
    .vidioc_s_std         = vpbe_display_s_std, 
 | 
    .vidioc_g_std         = vpbe_display_g_std, 
 | 
  
 | 
    .vidioc_enum_output     = vpbe_display_enum_output, 
 | 
    .vidioc_s_output     = vpbe_display_s_output, 
 | 
    .vidioc_g_output     = vpbe_display_g_output, 
 | 
  
 | 
    .vidioc_s_dv_timings     = vpbe_display_s_dv_timings, 
 | 
    .vidioc_g_dv_timings     = vpbe_display_g_dv_timings, 
 | 
    .vidioc_enum_dv_timings     = vpbe_display_enum_dv_timings, 
 | 
}; 
 | 
  
 | 
static const struct v4l2_file_operations vpbe_fops = { 
 | 
    .owner = THIS_MODULE, 
 | 
    .open = vpbe_display_open, 
 | 
    .release = vpbe_display_release, 
 | 
    .unlocked_ioctl = video_ioctl2, 
 | 
    .mmap = vb2_fop_mmap, 
 | 
    .poll =  vb2_fop_poll, 
 | 
}; 
 | 
  
 | 
static int vpbe_device_get(struct device *dev, void *data) 
 | 
{ 
 | 
    struct platform_device *pdev = to_platform_device(dev); 
 | 
    struct vpbe_display *vpbe_disp  = data; 
 | 
  
 | 
    if (strcmp("vpbe_controller", pdev->name) == 0) 
 | 
        vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); 
 | 
  
 | 
    if (strstr(pdev->name, "vpbe-osd")) 
 | 
        vpbe_disp->osd_device = platform_get_drvdata(pdev); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int init_vpbe_layer(int i, struct vpbe_display *disp_dev, 
 | 
               struct platform_device *pdev) 
 | 
{ 
 | 
    struct vpbe_layer *vpbe_display_layer = NULL; 
 | 
    struct video_device *vbd = NULL; 
 | 
  
 | 
    /* Allocate memory for four plane display objects */ 
 | 
    disp_dev->dev[i] = kzalloc(sizeof(*disp_dev->dev[i]), GFP_KERNEL); 
 | 
    if (!disp_dev->dev[i]) 
 | 
        return  -ENOMEM; 
 | 
  
 | 
    spin_lock_init(&disp_dev->dev[i]->irqlock); 
 | 
    mutex_init(&disp_dev->dev[i]->opslock); 
 | 
  
 | 
    /* Get the pointer to the layer object */ 
 | 
    vpbe_display_layer = disp_dev->dev[i]; 
 | 
    vbd = &vpbe_display_layer->video_dev; 
 | 
    /* Initialize field of video device */ 
 | 
    vbd->release    = video_device_release_empty; 
 | 
    vbd->fops    = &vpbe_fops; 
 | 
    vbd->ioctl_ops    = &vpbe_ioctl_ops; 
 | 
    vbd->minor    = -1; 
 | 
    vbd->v4l2_dev   = &disp_dev->vpbe_dev->v4l2_dev; 
 | 
    vbd->lock    = &vpbe_display_layer->opslock; 
 | 
    vbd->vfl_dir    = VFL_DIR_TX; 
 | 
    vbd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 
 | 
  
 | 
    if (disp_dev->vpbe_dev->current_timings.timings_type & 
 | 
            VPBE_ENC_STD) 
 | 
        vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); 
 | 
  
 | 
    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); 
 | 
  
 | 
    vpbe_display_layer->device_id = i; 
 | 
  
 | 
    vpbe_display_layer->layer_info.id = 
 | 
        ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); 
 | 
  
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int register_device(struct vpbe_layer *vpbe_display_layer, 
 | 
               struct vpbe_display *disp_dev, 
 | 
               struct platform_device *pdev) 
 | 
{ 
 | 
    int err; 
 | 
  
 | 
    v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, 
 | 
          "Trying to register VPBE display device.\n"); 
 | 
    v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, 
 | 
          "layer=%p,layer->video_dev=%p\n", 
 | 
          vpbe_display_layer, 
 | 
          &vpbe_display_layer->video_dev); 
 | 
  
 | 
    vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue; 
 | 
    err = video_register_device(&vpbe_display_layer->video_dev, 
 | 
                    VFL_TYPE_VIDEO, 
 | 
                    -1); 
 | 
    if (err) 
 | 
        return -ENODEV; 
 | 
  
 | 
    vpbe_display_layer->disp_dev = disp_dev; 
 | 
    /* set the driver data in platform device */ 
 | 
    platform_set_drvdata(pdev, disp_dev); 
 | 
    video_set_drvdata(&vpbe_display_layer->video_dev, 
 | 
              vpbe_display_layer); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
  
 | 
  
 | 
/* 
 | 
 * vpbe_display_probe() 
 | 
 * This function creates device entries by register itself to the V4L2 driver 
 | 
 * and initializes fields of each layer objects 
 | 
 */ 
 | 
static int vpbe_display_probe(struct platform_device *pdev) 
 | 
{ 
 | 
    struct vpbe_display *disp_dev; 
 | 
    struct v4l2_device *v4l2_dev; 
 | 
    struct resource *res = NULL; 
 | 
    struct vb2_queue *q; 
 | 
    int k; 
 | 
    int i; 
 | 
    int err; 
 | 
    int irq; 
 | 
  
 | 
    printk(KERN_DEBUG "vpbe_display_probe\n"); 
 | 
    /* Allocate memory for vpbe_display */ 
 | 
    disp_dev = devm_kzalloc(&pdev->dev, sizeof(*disp_dev), GFP_KERNEL); 
 | 
    if (!disp_dev) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    spin_lock_init(&disp_dev->dma_queue_lock); 
 | 
    /* 
 | 
     * 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, disp_dev, 
 | 
            vpbe_device_get); 
 | 
    if (err < 0) 
 | 
        return err; 
 | 
  
 | 
    v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; 
 | 
    /* Initialize the vpbe display controller */ 
 | 
    if (disp_dev->vpbe_dev->ops.initialize) { 
 | 
        err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, 
 | 
                             disp_dev->vpbe_dev); 
 | 
        if (err) { 
 | 
            v4l2_err(v4l2_dev, "Error initing vpbe\n"); 
 | 
            err = -ENOMEM; 
 | 
            goto probe_out; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { 
 | 
        if (init_vpbe_layer(i, disp_dev, pdev)) { 
 | 
            err = -ENODEV; 
 | 
            goto probe_out; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 
 | 
    if (!res) { 
 | 
        v4l2_err(v4l2_dev, "Unable to get VENC interrupt resource\n"); 
 | 
        err = -ENODEV; 
 | 
        goto probe_out; 
 | 
    } 
 | 
  
 | 
    irq = res->start; 
 | 
    err = devm_request_irq(&pdev->dev, irq, venc_isr, 0, 
 | 
                   VPBE_DISPLAY_DRIVER, disp_dev); 
 | 
    if (err) { 
 | 
        v4l2_err(v4l2_dev, "VPBE IRQ request failed\n"); 
 | 
        goto probe_out; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { 
 | 
        /* initialize vb2 queue */ 
 | 
        q = &disp_dev->dev[i]->buffer_queue; 
 | 
        memset(q, 0, sizeof(*q)); 
 | 
        q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 
 | 
        q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 
 | 
        q->drv_priv = disp_dev->dev[i]; 
 | 
        q->ops = &video_qops; 
 | 
        q->mem_ops = &vb2_dma_contig_memops; 
 | 
        q->buf_struct_size = sizeof(struct vpbe_disp_buffer); 
 | 
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 
 | 
        q->min_buffers_needed = 1; 
 | 
        q->lock = &disp_dev->dev[i]->opslock; 
 | 
        q->dev = disp_dev->vpbe_dev->pdev; 
 | 
        err = vb2_queue_init(q); 
 | 
        if (err) { 
 | 
            v4l2_err(v4l2_dev, "vb2_queue_init() failed\n"); 
 | 
            goto probe_out; 
 | 
        } 
 | 
  
 | 
        INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue); 
 | 
  
 | 
        if (register_device(disp_dev->dev[i], disp_dev, pdev)) { 
 | 
            err = -ENODEV; 
 | 
            goto probe_out; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    v4l2_dbg(1, debug, v4l2_dev, 
 | 
         "Successfully completed the probing of vpbe v4l2 device\n"); 
 | 
  
 | 
    return 0; 
 | 
  
 | 
probe_out: 
 | 
    for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { 
 | 
        /* Unregister video device */ 
 | 
        if (disp_dev->dev[k]) { 
 | 
            video_unregister_device(&disp_dev->dev[k]->video_dev); 
 | 
            kfree(disp_dev->dev[k]); 
 | 
        } 
 | 
    } 
 | 
    return err; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vpbe_display_remove() 
 | 
 * It un-register hardware layer from V4L2 driver 
 | 
 */ 
 | 
static int vpbe_display_remove(struct platform_device *pdev) 
 | 
{ 
 | 
    struct vpbe_layer *vpbe_display_layer; 
 | 
    struct vpbe_display *disp_dev = platform_get_drvdata(pdev); 
 | 
    struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; 
 | 
    int i; 
 | 
  
 | 
    v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); 
 | 
  
 | 
    /* deinitialize the vpbe display controller */ 
 | 
    if (vpbe_dev->ops.deinitialize) 
 | 
        vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); 
 | 
    /* un-register device */ 
 | 
    for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { 
 | 
        /* Get the pointer to the layer object */ 
 | 
        vpbe_display_layer = disp_dev->dev[i]; 
 | 
        /* Unregister video device */ 
 | 
        video_unregister_device(&vpbe_display_layer->video_dev); 
 | 
  
 | 
    } 
 | 
    for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { 
 | 
        kfree(disp_dev->dev[i]); 
 | 
        disp_dev->dev[i] = NULL; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static struct platform_driver vpbe_display_driver = { 
 | 
    .driver = { 
 | 
        .name = VPBE_DISPLAY_DRIVER, 
 | 
        .bus = &platform_bus_type, 
 | 
    }, 
 | 
    .probe = vpbe_display_probe, 
 | 
    .remove = vpbe_display_remove, 
 | 
}; 
 | 
  
 | 
module_platform_driver(vpbe_display_driver); 
 | 
  
 | 
MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller"); 
 | 
MODULE_LICENSE("GPL"); 
 | 
MODULE_AUTHOR("Texas Instruments"); 
 |