android_kernel_cmhtcleo/drivers/video/msm/hdmi/transmitter.c
2010-08-27 11:19:57 +02:00

1020 lines
26 KiB
C

/*
* Copyright (C) 2009 HTC Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <mach/msm_hdmi.h>
#include <mach/msm_fb.h>
#include "include/fb-hdmi.h"
#include "include/sil902x.h"
#ifdef CONFIG_HTC_HEADSET_MGR
#include <mach/htc_headset_mgr.h>
#endif
#if 1
#define HDMI_DBG(s...) printk("[hdmi/tx]" s)
#else
#define HDMI_DBG(s...) do {} while (0)
#endif
#define HDMI_NAME "SiL902x-hdmi"
//#define HDMI_DEBUGFS
static struct class *hdmi_class;
enum {
ESTABLISHED_TIMING_OFFSET = 35,
LONG_DESCR_LEN = 18,
NUM_DETAILED_DESC = 4,
NUM_STANDARD_TIMING = 8,
};
#if 1
int hdmi_read(struct i2c_client *client, u8 cmd)
#else
#define hdmi_read(client, cmd) _hdmi_read(client, cmd, __func__)
int _hdmi_read(struct i2c_client *client, u8 cmd, const char *caller)
#endif
{
int ret = -EIO, retry = 10;
while (retry--) {
ret = i2c_smbus_read_byte_data(client, cmd);
if (ret >= 0)
break;
msleep(1);
}
/*
if (retry!=9)
HDMI_DBG("%s, retry=%d, caller=%s\n", __func__, 10-retry,
caller);
*/
return ret;
}
int tpi_readb(struct hdmi_info *hdmi, u8 reg)
{
int i, ret = -EIO, retrial = 10, timeout = 1;
struct i2c_client *client = hdmi->client;
for (i = 1 ; i < retrial ; i++) {
ret = i2c_smbus_read_byte_data(client, reg);
if (ret >= 0)
break;
msleep(timeout++);
}
return ret;
}
int tpi_readb_oneshoot(struct hdmi_info *hdmi, u8 reg)
{
return i2c_smbus_read_byte_data(hdmi->client, reg);
}
#if 1
int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val)
#else
#define hdmi_write_byte(client, reg, val) \
_hdmi_write_byte(client, reg, val, __func__)
int _hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val, const char *caller)
#endif
{
int ret = -EIO, retry = 10;
while (retry--) {
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret == 0)
break;
msleep(1);
}
/*
if (retry!=9) HDMI_DBG("%s, retry=%d, caller=%s\n", __func__,
10 - retry, caller);
*/
return ret;
}
int tpi_writeb(struct hdmi_info *hdmi, u8 reg, u8 val)
{
int i, ret = -EIO, retrial = 10, timeout = 1;
struct i2c_client *client = hdmi->client;
for (i = 1 ; i < retrial ; i++) {
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret == 0)
break;
msleep(timeout);
}
return ret;
}
int tpi_writeb_oneshot(struct hdmi_info *hdmi, u8 reg, u8 val)
{
return i2c_smbus_write_byte_data(hdmi->client, reg, val);
}
int hdmi_enable_int(struct i2c_client *client)
{
u8 data;
HDMI_DBG("%s\n", __func__);
data = hdmi_read(client, HDMI_INT_EN);
return hdmi_write_byte(client, HDMI_INT_EN, data | 0x01);
}
int hdmi_disable_int(struct i2c_client *client)
{
u8 data;
HDMI_DBG("%s\n", __func__);
data = hdmi_read(client, HDMI_INT_EN);
return hdmi_write_byte(client, HDMI_INT_EN, data & 0xfe);
}
/*
* Tx is brought to low-power state, off audio codec.
* i2c alive. Still be able to response to INT.
*/
int hdmi_standby(struct hdmi_info *hdmi)
{
u8 data;
int ret;
struct i2c_client *client = hdmi->client;
HDMI_DBG("%s\n", __func__);
#if 0
/* D2 sleep mode */
data = hdmi_read(client, HDMI_POWER);
return hdmi_write_byte(client, HDMI_POWER, (data & 0xfc) | 0x02);
#else
if (SLEEP == hdmi->sleeping)
return 0;
/* D3 sleep mode */
hdmi->sleeping = SLEEP;
hdmi->cable_connected = false;
data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) | 1);
data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) & ~2);
HDMI_DBG("%s: INT_EN=%02x\n", __func__, hdmi_read(client, 0x3c));
data = hdmi_read(client, HDMI_POWER);
data |= 4;
ret = hdmi_write_byte(client, HDMI_POWER, data );
if (ret)
dev_err(&client->dev,
"error on entering D3 sleep mode: into cold mode\n");
#if 0
ret = hdmi_write_byte(client, HDMI_POWER, 7);
#else
tpi_writeb_oneshot(hdmi, HDMI_POWER, 7);
#endif
/*
if (ret)
dev_err(&client->dev,
"error on entering D3 sleep mode: set D3 mode\n");
*/
#endif
return ret;
}
int hdmi_wakeup(struct hdmi_info *hdmi)
{
int err = -EIO;
int ret;
u8 data;
struct i2c_client *client = hdmi->client;
HDMI_DBG("%s\n", __func__);
#if 0
data = hdmi_read(client, HDMI_POWER);
err = hdmi_write_byte(client, HDMI_POWER, data & 0xfc);
if (err)
goto exit;
#else
/* Exiting D3 sleep mode */
ret = hdmi_write_byte(client, 0xc7, 0);
if (ret)
dev_err(&client->dev,
"error on exiting D3 sleep mode: 0xc7=0\n");
data = hdmi_read(client, HDMI_POWER);
data = ( data & 0xfc ) ;
ret = hdmi_write_byte(client, HDMI_POWER, data );
if (ret)
dev_err(&client->dev,
"error on exiting D3 sleep mode: 0x1e=0\n");
/* Enable insternal TMDS source termination */
hdmi_write_byte(client, 0xbc, 0x01);
hdmi_write_byte(client, 0xbd, 0x82);
data = hdmi_read(client, 0xbe);
hdmi_write_byte(client, 0xbe, data | 0x01);
hdmi->sleeping = AWAKE;
#endif
/*
data = hdmi_read(client, HDMI_SYS_CTL);
dev_info(&client->dev, "%s, HDMI_SYS_CTL=0x%x\n", __func__, data);
err = hdmi_write_byte(client, HDMI_SYS_CTL, 0x01);
if (err)
goto exit;
*/
return 0;
exit:
dev_err(&client->dev, "%s: fail, err = %d\n", __func__, err);
return err;
}
static int
hdmi_check_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var)
{
if (((var->xres == 1280) && (var->yres == 720)) ||
((var->xres == 800) && (var->yres == 600)) ||
((var->xres == 720) && (var->yres == 576)) ||
((var->xres == 720) && (var->yres == 480)) ||
((var->xres == 640) && (var->yres == 480))) {
dev_info(&hdmi_device->dev, "resolution check successfully\n");
/* check pixel clock also */
return 0;
}
return -EINVAL;
}
static struct msm_lcdc_timing hdmi_lcdc_timing[] = {
[hd_720p] = {
.clk_rate = 74250000,
.hsync_pulse_width = 40,
.hsync_back_porch = 220,
.hsync_front_porch = 110,
.hsync_skew = 0,
.vsync_pulse_width = 5,
.vsync_back_porch = 20,
.vsync_front_porch = 5,
.vsync_act_low = 0,
.hsync_act_low = 0,
.den_act_low = 0,
},
[svga] = {
.clk_rate = 40000000,
.hsync_pulse_width = 128,
.hsync_back_porch = 88,
.hsync_front_porch = 40,
.hsync_skew = 0,
.vsync_pulse_width = 4,
.vsync_back_porch = 23,
.vsync_front_porch = 1,
.vsync_act_low = 0,
.hsync_act_low = 0,
.den_act_low = 0,
},
[pal] = {
.clk_rate = 27027000,
.hsync_pulse_width = 64,
.hsync_back_porch = 68,
.hsync_front_porch = 12,
.hsync_skew = 0,
.vsync_pulse_width = 5,
.vsync_back_porch = 39,
.vsync_front_porch = 5,
.vsync_act_low = 1,
.hsync_act_low = 1,
.den_act_low = 0,
},
[edtv] = {
.clk_rate = 27027000,
.hsync_pulse_width = 62,
.hsync_back_porch = 60,
.hsync_front_porch = 16,
.hsync_skew = 0,
.vsync_pulse_width = 6,
.vsync_back_porch = 30,
.vsync_front_porch = 9,
#if 1
.vsync_act_low = 1,
.hsync_act_low = 1,
#else
.vsync_act_low = 0,
.hsync_act_low = 0,
#endif
.den_act_low = 0,
},
[vga] = {
.clk_rate = 25175000,
.hsync_pulse_width = 96,
.hsync_back_porch = 48,
.hsync_front_porch = 16,
.hsync_skew = 0,
.vsync_pulse_width = 2,
//.vsync_pulse_width = 3,
.vsync_back_porch = 33,
.vsync_front_porch = 10,
.vsync_act_low = 1,
.hsync_act_low = 1,
.den_act_low = 0,
},
};
static struct msm_lcdc_timing *
hdmi_set_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var)
{
struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info,
hdmi_dev);
printk(KERN_DEBUG "%s, info->res=%d=(%d x %d)\n",
__func__, info->res, var->xres, var->yres);
if ((var->xres == 1280) && (var->yres == 720))
info->res = hd_720p;
else if ((var->xres == 800) && (var->yres == 600))
info->res = svga;
else if ((var->xres == 720) && (var->yres == 576))
info->res = pal;
else if ((var->xres == 720) && (var->yres == 480))
info->res = edtv;
else if ((var->xres == 640) && (var->yres == 480))
info->res = vga;
else
return ERR_PTR(-EINVAL);
/*
if (info->user_playing)
avc_send_avi_info_frames(info);
*/
return &hdmi_lcdc_timing[info->res];
}
static int hdmi_get_cable_state(struct hdmi_device *hdmi_device, int *connect)
{
#if 0
struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info,
hdmi_dev);
struct i2c_client *client = info->client;
u8 status;
*connect = 0;
status = hdmi_read(client, HDMI_INT_STAT);
if (status & HOT_PLUG_STATE)
*connect = 1;
#else
struct hdmi_info *hdmi =
container_of(hdmi_device, struct hdmi_info, hdmi_dev);
*connect = hdmi->cable_connected;
#endif
HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" );
return 0;
}
static int
hdmi_get_established_timing(struct hdmi_device *hdmi_device, u8 *byte)
{
struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info,
hdmi_dev);
HDMI_DBG("%s\n", __func__);
memcpy(byte, &info->edid_buf[ESTABLISHED_TIMING_OFFSET], 3);
return 0;
}
#if 0
// FIXME: remove the parameter: data
static u8 hdmi_request_ddc(struct i2c_client *client, int request, u8 data)
{
int retry = 10;
HDMI_DBG("%s, request=%d\n", __func__, request);
if (request) {
data = hdmi_read(client, HDMI_SYS_CTL);
hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x04));
msleep(1);
hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x06));
msleep(1);
} else {
hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9));
hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9));
/* make sure bit [2:1] = 00 */
data = hdmi_read(client, HDMI_SYS_CTL);
while ((data & 0x03) & retry--)
msleep(1);
}
return data;
}
#else
// FIXME: remove the static varible. if caller need to presev the reg,
// it should use hdmi_read() first.
static u8 hdmi_request_ddc(struct i2c_client *client, int request)
{
int retry = 10;
static u8 val = 0;
u8 tmp;
HDMI_DBG("%s, request=%d\n", __func__, request);
if (request) {
val = hdmi_read(client, HDMI_SYS_CTL);
hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x04));
msleep(1);
hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x06));
msleep(1);
} else {
do {
hdmi_write_byte(client, HDMI_SYS_CTL, (val & 0xf9));
tmp = hdmi_read(client, HDMI_SYS_CTL);
msleep(1);
/* make sure bit [2:1] = 00 */
} while ((tmp & 0x06) & retry--) ;
}
return 0;
}
#endif
static uint8_t timing_id[][3] = {
{ 0x81, 0xc0, 1 << 6 }, /* 1280x720 */
{ 0x3b, 0x80, 1 << 5 }, /* 720x576 */
{ 0x3b, 0x00, 1 << 4 }, /* 720x480 */
};
//----------------------------------------------------------------------
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_info *hdmi = (struct hdmi_info *) data;
HDMI_DBG("%s\n", __func__);
disable_irq_nosync(hdmi->client->irq);
hdmi->isr_enabled = false;
hdmi->first = true;
if (!hdmi->cable_connected) {
hdmi->timer.expires = jiffies + INTERVAL_HDCP_POLLING;
add_timer(&hdmi->timer);
}
return IRQ_HANDLED;
}
/* ---------------------------------------------------------------- */
extern bool hdmifb_suspending;
static int hdmi_panel_blank(struct msm_lcdc_panel_ops *ops)
{
struct hdmi_info *info = container_of(ops, struct hdmi_info,
hdmi_lcdc_ops);
HDMI_DBG("%s\n", __func__);
info->user_playing = false;
info->video_streaming= false;
#if 0
/* if called from suspending */
if (hdmifb_suspending) {
/* to avoid timer been revoked after standby */
HDMI_DBG("suspending=true, disable timer\n");
cancel_work_sync(&info->polling_work);
del_timer(&info->timer);
HDMI_DBG("%s\n", __func__);
mutex_lock(&info->lock);
hdmi_standby(info);
info->power(2);
mutex_unlock(&info->lock);
}
#endif
return 0;
}
static int hdmi_panel_unblank(struct msm_lcdc_panel_ops *ops)
{
struct hdmi_info *info = container_of(ops, struct hdmi_info,
hdmi_lcdc_ops);
struct i2c_client *client = info->client;
HDMI_DBG("%s\n", __func__);
clk_set_rate(info->ebi1_clk, 120000000);
if (info->suspending == true) {
HDMI_DBG("%s :actived before panel_init\n", __func__);
msleep(500);
}
info->user_playing = true;
return 0;
}
static int hdmi_panel_init(struct msm_lcdc_panel_ops *ops)
{
u8 conn;
struct hdmi_info *hd = container_of(ops, struct hdmi_info,
hdmi_lcdc_ops);
struct i2c_client *client = hd->client;
HDMI_DBG("%s\n", __func__);
if (hd->hdmi_gpio_on)
hd->hdmi_gpio_on();
/* Turn-on 5V to ensure hot-plug detection */
hd->power(5);
#if 0
/* For D2 sleep mode */
hd->power(1);
ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00);
if (ret < 0)
goto fail;
hdmi_disable_int(client);
data = hdmi_read(client, HDMI_POWER);
if (data & 0xfc) {
dev_info(&client->dev, "power state = %d\n", data & 0xfc);
} else {
dev_info(&client->dev, "bring HDMI back\n");
hdmi_enable_int(client);
HDMI_DBG("hotplug state=%d\n", hd->cable_connected);
}
#else
if (hd->polling) {
mutex_lock(&hd->lock);
hd->power(3);
hdmi_wakeup(hd);
hd->first = true;
mod_timer(&hd->timer, jiffies + INTERVAL_HDCP_POLLING);
conn = hdmi_read(client, HDMI_INT_STAT) & HOT_PLUG_STATE;
tpi_init(hd);
#ifdef CONFIG_HTC_HEADSET_MGR
switch_send_event(BIT_HDMI_AUDIO, conn);
#endif
mutex_unlock(&hd->lock);
}
hd->suspending = false;
#endif
return 0;
/*
fail:
return ret;
*/
}
static int hdmi_panel_uninit(struct msm_lcdc_panel_ops *ops)
{
struct hdmi_info *info = container_of(ops, struct hdmi_info,
hdmi_lcdc_ops);
HDMI_DBG("%s\n", __func__);
if (info->hdmi_gpio_off)
info->hdmi_gpio_off();
#if 0
/* For D2 sleep mode */
info->power(0);
#endif
if (hdmifb_suspending) {
/* to avoid timer been revoked after standby */
HDMI_DBG("suspending=true, disable timer\n");
cancel_work_sync(&info->polling_work);
del_timer(&info->timer);
flush_scheduled_work();
HDMI_DBG("%s\n", __func__);
mutex_lock(&info->lock);
hdmi_standby(info);
info->power(4);
mutex_unlock(&info->lock);
}
info->suspending = true;
return 0;
}
int avc_set_video_parm(struct hdmi_info *hdmi);
int avc_set_blank_screen(struct hdmi_info *hdmi);
void hdmi_pre_change(struct hdmi_info *hdmi) {
if (hdmi->sleeping == SLEEP)
return;
mutex_lock(&hdmi->polling_lock);
HDMI_DBG("%s\n", __func__);
avc_set_blank_screen(hdmi);
hdcp_off(hdmi);
mutex_unlock(&hdmi->polling_lock);
}
void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var)
{
u8 data[4];
int i, ret, retry = 10;
struct msm_lcdc_timing *timing;
unsigned h_total, v_total, h_curr, v_curr;
if (info->sleeping == SLEEP)
return;
mutex_lock(&info->polling_lock);
HDMI_DBG("%s\n", __func__);
timing = &hdmi_lcdc_timing[info->res];
h_total = var->xres + timing->hsync_pulse_width +
timing->hsync_back_porch + timing->hsync_front_porch;
v_total = var->yres + timing->vsync_pulse_width +
timing->vsync_back_porch + timing->vsync_front_porch;
/* Waiting for video stream until steady */
for (i = 0; i < retry ; i++) {
/* TODO: error handling. */
/* Read current horizontal/vertical info of video */
data[0] = hdmi_read(info->client, 0x6a);
data[1] = hdmi_read(info->client, 0x6b);
data[2] = hdmi_read(info->client, 0x6c);
data[3] = hdmi_read(info->client, 0x6d);
h_curr = ((int)data[1]) << 8 | data[0];
v_curr = ((int)data[3]) << 8 | data[2];
if (h_curr == h_total && v_curr == v_total)
break;
msleep(17);
}
avc_set_video_parm(info);
avc_send_avi_info_frames(info);
info->video_streaming = true;
mutex_unlock(&info->polling_lock);
}
static struct msm_fb_data hdmi_lcdc_fb_data = {
#if 1
.xres = 1280,
.yres = 720,
#else
.xres = 720,
.yres = 480,
#endif
.width = 94,
.height = 57,
.output_format = 0,
};
static struct msm_lcdc_platform_data hdmi_lcdc_platform_data = {
.timing = &hdmi_lcdc_timing[hd_720p],
.fb_id = 0,
.fb_data = &hdmi_lcdc_fb_data,
};
static struct platform_device hdmi_lcdc_device = {
.name = "msm_mdp_hdmi",
.id = -1,
};
int register_hdmi_client(struct class_interface *interface)
{
if (!hdmi_class) {
pr_err("mdp: no hdmi_class when register hdmi client\n");
return -ENODEV;
}
interface->class = hdmi_class;
return class_interface_register(interface);
}
#if defined(OLD_DEBUGFS)
static spinlock_t hdmi_dbgfs_lock;
ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t hdmi_edid_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
static char line[80], buffer[80*8*4];
static char hextab[] = "0123456789abcdef";
int i, j, n = 0, v, len, offset, line_size;
unsigned long irq_flags;
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
len = ((int)hdmi->edid_buf[0x7e]+1) * 128;
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
memset(line, ' ', 79);
line[79] = '\0';
offset = strlen("0000 | ");
line_size = offset + 3 * 16 + 1;
for (i = 0; i < len / 16 ; i++) {
scnprintf(line, offset + 1, "%04x | ", (i << 4));
for (j = 0; j < 16 ; j++) {
v = hdmi->edid_buf[i * 16 + j];
line[offset + j * 3] = hextab[v / 16];
line[offset + j * 3 + 1] = hextab[v % 16];
}
line[line_size - 1] = '\n';
strncpy(buffer + i * line_size, line, line_size);
n += line_size;
}
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
#if 0
static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long v;
unsigned long irq_flags;
char buff[80];
struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data;
if (count >= sizeof(buff))
return -EINVAL;
if (copy_from_user(&buff, buf, 80))
return -EFAULT;
buff[count] = 0;
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
strict_strtoul(buff, 16, &v);
buff[strlen(buff)]=0;
writel(v, tvenc_base+trd->offset);
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
return count;
}
#endif
static ssize_t hdmi_cable_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n;
char buffer[80];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
n = scnprintf(buffer, 80, "%d\n", hdmi->cable_connected);
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t hdmi_sleeping_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n;
char buffer[80];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
n = scnprintf(buffer, 80, "%d\n", hdmi->sleeping);
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
int hdmifb_get_mode(void);
static ssize_t hdmi_fb_mode_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n;
char buffer[80];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
n = scnprintf(buffer, 80, "%d\n", hdmifb_get_mode());
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t hdmi_isr_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n;
char buffer[80];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
n = scnprintf(buffer, 80, "%d\n", hdmi->isr_enabled);
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static struct file_operations hdmi_fops[] = {
{
.open = hdmi_dbgfs_open,
.read = hdmi_edid_read,
},
{ /* cable*/
.open = hdmi_dbgfs_open,
.read = hdmi_cable_read,
},
{ /* sleeping */
.open = hdmi_dbgfs_open,
.read = hdmi_sleeping_read,
},
{ /* fb_mode */
.open = hdmi_dbgfs_open,
.read = hdmi_fb_mode_read,
},
{ /* isr_enabled */
.open = hdmi_dbgfs_open,
.read = hdmi_isr_read,
},
};
static int hdmi_debugfs_init(struct hdmi_info *hdmi)
{
struct dentry *dent_hdmi;
int ret;
spin_lock_init(&hdmi_dbgfs_lock);
dent_hdmi = debugfs_create_dir("hdmi", 0);
if (IS_ERR(dent_hdmi))
return PTR_ERR(dent_hdmi);
debugfs_create_file("edid", 0644, dent_hdmi, hdmi, &hdmi_fops[0]);
debugfs_create_file("cable", 0444, dent_hdmi, hdmi, &hdmi_fops[1]);
debugfs_create_file("sleeping", 0444, dent_hdmi, hdmi, &hdmi_fops[2]);
debugfs_create_file("fb_mode", 0444, dent_hdmi, hdmi, &hdmi_fops[3]);
debugfs_create_file("isr", 0444, dent_hdmi, hdmi, &hdmi_fops[4]);
return 0;
}
#endif
static int __init hdmi_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct hdmi_info *hd;
struct hdmi_platform_data *pdata;
int ret = -EIO;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "No supported I2C func\n");
ret = -ENOTSUPP;
goto exit;
}
hd = kzalloc(sizeof(*hd), GFP_KERNEL);
if (hd == NULL) {
ret = -ENOMEM;
goto exit;
}
hd->client = client;
i2c_set_clientdata(client, hd);
mutex_init(&hd->lock);
mutex_init(&hd->lock2);
mutex_init(&hd->polling_lock);
hd->ebi1_clk = clk_get(NULL, "ebi1_clk");
if (IS_ERR(hd->ebi1_clk)) {
dev_err(&client->dev, "get ebi1 clk fail\n");
goto fail_get_ebi1;
}
pdata = client->dev.platform_data;
if (unlikely(!pdata) || unlikely(!pdata->power)) {
dev_err(&client->dev, "No platform data\n");
ret = -ENXIO;
goto fail_power;
} else {
if (pdata->hdmi_gpio_on)
pdata->hdmi_gpio_on();
hd->power = pdata->power;
ret = hd->power(1);
if (ret) {
dev_err(&client->dev, "hdmi power on failed\n");
ret = -EIO;
goto fail_power;
}
}
ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00);
if (ret < 0) {
ret = -EIO;
goto fail_hdmi_init;
}
ret = hdmi_read(client, HDMI_IDENTIFY);
if (ret < 0) {
ret = -EIO;
goto fail_hdmi_init;
} else if (ret != 0xb0) {
dev_err(&client->dev, "can not recognize, 0x%x\n", ret);
ret = -ENXIO;
goto fail_hdmi_init;
}
hdmi_disable_int(client);
hd->user_playing = false;
tpi_prepare(hd);
ret = request_irq(client->irq, hdmi_irq_handler, IRQF_TRIGGER_LOW,
client->name, hd);
if (ret) {
/* HDMI did not care if interrupt fail */
dev_err(&client->dev, "request irq fail, err = %d\n", ret);
} else {
ret = hdmi_enable_int(client);
if (ret) {
free_irq(client->irq, hd);
ret = -ENOTSUPP;
}
}
dev_info(&client->dev, "hdmi is on line with irq %s\n",
ret ? "Disabled" : "Enabled");
/* set up "panel" */
hd->hdmi_lcdc_ops.init = hdmi_panel_init;
hd->hdmi_lcdc_ops.uninit = hdmi_panel_uninit;
hd->hdmi_lcdc_ops.blank = hdmi_panel_blank;
hd->hdmi_lcdc_ops.unblank = hdmi_panel_unblank;
hd->hdmi_gpio_on = pdata->hdmi_gpio_on;
hd->hdmi_gpio_off = pdata->hdmi_gpio_off;
hdmi_lcdc_platform_data.panel_ops = &hd->hdmi_lcdc_ops;
hdmi_lcdc_platform_data.fb_resource = &pdata->hdmi_res;
hdmi_lcdc_device.dev.platform_data = &hdmi_lcdc_platform_data;
ret = platform_device_register(&hdmi_lcdc_device);
if (ret)
goto fail_hdmi_init;
hd->hdmi_dev.check_res = hdmi_check_res;
hd->hdmi_dev.set_res = hdmi_set_res;
hd->hdmi_dev.get_cable_state = hdmi_get_cable_state;
hd->hdmi_dev.get_establish_timing = hdmi_get_established_timing;
hd->hdmi_dev.dev.parent = &client->dev;
hd->hdmi_dev.dev.class = hdmi_class;
//snprintf(hd->hdmi_dev.dev.bus_id, BUS_ID_SIZE, "hdmi%d", 0);
dev_set_name(&hd->hdmi_dev.dev, "hdmi%d", 0);
ret = device_register(&hd->hdmi_dev.dev);
if (ret)
dev_err(&client->dev, "device register fail\n");
#if defined(HDMI_DEBUGFS)
hdmi_debugfs_init(hd);
#endif
/* check any pending interrupt */
hdmi_irq_handler(client->irq, hd);
return 0;
fail_hdmi_init:
fail_get_ebi1:
clk_put(hd->ebi1_clk);
fail_power:
kfree(hd);
exit:
dev_err(&client->dev, "%s fail, err = %d\n", __func__, ret);
return ret;
}
/* -------------------------------------------------------------------- */
static const struct i2c_device_id hdmi_id[] = {
{HDMI_NAME, 0},
{ }
};
static struct i2c_driver hdmi_driver = {
.probe = hdmi_probe,
/*.remove = hdmi_remove,*/
.id_table = hdmi_id,
.driver = {
.name = HDMI_NAME,
},
};
static int __init hdmi_init(void)
{
hdmi_class = class_create(THIS_MODULE, "msm_hdmi");
if (IS_ERR(hdmi_class)) {
printk(KERN_ERR "Error creating hdmi class\n");
return PTR_ERR(hdmi_class);
}
return i2c_add_driver(&hdmi_driver);
}
static void __exit hdmi_exit(void)
{
i2c_del_driver(&hdmi_driver);
}
module_init(hdmi_init);
module_exit(hdmi_exit);
MODULE_DESCRIPTION("Sil902x hdmi driver");
MODULE_LICENSE("GPL");