/*
* linux/drivers/video/omap2/dss/hdmi.c
*
* Copyright (C) 2009 Texas Instruments
* Author: Yong Zhi
*
* HDMI settings from TI's DSS driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* 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, see .
* History:
* Mythripk Apr 2010 Modified for EDID reading and adding OMAP
* related timing
* May 2010 Added support of Hot Plug Detect
* July 2010 Redesigned HDMI EDID for Auto-detect of timing
* August 2010 Char device user space control for HDMI
*/
#define DSS_SUBSYS_NAME "HDMI"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dss.h"
#include
static int hdmi_enable_display(struct omap_dss_device *dssdev);
static void hdmi_disable_display(struct omap_dss_device *dssdev);
static int hdmi_display_suspend(struct omap_dss_device *dssdev);
static int hdmi_display_resume(struct omap_dss_device *dssdev);
static void hdmi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
static void hdmi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
static void hdmi_set_custom_edid_timing_code(struct omap_dss_device *dssdev, int code , int mode);
static void hdmi_get_edid(struct omap_dss_device *dssdev);
static int hdmi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
static int hdmi_read_edid(struct omap_video_timings *);
static int get_edid_timing_data(struct HDMI_EDID *edid);
static irqreturn_t hdmi_irq_handler(int irq, void *arg);
static int hdmi_enable_hpd(struct omap_dss_device *dssdev);
static void hdmi_power_off(struct omap_dss_device *dssdev);
static int hdmi_open(struct inode *inode, struct file *filp);
static int hdmi_release(struct inode *inode, struct file *filp);
static int hdmi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
/*Structures for chardevice move this to panel*/
static int hdmi_major;
static struct cdev hdmi_cdev;
static dev_t hdmi_dev_id;
/*This is a basic structure read and write ioctls will be added to configure parameters*/
static struct file_operations hdmi_fops = {
.owner = THIS_MODULE,
.open = hdmi_open,
.release = hdmi_release,
.ioctl = hdmi_ioctl,
};
#define HDMI_PLLCTRL 0x58006200
#define HDMI_PHY 0x58006300
u8 edid[HDMI_EDID_MAX_LENGTH] = {0};
u8 edid_set = false;
u8 header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0};
u8 hpd_mode = 0, custom_set = 0;
enum hdmi_ioctl_cmds {
HDMI_ENABLE,
HDMI_DISABLE,
HDMI_READ_EDID,
};
/* PLL */
#define PLLCTRL_PLL_CONTROL 0x0ul
#define PLLCTRL_PLL_STATUS 0x4ul
#define PLLCTRL_PLL_GO 0x8ul
#define PLLCTRL_CFG1 0xCul
#define PLLCTRL_CFG2 0x10ul
#define PLLCTRL_CFG3 0x14ul
#define PLLCTRL_CFG4 0x20ul
/* HDMI PHY */
#define HDMI_TXPHY_TX_CTRL 0x0ul
#define HDMI_TXPHY_DIGITAL_CTRL 0x4ul
#define HDMI_TXPHY_POWER_CTRL 0x8ul
#define HDMI_TXPHY_PAD_CFG_CTRL 0xCul
struct hdmi_hvsync_pol {
int vsync_pol;
int hsync_pol;
};
/*This is the structure which has all supported timing values that OMAP4 supports*/
const struct omap_video_timings all_timings_direct[32] = {
{640, 480, 25200, 96, 16, 48, 2, 10, 33},
{1280, 720, 74250, 40, 440, 220, 5, 5, 20},
{1280, 720, 74250, 40, 110, 220, 5, 5, 20},
{720, 480, 27000, 62, 16, 60, 6, 9, 30},
{2880, 576, 108000, 256, 48, 272, 5, 5, 39},
{1440, 240, 27000, 124, 38, 114, 3, 4, 15},
{1440, 288, 27000, 126, 24, 138, 3, 2, 19},
{1920, 540, 74250, 44, 528, 148, 5, 2, 15},
{1920, 540, 74250, 44, 88, 148, 5, 2, 15},
{1920, 1080, 148500, 44, 88, 148, 5, 4, 36},
{720, 576, 27000, 64, 12, 68, 5, 5, 39},
{1440, 576, 54000, 128, 24, 136, 5, 5, 39},
{1920, 1080, 148500, 44, 528, 148, 5, 4, 36},
{2880, 480, 108000, 248, 64, 240, 6, 9, 30},
/*Vesa frome here*/
{640, 480, 25175, 96, 16, 48, 2 , 11, 31},
{800, 600, 40000, 128, 40, 88, 4 , 1, 23},
{848, 480, 33750, 112, 16, 112, 8 , 6, 23},
{1280, 768, 79500, 128, 64, 192, 7 , 3, 20},
{1280, 800, 83500, 128, 72, 200, 6 , 3, 22},
{1360, 768, 85500, 112, 64, 256, 6 , 3, 18},
{1280, 960, 108000, 112, 96, 312, 3 , 1, 36},
{1280, 1024, 108000, 112, 48, 248, 3 , 1, 38},
{1024, 768, 65000, 136, 24, 160, 6, 3, 29},
{1400, 1050, 121750, 144, 88, 232, 4, 3, 32},
{1440, 900, 106500, 152, 80, 232, 6, 3, 25},
{1680, 1050, 146250, 176 , 104, 280, 6, 3, 30},
{1366, 768, 85500, 143, 70, 213, 3, 3, 24},
{1920, 1080, 148500, 44, 88, 80, 5, 4, 36},
{1280, 768, 68250, 32, 48, 80, 7, 3, 12},
{1400, 1050, 101000, 32, 48, 80, 4, 3, 23},
{1680, 1050, 119000, 32, 48, 80, 6, 3, 21},
{1280, 800, 79500, 32, 48, 80, 6, 3, 14} };
/*This is a static Mapping array which maps the timing values with corresponding CEA / VESA code*/
int code_index[32] = {1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35,
/* <--14 CEA 17--> vesa*/
4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A,
0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B};
/*Static mapping of the Timing values with the corresponding Vsync and Hsync polarity*/
const struct hdmi_hvsync_pol hvpol_mapping[32] = {
{0, 0}, {1, 1}, {1, 1}, {0, 0},
{0, 0}, {0, 0}, {0, 0}, {1, 1},
{1, 1}, {1, 1}, {0, 0}, {0, 0},
{1, 1}, {0, 0}, {0, 0}, {1, 1},
{1, 1}, {1, 0}, {1, 0}, {1, 1},
{1, 1}, {1, 1}, {0, 0}, {1, 0},
{1, 0}, {1, 0}, {1, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1} };
/*This is revere static mapping which maps the CEA / VESA code to the corresponding timing values*/
/* note: table is 10 entries per line to make it easier to find index.. */
int code_cea[39] = {
-1, 0, 3, 3, 2, 8, 5, 5, -1, -1,
-1, -1, -1, -1, -1, -1, 9, 10, 10, 1,
7, 6, 6, -1, -1, -1, -1, -1, -1, 11,
11, 12, -1, -1, -1, 13, 13, 4, 4};
/* note: table is 10 entries per line to make it easier to find index.. */
int code_vesa[83] = {
-1, -1, -1, -1, 14, -1, -1, -1, -1, 15,
-1, -1, -1, -1, 16, -1, 22, -1, -1, -1,
-1, -1, 28, 17, -1, -1, -1, 31, 18, -1,
-1, -1, 20, -1, -1, 21, -1, -1, -1, 19,
-1, 29, 23, -1, -1, -1, -1, 24, -1, -1,
-1, -1, -1, -1, -1, -1, -1, 30, 25, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 26, 27};
struct hdmi {
struct kobject kobj;
void __iomem *base_phy;
void __iomem *base_pll;
struct mutex lock;
int code;
int mode;
struct hdmi_config cfg;
struct omap_display_platform_data *pdata;
struct platform_device *pdev;
};
struct hdmi hdmi;
struct hdmi_cm {
int code;
int mode;
};
struct omap_video_timings edid_timings;
static void update_cfg (struct hdmi_config *cfg, struct omap_video_timings *timings)
{
cfg->ppl = timings->x_res;
cfg->lpp = timings->y_res;
cfg->hbp = timings->hbp;
cfg->hfp = timings->hfp;
cfg->hsw = timings->hsw;
cfg->vbp = timings->vbp;
cfg->vfp = timings->vfp;
cfg->vsw = timings->vsw;
cfg->pixel_clock = timings->pixel_clock;
}
static void update_cfg_pol(struct hdmi_config *cfg, int code)
{
cfg->v_pol = hvpol_mapping[code].vsync_pol;
cfg->h_pol = hvpol_mapping[code].hsync_pol;
}
static inline void hdmi_write_reg(u32 base, u16 idx, u32 val)
{
void __iomem *b;
switch (base) {
case HDMI_PHY:
b = hdmi.base_phy;
break;
case HDMI_PLLCTRL:
b = hdmi.base_pll;
break;
default:
BUG();
}
__raw_writel(val, b + idx);
/* DBG("write = 0x%x idx =0x%x\r\n", val, idx); */
}
static inline u32 hdmi_read_reg(u32 base, u16 idx)
{
void __iomem *b;
u32 l;
switch (base) {
case HDMI_PHY:
b = hdmi.base_phy;
break;
case HDMI_PLLCTRL:
b = hdmi.base_pll;
break;
default:
BUG();
}
l = __raw_readl(b + idx);
/* DBG("addr = 0x%p rd = 0x%x idx = 0x%x\r\n", (b+idx), l, idx); */
return l;
}
#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
#define FLD_MOD(orig, val, start, end) \
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
#define REG_FLD_MOD(b, i, v, s, e) \
hdmi_write_reg(b, i, FLD_MOD(hdmi_read_reg(b, i), v, s, e))
static ssize_t hdmi_edid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
memcpy(buf, edid, HDMI_EDID_MAX_LENGTH);
return HDMI_EDID_MAX_LENGTH;
}
static ssize_t hdmi_edid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
return 0;
}
static ssize_t hdmi_yuv_supported(struct device *dev,
struct device_attribute *attr, char *buf)
{
bool enabled = hdmi_tv_yuv_supported(edid);
return snprintf(buf, PAGE_SIZE, "%s\n", enabled ? "true" : "false");
}
static ssize_t hdmi_yuv_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int enabled;
enabled = simple_strtoul(buf, NULL, 10);
if (enabled)
hdmi_configure_csc(RGB_TO_YUV);
else
hdmi_configure_csc(RGB);
return size;
}
static DEVICE_ATTR(edid, S_IRUGO, hdmi_edid_show, hdmi_edid_store);
static DEVICE_ATTR(yuv, S_IRUGO | S_IWUSR, hdmi_yuv_supported, hdmi_yuv_set);
int hdmi_hot_plug_event_send(struct omap_dss_device *dssdev, int onoff)
{
printk("hot plug event %d", onoff);
return kobject_uevent(&dssdev->dev.kobj, onoff ? KOBJ_ADD : KOBJ_REMOVE);
}
static int hdmi_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int hdmi_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int hdmi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct omap_dss_device *dssdev = NULL;
const char *buf = "hdmi";
int r = 0;
int match(struct omap_dss_device *dssdev2 , void *data)
{
const char *str = data;
return sysfs_streq(dssdev2->name , str);
}
dssdev = omap_dss_find_device((void *)buf , match);
switch (cmd) {
case HDMI_ENABLE:
r = hdmi_enable_display(dssdev);
break;
case HDMI_DISABLE:
hdmi_disable_display(dssdev);
break;
case HDMI_READ_EDID:
hdmi_get_edid(dssdev);
break;
default:
r = -EINVAL;
DSSDBG("Un-recoganized command");
break;
}
return r;
}
/*
* refclk = (sys_clk/(highfreq+1))/(n+1)
* so refclk = 38.4/2/(n+1) = 19.2/(n+1)
* choose n = 15, makes refclk = 1.2
*
* m = tclk/cpf*refclk = tclk/2*1.2
*
* for clkin = 38.2/2 = 192
* phy = 2520
*
* m = 2520*16/2* 192 = 105;
*
* for clkin = 38.4
* phy = 2520
*
*/
#define CPF 2
struct hdmi_pll_info {
u16 regn;
u16 regm;
u32 regmf;
u16 regm4; /* M4_CLOCK_DIV */
u16 regm2;
u16 regsd;
u16 dcofreq;
};
static inline void print_omap_video_timings(struct omap_video_timings *timings)
{
extern unsigned int dss_debug;
if (dss_debug) {
printk(KERN_INFO "Timing Info:\n");
printk(KERN_INFO " pixel_clk = %d\n", timings->pixel_clock);
printk(KERN_INFO " x_res = %d\n", timings->x_res);
printk(KERN_INFO " y_res = %d\n", timings->y_res);
printk(KERN_INFO " hfp = %d\n", timings->hfp);
printk(KERN_INFO " hsw = %d\n", timings->hsw);
printk(KERN_INFO " hbp = %d\n", timings->hbp);
printk(KERN_INFO " vfp = %d\n", timings->vfp);
printk(KERN_INFO " vsw = %d\n", timings->vsw);
printk(KERN_INFO " vbp = %d\n", timings->vbp);
}
}
static void compute_pll(int clkin, int phy,
int n, struct hdmi_pll_info *pi)
{
int refclk;
u32 temp, mf;
if (clkin > 3200) /* 32 mHz */
refclk = clkin / (2 * (n + 1));
else
refclk = clkin / (n + 1);
temp = phy * 100/(CPF * refclk);
pi->regn = n;
pi->regm = temp/100;
pi->regm2 = 1;
mf = (phy - pi->regm * CPF * refclk) * 262144;
pi->regmf = mf/(CPF * refclk);
if (phy > 1000 * 100) {
pi->regm4 = phy / 10000;
pi->dcofreq = 1;
pi->regsd = ((pi->regm * 384)/((n + 1) * 250) + 5)/10;
} else {
pi->regm4 = 1;
pi->dcofreq = 0;
pi->regsd = 0;
}
DSSDBG("M = %d Mf = %d, m4= %d\n", pi->regm, pi->regmf, pi->regm4);
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
}
static int hdmi_pll_init(int refsel, int dcofreq, struct hdmi_pll_info *fmt, u16 sd)
{
u32 r;
unsigned t = 500000;
u32 pll = HDMI_PLLCTRL;
/* PLL start always use manual mode */
REG_FLD_MOD(pll, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
r = hdmi_read_reg(pll, PLLCTRL_CFG1);
r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1__PLL_REGM */
r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1__PLL_REGN */
r = FLD_MOD(r, fmt->regm4, 25, 21); /* M4_CLOCK_DIV */
hdmi_write_reg(pll, PLLCTRL_CFG1, r);
r = hdmi_read_reg(pll, PLLCTRL_CFG2);
/* SYS w/o divide by 2 [22:21] = donot care [11:11] = 0x0 */
/* SYS divide by 2 [22:21] = 0x3 [11:11] = 0x1 */
/* PCLK, REF1 or REF2 [22:21] = 0x0, 0x 1 or 0x2 [11:11] = 0x1 */
r = FLD_MOD(r, 0x0, 11, 11); /* PLL_CLKSEL 1: PLL 0: SYS*/
r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
r = FLD_MOD(r, 0x1, 20, 20); /* HSDIVBYPASS assert during locking */
r = FLD_MOD(r, refsel, 22, 21); /* REFSEL */
/* DPLL3 used by DISPC or HDMI itself*/
r = FLD_MOD(r, 0x0, 17, 17); /* M4_CLOCK_PWDN */
r = FLD_MOD(r, 0x1, 16, 16); /* M4_CLOCK_EN */
if (dcofreq) {
/* divider programming for 1080p */
REG_FLD_MOD(pll, PLLCTRL_CFG3, sd, 17, 10);
r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
} else
r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
hdmi_write_reg(pll, PLLCTRL_CFG2, r);
r = hdmi_read_reg(pll, PLLCTRL_CFG4);
r = FLD_MOD(r, 0, 24, 18); /* todo: M2 */
r = FLD_MOD(r, fmt->regmf, 17, 0);
/* go now */
REG_FLD_MOD(pll, PLLCTRL_PLL_GO, 0x1ul, 0, 0);
/* wait for bit change */
while (FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_GO), 0, 0))
/* Wait till the lock bit is set */
/* read PLL status */
while (0 == FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_STATUS), 1, 1)) {
udelay(1);
if (!--t) {
printk(KERN_WARNING "HDMI: cannot lock PLL\n");
DSSDBG("CFG1 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG1));
DSSDBG("CFG2 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG2));
DSSDBG("CFG4 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG4));
return -EIO;
}
}
DSSDBG("PLL locked!\n");
r = hdmi_read_reg(pll, PLLCTRL_CFG2);
r = FLD_MOD(r, 0, 0, 0); /* PLL_IDLE */
r = FLD_MOD(r, 0, 5, 5); /* PLL_PLLLPMODE */
r = FLD_MOD(r, 0, 6, 6); /* PLL_LOWCURRSTBY */
r = FLD_MOD(r, 0, 8, 8); /* PLL_DRIFTGUARDEN */
r = FLD_MOD(r, 0, 10, 9); /* PLL_LOCKSEL */
r = FLD_MOD(r, 1, 13, 13); /* PLL_REFEN */
r = FLD_MOD(r, 1, 14, 14); /* PHY_CLKINEN */
r = FLD_MOD(r, 0, 15, 15); /* BYPASSEN */
r = FLD_MOD(r, 0, 20, 20); /* HSDIVBYPASS */
hdmi_write_reg(pll, PLLCTRL_CFG2, r);
return 0;
}
static int hdmi_pll_reset(void)
{
int t = 0;
/* SYSREEST controled by power FSM*/
REG_FLD_MOD(HDMI_PLLCTRL, PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
/* READ 0x0 reset is in progress */
while (!FLD_GET(hdmi_read_reg(HDMI_PLLCTRL,
PLLCTRL_PLL_STATUS), 0, 0)) {
udelay(1);
if (t++ > 1000) {
ERR("Failed to sysrest PLL\n");
return -ENODEV;
}
}
return 0;
}
int hdmi_pll_program(struct hdmi_pll_info *fmt)
{
u32 r;
int refsel;
HDMI_PllPwr_t PllPwrWaitParam;
/* wait for wrapper rest */
HDMI_W1_SetWaitSoftReset();
/* power off PLL */
PllPwrWaitParam = HDMI_PLLPWRCMD_ALLOFF;
r = HDMI_W1_SetWaitPllPwrState(HDMI_WP,
PllPwrWaitParam);
if (r)
return r;
/* power on PLL */
PllPwrWaitParam = HDMI_PLLPWRCMD_BOTHON_ALLCLKS;
r = HDMI_W1_SetWaitPllPwrState(HDMI_WP,
PllPwrWaitParam);
if (r)
return r;
hdmi_pll_reset();
refsel = 0x3; /* select SYSCLK reference */
r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd);
return r;
}
/* double check the order */
static int hdmi_phy_init(u32 w1,
u32 phy)
{
u32 count;
int r;
/* wait till PHY_PWR_STATUS=LDOON */
/* HDMI_PHYPWRCMD_LDOON = 1 */
r = HDMI_W1_SetWaitPhyPwrState(w1, 1);
if (r)
return r;
/* wait till PHY_PWR_STATUS=TXON */
r = HDMI_W1_SetWaitPhyPwrState(w1, 2);
if (r)
return r;
/* read address 0 in order to get the SCPreset done completed */
/* Dummy access performed to solve resetdone issue */
hdmi_read_reg(phy, HDMI_TXPHY_TX_CTRL);
/* write to phy address 0 to configure the clock */
/* use HFBITCLK write HDMI_TXPHY_TX_CONTROL__FREQOUT field */
REG_FLD_MOD(phy, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
/* write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
hdmi_write_reg(phy, HDMI_TXPHY_DIGITAL_CTRL,
0xF0000000);
/* setup max LDO voltage */
REG_FLD_MOD(phy, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
/* write to phy address 3 to change the polarity control */
REG_FLD_MOD(phy, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
count = 0;
while (count++ < 1000)
;
return 0;
}
static int hdmi_phy_off(u32 name)
{
int r = 0;
u32 count;
/* wait till PHY_PWR_STATUS=OFF */
/* HDMI_PHYPWRCMD_OFF = 0 */
r = HDMI_W1_SetWaitPhyPwrState(name, 0);
if (r)
return r;
count = 0;
while (count++ < 200)
;
return 0;
}
/* driver */
static int get_timings_index(void)
{
int code;
if (hdmi.mode == 0)
code = code_vesa[hdmi.code];
else
code = code_cea[hdmi.code];
if (code == -1) {
code = 9;
hdmi.code = 16;
hdmi.mode = 1;
}
return code;
}
static int hdmi_panel_probe(struct omap_dss_device *dssdev)
{
int code;
printk("ENTER hdmi_panel_probe()\n");
dssdev->panel.config = OMAP_DSS_LCD_TFT |
OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS;
code = get_timings_index();
dssdev->panel.timings = all_timings_direct[code];
printk("hdmi_panel_probe x_res= %d y_res = %d", \
dssdev->panel.timings.x_res, dssdev->panel.timings.y_res);
mdelay(50);
return 0;
}
static void hdmi_panel_remove(struct omap_dss_device *dssdev)
{
}
static int hdmi_panel_enable(struct omap_dss_device *dssdev)
{
hdmi_enable_display(dssdev);
return 0;
}
static void hdmi_panel_disable(struct omap_dss_device *dssdev)
{
hdmi_disable_display(dssdev);
}
static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
{
hdmi_display_suspend(dssdev);
return 0;
}
static int hdmi_panel_resume(struct omap_dss_device *dssdev)
{
hdmi_display_resume(dssdev);
return 0;
}
static void hdmi_enable_clocks(int enable)
{
if (enable)
dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
DSS_CLK_96M);
else
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
DSS_CLK_96M);
}
static struct omap_dss_driver hdmi_driver = {
.probe = hdmi_panel_probe,
.remove = hdmi_panel_remove,
.enable = hdmi_panel_enable,
.disable = hdmi_panel_disable,
.suspend = hdmi_panel_suspend,
.resume = hdmi_panel_resume,
.get_timings = hdmi_get_timings,
.set_timings = hdmi_set_timings,
.check_timings = hdmi_check_timings,
.get_edid = hdmi_get_edid,
.set_custom_edid_timing_code = hdmi_set_custom_edid_timing_code,
.hpd_enable = hdmi_enable_hpd,
.driver = {
.name = "hdmi_panel",
.owner = THIS_MODULE,
},
};
/* driver end */
int hdmi_init(struct platform_device *pdev)
{
int r = 0, hdmi_irq;
struct resource *hdmi_mem;
printk("Enter hdmi_init()\n");
hdmi.pdata = pdev->dev.platform_data;
hdmi.pdev = pdev;
mutex_init(&hdmi.lock);
hdmi_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
hdmi.base_pll = ioremap((hdmi_mem->start + 0x200), resource_size(hdmi_mem));
if (!hdmi.base_pll) {
ERR("can't ioremap pll\n");
return -ENOMEM;
}
hdmi.base_phy = ioremap((hdmi_mem->start + 0x300), 64);
if (!hdmi.base_phy) {
ERR("can't ioremap phy\n");
return -ENOMEM;
}
hdmi_enable_clocks(1);
hdmi_lib_init();
hdmi_enable_clocks(0);
/* Get the major number for this module */
r = alloc_chrdev_region(&hdmi_dev_id, 0, 1, "hdmi_panel");
if (r) {
printk("HDMI: Cound not register character device\n");
return -ENOMEM;
}
hdmi_major = MAJOR(hdmi_dev_id);
/* initialize character device */
cdev_init(&hdmi_cdev, &hdmi_fops);
hdmi_cdev.owner = THIS_MODULE;
hdmi_cdev.ops = &hdmi_fops;
/* add char driver */
r = cdev_add(&hdmi_cdev, hdmi_dev_id, 1);
if (r) {
printk("HDMI: Could not add hdmi char driver\n");
unregister_chrdev_region(hdmi_dev_id, 1);
return -ENOMEM;
}
hdmi_irq = platform_get_irq(pdev, 0);
r = request_irq(hdmi_irq,
hdmi_irq_handler,
0, "OMAP HDMI", (void *)0);
return omap_dss_register_driver(&hdmi_driver);
}
void hdmi_exit(void)
{
hdmi_lib_exit();
free_irq(OMAP44XX_IRQ_DSS_HDMI, NULL);
iounmap(hdmi.base_pll);
iounmap(hdmi.base_phy);
}
static int hdmi_power_on(struct omap_dss_device *dssdev)
{
int r = 0;
int code = 0;
int dirty = false;
struct omap_video_timings *p;
struct hdmi_pll_info pll_data;
int clkin, n, phy;
code = get_timings_index();
dssdev->panel.timings = all_timings_direct[code];
hdmi_enable_clocks(1);
p = &dssdev->panel.timings;
if (!custom_set) {
code = get_timings_index();
DSSDBG("No edid set thus will be calling hdmi_read_edid");
r = hdmi_read_edid(p);
if (r) {
r = -EIO;
goto err;
}
if (get_timings_index() != code) {
dirty = true;
}
} else {
dirty = true;
}
update_cfg(&hdmi.cfg, p);
update_cfg_pol(&hdmi.cfg, code);
code = get_timings_index();
dssdev->panel.timings = all_timings_direct[code];
DSSDBG("hdmi_power on x_res= %d y_res = %d", \
dssdev->panel.timings.x_res, dssdev->panel.timings.y_res);
DSSDBG("hdmi_power on code= %d mode = %d", hdmi.code,
hdmi.mode);
clkin = 3840; /* 38.4 mHz */
n = 15; /* this is a constant for our math */
phy = p->pixel_clock;
compute_pll(clkin, phy, n, &pll_data);
HDMI_W1_StopVideoFrame(HDMI_WP);
dispc_enable_digit_out(0);
if (dirty) {
omap_dss_notify(dssdev, OMAP_DSS_SIZE_CHANGE);
}
/* config the PLL and PHY first */
r = hdmi_pll_program(&pll_data);
if (r) {
DSSERR("Failed to lock PLL\n");
r = -EIO;
goto err;
}
r = hdmi_phy_init(HDMI_WP, HDMI_PHY);
if (r) {
DSSERR("Failed to start PHY\n");
r = -EIO;
goto err;
}
hdmi.cfg.hdmi_dvi = hdmi.mode;
hdmi.cfg.video_format = hdmi.code;
if ((hdmi.mode)) {
switch (hdmi.code) {
case 20:
case 5:
case 6:
case 21:
hdmi.cfg.interlace = 1;
break;
default:
hdmi.cfg.interlace = 0;
break;
}
}
hdmi_lib_enable(&hdmi.cfg);
/* these settings are independent of overlays */
dss_switch_tv_hdmi(1);
/* bypass TV gamma table*/
dispc_enable_gamma_table(0);
/* do not fall into any sort of idle */
dispc_set_idle_mode();
#ifndef CONFIG_OMAP4_ES1
/*The default reset value for DISPC.DIVISOR1 LCD is 4
* in ES2.0 and the clock will run at 1/4th the speed
* resulting in the sync_lost_digit */
dispc_set_tv_divisor();
#endif
/* tv size */
dispc_set_digit_size(dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
HDMI_W1_StartVideoFrame(HDMI_WP);
dispc_enable_digit_out(1);
return 0;
err:
return r;
}
int hdmi_min_enable(void)
{
int r;
DSSDBG("hdmi_min_enable");
r = hdmi_phy_init(HDMI_WP, HDMI_PHY);
if (r) {
DSSERR("Failed to start PHY\n");
}
hdmi.cfg.hdmi_dvi = hdmi.mode;
hdmi.cfg.video_format = hdmi.code;
hdmi_lib_enable(&hdmi.cfg);
return 0;
}
static spinlock_t irqstatus_lock = SPIN_LOCK_UNLOCKED;
static volatile int irqstatus;
void hdmi_work_queue(struct work_struct *work)
{
struct omap_dss_device *dssdev = NULL;
const char *buf = "hdmi";
int r;
unsigned long flags;
int match(struct omap_dss_device *dssdev2 , void *data)
{
const char *str = data;
return sysfs_streq(dssdev2->name , str);
}
dssdev = omap_dss_find_device((void *)buf , match);
DSSDBG("found hdmi handle %s" , dssdev->name);
spin_lock_irqsave(&irqstatus_lock, flags);
r = irqstatus;
irqstatus = 0;
spin_unlock_irqrestore(&irqstatus_lock, flags);
DSSDBG("irqstatus=%08x\n hdp_mode = %d dssdev->state = %d",\
r, hpd_mode, dssdev->state);
if (r & HDMI_DISCONNECT) {
printk(KERN_INFO"Display disabled\n");
HDMI_W1_StopVideoFrame(HDMI_WP);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
dispc_enable_digit_out(0);
HDMI_W1_SetWaitPllPwrState(HDMI_WP, HDMI_PLLPWRCMD_ALLOFF);
edid_set = false;
hdmi_enable_clocks(0);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
hdmi_enable_hpd(dssdev);
hpd_mode = 1;
hdmi_hot_plug_event_send(dssdev, 0);
}
if (r & (HDMI_CONNECT) && (hpd_mode == 1) &&
(dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)) {
printk(KERN_INFO"Physical Connect\n");
hdmi_enable_clocks(1);
hdmi_power_on(dssdev);
mdelay(1000);
}
if (r & (HDMI_HPD) && (hpd_mode == 1) &&
dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
mdelay(1000);
printk(KERN_INFO "Enabling display\n");
hdmi_enable_clocks(1);
hdmi_power_on(dssdev);
hdmi_hot_plug_event_send(dssdev, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
}
kfree(work);
}
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
struct work_struct *work;
unsigned long flags;
int r = 0;
int work_pending;
HDMI_W1_HPD_handler(&r);
DSSDBG("r=%08x, prev irqstatus=%08x\n", r, irqstatus);
if (((r & HDMI_CONNECT) || (r & HDMI_HPD)) && (hpd_mode == 1))
hdmi_enable_clocks(1);
spin_lock_irqsave(&irqstatus_lock, flags);
work_pending = irqstatus;
if ((r & HDMI_DISCONNECT))
irqstatus = HDMI_DISCONNECT;
else
irqstatus = r;
spin_unlock_irqrestore(&irqstatus_lock, flags);
if (r && !work_pending) {
work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
if (work) {
INIT_WORK(work, hdmi_work_queue);
schedule_work(work);
} else {
printk(KERN_ERR "Cannot allocate memory to create work");
}
}
return IRQ_HANDLED;
}
static void hdmi_power_off(struct omap_dss_device *dssdev)
{
HDMI_W1_StopVideoFrame(HDMI_WP);
dispc_enable_digit_out(0);
hdmi_phy_off(HDMI_WP);
HDMI_W1_SetWaitPllPwrState(HDMI_WP, HDMI_PLLPWRCMD_ALLOFF);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
edid_set = false;
hdmi_enable_clocks(0);
hdmi_hot_plug_event_send(dssdev, 0);
/* reset to default */
}
static int hdmi_enable_display(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("ENTER hdmi_enable_display()\n");
mutex_lock(&hdmi.lock);
/* the tv overlay manager is shared*/
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
goto err;
}
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
r = -EINVAL;
goto err;
}
free_irq(OMAP44XX_IRQ_DSS_HDMI, NULL);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_writel(0x01180118, 0x4A100098);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_writel(0x01180118 , 0x4A10009C);
/* CONTROL_HDMI_TX_PHY */
omap_writel(0x10000000, 0x4A100610);
if (dssdev->platform_enable)
dssdev->platform_enable(dssdev);
r = hdmi_power_on(dssdev);
hdmi_hot_plug_event_send(dssdev, 1);
if (r) {
DSSERR("failed to power on device\n");
goto err;
}
r = request_irq(OMAP44XX_IRQ_DSS_HDMI, hdmi_irq_handler,
0, "OMAP HDMI", (void *)0);
err:
mutex_unlock(&hdmi.lock);
return r;
}
static int hdmi_enable_hpd(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("ENTER hdmi_enable_hpd()\n");
mutex_lock(&hdmi.lock);
/* the tv overlay manager is shared*/
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
goto err;
}
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
r = -EINVAL;
goto err;
}
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_writel(0x01180118, 0x4A100098);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_writel(0x01180118 , 0x4A10009C);
/* CONTROL_HDMI_TX_PHY */
omap_writel(0x10000000, 0x4A100610);
if (dssdev->platform_enable)
dssdev->platform_enable(dssdev);
hpd_mode = 1;
r = hdmi_min_enable();
if (r) {
DSSERR("failed to power on device\n");
goto err;
}
err:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_disable_display(struct omap_dss_device *dssdev)
{
DSSDBG("Enter hdmi_disable_display()\n");
mutex_lock(&hdmi.lock);
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
goto end;
if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) {
/* suspended is the same as disabled with venc */
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
goto end;
}
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
omap_dss_stop_device(dssdev);
hdmi_power_off(dssdev);
hdmi.code = 16;
hdmi.mode = 1 ; /*setting to default only in case of disable and not suspend*/
end:
mutex_unlock(&hdmi.lock);
}
static int hdmi_display_suspend(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("hdmi_display_suspend\n");
mutex_lock(&hdmi.lock);
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
goto end;
if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
goto end;
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
omap_dss_stop_device(dssdev);
hdmi_power_off(dssdev);
end:
mutex_unlock(&hdmi.lock);
return r;
}
static int hdmi_display_resume(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("hdmi_display_resume\n");
mutex_lock(&hdmi.lock);
/* the tv overlay manager is shared*/
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
goto err;
}
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
r = -EINVAL;
goto err;
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_writel(0x01180118, 0x4A100098);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_writel(0x01180118 , 0x4A10009C);
/* CONTROL_HDMI_TX_PHY */
omap_writel(0x10000000, 0x4A100610);
if (dssdev->platform_enable)
dssdev->platform_enable(dssdev);
r = hdmi_power_on(dssdev);
hdmi_hot_plug_event_send(dssdev, 1);
if (r) {
DSSERR("failed to power on device\n");
goto err;
}
err:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
static void hdmi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
DSSDBG("hdmi_set_timings\n");
dssdev->panel.timings = *timings;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
/* turn the hdmi off and on to get new timings to use */
hdmi_disable_display(dssdev);
hdmi_enable_display(dssdev);
}
}
static void hdmi_set_custom_edid_timing_code(struct omap_dss_device *dssdev, int code , int mode)
{
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
/* turn the hdmi off and on to get new timings to use */
hdmi_disable_display(dssdev);
hdmi.code = code;
hdmi.mode = mode;
custom_set = 1;
hdmi_enable_display(dssdev);
custom_set = 0;
}
}
static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
{
int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0;
int timing_vsync = 0, timing_hsync = 0;
struct omap_video_timings temp;
struct hdmi_cm cm = {-1};
DSSDBG("hdmi_get_code");
for (i = 0; i < 32; i++) {
temp = all_timings_direct[i];
if ((temp.pixel_clock == timing->pixel_clock) &&
(temp.x_res == timing->x_res) &&
(temp.y_res == timing->y_res)) {
temp_hsync = temp.hfp + temp.hsw + temp.hbp;
timing_hsync = timing->hfp + timing->hsw + timing->hbp;
temp_vsync = temp.vfp + temp.vsw + temp.vbp;
timing_vsync = timing->vfp + timing->vsw + timing->vbp;
printk("Temp_hsync = %d , temp_vsync = %d , \
timing_hsync = %d, timing_vsync = %d", \
temp_hsync, temp_hsync, timing_hsync, timing_vsync);
if ((temp_hsync == timing_hsync) && (temp_vsync == timing_vsync)) {
code = i;
cm.code = code_index[i];
if (code < 14)
cm.mode = 1;
else
cm.mode = 0;
DSSDBG("Hdmi_code = %d mode = %d\n", cm.code, cm.mode);
print_omap_video_timings(&temp);
break;
}
}
}
return cm;
}
static void hdmi_get_edid(struct omap_dss_device *dssdev)
{
u8 i = 0, flag = 0, mark = 0;
int count, offset, effective_addrs, current_descriptor_addrs = 0;
struct HDMI_EDID * edid_st = (struct HDMI_EDID *)edid;
struct image_format *img_format;
struct audio_format *aud_format;
struct deep_color *vsdb_format;
struct latency *lat;
struct omap_video_timings timings;
img_format = kzalloc(sizeof(*img_format), GFP_KERNEL);
aud_format = kzalloc(sizeof(*aud_format), GFP_KERNEL);
vsdb_format = kzalloc(sizeof(*vsdb_format), GFP_KERNEL);
lat = kzalloc(sizeof(*lat), GFP_KERNEL);
if (edid_set != 1) {
printk(KERN_WARNING "Display doesnt seem to be enabled invalid read\n");
if (HDMI_CORE_DDC_READEDID(HDMI_CORE_SYS, edid) != 0) {
printk(KERN_WARNING "HDMI failed to read E-EDID\n");
}
for (i = 0x00; i < 0x08; i++) {
if (edid[i] == header[i])
continue;
else {
flag = 1;
break;
}
}
if (flag == 0)
edid_set = 1;
}
mdelay(1000);
printk("\nHeader:\n");
for (i = 0x00; i < 0x08; i++)
printk("%02x ", edid[i]);
printk("\nVendor & Product:\n");
for (i = 0x08; i < 0x12; i++)
printk("%02x ", edid[i]);
printk("\nEDID Structure:\n");
for (i = 0x12; i < 0x14; i++)
printk("%02x ", edid[i]);
printk("\nBasic Display Parameter:\n");
for (i = 0x14; i < 0x19; i++)
printk("%02x ", edid[i]);
printk("\nColor Characteristics:\n");
for (i = 0x19; i < 0x23; i++)
printk("%02x ", edid[i]);
printk("\nEstablished timings:\n");
for (i = 0x23; i < 0x26; i++)
printk("%02x ", edid[i]);
printk("\nStandard timings:\n");
for (i = 0x26; i < 0x36; i++)
printk("%02x ", edid[i]);
for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) {
current_descriptor_addrs =
EDID_DESCRIPTOR_BLOCK0_ADDRESS +
count * EDID_TIMING_DESCRIPTOR_SIZE;
printk("Extension 0 Block %d", count);
get_edid_timing_info(&edid_st->DTD[count], &timings);
if (!dss_debug) {
dss_debug = 1;
mark = 1;
}
print_omap_video_timings(&timings);
if (mark)
dss_debug = 0;
}
if (edid[0x7e] != 0x00) {
offset = edid[EDID_DESCRIPTOR_BLOCK1_ADDRESS + 2];
printk("\n offset %x\n", offset);
if (offset != 0) {
effective_addrs = EDID_DESCRIPTOR_BLOCK1_ADDRESS
+ offset;
/*to determine the number of descriptor blocks */
for (count = 0;
count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR;
count++) {
current_descriptor_addrs = effective_addrs +
count * EDID_TIMING_DESCRIPTOR_SIZE;
printk("Extension 1 Block %d", count);
get_eedid_timing_info(current_descriptor_addrs, edid ,
&timings);
if (!dss_debug) {
dss_debug = 1;
mark = 1;
}
print_omap_video_timings(&timings);
if (mark)
dss_debug = 0;
}
}
}
hdmi_get_image_format(edid, img_format);
printk("%d audio length\n", img_format->length);
for (i = 0 ; i < img_format->length ; i++)
printk("%d %d pref code\n", img_format->fmt[i].pref, img_format->fmt[i].code);
hdmi_get_audio_format(edid, aud_format);
printk("%d audio length\n", aud_format->length);
for (i = 0 ; i < aud_format->length ; i++)
printk("%d %d format num_of_channels\n", aud_format->fmt[i].format,
aud_format->fmt[i].num_of_ch);
hdmi_deep_color_support_info(edid, vsdb_format);
printk("%d deep color bit 30 %d deep color 36 bit %d max tmds freq",
vsdb_format->bit_30, vsdb_format->bit_36, vsdb_format->max_tmds_freq);
hdmi_get_av_delay(edid, lat);
printk("%d vid_latency %d aud_latency %d interlaced vid latency"
"%d interlaced aud latency", lat->vid_latency, lat->aud_latency,
lat->int_vid_latency, lat->int_aud_latency);
printk("YUV supported %d", hdmi_tv_yuv_supported(edid));
kfree(img_format);
kfree(aud_format);
kfree(vsdb_format);
kfree(lat);
}
static int hdmi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
DSSDBG("hdmi_check_timings\n");
if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings)) == 0)
return 0;
return -EINVAL;
}
int hdmi_init_display(struct omap_dss_device *dssdev)
{
printk("init_display\n");
/* register HDMI specific sysfs files */
/* note: custom_edid_timing should perhaps be moved here too,
* instead of generic code? Or edid sysfs file should be moved
* to generic code.. either way they should be in same place..
*/
if (device_create_file(&dssdev->dev, &dev_attr_edid))
DSSERR("failed to create sysfs file\n");
if (device_create_file(&dssdev->dev, &dev_attr_yuv))
DSSERR("failed to create sysfs file\n");
return 0;
}
static int hdmi_read_edid(struct omap_video_timings *dp)
{
int r = 0, ret, code;
memset(edid, 0, HDMI_EDID_MAX_LENGTH);
if (!edid_set) {
ret = HDMI_CORE_DDC_READEDID(HDMI_CORE_SYS, edid);
}
if (ret != 0)
printk(KERN_WARNING "HDMI failed to read E-EDID\n");
else {
if (!memcmp(edid, header, sizeof(header))) {
/* search for timings of default resolution */
if (get_edid_timing_data((struct HDMI_EDID *) edid))
edid_set = true;
}
}
if (!edid_set) {
DSSDBG("fallback to VGA\n");
hdmi.code = 4; /*setting default value of 640 480 VGA*/
hdmi.mode = 0;
}
code = get_timings_index();
*dp = all_timings_direct[code];
DSSDBG(KERN_INFO"hdmi read EDID:\n");
print_omap_video_timings(dp);
return r;
}
/*------------------------------------------------------------------------------
| Function : get_edid_timing_data
+------------------------------------------------------------------------------
| Description : This function gets the resolution information from EDID
|
| Parameters : void
|
| Returns : void
+----------------------------------------------------------------------------*/
static int get_edid_timing_data(struct HDMI_EDID *edid)
{
u8 count, code, offset = 0, effective_addrs = 0, current_descriptor_addrs = 0;
struct hdmi_cm cm;
/* Seach block 0, there are 4 DTDs arranged in priority order */
for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) {
get_edid_timing_info(&edid->DTD[count], &edid_timings);
DSSDBG("Block0 [%d] timings:", count);
print_omap_video_timings(&edid_timings);
cm = hdmi_get_code(&edid_timings);
DSSDBG("Block0[%d] value matches code = %d , mode = %d",\
count, cm.code, cm.mode);
if (cm.code == -1)
continue;
else {
hdmi.code = cm.code;
hdmi.mode = cm.mode;
DSSDBG("code = %d , mode = %d", hdmi.code, hdmi.mode);
return 1;
}
}
if (edid->extension_edid != 0x00) {
offset = edid->offset_dtd;
if (offset != 0)
effective_addrs = EDID_DESCRIPTOR_BLOCK1_ADDRESS
+ offset;
for (count = 0; count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; count++) {
current_descriptor_addrs =
effective_addrs + count * EDID_TIMING_DESCRIPTOR_SIZE;
get_eedid_timing_info(current_descriptor_addrs, (u8 *)edid,\
&edid_timings);
cm = hdmi_get_code(&edid_timings);
DSSDBG("Block1[%d] value matches code = %d , mode = %d",\
count, cm.code, cm.mode);
if (cm.code == -1)
continue;
else {
hdmi.code = cm.code;
hdmi.mode = cm.mode;
DSSDBG("code = %d , mode = %d", hdmi.code, hdmi.mode);
return 1;
}
}
}
/*As last resort, check for best standard timing supported:*/
if (edid->timing_1 & 0x01) {
DSSDBG("800x600@60Hz\n");
hdmi.mode = 0;
hdmi.code = 9;
return 1;
}
if (edid->timing_2 & 0x08) {
DSSDBG("1024x768@60Hz\n");
hdmi.mode = 0;
hdmi.code = 16;
return 1;
}
hdmi.code = 4; /*setting default value of 640 480 VGA*/
hdmi.mode = 0;
code = code_vesa[hdmi.code];
edid_timings = all_timings_direct[code];
return 1;
}
bool is_hdmi_interlaced(void)
{
return hdmi.cfg.interlace;
}