mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 11:50:43 +09:00
rk30: support hdmi.
This commit is contained in:
@@ -782,6 +782,28 @@ static struct platform_device device_lcdc1 = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HDMI_RK30
|
||||
static struct resource resource_hdmi[] = {
|
||||
[0] = {
|
||||
.start = RK30_HDMI_PHYS,
|
||||
.end = RK30_HDMI_PHYS + RK30_HDMI_SIZE - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = IRQ_HDMI,
|
||||
.end = IRQ_HDMI,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device device_hdmi = {
|
||||
.name = "rk30-hdmi",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(resource_hdmi),
|
||||
.resource = resource_hdmi,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RGA_RK30
|
||||
static struct resource resource_rga[] = {
|
||||
[0] = {
|
||||
@@ -1089,6 +1111,9 @@ static int __init rk30_init_devices(void)
|
||||
#ifdef CONFIG_LCDC1_RK30
|
||||
platform_device_register(&device_lcdc1);
|
||||
#endif
|
||||
#ifdef CONFIG_HDMI_RK30
|
||||
platform_device_register(&device_hdmi);
|
||||
#endif
|
||||
#ifdef CONFIG_ADC_RK30
|
||||
platform_device_register(&device_adc);
|
||||
#endif
|
||||
|
||||
@@ -27,4 +27,5 @@ config LCDC1_RK30
|
||||
help
|
||||
Support rk30 lcdc1 if you say y here
|
||||
|
||||
source "drivers/video/rockchip/hdmi/Kconfig"
|
||||
source "drivers/video/rockchip/rga/Kconfig"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
obj-$(CONFIG_FB_ROCKCHIP) += rk_fb.o rkfb_sysfs.o
|
||||
obj-$(CONFIG_LCDC_RK30) += chips/rk30_lcdc.o
|
||||
obj-$(CONFIG_RGA_RK30) += rga/
|
||||
obj-$(CONFIG_HDMI_RK30) += hdmi/
|
||||
|
||||
7
drivers/video/rockchip/hdmi/Kconfig
Normal file
7
drivers/video/rockchip/hdmi/Kconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
config HDMI_RK30
|
||||
bool "hdmi support"
|
||||
depends on LCDC_RK30
|
||||
# default y
|
||||
help
|
||||
Support rk30 hdmi if you say y here
|
||||
|
||||
1
drivers/video/rockchip/hdmi/Makefile
Executable file
1
drivers/video/rockchip/hdmi/Makefile
Executable file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi.o
|
||||
196
drivers/video/rockchip/hdmi/rk30_hdmi.c
Executable file
196
drivers/video/rockchip/hdmi/rk30_hdmi.c
Executable file
@@ -0,0 +1,196 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <mach/board.h>
|
||||
#include <mach/io.h>
|
||||
#include <mach/gpio.h>
|
||||
#include <mach/iomux.h>
|
||||
#include "rk30_hdmi.h"
|
||||
#include "rk30_hdmi_hw.h"
|
||||
|
||||
struct hdmi *hdmi = NULL;
|
||||
|
||||
extern irqreturn_t hdmi_irq(int irq, void *priv);
|
||||
extern void hdmi_work(struct work_struct *work);
|
||||
extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(int id);
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
static void hdmi_early_suspend(struct early_suspend *h)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "hdmi enter early suspend\n");
|
||||
disable_irq(hdmi->irq);
|
||||
if(hdmi->hotplug)
|
||||
hdmi_sys_remove();
|
||||
return;
|
||||
}
|
||||
|
||||
static void hdmi_early_resume(struct early_suspend *h)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");
|
||||
enable_irq(hdmi->irq);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static inline void hdmi_io_remap(void)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
// Remap HDMI IO Pin
|
||||
rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA);
|
||||
rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL);
|
||||
rk30_mux_api_set(GPIO0A0_HDMIHOTPLUGIN_NAME, GPIO0A_HDMI_HOT_PLUG_IN);
|
||||
|
||||
// Select LCDC0 as video source and enabled.
|
||||
value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30);
|
||||
writel(value, GRF_SOC_CON0 + RK30_GRF_BASE);
|
||||
|
||||
// internal hclk = hdmi_hclk/32
|
||||
HDMIWrReg(0x800, 19);
|
||||
|
||||
hdmi->lcdc = rk_get_lcdc_drv(HDMI_SOURCE_DEFAULT);
|
||||
}
|
||||
|
||||
static int __devinit rk30_hdmi_probe (struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *res;
|
||||
struct resource *mem;
|
||||
|
||||
hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL);
|
||||
if(!hdmi)
|
||||
{
|
||||
dev_err(&pdev->dev, ">>rk30 lcdc inf kmalloc fail!");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(hdmi, 0, sizeof(struct hdmi));
|
||||
hdmi->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, hdmi);
|
||||
|
||||
hdmi->hclk = clk_get(NULL,"hclk_hdmi");
|
||||
if(IS_ERR(hdmi->hclk))
|
||||
{
|
||||
dev_err(hdmi->dev, "Unable to get hdmi hclk\n");
|
||||
ret = -ENXIO;
|
||||
goto err0;
|
||||
}
|
||||
clk_enable(hdmi->hclk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(hdmi->dev, "Unable to get register resource\n");
|
||||
ret = -ENXIO;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);
|
||||
if (!mem)
|
||||
{
|
||||
dev_err(hdmi->dev, "failed to request mem region for hdmi\n");
|
||||
ret = -ENOENT;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
|
||||
hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1);
|
||||
if (!hdmi->regbase) {
|
||||
dev_err(hdmi->dev, "cannot ioremap registers\n");
|
||||
ret = -ENXIO;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hdmi_io_remap();
|
||||
|
||||
hdmi_sys_init();
|
||||
|
||||
hdmi->workqueue = create_singlethread_workqueue("hdmi");
|
||||
INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work);
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
hdmi->early_suspend.suspend = hdmi_early_suspend;
|
||||
hdmi->early_suspend.resume = hdmi_early_resume;
|
||||
hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1;
|
||||
register_early_suspend(&hdmi->early_suspend);
|
||||
#endif
|
||||
|
||||
/* get the IRQ */
|
||||
hdmi->irq = platform_get_irq(pdev, 0);
|
||||
if(hdmi->irq <= 0) {
|
||||
dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq);
|
||||
ret = -ENXIO;
|
||||
goto err2;
|
||||
}
|
||||
hdmi_dbg(hdmi->dev, "[%s] hdmi irq is 0x%x\n", __FUNCTION__, hdmi->irq);
|
||||
/* request the IRQ */
|
||||
ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi);
|
||||
if (ret)
|
||||
{
|
||||
dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n");
|
||||
return 0;
|
||||
err2:
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
unregister_early_suspend(&hdmi->early_suspend);
|
||||
#endif
|
||||
iounmap((void*)hdmi->regbase);
|
||||
err1:
|
||||
release_mem_region(res->start,(res->end - res->start) + 1);
|
||||
clk_disable(hdmi->hclk);
|
||||
err0:
|
||||
kfree(hdmi);
|
||||
hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit rk30_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk30_hdmi_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static struct platform_driver rk30_hdmi_driver = {
|
||||
.probe = rk30_hdmi_probe,
|
||||
.remove = __devexit_p(rk30_hdmi_remove),
|
||||
.driver = {
|
||||
.name = "rk30-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.shutdown = rk30_hdmi_shutdown,
|
||||
};
|
||||
|
||||
static int __init rk30_hdmi_init(void)
|
||||
{
|
||||
return platform_driver_register(&rk30_hdmi_driver);
|
||||
}
|
||||
|
||||
static void __exit rk30_hdmi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rk30_hdmi_driver);
|
||||
}
|
||||
|
||||
|
||||
fs_initcall(rk30_hdmi_init);
|
||||
//module_init(rk30_hdmi_init);
|
||||
module_exit(rk30_hdmi_exit);
|
||||
73
drivers/video/rockchip/hdmi/rk30_hdmi.h
Executable file
73
drivers/video/rockchip/hdmi/rk30_hdmi.h
Executable file
@@ -0,0 +1,73 @@
|
||||
#ifndef __RK30_HDMI_H__
|
||||
#define __RK30_HDMI_H__
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/display-sys.h>
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
#include <linux/earlysuspend.h>
|
||||
#endif
|
||||
|
||||
#include "../../display/screen/screen.h"
|
||||
#include <linux/rk_fb.h>
|
||||
#include "rk_hdmi.h"
|
||||
|
||||
// HDMI video source
|
||||
enum {
|
||||
HDMI_SOURCE_LCDC0 = 0,
|
||||
HDMI_SOURCE_LCDC1
|
||||
};
|
||||
|
||||
#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1
|
||||
|
||||
/* default HDMI output video mode */
|
||||
#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz
|
||||
#define HDMI_AUDIO_DEFAULT_CHANNEL 2
|
||||
#define HDMI_AUDIO_DEFAULT_RATE HDMI_AUDIO_FS_44100
|
||||
#define HDMI_AUDIO_DEFAULT_WORD_LENGTH HDMI_AUDIO_WORD_LENGTH_16bit
|
||||
|
||||
struct hdmi {
|
||||
struct device *dev;
|
||||
struct clk *hclk; //HDMI AHP clk
|
||||
int regbase;
|
||||
int irq;
|
||||
struct rk_lcdc_device_driver *lcdc;
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct delayed_work delay_work;
|
||||
|
||||
int wait;
|
||||
struct completion complete;
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
struct early_suspend early_suspend;
|
||||
#endif
|
||||
|
||||
struct hdmi_edid edid;
|
||||
int enable; // Enable HDMI output or not
|
||||
int vic; // HDMI output video mode code
|
||||
struct hdmi_audio audio; // HDMI output audio type.
|
||||
|
||||
int pwr_mode; // power mode
|
||||
int hotplug; // hot plug status
|
||||
int state; // hdmi state machine status
|
||||
int autoconfig; // if true, auto config hdmi output mode according to EDID.
|
||||
int command; // HDMI configuration command
|
||||
|
||||
};
|
||||
|
||||
extern struct hdmi *hdmi;
|
||||
|
||||
extern int hdmi_sys_init(void);
|
||||
extern int hdmi_sys_parse_edid(struct hdmi* hdmi);
|
||||
extern const char *hdmi_get_video_mode_name(unsigned char vic);
|
||||
extern int hdmi_videomode_to_vic(struct fb_videomode *vmode);
|
||||
extern const struct fb_videomode* hdmi_vic_to_videomode(int vic);
|
||||
extern int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head);
|
||||
extern struct hdmi_video_timing * hdmi_find_mode(int vic);
|
||||
extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic);
|
||||
extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok);
|
||||
extern int hdmi_switch_fb(struct hdmi *hdmi, int vic);
|
||||
extern void hdmi_sys_remove(void);
|
||||
#endif /* __RK30_HDMI_H__ */
|
||||
410
drivers/video/rockchip/hdmi/rk30_hdmi_edid.c
Normal file
410
drivers/video/rockchip/hdmi/rk30_hdmi_edid.c
Normal file
@@ -0,0 +1,410 @@
|
||||
#include "rk30_hdmi.h"
|
||||
#include "rk30_hdmi_hw.h"
|
||||
#include "../../edid.h"
|
||||
|
||||
#define hdmi_edid_error(fmt, ...) \
|
||||
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
|
||||
|
||||
#if 0
|
||||
#define hdmi_edid_debug(fmt, ...) \
|
||||
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
#define hdmi_edid_debug(fmt, ...)
|
||||
#endif
|
||||
|
||||
typedef enum HDMI_EDID_ERRORCODE
|
||||
{
|
||||
E_HDMI_EDID_SUCCESS = 0,
|
||||
E_HDMI_EDID_PARAM,
|
||||
E_HDMI_EDID_HEAD,
|
||||
E_HDMI_EDID_CHECKSUM,
|
||||
E_HDMI_EDID_VERSION,
|
||||
E_HDMI_EDID_UNKOWNDATA,
|
||||
E_HDMI_EDID_NOMEMORY
|
||||
}HDMI_EDID_ErrorCode;
|
||||
|
||||
static const unsigned int double_aspect_vic[] = {3, 7, 9, 11, 13, 15, 18, 22, 24, 26, 28, 30, 36, 38, 43, 45, 49, 51, 53, 55, 57, 59};
|
||||
static int hdmi_edid_checksum(unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
int checksum = 0;
|
||||
|
||||
for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)
|
||||
checksum += buf[i];
|
||||
|
||||
checksum &= 0xff;
|
||||
|
||||
if(checksum == 0)
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
else
|
||||
return E_HDMI_EDID_CHECKSUM;
|
||||
}
|
||||
|
||||
/*
|
||||
@Des Parse Detail Timing Descriptor.
|
||||
@Param buf : pointer to DTD data.
|
||||
@Param pvic: VIC of DTD descripted.
|
||||
*/
|
||||
static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)
|
||||
{
|
||||
mode->xres = H_ACTIVE;
|
||||
mode->yres = V_ACTIVE;
|
||||
mode->pixclock = PIXEL_CLOCK;
|
||||
// mode->pixclock /= 1000;
|
||||
// mode->pixclock = KHZ2PICOS(mode->pixclock);
|
||||
mode->right_margin = H_SYNC_OFFSET;
|
||||
mode->left_margin = (H_ACTIVE + H_BLANKING) -
|
||||
(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
|
||||
mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
|
||||
V_SYNC_WIDTH;
|
||||
mode->lower_margin = V_SYNC_OFFSET;
|
||||
mode->hsync_len = H_SYNC_WIDTH;
|
||||
mode->vsync_len = V_SYNC_WIDTH;
|
||||
if (HSYNC_POSITIVE)
|
||||
mode->sync |= FB_SYNC_HOR_HIGH_ACT;
|
||||
if (VSYNC_POSITIVE)
|
||||
mode->sync |= FB_SYNC_VERT_HIGH_ACT;
|
||||
mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
|
||||
(V_ACTIVE + V_BLANKING));
|
||||
if (INTERLACED) {
|
||||
mode->yres *= 2;
|
||||
mode->upper_margin *= 2;
|
||||
mode->lower_margin *= 2;
|
||||
mode->vsync_len *= 2;
|
||||
mode->vmode |= FB_VMODE_INTERLACED;
|
||||
}
|
||||
mode->flag = FB_MODE_IS_DETAILED;
|
||||
|
||||
hdmi_edid_debug("<<<<<<<<Detailed Time>>>>>>>>>\n");
|
||||
hdmi_edid_debug("%d KHz Refresh %d Hz", PIXEL_CLOCK/1000, mode->refresh);
|
||||
hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
|
||||
H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
|
||||
hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
|
||||
V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
|
||||
hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
|
||||
(VSYNC_POSITIVE) ? "+" : "-");
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
}
|
||||
|
||||
static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if(buf == NULL || extend_num == NULL)
|
||||
return E_HDMI_EDID_PARAM;
|
||||
|
||||
#ifdef DEBUG
|
||||
for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)
|
||||
{
|
||||
hdmi_edid_debug("%02x ", buf[i]&0xff);
|
||||
if((i+1) % 16 == 0)
|
||||
hdmi_edid_debug("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check first 8 byte to ensure it is an edid base block.
|
||||
if( buf[0] != 0x00 ||
|
||||
buf[1] != 0xFF ||
|
||||
buf[2] != 0xFF ||
|
||||
buf[3] != 0xFF ||
|
||||
buf[4] != 0xFF ||
|
||||
buf[5] != 0xFF ||
|
||||
buf[6] != 0xFF ||
|
||||
buf[7] != 0x00)
|
||||
{
|
||||
hdmi_edid_error("[EDID] check header error\n");
|
||||
return E_HDMI_EDID_HEAD;
|
||||
}
|
||||
|
||||
*extend_num = buf[0x7e];
|
||||
#ifdef DEBUG
|
||||
hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]);
|
||||
#endif
|
||||
|
||||
// Checksum
|
||||
rc = hdmi_edid_checksum(buf);
|
||||
if( rc != E_HDMI_EDID_SUCCESS)
|
||||
{
|
||||
hdmi_edid_error("[EDID] base block checksum error\n");
|
||||
return E_HDMI_EDID_CHECKSUM;
|
||||
}
|
||||
|
||||
pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);
|
||||
if(pedid->specs == NULL)
|
||||
return E_HDMI_EDID_NOMEMORY;
|
||||
|
||||
fb_edid_to_monspecs(buf, pedid->specs);
|
||||
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
}
|
||||
|
||||
// Parse CEA Short Video Descriptor
|
||||
static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid)
|
||||
{
|
||||
const struct fb_videomode *mode;
|
||||
int count, i, j, vic;
|
||||
|
||||
count = buf[0] & 0x1F;
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7);
|
||||
vic = buf[1 + i] & 0x7f;
|
||||
for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++)
|
||||
{
|
||||
if(vic == double_aspect_vic[j])
|
||||
{
|
||||
vic--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(vic)
|
||||
{
|
||||
mode = hdmi_vic_to_videomode(vic);
|
||||
if(mode)
|
||||
{
|
||||
hdmi_add_videomode(mode, &pedid->modelist);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse CEA Short Audio Descriptor
|
||||
static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid)
|
||||
{
|
||||
int i, count;
|
||||
|
||||
count = buf[0] & 0x1F;
|
||||
pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL);
|
||||
if(pedid->audio == NULL)
|
||||
return E_HDMI_EDID_NOMEMORY;
|
||||
pedid->audio_num = count/3;
|
||||
for(i = 0; i < pedid->audio_num; i++)
|
||||
{
|
||||
pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F;
|
||||
pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1;
|
||||
pedid->audio[i].rate = buf[1 + i*3 + 1];
|
||||
if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM
|
||||
{
|
||||
pedid->audio[i].word_length = buf[1 + i*3 + 2];
|
||||
}
|
||||
// printk("[EDID-CEA] type %d channel %d rate %d word length %d\n",
|
||||
// pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length);
|
||||
}
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
}
|
||||
// Parse CEA 861 Serial Extension.
|
||||
static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid)
|
||||
{
|
||||
unsigned int ddc_offset, native_dtd_num, cur_offset = 4;
|
||||
unsigned int underscan_support, baseaudio_support;
|
||||
unsigned int tag, IEEEOUI = 0;
|
||||
// unsigned int supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444;
|
||||
// unsigned char vic;
|
||||
|
||||
if(buf == NULL)
|
||||
return E_HDMI_EDID_PARAM;
|
||||
|
||||
// Check ces extension version
|
||||
if(buf[1] != 3)
|
||||
{
|
||||
hdmi_edid_error("[EDID-CEA] error version.\n");
|
||||
return E_HDMI_EDID_VERSION;
|
||||
}
|
||||
|
||||
ddc_offset = buf[2];
|
||||
underscan_support = (buf[3] >> 7) & 0x01;
|
||||
baseaudio_support = (buf[3] >> 6) & 0x01;
|
||||
pedid->ycbcr444 = (buf[3] >> 5) & 0x01;
|
||||
pedid->ycbcr422 = (buf[3] >> 4) & 0x01;
|
||||
native_dtd_num = buf[3] & 0x0F;
|
||||
hdmi_edid_debug("[EDID-CEA] ddc_offset %d underscan_support %d baseaudio_support %d yuv_support %d native_dtd_num %d\n", ddc_offset, underscan_support, baseaudio_support, yuv_support, native_dtd_num);
|
||||
// Parse data block
|
||||
while(cur_offset < ddc_offset)
|
||||
{
|
||||
tag = buf[cur_offset] >> 5;
|
||||
switch(tag)
|
||||
{
|
||||
case 0x02: // Video Data Block
|
||||
hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n");
|
||||
hdmi_edid_get_cea_svd(buf + cur_offset, pedid);
|
||||
break;
|
||||
case 0x01: // Audio Data Block
|
||||
hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n");
|
||||
hdmi_edid_parse_cea_sad(buf + cur_offset, pedid);
|
||||
break;
|
||||
case 0x04: // Speaker Allocation Data Block
|
||||
hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n");
|
||||
break;
|
||||
case 0x03: // Vendor Specific Data Block
|
||||
hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n");
|
||||
|
||||
IEEEOUI = buf[cur_offset + 2 + 1];
|
||||
IEEEOUI <<= 8;
|
||||
IEEEOUI += buf[cur_offset + 1 + 1];
|
||||
IEEEOUI <<= 8;
|
||||
IEEEOUI += buf[cur_offset + 1];
|
||||
hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI);
|
||||
if(IEEEOUI == 0x0c03)
|
||||
pedid->sink_hdmi = 1;
|
||||
// if(count > 5)
|
||||
// {
|
||||
// pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F;
|
||||
// supports_ai = buf[cur_offset + 5] >> 7;
|
||||
// dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1;
|
||||
// dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1;
|
||||
// dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1;
|
||||
// dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1;
|
||||
// hdmi_edid_debug("[EDID-CEA] supports_ai %d dc_48bit %d dc_36bit %d dc_30bit %d dc_y444 %d \n", supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444);
|
||||
// }
|
||||
// if(count > 6)
|
||||
// pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000;
|
||||
// if(count > 7)
|
||||
// {
|
||||
// pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0;
|
||||
// pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0;
|
||||
// }
|
||||
// if(count > 9 && pedid->latency_fields_present)
|
||||
// {
|
||||
// pedid->video_latency = buf[cur_offset + 8];
|
||||
// pedid->audio_latency = buf[cur_offset + 9];
|
||||
// }
|
||||
// if(count > 11 && pedid->i_latency_fields_present)
|
||||
// {
|
||||
// pedid->interlaced_video_latency = buf[cur_offset + 10];
|
||||
// pedid->interlaced_audio_latency = buf[cur_offset + 11];
|
||||
// }
|
||||
break;
|
||||
case 0x05: // VESA DTC Data Block
|
||||
hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n");
|
||||
break;
|
||||
case 0x07: // Use Extended Tag
|
||||
hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n");
|
||||
break;
|
||||
default:
|
||||
hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n");
|
||||
break;
|
||||
}
|
||||
cur_offset += (buf[cur_offset] & 0x1F) + 1;
|
||||
}
|
||||
#if 1
|
||||
{
|
||||
// Parse DTD
|
||||
struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL);
|
||||
if(vmode == NULL)
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2) //buf[126] = 0 and buf[127] = checksum
|
||||
{
|
||||
if(!buf[ddc_offset] && !buf[ddc_offset + 1])
|
||||
break;
|
||||
memset(vmode, 0, sizeof(struct fb_videomode));
|
||||
hdmi_edid_parse_dtd(buf + ddc_offset, vmode);
|
||||
hdmi_add_videomode(vmode, &pedid->modelist);
|
||||
ddc_offset += 18;
|
||||
}
|
||||
kfree(vmode);
|
||||
}
|
||||
#endif
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
}
|
||||
|
||||
static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if(buf == NULL || pedid == NULL)
|
||||
return E_HDMI_EDID_PARAM;
|
||||
|
||||
// Checksum
|
||||
rc = hdmi_edid_checksum(buf);
|
||||
if( rc != E_HDMI_EDID_SUCCESS)
|
||||
{
|
||||
hdmi_edid_error("[EDID] extensions block checksum error\n");
|
||||
return E_HDMI_EDID_CHECKSUM;
|
||||
}
|
||||
|
||||
switch(buf[0])
|
||||
{
|
||||
case 0xF0:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n");
|
||||
break;
|
||||
case 0x02:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a CEA 861 Series Extension.\n");
|
||||
hdmi_edid_parse_extensions_cea(buf, pedid);
|
||||
break;
|
||||
case 0x10:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n");
|
||||
break;
|
||||
case 0x40:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n");
|
||||
break;
|
||||
case 0x50:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n");
|
||||
break;
|
||||
case 0x60:
|
||||
hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n");
|
||||
break;
|
||||
default:
|
||||
hdmi_edid_debug("[EDID-EXTEND] Unkowned extension.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return E_HDMI_EDID_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int hdmi_sys_parse_edid(struct hdmi* hdmi)
|
||||
{
|
||||
struct hdmi_edid *pedid;
|
||||
unsigned char *buff = NULL;
|
||||
int rc = HDMI_ERROR_SUCESS, extendblock = 0, i;
|
||||
|
||||
if(hdmi == NULL)
|
||||
return HDMI_ERROR_FALSE;
|
||||
|
||||
pedid = &(hdmi->edid);
|
||||
memset(pedid, 0, sizeof(struct hdmi_edid));
|
||||
INIT_LIST_HEAD(&pedid->modelist);
|
||||
|
||||
buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL);
|
||||
if(buff == NULL)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
// Read base block edid.
|
||||
memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);
|
||||
rc = rk30_hdmi_read_edid(0, buff);
|
||||
if(rc)
|
||||
{
|
||||
dev_err(hdmi->dev, "[HDMI] read edid base block error\n");
|
||||
goto out;
|
||||
}
|
||||
rc = hdmi_edid_parse_base(buff, &extendblock, pedid);
|
||||
if(rc)
|
||||
{
|
||||
dev_err(hdmi->dev, "[HDMI] parse edid base block error\n");
|
||||
goto out;
|
||||
}
|
||||
for(i = 1; i < extendblock + 1; i++)
|
||||
{
|
||||
memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);
|
||||
rc = rk30_hdmi_read_edid(i, buff);
|
||||
if(rc)
|
||||
{
|
||||
printk("[HDMI] read edid block %d error\n", i);
|
||||
goto out;
|
||||
}
|
||||
rc = hdmi_edid_parse_extensions(buff, pedid);
|
||||
if(rc)
|
||||
{
|
||||
dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if(buff)
|
||||
kfree(buff);
|
||||
rc = hdmi_ouputmode_select(hdmi, rc);
|
||||
return rc;
|
||||
}
|
||||
440
drivers/video/rockchip/hdmi/rk30_hdmi_hw.c
Executable file
440
drivers/video/rockchip/hdmi/rk30_hdmi_hw.c
Executable file
@@ -0,0 +1,440 @@
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/io.h>
|
||||
#include "rk30_hdmi.h"
|
||||
#include "rk30_hdmi_hw.h"
|
||||
|
||||
static char interrupt1 = 0, interrupt2 = 0;
|
||||
|
||||
static inline void delay100us(void)
|
||||
{
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
static void rk30_hdmi_set_pwr_mode(int mode)
|
||||
{
|
||||
if(hdmi->pwr_mode == mode)
|
||||
return;
|
||||
hdmi_dbg(hdmi->dev, "[%s] mode %d\n", __FUNCTION__, mode);
|
||||
switch(mode)
|
||||
{
|
||||
case PWR_SAVE_MODE_A:
|
||||
HDMIWrReg(SYS_CTRL, 0x10);
|
||||
break;
|
||||
case PWR_SAVE_MODE_B:
|
||||
HDMIWrReg(SYS_CTRL, 0x20);
|
||||
break;
|
||||
case PWR_SAVE_MODE_D:
|
||||
// reset PLL A&B
|
||||
HDMIWrReg(SYS_CTRL, 0x4C);
|
||||
delay100us();
|
||||
// release PLL A reset
|
||||
HDMIWrReg(SYS_CTRL, 0x48);
|
||||
delay100us();
|
||||
// release PLL B reset
|
||||
HDMIWrReg(SYS_CTRL, 0x40);
|
||||
break;
|
||||
case PWR_SAVE_MODE_E:
|
||||
HDMIWrReg(SYS_CTRL, 0x80);
|
||||
break;
|
||||
}
|
||||
hdmi->pwr_mode = mode;
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
int rk30_hdmi_detect_hotplug(void)
|
||||
{
|
||||
int value = HDMIRdReg(HPD_MENS_STA);
|
||||
|
||||
hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value);
|
||||
if( (value & (m_HOTPLUG_STATUS | m_MSEN_STATUS)) == (m_HOTPLUG_STATUS | m_MSEN_STATUS) )
|
||||
return HDMI_HPD_INSERT;
|
||||
else
|
||||
return HDMI_HPD_REMOVED;
|
||||
}
|
||||
|
||||
#define HDMI_EDID_DDC_CLK 100000
|
||||
int rk30_hdmi_read_edid(int block, unsigned char *buff)
|
||||
{
|
||||
int value, ret = -ENXIO, ddc_bus_freq = 0;
|
||||
char interrupt = 0;
|
||||
|
||||
hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block);
|
||||
|
||||
//Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz.
|
||||
//Set DDC I2C CLK which devided from DDC_CLK to 100KHz.
|
||||
ddc_bus_freq = (30000000/HDMI_EDID_DDC_CLK)/4;
|
||||
HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
|
||||
HDMIWrReg(DDC_BUS_FREQ_L, (ddc_bus_freq >> 8) & 0xFF);
|
||||
|
||||
// Enable edid interrupt
|
||||
// HDMIMskReg(value, INTR_MASK1, (m_INT_EDID_ERR | m_INT_EDID_READY), (m_INT_EDID_ERR | m_INT_EDID_READY));
|
||||
HDMIWrReg(INTR_MASK1, m_INT_EDID_ERR | m_INT_EDID_READY | m_INT_HOTPLUG | m_INT_MSENS);
|
||||
// Config EDID block and segment addr
|
||||
HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80);
|
||||
HDMIWrReg(EDID_SEGMENT_POINTER, block/2);
|
||||
|
||||
value = 200;
|
||||
while(value--)
|
||||
{
|
||||
interrupt = interrupt1;
|
||||
// hdmi_dbg(hdmi->dev, "[%s] interrupt %02x\n", __FUNCTION__, interrupt);
|
||||
if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY))
|
||||
{
|
||||
interrupt1 &= ~(m_INT_EDID_ERR | m_INT_EDID_READY);
|
||||
break;
|
||||
}
|
||||
msleep(10);
|
||||
}
|
||||
hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value);
|
||||
// Disable edid interrupt
|
||||
HDMIMskReg(value, INTR_MASK1, (m_INT_EDID_ERR|m_INT_EDID_READY), 0);
|
||||
if(interrupt & m_INT_EDID_READY)
|
||||
{
|
||||
for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++)
|
||||
buff[value] = HDMIRdReg(DDC_READ_FIFO_ADDR);
|
||||
ret = 0;
|
||||
hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__);
|
||||
// for(value = 0; value < 128; value++) {
|
||||
// printk("%02x ,", buff[value]);
|
||||
// if( (value + 1) % 8 == 0)
|
||||
// printk("\n");
|
||||
// }
|
||||
}
|
||||
if(interrupt & m_INT_EDID_ERR)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "[%s] edid read error\n", __FUNCTION__);
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rk30_hdmi_config_phy_reg(int reg, int value)
|
||||
{
|
||||
HDMIWrReg(reg, value);
|
||||
HDMIWrReg(SYS_CTRL, 0x2C);
|
||||
delay100us();
|
||||
HDMIWrReg(SYS_CTRL, 0x20);
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
static void rk30_hdmi_config_phy(unsigned char vic)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "[%s] line %d\n", __FUNCTION__, __LINE__);
|
||||
HDMIWrReg(DEEP_COLOR_MODE, 0x22); // tmds frequency same as input dlck
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B);
|
||||
switch(vic)
|
||||
{
|
||||
case HDMI_1920x1080p_60Hz:
|
||||
case HDMI_1920x1080p_50Hz:
|
||||
rk30_hdmi_config_phy_reg(0x158, 0x0E);
|
||||
rk30_hdmi_config_phy_reg(0x15c, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x160, 0x60);
|
||||
rk30_hdmi_config_phy_reg(0x164, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x168, 0xDA);
|
||||
rk30_hdmi_config_phy_reg(0x16c, 0xA2);
|
||||
rk30_hdmi_config_phy_reg(0x170, 0x0e);
|
||||
rk30_hdmi_config_phy_reg(0x174, 0x22);
|
||||
rk30_hdmi_config_phy_reg(0x178, 0x00);
|
||||
break;
|
||||
case HDMI_1280x720p_60Hz:
|
||||
case HDMI_1280x720p_50Hz:
|
||||
rk30_hdmi_config_phy_reg(0x158, 0x06);
|
||||
rk30_hdmi_config_phy_reg(0x15c, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x160, 0x60);
|
||||
rk30_hdmi_config_phy_reg(0x164, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x168, 0xCA);
|
||||
rk30_hdmi_config_phy_reg(0x16c, 0xA3);
|
||||
rk30_hdmi_config_phy_reg(0x170, 0x0e);
|
||||
rk30_hdmi_config_phy_reg(0x174, 0x20);
|
||||
rk30_hdmi_config_phy_reg(0x178, 0x00);
|
||||
break;
|
||||
case HDMI_720x480p_60Hz_4_3:
|
||||
case HDMI_720x480p_60Hz_16_9:
|
||||
rk30_hdmi_config_phy_reg(0x158, 0x02);
|
||||
rk30_hdmi_config_phy_reg(0x15c, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x160, 0x60);
|
||||
rk30_hdmi_config_phy_reg(0x164, 0x00);
|
||||
rk30_hdmi_config_phy_reg(0x168, 0xC2);
|
||||
rk30_hdmi_config_phy_reg(0x16c, 0xA2);
|
||||
rk30_hdmi_config_phy_reg(0x170, 0x0e);
|
||||
rk30_hdmi_config_phy_reg(0x174, 0x20);
|
||||
rk30_hdmi_config_phy_reg(0x178, 0x00);
|
||||
break;
|
||||
default:
|
||||
hdmi_dbg(hdmi->dev, "not support such vic %d\n", vic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rk30_hdmi_config_avi(unsigned char vic, unsigned char output_color)
|
||||
{
|
||||
int i;
|
||||
char info[SIZE_AVI_INFOFRAME];
|
||||
|
||||
HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
|
||||
HDMIWrReg(CONTROL_PACKET_HB0, 0x82);
|
||||
HDMIWrReg(CONTROL_PACKET_HB1, 0x2);
|
||||
HDMIWrReg(CONTROL_PACKET_HB2, 0x0D);
|
||||
memset(info, 0, SIZE_AVI_INFOFRAME);
|
||||
|
||||
info[1] = (AVI_COLOR_MODE_RGB << 5);
|
||||
info[2] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
|
||||
info[3] = 0;
|
||||
info[4] = vic;
|
||||
info[5] = 0;
|
||||
|
||||
// Calculate AVI InfoFrame ChecKsum
|
||||
info[0] = 0x82 + 0x02 +0x0D;
|
||||
for (i = 1; i < SIZE_AVI_INFOFRAME; i++)
|
||||
{
|
||||
info[0] += info[i];
|
||||
}
|
||||
info[0] = 0x100 - info[0];
|
||||
|
||||
for(i = 0; i < SIZE_AVI_INFOFRAME; i++)
|
||||
HDMIWrReg(CONTROL_PACKET_PB_ADDR + i*4, info[i]);
|
||||
}
|
||||
|
||||
int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
|
||||
{
|
||||
int value;
|
||||
struct fb_videomode *mode;
|
||||
|
||||
hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__);
|
||||
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_E)
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D);
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_D || hdmi->pwr_mode == PWR_SAVE_MODE_A)
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B);
|
||||
|
||||
// Input video mode is RGB24bit, Data enable signal from external
|
||||
HDMIMskReg(value, AV_CTRL1, m_INPUT_VIDEO_MODE | m_DE_SIGNAL_SELECT, \
|
||||
v_INPUT_VIDEO_MODE(VIDEO_INPUT_RGB_YCBCR_444) | EXTERNAL_DE)
|
||||
HDMIMskReg(value, VIDEO_CTRL1, m_VIDEO_OUTPUT_MODE | m_VIDEO_INPUT_DEPTH | m_VIDEO_INPUT_COLOR_MODE, \
|
||||
v_VIDEO_OUTPUT_MODE(output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | VIDEO_INPUT_COLOR_RGB)
|
||||
|
||||
// Set HDMI Mode
|
||||
HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(output_mode));
|
||||
|
||||
// Set ext video
|
||||
mode = (struct fb_videomode *)hdmi_vic_to_videomode(vic);
|
||||
if(mode == NULL)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vic);
|
||||
return -ENOENT;
|
||||
}
|
||||
value = v_EXT_VIDEO_ENABLE(1) | v_INTERLACE(mode->vmode);
|
||||
if(mode->sync & FB_SYNC_HOR_HIGH_ACT)
|
||||
value |= v_HSYNC_POLARITY(1);
|
||||
if(mode->sync | FB_SYNC_VERT_HIGH_ACT)
|
||||
value |= v_VSYNC_POLARITY(1);
|
||||
HDMIWrReg(EXT_VIDEO_PARA, value);
|
||||
value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_L, value & 0xFF);
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_H, (value >> 8) & 0xFF);
|
||||
|
||||
value = mode->left_margin + mode->right_margin + mode->hsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HBLANK_L, value & 0xFF);
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HBLANK_H, (value >> 8) & 0xFF);
|
||||
|
||||
value = mode->left_margin + mode->hsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HDELAY_L, value & 0xFF);
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HDELAY_H, (value >> 8) & 0xFF);
|
||||
|
||||
value = mode->hsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_L, value & 0xFF);
|
||||
HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_H, (value >> 8) & 0xFF);
|
||||
|
||||
value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_L, value & 0xFF);
|
||||
HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_H, (value >> 8) & 0xFF);
|
||||
|
||||
value = mode->upper_margin + mode->vsync_len + mode->lower_margin;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_VBLANK_L, value & 0xFF);
|
||||
|
||||
value = mode->upper_margin + mode->vsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_VDELAY, value & 0xFF);
|
||||
|
||||
value = mode->vsync_len;
|
||||
HDMIWrReg(EXT_VIDEO_PARA_VSYNCWIDTH, value & 0xFF);
|
||||
|
||||
if(output_mode == OUTPUT_HDMI) {
|
||||
rk30_hdmi_config_avi(vic, output_mode);
|
||||
hdmi_dbg(hdmi->dev, "[%s] sucess output HDMI.\n", __FUNCTION__);
|
||||
}
|
||||
else {
|
||||
hdmi_dbg(hdmi->dev, "[%s] sucess output DVI.\n", __FUNCTION__);
|
||||
}
|
||||
rk30_hdmi_config_phy(vic);
|
||||
rk30_hdmi_control_output(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk30_hdmi_config_aai(void)
|
||||
{
|
||||
int i;
|
||||
char info[SIZE_AUDIO_INFOFRAME];
|
||||
|
||||
memset(info, 0, SIZE_AUDIO_INFOFRAME);
|
||||
|
||||
info[0] = 0x84;
|
||||
info[1] = 0x01;
|
||||
info[2] = 0x0A;
|
||||
|
||||
info[3] = info[0] + info[1] + info[2];
|
||||
for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++)
|
||||
info[3] += info[i];
|
||||
|
||||
info[3] = 0x100 - info[3];
|
||||
|
||||
HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI);
|
||||
for(i = 0; i < SIZE_AUDIO_INFOFRAME; i++)
|
||||
HDMIWrReg(CONTROL_PACKET_HB0 + i*4, info[i]);
|
||||
}
|
||||
|
||||
int rk30_hdmi_config_audio(struct hdmi_audio *audio)
|
||||
{
|
||||
int value, rate, N;
|
||||
char word_length;
|
||||
|
||||
switch(audio->rate)
|
||||
{
|
||||
case HDMI_AUDIO_FS_32000:
|
||||
rate = AUDIO_32K;
|
||||
N = N_32K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_44100:
|
||||
rate = AUDIO_441K;
|
||||
N = N_441K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_48000:
|
||||
rate = AUDIO_48K;
|
||||
N = N_48K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_88200:
|
||||
rate = AUDIO_882K;
|
||||
N = N_882K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_96000:
|
||||
rate = AUDIO_96K;
|
||||
N = N_96K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_176400:
|
||||
rate = AUDIO_1764K;
|
||||
N = N_1764K;
|
||||
break;
|
||||
case HDMI_AUDIO_FS_192000:
|
||||
rate = AUDIO_192K;
|
||||
N = N_192K;
|
||||
break;
|
||||
default:
|
||||
dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate);
|
||||
return -ENOENT;
|
||||
}
|
||||
switch(audio->word_length)
|
||||
{
|
||||
case HDMI_AUDIO_WORD_LENGTH_16bit:
|
||||
word_length = 0x02;
|
||||
break;
|
||||
case HDMI_AUDIO_WORD_LENGTH_20bit:
|
||||
word_length = 0x0a;
|
||||
break;
|
||||
case HDMI_AUDIO_WORD_LENGTH_24bit:
|
||||
word_length = 0x0b;
|
||||
break;
|
||||
default:
|
||||
dev_err(hdmi->dev, "[%s] not support such word length %d\n", __FUNCTION__, audio->word_length);
|
||||
return -ENOENT;
|
||||
}
|
||||
//set_audio_if I2S
|
||||
HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK
|
||||
HDMIWrReg(AUDIO_CTRL2, 0x40);
|
||||
HDMIWrReg(I2S_AUDIO_CTRL, v_I2S_MODE(I2S_MODE_STANDARD) | v_I2S_CHANNEL( (audio->channel + audio->channel%2)/2));
|
||||
HDMIWrReg(I2S_INPUT_SWAP, 0x00); //no swap
|
||||
HDMIMskReg(value, AV_CTRL1, m_AUDIO_SAMPLE_RATE, v_AUDIO_SAMPLE_RATE(rate))
|
||||
HDMIWrReg(SRC_NUM_AUDIO_LEN, word_length);
|
||||
|
||||
//Set N value 6144, fs=48kHz
|
||||
HDMIWrReg(N_1, N & 0xFF);
|
||||
HDMIWrReg(N_2, (N >> 8) & 0xFF);
|
||||
HDMIWrReg(LR_SWAP_N3, (N >> 16) & 0x0F);
|
||||
|
||||
rk30_hdmi_config_aai();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk30_hdmi_audio_reset(void)
|
||||
{
|
||||
int value;
|
||||
|
||||
HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, AUDIO_CAPTURE_RESET)
|
||||
msleep(1);
|
||||
HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, 0)
|
||||
}
|
||||
|
||||
void rk30_hdmi_control_output(int enable)
|
||||
{
|
||||
hdmi_dbg(hdmi->dev, "[%s] %d\n", __FUNCTION__, enable);
|
||||
if(enable == 0) {
|
||||
HDMIWrReg(VIDEO_SETTING2, 0x03);
|
||||
}
|
||||
else {
|
||||
// Switch to power save mode_d
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D);
|
||||
// Switch to power save mode_e
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_E);
|
||||
HDMIWrReg(VIDEO_SETTING2, 0x00);
|
||||
rk30_hdmi_audio_reset();
|
||||
}
|
||||
}
|
||||
|
||||
int rk30_hdmi_removed(void)
|
||||
{
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_E)
|
||||
{
|
||||
HDMIWrReg(VIDEO_SETTING2, 0x00);
|
||||
rk30_hdmi_audio_reset();
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D);
|
||||
}
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_D)
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B);
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_B)
|
||||
rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_A);
|
||||
|
||||
return HDMI_ERROR_SUCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
irqreturn_t hdmi_irq(int irq, void *priv)
|
||||
{
|
||||
unsigned int count = 0x10000;
|
||||
if(hdmi->pwr_mode == PWR_SAVE_MODE_A)
|
||||
{
|
||||
HDMIWrReg(SYS_CTRL, 0x20);
|
||||
hdmi->pwr_mode = PWR_SAVE_MODE_B;
|
||||
while(count--);
|
||||
}
|
||||
else
|
||||
{
|
||||
interrupt1 = HDMIRdReg(INTR_STATUS1);
|
||||
interrupt2 = HDMIRdReg(INTR_STATUS2);
|
||||
HDMIWrReg(INTR_STATUS1, interrupt1);
|
||||
HDMIWrReg(INTR_STATUS2, interrupt2);
|
||||
// hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x\n", __FUNCTION__, interrupt1);
|
||||
if( interrupt1 & (m_INT_HOTPLUG | m_INT_MSENS) )
|
||||
{
|
||||
if(hdmi->state == HDMI_SLEEP)
|
||||
hdmi->state = WAIT_HOTPLUG;
|
||||
interrupt1 &= ~(m_INT_HOTPLUG | m_INT_MSENS);
|
||||
queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 100);
|
||||
}
|
||||
// else if(hdmi->state == HDMI_SLEEP)
|
||||
// HDMIWrReg(SYS_CTRL, 0x10);
|
||||
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
262
drivers/video/rockchip/hdmi/rk30_hdmi_hw.h
Executable file
262
drivers/video/rockchip/hdmi/rk30_hdmi_hw.h
Executable file
@@ -0,0 +1,262 @@
|
||||
#ifndef __RK30_HDMI_HW_H__
|
||||
#define __RK30_HDMI_HW_H__
|
||||
|
||||
/* HDMI_SYS_CONTROL */
|
||||
#define SYS_CTRL 0x0
|
||||
|
||||
enum {
|
||||
PWR_SAVE_MODE_A = 1,
|
||||
PWR_SAVE_MODE_B = 2,
|
||||
PWR_SAVE_MODE_D = 4,
|
||||
PWR_SAVE_MODE_E = 8
|
||||
};
|
||||
#define m_PWR_SAVE_MODE 0xF0
|
||||
#define v_PWR_SAVE_MODE(n) (n << 4)
|
||||
#define PLL_B_RESET (1 << 3)
|
||||
|
||||
#define N_32K 0x1000
|
||||
#define N_441K 0x1880
|
||||
#define N_882K 0x3100
|
||||
#define N_1764K 0x6200
|
||||
#define N_48K 0x1800
|
||||
#define N_96K 0x3000
|
||||
#define N_192K 0x6000
|
||||
|
||||
#define LR_SWAP_N3 0x04
|
||||
#define N_2 0x08
|
||||
#define N_1 0x0c
|
||||
|
||||
#define AUDIO_CTRL1 0x28
|
||||
#define AUDIO_CTRL2 0x2c
|
||||
#define I2S_AUDIO_CTRL 0x30
|
||||
enum {
|
||||
I2S_MODE_STANDARD = 0,
|
||||
I2S_MODE_RIGHT_JUSTIFIED,
|
||||
I2S_MODE_LEFT_JUSTIFIED
|
||||
};
|
||||
#define v_I2S_MODE(n) n
|
||||
enum {
|
||||
I2S_CHANNEL_1_2 = 1,
|
||||
I2S_CHANNEL_3_4 = 3,
|
||||
I2S_CHANNEL_5_6 = 7,
|
||||
I2S_CHANNEL_7_8 = 0xf
|
||||
};
|
||||
#define v_I2S_CHANNEL(n) ( n << 2 )
|
||||
|
||||
#define I2S_INPUT_SWAP 0x40
|
||||
|
||||
#define SRC_NUM_AUDIO_LEN 0x50
|
||||
|
||||
/* HDMI_AV_CTRL1*/
|
||||
#define AV_CTRL1 0x54
|
||||
enum {
|
||||
AUDIO_32K = 0x3,
|
||||
AUDIO_441K = 0x0,
|
||||
AUDIO_48K = 0x2,
|
||||
AUDIO_882K = 0x8,
|
||||
AUDIO_96K = 0xa,
|
||||
AUDIO_1764K = 0xc,
|
||||
AUDIO_192K = 0xe,
|
||||
};
|
||||
#define m_AUDIO_SAMPLE_RATE 0xF0
|
||||
#define v_AUDIO_SAMPLE_RATE(n) (n << 4)
|
||||
enum {
|
||||
VIDEO_INPUT_RGB_YCBCR_444 = 0,
|
||||
VIDEO_INPUT_YCBCR422,
|
||||
VIDEO_INPUT_YCBCR422_EMBEDDED_SYNC,
|
||||
VIDEO_INPUT_2X_CLOCK,
|
||||
VIDEO_INPUT_2X_CLOCK_EMBEDDED_SYNC,
|
||||
VIDEO_INPUT_RGB444_DDR,
|
||||
VIDEO_INPUT_YCBCR422_DDR
|
||||
};
|
||||
#define m_INPUT_VIDEO_MODE (7 << 1)
|
||||
#define v_INPUT_VIDEO_MODE(n) (n << 1)
|
||||
enum {
|
||||
INTERNAL_DE = 0,
|
||||
EXTERNAL_DE
|
||||
};
|
||||
#define m_DE_SIGNAL_SELECT (1 << 0)
|
||||
|
||||
/* HDMI_AV_CTRL2 */
|
||||
#define VIDEO_CTRL1 0x58
|
||||
enum {
|
||||
VIDEO_OUTPUT_RGB444 = 0,
|
||||
VIDEO_OUTPUT_YCBCR444,
|
||||
VIDEO_OUTPUT_YCBCR422
|
||||
};
|
||||
#define m_VIDEO_OUTPUT_MODE (0x3 << 6)
|
||||
#define v_VIDEO_OUTPUT_MODE(n) (n << 6)
|
||||
enum {
|
||||
VIDEO_INPUT_DEPTH_12BIT = 0,
|
||||
VIDEO_INPUT_DEPTH_10BIT = 0x1,
|
||||
VIDEO_INPUT_DEPTH_8BIT = 0x3
|
||||
};
|
||||
#define m_VIDEO_INPUT_DEPTH (3 << 4)
|
||||
#define v_VIDEO_INPUT_DEPTH(n) (n << 4)
|
||||
enum {
|
||||
VIDEO_EMBEDDED_SYNC_LOCATION_0 = 0,
|
||||
VIDEO_EMBEDDED_SYNC_LOCATION_1,
|
||||
VIDEO_EMBEDDED_SYNC_LOCATION_2
|
||||
};
|
||||
#define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2)
|
||||
#define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2)
|
||||
enum {
|
||||
VIDEO_INPUT_COLOR_RGB = 0,
|
||||
VIDEO_INPUT_COLOR_YCBCR
|
||||
};
|
||||
#define m_VIDEO_INPUT_COLOR_MODE (1 << 0)
|
||||
|
||||
/* DEEP_COLOR_MODE */
|
||||
#define DEEP_COLOR_MODE 0x5c
|
||||
enum{
|
||||
TMDS_CLOCK_MODE_8BIT = 0,
|
||||
TMDS_CLOKK_MODE_10BIT,
|
||||
TMDS_CLOKK_MODE_12BIT
|
||||
};
|
||||
#define TMDS_CLOCK_MODE_MASK 0x3 << 6
|
||||
#define TMDS_CLOCK_MODE(n) (n) << 6
|
||||
|
||||
/* VIDEO_SETTING2 */
|
||||
#define VIDEO_SETTING2 0x114
|
||||
#define m_UNMUTE (1 << 7)
|
||||
#define m_MUTE (1 << 6)
|
||||
#define m_AUDIO_RESET (1 << 2)
|
||||
#define m_NOT_SEND_AUDIO (1 << 1)
|
||||
#define m_NOT_SEND_VIDEO (1 << 0)
|
||||
#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data
|
||||
#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data
|
||||
#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode.
|
||||
#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data
|
||||
#define NOT_SEND_VIDEO (1 << 0) // Send black video data
|
||||
|
||||
/* CONTROL_PACKET_BUF_INDEX */
|
||||
#define CONTROL_PACKET_BUF_INDEX 0x17c
|
||||
enum {
|
||||
INFOFRAME_AVI = 0x06,
|
||||
INFOFRAME_AAI = 0x08
|
||||
};
|
||||
#define CONTROL_PACKET_HB0 0x180
|
||||
#define CONTROL_PACKET_HB1 0x184
|
||||
#define CONTROL_PACKET_HB2 0x188
|
||||
#define CONTROL_PACKET_PB_ADDR 0x18c
|
||||
#define SIZE_AVI_INFOFRAME 0xe // 14 bytes
|
||||
#define SIZE_AUDIO_INFOFRAME 0x0F // 15 bytes
|
||||
enum {
|
||||
AVI_COLOR_MODE_RGB = 0,
|
||||
AVI_COLOR_MODE_YCBCR422,
|
||||
AVI_COLOR_MODE_YCBCR444
|
||||
};
|
||||
enum {
|
||||
AVI_COLORIMETRY_NO_DATA = 0,
|
||||
AVI_COLORIMETRY_SMPTE_170M,
|
||||
AVI_COLORIMETRY_ITU709,
|
||||
AVI_COLORIMETRY_EXTENDED
|
||||
};
|
||||
enum {
|
||||
AVI_CODED_FRAME_ASPECT_NO_DATA,
|
||||
AVI_CODED_FRAME_ASPECT_4_3,
|
||||
AVI_CODED_FRAME_ASPECT_16_9
|
||||
};
|
||||
enum {
|
||||
ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
|
||||
ACTIVE_ASPECT_RATE_4_3,
|
||||
ACTIVE_ASPECT_RATE_16_9,
|
||||
ACTIVE_ASPECT_RATE_14_9
|
||||
};
|
||||
|
||||
|
||||
/* HDCP_CTRL */
|
||||
#define HDCP_CTRL 0x2bc
|
||||
|
||||
enum {
|
||||
OUTPUT_DVI = 0,
|
||||
OUTPUT_HDMI
|
||||
};
|
||||
#define m_HDMI_DVI (1 << 1)
|
||||
#define v_HDMI_DVI(n) (n << 1)
|
||||
|
||||
#define EXT_VIDEO_PARA 0xC0
|
||||
#define m_VSYNC_OFFSET (0xF << 4)
|
||||
#define m_VSYNC_POLARITY (1 << 3)
|
||||
#define m_HSYNC_POLARITY (1 << 2)
|
||||
#define m_INTERLACE (1 << 1)
|
||||
#define m_EXT_VIDEO_ENABLE (1 << 0)
|
||||
|
||||
#define v_VSYNC_OFFSET(n) (n << 4)
|
||||
#define v_VSYNC_POLARITY(n) (n << 3)
|
||||
#define v_HSYNC_POLARITY(n) (n << 2)
|
||||
#define v_INTERLACE(n) (n << 1)
|
||||
#define v_EXT_VIDEO_ENABLE(n) (n << 0)
|
||||
|
||||
#define EXT_VIDEO_PARA_HTOTAL_L 0xC4
|
||||
#define EXT_VIDEO_PARA_HTOTAL_H 0xC8
|
||||
#define EXT_VIDEO_PARA_HBLANK_L 0xCC
|
||||
#define EXT_VIDEO_PARA_HBLANK_H 0xD0
|
||||
#define EXT_VIDEO_PARA_HDELAY_L 0xD4
|
||||
#define EXT_VIDEO_PARA_HDELAY_H 0xD8
|
||||
#define EXT_VIDEO_PARA_HSYNCWIDTH_L 0xDC
|
||||
#define EXT_VIDEO_PARA_HSYNCWIDTH_H 0xE0
|
||||
|
||||
#define EXT_VIDEO_PARA_VTOTAL_L 0xE4
|
||||
#define EXT_VIDEO_PARA_VTOTAL_H 0xE8
|
||||
#define EXT_VIDEO_PARA_VBLANK_L 0xF4
|
||||
#define EXT_VIDEO_PARA_VDELAY 0xF8
|
||||
#define EXT_VIDEO_PARA_VSYNCWIDTH 0xFC
|
||||
|
||||
#define INTR_MASK1 0x248
|
||||
#define INTR_MASK2 0x24c
|
||||
#define INTR_MASK3 0x258
|
||||
#define INTR_MASK4 0x25c
|
||||
#define INTR_STATUS1 0x250
|
||||
#define INTR_STATUS2 0x254
|
||||
#define INTR_STATUS3 0x260
|
||||
#define INTR_STATUS4 0x264
|
||||
|
||||
#define m_INT_HOTPLUG (1 << 7)
|
||||
#define m_INT_MSENS (1 << 6)
|
||||
#define m_INT_VSYNC (1 << 5)
|
||||
#define m_INT_AUDIO_FIFO_FULL (1 << 4)
|
||||
#define m_INT_EDID_READY (1 << 2)
|
||||
#define m_INT_EDID_ERR (1 << 1)
|
||||
|
||||
#define DDC_READ_FIFO_ADDR 0x200
|
||||
#define DDC_BUS_FREQ_L 0x204
|
||||
#define DDC_BUS_FREQ_H 0x208
|
||||
#define DDC_BUS_CTRL 0x2dc
|
||||
#define DDC_I2C_LEN 0x278
|
||||
#define DDC_I2C_OFFSET 0x280
|
||||
#define DDC_I2C_CTRL 0x284
|
||||
#define DDC_I2C_READ_BUF0 0x288
|
||||
#define DDC_I2C_READ_BUF1 0x28c
|
||||
#define DDC_I2C_READ_BUF2 0x290
|
||||
#define DDC_I2C_READ_BUF3 0x294
|
||||
#define DDC_I2C_WRITE_BUF0 0x298
|
||||
#define DDC_I2C_WRITE_BUF1 0x29c
|
||||
#define DDC_I2C_WRITE_BUF2 0x2a0
|
||||
#define DDC_I2C_WRITE_BUF3 0x2a4
|
||||
#define DDC_I2C_WRITE_BUF4 0x2ac
|
||||
#define DDC_I2C_WRITE_BUF5 0x2b0
|
||||
#define DDC_I2C_WRITE_BUF6 0x2b4
|
||||
|
||||
#define EDID_SEGMENT_POINTER 0x310
|
||||
#define EDID_WORD_ADDR 0x314
|
||||
#define EDID_FIFO_ADDR 0x318
|
||||
|
||||
#define HPD_MENS_STA 0x37c
|
||||
#define m_HOTPLUG_STATUS (1 << 7)
|
||||
#define m_MSEN_STATUS (1 << 6)
|
||||
|
||||
|
||||
#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr)
|
||||
#define HDMIWrReg(addr, val) __raw_writel(val, hdmi->regbase + addr);
|
||||
#define HDMIMskReg(temp, addr, msk, val) \
|
||||
temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \
|
||||
__raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr);
|
||||
|
||||
extern int rk30_hdmi_detect_hotplug(void);
|
||||
extern int rk30_hdmi_read_edid(int block, unsigned char *buff);
|
||||
extern int rk30_hdmi_removed(void);
|
||||
extern int rk30_hdmi_config_video(int vic, int output_color, int output_mode);
|
||||
extern int rk30_hdmi_config_audio(struct hdmi_audio *audio);
|
||||
extern void rk30_hdmi_control_output(int enable);
|
||||
#endif
|
||||
505
drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c
Executable file
505
drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c
Executable file
@@ -0,0 +1,505 @@
|
||||
#include <linux/console.h>
|
||||
#include "rk30_hdmi.h"
|
||||
#include "rk30_hdmi_hw.h"
|
||||
#include<linux/rk_fb.h>
|
||||
|
||||
#define OUT_TYPE SCREEN_HDMI
|
||||
#define OUT_FACE OUT_P888
|
||||
#define DCLK_POL 1
|
||||
#define SWAP_RB 0
|
||||
#define LCD_ACLK 800000000
|
||||
|
||||
const struct fb_videomode hdmi_mode [] = {
|
||||
//name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag(used for vic)
|
||||
//{ "640x480p@60Hz", 60, 640, 480, 25175000, 48, 16, 33, 10, 96, 2, 0, 0, 1 },
|
||||
//{ "720x480i@60Hz", 60, 720, 480, 27000000, 114, 38, 15, 4, 124, 3, 0, 1, 6 },
|
||||
//{ "720x576i@50Hz", 50, 720, 576, 27000000, 138, 24, 19, 2, 126, 3, 0, 1, 21 },
|
||||
{ "720x480p@60Hz", 60, 720, 480, 27000000, 60, 16, 30, 9, 62, 6, 0, 0, 2 },
|
||||
{ "720x576p@50Hz", 50, 720, 576, 27000000, 68, 12, 39, 5, 64, 5, 0, 0, 17 },
|
||||
//{ "1280x720p@24Hz", 24, 1280, 720, 59400000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 60 },
|
||||
//{ "1280x720p@25Hz", 25, 1280, 720, 74250000, 220, 2420, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 61 },
|
||||
//{ "1280x720p@30Hz", 30, 1280, 720, 74250000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 62 },
|
||||
{ "1280x720p@50Hz", 50, 1280, 720, 74250000, 220, 440, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 19 },
|
||||
{ "1280x720p@60Hz", 60, 1280, 720, 74250000, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 4 },
|
||||
//{ "1920x1080p@24Hz", 24, 1920, 1080, 74250000, 148, 638, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 32 },
|
||||
//{ "1920x1080p@25Hz", 25, 1920, 1080, 74250000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 33 },
|
||||
//{ "1920x1080p@30Hz", 30, 1920, 1080, 74250000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 34 },
|
||||
//{ "1920x1080i@50Hz_2",50, 1920, 1080, 72000000, 184, 32, 57, 23, 168, 5, FB_SYNC_HOR_HIGH_ACT, 1, 39 },
|
||||
//{ "1920x1080i@50Hz", 50, 1920, 1080, 74250000, 148, 528, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 20 },
|
||||
//{ "1920x1080i@60Hz", 60, 1920, 1080, 74250000, 148, 88, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 5 },
|
||||
{ "1920x1080p@50Hz", 50, 1920, 1080, 148500000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 31 },
|
||||
{ "1920x1080p@60Hz", 60, 1920, 1080, 148500000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 16 },
|
||||
/*
|
||||
{ "1440x288p@50Hz", 50, 720, 480, 27000000, 138, 24, 19, 2, 126, 3, 0, 0, 23 },
|
||||
{ "2880x576i@50Hz", 50, 1440, 240, 54000000, 276, 48, 19, 2, 252, 3, 0, 1, 25 },
|
||||
{ "2880x288p@50Hz", 50, 2880, 480, 54000000, 276, 48, 19, 3, 252, 3, 0, 0, 27 },
|
||||
{ "1440x576p@50Hz", 50, 2880, 480, 54000000, 136, 24, 39, 5, 128, 5, 0, 0, 29 },
|
||||
{ "2880x576p@50Hz", 50, 1920, 1080, 108000000, 272, 48, 39, 5, 256, 5, 0, 0, 37 },
|
||||
{ "1440x240p@60Hz", 60, 1440, 240, 27000000, 114, 38, 15, 4, 124, 3, 0, 0, 8 },
|
||||
{ "2880x480i@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 1, 10 },
|
||||
{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 0, 12 },
|
||||
{ "1440x480p@60Hz", 60, 1440, 480, 54000000, 120, 32, 30, 9, 124, 6, 0, 0, 14 },
|
||||
{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 240, 64, 30, 9, 248, 6, 0, 0, 35 },
|
||||
|
||||
{ "1920x1080i@100Hz", 100, 1920, 1080, 148500000, 148, 528, 15, 2, 44, 5, 1, 1, 40 },
|
||||
{ "1280x720p@100Hz", 100, 1280, 720, 148500000, 220, 440, 20, 5, 40, 5, 1, 0, 41 },
|
||||
{ "720x576p@100Hz", 100, 720, 576, 54000000, 68, 12, 39, 5, 64, 5, 0, 0, 42 },
|
||||
{ "1440x576i@100Hz", 100, 1440, 576, 54000000, 138, 24, 19, 2, 12, 3, 0, 1, 44 },
|
||||
{ "1920x1080p@100Hz", 100, 1920, 1080, 297000000, 148, 528, 36, 4, 44, 5, 1, 0, 64 },
|
||||
|
||||
{ "1920x1080i@120Hz", 120, 1920, 1080, 148500000, 148, 88, 15, 2, 44, 5, 1, 1, 46 },
|
||||
{ "1280x720p@120Hz", 120, 1280, 720, 148500000, 220, 110, 20, 5, 40, 5, 1, 0, 47 },
|
||||
{ "720x480p@120Hz", 120, 720, 480, 54000000, 60, 16, 30, 9, 62, 6, 0, 0, 48 },
|
||||
{ "1440x480i@120Hz", 120, 1440, 480, 54000000, 114, 38, 15, 4, 12, 3, 0, 1, 50 },
|
||||
{ "1920x1080p@120Hz", 120, 1920, 1080, 297000000, 148, 88, 36, 4, 44, 5, 1, 0, 63 },
|
||||
|
||||
{ "720x576p@200Hz", 200, 720, 576, 108000000, 68, 12, 39, 5, 64, 5, 0, 0, 52 },
|
||||
{ "1440x576i@200Hz", 200, 1920, 1080, 108000000, 138, 24, 19, 2, 12, 3, 0, 1, 54 },
|
||||
|
||||
{ "720x480p@240Hz", 240, 720, 480, 108000000, 60, 16, 30, 9, 62, 6, 0, 0, 56 },
|
||||
{ "1440x480i@240Hz", 240, 1440, 480, 108000000, 114, 38, 15, 4, 12, 3, 0, 1, 58 },
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(screen == NULL || vic == 0)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++)
|
||||
{
|
||||
if(hdmi_mode[i].flag == vic)
|
||||
break;
|
||||
}
|
||||
if(i == ARRAY_SIZE(hdmi_mode))
|
||||
return -1;
|
||||
|
||||
memset(screen, 0, sizeof(struct rk29fb_screen));
|
||||
|
||||
/* screen type & face */
|
||||
screen->type = OUT_TYPE;
|
||||
screen->face = OUT_FACE;
|
||||
|
||||
/* Screen size */
|
||||
screen->x_res = hdmi_mode[i].xres;
|
||||
screen->y_res = hdmi_mode[i].yres;
|
||||
|
||||
/* Timing */
|
||||
screen->pixclock = hdmi_mode[i].pixclock;
|
||||
screen->lcdc_aclk = LCD_ACLK;
|
||||
screen->left_margin = hdmi_mode[i].left_margin;
|
||||
screen->right_margin = hdmi_mode[i].right_margin;
|
||||
screen->hsync_len = hdmi_mode[i].hsync_len;
|
||||
screen->upper_margin = hdmi_mode[i].upper_margin;
|
||||
screen->lower_margin = hdmi_mode[i].lower_margin;
|
||||
screen->vsync_len = hdmi_mode[i].vsync_len;
|
||||
|
||||
/* Pin polarity */
|
||||
if(FB_SYNC_HOR_HIGH_ACT & hdmi_mode[i].sync)
|
||||
screen->pin_hsync = 1;
|
||||
else
|
||||
screen->pin_hsync = 0;
|
||||
if(FB_SYNC_VERT_HIGH_ACT & hdmi_mode[i].sync)
|
||||
screen->pin_vsync = 1;
|
||||
else
|
||||
screen->pin_vsync = 0;
|
||||
screen->pin_den = 0;
|
||||
screen->pin_dclk = DCLK_POL;
|
||||
|
||||
/* Swap rule */
|
||||
screen->swap_rb = SWAP_RB;
|
||||
screen->swap_rg = 0;
|
||||
screen->swap_gb = 0;
|
||||
screen->swap_delta = 0;
|
||||
screen->swap_dumy = 0;
|
||||
|
||||
/* Operation function*/
|
||||
screen->init = NULL;
|
||||
screen->standby = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_show_sink_info(struct hdmi *hdmi)
|
||||
{
|
||||
struct list_head *pos, *head = &hdmi->edid.modelist;
|
||||
struct fb_modelist *modelist;
|
||||
struct fb_videomode *m;
|
||||
int i;
|
||||
struct hdmi_audio *audio;
|
||||
|
||||
hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n");
|
||||
hdmi_dbg(hdmi->dev, "Support video mode: \n");
|
||||
list_for_each(pos, head) {
|
||||
modelist = list_entry(pos, struct fb_modelist, list);
|
||||
m = &modelist->mode;
|
||||
hdmi_dbg(hdmi->dev, " %s.\n", m->name);
|
||||
}
|
||||
|
||||
for(i = 0; i < hdmi->edid.audio_num; i++)
|
||||
{
|
||||
audio = &(hdmi->edid.audio[i]);
|
||||
switch(audio->type)
|
||||
{
|
||||
case HDMI_AUDIO_LPCM:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: LPCM\n");
|
||||
break;
|
||||
case HDMI_AUDIO_AC3:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: AC3\n");
|
||||
break;
|
||||
case HDMI_AUDIO_MPEG1:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: MPEG1\n");
|
||||
break;
|
||||
case HDMI_AUDIO_MP3:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: MP3\n");
|
||||
break;
|
||||
case HDMI_AUDIO_MPEG2:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: MPEG2\n");
|
||||
break;
|
||||
case HDMI_AUDIO_AAC_LC:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: AAC\n");
|
||||
break;
|
||||
case HDMI_AUDIO_DTS:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: DTS\n");
|
||||
break;
|
||||
case HDMI_AUDIO_ATARC:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: ATARC\n");
|
||||
break;
|
||||
case HDMI_AUDIO_DSD:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: DSD\n");
|
||||
break;
|
||||
case HDMI_AUDIO_E_AC3:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: E-AC3\n");
|
||||
break;
|
||||
case HDMI_AUDIO_DTS_HD:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: DTS-HD\n");
|
||||
break;
|
||||
case HDMI_AUDIO_MLP:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: MLP\n");
|
||||
break;
|
||||
case HDMI_AUDIO_DST:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: DST\n");
|
||||
break;
|
||||
case HDMI_AUDIO_WMA_PRO:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: WMP-PRO\n");
|
||||
break;
|
||||
default:
|
||||
hdmi_dbg(hdmi->dev, "Support audio type: Unkown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
hdmi_dbg(hdmi->dev, "Support audio sample rate: \n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_32000)
|
||||
hdmi_dbg(hdmi->dev, " 32000\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_44100)
|
||||
hdmi_dbg(hdmi->dev, " 44100\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_48000)
|
||||
hdmi_dbg(hdmi->dev, " 48000\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_88200)
|
||||
hdmi_dbg(hdmi->dev, " 88200\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_96000)
|
||||
hdmi_dbg(hdmi->dev, " 96000\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_176400)
|
||||
hdmi_dbg(hdmi->dev, " 176400\n");
|
||||
if(audio->rate & HDMI_AUDIO_FS_192000)
|
||||
hdmi_dbg(hdmi->dev, " 192000\n");
|
||||
|
||||
hdmi_dbg(hdmi->dev, "Support audio word lenght: \n");
|
||||
if(audio->rate & HDMI_AUDIO_WORD_LENGTH_16bit)
|
||||
hdmi_dbg(hdmi->dev, " 16bit\n");
|
||||
if(audio->rate & HDMI_AUDIO_WORD_LENGTH_20bit)
|
||||
hdmi_dbg(hdmi->dev, " 20bit\n");
|
||||
if(audio->rate & HDMI_AUDIO_WORD_LENGTH_24bit)
|
||||
hdmi_dbg(hdmi->dev, " 24bit\n");
|
||||
}
|
||||
hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_ouputmode_select - select hdmi transmitter output mode: hdmi or dvi?
|
||||
* @hdmi: handle of hdmi
|
||||
* @edid_ok: get EDID data success or not, HDMI_ERROR_SUCESS means success.
|
||||
*/
|
||||
int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok)
|
||||
{
|
||||
struct list_head *head = &hdmi->edid.modelist;
|
||||
struct fb_monspecs *specs = hdmi->edid.specs;
|
||||
struct fb_videomode *modedb = NULL;
|
||||
int i, pixclock;
|
||||
|
||||
if(edid_ok != HDMI_ERROR_SUCESS) {
|
||||
dev_err(hdmi->dev, "warning: EDID error, assume sink as HDMI !!!!");
|
||||
hdmi->edid.sink_hdmi = 1;
|
||||
}
|
||||
|
||||
if(edid_ok != HDMI_ERROR_SUCESS) {
|
||||
hdmi->edid.ycbcr444 = 0;
|
||||
hdmi->edid.ycbcr422 = 0;
|
||||
hdmi->autoconfig = HDMI_DISABLE;
|
||||
}
|
||||
if(head->next == head) {
|
||||
dev_info(hdmi->dev, "warning: no CEA video mode parsed from EDID !!!!");
|
||||
// If EDID get error, list all system supported mode.
|
||||
// If output mode is set to DVI and EDID is ok, check
|
||||
// the output timing.
|
||||
|
||||
if(hdmi->edid.sink_hdmi == 0 && specs && specs->modedb_len) {
|
||||
/* Get max resolution timing */
|
||||
modedb = &specs->modedb[0];
|
||||
for (i = 0; i < specs->modedb_len; i++) {
|
||||
if(specs->modedb[i].xres > modedb->xres)
|
||||
modedb = &specs->modedb[i];
|
||||
else if(specs->modedb[i].yres > modedb->yres)
|
||||
modedb = &specs->modedb[i];
|
||||
}
|
||||
// For some monitor, the max pixclock read from EDID is smaller
|
||||
// than the clock of max resolution mode supported. We fix it.
|
||||
pixclock = PICOS2KHZ(modedb->pixclock);
|
||||
pixclock /= 250;
|
||||
pixclock *= 250;
|
||||
pixclock *= 1000;
|
||||
if(pixclock == 148250000)
|
||||
pixclock = 148500000;
|
||||
if(pixclock > specs->dclkmax)
|
||||
specs->dclkmax = pixclock;
|
||||
}
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) {
|
||||
if(modedb) {
|
||||
if( (hdmi_mode[i].pixclock < specs->dclkmin) ||
|
||||
(hdmi_mode[i].pixclock > specs->dclkmax) ||
|
||||
(hdmi_mode[i].refresh < specs->vfmin) ||
|
||||
(hdmi_mode[i].refresh > specs->vfmax) ||
|
||||
(hdmi_mode[i].xres > modedb->xres) ||
|
||||
(hdmi_mode[i].yres > modedb->yres) )
|
||||
continue;
|
||||
}
|
||||
hdmi_add_videomode(&hdmi_mode[i], head);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HDMI_DEBUG
|
||||
hdmi_show_sink_info(hdmi);
|
||||
#endif
|
||||
return HDMI_ERROR_SUCESS;
|
||||
}
|
||||
/**
|
||||
* hdmi_videomode_compare - compare 2 videomodes
|
||||
* @mode1: first videomode
|
||||
* @mode2: second videomode
|
||||
*
|
||||
* RETURNS:
|
||||
* 1 if mode1 > mode2, 0 if mode1 = mode2, -1 mode1 < mode2
|
||||
*/
|
||||
static int hdmi_videomode_compare(const struct fb_videomode *mode1,
|
||||
const struct fb_videomode *mode2)
|
||||
{
|
||||
if(mode1->xres > mode2->xres)
|
||||
return 1;
|
||||
else if(mode1->xres == mode2->xres)
|
||||
{
|
||||
if(mode1->yres > mode2->yres)
|
||||
return 1;
|
||||
else if(mode1->yres == mode2->yres)
|
||||
{
|
||||
if(mode1->pixclock > mode2->pixclock)
|
||||
return 1;
|
||||
else if(mode1->pixclock == mode2->pixclock)
|
||||
{
|
||||
if(mode1->refresh > mode2->refresh)
|
||||
return 1;
|
||||
else if(mode1->refresh == mode2->refresh)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* hdmi_add_videomode: adds videomode entry to modelist
|
||||
* @mode: videomode to add
|
||||
* @head: struct list_head of modelist
|
||||
*
|
||||
* NOTES:
|
||||
* Will only add unmatched mode entries
|
||||
*/
|
||||
int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct fb_modelist *modelist, *modelist_new;
|
||||
struct fb_videomode *m;
|
||||
int i, found = 0;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++)
|
||||
{
|
||||
m =(struct fb_videomode*) &hdmi_mode[i];
|
||||
if (fb_mode_is_equal(m, mode)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_for_each(pos, head) {
|
||||
modelist = list_entry(pos, struct fb_modelist, list);
|
||||
m = &modelist->mode;
|
||||
if (fb_mode_is_equal(m, mode)) {
|
||||
// m == mode
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hdmi_videomode_compare(m, mode) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modelist_new = kmalloc(sizeof(struct fb_modelist),
|
||||
GFP_KERNEL);
|
||||
if (!modelist_new)
|
||||
return -ENOMEM;
|
||||
modelist_new->mode = hdmi_mode[i];
|
||||
list_add_tail(&modelist_new->list, pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_videomode_to_vic: transverse video mode to vic
|
||||
* @vmode: videomode to transverse
|
||||
*
|
||||
*/
|
||||
int hdmi_videomode_to_vic(struct fb_videomode *vmode)
|
||||
{
|
||||
unsigned char vic = 0;
|
||||
int i = 0;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++)
|
||||
{
|
||||
if( vmode->vmode == hdmi_mode[i].vmode &&
|
||||
vmode->refresh == hdmi_mode[i].refresh &&
|
||||
vmode->xres == hdmi_mode[i].xres &&
|
||||
vmode->left_margin == hdmi_mode[i].left_margin &&
|
||||
vmode->right_margin == hdmi_mode[i].right_margin &&
|
||||
vmode->upper_margin == hdmi_mode[i].upper_margin &&
|
||||
vmode->lower_margin == hdmi_mode[i].lower_margin &&
|
||||
vmode->hsync_len == hdmi_mode[i].hsync_len &&
|
||||
vmode->vsync_len == hdmi_mode[i].vsync_len)
|
||||
{
|
||||
if( (vmode->vmode == FB_VMODE_NONINTERLACED && vmode->yres == hdmi_mode[i].yres) ||
|
||||
(vmode->vmode == FB_VMODE_INTERLACED && vmode->yres == hdmi_mode[i].yres/2))
|
||||
{
|
||||
vic = hdmi_mode[i].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vic;
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_vic_to_videomode: transverse vic mode to video mode
|
||||
* @vmode: vic to transverse
|
||||
*
|
||||
*/
|
||||
const struct fb_videomode* hdmi_vic_to_videomode(int vic)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(vic == 0)
|
||||
return NULL;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++)
|
||||
{
|
||||
if(hdmi_mode[i].flag == vic)
|
||||
return &hdmi_mode[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_find_best_mode: find the video mode nearest to input vic
|
||||
* @hdmi:
|
||||
* @vic: input vic
|
||||
*
|
||||
* NOTES:
|
||||
* If vic is zero, return the high resolution video mode vic.
|
||||
*/
|
||||
int hdmi_find_best_mode(struct hdmi* hdmi, int vic)
|
||||
{
|
||||
struct list_head *pos, *head = &hdmi->edid.modelist;
|
||||
struct fb_modelist *modelist;
|
||||
struct fb_videomode *m = NULL;
|
||||
int found = 0;
|
||||
|
||||
if(vic)
|
||||
{
|
||||
list_for_each(pos, head) {
|
||||
modelist = list_entry(pos, struct fb_modelist, list);
|
||||
m = &modelist->mode;
|
||||
if(m->flag == vic)
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (vic == 0 || found == 0) && head->next != head)
|
||||
{
|
||||
modelist = list_entry(head->next, struct fb_modelist, list);
|
||||
m = &modelist->mode;
|
||||
}
|
||||
if(m != NULL)
|
||||
return m->flag;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *hdmi_get_video_mode_name(unsigned char vic)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++)
|
||||
{
|
||||
if(vic == hdmi_mode[i].flag)
|
||||
break;
|
||||
}
|
||||
if(i == ARRAY_SIZE(hdmi_mode))
|
||||
return NULL;
|
||||
else
|
||||
return hdmi_mode[i].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_switch_fb: switch lcdc mode to required video mode
|
||||
* @hdmi:
|
||||
* @type:
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
*/
|
||||
int hdmi_switch_fb(struct hdmi *hdmi, int vic)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
// if(hdmi->config_set.resolution == 0)
|
||||
// hdmi->config_set.resolution = HDMI_DEFAULT_RESOLUTION;
|
||||
|
||||
if(hdmi->lcdc == NULL || hdmi->lcdc->screen == NULL) {
|
||||
printk("***************\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = hdmi_set_info(hdmi->lcdc->screen, vic);
|
||||
|
||||
if(rc == 0 && hdmi->lcdc->load_screen) {
|
||||
|
||||
rc = hdmi->lcdc->load_screen(hdmi->lcdc, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
181
drivers/video/rockchip/hdmi/rk30_hdmi_task.c
Executable file
181
drivers/video/rockchip/hdmi/rk30_hdmi_task.c
Executable file
@@ -0,0 +1,181 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include "rk30_hdmi.h"
|
||||
#include "rk30_hdmi_hw.h"
|
||||
|
||||
|
||||
#define HDMI_MAX_TRY_TIMES 1
|
||||
|
||||
static char *envp[] = {"INTERFACE=HDMI", NULL};
|
||||
|
||||
int hdmi_sys_init(void)
|
||||
{
|
||||
hdmi->pwr_mode = PWR_SAVE_MODE_A;
|
||||
hdmi->hotplug = HDMI_HPD_REMOVED;
|
||||
hdmi->state = HDMI_SLEEP;
|
||||
hdmi->enable = HDMI_ENABLE;
|
||||
|
||||
hdmi->vic = HDMI_VIDEO_DEFAULT_MODE;
|
||||
hdmi->audio.channel = HDMI_AUDIO_DEFAULT_CHANNEL;
|
||||
hdmi->audio.rate = HDMI_AUDIO_DEFAULT_RATE;
|
||||
hdmi->audio.word_length = HDMI_AUDIO_DEFAULT_WORD_LENGTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hdmi_sys_remove(void)
|
||||
{
|
||||
rk30_hdmi_removed();
|
||||
fb_destroy_modelist(&hdmi->edid.modelist);
|
||||
if(hdmi->edid.audio)
|
||||
kfree(hdmi->edid.audio);
|
||||
if(hdmi->edid.specs)
|
||||
{
|
||||
if(hdmi->edid.specs->modedb)
|
||||
kfree(hdmi->edid.specs->modedb);
|
||||
kfree(hdmi->edid.specs);
|
||||
}
|
||||
memset(&hdmi->edid, 0, sizeof(struct hdmi_edid));
|
||||
INIT_LIST_HEAD(&hdmi->edid.modelist);
|
||||
hdmi->state = HDMI_SLEEP;
|
||||
hdmi->hotplug = HDMI_HPD_REMOVED;
|
||||
}
|
||||
|
||||
static int hdmi_process_command(void)
|
||||
{
|
||||
int change, state = hdmi->state;
|
||||
|
||||
change = hdmi->command;
|
||||
if(change != HDMI_CONFIG_NONE)
|
||||
{
|
||||
hdmi->command = HDMI_CONFIG_NONE;
|
||||
switch(change)
|
||||
{
|
||||
case HDMI_CONFIG_ENABLE:
|
||||
/* disable HDMI */
|
||||
if(!hdmi->enable)
|
||||
{
|
||||
if(hdmi->hotplug)
|
||||
hdmi_sys_remove();
|
||||
state = HDMI_SLEEP;
|
||||
}
|
||||
if(hdmi->wait == 1) {
|
||||
complete(&hdmi->complete);
|
||||
hdmi->wait = 0;
|
||||
}
|
||||
break;
|
||||
case HDMI_CONFIG_COLOR:
|
||||
if(state > CONFIG_VIDEO)
|
||||
state = CONFIG_VIDEO;
|
||||
break;
|
||||
case HDMI_CONFIG_HDCP:
|
||||
break;
|
||||
case HDMI_CONFIG_DISPLAY:
|
||||
break;
|
||||
case HDMI_CONFIG_AUDIO:
|
||||
if(state > CONFIG_AUDIO)
|
||||
state = CONFIG_AUDIO;
|
||||
break;
|
||||
case HDMI_CONFIG_VIDEO:
|
||||
default:
|
||||
if(state > SYSTEM_CONFIG)
|
||||
state = SYSTEM_CONFIG;
|
||||
else
|
||||
{
|
||||
if(hdmi->wait == 1) {
|
||||
complete(&hdmi->complete);
|
||||
hdmi->wait = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void hdmi_work(struct work_struct *work)
|
||||
{
|
||||
int hotplug, state_last, state;
|
||||
int rc = HDMI_ERROR_SUCESS, trytimes = 0;
|
||||
/* Process hdmi command */
|
||||
state = hdmi_process_command();
|
||||
|
||||
if(!hdmi->enable)
|
||||
return;
|
||||
|
||||
hotplug = rk30_hdmi_detect_hotplug();
|
||||
hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug);
|
||||
if(hotplug != hdmi->hotplug)
|
||||
{
|
||||
hdmi->hotplug = hotplug;
|
||||
if(hdmi->hotplug == HDMI_HPD_INSERT)
|
||||
state = READ_PARSE_EDID;
|
||||
else {
|
||||
hdmi_sys_remove();
|
||||
kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
do {
|
||||
state_last = state;
|
||||
switch(state)
|
||||
{
|
||||
case READ_PARSE_EDID:
|
||||
rc = hdmi_sys_parse_edid(hdmi);
|
||||
if(rc == HDMI_ERROR_SUCESS)
|
||||
{
|
||||
state = SYSTEM_CONFIG;
|
||||
kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD, envp);
|
||||
}
|
||||
break;
|
||||
case SYSTEM_CONFIG:
|
||||
if(hdmi->autoconfig)
|
||||
hdmi->vic = hdmi_find_best_mode(hdmi, 0);
|
||||
else
|
||||
hdmi->vic = hdmi_find_best_mode(hdmi, hdmi->vic);
|
||||
rc = hdmi_switch_fb(hdmi, hdmi->vic);
|
||||
if(rc == HDMI_ERROR_SUCESS)
|
||||
state = CONFIG_VIDEO;
|
||||
break;
|
||||
case CONFIG_VIDEO:
|
||||
rc = rk30_hdmi_config_video(hdmi->vic, VIDEO_OUTPUT_RGB444, hdmi->edid.sink_hdmi);
|
||||
if(rc == HDMI_ERROR_SUCESS)
|
||||
{
|
||||
if(hdmi->edid.sink_hdmi)
|
||||
state = CONFIG_AUDIO;
|
||||
else
|
||||
state = PLAY_BACK;
|
||||
}
|
||||
break;
|
||||
case CONFIG_AUDIO:
|
||||
rc = rk30_hdmi_config_audio(&(hdmi->audio));
|
||||
|
||||
if(rc == HDMI_ERROR_SUCESS)
|
||||
state = PLAY_BACK;
|
||||
break;
|
||||
case PLAY_BACK:
|
||||
rk30_hdmi_control_output(1);
|
||||
if(hdmi->wait == 1) {
|
||||
complete(&hdmi->complete);
|
||||
hdmi->wait = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(rc != HDMI_ERROR_SUCESS)
|
||||
{
|
||||
trytimes++;
|
||||
msleep(10);
|
||||
}
|
||||
if(state != state_last)
|
||||
trytimes = 0;
|
||||
}while((state != state_last || (rc != HDMI_ERROR_SUCESS) ) && trytimes < HDMI_MAX_TRY_TIMES);
|
||||
|
||||
if(trytimes == HDMI_MAX_TRY_TIMES)
|
||||
{
|
||||
if(hdmi->hotplug)
|
||||
hdmi_sys_remove();
|
||||
}
|
||||
}
|
||||
198
drivers/video/rockchip/hdmi/rk_hdmi.h
Executable file
198
drivers/video/rockchip/hdmi/rk_hdmi.h
Executable file
@@ -0,0 +1,198 @@
|
||||
#ifndef __RK_HDMI_H__
|
||||
#define __RK_HDMI_H__
|
||||
|
||||
/********************************************************************
|
||||
** <20>ṹ<EFBFBD><E1B9B9><EFBFBD><EFBFBD> *
|
||||
********************************************************************/
|
||||
/* HDMI video mode code according CEA-861-E*/
|
||||
enum hdmi_video_mode
|
||||
{
|
||||
HDMI_640x480p_60Hz = 1,
|
||||
HDMI_720x480p_60Hz_4_3,
|
||||
HDMI_720x480p_60Hz_16_9,
|
||||
HDMI_1280x720p_60Hz,
|
||||
HDMI_1920x1080i_60Hz, //5
|
||||
HDMI_720x480i_60Hz_4_3,
|
||||
HDMI_720x480i_60Hz_16_9,
|
||||
HDMI_720x240p_60Hz_4_3,
|
||||
HDMI_720x240p_60Hz_16_9,
|
||||
HDMI_2880x480i_60Hz_4_3, //10
|
||||
HDMI_2880x480i_60Hz_16_9,
|
||||
HDMI_2880x240p_60Hz_4_3,
|
||||
HDMI_2880x240p_60Hz_16_9,
|
||||
HDMI_1440x480p_60Hz_4_3,
|
||||
HDMI_1440x480p_60Hz_16_9, //15
|
||||
HDMI_1920x1080p_60Hz,
|
||||
HDMI_720x576p_50Hz_4_3,
|
||||
HDMI_720x576p_50Hz_16_9,
|
||||
HDMI_1280x720p_50Hz,
|
||||
HDMI_1920x1080i_50Hz, //20
|
||||
HDMI_720x576i_50Hz_4_3,
|
||||
HDMI_720x576i_50Hz_16_9,
|
||||
HDMI_720x288p_50Hz_4_3,
|
||||
HDMI_720x288p_50Hz_16_9,
|
||||
HDMI_2880x576i_50Hz_4_3, //25
|
||||
HDMI_2880x576i_50Hz_16_9,
|
||||
HDMI_2880x288p_50Hz_4_3,
|
||||
HDMI_2880x288p_50Hz_16_9,
|
||||
HDMI_1440x576p_50Hz_4_3,
|
||||
HDMI_1440x576p_50Hz_16_9, //30
|
||||
HDMI_1920x1080p_50Hz,
|
||||
HDMI_1920x1080p_24Hz,
|
||||
HDMI_1920x1080p_25Hz,
|
||||
HDMI_1920x1080p_30Hz,
|
||||
HDMI_2880x480p_60Hz_4_3, //35
|
||||
HDMI_2880x480p_60Hz_16_9,
|
||||
HDMI_2880x576p_50Hz_4_3,
|
||||
HDMI_2880x576p_50Hz_16_9,
|
||||
HDMI_1920x1080i_50Hz_2, // V Line 1250 total
|
||||
HDMI_1920x1080i_100Hz, //40
|
||||
HDMI_1280x720p_100Hz,
|
||||
HDMI_720x576p_100Hz_4_3,
|
||||
HDMI_720x576p_100Hz_16_9,
|
||||
HDMI_720x576i_100Hz_4_3,
|
||||
HDMI_720x576i_100Hz_16_9, //45
|
||||
HDMI_1920x1080i_120Hz,
|
||||
HDMI_1280x720p_120Hz,
|
||||
HDMI_720x480p_120Hz_4_3,
|
||||
HDMI_720x480p_120Hz_16_9,
|
||||
HDMI_720x480i_120Hz_4_3, //50
|
||||
HDMI_720x480i_120Hz_16_9,
|
||||
HDMI_720x576p_200Hz_4_3,
|
||||
HDMI_720x576p_200Hz_16_9,
|
||||
HDMI_720x576i_200Hz_4_3,
|
||||
HDMI_720x576i_200Hz_16_9, //55
|
||||
HDMI_720x480p_240Hz_4_3,
|
||||
HDMI_720x480p_240Hz_16_9,
|
||||
HDMI_720x480i_240Hz_4_3,
|
||||
HDMI_720x480i_240Hz_16_9,
|
||||
HDMI_1280x720p_24Hz, //60
|
||||
HDMI_1280x720p_25Hz,
|
||||
HDMI_1280x720p_30Hz,
|
||||
HDMI_1920x1080p_120Hz,
|
||||
HDMI_1920x1080p_100Hz,
|
||||
};
|
||||
|
||||
/* HDMI Video Data Color Mode */
|
||||
enum {
|
||||
HDMI_COLOR_RGB = 0,
|
||||
HDMI_COLOR_YCbCr422,
|
||||
HDMI_COLOR_YCbCr444
|
||||
};
|
||||
|
||||
/* HDMI Audio type */
|
||||
enum hdmi_audio_type
|
||||
{
|
||||
HDMI_AUDIO_LPCM = 1,
|
||||
HDMI_AUDIO_AC3,
|
||||
HDMI_AUDIO_MPEG1,
|
||||
HDMI_AUDIO_MP3,
|
||||
HDMI_AUDIO_MPEG2,
|
||||
HDMI_AUDIO_AAC_LC, //AAC
|
||||
HDMI_AUDIO_DTS,
|
||||
HDMI_AUDIO_ATARC,
|
||||
HDMI_AUDIO_DSD, //One bit Audio
|
||||
HDMI_AUDIO_E_AC3,
|
||||
HDMI_AUDIO_DTS_HD,
|
||||
HDMI_AUDIO_MLP,
|
||||
HDMI_AUDIO_DST,
|
||||
HDMI_AUDIO_WMA_PRO
|
||||
};
|
||||
|
||||
/* I2S Fs */
|
||||
enum hdmi_audio_fs {
|
||||
HDMI_AUDIO_FS_32000 = 0x1,
|
||||
HDMI_AUDIO_FS_44100 = 0x2,
|
||||
HDMI_AUDIO_FS_48000 = 0x4,
|
||||
HDMI_AUDIO_FS_88200 = 0x8,
|
||||
HDMI_AUDIO_FS_96000 = 0x10,
|
||||
HDMI_AUDIO_FS_176400 = 0x20,
|
||||
HDMI_AUDIO_FS_192000 = 0x40
|
||||
};
|
||||
|
||||
/* Audio Word Length */
|
||||
enum hdmi_audio_word_length {
|
||||
HDMI_AUDIO_WORD_LENGTH_16bit = 0x1,
|
||||
HDMI_AUDIO_WORD_LENGTH_20bit = 0x2,
|
||||
HDMI_AUDIO_WORD_LENGTH_24bit = 0x4
|
||||
};
|
||||
|
||||
/* EDID block size */
|
||||
#define HDMI_EDID_BLOCK_SIZE 128
|
||||
|
||||
// HDMI state machine
|
||||
enum hdmi_state{
|
||||
HDMI_SLEEP = 0,
|
||||
HDMI_INITIAL,
|
||||
WAIT_HOTPLUG,
|
||||
READ_PARSE_EDID,
|
||||
WAIT_HDMI_ENABLE,
|
||||
SYSTEM_CONFIG,
|
||||
CONFIG_VIDEO,
|
||||
CONFIG_AUDIO,
|
||||
PLAY_BACK,
|
||||
};
|
||||
|
||||
// HDMI configuration command
|
||||
enum hdmi_change {
|
||||
HDMI_CONFIG_NONE = 0,
|
||||
HDMI_CONFIG_VIDEO,
|
||||
HDMI_CONFIG_AUDIO,
|
||||
HDMI_CONFIG_COLOR,
|
||||
HDMI_CONFIG_HDCP,
|
||||
HDMI_CONFIG_ENABLE,
|
||||
HDMI_CONFIG_DISABLE,
|
||||
HDMI_CONFIG_DISPLAY
|
||||
};
|
||||
|
||||
// HDMI Hotplug status
|
||||
enum {
|
||||
HDMI_HPD_REMOVED = 0,
|
||||
HDMI_HPD_INSERT
|
||||
};
|
||||
|
||||
/* HDMI STATUS */
|
||||
#define HDMI_DISABLE 0
|
||||
#define HDMI_ENABLE 1
|
||||
#define HDMI_UNKOWN 0xFF
|
||||
|
||||
/* HDMI Error Code */
|
||||
enum hdmi_errorcode
|
||||
{
|
||||
HDMI_ERROR_SUCESS = 0,
|
||||
HDMI_ERROR_FALSE,
|
||||
HDMI_ERROR_I2C,
|
||||
HDMI_ERROR_EDID,
|
||||
};
|
||||
|
||||
/* HDMI audio parameters */
|
||||
struct hdmi_audio {
|
||||
u32 type; //Audio type
|
||||
u32 channel; //Audio channel number
|
||||
u32 rate; //Audio sampling rate
|
||||
u32 word_length; //Audio data word length
|
||||
};
|
||||
|
||||
struct hdmi_edid {
|
||||
unsigned char sink_hdmi; //HDMI display device flag
|
||||
unsigned char ycbcr444; //Display device support YCbCr444
|
||||
unsigned char ycbcr422; //Display device support YCbCr422
|
||||
unsigned char deepcolor; //bit3:DC_48bit; bit2:DC_36bit; bit1:DC_30bit; bit0:DC_Y444;
|
||||
struct fb_monspecs *specs; //Device spec
|
||||
struct list_head modelist; //Device supported display mode list
|
||||
struct hdmi_audio *audio; //Device supported audio info
|
||||
int audio_num; //Device supported audio type number
|
||||
};
|
||||
|
||||
extern const struct fb_videomode hdmi_mode[];
|
||||
|
||||
#define HDMI_DEBUG
|
||||
|
||||
#ifdef HDMI_DEBUG
|
||||
#define hdmi_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_INFO , dev , format , ## arg)
|
||||
#else
|
||||
#define hdmi_dbg(dev, format, arg...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user