oleavr-rgl-a500-mini-linux-.../drivers/video/sunxi/disp2/hdmi/drv_hdmi.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

1167 lines
25 KiB
C
Executable file

#include "drv_hdmi_i.h"
#include "hdmi_core.h"
#include "../disp/disp_sys_intf.h"
#include <linux/regulator/consumer.h>
#include <linux/clk-private.h>
#include <linux/sunxi-clk-prepare.h>
#define HDMI_IO_NUM 5
static bool hdmi_io_used[HDMI_IO_NUM]={0};
static disp_gpio_set_t hdmi_io[HDMI_IO_NUM];
static u32 io_enable_count = 0;
static struct semaphore *run_sem = NULL;
static struct task_struct * HDMI_task = NULL;
static struct task_struct * cec_task = NULL;
static bool hdmi_cec_support = false;
static char hdmi_power[25];
static bool hdmi_power_used;
static bool hdmi_used;
static bool boot_hdmi = false;
#if defined(CONFIG_COMMON_CLK)
static struct clk *hdmi_clk = NULL;
static struct clk *hdmi_ddc_clk = NULL;
#endif
static u32 power_enable_count = 0;
static u32 clk_enable_count = 0;
static struct mutex mlock;
#if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO)
static bool audio_enable = false;
#endif
static bool b_hdmi_suspend;
static bool b_hdmi_suspend_pre;
static struct cdev *my_cdev;
static dev_t devid ;
static struct class *hdmi_class;
hdmi_info_t ghdmi;
void hdmi_delay_ms(unsigned long ms)
{
u32 timeout = ms*HZ/1000;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(timeout);
}
void hdmi_delay_us(unsigned long us)
{
udelay(us);
}
unsigned int hdmi_get_soc_version(void)
{
unsigned int version = 0;
#if defined(CONFIG_ARCH_SUN8IW7)
unsigned int chip_ver = sunxi_get_soc_ver();
switch (chip_ver) {
case SUN8IW7P1_REV_A:
case SUN8IW7P2_REV_A:
version = 0;
break;
case SUN8IW7P1_REV_B:
case SUN8IW7P2_REV_B:
version = 1;
}
#endif
return version;
}
static int hdmi_parse_io_config(void)
{
disp_gpio_set_t *gpio_info;
int i, ret;
char io_name[32];
for (i=0; i<HDMI_IO_NUM; i++) {
gpio_info = &(hdmi_io[i]);
sprintf(io_name, "hdmi_io_%d", i);
ret = disp_sys_script_get_item("hdmi", io_name, (int *)gpio_info, sizeof(disp_gpio_set_t)/sizeof(int));
if (ret == 3)
hdmi_io_used[i]= 1;
else
hdmi_io_used[i] = 0;
}
return 0;
}
static int hdmi_io_config(u32 bon)
{
int hdl,i;
for (i=0; i<HDMI_IO_NUM; i++) {
if (hdmi_io_used[i]) {
disp_gpio_set_t gpio_info[1];
memcpy(gpio_info, &(hdmi_io[i]), sizeof(disp_gpio_set_t));
if (!bon) {
gpio_info->mul_sel = 7;
}
hdl = disp_sys_gpio_request(gpio_info, 1);
disp_sys_gpio_release(hdl, 2);
}
}
return 0;
}
#if defined(CONFIG_COMMON_CLK)
static int hdmi_clk_enable(void)
{
int ret = 0;
if (hdmi_clk)
ret = clk_prepare_enable(hdmi_clk);
if (0 != ret)
return ret;
if (hdmi_ddc_clk)
ret = clk_prepare_enable(hdmi_ddc_clk);
if (0 != ret)
clk_disable(hdmi_clk);
return ret;
}
static int hdmi_clk_disable(void)
{
if (hdmi_clk)
clk_disable(hdmi_clk);
if (hdmi_ddc_clk)
clk_disable(hdmi_ddc_clk);
return 0;
}
static int hdmi_clk_config(u32 vic)
{
int index = 0;
index = hdmi_core_get_video_info(vic);
if (hdmi_clk)
clk_set_rate(hdmi_clk, video_timing[index].pixel_clk);
return 0;
}
#else
static int hdmi_clk_enable(void){}
static int hdmi_clk_disable(void){}
static int hdmi_clk_config(u32 vic){}
#endif
/* hdmi_clk_enable_prepare - prepare for hdmi enable
* if there is some other clk will affect hdmi module,
* should enable these clk before enable hdmi
*/
int hdmi_clk_enable_prepare(void)
{
int ret = 0;
pr_warn("%s()L%d\n", __func__, __LINE__);
if (hdmi_clk)
ret = sunxi_clk_enable_prepare(hdmi_clk);
if (0 != ret)
return ret;
return ret;
}
/* hdmi_clk_disable_prepare - prepare for hdmi disable
* if there is some other clk will affect hdmi module,
* should disable these clk after disable hdmi
*/
int hdmi_clk_disable_prepare(void)
{
pr_warn("%s()L%d\n", __func__, __LINE__);
if (hdmi_clk)
sunxi_clk_disable_prepare(hdmi_clk);
return 0;
}
#ifdef CONFIG_AW_AXP
static int hdmi_power_enable(char *name)
{
struct regulator *regu= NULL;
int ret = -1;
regu= regulator_get(NULL, name);
if (IS_ERR(regu)) {
pr_err("%s: some error happen, fail to get regulator %s\n", __func__, name);
goto exit;
}
/* enalbe regulator */
ret = regulator_enable(regu);
if (0 != ret) {
pr_err("%s: some error happen, fail to enable regulator %s!\n", __func__, name);
goto exit1;
} else {
__inf("suceess to enable regulator %s!\n", name);
}
exit1:
/* put regulater, when module exit */
regulator_put(regu);
exit:
return ret;
}
static int hdmi_power_disable(char *name)
{
struct regulator *regu= NULL;
int ret = 0;
regu= regulator_get(NULL, name);
if (IS_ERR(regu)) {
__wrn("%s: some error happen, fail to get regulator %s\n", __func__, name);
goto exit;
}
//disalbe regulator
ret = regulator_disable(regu);
if (0 != ret) {
__wrn("%s: some error happen, fail to disable regulator %s!\n", __func__, name);
goto exit1;
} else {
__inf("suceess to disable regulator %s!\n", name);
}
exit1:
//put regulater, when module exit
regulator_put(regu);
exit:
return ret;
}
#else
static int hdmi_power_enable(char *name){return 0;}
static int hdmi_power_disable(char *name){return 0;}
#endif
static s32 hdmi_enable(void)
{
__inf("[hdmi_enable]\n");
mutex_lock(&mlock);
if (1 != ghdmi.bopen) {
hdmi_clk_config(ghdmi.mode);
hdmi_core_set_video_enable(1);
ghdmi.bopen = 1;
}
mutex_unlock(&mlock);
return 0;
}
static s32 hdmi_disable(void)
{
__inf("[hdmi_disable]\n");
mutex_lock(&mlock);
if (0 != ghdmi.bopen) {
hdmi_core_set_video_enable(0);
ghdmi.bopen = 0;
}
mutex_unlock(&mlock);
return 0;
}
static struct disp_hdmi_mode hdmi_mode_tbl[] = {
{DISP_TV_MOD_480I, HDMI1440_480I, },
{DISP_TV_MOD_576I, HDMI1440_576I, },
{DISP_TV_MOD_480P, HDMI480P, },
{DISP_TV_MOD_576P, HDMI576P, },
{DISP_TV_MOD_720P_50HZ, HDMI720P_50, },
{DISP_TV_MOD_720P_60HZ, HDMI720P_60, },
{DISP_TV_MOD_1080I_50HZ, HDMI1080I_50, },
{DISP_TV_MOD_1080I_60HZ, HDMI1080I_60, },
{DISP_TV_MOD_1080P_24HZ, HDMI1080P_24, },
{DISP_TV_MOD_1080P_50HZ, HDMI1080P_50, },
{DISP_TV_MOD_1080P_60HZ, HDMI1080P_60, },
{DISP_TV_MOD_1080P_25HZ, HDMI1080P_25, },
{DISP_TV_MOD_1080P_30HZ, HDMI1080P_30, },
{DISP_TV_MOD_1080P_24HZ_3D_FP, HDMI1080P_24_3D_FP,},
{DISP_TV_MOD_720P_50HZ_3D_FP, HDMI720P_50_3D_FP, },
{DISP_TV_MOD_720P_60HZ_3D_FP, HDMI720P_60_3D_FP, },
{DISP_TV_MOD_3840_2160P_30HZ, HDMI3840_2160P_30, },
{DISP_TV_MOD_3840_2160P_25HZ, HDMI3840_2160P_25, },
{DISP_TV_MOD_3840_2160P_24HZ, HDMI3840_2160P_24, },
{DISP_TV_MOD_4096_2160P_24HZ, HDMI4096_2160P_24, },
};
static u32 hdmi_get_vic(u32 mode)
{
u32 hdmi_mode = DISP_TV_MOD_720P_50HZ;
u32 i;
bool find = false;
for (i=0; i<sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode); i++)
{
if (hdmi_mode_tbl[i].mode == mode) {
hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;
find = true;
break;
}
}
if (false == find)
pr_warn("[HDMI]can't find vic for mode(%d)\n", mode);
return hdmi_mode;
}
static s32 hdmi_set_display_mode(u32 mode)
{
u32 hdmi_mode;
u32 i;
bool find = false;
__inf("[hdmi_set_display_mode],mode:%d\n",mode);
for (i=0; i<sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode); i++)
{
if (hdmi_mode_tbl[i].mode == (enum disp_tv_mode)mode) {
hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;
find = true;
break;
}
}
if (find) {
ghdmi.mode = hdmi_mode;
return hdmi_core_set_video_mode(hdmi_mode);
} else {
__wrn("unsupported video mode %d when set display mode\n", mode);
return -1;
}
}
#if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO)
static s32 hdmi_audio_enable(u8 mode, u8 channel)
{
__inf("[hdmi_audio_enable],mode:%d,ch:%d\n",mode, channel);
mutex_lock(&mlock);
audio_enable = mode;
mutex_unlock(&mlock);
return hdmi_core_set_audio_enable(audio_enable);
}
static s32 hdmi_set_audio_para(hdmi_audio_t * audio_para)
{
__inf("[hdmi_set_audio_para]\n");
return hdmi_core_audio_config(audio_para);
}
#endif
static s32 hdmi_mode_support(u32 mode)
{
u32 hdmi_mode;
u32 i;
bool find = false;
for (i=0; i<sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode); i++)
{
if (hdmi_mode_tbl[i].mode == (enum disp_tv_mode)mode) {
hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;
find = true;
break;
}
}
if (find) {
return hdmi_core_mode_support(hdmi_mode);
} else {
return 0;
}
}
static s32 hdmi_get_HPD_status(void)
{
return hdmi_core_hpd_check();
}
static s32 hdmi_get_hdcp_enable(void)
{
return hdmi_core_get_hdcp_enable();
}
static s32 hdmi_get_video_timming_info(struct disp_video_timings **video_info)
{
struct disp_video_timings *info;
int ret = -1;
int i, list_num;
info = video_timing;
list_num = hdmi_core_get_list_num();
for (i=0; i<list_num; i++) {
if (info->vic == ghdmi.mode) {
*video_info = info;
ret = 0;
break;
}
info ++;
}
return ret;
}
static s32 hdmi_get_input_csc(void)
{
return hdmi_core_get_csc_type();
}
static int hdmi_run_thread(void *parg)
{
while (1) {
if (kthread_should_stop()) {
break;
}
mutex_lock(&mlock);
if (false == b_hdmi_suspend) {
/* normal state */
b_hdmi_suspend_pre = b_hdmi_suspend;
mutex_unlock(&mlock);
hdmi_core_loop();
if (false == b_hdmi_suspend) {
if (hdmi_get_hdcp_enable()==1)
hdmi_delay_ms(100);
else
hdmi_delay_ms(200);
}
} else {
/* suspend state */
if (false == b_hdmi_suspend_pre) {
/* first time after enter suspend state */
//hdmi_core_enter_lp();
}
b_hdmi_suspend_pre = b_hdmi_suspend;
mutex_unlock(&mlock);
}
}
return 0;
}
void cec_msg_sent(char *buf)
{
char *envp[2];
envp[0] = buf;
envp[1] = NULL;
kobject_uevent_env(&ghdmi.dev->kobj, KOBJ_CHANGE, envp);
}
#define CEC_BUF_SIZE 32
static int cec_thread(void *parg)
{
int ret = 0;
char buf[CEC_BUF_SIZE];
unsigned char msg;
while (1) {
if (kthread_should_stop()) {
break;
}
mutex_lock(&mlock);
ret = -1;
if (false == b_hdmi_suspend) {
ret = hdmi_core_cec_get_simple_msg(&msg);
}
mutex_unlock(&mlock);
if (0 == ret) {
memset(buf, 0, CEC_BUF_SIZE);
snprintf(buf, sizeof(buf), "CEC_MSG=0x%x", msg);
cec_msg_sent(buf);
}
hdmi_delay_ms(10);
}
return 0;
}
#if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH)
static struct switch_dev hdmi_switch_dev = {
.name = "hdmi",
};
s32 disp_set_hdmi_detect(bool hpd);
static void hdmi_report_hpd_work(struct work_struct *work)
{
if (hdmi_get_HPD_status()) {
if (hdmi_switch_dev.dev)
switch_set_state(&hdmi_switch_dev, 1);
disp_set_hdmi_detect(1);
__inf("switch_set_state 1\n");
} else {
if (hdmi_switch_dev.dev)
switch_set_state(&hdmi_switch_dev, 0);
disp_set_hdmi_detect(0);
__inf("switch_set_state 0\n");
}
}
s32 hdmi_hpd_state(u32 state)
{
if (state == 0) {
if (hdmi_switch_dev.dev)
switch_set_state(&hdmi_switch_dev, 0);
} else {
if (hdmi_switch_dev.dev)
switch_set_state(&hdmi_switch_dev, 1);
}
return 0;
}
#else
static void hdmi_report_hpd_work(struct work_struct *work)
{
}
s32 hdmi_hpd_state(u32 state)
{
return 0;
}
#endif
/**
* hdmi_hpd_report - report hdmi hot plug state to user space
* @hotplug: 0: hdmi plug out; 1:hdmi plug in
*
* always return success.
*/
s32 hdmi_hpd_event(void)
{
schedule_work(&ghdmi.hpd_work);
return 0;
}
static s32 hdmi_suspend(void)
{
hdmi_core_update_detect_time(0);
if (hdmi_used && (false == b_hdmi_suspend)) {
if (HDMI_task) {
kthread_stop(HDMI_task);
HDMI_task = NULL;
}
if (hdmi_cec_support && cec_task) {
kthread_stop(cec_task);
cec_task = NULL;
}
mutex_lock(&mlock);
b_hdmi_suspend = true;
hdmi_core_enter_lp();
if (0 != clk_enable_count) {
hdmi_clk_disable();
clk_enable_count --;
}
if (0 != io_enable_count) {
hdmi_io_config(0);
io_enable_count --;
}
if ((hdmi_power_used) && (0 != power_enable_count)) {
hdmi_power_disable(hdmi_power);
power_enable_count --;
}
mutex_unlock(&mlock);
pr_info("[HDMI]hdmi suspend\n");
}
return 0;
}
static s32 hdmi_resume(void)
{
int ret;
mutex_lock(&mlock);
if (hdmi_used && (true == b_hdmi_suspend)) {
/* normal state */
if (clk_enable_count == 0) {
ret = hdmi_clk_enable();
if (0 == ret)
clk_enable_count ++;
else {
pr_warn("fail to enable hdmi's clock\n");
goto exit;
}
}
if ((hdmi_power_used) && (power_enable_count == 0)) {
hdmi_power_enable(hdmi_power);
power_enable_count ++;
}
if (io_enable_count == 0) {
hdmi_io_config(1);
io_enable_count ++;
}
/* first time after exit suspend state */
hdmi_core_exit_lp();
HDMI_task = kthread_create(hdmi_run_thread, (void*)0, "hdmi proc");
if (IS_ERR(HDMI_task)) {
s32 err = 0;
pr_warn("Unable to start kernel thread %s.\n\n", "hdmi proc");
err = PTR_ERR(HDMI_task);
HDMI_task = NULL;
} else
wake_up_process(HDMI_task);
if (hdmi_cec_support) {
cec_task = kthread_create(cec_thread, (void*)0, "cec proc");
if (IS_ERR(cec_task)) {
s32 err = 0;
pr_warn("Unable to start kernel thread %s.\n\n", "cec proc");
err = PTR_ERR(cec_task);
cec_task = NULL;
} else
wake_up_process(cec_task);
}
pr_info("[HDMI]hdmi resume\n");
}
exit:
mutex_unlock(&mlock);
hdmi_core_update_detect_time(200);//200ms
b_hdmi_suspend = false;
return 0;
}
#if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO)
extern void audio_set_hdmi_func(__audio_hdmi_func * hdmi_func);
#endif
extern s32 disp_set_hdmi_func(struct disp_device_func * func);
extern unsigned int disp_boot_para_parse(const char *name);
s32 hdmi_init(struct platform_device *pdev)
{
#if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO)
__audio_hdmi_func audio_func;
#if defined (CONFIG_SND_SUNXI_SOC_AUDIOHUB_INTERFACE)
__audio_hdmi_func audio_func_muti;
#endif
#endif
unsigned int value, output_type0, output_mode0, output_type1, output_mode1;
struct disp_device_func disp_func;
int ret = 0;
uintptr_t reg_base;
hdmi_used = 0;
b_hdmi_suspend_pre = b_hdmi_suspend = false;
hdmi_power_used = 0;
hdmi_used = 1;
/* parse boot para */
value = disp_boot_para_parse("boot_disp");
output_type0 = (value >> 8) & 0xff;
output_mode0 = (value) & 0xff;
output_type1 = (value >> 24)& 0xff;
output_mode1 = (value >> 16) & 0xff;
if ((output_type0 == DISP_OUTPUT_TYPE_HDMI) ||
(output_type1 == DISP_OUTPUT_TYPE_HDMI)) {
boot_hdmi = true;
ghdmi.bopen = 1;
ghdmi.mode = (output_type0 == DISP_OUTPUT_TYPE_HDMI)?output_mode0:output_mode1;
ghdmi.mode = hdmi_get_vic(ghdmi.mode);
}
/* iomap */
reg_base = (uintptr_t __force)of_iomap(pdev->dev.of_node, 0);
if (0 == reg_base) {
dev_err(&pdev->dev, "unable to map hdmi registers\n");
ret = -EINVAL;
goto err_iomap;
}
hdmi_core_set_base_addr(reg_base);
/* get clk */
hdmi_clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(hdmi_clk)) {
dev_err(&pdev->dev, "fail to get clk for hdmi\n");
goto err_clk_get;
}
clk_enable_count = hdmi_clk->enable_count;
hdmi_ddc_clk = of_clk_get(pdev->dev.of_node, 1);
if (IS_ERR(hdmi_ddc_clk)) {
dev_err(&pdev->dev, "fail to get clk for hdmi ddc\n");
goto err_clk_get;
}
/* parse io config */
hdmi_parse_io_config();
mutex_init(&mlock);
if (io_enable_count == 0) {
hdmi_io_config(1);
io_enable_count ++;
}
mutex_lock(&mlock);
if (clk_enable_count == 0) {
ret = hdmi_clk_enable();
clk_enable_count ++;
}
mutex_unlock(&mlock);
if (0 != ret) {
clk_enable_count --;
dev_err(&pdev->dev, "fail to enable hdmi clk\n");
goto err_clk_enable;
}
INIT_WORK(&ghdmi.hpd_work, hdmi_report_hpd_work);
#if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH)
switch_dev_register(&hdmi_switch_dev);
#endif
ret = disp_sys_script_get_item("hdmi", "hdmi_power", (int*)hdmi_power, 2);
if (2 == ret) {
hdmi_power_used = 1;
if (hdmi_power_used) {
pr_info("[HDMI] power %s\n", hdmi_power);
mutex_lock(&mlock);
ret = hdmi_power_enable(hdmi_power);
power_enable_count ++;
mutex_unlock(&mlock);
if (0 != ret) {
power_enable_count --;
dev_err(&pdev->dev, "fail to enable hdmi power %s\n", hdmi_power);
goto err_power;
}
}
}
ret = disp_sys_script_get_item("hdmi", "hdmi_cts_compatibility", &value, 1);
if (1 == ret) {
hdmi_core_set_cts_enable(value);
}
ret = disp_sys_script_get_item("hdmi", "hdmi_hdcp_enable", &value, 1);
if (1 == ret) {
hdmi_core_set_hdcp_enable(value);
}
ret = disp_sys_script_get_item("hdmi", "hdmi_hpd_mask", &value, 1);
if (1 == ret) {
hdmi_hpd_mask = value;
}
ret = disp_sys_script_get_item("hdmi", "hdmi_cec_support", &value, 1);
if ((1 == ret) && (1 == value)) {
hdmi_cec_support = true;
}
hdmi_core_cec_enable(hdmi_cec_support);
printk("[HDMI] cec support = %d\n", hdmi_cec_support);
hdmi_core_initial(boot_hdmi);
run_sem = kmalloc(sizeof(struct semaphore),GFP_KERNEL | __GFP_ZERO);
if (!run_sem) {
dev_err(&pdev->dev, "fail to kmalloc memory for run_sem\n");
goto err_sem;
}
sema_init((struct semaphore*)run_sem,0);
HDMI_task = kthread_create(hdmi_run_thread, (void*)0, "hdmi proc");
if (IS_ERR(HDMI_task)) {
s32 err = 0;
dev_err(&pdev->dev, "Unable to start kernel thread %s.\n\n", "hdmi proc");
err = PTR_ERR(HDMI_task);
HDMI_task = NULL;
goto err_thread;
}
wake_up_process(HDMI_task);
if (hdmi_cec_support) {
cec_task = kthread_create(cec_thread, (void*)0, "cec proc");
if (IS_ERR(cec_task)) {
s32 err = 0;
dev_err(&pdev->dev, "Unable to start kernel thread %s.\n\n", "cec proc");
err = PTR_ERR(cec_task);
cec_task = NULL;
goto err_thread;
}
wake_up_process(cec_task);
}
#if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO)
audio_func.hdmi_audio_enable = hdmi_audio_enable;
audio_func.hdmi_set_audio_para = hdmi_set_audio_para;
audio_set_hdmi_func(&audio_func);
#if defined (CONFIG_SND_SUNXI_SOC_AUDIOHUB_INTERFACE)
audio_func_muti.hdmi_audio_enable = hdmi_audio_enable;
audio_func_muti.hdmi_set_audio_para = hdmi_set_audio_para;
audio_set_muti_hdmi_func(&audio_func_muti);
#endif
#endif
memset(&disp_func, 0, sizeof(struct disp_device_func));
disp_func.enable = hdmi_enable;
disp_func.disable = hdmi_disable;
disp_func.set_mode = hdmi_set_display_mode;
disp_func.mode_support = hdmi_mode_support;
disp_func.get_HPD_status = hdmi_get_HPD_status;
disp_func.get_input_csc = hdmi_get_input_csc;
disp_func.get_video_timing_info = hdmi_get_video_timming_info;
disp_func.suspend = hdmi_suspend;
disp_func.resume = hdmi_resume;
disp_set_hdmi_func(&disp_func);
return 0;
err_thread:
kfree(run_sem);
err_sem:
hdmi_power_disable(hdmi_power);
err_power:
hdmi_clk_disable();
err_clk_enable:
err_clk_get:
iounmap((char __iomem *)reg_base);
err_iomap:
return -1;
}
s32 hdmi_exit(void)
{
if (hdmi_used) {
hdmi_core_exit();
if (run_sem) {
kfree(run_sem);
}
run_sem = NULL;
if (HDMI_task) {
kthread_stop(HDMI_task);
HDMI_task = NULL;
}
if (hdmi_cec_support && cec_task) {
kthread_stop(cec_task);
cec_task = NULL;
}
if ((1 == hdmi_power_used) && (0 != power_enable_count)) {
hdmi_power_disable(hdmi_power);
}
if (0 != clk_enable_count) {
hdmi_clk_disable();
clk_enable_count--;
}
}
return 0;
}
#ifndef CONFIG_OF
static struct resource hdmi_resource[1] =
{
[0] = {
.start = 0x01c16000,
.end = 0x01c165ff,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device hdmi_device =
{
.name = "hdmi",
.id = -1,
.num_resources = ARRAY_SIZE(hdmi_resource),
.resource = hdmi_resource,
.dev = {}
};
#else
static const struct of_device_id sunxi_hdmi_match[] = {
{ .compatible = "allwinner,sunxi-hdmi", },
{},
};
#endif
static ssize_t hdmi_debug_show(struct device *dev,struct device_attribute *attr, char *buf)
{
return sprintf(buf, "debug=%s\n", hdmi_print?"on" : "off");
}
static ssize_t hdmi_debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (count < 1)
return -EINVAL;
if (strnicmp(buf, "on", 2) == 0 || strnicmp(buf, "1", 1) == 0) {
hdmi_print = 1;
} else if (strnicmp(buf, "off", 3) == 0 || strnicmp(buf, "0", 1) == 0) {
hdmi_print = 0;
} else {
return -EINVAL;
}
return count;
}
static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_debug_show, hdmi_debug_store);
s32 hdmi_hpd_state(u32 state);
static ssize_t hdmi_state_show(struct device *dev,struct device_attribute *attr, char *buf)
{
return sprintf(buf, "nothing\n");
}
static ssize_t hdmi_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (count < 1)
return -EINVAL;
if (strnicmp(buf, "1", 1) == 0) {
hdmi_hpd_state(1);
} else {
hdmi_hpd_state(0);
}
return count;
}
static DEVICE_ATTR(state, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_state_show, hdmi_state_store);
static ssize_t hdmi_rgb_only_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "rgb_only=%s\n", rgb_only?"on" : "off");
}
static ssize_t hdmi_rgb_only_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (count < 1)
return -EINVAL;
if (strnicmp(buf, "on", 2) == 0 || strnicmp(buf, "1", 1) == 0) {
rgb_only = 1;
} else if (strnicmp(buf, "off", 3) == 0 || strnicmp(buf, "0", 1) == 0) {
rgb_only = 0;
} else {
return -EINVAL;
}
return count;
}
static DEVICE_ATTR(rgb_only, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_rgb_only_show, hdmi_rgb_only_store);
static ssize_t hdmi_hpd_mask_show(struct device *dev,struct device_attribute *attr, char *buf)
{
return sprintf(buf, "0x%x\n", hdmi_hpd_mask);
}
static ssize_t hdmi_hpd_mask_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long val;
if (count < 1)
return -EINVAL;
err = strict_strtoul(buf, 16, &val);
if (err) {
printk("Invalid size\n");
return err;
}
printk("val=0x%x\n", (u32)val);
hdmi_hpd_mask = val;
return count;
}
static DEVICE_ATTR(hpd_mask, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_hpd_mask_show, hdmi_hpd_mask_store);
static ssize_t hdmi_edid_show(struct device *dev,struct device_attribute *attr, char *buf)
{
void* pedid = (void*)hdmi_edid_get_data();
memcpy(buf, pedid, HDMI_EDID_LEN);
return HDMI_EDID_LEN;
}
static ssize_t hdmi_edid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return count;
}
static DEVICE_ATTR(edid, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_edid_show, hdmi_edid_store);
static ssize_t hdmi_hdcp_enable_show(struct device *dev,struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", hdmi_core_get_hdcp_enable());
}
static ssize_t hdmi_hdcp_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (count < 1)
return -EINVAL;
if (strnicmp(buf, "1", 1) == 0) {
if (1 != hdmi_core_get_hdcp_enable())
hdmi_core_set_hdcp_enable(1);
} else {
if (0 != hdmi_core_get_hdcp_enable())
hdmi_core_set_hdcp_enable(0);
}
return count;
}
static DEVICE_ATTR(hdcp_enable, S_IRUGO|S_IWUSR|S_IWGRP,hdmi_hdcp_enable_show, hdmi_hdcp_enable_store);
static int hdmi_probe(struct platform_device *pdev)
{
__inf("hdmi_probe call\n");
memset(&ghdmi, 0, sizeof(hdmi_info_t));
ghdmi.dev = &pdev->dev;
hdmi_init(pdev);
return 0;
}
static int hdmi_remove(struct platform_device *pdev)
{
__inf("hdmi_remove call\n");
hdmi_exit();
return 0;
}
static struct platform_driver hdmi_driver =
{
.probe = hdmi_probe,
.remove = hdmi_remove,
.driver =
{
.name = "hdmi",
.owner = THIS_MODULE,
.of_match_table = sunxi_hdmi_match,
},
};
static int hdmi_open(struct inode *inode, struct file *file)
{
return 0;
}
static int hdmi_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t hdmi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static ssize_t hdmi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static int hdmi_mmap(struct file *file, struct vm_area_struct * vma)
{
return 0;
}
static long hdmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
static const struct file_operations hdmi_fops =
{
.owner = THIS_MODULE,
.open = hdmi_open,
.release = hdmi_release,
.write = hdmi_write,
.read = hdmi_read,
.unlocked_ioctl = hdmi_ioctl,
.mmap = hdmi_mmap,
};
static struct attribute *hdmi_attributes[] =
{
&dev_attr_debug.attr,
&dev_attr_state.attr,
&dev_attr_rgb_only.attr,
&dev_attr_hpd_mask.attr,
&dev_attr_edid.attr,
&dev_attr_hdcp_enable.attr,
NULL
};
static struct attribute_group hdmi_attribute_group = {
.name = "attr",
.attrs = hdmi_attributes
};
static int __init hdmi_module_init(void)
{
int ret = 0, err;
alloc_chrdev_region(&devid, 0, 1, "hdmi");
my_cdev = cdev_alloc();
cdev_init(my_cdev, &hdmi_fops);
my_cdev->owner = THIS_MODULE;
err = cdev_add(my_cdev, devid, 1);
if (err) {
__wrn("cdev_add fail.\n");
return -1;
}
hdmi_class = class_create(THIS_MODULE, "hdmi");
if (IS_ERR(hdmi_class)) {
__wrn("class_create fail\n");
return -1;
}
ghdmi.dev = device_create(hdmi_class, NULL, devid, NULL, "hdmi");
ret = sysfs_create_group(&ghdmi.dev->kobj,&hdmi_attribute_group);
#ifndef CONFIG_OF
ret = platform_device_register(&hdmi_device);
#endif
if (ret == 0) {
ret = platform_driver_register(&hdmi_driver);
}
__inf("hdmi_module_init\n");
return ret;
}
static void __exit hdmi_module_exit(void)
{
__inf("hdmi_module_exit\n");
platform_driver_unregister(&hdmi_driver);
#ifndef CONFIG_OF
platform_device_unregister(&hdmi_device);
#endif
class_destroy(hdmi_class);
cdev_del(my_cdev);
}
late_initcall(hdmi_module_init);
module_exit(hdmi_module_exit);
MODULE_AUTHOR("tyle");
MODULE_DESCRIPTION("hdmi driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:hdmi");