| /* |
| * linux/drivers/video/ti81xx/vpss/dctrl.c |
| * |
| * VPSS display controller driver for TI 81XX |
| * |
| * Copyright (C) 2009 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 "DCTRL" |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/device.h> |
| #include <linux/interrupt.h> |
| #include <linux/err.h> |
| #include <linux/sysfs.h> |
| #include <linux/kobject.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/slab.h> |
| #include <mach/board-ti816x.h> |
| |
| #include "core.h" |
| #include "system.h" |
| #include "dc.h" |
| |
| |
| static struct vps_dispctrl *disp_ctrl; |
| static void *dc_handle; |
| static struct vps_payload_info *dc_payload_info; |
| |
| /*store the current VENC setting*/ |
| static struct vps_dcvencinfo venc_info = { |
| { |
| {VPS_DC_VENC_HDMI, |
| {FVID2_STD_1080P_60, 1920, 1080, FVID2_SF_PROGRESSIVE,\ |
| 148500, 60, 88, 148, 44, 4, 36, 5}, |
| 3, 0, 0}, |
| |
| {VPS_DC_VENC_DVO2, |
| {FVID2_STD_1080P_60, 1920, 1080, FVID2_SF_PROGRESSIVE,\ |
| 148500, 60, 88, 148, 44, 4, 36, 5}, |
| 3, 0, 0}, |
| |
| {VPS_DC_VENC_SD, |
| {FVID2_STD_NTSC, 720, 480, FVID2_SF_INTERLACED,\ |
| 216000, 60, 12, 68, 64, 5, 41, 5}, |
| 0, 0, 0}, |
| |
| {VPS_DC_VENC_HDCOMP, |
| {FVID2_STD_1080I_60, 1920, 1080, FVID2_SF_PROGRESSIVE,\ |
| 74250, 60, 88, 148, 44, 2, 15, 5}, |
| 2, 0, 0} |
| |
| }, |
| 0, |
| VPS_DC_MAX_VENC, |
| }; |
| |
| /********************************************************** |
| The name in the followig arrays are the value used in the sysfs. |
| **********************************************************/ |
| |
| /*store the current mode info*/ |
| static const struct dc_vencmode_info vmode_info[] = { |
| {"ntsc", FVID2_STD_NTSC, |
| {FVID2_STD_NTSC, 720, 480, FVID2_SF_INTERLACED, |
| 216000, 60, 12, 68, 64, 5, 41, 5} |
| }, |
| {"pal", FVID2_STD_PAL, |
| {FVID2_STD_PAL, 720, 576, FVID2_SF_INTERLACED, |
| 216000, 50, 16, 58, 64, 6, 31, 6} |
| }, |
| {"1080p-60", FVID2_STD_1080P_60, |
| {FVID2_STD_1080P_60, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 148500, 60, 88, 148, 44, 4, 36, 5} |
| }, |
| {"1920x1080@60", FVID2_STD_1080P_60, |
| {FVID2_STD_CUSTOM, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 148500, 60, 88, 148, 44, 4, 36, 5} |
| }, |
| {"1080p-50", FVID2_STD_1080P_50, |
| {FVID2_STD_1080P_50, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 148500, 50, 528, 148, 44, 4, 36, 5} |
| }, |
| {"1920x1080@50", FVID2_STD_1080P_50, |
| {FVID2_STD_CUSTOM, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 148500, 50, 528, 148, 44, 4, 36, 5} |
| }, |
| {"1080p-30", FVID2_STD_1080P_30, |
| {FVID2_STD_1080P_30, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 74250, 30, 88, 148, 44, 4, 36, 5} |
| }, |
| {"1920x1080@30", FVID2_STD_1080P_30, |
| {FVID2_STD_CUSTOM, 1920, 1080, FVID2_SF_PROGRESSIVE, |
| 74250, 30, 88, 148, 44, 4, 36, 5} |
| }, |
| {"720p-60", FVID2_STD_720P_60, |
| {FVID2_STD_720P_60, 1280, 720, FVID2_SF_PROGRESSIVE, |
| 74250, 60, 110, 220, 40, 5, 20, 5} |
| }, |
| {"1280x720@60", FVID2_STD_720P_60, |
| {FVID2_STD_CUSTOM, 1280, 720, FVID2_SF_PROGRESSIVE, |
| 74250, 60, 110, 220, 40, 5, 20, 5} |
| }, |
| {"720p-50", FVID2_STD_720P_50, |
| {FVID2_STD_720P_50, 1280, 720, FVID2_SF_PROGRESSIVE, |
| 74250, 50, 440, 220, 40, 5, 20, 5} |
| }, |
| {"1280x720@50", FVID2_STD_720P_50, |
| { FVID2_STD_CUSTOM, 1280, 720, FVID2_SF_PROGRESSIVE, |
| 74250, 50, 440, 220, 40, 5, 20, 5} |
| }, |
| {"1080i-60", FVID2_STD_1080I_60, |
| {FVID2_STD_1080I_60, 1920, 1080, FVID2_SF_INTERLACED, |
| 74250, 60, 88, 148, 44, 2, 15, 5} |
| }, |
| {"1920x1080@60i", FVID2_STD_1080I_60, |
| {FVID2_STD_CUSTOM, 1920, 1080, FVID2_SF_INTERLACED, |
| 74250, 60, 88, 148, 44, 2, 15, 5} |
| }, |
| {"1080i-50", FVID2_STD_1080I_50, |
| {FVID2_STD_1080I_50, 1920, 1080, FVID2_SF_INTERLACED, |
| 742500, 50, 528, 148, 44, 2, 15, 5} |
| }, |
| {"1920x1080@50i", FVID2_STD_1080I_50, |
| {FVID2_STD_CUSTOM, 1920, 1080, FVID2_SF_INTERLACED, |
| 742500, 50, 528, 148, 44, 2, 15, 5} |
| }, |
| /*VGA*/ |
| {"640x480@60", FVID2_STD_VGA_60, |
| {FVID2_STD_CUSTOM, 640, 480, FVID2_SF_PROGRESSIVE, |
| 25088, 60, 16, 48, 96, 10, 33, 2} |
| }, |
| {"640x480@72", FVID2_STD_VGA_72, |
| {FVID2_STD_CUSTOM, 640, 480, FVID2_SF_PROGRESSIVE, |
| 31488, 72, 24, 128, 40, 9, 29, 2} |
| }, |
| {"640x480@75", FVID2_STD_VGA_75, |
| {FVID2_STD_CUSTOM, 640, 480, FVID2_SF_PROGRESSIVE, |
| 31488, 75, 16, 120, 64, 1, 16, 3} |
| }, |
| {"640x480@85", FVID2_STD_VGA_85, |
| {FVID2_STD_CUSTOM, 640, 480, FVID2_SF_PROGRESSIVE, |
| 35840, 85, 56, 80, 56, 1, 25, 3} |
| }, |
| /*SVGA*/ |
| {"800x600@60", FVID2_STD_SVGA_60, |
| {FVID2_STD_CUSTOM, 800, 600, FVID2_SF_PROGRESSIVE, |
| 39936, 60, 40, 88, 128, 1, 23, 4} |
| }, |
| {"800x600@72", FVID2_STD_SVGA_72, |
| {FVID2_STD_CUSTOM, 800, 600, FVID2_SF_PROGRESSIVE, |
| 49920, 72, 56, 64, 120, 37, 23, 6} |
| }, |
| {"800x600@75", FVID2_STD_SVGA_75, |
| {FVID2_STD_CUSTOM, 800, 600, FVID2_SF_PROGRESSIVE, |
| 49400, 75, 16, 160, 80, 1, 21, 3} |
| }, |
| {"800x600@85", FVID2_STD_SVGA_85, |
| {FVID2_STD_CUSTOM, 800, 600, FVID2_SF_PROGRESSIVE, |
| 56000, 85, 32, 152, 64, 1, 27, 3} |
| }, |
| /*XGA*/ |
| {"1024x768@60", FVID2_STD_XGA_60, |
| {FVID2_STD_XGA_60, 1024, 768, FVID2_SF_PROGRESSIVE, |
| 65000, 60, 24, 160, 136, 3, 29, 6} |
| }, |
| {"1024x768@70", FVID2_STD_XGA_70, |
| {FVID2_STD_CUSTOM, 1024, 768, FVID2_SF_PROGRESSIVE, |
| 74752, 70, 24, 144, 136, 3, 29, 6} |
| }, |
| {"1024x768@75", FVID2_STD_XGA_75, |
| {FVID2_STD_XGA_75, 1024, 768, FVID2_SF_PROGRESSIVE, |
| 78720, 75, 16, 176, 96, 1, 28, 3} |
| }, |
| {"1024x768@85", FVID2_STD_XGA_85, |
| {FVID2_STD_CUSTOM, 1024, 768, FVID2_SF_PROGRESSIVE, |
| 94464, 85, 48, 208, 96, 1, 36, 3} |
| }, |
| /*SXGA*/ |
| {"1280x1024@60", FVID2_STD_SXGA_60, |
| {FVID2_STD_SXGA_60, 1280, 1024, FVID2_SF_PROGRESSIVE, |
| 108000, 60, 48, 248, 112, 1, 38, 3} |
| }, |
| {"1280x1024@75", FVID2_STD_SXGA_75, |
| {FVID2_STD_SXGA_75, 1280, 1024, FVID2_SF_PROGRESSIVE, |
| 135000, 75, 16, 248, 144, 1, 38, 3} |
| }, |
| {"1280x1024@85", FVID2_STD_SXGA_85, |
| {FVID2_STD_CUSTOM, 1280, 1024, FVID2_SF_PROGRESSIVE, |
| 157440, 85, 64, 224, 160, 1, 44, 3} |
| }, |
| /*UXGA*/ |
| {"1600x1200@60", FVID2_STD_UXGA_60, |
| {FVID2_STD_UXGA_60, 1600, 1200, FVID2_SF_PROGRESSIVE, |
| 162000, 60, 64, 304, 192, 1, 46, 3} |
| } |
| |
| }; |
| /*use for the venc name*/ |
| static const struct dc_vencname_info venc_name[VPS_DC_MAX_VENC] = { |
| {"hdmi", VPS_DC_VENC_HDMI, VPS_DC_HDMI_BLEND, HDMI}, |
| {"dvo2", VPS_DC_VENC_DVO2, VPS_DC_DVO2_BLEND, DVO2}, |
| {"sd", VPS_DC_VENC_SD, VPS_DC_SDVENC_BLEND, SDVENC}, |
| {"hdcomp", VPS_DC_VENC_HDCOMP, VPS_DC_HDCOMP_BLEND, HDCOMP} |
| }; |
| /*use for pll sysfs*/ |
| static const struct vps_sname_info pllvenc_name[] = { |
| {"rfclk", VPS_SYSTEM_VPLL_OUTPUT_VENC_RF}, |
| {"dclk", VPS_SYSTEM_VPLL_OUTPUT_VENC_D}, |
| {"aclk", VPS_SYSTEM_VPLL_OUTPUT_VENC_A} |
| }; |
| /*used for clock source sysfs*/ |
| static const struct vps_sname_info vclksrc_name[] = { |
| {"dclk", VPS_DC_CLKSRC_VENCD}, |
| {"dclkdiv2", VPS_DC_CLKSRC_VENCD_DIV2}, |
| {"dclkdiff", VPS_DC_CLKSRC_VENCD_DIV2_DIFF}, |
| {"aclk", VPS_DC_CLKSRC_VENCA}, |
| {"aclkdiv2", VPS_DC_CLKSRC_VENCA_DIV2}, |
| {"aclkdiff", VPS_DC_CLKSRC_VENCA_DIV2_DIFF} |
| }; |
| /*used for output sysfs*/ |
| static const struct vps_sname_info dfmt_name[VPS_DC_DVOFMT_MAX] = { |
| {"single", VPS_DC_DVOFMT_SINGLECHAN}, |
| {"double", VPS_DC_DVOFMT_DOUBLECHAN}, |
| {"triple", VPS_DC_DVOFMT_TRIPLECHAN_EMBSYNC}, |
| {"triplediscrete", VPS_DC_DVOFMT_TRIPLECHAN_DISCSYNC}, |
| {"doublediscrete", VPS_DC_DVOFMT_DOUBLECHAN_DISCSYNC}, |
| |
| }; |
| /*used for output sysfs*/ |
| static const struct vps_sname_info afmt_name[VPS_DC_A_OUTPUT_MAX] = { |
| {"composite", VPS_DC_A_OUTPUT_COMPOSITE}, |
| {"svideo", VPS_DC_A_OUTPUT_SVIDEO}, |
| {"component", VPS_DC_A_OUTPUT_COMPONENT}, |
| }; |
| /*used for output sysfs*/ |
| static const struct vps_sname_info datafmt_name[] = { |
| {"rgb888", FVID2_DF_RGB24_888}, |
| {"yuv444p", FVID2_DF_YUV444P}, |
| {"yuv422spuv", FVID2_DF_YUV422SP_UV}, |
| }; |
| /*used for nodes sysfs*/ |
| static const struct vps_sname_info dc_nodes[] = { |
| {"main", VPS_DC_MAIN_INPUT_PATH}, /*0*/ |
| {"vcompmux", VPS_DC_VCOMP_MUX}, /*1*/ |
| {"hdcompmux", VPS_DC_HDCOMP_MUX}, /*2*/ |
| {"sdmux", VPS_DC_SDVENC_MUX }, /*3*/ |
| {"aux", VPS_DC_AUX_INPUT_PATH}, /*4*/ |
| {"bp0", VPS_DC_BP0_INPUT_PATH}, /*5*/ |
| {"bp1", VPS_DC_BP1_INPUT_PATH}, /*6*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*7*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*8*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*9*/ |
| {"sd", VPS_DC_SEC1_INPUT_PATH}, /*10*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*11*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*12*/ |
| {"dummy", VPS_DC_MAX_NODE_NUM}, /*13*/ |
| {"vcomp", VPS_DC_VCOMP}, /*14*/ |
| {"cigcons", VPS_DC_CIG_CONSTRAINED_OUTPUT}, /*15*/ |
| {"cigin", VPS_DC_CIG_PIP_INPUT}, /*16*/ |
| {"cigncons", VPS_DC_CIG_NON_CONSTRAINED_OUTPUT}, /*17*/ |
| {"cigout", VPS_DC_CIG_PIP_OUTPUT}, /*18*/ |
| {"grpx0", VPS_DC_GRPX0_INPUT_PATH}, /*19*/ |
| {"grpx1", VPS_DC_GRPX1_INPUT_PATH}, /*20*/ |
| {"grpx2", VPS_DC_GRPX2_INPUT_PATH}, /*21*/ |
| {"hdmi", VPS_DC_HDMI_BLEND}, /*22*/ |
| #ifdef CONFIG_ARCH_TI816X |
| {"hdcomp", VPS_DC_HDCOMP_BLEND}, /*23*/ |
| {"dvo2", VPS_DC_DVO2_BLEND}, /*24*/ |
| {"sd", VPS_DC_SDVENC_BLEND}, /*25*/ |
| #else |
| {"dvo2", VPS_DC_DVO2_BLEND}, /*23*/ |
| {"sd", VPS_DC_SDVENC_BLEND}, /*24*/ |
| #endif |
| }; |
| |
| /*S***************************private funtions*******************/ |
| |
| static inline void dc_lock(struct vps_dispctrl *dctrl) |
| { |
| mutex_lock(&dctrl->dcmutex); |
| } |
| |
| static inline void dc_unlock(struct vps_dispctrl *dctrl) |
| { |
| mutex_unlock(&dctrl->dcmutex); |
| } |
| |
| static inline bool isdigitalvenc(int vid) |
| { |
| if ((vid == VPS_DC_VENC_HDMI) || |
| (vid == VPS_DC_VENC_DVO2)) |
| return true; |
| |
| return false; |
| } |
| |
| static inline bool isdigitalclk(u32 clk) |
| { |
| if ((clk == VPS_DC_CLKSRC_VENCA) || |
| (clk == VPS_DC_CLKSRC_VENCA_DIV2) || |
| (clk == VPS_DC_CLKSRC_VENCA_DIV2_DIFF)) |
| return false; |
| |
| return true; |
| |
| } |
| static inline bool isvalidclksrc(int vid, enum vps_dcvencclksrcsel clk) |
| { |
| if ((vid == VPS_DC_VENC_HDMI) && (!isdigitalclk(clk))) |
| return false; |
| /*ti814x, DVO2 must be aclk*/ |
| if (cpu_is_ti814x()) { |
| if ((vid == VPS_DC_VENC_DVO2) && (isdigitalclk(clk))) |
| return false; |
| } |
| return true; |
| } |
| |
| static inline bool isvalidmode(int vid, int mid) |
| { |
| switch (vid) { |
| case VPS_DC_VENC_HDMI: |
| if (cpu_is_ti816x()) |
| case VPS_DC_VENC_HDCOMP: |
| case VPS_DC_VENC_DVO2: |
| if ((mid == FVID2_STD_NTSC) || (mid == FVID2_STD_PAL)) |
| return false; |
| break; |
| case VPS_DC_VENC_SD: |
| if (!((mid == FVID2_STD_NTSC) || (mid == FVID2_STD_PAL))) |
| return false; |
| break; |
| } |
| |
| return true; |
| } |
| |
| /*get the clock venc*/ |
| static inline u32 get_plloutputvenc(int bidx) |
| { |
| struct vps_dcvencclksrc clksrc = disp_ctrl->blenders[bidx].clksrc; |
| |
| if (bidx == SDVENC) |
| return VPS_SYSTEM_VPLL_OUTPUT_VENC_RF; |
| |
| if (cpu_is_ti814x()) |
| return VPS_SYSTEM_VPLL_OUTPUT_VENC_D; |
| |
| if (isdigitalclk(clksrc.clksrc)) |
| return VPS_SYSTEM_VPLL_OUTPUT_VENC_D; |
| else |
| return VPS_SYSTEM_VPLL_OUTPUT_VENC_A; |
| } |
| |
| /*get the pixel clock for the standard mode*/ |
| static inline int get_pllclock(u32 mid, u32 *freq) |
| { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(vmode_info); i++) { |
| if (vmode_info[i].standard == mid) { |
| *freq = vmode_info[i].minfo.pixelclock; |
| if ((mid == FVID2_STD_NTSC) || |
| (mid == FVID2_STD_PAL)) { |
| if (cpu_is_ti814x()) |
| *freq = 54000; |
| } |
| return 0; |
| } |
| } |
| return -EINVAL; |
| } |
| /*get the current format based on the mode id*/ |
| static int dc_get_format_from_mid(int mid, |
| u32 *width, |
| u32 *height, |
| u8 *scformat) |
| { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(vmode_info); i++) { |
| if (mid == vmode_info[i].standard) { |
| *width = vmode_info[i].minfo.width; |
| *height = vmode_info[i].minfo.height; |
| *scformat = vmode_info[i].minfo.scanformat; |
| return 0; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int dc_get_timing(int mid, struct fvid2_modeinfo *minfo) |
| { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(vmode_info); i++) { |
| if (mid == vmode_info[i].standard) { |
| memcpy(minfo, &vmode_info[i].minfo, sizeof(*minfo)); |
| if ((mid == FVID2_STD_NTSC) || (mid == FVID2_STD_PAL)) { |
| if (cpu_is_ti814x()) |
| minfo->pixelclock = 54000; |
| } |
| |
| return 0; |
| } |
| } |
| return -EINVAL; |
| } |
| /*get the index of the desired venc id in the database*/ |
| static int get_idx_from_vid(int vid, int *idx) |
| { |
| int i; |
| |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| if (vid == venc_name[i].vid) { |
| *idx = venc_name[i].idx; |
| return 0; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| |
| /*get the venc id based on the name*/ |
| static int dc_get_vencid(char *vname, int *vid) |
| { |
| |
| int i; |
| |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| const struct dc_vencname_info *vnid = &venc_name[i]; |
| if (sysfs_streq(vname, vnid->name)) { |
| *vid = vnid->vid; |
| return 0; |
| } |
| } |
| return -1; |
| } |
| /*get the blender id from the indx */ |
| static int get_bid_from_idx(int idx, int *bid) |
| { |
| int i; |
| |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| const struct dc_vencname_info *vnid = &venc_name[i]; |
| if (vnid->idx == idx) { |
| *bid = vnid->bid; |
| return 0; |
| } |
| } |
| return -1; |
| |
| } |
| /*get the mode id based on the mode name*/ |
| static int dc_get_modeid(char *mname, int *mid) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(vmode_info); i++) { |
| const struct dc_vencmode_info *vinfo = &vmode_info[i]; |
| if (sysfs_streq(mname, vinfo->name)) { |
| *mid = vinfo->standard; |
| return 0; |
| } |
| } |
| return -1; |
| } |
| |
| /*get the node id based on the name*/ |
| static int dc_get_nodeid(char *name, int *nid) |
| { |
| int i; |
| |
| |
| if (sysfs_streq(name, "dummy")) |
| return -EINVAL; |
| |
| for (i = 0; i < ARRAY_SIZE(dc_nodes); i++) { |
| const struct vps_sname_info *ninfo = &dc_nodes[i]; |
| if (sysfs_streq(name, ninfo->name)) { |
| *nid = ninfo->value; |
| return 0; |
| } |
| } |
| return -EINVAL; |
| } |
| |
| /*get the venc information from M3*/ |
| static int dc_get_vencinfo(struct vps_dcvencinfo *vinfo) |
| { |
| int r = 0; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| memcpy(disp_ctrl->vinfo, vinfo, sizeof(struct vps_dcvencinfo)); |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_VENC_MODE, |
| (void *)disp_ctrl->vinfo_phy, |
| NULL); |
| |
| if (r) |
| VPSSERR("failed to get venc info.\n"); |
| else |
| memcpy(vinfo, |
| disp_ctrl->vinfo, |
| sizeof(struct vps_dcvencinfo)); |
| |
| return r; |
| |
| } |
| |
| /*is venc running*/ |
| static int dc_isvencrunning(int vid) |
| { |
| struct vps_dcvencinfo vinfo; |
| int r = 0; |
| vinfo.numvencs = 1; |
| vinfo.modeinfo[0].vencid = vid; |
| r = dc_get_vencinfo(&vinfo); |
| |
| if (!r) |
| return vinfo.modeinfo[0].isvencrunning; |
| |
| return r; |
| } |
| |
| /*Get the current VENC output info*/ |
| static int dc_get_output(struct vps_dcoutputinfo *oinfo) |
| { |
| int r = 0; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| /*get the venc output info*/ |
| disp_ctrl->opinfo->vencnodenum = oinfo->vencnodenum; |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_VENC_OUTPUT, |
| (void *)disp_ctrl->opinfo_phy, |
| NULL); |
| |
| if (r) |
| VPSSERR("failed to get venc output info\n"); |
| else |
| memcpy(oinfo, |
| disp_ctrl->opinfo, |
| sizeof(struct vps_dcoutputinfo)); |
| return r; |
| } |
| |
| /*Set the VENC outputs*/ |
| static int dc_set_output(struct vps_dcoutputinfo *oinfo) |
| { |
| int r; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| |
| memcpy(disp_ctrl->opinfo, oinfo, sizeof(struct vps_dcoutputinfo)); |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_VENC_OUTPUT, |
| (void *)disp_ctrl->opinfo_phy, |
| NULL); |
| if (r) |
| VPSSERR("failed to set venc output\n"); |
| |
| return r; |
| |
| } |
| /*set up the pll clock*/ |
| static int dc_set_pllclock(int bidx, u32 clock) |
| { |
| struct vps_systemvpllclk pll; |
| int r = 0; |
| |
| /*FIXME: call function of HDMI driver to set HDMI for ti814x*/ |
| if (cpu_is_ti814x() && (bidx == HDMI)) |
| return r; |
| |
| pll.outputvenc = (enum vps_vplloutputclk)get_plloutputvenc(bidx); |
| pll.outputclk = clock; |
| r = vps_system_setpll(&pll); |
| return r; |
| } |
| |
| static int dc_set_pll_by_mid(int bidx, int mid) |
| { |
| int r = 0; |
| u32 clock; |
| |
| r = get_pllclock(mid, &clock); |
| if (r) { |
| VPSSERR("nonexit mode %d\n", mid); |
| return r; |
| } |
| |
| r = dc_set_pllclock(bidx, clock); |
| |
| return r; |
| } |
| |
| /*get the clock source*/ |
| static int dc_get_clksrc(struct vps_dcvencclksrc *clksrc) |
| { |
| int r = 0; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| disp_ctrl->clksrc->venc = clksrc->venc; |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_VENC_CLK_SRC, |
| (void *)disp_ctrl->clksrc_phy, |
| NULL); |
| |
| if (r) |
| VPSSERR("get clock source failed\n"); |
| else |
| clksrc->clksrc = disp_ctrl->clksrc->clksrc; |
| |
| return r; |
| } |
| |
| /*set the clock source*/ |
| static int dc_set_clksrc(struct vps_dcvencclksrc *clksrc) |
| { |
| int r = 0; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| if (!isvalidclksrc(clksrc->venc, clksrc->clksrc)) { |
| VPSSERR("invalid clock source\n"); |
| return -EINVAL; |
| } |
| |
| disp_ctrl->clksrc->venc = clksrc->venc; |
| disp_ctrl->clksrc->clksrc = clksrc->clksrc; |
| |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_VENC_CLK_SRC, |
| (void *)disp_ctrl->clksrc_phy, |
| NULL); |
| if (r) |
| VPSSERR("set clock source failed\n"); |
| |
| return r; |
| } |
| /*get the format based on the venc id*/ |
| static int dc_get_format_from_vid(int vid, |
| u32 *width, |
| u32 *height, |
| u8 *scformat) |
| { |
| int r = 0; |
| struct vps_dcvencinfo vinfo; |
| |
| vinfo.numvencs = 1; |
| vinfo.modeinfo[0].vencid = vid; |
| |
| r = dc_get_vencinfo(&vinfo); |
| if (r) |
| return -EINVAL; |
| |
| if (vinfo.modeinfo[0].minfo.standard == FVID2_STD_CUSTOM) { |
| *width = vinfo.modeinfo[0].minfo.width; |
| *height = vinfo.modeinfo[0].minfo.height; |
| *scformat = vinfo.modeinfo[0].minfo.scanformat; |
| } else { |
| r = dc_get_format_from_mid(vinfo.modeinfo[0].minfo.standard, |
| width, |
| height, |
| scformat); |
| } |
| |
| |
| return 0; |
| } |
| |
| /*get the format based on the blender id*/ |
| static int dc_get_format_from_bid(int bid, |
| u32 *width, |
| u32 *height, |
| u8 *scformat) |
| { |
| int i; |
| int r = -EINVAL; |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| if (bid == venc_name[i].bid) { |
| r = dc_get_format_from_vid(venc_name[i].vid, |
| width, |
| height, |
| scformat); |
| break; |
| } |
| } |
| |
| return r; |
| } |
| |
| /*disable the desired vencs*/ |
| static int dc_venc_disable(int vid) |
| { |
| int i = 0; |
| int r = 0; |
| struct vps_dcvencinfo vinfo; |
| int venc_ids = vid; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| if (vid == 0) |
| return 0; |
| |
| if (vid & (~disp_ctrl->vencmask)) { |
| VPSSERR("wrong venc id.\n"); |
| return -EINVAL; |
| } |
| |
| |
| VPSSDBG("enter venc disable\n"); |
| |
| vinfo.numvencs = 0; |
| /*get the id of each venc to be disabled*/ |
| while (venc_ids >> i) { |
| if ((venc_ids >> i++) & 1) |
| vinfo.modeinfo[vinfo.numvencs++].vencid = |
| 1 << (i - 1); |
| } |
| |
| r = dc_get_vencinfo(&vinfo); |
| |
| if (r) { |
| VPSSERR("faild to get venc info.\n"); |
| return r; |
| } |
| |
| venc_ids = vid; |
| for (i = 0; i < vinfo.numvencs; i++) { |
| if (vinfo.modeinfo[i].isvencrunning == 0) { |
| VPSSERR("venc %d already stop\n", |
| vinfo.modeinfo[i].vencid); |
| venc_ids &= ~vinfo.modeinfo[i].vencid; |
| } |
| } |
| |
| if (venc_ids && !r) { |
| *disp_ctrl->dis_vencs = venc_ids; |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_DISABLE_VENC, |
| (void *)disp_ctrl->dis_vencsphy, |
| NULL); |
| |
| if (r == 0) { |
| disp_ctrl->enabled_venc_ids &= ~venc_ids; |
| if (disp_ctrl->tiedvenc) { |
| disp_ctrl->tiedvenc &= ~venc_ids; |
| venc_ids = 0; |
| i = 0; |
| /*calculate how vencs left in tied list*/ |
| while (disp_ctrl->tiedvenc >> i) { |
| if ((disp_ctrl->tiedvenc >> i++) & 1) |
| venc_ids++; |
| |
| } |
| /*if one venc left,set tiedvenc to zero*/ |
| if (venc_ids == 1) |
| disp_ctrl->tiedvenc = 0; |
| } |
| } else |
| VPSSERR("failed to disable the venc.\n"); |
| |
| } |
| |
| return r; |
| } |
| |
| /*set the mode for desired vencs*/ |
| static int dc_set_vencmode(struct vps_dcvencinfo *vinfo) |
| { |
| int i, r = 0; |
| int vencs = 0; |
| struct vps_dcvencinfo vi; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| |
| /*get the current setting based on the app inputs*/ |
| for (i = 0; i < vinfo->numvencs; i++) |
| vi.modeinfo[i].vencid = vinfo->modeinfo[i].vencid; |
| |
| vi.numvencs = vinfo->numvencs; |
| |
| r = dc_get_vencinfo(&vi); |
| |
| if (r) { |
| VPSSERR("failed to get venc info.\n"); |
| goto exit; |
| } |
| |
| /*make sure current venc status is matching */ |
| disp_ctrl->vinfo->numvencs = 0; |
| disp_ctrl->vinfo->tiedvencs = 0; |
| for (i = 0; i < vinfo->numvencs; i++) { |
| if (vi.modeinfo[i].isvencrunning) { |
| if (vi.modeinfo[i].minfo.standard != |
| vinfo->modeinfo[i].minfo.standard) { |
| r = -EINVAL; |
| VPSSERR("venc %d already running with \ |
| different mode\n", |
| vi.modeinfo[i].vencid); |
| goto exit; |
| } else |
| VPSSDBG("venc %d already running\n", |
| vi.modeinfo[i].vencid); |
| |
| } else { |
| memcpy(&disp_ctrl->vinfo->modeinfo \ |
| [disp_ctrl->vinfo->numvencs++], |
| &vinfo->modeinfo[i], |
| sizeof(struct vps_dcmodeinfo)); |
| vencs |= vinfo->modeinfo[i].vencid; |
| } |
| } |
| if (vinfo->tiedvencs) { |
| if ((vencs & vinfo->tiedvencs) != vinfo->tiedvencs) { |
| r = -EINVAL; |
| VPSSERR("can not tied venc\n"); |
| goto exit; |
| } else |
| disp_ctrl->vinfo->tiedvencs = vinfo->tiedvencs; |
| } |
| |
| if (disp_ctrl->vinfo->numvencs) { |
| |
| /*set the VENC Mode*/ |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_VENC_MODE, |
| (void *)disp_ctrl->vinfo_phy, |
| NULL); |
| if (r) { |
| VPSSERR("failed to set venc mdoe.\n"); |
| goto exit; |
| } |
| disp_ctrl->enabled_venc_ids |= vencs; |
| } |
| exit: |
| return r; |
| |
| } |
| |
| static int dc_enum_node_input(struct vps_dispctrl *dctrl, |
| struct vps_dcenumnodeinput *eninput) |
| { |
| int r = 0; |
| |
| if ((dctrl == NULL) || (dctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| *dctrl->dceninput = *eninput; |
| |
| r = vps_fvid2_control(dctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_ENUM_NODE_INPUTS, |
| (void *)dctrl->dceninput_phy, |
| NULL); |
| |
| if (!r) |
| *eninput = *dctrl->dceninput; |
| |
| return r; |
| |
| } |
| |
| static int dc_get_node_status(struct vps_dispctrl *dctrl, |
| struct vps_dcnodeinput *ninput) |
| { |
| int r = 0; |
| |
| if ((dctrl == NULL) || (dctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| *dctrl->nodeinfo = *ninput; |
| |
| r = vps_fvid2_control(dctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_NODE_INPUT_STATUS, |
| (void *)dctrl->ninfo_phy, |
| NULL); |
| |
| if (r) |
| VPSSERR("failed to get node status\n"); |
| else |
| *ninput = *dctrl->nodeinfo; |
| |
| return r; |
| |
| } |
| |
| static int dc_get_comp_rtconfig(struct vps_dispctrl *dctrl, |
| struct vps_dccomprtconfig *compcfg) |
| { |
| int r = 0; |
| |
| if ((dctrl == NULL) || (dctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| dctrl->comprtcfg->nodeid = compcfg->nodeid; |
| |
| r = vps_fvid2_control(dctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_COMP_RTCONFIG, |
| (void *)dctrl->comprtcfg_phy, |
| NULL); |
| |
| if (r) |
| VPSSERR("Get compositor RT config failed\n"); |
| else |
| *compcfg = *dctrl->comprtcfg; |
| |
| return r; |
| |
| |
| } |
| |
| static int dc_set_comp_rtconfig(struct vps_dispctrl *dctrl, |
| struct vps_dccomprtconfig *compcfg) |
| { |
| int r = 0; |
| |
| if ((dctrl == NULL) || (dctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| *dctrl->comprtcfg = *compcfg; |
| r = vps_fvid2_control(dctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_COMP_RTCONFIG, |
| (void *)dctrl->comprtcfg_phy, |
| NULL); |
| if (r) |
| VPSSERR("Set compositor RT config failed\n"); |
| |
| return r; |
| |
| } |
| /*E******************************** private functions *********************/ |
| |
| /*S******************************* public functions *********************/ |
| |
| /*get the id(venc,blender,mode) based on the name*/ |
| int vps_dc_get_id(char *name, int *id, enum dc_idtype type) |
| { |
| |
| int r = -EINVAL; |
| switch (type) { |
| case DC_BLEND_ID: |
| case DC_NODE_ID: |
| r = dc_get_nodeid(name, id); |
| break; |
| case DC_VENC_ID: |
| r = dc_get_vencid(name, id); |
| break; |
| case DC_MODE_ID: |
| r = dc_get_modeid(name, id); |
| break; |
| } |
| |
| return r; |
| } |
| |
| /*get the tied venc information*/ |
| int vps_dc_get_tiedvenc(u8 *tiedvenc) |
| { |
| *tiedvenc = disp_ctrl->tiedvenc; |
| return 0; |
| } |
| /*set the streaming on the blender, not used*/ |
| void vps_dc_set_actnodes(u8 setflag, u8 bidx) |
| { |
| struct dc_blender_info *binfo = &disp_ctrl->blenders[bidx]; |
| |
| if (setflag) |
| binfo->actnodes++; |
| else |
| if (binfo->actnodes != 0) |
| binfo->actnodes--; |
| |
| } |
| /*get the venc infor for the desired vencs*/ |
| int vps_dc_get_vencinfo(struct vps_dcvencinfo *vinfo) |
| { |
| int r; |
| dc_lock(disp_ctrl); |
| r = dc_get_vencinfo(vinfo); |
| dc_unlock(disp_ctrl); |
| |
| return r; |
| } |
| |
| /*get the node name based on the id*/ |
| int vps_dc_get_node_name(int id, char *name) |
| { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(dc_nodes); i++) { |
| const struct vps_sname_info *ninfo = &dc_nodes[i]; |
| if (id == ninfo->value) { |
| strcpy(name, (char *)ninfo->name); |
| return 0; |
| } |
| } |
| return -EINVAL; |
| |
| } |
| |
| /*set dc config not used now*/ |
| int vps_dc_set_config(struct vps_dcconfig *usercfg, int setflag) |
| { |
| int r = 0; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| if (usercfg->vencinfo.numvencs > disp_ctrl->numvencs) { |
| VPSSERR("num vens (%d) over max\n", |
| usercfg->vencinfo.numvencs); |
| return -EINVAL; |
| } |
| if (usercfg->vencinfo.tiedvencs & (~disp_ctrl->tiedvenc)) { |
| VPSSERR("tied venc not match.\n"); |
| return -EINVAL; |
| } |
| VPSSDBG("enter set config\n"); |
| dc_lock(disp_ctrl); |
| |
| memcpy(disp_ctrl->dccfg, usercfg, sizeof(struct vps_dcconfig)); |
| |
| |
| if (setflag) { |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_CONFIG, |
| (void *)disp_ctrl->dccfg_phy, |
| NULL); |
| if (r) |
| VPSSDBG("faield to set the DC config.\n"); |
| } else { |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_CLEAR_CONFIG, |
| (void *)disp_ctrl->dccfg_phy, |
| NULL); |
| |
| if (r) |
| VPSSDBG("faield to clear the DC config.\n"); |
| |
| } |
| |
| dc_unlock(disp_ctrl); |
| |
| return r; |
| } |
| |
| /*get current venc output format*/ |
| int vps_dc_get_outpfmt(int id, u32 *width, |
| u32 *height, |
| u8 *scformat, |
| enum dc_idtype type) |
| { |
| int r; |
| |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| VPSSDBG("enter get output format\n"); |
| |
| dc_lock(disp_ctrl); |
| if (type == DC_VENC_ID) |
| r = dc_get_format_from_vid(id, width, height, scformat); |
| else if (type == DC_BLEND_ID) |
| r = dc_get_format_from_bid(id, width, height, scformat); |
| else if (type == DC_MODE_ID) |
| r = dc_get_format_from_mid(id, width, height, scformat); |
| else |
| r = -EINVAL; |
| |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| /* set/clear the node path/edge */ |
| int vps_dc_set_node(u8 nodeid, u8 inputid, u8 enable) |
| { |
| |
| int r = 0; |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL)) |
| return -EINVAL; |
| |
| VPSSDBG("enter set node\n"); |
| dc_lock(disp_ctrl); |
| |
| disp_ctrl->nodeinfo->nodeid = nodeid; |
| disp_ctrl->nodeinfo->inputid = inputid; |
| |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_NODE_INPUT_STATUS, |
| (void *)disp_ctrl->ninfo_phy, |
| NULL); |
| |
| if (r) { |
| VPSSERR("failed to get node input status\n"); |
| goto exit; |
| } |
| if (disp_ctrl->nodeinfo->isenable == enable) { |
| if (enable) |
| VPSSDBG("node already connected\n"); |
| else |
| VPSSDBG("node already disconnected\n"); |
| |
| goto exit; |
| } |
| /*call ioctl to set/clear the node */ |
| disp_ctrl->nodeinfo->isenable = enable; |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_NODE_INPUT, |
| (void *)disp_ctrl->ninfo_phy, |
| NULL); |
| if (r) |
| VPSSERR("failed to enable node.\n"); |
| |
| exit: |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| int vps_dc_set_color(struct vps_dccigrtconfig *cigconfig) |
| { |
| int r; |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL) || |
| (cigconfig == NULL)) |
| return -EINVAL; |
| |
| VPSSDBG("set color\n"); |
| dc_lock(disp_ctrl); |
| |
| memcpy(disp_ctrl->cigcfg, cigconfig, sizeof(*cigconfig)); |
| |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_SET_CIG_RTCONFIG, |
| (void *)disp_ctrl->cigcfg_phy, |
| NULL); |
| if (r) |
| VPSSERR("failed to set CIG color\n"); |
| |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| int vps_dc_get_color(struct vps_dccigrtconfig *cigconfig) |
| { |
| int r; |
| if ((disp_ctrl == NULL) || (disp_ctrl->fvid2_handle == NULL) || |
| (cigconfig == NULL)) |
| return -EINVAL; |
| |
| VPSSDBG("get color\n"); |
| dc_lock(disp_ctrl); |
| |
| disp_ctrl->cigcfg->nodeid = cigconfig->nodeid; |
| r = vps_fvid2_control(disp_ctrl->fvid2_handle, |
| IOCTL_VPS_DCTRL_GET_CIG_RTCONFIG, |
| (void *)disp_ctrl->cigcfg_phy, |
| NULL); |
| if (!r) |
| memcpy(cigconfig, disp_ctrl->cigcfg, sizeof(*cigconfig)); |
| else |
| VPSSERR("failed to get cig color\n"); |
| |
| dc_unlock(disp_ctrl); |
| return r; |
| |
| } |
| |
| int vps_dc_enum_node_input(struct vps_dcenumnodeinput *eninput) |
| { |
| int r = 0; |
| |
| VPSSDBG("enum node input\n"); |
| dc_lock(disp_ctrl); |
| r = dc_enum_node_input(disp_ctrl, eninput); |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| int vps_dc_get_node_status(struct vps_dcnodeinput *ninput) |
| { |
| int r = 0; |
| |
| VPSSDBG("get node status\n"); |
| dc_lock(disp_ctrl); |
| r = dc_get_node_status(disp_ctrl, ninput); |
| dc_unlock(disp_ctrl); |
| return r; |
| |
| } |
| int vps_dc_get_timing(u32 bid, struct fvid2_modeinfo *tinfo) |
| { |
| int i; |
| |
| |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| if (bid == venc_name[i].bid) { |
| *tinfo = venc_info.modeinfo[i].minfo; |
| if ((tinfo->standard == FVID2_STD_NTSC) || |
| (tinfo->standard == FVID2_STD_PAL)) { |
| if (cpu_is_ti814x()) |
| tinfo->pixelclock = 54000; |
| } |
| |
| |
| return 0; |
| } |
| } |
| return -EINVAL; |
| } |
| /*E********************************* public functions *****************/ |
| |
| /*sysfs function for blender starting from here*/ |
| static ssize_t blender_mode_show(struct dc_blender_info *binfo, char *buf) |
| { |
| int i; |
| u32 idx = binfo->idx; |
| int l = 0; |
| for (i = 0; i < ARRAY_SIZE(vmode_info); i++) { |
| u32 standard = venc_info.modeinfo[idx].minfo.standard; |
| if (standard == FVID2_STD_CUSTOM) { |
| if (venc_info.modeinfo[idx].minfo.scanformat == |
| FVID2_SF_INTERLACED) |
| l = snprintf(buf, PAGE_SIZE, "%ux%u@%ui\n", |
| venc_info.modeinfo[idx].minfo.width, |
| venc_info.modeinfo[idx].minfo.height, |
| venc_info.modeinfo[idx].minfo.fps); |
| else |
| l = snprintf(buf, PAGE_SIZE, "%ux%u@%u\n", |
| venc_info.modeinfo[idx].minfo.width, |
| venc_info.modeinfo[idx].minfo.height, |
| venc_info.modeinfo[idx].minfo.fps); |
| |
| } else if (vmode_info[i].standard == standard) { |
| l = snprintf(buf, PAGE_SIZE, "%s\n", |
| vmode_info[i].name); |
| break; |
| } |
| } |
| return l; |
| } |
| |
| static ssize_t blender_mode_store(struct dc_blender_info *binfo, |
| const char *buf, size_t size) |
| { |
| int r = 0; |
| u32 idx = binfo->idx; |
| u32 mid; |
| |
| dc_lock(binfo->dctrl); |
| |
| /*venc should be stop before changes*/ |
| |
| if (dc_isvencrunning(venc_info.modeinfo[idx].vencid)) { |
| VPSSERR("stop venc before changing mode\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| if (dc_get_modeid((char *)buf, &mid)) { |
| VPSSERR("failed to get the mode %s.", buf); |
| r = -EINVAL; |
| goto exit; |
| } |
| /*make sure the mode is supported by the venc*/ |
| if (!isvalidmode(venc_info.modeinfo[idx].vencid, mid)) |
| goto exit; |
| |
| |
| /*only set the PLL if it is auto mode*/ |
| if (binfo->dctrl->automode) { |
| r = dc_set_pll_by_mid(binfo->idx, mid); |
| if (r) |
| goto exit; |
| } |
| venc_info.modeinfo[idx].minfo.standard = mid; |
| dc_get_timing(mid, &venc_info.modeinfo[idx].minfo); |
| #ifdef CONFIG_ARCH_TI816X |
| if (cpu_is_ti816x()) { |
| if ((binfo->idx == HDCOMP) && (binfo->isdeviceon == true)) { |
| if ((mid == FVID2_STD_1080P_60) || |
| (mid == FVID2_STD_1080P_50)) |
| r = pcf8575_ths7360_hd_enable( |
| TI816X_THS7360_SF_TRUE_HD_MODE); |
| else |
| r = pcf8575_ths7360_hd_enable( |
| TI816X_THS7360_SF_HD_MODE); |
| if (r < 0) { |
| VPSSERR("failed to set THS filter\n"); |
| goto exit; |
| } |
| |
| } |
| } |
| #endif |
| r = size; |
| exit: |
| dc_unlock(binfo->dctrl); |
| return r; |
| } |
| |
| static ssize_t blender_timings_show(struct dc_blender_info *binfo, char *buf) |
| { |
| int r; |
| struct fvid2_modeinfo *t; |
| |
| t = &venc_info.modeinfo[binfo->idx].minfo; |
| r = snprintf(buf, |
| PAGE_SIZE, |
| "%u,%u/%u/%u/%u,%u/%u/%u/%u,%u\n", |
| t->pixelclock, |
| t->width, t->hfrontporch, t->hbackporch, t->hsynclen, |
| t->height, t->vfrontporch, t->vbackporch, t->vsynclen, |
| t->scanformat); |
| |
| return r; |
| |
| } |
| |
| static ssize_t blender_timings_store(struct dc_blender_info *binfo, |
| const char *buf, size_t size) |
| { |
| int r = 0; |
| struct fvid2_modeinfo t; |
| u32 num; |
| u32 vmode; |
| if (binfo->idx == SDVENC) |
| return -EINVAL; |
| |
| dc_lock(binfo->dctrl); |
| |
| /*venc should be stop before changes*/ |
| if (dc_isvencrunning(venc_info.modeinfo[binfo->idx].vencid)) { |
| VPSSERR("stop venc before changing timing\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| num = sscanf(buf, "%u,%u/%u/%u/%u,%u/%u/%u/%u,%u/%u", |
| &t.pixelclock, |
| &t.width, &t.hfrontporch, &t.hbackporch, &t.hsynclen, |
| &t.height, &t.vfrontporch, &t.vbackporch, &t.vsynclen, |
| &t.scanformat, &vmode); |
| |
| if (!((num == 11) || (num == 10))) { |
| r = -EINVAL; |
| VPSSERR("wrong timing input %d\n", num); |
| goto exit; |
| } |
| /*if use did not assign mode, than we fix it to 1*/ |
| if (num == 10) |
| vmode = 1; |
| |
| memcpy(&venc_info.modeinfo[binfo->idx].minfo, &t, sizeof(t)); |
| venc_info.modeinfo[binfo->idx].minfo.standard = FVID2_STD_CUSTOM; |
| venc_info.modeinfo[binfo->idx].mode = vmode; |
| /*calculate the refresh rate*/ |
| venc_info.modeinfo[binfo->idx].minfo.fps = |
| (t.pixelclock * 1000) / |
| ((t.width + t.hfrontporch + t.hbackporch + t.hsynclen) * |
| (t.height + t.vfrontporch + t.vbackporch + t.vsynclen)); |
| |
| if (t.scanformat == 0) |
| venc_info.modeinfo[binfo->idx].minfo.fps *= 2; |
| r = dc_set_pllclock(binfo->idx, t.pixelclock); |
| |
| if (r) { |
| VPSSERR("failed to set %dKHz clock\n", |
| t.pixelclock); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| r = size; |
| exit: |
| dc_unlock(binfo->dctrl); |
| return r; |
| } |
| |
| static ssize_t blender_enabled_show(struct dc_blender_info *binfo, char *buf) |
| { |
| int r; |
| struct vps_dcvencinfo vinfo; |
| |
| dc_lock(binfo->dctrl); |
| |
| vinfo.numvencs = 1; |
| vinfo.modeinfo[0].vencid = venc_name[binfo->idx].vid; |
| r = dc_get_vencinfo(&vinfo); |
| |
| if (r) { |
| VPSSERR(" Failed to get venc infor\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| r = snprintf(buf, PAGE_SIZE, "%d\n", vinfo.modeinfo[0].isvencrunning); |
| |
| exit: |
| dc_unlock(binfo->dctrl); |
| return r; |
| } |
| |
| static ssize_t blender_enabled_store(struct dc_blender_info *binfo, |
| const char *buf, |
| size_t size) |
| { |
| int enabled; |
| int vid; |
| int r = 0; |
| |
| enabled = simple_strtoul(buf, NULL, 10); |
| |
| dc_lock(disp_ctrl); |
| /*get vid id*/ |
| vid = venc_name[binfo->idx].vid; |
| |
| if (enabled == 0) { |
| r = dc_venc_disable(vid); |
| if (r) { |
| VPSSERR("failed to disable %s venc\n", |
| binfo->name); |
| r = -EINVAL; |
| goto exit; |
| } |
| } else { |
| int idx; |
| struct vps_dcvencinfo vinfo; |
| get_idx_from_vid(vid, &idx); |
| memcpy(&vinfo.modeinfo[0], |
| &venc_info.modeinfo[idx], |
| sizeof(struct vps_dcvencinfo)); |
| |
| vinfo.numvencs = 1; |
| vinfo.tiedvencs = 0; |
| r = dc_set_vencmode(&vinfo); |
| if (r) { |
| VPSSERR("failed to enable venc %s\n", |
| binfo->name); |
| r = -EINVAL; |
| goto exit; |
| } |
| } |
| |
| r = size; |
| exit: |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| static ssize_t blender_clksrc_show(struct dc_blender_info *binfo, char *buf) |
| { |
| int r = 0; |
| struct vps_dcvencclksrc *clksrc = &binfo->clksrc; |
| |
| if (binfo->idx == SDVENC) { |
| VPSSERR("no clock soure for SD VENC\n"); |
| return -EINVAL; |
| } |
| |
| dc_lock(binfo->dctrl); |
| clksrc->venc = venc_name[binfo->idx].vid; |
| r = dc_get_clksrc(clksrc); |
| dc_unlock(binfo->dctrl); |
| if (r) |
| return r; |
| else |
| return snprintf(buf, PAGE_SIZE, "%s\n", |
| vclksrc_name[clksrc->clksrc].name); |
| |
| } |
| |
| static ssize_t blender_clksrc_store(struct dc_blender_info *binfo, |
| const char *buf, |
| size_t size) |
| { |
| int r = 0, i; |
| struct vps_dcvencclksrc clksrc; |
| bool found = false; |
| |
| if (binfo->idx == SDVENC) { |
| VPSSERR("no clock soure for SD VENC\n"); |
| return -EINVAL; |
| } |
| |
| dc_lock(binfo->dctrl); |
| clksrc.venc = venc_name[binfo->idx].vid; |
| |
| if (dc_isvencrunning(clksrc.venc)) { |
| VPSSERR("please stop venc before changing clock source\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| /*found the matching clock source*/ |
| for (i = 0; i < ARRAY_SIZE(vclksrc_name); i++) { |
| if (sysfs_streq(buf, vclksrc_name[i].name)) { |
| clksrc.clksrc = vclksrc_name[i].value; |
| found = true; |
| break; |
| } |
| } |
| /*set the clock source*/ |
| if (found == true) { |
| r = dc_set_clksrc(&clksrc); |
| if (!r) { |
| r = size; |
| /*store back*/ |
| binfo->clksrc.clksrc = clksrc.clksrc; |
| } |
| } else { |
| r = -EINVAL; |
| VPSSERR("invalid clock source input\n"); |
| } |
| |
| exit: |
| dc_unlock(binfo->dctrl); |
| return r; |
| } |
| |
| static ssize_t blender_output_show(struct dc_blender_info *binfo, char *buf) |
| { |
| struct vps_dcoutputinfo oinfo; |
| int r = 0; |
| int l = 0, i; |
| |
| oinfo.vencnodenum = venc_name[binfo->idx].vid; |
| dc_lock(binfo->dctrl); |
| r = dc_get_output(&oinfo); |
| dc_unlock(binfo->dctrl); |
| if (r) |
| return -EINVAL; |
| |
| |
| if (isdigitalvenc(oinfo.vencnodenum)) |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, "%s", |
| dfmt_name[oinfo.dvofmt].name); |
| else |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, "%s", |
| afmt_name[oinfo.afmt].name); |
| |
| for (i = 0 ; i < ARRAY_SIZE(datafmt_name); i++) { |
| if (datafmt_name[i].value == oinfo.dataformat) |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, ",%s\n", |
| datafmt_name[i].name); |
| } |
| |
| return l; |
| |
| } |
| |
| static ssize_t blender_output_store(struct dc_blender_info *binfo, |
| const char *buf, |
| size_t size) |
| { |
| struct vps_dcoutputinfo oinfo; |
| int r = 0; |
| char *input = (char *)buf; |
| char *ptr; |
| enum vps_dcdigitalfmt dfmt = VPS_DC_DVOFMT_MAX; |
| enum vps_dcanalogfmt afmt = VPS_DC_A_OUTPUT_MAX; |
| enum fvid2_dataformat fmt = FVID2_DF_MAX; |
| |
| oinfo.vencnodenum = venc_name[binfo->idx].vid; |
| |
| dc_lock(binfo->dctrl); |
| |
| /*venc should be off before changed output*/ |
| if (dc_isvencrunning(oinfo.vencnodenum)) { |
| VPSSERR("please disable VENC before changing output\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| dc_get_output(&oinfo); |
| |
| |
| /*process the input buf*/ |
| while ((ptr = strsep(&input, ",")) != NULL) { |
| int i; |
| bool found; |
| found = false; |
| /*check data format first*/ |
| for (i = 0; i < ARRAY_SIZE(datafmt_name); i++) { |
| if (sysfs_streq(ptr, datafmt_name[i].name)) { |
| fmt = datafmt_name[i].value; |
| found = true; |
| } |
| } |
| /*check digital format or analog format based on current venc*/ |
| if (!found) { |
| if (isdigitalvenc(oinfo.vencnodenum)) { |
| for (i = 0; i < VPS_DC_DVOFMT_MAX; i++) |
| if (sysfs_streq(ptr, |
| dfmt_name[i].name)) { |
| dfmt = dfmt_name[i].value; |
| found = true; |
| break; |
| } |
| } else { |
| for (i = 0; i < VPS_DC_A_OUTPUT_MAX; i++) |
| if (sysfs_streq(ptr, |
| afmt_name[i].name)) { |
| afmt = afmt_name[i].value; |
| found = true; |
| break; |
| } |
| } |
| |
| if (found == false) { |
| VPSSERR("invalid output value %s\n", ptr); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| } |
| |
| if (input == NULL) |
| break; |
| } |
| |
| /*make sure the input is right before send out to M3*/ |
| if (isdigitalvenc(oinfo.vencnodenum)) { |
| if ((dfmt == VPS_DC_DVOFMT_MAX) && (fmt == FVID2_DF_MAX)) { |
| VPSSERR("no valid digital output settings\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| if (dfmt != VPS_DC_DVOFMT_MAX) |
| oinfo.dvofmt = dfmt; |
| |
| } else { |
| if ((afmt == VPS_DC_A_OUTPUT_MAX) && (fmt == FVID2_DF_MAX)) { |
| VPSSERR("no valid analog output settings\n"); |
| r = -EINVAL; |
| goto exit; |
| |
| } |
| if ((binfo->idx == SDVENC) && |
| (afmt == VPS_DC_A_OUTPUT_COMPONENT)) { |
| VPSSERR("component out not supported on sdvenc\n"); |
| r = -EINVAL; |
| goto exit; |
| |
| } |
| if (afmt != VPS_DC_A_OUTPUT_MAX) |
| oinfo.afmt = afmt; |
| } |
| |
| if (fmt != FVID2_DF_MAX) |
| oinfo.dataformat = fmt; |
| r = dc_set_output(&oinfo); |
| if (!r) |
| r = size; |
| exit: |
| dc_unlock(binfo->dctrl); |
| return r; |
| |
| } |
| |
| static ssize_t blender_order_show(struct dc_blender_info *binfo, char *buf) |
| { |
| int r; |
| int l; |
| struct vps_dccomprtconfig comprtcfg; |
| |
| r = get_bid_from_idx(binfo->idx, &comprtcfg.nodeid); |
| if (r) |
| return r; |
| |
| dc_lock(binfo->dctrl); |
| r = dc_get_comp_rtconfig(binfo->dctrl, &comprtcfg); |
| dc_unlock(binfo->dctrl); |
| |
| if (r) |
| return r; |
| if (comprtcfg.isglobalreorderenable) |
| l = snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u\n", |
| comprtcfg.isglobalreorderenable, |
| comprtcfg.displayorder[0], |
| comprtcfg.displayorder[1], |
| comprtcfg.displayorder[2], |
| comprtcfg.displayorder[3]); |
| else |
| l = snprintf(buf, PAGE_SIZE, "%u,%u\n", |
| comprtcfg.isglobalreorderenable, |
| comprtcfg.displayorder[0]); |
| return l; |
| } |
| |
| static ssize_t blender_order_store(struct dc_blender_info *binfo, |
| const char *buf, |
| size_t size) |
| { |
| int r; |
| int num; |
| struct vps_dccomprtconfig comprtcfg; |
| |
| r = get_bid_from_idx(binfo->idx, &comprtcfg.nodeid); |
| if (r) |
| return r; |
| |
| num = sscanf(buf, "%u,%u/%u/%u/%u", |
| &comprtcfg.isglobalreorderenable, |
| &comprtcfg.displayorder[0], |
| &comprtcfg.displayorder[1], |
| &comprtcfg.displayorder[2], |
| &comprtcfg.displayorder[3]); |
| /*error check*/ |
| if (comprtcfg.isglobalreorderenable == 1) { |
| if (num != 5) { |
| VPSSERR("Wrong display re-order format\n"); |
| return -EINVAL; |
| } |
| |
| } else { |
| if (num < 2) { |
| VPSSERR("Wrong display re-order format\n"); |
| return -EINVAL; |
| } |
| } |
| |
| dc_lock(binfo->dctrl); |
| r = dc_set_comp_rtconfig(binfo->dctrl, &comprtcfg); |
| if (!r) |
| r = size; |
| dc_unlock(binfo->dctrl); |
| return r; |
| } |
| |
| static ssize_t blender_name_show(struct dc_blender_info *binfo, char *buf) |
| { |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%s\n", venc_name[binfo->idx].name); |
| } |
| |
| struct blender_attribute { |
| struct attribute attr; |
| ssize_t (*show)(struct dc_blender_info *, char *); |
| ssize_t (*store)(struct dc_blender_info *, const char *, size_t); |
| }; |
| |
| |
| #define BLENDER_ATTR(_name, _mode, _show, _store) \ |
| struct blender_attribute blender_attr_##_name = \ |
| __ATTR(_name, _mode, _show, _store) |
| |
| static BLENDER_ATTR(name, S_IRUGO, blender_name_show, NULL); |
| static BLENDER_ATTR(mode, S_IRUGO | S_IWUSR, |
| blender_mode_show, blender_mode_store); |
| static BLENDER_ATTR(timings, S_IRUGO | S_IWUSR, |
| blender_timings_show, blender_timings_store); |
| static BLENDER_ATTR(enabled, S_IRUGO | S_IWUSR, |
| blender_enabled_show, blender_enabled_store); |
| static BLENDER_ATTR(output, S_IRUGO | S_IWUSR, |
| blender_output_show, blender_output_store); |
| static BLENDER_ATTR(clksrc, S_IRUGO | S_IWUSR, |
| blender_clksrc_show, blender_clksrc_store); |
| static BLENDER_ATTR(order, S_IRUGO | S_IWUSR, |
| blender_order_show, blender_order_store); |
| |
| static struct attribute *blender_sysfs_attrs[] = { |
| &blender_attr_mode.attr, |
| &blender_attr_timings.attr, |
| &blender_attr_enabled.attr, |
| &blender_attr_output.attr, |
| &blender_attr_clksrc.attr, |
| &blender_attr_order.attr, |
| &blender_attr_name.attr, |
| NULL |
| }; |
| |
| static ssize_t blender_attr_show(struct kobject *kobj, |
| struct attribute *attr, |
| char *buf) |
| { |
| struct dc_blender_info *binfo = NULL; |
| struct blender_attribute *blend_attr = NULL; |
| |
| binfo = container_of(kobj, struct dc_blender_info, kobj); |
| |
| blend_attr = container_of(attr, struct blender_attribute, attr); |
| if (!blend_attr->show) |
| return -ENOENT; |
| |
| return blend_attr->show(binfo, buf); |
| } |
| |
| static ssize_t blender_attr_store(struct kobject *kobj, |
| struct attribute *attr, |
| const char *buf, |
| size_t size) |
| { |
| struct dc_blender_info *blend; |
| struct blender_attribute *blend_attr; |
| |
| blend = container_of(kobj, struct dc_blender_info, kobj); |
| blend_attr = container_of(attr, struct blender_attribute, attr); |
| |
| if (!blend_attr->store) |
| return -ENOENT; |
| |
| return blend_attr->store(blend, buf, size); |
| } |
| |
| static const struct sysfs_ops blender_sysfs_ops = { |
| .show = blender_attr_show, |
| .store = blender_attr_store, |
| }; |
| |
| static struct kobj_type blender_ktype = { |
| .sysfs_ops = &blender_sysfs_ops, |
| .default_attrs = blender_sysfs_attrs, |
| }; |
| |
| |
| |
| /*sysfs for the display controller*/ |
| |
| static ssize_t dctrl_pllclks_show(struct vps_dispctrl *dctrl, char *buf) |
| { |
| int r = 0, l = 0, i; |
| struct vps_systemvpllclk pllclk; |
| |
| for (i = 0; i < VPS_SYSTEM_VPLL_OUTPUT_MAX_VENC; i++) { |
| pllclk.outputvenc = (enum vps_vplloutputclk)i; |
| /*no need for APLL for TI814X*/ |
| if ((pllclk.outputvenc == VPS_SYSTEM_VPLL_OUTPUT_VENC_A) && |
| cpu_is_ti814x()) |
| continue; |
| r = vps_system_getpll(&pllclk); |
| if (r) |
| return -EINVAL; |
| if (i == 0) |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, |
| "%s:%d", |
| pllvenc_name[i].name, |
| pllclk.outputclk); |
| else |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, |
| ",%s:%d", |
| pllvenc_name[i].name, |
| pllclk.outputclk); |
| |
| } |
| l += snprintf(buf + l, |
| PAGE_SIZE - l, |
| "\n"); |
| |
| return l; |
| } |
| |
| static ssize_t dctrl_pllclks_store(struct vps_dispctrl *dctrl, |
| const char *buf, |
| size_t size) |
| { |
| struct vps_systemvpllclk pllclk; |
| char *input = (char *)buf, *this_opt; |
| int r = 0; |
| if (dctrl->automode) { |
| VPSSERR("please turn off automode first\n"); |
| return -EINVAL; |
| } |
| |
| dc_lock(dctrl); |
| while (!r && (this_opt = strsep(&input, ",")) != NULL) { |
| char *p, *venc_str, *clk_str; |
| int i; |
| p = strchr(this_opt, ':'); |
| if (!p) |
| break; |
| |
| *p = 0; |
| venc_str = this_opt; |
| clk_str = p + 1; |
| pllclk.outputvenc = VPS_SYSTEM_VPLL_OUTPUT_MAX_VENC; |
| pllclk.outputclk = 0xFFFFFFFF; |
| /*get the output venc*/ |
| for (i = 0; i < VPS_SYSTEM_VPLL_OUTPUT_MAX_VENC; i++) { |
| if (sysfs_streq(venc_str, pllvenc_name[i].name)) { |
| pllclk.outputvenc = pllvenc_name[i].value; |
| break; |
| } |
| } |
| |
| if (i == VPS_SYSTEM_VPLL_OUTPUT_MAX_VENC) { |
| VPSSERR("wrong venc %s\n", venc_str); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| if ((pllclk.outputvenc == VPS_SYSTEM_VPLL_OUTPUT_VENC_A) && |
| cpu_is_ti814x()) { |
| VPSSERR("Invalid VENCA PLL\n"); |
| r = -EINVAL; |
| goto exit; |
| |
| } |
| /*get the pll clk*/ |
| pllclk.outputclk = simple_strtoul((const char *)clk_str, |
| NULL, |
| 10); |
| |
| r = vps_system_setpll(&pllclk); |
| if (r) |
| VPSSERR("set freq %s for %s failed\n", |
| clk_str, venc_str); |
| |
| if (input == NULL) |
| break; |
| } |
| if (!r) |
| r = size; |
| exit: |
| dc_unlock(dctrl); |
| return r; |
| } |
| |
| static ssize_t dctrl_automode_show(struct vps_dispctrl *dctrl, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%d\n", dctrl->automode); |
| } |
| |
| static ssize_t dctrl_automode_store(struct vps_dispctrl *dctrl, |
| const char *buf, |
| size_t size) |
| { |
| int enabled; |
| enabled = simple_strtoul(buf, NULL, 10); |
| |
| dctrl->automode = (bool)enabled; |
| return size; |
| } |
| |
| static ssize_t dctrl_tiedvencs_show(struct vps_dispctrl *dctrl, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%d\n", disp_ctrl->tiedvenc); |
| } |
| |
| static ssize_t dctrl_tiedvencs_store(struct vps_dispctrl *dctrl, |
| const char *buf, |
| size_t size) |
| { |
| int r = 0; |
| int vencs = 0; |
| int i = 0; |
| struct vps_dcvencinfo vinfo; |
| dc_lock(disp_ctrl); |
| vencs = simple_strtoul(buf, NULL, 10); |
| if (vencs & ~disp_ctrl->vencmask) { |
| r = -EINVAL; |
| VPSSERR("vencs %d over limit\n", vencs); |
| goto exit; |
| } |
| |
| if ((vencs == 0) || (disp_ctrl->tiedvenc == vencs)) { |
| r = size; |
| goto exit; |
| } |
| |
| vinfo.numvencs = 0; |
| vinfo.tiedvencs = vencs; |
| |
| /*assemble the structure based on the venc id*/ |
| while (vencs >> i) { |
| /*get id of each venc to be tied*/ |
| if ((vencs >> i++) & 1) { |
| int idx; |
| int vid = 1 << (i - 1); |
| get_idx_from_vid(vid, &idx); |
| memcpy(&vinfo.modeinfo[vinfo.numvencs++], |
| &venc_info.modeinfo[idx], |
| sizeof(struct vps_dcmodeinfo)); |
| |
| } |
| } |
| if (vinfo.numvencs < 2) { |
| VPSSERR("at least 2 vencs to tied.\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| /*set the tied venc mode*/ |
| r = dc_set_vencmode(&vinfo); |
| if (r) { |
| VPSSERR("failed to set tied venc\n"); |
| r = -EINVAL; |
| goto exit; |
| } |
| disp_ctrl->tiedvenc = vinfo.tiedvencs; |
| r = size; |
| exit: |
| dc_unlock(disp_ctrl); |
| return r; |
| } |
| |
| struct dctrl_attribute { |
| struct attribute attr; |
| ssize_t (*show)(struct vps_dispctrl *, char *); |
| ssize_t (*store)(struct vps_dispctrl *, const char *, size_t); |
| }; |
| |
| #define DCTRL_ATTR(_name, _mode, _show, _store) \ |
| struct dctrl_attribute dctrl_attr_##_name = \ |
| __ATTR(_name, _mode, _show, _store) |
| |
| static DCTRL_ATTR(tiedvencs, S_IRUGO | S_IWUSR, |
| dctrl_tiedvencs_show, dctrl_tiedvencs_store); |
| static DCTRL_ATTR(pllclks, S_IRUGO | S_IWUSR, |
| dctrl_pllclks_show, dctrl_pllclks_store); |
| static DCTRL_ATTR(automode, S_IRUGO | S_IWUSR, |
| dctrl_automode_show, dctrl_automode_store); |
| |
| static struct attribute *dctrl_sysfs_attrs[] = { |
| &dctrl_attr_tiedvencs.attr, |
| &dctrl_attr_pllclks.attr, |
| &dctrl_attr_automode.attr, |
| NULL |
| }; |
| |
| static ssize_t dctrl_attr_show(struct kobject *kobj, |
| struct attribute *attr, |
| char *buf) |
| { |
| struct vps_dispctrl *dctrl = NULL; |
| struct dctrl_attribute *dctrl_attr = NULL; |
| |
| dctrl = container_of(kobj, struct vps_dispctrl, kobj); |
| |
| dctrl_attr = container_of(attr, struct dctrl_attribute, attr); |
| if (!dctrl_attr->show) |
| return -ENOENT; |
| |
| return dctrl_attr->show(dctrl, buf); |
| } |
| |
| static ssize_t dctrl_attr_store(struct kobject *kobj, |
| struct attribute *attr, |
| const char *buf, |
| size_t size) |
| { |
| struct vps_dispctrl *dctrl; |
| struct dctrl_attribute *dctrl_attr; |
| |
| dctrl = container_of(kobj, struct vps_dispctrl, kobj); |
| dctrl_attr = container_of(attr, struct dctrl_attribute, attr); |
| |
| if (!dctrl_attr->store) |
| return -ENOENT; |
| |
| return dctrl_attr->store(dctrl, buf, size); |
| } |
| |
| |
| static const struct sysfs_ops dctrl_sysfs_ops = { |
| .show = dctrl_attr_show, |
| .store = dctrl_attr_store, |
| }; |
| |
| static struct kobj_type dctrl_ktype = { |
| .sysfs_ops = &dctrl_sysfs_ops, |
| .default_attrs = dctrl_sysfs_attrs, |
| }; |
| |
| /*end of sysfs function for display controller*/ |
| static int parse_def_clksrc(const char *clksrc) |
| { |
| int r = 0, i; |
| char *str, *options, *this_opt; |
| |
| if (clksrc == NULL) |
| return 0; |
| |
| str = kmalloc(strlen(clksrc) + 1, GFP_KERNEL); |
| strcpy(str, clksrc); |
| options = str; |
| VPSSDBG("clksrc %s\n", clksrc); |
| while (!r && (this_opt = strsep(&options, ",")) != NULL) { |
| char *p, *venc, *csrc; |
| int vid, idx; |
| struct dc_blender_info *binfo; |
| |
| p = strchr(this_opt, ':'); |
| if (!p) |
| break; |
| |
| *p = 0; |
| venc = this_opt; |
| csrc = p + 1; |
| /*parse the clock source for each possible venc input*/ |
| for (i = 0; i < ARRAY_SIZE(vclksrc_name); i++) { |
| if (sysfs_streq(csrc, vclksrc_name[i].name)) { |
| if (dc_get_vencid(venc, &vid)) { |
| VPSSERR("wrong venc\n"); |
| break; |
| } |
| /* no clock for SD VENC*/ |
| if (vid == VPS_DC_VENC_SD) |
| break; |
| |
| get_idx_from_vid(vid, &idx); |
| binfo = &disp_ctrl->blenders[idx]; |
| /*is valid clock source*/ |
| if (isvalidclksrc(vid, |
| vclksrc_name[i].value)) { |
| binfo->clksrc.clksrc = |
| vclksrc_name[i].value; |
| |
| } else |
| VPSSERR("wrong clock source\n"); |
| |
| break; |
| |
| } |
| } |
| if (i == ARRAY_SIZE(vclksrc_name)) |
| VPSSERR("wrong clock source\n"); |
| |
| if (options == NULL) |
| break; |
| |
| |
| } |
| kfree(str); |
| return r; |
| } |
| |
| static int parse_def_modes(const char *mode) |
| |
| { |
| char *str, *options, *this_opt; |
| int r = 0; |
| struct vps_dcvencinfo *vinfo = &venc_info; |
| if (mode == NULL) |
| return 0; |
| |
| str = kmalloc(strlen(mode) + 1, GFP_KERNEL); |
| strcpy(str, mode); |
| options = str; |
| VPSSDBG("mode %s\n", mode); |
| |
| while (!r && (this_opt = strsep(&options, ",")) != NULL) { |
| char *p, *display_str, *mode_str; |
| int vid, mid; |
| int idx; |
| p = strchr(this_opt, ':'); |
| if (!p) { |
| r = -EINVAL; |
| break; |
| } |
| *p = 0; |
| display_str = this_opt; |
| mode_str = p + 1; |
| |
| if (dc_get_vencid(display_str, &vid)) { |
| VPSSERR("venc name(%s) not existing.\n", |
| display_str); |
| continue; |
| } |
| if (dc_get_modeid(mode_str, &mid)) { |
| VPSSERR("venc mode(%s) is not supported.\n", |
| mode_str); |
| continue; |
| } |
| |
| if (!isvalidmode(vid, mid)) |
| continue; |
| |
| get_idx_from_vid(vid, &idx); |
| vinfo->modeinfo[idx].vencid = vid; |
| vinfo->modeinfo[idx].minfo.standard = mid; |
| dc_get_timing(mid, &vinfo->modeinfo[idx].minfo); |
| |
| if (options == NULL) |
| break; |
| } |
| |
| kfree(str); |
| |
| return r; |
| |
| } |
| void __init vps_dc_ctrl_init(struct vps_dispctrl *dctrl) |
| { |
| struct vps_dcedeconfig *edecfg = dctrl->dcedecfg; |
| |
| dctrl->dccreatecfg->edeconfig = |
| (struct vps_dcedeconfig *)dctrl->dcede_phy; |
| /*setup default ede values*/ |
| edecfg->ltienable = 0; |
| edecfg->horzpeaking = 0; |
| edecfg->ctienable = 0; |
| edecfg->transadjustenable = 0; |
| edecfg->lumapeaking = 0; |
| edecfg->chromapeaking = 0; |
| edecfg->minclipluma = 0; |
| edecfg->maxclipluma = 1023; |
| edecfg->minclipchroma = 0; |
| edecfg->maxclipchroma = 1023; |
| edecfg->bypass = 0; |
| |
| } |
| static inline int get_payload_size(void) |
| { |
| int size = 0; |
| size = sizeof(struct vps_dccreateconfig); |
| size += sizeof(struct vps_dcedeconfig); |
| size += sizeof(u32); |
| size += sizeof(struct vps_dcconfig); |
| size += sizeof(struct vps_dcvencinfo); |
| size += sizeof(struct vps_dcnodeinput); |
| size += sizeof(struct vps_dcmodeinfo); |
| size += sizeof(struct vps_dcoutputinfo); |
| size += sizeof(struct vps_dcvencclksrc); |
| size += sizeof(struct vps_dccigrtconfig); |
| size += sizeof(struct vps_dcenumnodeinput); |
| size += sizeof(struct vps_dccomprtconfig); |
| size += sizeof(u32); /*this is for the disable venc command*/ |
| /*FIXME add more here*/ |
| |
| return size; |
| } |
| |
| static inline void assign_payload_addr(struct vps_dispctrl *dctrl, |
| struct vps_payload_info *pinfo, |
| u32 *buf_offset) |
| { |
| int offset = *buf_offset; |
| |
| /*dc create config*/ |
| dctrl->dccreatecfg = (struct vps_dccreateconfig *)setaddr(pinfo, |
| &offset, |
| &dctrl->dccreate_phy, |
| sizeof(struct vps_dccreateconfig)); |
| |
| /*ede config*/ |
| dctrl->dcedecfg = (struct vps_dcedeconfig *)setaddr(pinfo, |
| &offset, |
| &dctrl->dcede_phy, |
| sizeof(struct vps_dcedeconfig)); |
| /*return status*/ |
| dctrl->dcrtstatus = (u32 *)setaddr(pinfo, |
| &offset, |
| &dctrl->dcrtst_phy, |
| sizeof(u32)); |
| /*dc config */ |
| dctrl->dccfg = (struct vps_dcconfig *)setaddr(pinfo, |
| &offset, |
| &dctrl->dccfg_phy, |
| sizeof(struct vps_dcconfig)); |
| |
| /* venc info*/ |
| dctrl->vinfo = (struct vps_dcvencinfo *)setaddr(pinfo, |
| &offset, |
| &dctrl->vinfo_phy, |
| sizeof(struct vps_dcvencinfo)); |
| |
| /*node input*/ |
| dctrl->nodeinfo = (struct vps_dcnodeinput *)setaddr( |
| pinfo, |
| &offset, |
| &dctrl->ninfo_phy, |
| sizeof(struct vps_dcnodeinput)); |
| /*venc disable*/ |
| dctrl->dis_vencs = (u32 *)setaddr(pinfo, |
| &offset, |
| &dctrl->dis_vencsphy, |
| sizeof(u32)); |
| |
| /*venc output infor*/ |
| dctrl->opinfo = (struct vps_dcoutputinfo *)setaddr( |
| pinfo, |
| &offset, |
| &dctrl->opinfo_phy, |
| sizeof(struct vps_dcoutputinfo)); |
| |
| /*venc clock source*/ |
| dctrl->clksrc = (struct vps_dcvencclksrc *)setaddr( |
| pinfo, |
| &offset, |
| &dctrl->clksrc_phy, |
| sizeof(struct vps_dcvencclksrc)); |
| |
| /*CIG runtime configuration*/ |
| dctrl->cigcfg = (struct vps_dccigrtconfig *)setaddr( |
| pinfo, |
| &offset, |
| &dctrl->cigcfg_phy, |
| sizeof(struct vps_dccigrtconfig)); |
| /*DC enum node input*/ |
| dctrl->dceninput = (struct vps_dcenumnodeinput *)setaddr( |
| pinfo, |
| &offset, |
| &dctrl->dceninput_phy, |
| sizeof(struct vps_dcenumnodeinput)); |
| /*COMP RT Config*/ |
| dctrl->comprtcfg = (struct vps_dccomprtconfig *) setaddr( |
| pinfo, |
| &offset, |
| &dctrl->comprtcfg_phy, |
| sizeof(struct vps_dccomprtconfig)); |
| |
| *buf_offset = offset; |
| } |
| |
| int __init vps_dc_init(struct platform_device *pdev, |
| const char *mode, |
| int tied_vencs, |
| const char *clksrc) |
| { |
| int r = 0; |
| int i; |
| int size = 0, offset = 0; |
| |
| VPSSDBG("dctrl init\n"); |
| |
| dc_payload_info = kzalloc(sizeof(struct vps_payload_info), |
| GFP_KERNEL); |
| |
| if (!dc_payload_info) { |
| VPSSERR("allocated payload info failed.\n"); |
| return -ENOMEM; |
| } |
| |
| /*allocate non-cacheable memory*/ |
| size = get_payload_size(); |
| dc_payload_info->vaddr = vps_sbuf_alloc(size, &dc_payload_info->paddr); |
| if (dc_payload_info->vaddr == NULL) { |
| VPSSERR("alloc dctrl dma buffer failed\n"); |
| dc_payload_info->paddr = 0u; |
| r = -ENOMEM; |
| goto cleanup; |
| } |
| dc_payload_info->size = PAGE_ALIGN(size); |
| memset(dc_payload_info->vaddr, 0, dc_payload_info->size); |
| /*allocate display_control memory*/ |
| disp_ctrl = kzalloc(sizeof(struct vps_dispctrl), GFP_KERNEL); |
| if (disp_ctrl == NULL) { |
| r = -ENOMEM; |
| goto cleanup; |
| } |
| disp_ctrl->automode = true; |
| disp_ctrl->numvencs = vps_get_numvencs(); |
| venc_info.numvencs = disp_ctrl->numvencs; |
| disp_ctrl->vencmask = (1 << VPS_DC_MAX_VENC) - 1; |
| if (cpu_is_ti814x()) |
| disp_ctrl->vencmask -= VPS_DC_VENC_HDCOMP; |
| |
| assign_payload_addr(disp_ctrl, dc_payload_info, &offset); |
| |
| vps_dc_ctrl_init(disp_ctrl); |
| /*get dc handle*/ |
| dc_handle = vps_fvid2_create(FVID2_VPS_DCTRL_DRV, |
| VPS_DCTRL_INST_0, |
| (void *)disp_ctrl->dccreate_phy, |
| (void *)dc_payload_info->paddr, |
| NULL); |
| |
| if (dc_handle == NULL) { |
| VPSSDBG("Create FVID2 DC handle status 0x%08x.\n", |
| *(u32 *)dc_payload_info->vaddr); |
| r = -EINVAL; |
| goto cleanup; |
| } |
| |
| disp_ctrl->fvid2_handle = dc_handle; |
| mutex_init(&disp_ctrl->dcmutex); |
| |
| r = kobject_init_and_add( |
| &disp_ctrl->kobj, |
| &dctrl_ktype, |
| &pdev->dev.kobj, |
| "system"); |
| if (r) |
| VPSSERR("failed to create dctrl sysfs file.\n"); |
| |
| /*create sysfs*/ |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| struct dc_blender_info *blend = &disp_ctrl->blenders[i];; |
| |
| blend->idx = i; |
| blend->actnodes = 0; |
| blend->name = (char *)venc_name[i].name; |
| blend->dctrl = disp_ctrl; |
| blend->isdeviceon = true; |
| r = kobject_init_and_add( |
| &blend->kobj, &blender_ktype, |
| &pdev->dev.kobj, "display%d", i); |
| |
| if (r) { |
| VPSSERR("failed to create blender \ |
| %d sysfs file.\n", i); |
| continue; |
| } |
| } |
| |
| disp_ctrl->tiedvenc = tied_vencs; |
| venc_info.tiedvencs = disp_ctrl->tiedvenc; |
| |
| |
| /*parse the mode*/ |
| r = parse_def_modes(mode); |
| if (r) { |
| VPSSERR("failed to parse mode.\n"); |
| goto cleanup; |
| } |
| |
| /*set up the default clksrc and output format*/ |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| struct vps_dcvencclksrc *clksrcp = |
| &disp_ctrl->blenders[i].clksrc; |
| struct vps_dcoutputinfo opinfo; |
| |
| clksrcp->venc = venc_name[i].vid; |
| /*set the venc output*/ |
| opinfo.dvofidpolarity = VPS_DC_POLARITY_ACT_HIGH; |
| opinfo.dvohspolarity = VPS_DC_POLARITY_ACT_HIGH; |
| opinfo.dvovspolarity = VPS_DC_POLARITY_ACT_HIGH; |
| opinfo.dvoactvidpolarity = VPS_DC_POLARITY_ACT_HIGH; |
| |
| switch (i) { |
| case HDMI: |
| opinfo.vencnodenum = VPS_DC_VENC_HDMI; |
| opinfo.dvofmt = VPS_DC_DVOFMT_TRIPLECHAN_DISCSYNC; |
| opinfo.dataformat = FVID2_DF_RGB24_888; |
| if (cpu_is_ti816x() && (VPS_PLATFORM_CPU_REV_1_0 == |
| vps_system_getcpurev())) |
| clksrcp->clksrc = VPS_DC_CLKSRC_VENCD_DIV2; |
| else |
| clksrcp->clksrc = VPS_DC_CLKSRC_VENCD; |
| break; |
| |
| case DVO2: |
| opinfo.vencnodenum = VPS_DC_VENC_DVO2; |
| opinfo.dvofmt = VPS_DC_DVOFMT_DOUBLECHAN; |
| opinfo.dataformat = FVID2_DF_YUV422SP_UV; |
| |
| |
| if (cpu_is_ti816x()) { |
| if (VPS_PLATFORM_CPU_REV_1_0 == |
| vps_system_getcpurev()) |
| clksrcp->clksrc = |
| VPS_DC_CLKSRC_VENCD_DIV2; |
| else |
| clksrcp->clksrc = VPS_DC_CLKSRC_VENCD; |
| } else |
| clksrcp->clksrc = VPS_DC_CLKSRC_VENCA; |
| |
| break; |
| case SDVENC: |
| opinfo.vencnodenum = VPS_DC_VENC_SD; |
| if (cpu_is_ti816x()) |
| opinfo.afmt = VPS_DC_A_OUTPUT_COMPOSITE; |
| else |
| opinfo.afmt = VPS_DC_A_OUTPUT_SVIDEO; |
| opinfo.dataformat = FVID2_DF_RGB24_888; |
| break; |
| if (cpu_is_ti816x()) { |
| case HDCOMP: |
| opinfo.vencnodenum = VPS_DC_VENC_HDCOMP; |
| opinfo.afmt = VPS_DC_A_OUTPUT_COMPONENT; |
| opinfo.dataformat = FVID2_DF_YUV422SP_UV; |
| |
| clksrcp->clksrc = VPS_DC_CLKSRC_VENCA; |
| break; |
| } |
| |
| } |
| r = dc_set_output(&opinfo); |
| if (r) { |
| VPSSERR("failed to set venc output\n"); |
| goto cleanup; |
| } |
| |
| } |
| /*parse command line clksrc*/ |
| r = parse_def_clksrc(clksrc); |
| if (r) { |
| VPSSERR("failed to parse clock source\n"); |
| goto cleanup; |
| } |
| |
| /*set the clock source*/ |
| for (i = 0; i < venc_info.numvencs; i++) { |
| if (disp_ctrl->blenders[i].idx != SDVENC) { |
| r = dc_set_clksrc( |
| &disp_ctrl->blenders[i].clksrc); |
| |
| if (r) { |
| VPSSERR("failed to set clock resource"); |
| goto cleanup; |
| } |
| } |
| } |
| /*config the PLL*/ |
| for (i = 0; i < venc_info.numvencs; i++) { |
| r = dc_set_pll_by_mid(i, venc_info.modeinfo[i].minfo.standard); |
| if (r) { |
| VPSSERR("failed to set pll"); |
| goto cleanup; |
| } |
| } |
| |
| /*set the venc mode*/ |
| r = dc_set_vencmode(&venc_info); |
| if (r) { |
| VPSSERR("Failed to set venc mode.\n"); |
| goto cleanup; |
| } |
| /*set the the THS filter, device is still registered even |
| if setup is failed*/ |
| #ifdef CONFIG_ARCH_TI816X |
| if (cpu_is_ti816x()) { |
| r = pcf8575_ths7375_enable(TI816X_THSFILTER_ENABLE_MODULE); |
| if ((venc_info.modeinfo[HDCOMP].minfo.standard == |
| FVID2_STD_1080P_60) || |
| (venc_info.modeinfo[HDCOMP].minfo.standard == |
| FVID2_STD_1080P_50)) |
| r |= pcf8575_ths7360_hd_enable( |
| TI816X_THS7360_SF_TRUE_HD_MODE); |
| else |
| r |= pcf8575_ths7360_hd_enable( |
| TI816X_THS7360_SF_HD_MODE); |
| if (r < 0) { |
| VPSSERR("setup 7375 filter failed\n"); |
| disp_ctrl->blenders[HDCOMP].isdeviceon = false; |
| } |
| r = pcf8575_ths7360_sd_enable(TI816X_THSFILTER_ENABLE_MODULE); |
| |
| if (r < 0) { |
| VPSSERR("setup 7360 filter failed.\n"); |
| disp_ctrl->blenders[SDVENC].isdeviceon = false; |
| |
| } |
| } |
| #endif |
| return 0; |
| cleanup: |
| vps_dc_deinit(pdev); |
| return r; |
| } |
| |
| |
| int __exit vps_dc_deinit(struct platform_device *pdev) |
| { |
| int r = 0; |
| int i; |
| VPSSDBG("dctrl deinit\n"); |
| |
| if (disp_ctrl) { |
| /*disable vencs*/ |
| if (disp_ctrl->enabled_venc_ids != 0) { |
| r = dc_venc_disable(disp_ctrl->vencmask); |
| if (r) { |
| VPSSERR("Failed to disable vencs.\n"); |
| return r; |
| } |
| } |
| |
| kobject_del(&disp_ctrl->kobj); |
| kobject_put(&disp_ctrl->kobj); |
| |
| for (i = 0; i < disp_ctrl->numvencs; i++) { |
| kobject_del(&disp_ctrl->blenders[i].kobj); |
| kobject_put(&disp_ctrl->blenders[i].kobj); |
| } |
| |
| kfree(disp_ctrl); |
| disp_ctrl = NULL; |
| } |
| |
| if (dc_payload_info) { |
| |
| /*free memory*/ |
| if (dc_payload_info->vaddr) |
| vps_sbuf_free(dc_payload_info->paddr, |
| dc_payload_info->vaddr, |
| dc_payload_info->size); |
| |
| kfree(dc_payload_info); |
| dc_payload_info = NULL; |
| } |
| |
| if (dc_handle) { |
| r = vps_fvid2_delete(dc_handle, NULL); |
| if (r) { |
| VPSSERR("failed to delete DC fvid2 handle.\n"); |
| return r; |
| } |
| dc_handle = NULL; |
| } |
| |
| |
| return r; |
| } |
| |