blob: d032a47bd601bcf0ac67d3d126148ec2fe87b75f [file] [log] [blame] [edit]
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Shirish S <s.shirish@samsung.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <linux/vt.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/videodev2.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xv.h>
/* all driver need this */
#include "xf86.h"
#include "xf86_OSproc.h"
#include "mipointer.h"
#include "mibstore.h"
#include "micmap.h"
#include "colormapst.h"
#include "xf86cmap.h"
#include "xorg-server.h"
/* for visuals */
#include "fb.h"
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6
#include "xf86Resources.h"
#include "xf86RAC.h"
#endif
#include "xf86Crtc.h"
#include "xf86xv.h"
#include "fourcc.h"
#include "exynos5_hdmi.h"
#include "video.h"
#include "omap_driver.h"
/* Private data for TV output */
typedef struct _EXYNOS5HDMIOutputPrivateRec {
I2CBusPtr pI2CBus;
Bool hdtv_connected;
Bool hdtv_720x576_supported;
Bool hdtv_720x480_supported;
int hdtv_connector_type; /* HDMI/DVI */
v4l2_std_id mode_720x576;
v4l2_std_id mode_720x480;
int modeHDisplay;
int modeVDisplay;
} EXYNOS5HDMIOutputPrivateRec, *EXYNOS5HDMIOutputPrivatePtr;
/* Accepted resolutions for TV */
typedef struct _tv_input_res {
int HDisplay;
int VDisplay;
float VRefresh;
int Flags;
int res;
v4l2_std_id std;
int priority;
} tv_input_res;
static tv_input_res tv_input_res_table[] = {
{1920, 1080, 30.0, 0, v1920x1080p_30Hz, V4L2_STD_1080P_30, 0},
{1920, 1080, 60.0, 0, v1920x1080p_60Hz, V4L2_STD_1080P_60, 1},
{1920, 1080, 50.0, 0, v1920x1080p_50Hz, V4L2_STD_1080P_50, 2},
{1280, 720, 60.0, 0, v1280x720p_60Hz, V4L2_STD_720P_60, 3},
{1280, 720, 50.0, 0, v1280x720p_50Hz, V4L2_STD_720P_50, 4},
{720, 576, 50.0, 0, v720x576p_50Hz, V4L2_STD_576P_50_4_3, 5},
{720, 480, 60.0, 0, v720x480p_60Hz, V4L2_STD_480P_60_4_3, 6},
/*{1920, 1080, 60.0, V_INTERLACE, v1920x1080i_60Hz, V4L2_STD_1080I_60, 7},
{1920, 1080, 50.0, V_INTERLACE, v1920x1080i_50Hz, V4L2_STD_1080I_50, 8},*/
};
typedef unsigned short u16;
typedef struct _dv_presets {
u16 width;
u16 height;
const char *name;
} dv_presets;
#ifdef EXYNOS_EDID /* FUTURE REQUIREMENT FOR EDID */
static dv_presets dv_presets_table [] = {
{ 0, 0, "Invalid" }, /* V4L2_DV_INVALID */
{ 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */
{ 720, 576, "576p@50" }, /* V4L2_DV_576P50 */
{ 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */
{ 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */
{ 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */
{ 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */
{ 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */
{ 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */
{ 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */
{ 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */
{ 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */
{ 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */
{ 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */
{ 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */
{ 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */
{ 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */
{ 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */
{ 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */
};
#endif
#define TV_INPUT_RES_TABLE_SIZE (sizeof(tv_input_res_table)/sizeof(tv_input_res_table[0]))
/* Output property resources */
/* PAL modes */
#define PAL_MODE_NAME "pal_mode"
#define NUM_PAL_MODES 6
const char *pal_mode_names[] = {
"PAL-M",
"PAL-N",
"PAL-Nc",
"PAL-60",
"PAL-BDGHI",
"DIGITAL",
};
static Atom pal_mode_atom;
static Atom pal_mode_name_atoms[NUM_PAL_MODES];
static Bool isstreaming;
/* NTSC modes */
#define NTSC_MODE_NAME "ntsc_mode"
#define NUM_NTSC_MODES 3
const char *ntsc_mode_names[] = {
"NTSC-M",
"NTSC-443",
"DIGITAL",
};
static Atom ntsc_mode_atom;
static Atom ntsc_mode_name_atoms[NUM_NTSC_MODES];
#define BUFFER_CNT 2
struct buffer {
int index;
void *data;
size_t size;
size_t width;
size_t height;
/* buffer state */
double t;
};
struct setup {
char *path;
int width;
int height;
int x;
int y;
};
struct crop_info {
int full_width;
int full_height;
int width;
int height;
int xoffset;
int yoffset;
};
struct context {
int fd;
struct buffer *buffer;
struct v4l2_plane planes;
size_t buffer_cnt;
};
int exynos5_init_hdmi(int fp_tvout, v4l2_std_id std_id, int output_type, int bufFD)
{
int ret;
struct v4l2_buffer buf;
struct v4l2_plane plane;
struct v4l2_format fmt;
/* allocate buffers */
struct v4l2_requestbuffers rqbufs;
int type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
if(isstreaming)
return 0;
/* configure desired image size */
fmt.type = type;
fmt.fmt.pix_mp.width = TV_WIDTH;
fmt.fmt.pix_mp.height = TV_HEIGHT;
fmt.fmt.pix_mp.num_planes = 1;
/* format is hardcoded: draw procedures work only in 32-bit mode */
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_BGR32;
ret = ioctl(fp_tvout, VIDIOC_S_FMT, &fmt);
if(ret < 0) {
xf86Msg(X_ERROR," %s: VIDIOC_S_FMT failed\n", __func__);
return ret;
}
/* update format struct to values adjusted by a driver */
ret = ioctl(fp_tvout, VIDIOC_G_FMT, &fmt);
if(ret < 0) {
xf86Msg(X_ERROR," %s: VIDIOC_G_FMT failed\n", __func__);
return ret;
}
rqbufs.count = BUFFER_CNT;
rqbufs.type = type;
rqbufs.memory = V4L2_MEMORY_DMABUF;
ret = ioctl(fp_tvout, VIDIOC_REQBUFS, &rqbufs);
if((ret < 0) || (rqbufs.count != BUFFER_CNT)){
xf86Msg(X_ERROR," %s: VIDIOC_REQBUFS failed becase %s\n", __func__,strerror(-ret));
return ret;
}
memset(&buf, 0, sizeof(buf));
memset(&plane, 0, sizeof(plane));
buf.type = type;
buf.index = 0;
buf.memory = V4L2_MEMORY_DMABUF;
buf.m.planes = &plane;
buf.m.planes[0].m.fd = bufFD;
buf.length = 1;
ret = ioctl(fp_tvout, VIDIOC_QBUF, &buf);
if(ret < 0) {
xf86Msg(X_ERROR," %s: VIDIOC_QBUF failed ret=%s \n", __func__,strerror(-ret));
return ret;
}
/* start streaming */
ret = ioctl(fp_tvout, VIDIOC_STREAMON, &type);
if(ret < 0) {
xf86Msg(X_ERROR," %s: VIDIOC_STREAMON failed ret=%s\n", __func__,strerror(ret));
return ret;
}
isstreaming=1;
return 0;
}
#ifdef EXYNOS_EDID
/* I2C bus routines for DDC */
static void EXYNOS5I2CUDelay(I2CBusPtr b, int usec)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void EXYNOS5I2CPutBits(I2CBusPtr b, int scl, int sda)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void EXYNOS5I2CGetBits(I2CBusPtr b, int *scl, int *sda)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static Bool EXYNOS5I2CStart(I2CBusPtr b, int timeout)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return FALSE;
}
static Bool EXYNOS5I2CAddress(I2CDevPtr d, I2CSlaveAddr addr)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return FALSE;
}
static void EXYNOS5I2CStop(I2CDevPtr d)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static Bool EXYNOS5I2CPutByte(I2CDevPtr d, I2CByte data)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return FALSE;
}
static Bool EXYNOS5I2CGetByte(I2CDevPtr d, I2CByte *data, Bool last)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return FALSE;
}
static int EXYNOS5I2CWriteRead(I2CDevPtr d, I2CByte *WriteBuffer, int nWrite, I2CByte *ReadBuffer, int nRead)
{
int i2c_fd = (int)d->pI2CBus->DriverPrivate.uval;
struct i2c_rdwr_ioctl_data msgset;
struct i2c_msg msgs[2];
int ret;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
msgs[0].addr = d->SlaveAddr>>1;
msgs[0].flags = 0;
msgs[0].len = nWrite;
msgs[0].buf = WriteBuffer;
msgs[1].addr = d->SlaveAddr>>1;
msgs[1].flags = I2C_M_RD;
msgs[1].len = nRead;
msgs[1].buf = ReadBuffer;
msgset.nmsgs = 2;
msgset.msgs = msgs;
ret = ioctl(i2c_fd, I2C_RDWR, &msgset);
if(ret < 0) {
xf86Msg(X_ERROR,"%s: I2C_RDWR failed because %s\n", __func__,strerror(-ret));
return ret;
}
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return 0;
}
/* I2C bus initialization for DDC */
static Bool EXYNOS5I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_fd, char *name)
{
I2CBusPtr pI2CBus;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
pI2CBus = xf86CreateI2CBusRec();
if (!pI2CBus)
return FALSE;
pI2CBus->BusName = name;
pI2CBus->scrnIndex = pScrn->scrnIndex;
pI2CBus->DriverPrivate.uval = (unsigned long)i2c_fd;
pI2CBus->I2CWriteRead = EXYNOS5I2CWriteRead;
if (!xf86I2CBusInit(pI2CBus))
return FALSE;
*bus_ptr = pI2CBus;
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
#endif
/* NTSC/PAL SetProperty helper routines */
/* The mode to index and index to mode is required in different contexts:
* in exynos5_hdmi_output_create_resources and
* exynos5_hdmi_output_set_property.
*/
static int pal_mode_to_index(v4l2_std_id mode)
{
int index;
switch(mode) {
case V4L2_STD_PAL_M:
index = 0;
break;
case V4L2_STD_PAL_N:
index = 1;
break;
case V4L2_STD_PAL_Nc:
index = 2;
break;
case V4L2_STD_PAL_60:
index = 3;
break;
case V4L2_STD_PAL_BDGHI:
index = 4;
break;
case V4L2_STD_576P_50_4_3:
index = 5;
break;
default:
index = 0;
break;
}
return index;
}
static v4l2_std_id pal_index_to_mode(int index)
{
v4l2_std_id mode;
switch(index) {
case 0:
mode = V4L2_STD_PAL_M;
break;
case 1:
mode = V4L2_STD_PAL_N;
break;
case 2:
mode = V4L2_STD_PAL_Nc;
break;
case 3:
mode = V4L2_STD_PAL_60;
break;
case 4:
mode = V4L2_STD_PAL_BDGHI;
break;
case 5:
mode = V4L2_STD_576P_50_4_3;
break;
default:
mode = V4L2_STD_PAL_M;
break;
}
return mode;
}
/* The mode to index and index to mode is required in different contexts:
* in exynos5_hdmi_output_create_resources and
* exynos5_hdmi_output_set_property.
*/
static int ntsc_mode_to_index(v4l2_std_id mode)
{
int index;
switch(mode) {
case V4L2_STD_NTSC_M:
index = 0;
break;
case V4L2_STD_NTSC_443:
index = 1;
break;
case V4L2_STD_480P_60_4_3:
index = 2;
break;
default:
index = 0;
break;
}
return index;
}
static v4l2_std_id ntsc_index_to_mode(int index)
{
v4l2_std_id mode;
switch(index) {
case 0:
mode = V4L2_STD_NTSC_M;
break;
case 1:
mode = V4L2_STD_NTSC_443;
break;
case 2:
mode = V4L2_STD_480P_60_4_3;
break;
default:
mode = V4L2_STD_NTSC_M;
break;
}
return mode;
}
static int pal_mode_lookup(const char *name)
{
int i;
for (i = 0; i < NUM_PAL_MODES; i++)
if (!strcmp(name, pal_mode_names[i]))
return i;
return -1;
}
static int ntsc_mode_lookup(const char *name)
{
int i;
for (i = 0; i < NUM_NTSC_MODES; i++)
if (!strcmp(name, ntsc_mode_names[i]))
return i;
return -1;
}
/* CRTC routines for RandR support of TV */
static void
exynos5_hdmi_crtc_dpms(xf86CrtcPtr crtc, int mode)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
#ifdef EXYNOS_EDID
static void
exynos5_hdmi_crtc_save(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_restore(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
#endif
static Bool
exynos5_hdmi_crtc_lock(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
static void
exynos5_hdmi_crtc_unlock(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static Bool
exynos5_hdmi_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode,
DisplayModePtr adjusted_mode)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
static void
exynos5_hdmi_crtc_prepare(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
/* Sets the viewport coordinates(x,y) for TV in the virtual framebuffer */
static void
exynos5_hdmi_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode,
DisplayModePtr adjusted_mode, int x, int y)
{
OMAPPtr fPtr = OMAPPTR(crtc->scrn);
int ret;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
fPtr->fb_tv_var.xoffset=x;
fPtr->fb_tv_var.yoffset=y;
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, 0, 0,fPtr->dma_fd);
if(ret) {
xf86Msg(X_ERROR, "%s: TV Init failed because %s\n",__func__,strerror(-ret));
return;
}
ret = ioctl(fPtr->fb_tv_fd, FBIOPUT_VSCREENINFO, &fPtr->fb_tv_var);
if(ret < 0) {
xf86Msg(X_ERROR, "%s: FBIOPUT_VSCREENINFO failed %s\n", __func__,strerror(-ret));
return;
}
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_commit(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green,
CARD16 *blue, int size)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
#ifdef EXYNOS_EDID
static void *
exynos5_hdmi_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return NULL;
}
static PixmapPtr
exynos5_hdmi_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return NULL;
}
static void
exynos5_hdmi_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pPixmap, void *data)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_show_cursor(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_hide_cursor(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_load_cursor_image(xf86CrtcPtr crtc, CARD8 *image)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_crtc_destroy(xf86CrtcPtr crtc)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static Bool
exynos5_hdmi_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
Rotation rotation, int x, int y)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
#endif
static void
exynos5_hdmi_crtc_set_origin(xf86CrtcPtr crtc, int x, int y)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static const xf86CrtcFuncsRec exynos5_hdmi_crtc_funcs = {
.dpms = exynos5_hdmi_crtc_dpms,
.lock = exynos5_hdmi_crtc_lock,
.unlock = exynos5_hdmi_crtc_unlock,
.mode_fixup = exynos5_hdmi_crtc_mode_fixup,
.prepare = exynos5_hdmi_crtc_prepare,
.mode_set = exynos5_hdmi_crtc_mode_set,
.commit = exynos5_hdmi_crtc_commit,
.gamma_set = exynos5_hdmi_crtc_gamma_set,
.set_origin = exynos5_hdmi_crtc_set_origin,
};
/* TV output functions for RandR */
/* Creates resouces for NTSC/PAL SetProperty function */
static void
exynos5_hdmi_output_create_resources(xf86OutputPtr output)
{
int err, i;
EXYNOS5HDMIOutputPrivatePtr tv_priv = output->driver_private;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
/* Set up analog TV's PAL mode */
pal_mode_atom = MakeAtom(PAL_MODE_NAME, sizeof(PAL_MODE_NAME) - 1, TRUE);
if(pal_mode_atom == 0) {
xf86Msg(X_ERROR,"%s: MakeAtom failed, %d\n", __func__, (int)pal_mode_atom );
return;
}
for(i = 0; i < NUM_PAL_MODES; i++) {
pal_mode_name_atoms[i] = MakeAtom(pal_mode_names[i], strlen(pal_mode_names[i]), TRUE);
if(pal_mode_name_atoms[i] == 0) {
xf86Msg(X_ERROR,"%s: MakeAtom failed for pal_mode_name_atoms[%d] iteration\n", __func__, i );
return;
}
}
err = RRConfigureOutputProperty(output->randr_output, pal_mode_atom, FALSE, FALSE, FALSE, NUM_PAL_MODES, (INT32 *)pal_mode_name_atoms);
if (err != 0) {
xf86Msg(X_ERROR,"%s: RRConfigureOutputProperty error, %d\n", __func__, err);
return;
}
err = RRChangeOutputProperty(output->randr_output, pal_mode_atom, XA_ATOM, 32, PropModeReplace, 1, &pal_mode_name_atoms[pal_mode_to_index(tv_priv->mode_720x576)], FALSE, TRUE);
if (err != 0) {
xf86Msg(X_ERROR,"%s: RRChangeOutputProperty error, %d\n", __func__, err);
return;
}
/* Set up analog TV's NTSC mode */
ntsc_mode_atom = MakeAtom(NTSC_MODE_NAME, sizeof(NTSC_MODE_NAME) - 1, TRUE);
for(i = 0; i < NUM_NTSC_MODES; i++) {
ntsc_mode_name_atoms[i] = MakeAtom(ntsc_mode_names[i], strlen(ntsc_mode_names[i]), TRUE);
if(ntsc_mode_name_atoms[i] == 0) {
xf86Msg(X_ERROR,"%s: MakeAtom failed for ntsc_mode_name_atoms[%d] iteration\n", __func__,i );
return;
}
}
err = RRConfigureOutputProperty(output->randr_output, ntsc_mode_atom, FALSE, FALSE, FALSE, NUM_NTSC_MODES, (INT32 *)ntsc_mode_name_atoms);
if (err != 0) {
xf86Msg(X_ERROR,"%s: RRConfigureOutputProperty error, %d\n", __func__, err);
return;
}
err = RRChangeOutputProperty(output->randr_output, ntsc_mode_atom, XA_ATOM, 32, PropModeReplace, 1, &ntsc_mode_name_atoms[ntsc_mode_to_index(tv_priv->mode_720x480)], FALSE, TRUE);
if (err != 0)
xf86Msg(X_ERROR,"%s: RRChangeOutputProperty error, %d\n", __func__, err);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
/* Power management function */
static void
exynos5_hdmi_output_dpms(xf86OutputPtr output, int mode)
{
OMAPPtr fPtr = OMAPPTR(output->scrn);
EXYNOS5DBGMSG(X_INFO, "%s: ++++, mode=%d\n", __func__, mode);
/* TODO: The below functionalities need to be properly implemented
* in the device driver, currently they are dummy*/
if(mode == DPMSModeOn) {
if (ioctl(fPtr->fb_tv_fd, FBIOBLANK, FB_BLANK_UNBLANK)) {
xf86Msg(X_ERROR, "%s: failed ioctl FBIOBLANK\n", __func__);
return;
}
}
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_output_save(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_output_restore(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static int
exynos5_hdmi_output_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
pMode->HDisplay=TV_WIDTH;
pMode->VDisplay =TV_HEIGHT;
pMode->VRefresh = xf86ModeVRefresh(pMode);
/* If HDTV is not connected, we support 720x576 and 720x480 always so that analog
* TV can be enabled anytime */
if(pMode->HDisplay == TV_WIDTH && (pMode->VDisplay == 576 || pMode->VDisplay == TV_HEIGHT)) {
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return MODE_OK;
}else
{
xf86Msg(X_ERROR, "%s: Mode (%d x %d @ %f) not supported\n", __func__,pMode->HDisplay,pMode->VDisplay,pMode->VRefresh);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return MODE_ERROR;
}
}
static Bool
exynos5_hdmi_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
DisplayModePtr adjusted_mode)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
static void
exynos5_hdmi_output_prepare(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_output_commit(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
output->funcs->dpms(output, DPMSModeOn);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static void
exynos5_hdmi_output_mode_set(xf86OutputPtr output, DisplayModePtr mode,
DisplayModePtr adjusted_mode)
{
OMAPPtr fPtr = OMAPPTR(output->scrn);
EXYNOS5HDMIOutputPrivatePtr tv_priv = output->driver_private;
int ret;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
mode->VRefresh = xf86ModeVRefresh(mode);
/* Other than 720x576 and 720x480, rest of the resolutions are for HDTV */
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, tv_input_res_table[6].std, tv_priv->hdtv_connector_type, fPtr->dma_fd);
if(ret) {
xf86Msg(X_ERROR, "%s: TV Init failed %s \n", __func__,strerror(-ret));
return;
}
/* In case of dynamic stride */
if(!fPtr->bFixedStride) {
EXYNOS5DBGMSG(X_INFO, "%s: dynamic stride\n", __func__);
/* Change the framebuffer dimensions */
fPtr->fb_tv_var.xres = fPtr->fb_tv_var.xres_virtual = mode->HDisplay;
fPtr->fb_tv_var.yres = fPtr->fb_tv_var.yres_virtual = mode->VDisplay;
ret = ioctl(fPtr->fb_tv_fd, FBIOPUT_VSCREENINFO, &fPtr->fb_tv_var);
if( ret < 0) {
xf86Msg(X_ERROR, "%s: FBIOPUT_VSCREENINFO failed %s\n", __func__,strerror(-ret));
return ;
}
}
/* We need to remember the resolution that is set here */
tv_priv->modeHDisplay = mode->HDisplay;
tv_priv->modeVDisplay = mode->VDisplay;
xf86DrvMsg(0, X_INFO, "Changing mode to %i %i %i %i\n", mode->HDisplay, mode->VDisplay, mode->HDisplay, mode->HDisplay*2);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static xf86OutputStatus
exynos5_hdmi_output_detect(xf86OutputPtr output)
{
unsigned int hpd_status = 0;
#ifdef EXYNOS_HPD
int ret;
#endif
EXYNOS5HDMIOutputPrivatePtr tv_priv = output->driver_private;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
#ifdef EXYNOS_HPD
/* Open HPD driver for hot-plug detection of HDTV */
if((fPtr->hpd_fd = open(fPtr->hpd_dev_name, O_RDWR)) < 0) {
xf86Msg(X_ERROR, "%s: HPD device open failed\n", __func__);
tv_priv->hdtv_connected = FALSE;
return XF86OutputStatusDisconnected;
}
ret = read(fPtr->hpd_fd, &hpd_status, sizeof(unsigned int));
if(ret < 0) {
xf86Msg(X_ERROR, "%s: read failed %s\n", __func__,strerror(-ret));
return;
}
xf86Msg(X_INFO, "%s: HPD status = %u\n", __func__, hpd_status);
close(fPtr->hpd_fd);
#else
hpd_status = 1;
#endif
if(hpd_status == 1)
tv_priv->hdtv_connected = TRUE;
else
tv_priv->hdtv_connected = FALSE;
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return (hpd_status == 1) ? XF86OutputStatusConnected : XF86OutputStatusDisconnected ;
}
DisplayModePtr tv_make_mode( int xres, int yres, DisplayModePtr prev )
{
DisplayModePtr mode_ptr;
unsigned int hactive_s = xres;
unsigned int vactive_s = yres;
mode_ptr = xnfcalloc(1, sizeof(DisplayModeRec));
mode_ptr->HDisplay = hactive_s;
mode_ptr->HSyncStart = hactive_s + 20;
mode_ptr->HSyncEnd = hactive_s + 40;
mode_ptr->HTotal = hactive_s + 80;
mode_ptr->VDisplay = vactive_s;
mode_ptr->VSyncStart = vactive_s + 20;
mode_ptr->VSyncEnd = vactive_s + 40;
mode_ptr->VTotal = vactive_s + 80;
mode_ptr->VRefresh = 60.0;
mode_ptr->Clock = (int) (mode_ptr->VRefresh * mode_ptr->VTotal * mode_ptr->HTotal / 1000.0);
mode_ptr->type = M_T_DRIVER;
xf86SetModeDefaultName(mode_ptr);
mode_ptr->next = NULL;
mode_ptr->prev = prev;
return mode_ptr;
}
static DisplayModePtr
exynos5_hdmi_output_get_modes(xf86OutputPtr output)
{
OMAPPtr fPtr = OMAPPTR(output->scrn);
ScrnInfoPtr pScrn = output->scrn;
DisplayModePtr mode_ptr;
EXYNOS5HDMIOutputPrivatePtr tv_priv = output->driver_private;
struct v4l2_dv_preset preset_dv;
struct v4l2_dv_enum_preset preset;
int hactive_s = TV_WIDTH;
int vactive_s = TV_HEIGHT;
unsigned long preset_code[256];
int index=0;
int ret;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
/*HACK*/
/* Will be fixed with EDID*/
index=0;
ret = ioctl(fPtr->tvout_std_fd, VIDIOC_S_OUTPUT, &index);
if (ret<0) {
xf86Msg(X_INFO, "%s: VIDIOC_S_OUTPUT failed %s\n\n", __func__,strerror(-ret));
return 0;
}
EXYNOS5DBGMSG(X_INFO, "%s: Presets List: ", __func__);
/* setting presets */
for (index = 0;1; ++index) {
preset.index = index;
ret = ioctl(fPtr->tvout_std_fd, VIDIOC_ENUM_DV_PRESETS, &preset);
if (ret<0)
EXYNOS5DBGMSG(X_INFO, "%s: VIDIOC_ENUM_DV_PRESETS last index=%d\n\n", __func__,index);
if (ret && errno == EINVAL)
break;
preset_code[index] = preset.preset;
}
/*HACK*/
/* Will be fixed with EDID, hard coding to 720p*/
index=5;
preset_dv.preset = preset_code[index];
ret = ioctl(fPtr->tvout_std_fd, VIDIOC_S_DV_PRESET, &preset_dv);
if (ret < 0)
xf86DrvMsg(0, X_ERROR,"VIDIOC_S_DV_PRESET failed because : %s\n", strerror(-ret));
if ( pScrn->modes != NULL )
{
/* Use the modes supplied by the implementation if available */
DisplayModePtr mode, first = mode = pScrn->modes;
DisplayModePtr modeptr = NULL, modeptr_prev = NULL, modeptr_first = NULL;
do {
int xres = mode->HDisplay = hactive_s;
int yres = mode->VDisplay = vactive_s;
xf86DrvMsg(0, X_INFO, "Adding mode: %i x %i\n", xres, yres);
if ( modeptr_first == NULL )
{
modeptr_first = tv_make_mode( xres, yres, NULL );
modeptr = modeptr_first;
}
else
{
modeptr->next = tv_make_mode( xres, yres, modeptr_prev);
modeptr = modeptr->next;
}
modeptr_prev = modeptr;
mode = mode->next;
} while (mode != NULL && mode != first);
return modeptr_first;
}
mode_ptr = xnfcalloc(1, sizeof(DisplayModeRec));
mode_ptr->HDisplay = hactive_s;
mode_ptr->HSyncStart = hactive_s + 20;
mode_ptr->HSyncEnd = hactive_s + 40;
mode_ptr->HTotal = hactive_s + 80;
mode_ptr->VDisplay = vactive_s;
mode_ptr->VSyncStart = vactive_s + 20;
mode_ptr->VSyncEnd = vactive_s + 40;
mode_ptr->VTotal = vactive_s + 80;
mode_ptr->VRefresh = 60.0;
mode_ptr->Clock = (int) (mode_ptr->VRefresh * mode_ptr->VTotal * mode_ptr->HTotal / 1000.0);
mode_ptr->type = M_T_DRIVER;
xf86SetModeDefaultName(mode_ptr);
mode_ptr->next = NULL;
mode_ptr->prev = NULL;
tv_priv->hdtv_connector_type = V4L2_OUTPUT_TYPE_HDMI_RGB;
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return mode_ptr;
}
static Bool
exynos5_hdmi_output_set_property(xf86OutputPtr output, Atom property,
RRPropertyValuePtr value)
{
OMAPPtr fPtr = OMAPPTR(output->scrn);
EXYNOS5HDMIOutputPrivatePtr tv_priv = output->driver_private;
v4l2_std_id userMode;
Atom atom;
const char *name;
int index,ret;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
if(value->type != XA_ATOM || value->format != 32 || value->size != 1) {
xf86Msg(X_ERROR, "%s: Invalid property value\n", __func__);
return FALSE;
}
memcpy(&atom, value->data, 4);
name = NameForAtom(atom);
/* This function is not complete and all the variables like atom and name shall be
* used once EDID functionality is implemented */
if(property == pal_mode_atom) {
index = pal_mode_lookup(name);
if(index < 0) {
xf86Msg(X_ERROR, "%s: Invalid mode index\n", __func__);
return FALSE;
}
userMode = pal_index_to_mode(index);
/* Check if the mode is already set */
if(tv_priv->mode_720x576 != userMode) {
/* Check if user has set 720x576 mode for HDTV, but either the HDTV is not
* connected or the HDTV does not support 720x576 mode */
if(userMode == V4L2_STD_576P_50_4_3 && (!tv_priv->hdtv_connected || !tv_priv->hdtv_720x576_supported)) {
xf86Msg(X_ERROR, "%s: Mode can not be set\n", __func__);
return FALSE;
}
tv_priv->mode_720x576 = userMode;
if(tv_priv->modeHDisplay == 720 && tv_priv->modeVDisplay == 576) {
if(tv_priv->hdtv_connected && tv_priv->hdtv_720x576_supported && tv_priv->mode_720x576 == V4L2_STD_576P_50_4_3) {
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, tv_priv->mode_720x576, tv_priv->hdtv_connector_type, 0);
if(ret){
xf86Msg(X_ERROR, "%s: TV Init Failed %s line %d\n", __func__,strerror(-ret),__LINE__);
return FALSE;
}
}
else {
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, tv_priv->mode_720x576, V4L2_OUTPUT_TYPE_COMPOSITE, 0);
if(ret){
xf86Msg(X_ERROR, "%s: TV Init Failed %s line %d\n", __func__,strerror(-ret),__LINE__);
return FALSE;
}
}
}
}
}
else if(property == ntsc_mode_atom) {
index = ntsc_mode_lookup(name);
if(index < 0) {
xf86Msg(X_ERROR, "%s: Invalid mode index\n", __func__);
return FALSE;
}
userMode = ntsc_index_to_mode(index);
/* Check if the mode is already set */
if(tv_priv->mode_720x480 != userMode) {
/* Check if user has set 720x480 mode for HDTV, but either the HDTV is not
* connected or the HDTV does not support 720x480 mode */
if(userMode == V4L2_STD_480P_60_4_3 && (!tv_priv->hdtv_connected || !tv_priv->hdtv_720x480_supported)) {
xf86Msg(X_ERROR, "%s: Mode can not be set\n", __func__);
return FALSE;
}
tv_priv->mode_720x480 = userMode;
if(tv_priv->modeHDisplay == 720 && tv_priv->modeVDisplay == 480) {
if(tv_priv->hdtv_connected && tv_priv->hdtv_720x480_supported && tv_priv->mode_720x480 == V4L2_STD_480P_60_4_3) {
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, tv_priv->mode_720x480, tv_priv->hdtv_connector_type, 0);
if(ret){
xf86Msg(X_ERROR, "%s: TV Init Failed %s line %d\n", __func__,strerror(-ret),__LINE__);
return FALSE;
}
}
else {
ret = exynos5_init_hdmi(fPtr->tvout_std_fd, tv_priv->mode_720x480, V4L2_OUTPUT_TYPE_COMPOSITE, 0);
if(ret){
xf86Msg(X_ERROR, "%s: TV Init Failed %s line %d\n", __func__,strerror(-ret),__LINE__);
return FALSE;
}
}
}
}
}
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
static Bool
exynos5_hdmi_output_get_property(xf86OutputPtr output, Atom property)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}
static xf86CrtcPtr
exynos5_hdmi_output_get_crtc(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return output->crtc;
}
static void
exynos5_hdmi_output_destroy(xf86OutputPtr output)
{
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
}
static const xf86OutputFuncsRec exynos5_hdmi_output_funcs = {
.create_resources = exynos5_hdmi_output_create_resources,
.dpms = exynos5_hdmi_output_dpms,
.save = exynos5_hdmi_output_save,
.restore = exynos5_hdmi_output_restore,
.mode_valid = exynos5_hdmi_output_mode_valid,
.mode_fixup = exynos5_hdmi_output_mode_fixup,
.prepare = exynos5_hdmi_output_prepare,
.commit = exynos5_hdmi_output_commit,
.mode_set = exynos5_hdmi_output_mode_set,
.detect = exynos5_hdmi_output_detect,
.get_modes = exynos5_hdmi_output_get_modes,
#ifdef RANDR_12_INTERFACE
.set_property = exynos5_hdmi_output_set_property,
#endif
#ifdef RANDR_13_INTERFACE
.get_property = exynos5_hdmi_output_get_property,
#endif
#ifdef RANDR_GET_CRTC_INTERFACE
.get_crtc = exynos5_hdmi_output_get_crtc,
#endif
.destroy = exynos5_hdmi_output_destroy,
};
/* Init function called from driver's PreInit function */
Bool exynos5_hdmi_init(ScrnInfoPtr pScrn)
{
xf86CrtcPtr crtc;
xf86OutputPtr output;
EXYNOS5HDMIOutputPrivatePtr tv_priv;
OMAPPtr fPtr;
EXYNOS5DBGMSG(X_INFO, "%s: ++++\n", __func__);
/* Create CRTC */
crtc = xf86CrtcCreate(pScrn, &exynos5_hdmi_crtc_funcs);
if(crtc == NULL) {
xf86Msg(X_ERROR, "%s: xf86CrtcCreate failed\n", __func__);
return FALSE;
}
/* Create output for TV */
output = xf86OutputCreate(pScrn, &exynos5_hdmi_output_funcs, "TV");
if(output == NULL) {
xf86Msg(X_ERROR, "%s: xf86OutputCreate failed\n", __func__);
return FALSE;
}
fPtr = OMAPPTR(output->scrn);
/*HACK*/
output->possible_crtcs = 8;/* First 7 crtcs are assigned to kms possible crtcs */
fPtr->tvout_std_fd = open(/*fPtr->tvout_std_dev_name*/"/dev/video16", O_RDWR);
if (fPtr->tvout_std_fd < 0) {
xf86Msg(X_ERROR, "%s: %s open failed\n", __func__, fPtr->tvout_std_dev_name);
return FALSE;
}
xf86Msg(X_ERROR, "%s: %s openedi fd =%d\n", __func__, fPtr->tvout_std_dev_name,fPtr->tvout_std_fd);
tv_priv = xnfcalloc(sizeof(EXYNOS5HDMIOutputPrivateRec), 1);
/* xnfcalloc exits server when allocation failes hence no check is put*/
#if EXYNOS_EDID
/* Open I2C1 for getting (EDID+E-EDID) data through DDC bus */
if((i2c_fd = open(fPtr->ddc_dev_name, O_RDWR)) < 0) {
xf86Msg(X_ERROR, "%s: I2C-1 device open failed\n", __func__);
return FALSE;
}
/* Initialize DDC(I2C) bus */
if(!EXYNOS5I2CInit(pScrn, &tv_priv->pI2CBus, i2c_fd, "I2C-DDC")) {
xf86Msg(X_ERROR,"%s: EXYNOS5I2CInit failed\n", __func__);
return FALSE;
}
#endif
/* Default initialization of private data for TV output */
tv_priv->hdtv_connected = FALSE;
tv_priv->hdtv_720x576_supported = FALSE;
tv_priv->hdtv_720x480_supported = FALSE;
tv_priv->hdtv_connector_type = V4L2_OUTPUT_TYPE_HDMI;
tv_priv->mode_720x576 = V4L2_STD_PAL_M;
tv_priv->mode_720x480 = V4L2_STD_NTSC_M;
output->driver_private = tv_priv;
EXYNOS5DBGMSG(X_INFO, "%s: ----\n", __func__);
return TRUE;
}