blob: ac060de5df0b0c450a45136213008490ad86893a [file] [log] [blame]
/*
* linux/drivers/video/ti81xx/vpss/video.c
*
* VPSS video display driver for TI81XX
*
* Copyright (C) 2010 TI
* Author: Yihe Hu <yihehu@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define VPSS_SUBMODULE_NAME "VIDEO"
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <plat/ti81xx-vpss.h>
#include <linux/vpss-video.h>
#include "core.h"
#include "dc.h"
static struct list_head vctrl_list;
static int num_vctrl;
static struct platform_device *video_dev;
static struct vps_payload_info *video_payload_info;
static inline struct vps_video_ctrl *get_video_ctrl_from_handle(void * handle)
{
struct vps_video_ctrl *vctrl;
list_for_each_entry(vctrl, &vctrl_list, list) {
if (handle == vctrl->handle)
return vctrl;
}
return NULL;
}
static inline void video_print_status(struct vps_video_ctrl *vctrl)
{
int r;
if ((vctrl == NULL) || (vctrl->handle == NULL))
return;
r = vps_fvid2_control(
vctrl->handle,
IOCTL_VPS_DISP_GET_STATUS,
(struct vps_dispstatus *)vctrl->vs_phy,
NULL);
if (!r) {
printk(KERN_INFO "Number of request queued(%d): %d\n",
vctrl->idx, vctrl->vstatus->queueCount);
printk(KERN_INFO "Number of request dequeued(%d): %d\n",
vctrl->idx, vctrl->vstatus->dequeueCount);
printk(KERN_INFO "Number of frames displayed(%d): %d\n",
vctrl->idx, vctrl->vstatus->displayedFrameCount);
printk(KERN_INFO "Number of frames repeated(%d): %d\n",
vctrl->idx, vctrl->vstatus->repeatFrameCount);
}
}
static inline int video_get_inputid(struct vps_video_ctrl *vctrl,
int bid,
int inputnode)
{
switch (bid) {
case VPS_DC_HDMI_BLEND:
if ((inputnode == VPS_DC_VCOMP) ||
(inputnode == VPS_DC_VCOMP_MUX))
return VPS_DC_CIG_NON_CONSTRAINED_OUTPUT;
else if (inputnode == VPS_DC_HDCOMP_MUX)
return VPS_DC_CIG_PIP_OUTPUT;
else
return -EINVAL;
break;
case VPS_DC_HDCOMP_BLEND:
if ((inputnode == VPS_DC_VCOMP) ||
(inputnode == VPS_DC_VCOMP_MUX))
return VPS_DC_CIG_CONSTRAINED_OUTPUT;
else if (inputnode == VPS_DC_HDCOMP_MUX)
return VPS_DC_CIG_PIP_OUTPUT;
else
return -EINVAL;
break;
case VPS_DC_DVO2_BLEND:
if ((inputnode == VPS_DC_VCOMP) ||
(inputnode == VPS_DC_VCOMP_MUX))
return VPS_DC_CIG_NON_CONSTRAINED_OUTPUT;
else if (inputnode == VPS_DC_HDCOMP_MUX)
return VPS_DC_CIG_PIP_OUTPUT;
else
return -EINVAL;
break;
case VPS_DC_SDVENC_BLEND:
return VPS_DC_SDVENC_MUX;
break;
}
return -EINVAL;
}
static void video_rt_reset(struct vps_video_ctrl *vctrl)
{
if (vctrl == NULL)
return;
vctrl->framelist->perlistcfg = NULL;
vctrl->vrtparams->layoutid = NULL;
vctrl->vrtparams->infrmprms = NULL;
vctrl->vrtparams->vpdmaposcfg = NULL;
}
/* this will be the call back register to the VPSS m3 for the VSYNC isr*/
static int video_vsync_cb(void *handle, void *appdata, void * reserved)
{
struct vps_video_ctrl *vctrl = get_video_ctrl_from_handle(handle);
struct vps_isr_data *isrd;
if (vctrl == NULL)
return -EINVAL;
/*clear all rt flags*/
video_rt_reset(vctrl);
/*call all registered function*/
list_for_each_entry(isrd, &vctrl->cb_list, list) {
if (isrd->isr)
isrd->isr(isrd->arg);
}
return 0;
}
static int video_create(struct vps_video_ctrl *vctrl,
enum vps_vpdmamemorytype mtype)
{
u32 vidinstid;
int r = 0;
/* int i;*/
struct vps_dcenumnodeinput eninput;
struct vps_dcnodeinput ninput;
eninput.inputidx = 0;
while (r == 0) {
eninput.nodeid = vctrl->nodes[0].nodeid;
r = vps_dc_enum_node_input(&eninput);
ninput.nodeid = eninput.nodeid;
ninput.inputid = eninput.inputid;
vps_dc_get_node_status(&ninput);
VPSSERR("nid:%d iid%d on/off:%d\n",
ninput.nodeid, ninput.inputid, ninput.isenable);
eninput.inputidx++;
}
r = 0;
VPSSDBG("create video%d\n", vctrl->idx);
#if 0
for (i = 0; (i < vctrl->num_edges) && (r == 0); i++) {
vctrl->nodes[i].isenable = 1;
r = vps_dc_set_node(vctrl->nodes[i].nodeid,
vctrl->nodes[i].inputid,
vctrl->nodes[i].isenable);
}
if (r)
return r;
for (i = 0; (i < vctrl->num_outputs) && (r == 0); i++) {
vctrl->enodes[i].isenable = 1;
r = vps_dc_set_node(vctrl->enodes[i].nodeid,
vctrl->enodes[i].inputid,
vctrl->enodes[i].isenable);
}
if (r)
return r;
#endif
vctrl->cbparams->appdata = NULL;
vctrl->cbparams->errlist = NULL;
vctrl->cbparams->errcbfnx = NULL;
vctrl->cbparams->cbfxn = video_vsync_cb;
vctrl->vcparams->memtype = mtype;
/*set the numframe back to 1*/
vctrl->framelist->numframes = 1;
if (vctrl->idx == 0)
vidinstid = VPS_DISP_INST_BP0;
else if (vctrl->idx == 1)
vidinstid = VPS_DISP_INST_BP1;
else
vidinstid = VPS_DISP_INST_SEC1;
vctrl->handle = vps_fvid2_create(
FVID2_VPS_DISP_DRV,
vidinstid,
(void *)vctrl->vcp_phy,
(void *)vctrl->vcs_phy,
(struct fvid2_cbparams *)vctrl->cbp_phy);
if (vctrl->handle == NULL)
r = -EINVAL;
return r;
}
static int video_delete(struct vps_video_ctrl *vctrl)
{
int r = 0;
VPSSDBG("delete video%d\n", vctrl->idx);
if ((vctrl == NULL) || (vctrl->handle == NULL))
return -EINVAL;
r = vps_fvid2_delete(vctrl->handle, NULL);
if (!r)
vctrl->handle = NULL;
return r;
}
static int video_start(struct vps_video_ctrl *vctrl)
{
int r = 0;
VPSSDBG("start video%d\n", vctrl->idx);
if ((vctrl == NULL) || (vctrl->handle == NULL))
return -EINVAL;
if (!vctrl->isstarted) {
r = vps_fvid2_start(vctrl->handle, NULL);
if (!r)
vctrl->isstarted = true;
}
return r;
}
static int video_stop(struct vps_video_ctrl *vctrl)
{
int r = 0;
VPSSDBG("stop video%d\n", vctrl->idx);
if ((vctrl == NULL) || (vctrl->handle == NULL))
return -EINVAL;
if (vctrl->isstarted) {
r = vps_fvid2_stop(vctrl->handle, NULL);
if (!r) {
vctrl->isstarted = false;
video_print_status(vctrl);
}
}
return r;
}
static inline int frame_index(struct vps_video_ctrl *vctrl, u32 addr)
{
int i;
for (i = 0; i < MAX_BUFFER_NUM; i++) {
if (addr == vctrl->frm_phy[i])
return i;
}
return -1;
}
static int video_queue(struct vps_video_ctrl *vctrl)
{
int r = 0;
if ((vctrl == NULL) || (vctrl->handle == NULL))
return -EINVAL;
VPSSDBG("queue video\n");
#if 0
{
u32 addr = (u32)vctrl->framelist->frames[0];
int index;
index = frame_index(vctrl, addr);
if (vctrl->scformat == FVID2_SF_PROGRESSIVE)
VPSSDBG("queue %d video addr 0x%p",
index, vctrl->frames[index]->addr[0][0]);
else {
VPSSDBG("queue %d video addr 0x%p",
index, vctrl->frames[index]->addr[0][0]);
VPSSDBG("queue %d video addr 0x%p",
index, vctrl->frames[index]->addr[1][0]);
}
}
#endif
r = vps_fvid2_queue(vctrl->handle,
(struct fvid2_framelist *)vctrl->frmls_phy,
0);
return r;
}
static int video_dequeue(struct vps_video_ctrl *vctrl)
{
int r = 0;
if ((vctrl == NULL) || (vctrl->handle == NULL))
return -EINVAL;
VPSSDBG("dequeue video\n");
r = vps_fvid2_dequeue(vctrl->handle,
(struct fvid2_framelist *)vctrl->frmls_phy,
0,
FVID2_TIMEOUT_NONE);
#if 0
if (!r) {
u32 addr = (u32)vctrl->framelist->frames[0];
int index;
index = frame_index(vctrl, addr);
if (vctrl->scformat == FVID2_SF_PROGRESSIVE)
VPSSDBG("dequeue %d video addr 0x%p",
index, vctrl->frames[index]->addr[0][0]);
else {
VPSSDBG("dequeue %d video addr 0x%p",
index, vctrl->frames[index]->addr[0][0]);
VPSSDBG("dequeue %d video addr 0x%p",
index, vctrl->frames[index]->addr[1][0]);
}
}
#endif
return r;
}
static int video_get_resolution(struct vps_video_ctrl *vctrl,
u32 *fwidth, u32 *fheight, bool *isprogressive)
{
VPSSDBG("get resolution.\n");
vps_dc_get_outpfmt(
vctrl->enodes[vctrl->num_outputs - 1].nodeid,
&vctrl->framewidth,
&vctrl->frameheight,
&vctrl->scformat,
DC_BLEND_ID);
*fwidth = vctrl->framewidth;
*fheight = vctrl->frameheight;
*isprogressive = 0;
if (vctrl->scformat == FVID2_SF_PROGRESSIVE)
*isprogressive = 1;
return 0;
}
static int video_set_buffer(struct vps_video_ctrl *vctrl, u32 addr, u8 idx)
{
struct fvid2_format *dfmt;
struct fvid2_frame *frame;
u32 scfmt, fm;
int r = 0;
VPSSDBG("set buffer\n");
if (vctrl == NULL)
return -EINVAL;
dfmt = vctrl->fmt;
scfmt = vctrl->fmt->scanformat;
frame = vctrl->frames[idx];
/*get the field merged flag*/
fm = vctrl->fmt->fieldmerged[FVID2_YUV_INT_ADDR_IDX];
switch (vctrl->fmt->dataformat) {
case FVID2_DF_YUV422I_YUYV:
if (scfmt == FVID2_SF_PROGRESSIVE) {
frame->addr[FVID2_FRAME_ADDR_IDX] \
[FVID2_YUV_INT_ADDR_IDX] = (void *)addr;
} else {
/*interlaced display*/
if (fm) {
/*field merged*/
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_INT_ADDR_IDX] = (void *)addr;
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_INT_ADDR_IDX] = (void *)(addr
+ dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
} else {
/*field non-merged*/
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_INT_ADDR_IDX] = (void *)addr;
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_INT_ADDR_IDX] = (void *)(addr
+ dfmt->height *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
}
}
break;
case FVID2_DF_YUV422SP_UV:
if (scfmt == FVID2_SF_PROGRESSIVE) {
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] = (void *)addr;
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] = (void *)(addr +
(dfmt->height *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]));
} else {
if (fm) {
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] =
(void *)(addr);
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] =
(void *)(addr +
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] =
(void *)(addr + dfmt->height *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] =
(void *)(addr + ((dfmt->height + 1) *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]));
} else {
}
}
break;
case FVID2_DF_YUV420SP_UV:
if (scfmt == FVID2_SF_PROGRESSIVE) {
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] = (void *)addr;
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] = (void *)(addr +
(dfmt->height *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]));
} else {
/*interlaced display*/
if (fm) {
/*field merged*/
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] =
(void *)addr;
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] =
(void *)(addr +
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] =
(void *)(addr + dfmt->height *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] = (void *)
(addr + (dfmt->height + 1) *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
} else {
/*field non-merged*/
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] =
(void *)addr;
frame->addr[FVID2_FIELD_EVEN_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] = (void *)
(addr + ((dfmt->height >> 1) *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]));
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_Y_ADDR_IDX] = (void *)
(addr + (((dfmt->height * 3) / 4) *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX]));
frame->addr[FVID2_FIELD_ODD_ADDR_IDX] \
[FVID2_YUV_SP_CBCR_ADDR_IDX] = (void *)
(addr +
(((dfmt->height + dfmt->height / 4) *
dfmt->pitch[FVID2_YUV_INT_ADDR_IDX])));
}
}
break;
}
vctrl->framelist->frames[0] =
(struct fvid2_frame *)vctrl->frm_phy[idx];
if (r)
VPSSERR("failed to update buffer\n");
return r;
}
static int video_check_format(struct vps_video_ctrl *vctrl,
struct fvid2_format *fmt)
{
int r = 0;
VPSSDBG("check format\n");
if (fmt->width > vctrl->framewidth) {
VPSSERR("Width(%d) greater than frame width(%d)\n",
fmt->width, vctrl->framewidth);
r = -EINVAL;
}
if (fmt->height > vctrl->frameheight) {
VPSSERR("Height(%d) greater than frame height(%d)\n",
fmt->height, vctrl->frameheight);
r = -EINVAL;
}
if (FVID2_DF_YUV422I_YUYV != fmt->dataformat &&
FVID2_DF_YUV420SP_UV != fmt->dataformat &&
FVID2_DF_YUV422SP_UV != fmt->dataformat) {
VPSSERR("Buffer format (%d) not supported!!\n",
fmt->dataformat);
r = -EINVAL;
}
if (FVID2_DF_YUV420SP_UV == fmt->dataformat ||
FVID2_DF_YUV422SP_UV == fmt->dataformat) {
if (((fmt->pitch[FVID2_YUV_SP_Y_ADDR_IDX] <
(fmt->width))) ||
(fmt->pitch[FVID2_YUV_SP_CBCR_ADDR_IDX] <
(fmt->width))) {
VPSSERR("Pitch (%d) less than Width (%d) in bytes!!\n",
fmt->pitch[FVID2_YUV_SP_Y_ADDR_IDX],
(fmt->width));
r = -EINVAL;
}
} else if (FVID2_DF_YUV422I_YUYV == fmt->dataformat) {
if (fmt->pitch[FVID2_YUV_INT_ADDR_IDX] <
(fmt->width * 2u)) {
VPSSERR("pitch (%d) less than width (%d) in bytes!!\n",
fmt->pitch[FVID2_YUV_INT_ADDR_IDX],
(fmt->width * 2u));
r = -EINVAL;
}
}
/* Check whether window width is even */
if (fmt->width & 0x01u) {
VPSSERR("width(%d) can't be odd!!\n", fmt->width);
r = -EINVAL;
}
if (FVID2_DF_YUV420SP_UV == fmt->dataformat) {
if (FVID2_SF_INTERLACED == fmt->scanformat) {
/* Check whether window height is multiple of 4
* for YUV420 format in interlaced display mode */
if (fmt->height & 0x03u) {
VPSSERR("height(%d)should be multiple"
"of 4 for YUV420 "
"format in interlaced display mode!!\n",
fmt->height);
r = -EINVAL;
}
} else {
/* Check whether window height/startY is
even for YUV420 format */
if (fmt->height & 0x01u) {
VPSSERR("height(%d) can't be odd for "
"YUV420 format!!\n",
fmt->height);
r = -EINVAL;
}
}
}
return r;
}
static int video_try_format(struct vps_video_ctrl *vctrl, u32 width,
u32 height, u32 df, u32 pitch, u8 merged,
struct fvid2_format *fmt)
{
int r = 0;
bool fieldmerged;
if (vctrl == NULL)
return -EINVAL;
/*setup the fvid2_format*/
fmt->dataformat = df;
fmt->channelnum = 0;
fmt->height = height;
fmt->width = width;
fmt->scanformat = vctrl->scformat;
if (df == FVID2_DF_YUV420SP_UV)
fmt->bpp = FVID2_BPP_BITS12;
else
fmt->bpp = FVID2_BPP_BITS16;
switch (df) {
case FVID2_DF_YUV420SP_UV:
case FVID2_DF_YUV422SP_UV:
fmt->pitch[FVID2_YUV_SP_Y_ADDR_IDX] = pitch;
fmt->pitch[FVID2_YUV_SP_CBCR_ADDR_IDX] = pitch;
if (vctrl->scformat == FVID2_SF_PROGRESSIVE)
fieldmerged = 0;
else
fieldmerged = 1;
fmt->fieldmerged[FVID2_YUV_SP_Y_ADDR_IDX] = fieldmerged;
fmt->fieldmerged[FVID2_YUV_SP_CBCR_ADDR_IDX] = fieldmerged;
break;
case FVID2_DF_YUV422I_YUYV:
fmt->pitch[FVID2_YUV_INT_ADDR_IDX] = pitch;
if (vctrl->scformat == FVID2_SF_PROGRESSIVE)
fieldmerged = 0;
else
fieldmerged = 1;
fmt->fieldmerged[FVID2_YUV_INT_ADDR_IDX] = fieldmerged;
break;
}
VPSSDBG("try format %dx%d df %d pitch %d\n",
width, height, df, fmt->pitch[FVID2_YUV_INT_ADDR_IDX]);
return r;
}
static int video_set_format(struct vps_video_ctrl *vctrl,
struct fvid2_format *fmt)
{
int r = 0;
if (vctrl == NULL)
return -EINVAL;
memcpy(vctrl->fmt, fmt, sizeof(struct fvid2_format));
if ((!vctrl->isstarted) && (vctrl->handle))
r = vps_fvid2_setformat(vctrl->handle,
(struct fvid2_format *)vctrl->fmt_phy);
if (r)
VPSSERR("failed to set format\n");
return r;
}
static int video_get_format(struct vps_video_ctrl *vctrl,
struct fvid2_format *fmt)
{
int r = 0;
if (vctrl == NULL)
return -EINVAL;
memcpy(fmt, vctrl->fmt, sizeof(struct fvid2_format));
return r;
}
static int video_set_rtpos(struct vps_video_ctrl *vctrl, u32 posx, u32 posy)
{
VPSSDBG("set position\n");
if ((vctrl == NULL) || (!(vctrl->caps & VPSS_VID_CAPS_POSITIONING)))
return -EINVAL;
/*This need be even number*/
vctrl->vdmaposcfg->startx = (posx & ~1);
vctrl->vdmaposcfg->starty = (posy & ~1);
vctrl->vrtparams->vpdmaposcfg = (struct vps_posconfig *)
vctrl->vdpc_phy;
vctrl->framelist->perlistcfg = (void *)
vctrl->vrt_phy;
return 0;
}
static int video_set_rtcrop(struct vps_video_ctrl *vctrl, u32 posx, u32 posy,
u32 pitch, u32 w, u32 h)
{
VPSSDBG("Set crop\n");
if ((vctrl == NULL) || (!(vctrl->caps & VPSS_VID_CAPS_CROPING)))
return -EINVAL;
vctrl->vfrmprm->width = w;
vctrl->vfrmprm->height = h;
/*for 420SP format, height need be multiple of 4
when interlaced output*/
if ((vctrl->fmt->dataformat == FVID2_DF_YUV420SP_UV) &&
(vctrl->scformat == FVID2_SF_INTERLACED))
vctrl->vfrmprm->height &= ~3;
vctrl->vfrmprm->memtype = vctrl->vcparams->memtype;
vctrl->vfrmprm->pitch[0] = pitch;
vctrl->vfrmprm->pitch[1] = pitch;
vctrl->vfrmprm->pitch[2] = pitch;
vctrl->vrtparams->infrmprms = (struct vps_frameparams *)
vctrl->vfp_phy;
vctrl->framelist->perlistcfg = (void *)
vctrl->vrt_phy;
return 0;
}
static int video_get_color(struct vps_video_ctrl *vctrl,
struct vps_video_color *color)
{
int r;
VPSSDBG("get color\n");
if ((vctrl == NULL) || (!(vctrl->caps & VPSS_VID_CAPS_COLOR)) ||
(color == NULL))
return -EINVAL;
vctrl->cigconfig.nodeid = vctrl->enodes[0].inputid;
r = vps_dc_get_color(&vctrl->cigconfig);
if (r)
return r;
color->ben = vctrl->cigconfig.alphablending;
color->alpha = vctrl->cigconfig.alphavalue;
color->colorkey = ((vctrl->cigconfig.transcolor.r & 0xFF) << 16) |
((vctrl->cigconfig.transcolor.g & 0xFF) << 8) |
(vctrl->cigconfig.transcolor.b & 0xff);
color->ten = vctrl->cigconfig.transparency;
color->mask = vctrl->cigconfig.mask;
return 0;
}
static int video_set_color(struct vps_video_ctrl *vctrl,
struct vps_video_color *color)
{
int r = 0;
int i;
VPSSDBG("set color\n");
if ((vctrl == NULL) || (!(vctrl->caps & VPSS_VID_CAPS_COLOR)) ||
(color == NULL))
return -EINVAL;
vctrl->cigconfig.alphablending = color->ben;
vctrl->cigconfig.alphavalue = color->alpha;
vctrl->cigconfig.transcolor.r =
(color->colorkey >> 16) & 0xff;
vctrl->cigconfig.transcolor.g =
(color->colorkey >> 8) & 0xff;
vctrl->cigconfig.transcolor.b =
(color->colorkey & 0xff);
vctrl->cigconfig.transparency = color->ten;
vctrl->cigconfig.mask = color->mask;
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
return r;
}
static int video_apply_changes(struct vps_video_ctrl *vctrl)
{
int r = 0;
return r;
}
/*S********************SYSFS**************************************************/
static ssize_t video_default_color_show(struct vps_video_ctrl *vctrl,
char *buf)
{
return 0;
}
static ssize_t video_default_color_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r;
r = size;
return r;
}
static const struct vps_sname_info trans_key_type_name[] = {
{"nomasking", VPS_DC_CIG_TM_NO_MASK},
{"1lsbmasking", VPS_DC_CIG_TM_MASK_1_LSB},
{"2lsbmasking", VPS_DC_CIG_TM_MASK_2_LSB},
{"3lsbmasking", VPS_DC_CIG_TM_MASK_3_LSB}
};
static ssize_t video_trans_key_type_show(struct vps_video_ctrl *vctrl,
char *buf)
{
int i;
struct vps_video_color color;
if (vctrl->get_color(vctrl, &color))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(trans_key_type_name); i++) {
if (vctrl->cigconfig.mask == trans_key_type_name[i].value)
break;
}
if (i == ARRAY_SIZE(trans_key_type_name))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_name[i].name);
}
static ssize_t video_trans_key_type_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r = 0, i;
if (!(vctrl->caps & VPSS_VID_CAPS_COLOR))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(trans_key_type_name); i++) {
if (sysfs_streq(buf, (const char *)trans_key_type_name[i].name))
break;
}
if (i == ARRAY_SIZE(trans_key_type_name))
return -EINVAL;
video_lock(vctrl);
vctrl->cigconfig.mask = trans_key_type_name[i].value;
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
video_unlock(vctrl);
if (!r)
r = size;
return r;
}
static ssize_t video_trans_key_value_show(struct vps_video_ctrl *vctrl,
char *buf)
{
u32 rgb;
struct vps_video_color color;
if (vctrl->get_color(vctrl, &color))
return -EINVAL;
rgb = ((vctrl->cigconfig.transcolor.r & 0xFF) << 16) |
((vctrl->cigconfig.transcolor.g & 0xFF) << 8) |
(vctrl->cigconfig.transcolor.b & 0xFF);
return snprintf(buf, PAGE_SIZE, "%x\n", rgb);
}
static ssize_t video_trans_key_value_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r = 0, i;
u32 rgb;
if (!(vctrl->caps & VPSS_VID_CAPS_COLOR))
return -EINVAL;
rgb = simple_strtoul(buf, NULL, 16);
vctrl->cigconfig.transcolor.r = (rgb >> 16) & 0xFF;
vctrl->cigconfig.transcolor.g = (rgb >> 8) & 0xFF;
vctrl->cigconfig.transcolor.b = (rgb >> 0) & 0xFF;
video_lock(vctrl);
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
video_unlock(vctrl);
if (!r)
r = size;
return r;
}
static ssize_t video_trans_key_enabled_show(struct vps_video_ctrl *vctrl,
char *buf)
{
struct vps_video_color color;
if (vctrl->get_color(vctrl, &color))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", vctrl->cigconfig.transparency);
}
static ssize_t video_trans_key_enabled_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r = 0, i;
int enable;
if (!(vctrl->caps & VPSS_VID_CAPS_COLOR))
return -EINVAL;
if (sscanf(buf, "%d", &enable) != 1)
return -EINVAL;
vctrl->cigconfig.transparency = enable ? 1 : 0;
video_lock(vctrl);
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
video_unlock(vctrl);
if (!r)
r = size;
return r;
}
static ssize_t video_alpha_blending_enabled_show(struct vps_video_ctrl *vctrl,
char *buf)
{
struct vps_video_color color;
if (vctrl->get_color(vctrl, &color))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", vctrl->cigconfig.alphablending);
}
static ssize_t video_alpha_blending_enabled_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int enable;
int r = 0, i;
if (!(vctrl->caps & VPSS_VID_CAPS_COLOR))
return -EINVAL;
if (sscanf(buf, "%d", &enable) != 1)
return -EINVAL;
video_lock(vctrl);
vctrl->cigconfig.alphablending = enable ? 1 : 0;
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
video_unlock(vctrl);
if (!r)
r = size;
return r;
}
static ssize_t video_global_alpha_show(struct vps_video_ctrl *vctrl,
char *buf)
{
struct vps_video_color color;
if (vctrl->get_color(vctrl, &color))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", vctrl->cigconfig.alphavalue);
}
static ssize_t video_global_alpha_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r = 0, i;
if (!(vctrl->caps & VPSS_VID_CAPS_COLOR))
return -EINVAL;
vctrl->cigconfig.alphavalue = simple_strtoul(buf, NULL, 10);
if (vctrl->cigconfig.alphavalue > 255)
vctrl->cigconfig.alphavalue = 255;
video_lock(vctrl);
for (i = 0; i < vctrl->num_outputs && !r; i++) {
vctrl->cigconfig.nodeid = vctrl->enodes[i].inputid;
/*call DC function to setup the color and alpha value*/
r = vps_dc_set_color(&vctrl->cigconfig);
}
video_unlock(vctrl);
if (!r)
r = size;
return r;
}
/*show current video enabled status*/
static ssize_t video_enabled_show(struct vps_video_ctrl *vctrl,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", vctrl->isstarted);
}
/*enable the current video*/
static ssize_t video_enabled_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
bool enabled;
int r;
if (vctrl->handle == NULL) {
VPSSERR("please open /dev/video%d node first.\n",
vctrl->idx + 1);
return -EINVAL;
}
enabled = simple_strtoul(buf, NULL, 10);
if (vctrl->isstarted == enabled)
return size;
video_lock(vctrl);
if (enabled)
r = vctrl->start(vctrl);
else
r = vctrl->stop(vctrl);
video_unlock(vctrl);
if (r)
return r;
return size;
}
/*show how many blender are connecting to this video*/
static ssize_t video_nodes_show(struct vps_video_ctrl *vctrl,
char *buf)
{
int i, r = 0;
int l = 0;
char name[10];
struct vps_dcenumnodeinput eninput;
struct vps_dcnodeinput ninput;
eninput.inputidx = 0;
while (r == 0) {
eninput.nodeid = vctrl->nodes[0].nodeid;
r = vps_dc_enum_node_input(&eninput);
ninput.nodeid = eninput.nodeid;
ninput.inputid = eninput.inputid;
vps_dc_get_node_status(&ninput);
VPSSERR("nid:%d iid%d on/off:%d\n",
ninput.nodeid, ninput.inputid, ninput.isenable);
eninput.inputidx++;
}
vps_dc_get_node_name(vctrl->nodes[0].nodeid, name);
if (vctrl->num_edges)
l += snprintf(buf + l, PAGE_SIZE - l, "%s", name);
else
return snprintf(buf, PAGE_SIZE, " ");
for (i = 0; i < vctrl->num_outputs; i++) {
r = vps_dc_get_node_name(vctrl->enodes[i].nodeid, name);
l += snprintf(buf + l, PAGE_SIZE - l, ",%s", name);
}
l += snprintf(buf + l, PAGE_SIZE - l, "\n");
return l;
}
/*set the enodes for video in the format 0:XXXX,1,XXXX,2:XXXX,3:XXXX*/
static ssize_t video_nodes_store(struct vps_video_ctrl *vctrl,
const char *buf, size_t size)
{
int r = 0;
int total = 0;
int i;
int idx = 0;
u8 tiedvenc = 0;
char *input = (char *)buf, *this_opt;
/*this is store the edege without blender lever*/
u8 numedges = 0;
int nodeid;
struct vps_dcnodeinput nodes[VPS_DISPLAY_MAX_EDGES];
/*this is for the blender*/
struct vps_dcnodeinput enodes[VPS_DC_MAX_VENC - 1];
struct vps_dcvencinfo vinfo;
if (vctrl->isstarted) {
VPSSERR("please stop video%d before continue.\n",
vctrl->idx);
return -EINVAL;
}
video_lock(vctrl);
/*get the edge path first*/
this_opt = strsep(&input, ",");
if (vps_dc_get_id(this_opt, &nodeid, DC_NODE_ID)) {
VPSSERR("(%d): wrong paths\n", vctrl->idx);
r = -EINVAL;
goto exit;
}
if ((vctrl->idx == 2) && (nodeid != VPS_DC_SDVENC_MUX)) {
VPSSERR("(%d): can not connect other nodes except"
" sdvencmux only\n", vctrl->idx);
r = -EINVAL;
goto exit;
}
switch (nodeid) {
case VPS_DC_VCOMP:
nodes[0].nodeid = VPS_DC_VCOMP;
nodes[0].inputid = VPS_DC_MAIN_INPUT_PATH;
numedges = 1;
break;
case VPS_DC_VCOMP_MUX:
nodes[0].nodeid = VPS_DC_VCOMP_MUX;
if (vctrl->idx == 0)
nodes[0].inputid = VPS_DC_BP0_INPUT_PATH;
else if (vctrl->idx == 1)
nodes[0].inputid = VPS_DC_BP1_INPUT_PATH;
nodes[1].nodeid = VPS_DC_VCOMP;
nodes[1].inputid = VPS_DC_VCOMP_MUX;
numedges = 2;
break;
case VPS_DC_HDCOMP_MUX:
nodes[0].nodeid = VPS_DC_HDCOMP_MUX;
if (vctrl->idx == 0)
nodes[0].inputid = VPS_DC_BP0_INPUT_PATH;
else if (vctrl->idx == 1)
nodes[0].inputid = VPS_DC_BP1_INPUT_PATH;
nodes[1].nodeid = VPS_DC_CIG_PIP_INPUT;
nodes[1].inputid = VPS_DC_HDCOMP_MUX;
numedges = 2;
break;
case VPS_DC_SDVENC_MUX:
nodes[0].nodeid = VPS_DC_SDVENC_MUX;
if (vctrl->idx == 0)
nodes[0].inputid = VPS_DC_BP0_INPUT_PATH;
else if (vctrl->idx == 1)
nodes[0].inputid = VPS_DC_BP1_INPUT_PATH;
else if (vctrl->idx == 2)
nodes[0].inputid = VPS_DC_SEC1_INPUT_PATH;
numedges = 1;
}
/*get the output venc information, could be multiple output*/
if ((input == NULL) || (!(strcmp(input, "\0")))) {
r = -EINVAL;
VPSSERR("(%d)- wrong node information\n", vctrl->idx);
goto exit;
}
/*parse each end note*/
while (!r && (this_opt = strsep(&input, ",")) != NULL) {
int nid, inputid;
if (vps_dc_get_id(this_opt, &nid, DC_NODE_ID)) {
VPSSERR("(%d)- failed to parse node name %s\n",
vctrl->idx, this_opt);
r = -EINVAL;
goto exit;
}
vps_dc_get_id(this_opt,
&vinfo.modeinfo[idx].vencid,
DC_VENC_ID);
enodes[idx].nodeid = nid;
inputid = video_get_inputid(vctrl, nid, nodeid);
if (inputid < 0) {
VPSSERR("(%d) - invalid node %s",
vctrl->idx,
this_opt);
r = -EINVAL;
goto exit;
}
enodes[idx].inputid = inputid;
VPSSDBG("(%d)- s: %d e:%d vid:%d\n",
vctrl->idx,
enodes[idx].inputid,
enodes[idx].nodeid,
vinfo.modeinfo[idx].vencid);
idx++;
if (input == NULL)
break;
}
total = idx;
/*the total is not match what parsed in the string*/
if (idx != total) {
r = -EINVAL;
VPSSERR("(%d)- node number not match.\n",
vctrl->idx);
goto exit;
}
vinfo.numvencs = total;
for (i = 0; i < total; i++)
tiedvenc |= vinfo.modeinfo[i].vencid;
/*tied venc check*/
if (total == 1)
tiedvenc = 0;
else {
u8 tvencs;
vps_dc_get_tiedvenc(&tvencs);
if (tiedvenc & (~tvencs)) {
VPSSERR("(%d)- nodes not match tied vencs\n",
vctrl->idx);
r = -EINVAL;
goto exit;
}
}
/*make sure the VENCs are already running before continue*/
vinfo.numvencs = total;
r = vps_dc_get_vencinfo(&vinfo);
if (r)
goto exit;
for (i = 0; i < vinfo.numvencs; i++) {
if (vinfo.modeinfo[i].isvencrunning == 0) {
r = -EINVAL;
VPSSERR("(%d)- venc %d not running.\n",
vctrl->idx,
vinfo.modeinfo[i].vencid);
goto exit;
}
}
/*should we reconfig the display path or not*/
/* if (nodeid != vctrl->nodes[0].nodeid) */
{
/*disable old first*/
for (i = 0; (i < vctrl->num_edges) && (r == 0); i++) {
vctrl->nodes[i].isenable = 0;
r = vps_dc_set_node(vctrl->nodes[i].nodeid,
vctrl->nodes[i].inputid,
vctrl->nodes[i].isenable);
}
if (r) {
r = -EINVAL;
VPSSERR("(%d): failed to disable old path\n",
vctrl->idx);
goto exit;
}
/*enable the new path*/
for (i = 0; (i < numedges) && (r == 0); i++) {
nodes[i].isenable = 1;
r = vps_dc_set_node(nodes[i].nodeid,
nodes[i].inputid,
nodes[i].isenable);
}
if (r) {
r = -EINVAL;
VPSSERR("(%d): failed to enable new path\n",
vctrl->idx);
goto exit;
}
/*store back to the local structure*/
vctrl->num_edges = numedges;
for (i = 0; i < vctrl->num_edges; i++)
vctrl->nodes[i] = nodes[i];
}
/*disable the exit nodes*/
for (i = 0; (i < vctrl->num_outputs) && (r == 0); i++) {
vctrl->enodes[i].isenable = 0;
r = vps_dc_set_node(vctrl->enodes[i].nodeid,
vctrl->enodes[i].inputid,
vctrl->enodes[i].isenable);
}
if (r) {
r = -EINVAL;
VPSSERR("(%d)- failed to disable old output nodes\n",
vctrl->idx);
goto exit;
}
/*enable the new nodes*/
for (i = 0; (i < total) && (r == 0); i++) {
enodes[i].isenable = 1;
r = vps_dc_set_node(enodes[i].nodeid,
enodes[i].inputid,
enodes[i].isenable);
}
if (r) {
r = -EINVAL;
VPSSERR("(%d)- failed to enable new output nodes\n",
vctrl->idx);
goto exit;
}
/*update new nodes into local database*/
vctrl->num_outputs = total;
for (i = 0; i < vctrl->num_outputs; i++)
vctrl->enodes[i] = enodes[i];
VPSSDBG("(%d)- numedge :%d\n",
vctrl->idx, vctrl->num_outputs);
r = size;
exit:
video_unlock(vctrl);
return r;
}
struct video_attribute {
struct attribute attr;
ssize_t (*show)(struct vps_video_ctrl *, char *);
ssize_t (*store)(struct vps_video_ctrl *, const char *, size_t);
};
#define VIDEO_ATTR(_name, _mode, _show, _store) \
struct video_attribute video_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static VIDEO_ATTR(default_color, S_IRUGO | S_IWUSR,
video_default_color_show, video_default_color_store);
static VIDEO_ATTR(trans_key_type, S_IRUGO | S_IWUSR,
video_trans_key_type_show, video_trans_key_type_store);
static VIDEO_ATTR(trans_key_value, S_IRUGO | S_IWUSR,
video_trans_key_value_show, video_trans_key_value_store);
static VIDEO_ATTR(trans_key_enabled, S_IRUGO | S_IWUSR,
video_trans_key_enabled_show, video_trans_key_enabled_store);
static VIDEO_ATTR(alpha_blending_enabled, S_IRUGO | S_IWUSR,
video_alpha_blending_enabled_show, video_alpha_blending_enabled_store);
static VIDEO_ATTR(global_alpha, S_IRUGO | S_IWUSR,
video_global_alpha_show, video_global_alpha_store);
static VIDEO_ATTR(enabled, S_IRUGO | S_IWUSR,
video_enabled_show, video_enabled_store);
static VIDEO_ATTR(nodes, S_IRUGO | S_IWUSR,
video_nodes_show, video_nodes_store);
static struct attribute *video_sysfs_attrs[] = {
&video_attr_enabled.attr,
&video_attr_nodes.attr,
&video_attr_default_color.attr,
&video_attr_alpha_blending_enabled.attr,
&video_attr_global_alpha.attr,
&video_attr_trans_key_enabled.attr,
&video_attr_trans_key_value.attr,
&video_attr_trans_key_type.attr,
NULL
};
static ssize_t video_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
struct vps_video_ctrl *vctrl;
struct video_attribute *vid_attr;
vctrl = container_of(kobj, struct vps_video_ctrl, kobj);
vid_attr = container_of(attr, struct video_attribute, attr);
if (!vid_attr->show)
return -ENOENT;
return vid_attr->show(vctrl, buf);
}
static ssize_t video_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct vps_video_ctrl *vctrl;
struct video_attribute *vid_attr;
vctrl = container_of(kobj, struct vps_video_ctrl, kobj);
vid_attr = container_of(attr, struct video_attribute, attr);
if (!vid_attr->store)
return -ENOENT;
return vid_attr->store(vctrl, buf, size);
}
static const struct sysfs_ops video_sysfs_ops = {
.show = video_attr_show,
.store = video_attr_store,
};
static struct kobj_type video_ktype = {
.sysfs_ops = &video_sysfs_ops,
.default_attrs = video_sysfs_attrs,
};
static int video_create_sysfs(struct vps_video_ctrl *vctrl)
{
int r;
if (vctrl == NULL)
return -EINVAL;
r = kobject_init_and_add(&vctrl->kobj,
&video_ktype,
&video_dev->dev.kobj,
"video%d",
vctrl->idx);
if (r)
VPSSERR("failed to create video%d sysfs file.\n",
vctrl->idx);
else
vctrl->sysfs = true;
return r;
}
static void video_remove_sysfs(struct vps_video_ctrl *vctrl)
{
if (vctrl->sysfs) {
kobject_del(&vctrl->kobj);
kobject_put(&vctrl->kobj);
vctrl->sysfs = false;
}
}
/*E*******************SYSFS Function*********************************/
int vps_video_register_isr(vsync_callback_t cb, void *arg, int idx)
{
int r = 0;
struct vps_video_ctrl *vctrl;
struct vps_isr_data *isrd, *new;
vctrl = vps_video_get_ctrl(idx);
if (vctrl == NULL)
return -EINVAL;
video_lock(vctrl);
/*make sure not duplicate */
list_for_each_entry(isrd, &vctrl->cb_list, list) {
if ((isrd->isr == cb) && (isrd->arg == arg)) {
r = -EINVAL;
goto exit;
}
}
/*add to the list*/
new = kzalloc(sizeof(*isrd), GFP_KERNEL);
new->isr = cb;
new->arg = arg;
list_add_tail(&new->list, &vctrl->cb_list);
exit:
video_unlock(vctrl);
return r;
}
EXPORT_SYMBOL(vps_video_register_isr);
int vps_video_unregister_isr(vsync_callback_t cb , void *arg, int idx)
{
int r = 0;
struct vps_video_ctrl *vctrl;
struct vps_isr_data *isrd, *next;
vctrl = vps_video_get_ctrl(idx);
if (vctrl == NULL)
return -EINVAL;
video_lock(vctrl);
list_for_each_entry_safe(isrd,
next,
&vctrl->cb_list,
list) {
if ((isrd->arg == arg) && (isrd->isr == cb)) {
list_del(&isrd->list);
kfree(isrd);
break;
}
}
video_unlock(vctrl);
return r;
}
EXPORT_SYMBOL(vps_video_unregister_isr);
struct vps_video_ctrl *vps_video_get_ctrl(int num)
{
struct vps_video_ctrl *vctrl;
list_for_each_entry(vctrl, &vctrl_list, list) {
if (vctrl->idx == num)
return vctrl;
}
return NULL;
}
EXPORT_SYMBOL(vps_video_get_ctrl);
int vps_video_get_num_video(void)
{
return num_vctrl;
}
EXPORT_SYMBOL(vps_video_get_num_video);
static void vps_video_add_ctrl(struct vps_video_ctrl *vctrl)
{
++num_vctrl;
list_add_tail(&vctrl->list, &vctrl_list);
}
void __init vps_fvid2_video_ctrl_init(struct vps_video_ctrl *vctrl)
{
int i;
/*create paramters*/
vctrl->vcparams->memtype = VPS_VPDMA_MT_NONTILEDMEM;
vctrl->vcparams->percben = 0; /*do not need periodically callback*/
vctrl->fmt->channelnum = 0;
vctrl->framelist->numframes = 1;
for (i = 0 ; i < MAX_BUFFER_NUM; i++)
vctrl->framelist->frames[i] =
(struct fvid2_frame *)vctrl->frm_phy[i];
/*assign the function pointer*/
vctrl->create = video_create;
vctrl->delete = video_delete;
vctrl->start = video_start;
vctrl->stop = video_stop;
vctrl->queue = video_queue;
vctrl->dequeue = video_dequeue;
vctrl->get_resolution = video_get_resolution;
vctrl->set_buffer = video_set_buffer;
vctrl->check_format = video_check_format;
vctrl->try_format = video_try_format;
vctrl->set_format = video_set_format;
vctrl->get_format = video_get_format;
vctrl->set_pos = video_set_rtpos;
vctrl->set_crop = video_set_rtcrop;
vctrl->set_color = video_set_color;
vctrl->get_color = video_get_color;
vctrl->apply_changes = video_apply_changes;
}
static inline int get_alloc_size(void)
{
int size = 0;
size += sizeof(struct vps_dispcreateparams);
size += sizeof(struct vps_dispcreatestatus);
size += sizeof(struct vps_disprtparams);
size += sizeof(struct vps_posconfig);
size += sizeof(struct vps_frameparams);
size += sizeof(struct vps_dispstatus);
size += sizeof(struct fvid2_cbparams);
size += sizeof(struct fvid2_format);
size += sizeof(struct fvid2_framelist);
size += sizeof(struct fvid2_frame) *MAX_BUFFER_NUM;
return size * VPS_DISPLAY_INST_MAX;
}
static inline void assign_payload_addr(struct vps_video_ctrl *vctrl,
struct vps_payload_info *pinfo,
u32 *buf_offset)
{
int i;
vctrl->vcparams = (struct vps_dispcreateparams *)
setaddr(pinfo,
buf_offset,
&vctrl->vcp_phy,
sizeof(struct vps_dispcreateparams));
vctrl->vcstatus = (struct vps_dispcreatestatus *)
setaddr(pinfo,
buf_offset,
&vctrl->vcs_phy,
sizeof(struct vps_dispcreatestatus));
vctrl->vrtparams = (struct vps_disprtparams *)
setaddr(pinfo,
buf_offset,
&vctrl->vrt_phy,
sizeof(struct vps_disprtparams));
vctrl->vdmaposcfg = (struct vps_posconfig *)
setaddr(pinfo,
buf_offset,
&vctrl->vdpc_phy,
sizeof(struct vps_posconfig));
vctrl->vfrmprm = (struct vps_frameparams *)
setaddr(pinfo,
buf_offset,
&vctrl->vfp_phy,
sizeof(struct vps_frameparams));
vctrl->vstatus = (struct vps_dispstatus *)
setaddr(pinfo,
buf_offset,
&vctrl->vs_phy,
sizeof(struct vps_dispstatus));
vctrl->cbparams = (struct fvid2_cbparams *)
setaddr(pinfo,
buf_offset,
&vctrl->cbp_phy,
sizeof(struct fvid2_cbparams));
vctrl->fmt = (struct fvid2_format *)
setaddr(pinfo,
buf_offset,
&vctrl->fmt_phy,
sizeof(struct fvid2_format));
vctrl->framelist = (struct fvid2_framelist *)
setaddr(pinfo,
buf_offset,
&vctrl->frmls_phy,
sizeof(struct fvid2_framelist));
for (i = 0 ; i < MAX_BUFFER_NUM; i++)
vctrl->frames[i] = (struct fvid2_frame *)
setaddr(pinfo,
buf_offset,
&vctrl->frm_phy[i],
sizeof(struct fvid2_frame));
}
int __init vps_video_init(struct platform_device *pdev)
{
int i, r;
struct vps_payload_info *pinfo;
u32 size = 0;
u32 offset = 0;
u8 num_edges = 0;
u8 num_outputs = 0;
VPSSDBG("video init\n");
INIT_LIST_HEAD(&vctrl_list);
num_vctrl = 0;
video_dev = pdev;
/*allocate payload info*/
video_payload_info = kzalloc(sizeof(struct vps_payload_info),
GFP_KERNEL);
if (!video_payload_info) {
VPSSERR("allocate payload info failed\n");
return -ENOMEM;
}
pinfo = video_payload_info;
/*calcuate size to allocate*/
size = get_alloc_size();
pinfo->vaddr = vps_sbuf_alloc(size, &pinfo->paddr);
if (pinfo->vaddr == NULL) {
VPSSERR("alloc video dma buffer failed\n");
pinfo->paddr = 0;
r = -ENOMEM;
goto cleanup;
}
pinfo->size = PAGE_ALIGN(size);
memset(pinfo->vaddr, 0, pinfo->size);
for (i = 0; i < VPS_DISPLAY_INST_MAX; i++) {
struct vps_video_ctrl *vctrl;
int j;
vctrl = kzalloc(sizeof(*vctrl), GFP_KERNEL);
if (vctrl == NULL) {
VPSSERR("failed to allocate video%d\n", i);
r = -ENOMEM;
goto cleanup;
}
/*assign the dma buffer*/
assign_payload_addr(vctrl,
pinfo,
&offset);
/*init video control*/
vps_fvid2_video_ctrl_init(vctrl);
vctrl->idx = i;
vps_video_add_ctrl(vctrl);
mutex_init(&vctrl->vmutex);
INIT_LIST_HEAD(&vctrl->cb_list);
/*connected the nodes*/
switch (i) {
case 0:
num_edges = 2;
vctrl->nodes[0].nodeid = VPS_DC_VCOMP_MUX;
vctrl->nodes[0].inputid = VPS_DC_BP0_INPUT_PATH;
vctrl->nodes[1].nodeid = VPS_DC_VCOMP;
vctrl->nodes[1].inputid = VPS_DC_VCOMP_MUX;
num_outputs = 1;
vctrl->enodes[0].nodeid = VPS_DC_HDMI_BLEND;
vctrl->enodes[0].inputid =
VPS_DC_CIG_NON_CONSTRAINED_OUTPUT;
vctrl->caps = VPSS_VID_CAPS_POSITIONING |
VPSS_VID_CAPS_COLOR | VPSS_VID_CAPS_CROPING;
break;
case 1:
num_edges = 2;
vctrl->nodes[0].nodeid = VPS_DC_HDCOMP_MUX;
vctrl->nodes[0].inputid = VPS_DC_BP1_INPUT_PATH;
vctrl->nodes[1].nodeid = VPS_DC_CIG_PIP_INPUT;
vctrl->nodes[1].inputid = VPS_DC_HDCOMP_MUX;
num_outputs = 1;
if (cpu_is_ti816x())
vctrl->enodes[0].nodeid = VPS_DC_HDCOMP_BLEND;
else
vctrl->enodes[0].nodeid = VPS_DC_DVO2_BLEND;
vctrl->enodes[0].inputid = VPS_DC_CIG_PIP_OUTPUT;
vctrl->caps = VPSS_VID_CAPS_POSITIONING |
VPSS_VID_CAPS_COLOR | VPSS_VID_CAPS_CROPING;
break;
case 2:
num_edges = 1;
vctrl->nodes[0].nodeid = VPS_DC_SDVENC_MUX;
vctrl->nodes[0].inputid = VPS_DC_SEC1_INPUT_PATH;
num_outputs = 1;
vctrl->enodes[0].nodeid = VPS_DC_SDVENC_BLEND;
vctrl->enodes[0].inputid = VPS_DC_SDVENC_MUX;
vctrl->caps = VPSS_VID_CAPS_POSITIONING |
VPSS_VID_CAPS_CROPING;
break;
}
#if 1
for (j = 0; j < num_edges; j++) {
vctrl->nodes[j].isenable = 1;
r = vps_dc_set_node(vctrl->nodes[j].nodeid,
vctrl->nodes[j].inputid,
vctrl->nodes[j].isenable);
if (r) {
VPSSERR("failed to set nodes\n");
vctrl->num_edges = j;
goto cleanup;
}
}
vctrl->num_edges = num_edges;
for (j = 0; j < num_outputs; j++) {
vctrl->enodes[j].isenable = 1;
r = vps_dc_set_node(vctrl->enodes[j].nodeid,
vctrl->enodes[j].inputid,
vctrl->enodes[j].isenable);
if (r) {
VPSSERR("failed to set nodes\n");
vctrl->num_outputs = j;
goto cleanup;
}
}
vctrl->num_outputs = num_outputs;
#else
vctrl->num_edges = num_edges;
vctrl->num_outputs = num_outputs;
#endif
r = video_create_sysfs(vctrl);
if (r)
goto cleanup;
}
return 0;
cleanup:
vps_video_deinit(pdev);
return r;
}
void __exit vps_video_deinit(struct platform_device *pdev)
{
struct vps_video_ctrl *vctrl;
/*remove all ndoes*/
list_for_each_entry(vctrl, &vctrl_list, list) {
int i;
for (i = 0; i < vctrl->num_edges; i++) {
vctrl->nodes[i].isenable = 0;
vps_dc_set_node(vctrl->nodes[i].nodeid,
vctrl->nodes[i].inputid,
vctrl->nodes[i].isenable);
}
for (i = 0; i < vctrl->num_outputs; i++) {
vctrl->enodes[i].isenable = 0;
vps_dc_set_node(vctrl->enodes[i].nodeid,
vctrl->enodes[i].inputid,
vctrl->enodes[i].isenable);
}
}
/*free payload memory*/
if (video_payload_info) {
if (video_payload_info->vaddr) {
vps_sbuf_free(video_payload_info->paddr,
video_payload_info->vaddr,
video_payload_info->size);
}
kfree(video_payload_info);
video_payload_info = NULL;
}
/*free video ctrl */
while (!list_empty(&vctrl_list)) {
struct vps_isr_data *isrd;
vctrl = list_first_entry(&vctrl_list,
struct vps_video_ctrl, list);
/*free all cb list*/
while (!list_empty(&vctrl->cb_list)) {
isrd = list_first_entry(&vctrl->cb_list,
struct vps_isr_data,
list);
list_del(&isrd->list);
kfree(isrd);
}
video_remove_sysfs(vctrl);
list_del(&vctrl->list);
kfree(vctrl);
}
num_vctrl = 0;
video_dev = NULL;
}