/* * Allwinner SoCs hdmi2.0 driver. * * Copyright (C) 2016 Allwinner. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include "config.h" #if defined(__LINUX_PLAT__) #include #include #include #include #include #include #include #include #include #include #include #endif #include "hdmi_tx.h" /*#include "hdmi_test.h"*/ #define DDC_PIN_ACTIVE "ddc_active" #define DDC_PIN_SLEEP "ddc_sleep" #define CEC_PIN_ACTIVE "cec_active" #define CEC_PIN_SLEEP "cec_sleep" #define ESM_REG_BASE_OFFSET 0x8000 #define HDCP22_FIRMWARE_SIZE (1024 * 256) #define HDCP22_DATA_SIZE (1024 * 128) #if defined(__LINUX_PLAT__) static dev_t devid; static struct cdev *hdmi_cdev; static struct class *hdmi_class; static struct device *hdev; #endif static struct hdmi_tx_drv *hdmi_drv; /*0x10: force unplug; * 0x11: force plug; * 0x1xx: unreport hpd state * 0x1xxx: mask hpd */ u32 hdmi_hpd_mask_pre; u32 hdmi_hpd_mask; static u32 hpd_out_count; static u32 re_read_edid_delay_count; u32 hdmi_clk_enable_mask; static u32 hdmi_pin_config_mask; u32 hdmi_enable_mask; u32 hdmi_suspend_mask; static struct scene_lock hdmi_standby_lock; static bool boot_hdmi; static bool video_on; static u32 hpd_state; static u8 hdcp_encrypt_status; static u8 cec_wakeup; u32 hdmi_printf; #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) static struct switch_dev hdmi_switch_dev = { .name = "hdmi", }; #endif #if defined(__LINUX_PLAT__) static int hdmi_run_thread(void *parg); #endif struct hdmi_tx_drv *get_hdmi_drv(void) { return hdmi_drv; } u32 get_drv_hpd_state(void) { return hpd_state; } /** * @short List of the devices * Linked list that contains the installed devices */ static LIST_HEAD(devlist_global); #ifdef CONFIG_AW_AXP 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 { VIDEO_INF("suceess to enable regulator %s!\n", name); } exit1: /* put regulater, when module exit */ regulator_put(regu); exit: return ret; } int hdmi_power_disable(char *name) { struct regulator *regu = NULL; int ret = 0; 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; } /*disalbe regulator*/ ret = regulator_disable(regu); if (0 != ret) { pr_err("%s: some error happen, fail to disable regulator %s!\n", __func__, name); goto exit1; } else { VIDEO_INF("suceess to disable regulator %s!\n", name); } exit1: /*put regulater, when module exit*/ regulator_put(regu); exit: return ret; } #else int hdmi_power_enable(char *name) {return 0; } int hdmi_power_disable(char *name) {return 0; } #endif #if defined(__LINUX_PLAT__) static void hdmi_clk_enable(void) { if (hdmi_clk_enable_mask) return; clk_set_rate(hdmi_drv->hdmi_hdcp_clk, 300000000); if (hdmi_drv->hdmi_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_clk) != 0) pr_info("hdmi clk enable failed!\n"); if (hdmi_drv->hdmi_ddc_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_ddc_clk) != 0) pr_info("hdmi ddc clk enable failed!\n"); if (hdmi_drv->hdmi_hdcp_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_hdcp_clk) != 0) pr_info("hdmi ddc clk enable failed!\n"); if (hdmi_drv->hdmi_cec_clk != NULL) { struct clk *cec_clk_parent = NULL; cec_clk_parent = clk_get(NULL, "periph32k"); if (cec_clk_parent == NULL || IS_ERR(cec_clk_parent)) { pr_err("periph32k clk get failed\n"); } else { if (clk_set_parent(hdmi_drv->hdmi_cec_clk, cec_clk_parent) != 0) pr_err("hdmi ddc clk set parent clk 'periph32k' failed\n"); } if (clk_prepare_enable(hdmi_drv->hdmi_cec_clk) != 0) pr_info("hdmi cec clk enable failed!\n"); } hdmi_clk_enable_mask = 1; } static void hdmi_resume_clk_enable(void) { if (hdmi_clk_enable_mask) return; clk_set_rate(hdmi_drv->hdmi_hdcp_clk, 300000000); if (hdmi_drv->hdmi_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_clk) != 0) pr_info("hdmi clk enable failed!\n"); if ((hdmi_drv->hdmi_ddc_clk != NULL) && (!hdmi_drv->cec_super_standby)) if (clk_prepare_enable(hdmi_drv->hdmi_ddc_clk) != 0) pr_info("hdmi ddc clk enable failed!\n"); if (hdmi_drv->hdmi_hdcp_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_hdcp_clk) != 0) pr_info("hdmi ddc clk enable failed!\n"); if ((hdmi_drv->hdmi_cec_clk != NULL) && (!hdmi_drv->cec_super_standby)) { struct clk *cec_clk_parent = NULL; cec_clk_parent = clk_get(NULL, "periph32k"); if (cec_clk_parent == NULL || IS_ERR(cec_clk_parent)) { pr_err("periph32k clk get failed\n"); } else { if (clk_set_parent(hdmi_drv->hdmi_cec_clk, cec_clk_parent) != 0) pr_err("hdmi ddc clk set parent clk 'periph32k' failed\n"); } if (clk_prepare_enable(hdmi_drv->hdmi_cec_clk) != 0) pr_info("hdmi cec clk enable failed!\n"); } hdmi_clk_enable_mask = 1; } static void hdmi_clk_disable(void) { if (!hdmi_clk_enable_mask) return; hdmi_clk_enable_mask = 0; if (hdmi_drv->hdmi_hdcp_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_hdcp_clk); if (hdmi_drv->hdmi_ddc_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_ddc_clk); if (hdmi_drv->hdmi_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_clk); if (hdmi_drv->hdmi_cec_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_cec_clk); } static void hdmi_suspend_clk_disable(void) { if (!hdmi_clk_enable_mask) return; hdmi_clk_enable_mask = 0; if (hdmi_drv->hdmi_hdcp_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_hdcp_clk); if ((hdmi_drv->hdmi_ddc_clk != NULL) && (!hdmi_drv->cec_super_standby)) clk_disable_unprepare(hdmi_drv->hdmi_ddc_clk); if (hdmi_drv->hdmi_clk != NULL) clk_disable_unprepare(hdmi_drv->hdmi_clk); if ((hdmi_drv->hdmi_cec_clk != NULL) && (!hdmi_drv->cec_super_standby)) clk_disable_unprepare(hdmi_drv->hdmi_cec_clk); } static void hdmi_pin_configure(void) { s32 ret = 0; struct pinctrl_state *state, *cec_state; if (hdmi_pin_config_mask) return; /*pin configuration for ddc*/ if (hdmi_drv->pctl != NULL) { state = pinctrl_lookup_state(hdmi_drv->pctl, DDC_PIN_ACTIVE); if (IS_ERR(state)) { pr_info("pinctrl_lookup_state for HDMI2.0 SCL fail\n"); return; } ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 DDC fail\n"); cec_state = pinctrl_lookup_state(hdmi_drv->pctl, CEC_PIN_ACTIVE); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 CEC active fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, cec_state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 CEC active fail\n"); } hdmi_pin_config_mask = 1; } static void hdmi_resume_pin_configure(void) { s32 ret = 0; struct pinctrl_state *state, *cec_state; if (hdmi_pin_config_mask) return; /*pin configuration for ddc*/ if (hdmi_drv->pctl != NULL) { state = pinctrl_lookup_state(hdmi_drv->pctl, DDC_PIN_ACTIVE); if (IS_ERR(state)) { pr_info("pinctrl_lookup_state for HDMI2.0 SCL fail\n"); return; } ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 DDC fail\n"); if (!hdmi_drv->cec_super_standby) { cec_state = pinctrl_lookup_state(hdmi_drv->pctl, CEC_PIN_ACTIVE); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 CEC active fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, cec_state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 CEC active fail\n"); } } hdmi_pin_config_mask = 1; } static void hdmi_pin_release(void) { s32 ret = 0; struct pinctrl_state *state; if (!hdmi_pin_config_mask) return; /*pin configuration for ddc*/ if (hdmi_drv->pctl != NULL) { state = pinctrl_lookup_state(hdmi_drv->pctl, DDC_PIN_SLEEP); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 SCL fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 DDC fail\n"); state = pinctrl_lookup_state(hdmi_drv->pctl, CEC_PIN_SLEEP); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 CEC SLEEP fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 CEC SLEEP fail\n"); } hdmi_pin_config_mask = 0; } static void hdmi_suspend_pin_release(void) { s32 ret = 0; struct pinctrl_state *state; if (!hdmi_pin_config_mask) return; /*pin configuration for ddc*/ if (hdmi_drv->pctl != NULL) { state = pinctrl_lookup_state(hdmi_drv->pctl, DDC_PIN_SLEEP); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 SCL fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 DDC fail\n"); if (!hdmi_drv->cec_super_standby) { state = pinctrl_lookup_state(hdmi_drv->pctl, CEC_PIN_SLEEP); if (IS_ERR(state)) pr_info("pinctrl_lookup_state for HDMI2.0 CEC SLEEP fail\n"); ret = pinctrl_select_state(hdmi_drv->pctl, state); if (ret < 0) pr_info("pinctrl_select_state for HDMI2.0 CEC SLEEP fail\n"); } } hdmi_pin_config_mask = 0; } static void hdmi_sys_source_configure(void) { LOG_TRACE(); hdmi_clk_enable(); hdmi_pin_configure(); } static void hdmi_resume_sys_source_configure(void) { LOG_TRACE(); hdmi_resume_clk_enable(); hdmi_resume_pin_configure(); } static void hdmi_sys_source_release(void) { LOG_TRACE(); hdmi_clk_disable(); hdmi_pin_release(); } static void hdmi_suspend_sys_source_release(void) { LOG_TRACE(); hdmi_suspend_clk_disable(); hdmi_suspend_pin_release(); } static void hdmi_clk_reset(void) { hdmi_clk_disable(); udelay(10); hdmi_clk_enable(); hdmi_clk_disable(); udelay(10); hdmi_clk_enable(); } /*static void hdmi_sys_source_reset(void) { hdmi_sys_source_release(); mdelay(10); hdmi_sys_source_configure(); }*/ #endif static void set_hdcp_status(u8 status) { hdcp_encrypt_status = status; } static s32 hdmi_enable(void) { s32 ret = 0; LOG_TRACE(); mutex_lock(&hdmi_drv->ctrl_mutex); if (hdmi_enable_mask == 1) { mutex_unlock(&hdmi_drv->ctrl_mutex); return 0; } if (hpd_state && (!video_on)) { ret = hdmi_enable_core(); video_on = true; } hdmi_enable_mask = 1; mutex_unlock(&hdmi_drv->ctrl_mutex); return ret; } static s32 hdmi_smooth_enable(void) { s32 ret = 0; LOG_TRACE(); mutex_lock(&hdmi_drv->ctrl_mutex); if (hpd_state && video_on) ret = hdmi_smooth_enable_core(); mutex_unlock(&hdmi_drv->ctrl_mutex); return ret; } static s32 hdmi_disable(void) { s32 ret; LOG_TRACE(); mutex_lock(&hdmi_drv->ctrl_mutex); if (hdmi_enable_mask == 0) { mutex_unlock(&hdmi_drv->ctrl_mutex); return 0; } #if defined(__LINUX_PLAT__) ret = hdmi_disable_core(); #endif video_on = false; hdmi_enable_mask = 0; mutex_unlock(&hdmi_drv->ctrl_mutex); return ret; } #if defined(CONFIG_SND_SUNXI_SOC_HDMIAUDIO) s32 hdmi_audio_enable(u8 enable, u8 channel) { s32 ret = 0; ret = hdmi_core_audio_enable(enable, channel); return ret; } #endif #if defined(__LINUX_PLAT__) static s32 hdmi_suspend(void) { LOG_TRACE(); if (hdmi_suspend_mask) return 0; if (hdmi_drv->cec_support) { hdmi_cec_send_inactive_source(); if (!hdmi_drv->cec_super_standby) hdmi_cec_enable(0); else hdmi_cec_soft_disable(); cec_thread_exit(); } if (hdmi_drv->hdmi_core->mode.pHdcp.hdcp_on) set_hdcp_status(HDCP_ING); if (hdmi_drv->hdmi_core->mode.pHdcp.use_hdcp) hdmi_drv->hdmi_core->dev_func.hdcp_close(); if (hdmi_drv->hdmi_task) { kthread_stop(hdmi_drv->hdmi_task); hdmi_drv->hdmi_task = NULL; } hdmi_suspend_sys_source_release(); if (hdmi_drv->cec_support && hdmi_drv->cec_super_standby) { enable_wakeup_src(CPUS_HDMICEC_SRC, 0); scene_lock(&hdmi_standby_lock); } if (hdmi_drv->power_use) hdmi_power_disable(hdmi_drv->power); mutex_lock(&hdmi_drv->ctrl_mutex); hdmi_suspend_mask = 1; cec_wakeup = 0; mutex_unlock(&hdmi_drv->ctrl_mutex); return 0; } extern int sunxi_smc_refresh_hdcp(void); static s32 hdmi_resume(void) { s32 ret = 0; /*u32 hpd_state = 0;*/ LOG_TRACE(); #ifdef CONFIG_SUNXI_SMC if (sunxi_smc_refresh_hdcp()) { pr_err("refresh hdcp key failed!!!\n"); } #endif mutex_lock(&hdmi_drv->ctrl_mutex); if (hdmi_suspend_mask == 0) { mutex_unlock(&hdmi_drv->ctrl_mutex); return 0; } if (hdmi_drv->cec_support && hdmi_drv->cec_super_standby) { scene_unlock(&hdmi_standby_lock); disable_wakeup_src(CPUS_HDMICEC_SRC, 0); } if (hdmi_drv->power_use) hdmi_power_enable(hdmi_drv->power); hdmi_resume_sys_source_configure(); hdmi_clk_reset(); /*enable hpd sense*/ hpd_sense_enbale_core(hdmi_drv->hdmi_core); hpd_state = 0; #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) switch_set_state(&hdmi_switch_dev, hpd_state); #endif hdmi_drv->hdmi_task = kthread_create(hdmi_run_thread, (void *)0, "hdmi proc"); if (IS_ERR(hdmi_drv->hdmi_task)) { pr_info("Unable to start kernel thread %s.\n\n", "hdmi proc"); hdmi_drv->hdmi_task = NULL; } wake_up_process(hdmi_drv->hdmi_task); if (hdmi_drv->cec_support) cec_thread_init(hdmi_drv->parent_dev); #if defined(__LINUX_PLAT__) if (hdmi_drv->cec_support) { hdmi_cec_enable(1); if (!cec_wakeup) { hdmi_cec_wakup_request(); cec_wakeup = 1; } } #endif hdmi_suspend_mask = 0; mutex_unlock(&hdmi_drv->ctrl_mutex); return ret; } /*static void hdcp_handler(struct work_struct *work) { LOG_TRACE(); hdcp_handler_core(); }*/ static void edid_check(void) { LOG_TRACE(); if (!hdmi_drv->hdmi_core->mode.edid_done) { mutex_lock(&hdmi_drv->hdcp_mutex); edid_read_cap(); mutex_unlock(&hdmi_drv->hdcp_mutex); #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) if (!(hdmi_hpd_mask & 0x100)) switch_set_state(&hdmi_switch_dev, 1); #endif edid_correct_hardware_config(); } } static void hdmi_configure(struct hdmi_tx_drv *drv) { resistor_calibration_core(drv->hdmi_core, 0x10004, 0x80c00000); hdmi_configure_core(drv->hdmi_core); } /*sys_config.fex setting for hpd*/ static void hdmi_hpd_sys_config_set(void) { /*To enhance the ddc ability*/ if (hdmi_drv->ddc_ctrl_en == 1) { if (0 != gpio_direction_output(hdmi_drv->ddc_ctrl.gpio, 1)) { pr_info("ddc ctrl gpio set 1 error!\n"); return; } } } /*release sys_config.fex setting for hpd*/ static void hdmi_hpd_sys_config_release(void) { pr_info("%s\n", __func__); if (hdmi_drv->ddc_ctrl_en == 1) { if (0 != gpio_direction_output(hdmi_drv->ddc_ctrl.gpio, 0)) { pr_info("ddc ctrl gpio set 0 error!\n"); return; } } } static void hdmi_plugin_proc(void) { LOG_TRACE(); pr_info("HDMI cable is connected\n"); if (!(hdmi_hpd_mask & 0x10)) hpd_state = 1; hdmi_hpd_sys_config_set(); mutex_lock(&hdmi_drv->hdcp_mutex); edid_read_cap(); mutex_unlock(&hdmi_drv->hdcp_mutex); if (hdmi_drv->cec_support) { hdmi_cec_enable(1); if (!cec_wakeup) { hdmi_cec_wakup_request(); cec_wakeup = 1; } } if (hdmi_drv->hdmi_core->mode.edid_done) edid_set_video_prefered_core();/*update some configs by edid*/ mutex_lock(&hdmi_drv->ctrl_mutex); if ((!video_on) && (hdmi_enable_mask)) { hdmi_configure(hdmi_drv); video_on = true; } mutex_unlock(&hdmi_drv->ctrl_mutex); #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) if ((!(hdmi_hpd_mask & 0x100)) && hdmi_drv->hdmi_core->mode.edid_done) switch_set_state(&hdmi_switch_dev, 1); #endif edid_correct_hardware_config(); } static void hdmi_plugout_proc(void) { LOG_TRACE(); pr_info("HDMI cable is disconnected\n"); if (!(hdmi_hpd_mask & 0x10)) hpd_state = 0; video_on = false; cec_wakeup = 0; if (hdmi_drv->cec_support) hdmi_cec_enable(0); if (hdmi_drv->hdmi_core->mode.pHdcp.hdcp_on) { hdmi_drv->hdmi_core->dev_func.hdcp_disconfigure(); set_hdcp_status(HDCP_ING); } hdmi_hpd_out_core_process(hdmi_drv->hdmi_core); hdmi_clk_reset(); hpd_sense_enbale_core(hdmi_drv->hdmi_core); #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) if (!(hdmi_hpd_mask & 0x100)) switch_set_state(&hdmi_switch_dev, 0); #endif } static void hdmi_hpd_mask_process(void) { if (hdmi_hpd_mask == 0x10) { hdmi_plugout_proc(); } else if (hdmi_hpd_mask == 0x11) { msleep(400); hdmi_plugin_proc(); } else { pr_info("Unknow hpd event\n"); } /*hdmi_hpd_mask &= 0x0f0f;*/ } static int hdmi_run_thread(void *parg) { u32 hpd_state_now = 0; u32 hdcp_status = 0; u32 i; while (1) { if (kthread_should_stop()) break; if (hdmi_hpd_mask & 0x1000) { msleep(200); continue; } if (hdmi_hpd_mask & 0x10) { if (hdmi_hpd_mask != hdmi_hpd_mask_pre) { hdmi_hpd_mask_pre = hdmi_hpd_mask; hdmi_hpd_mask_process(); } msleep(80); continue; } hpd_state_now = hdmi_core_get_hpd_state(); if (hpd_state_now != hpd_state) { /*HPD Event Happen*/ if (!hpd_state_now) { re_read_edid_delay_count = 20; hdmi_plugout_proc(); hpd_out_count = 0; } else { for (i = 0; i < 2; i++) { msleep(200); hpd_state_now = hdmi_core_get_hpd_state(); if (hpd_state_now == hpd_state) break;/*it's not a real hpd event*/ } if (i >= 2) { hpd_out_count = 41; /*it's a real hpd event*/ hdmi_plugin_proc(); re_read_edid_delay_count = 0; } } } else { if (hpd_state_now && (hpd_state || (hdmi_hpd_mask == 0x1)) && hdmi_drv->hdmi_core->mode.pHdcp.hdcp_on && hdmi_drv->hdmi_core->mode.pHdcp.use_hdcp) { mutex_lock(&hdmi_drv->hdcp_mutex); hdcp_status = get_hdcp_status_core(); if (hdcp_status == 0) set_hdcp_status(HDCP_SUCCESS); else if (hdcp_status == -1) set_hdcp_status(HDCP_FAILED); else if (hdcp_status == 1) set_hdcp_status(HDCP_ING); else ;/*pr_info("Error: Unkown hdcp status\n");*/ mutex_unlock(&hdmi_drv->hdcp_mutex); } } msleep(80); hpd_out_count++; if (hpd_out_count == 0) hpd_out_count = 41; if ((!hpd_state) && (hpd_out_count == 40)) hdmi_hpd_sys_config_release(); re_read_edid_delay_count++; if (re_read_edid_delay_count == 0) re_read_edid_delay_count = 20; if (hpd_state && (re_read_edid_delay_count == 19)) edid_check(); } return 0; } #endif static void drv_global_value_init(void) { hdmi_printf = 0; hdmi_hpd_mask_pre = 0; hdmi_hpd_mask = 0; hpd_out_count = 0; re_read_edid_delay_count = 0; hdmi_clk_enable_mask = 0; hdmi_pin_config_mask = 0; hdmi_enable_mask = 0; hdmi_suspend_mask = 0; boot_hdmi = false; video_on = false; hpd_state = 0; hdcp_encrypt_status = HDCP_DISABLE; } #if defined(__LINUX_PLAT__) static int hdmi_tx_init(struct platform_device *pdev) #else s32 hdmi_init(void) #endif { int ret = 0; int phy_model = 301; uintptr_t reg_base = 0x0; struct disp_device_func disp_func; const char *hdmi_power; #if defined(__LINUX_PLAT__) unsigned int value/*, value0[10]*/; unsigned int output_type0, output_mode0, output_type1, output_mode1; struct clk *hdmi_parent_clk = NULL, *ddc_parent_clk = NULL; struct device_node *esm_np; u32 dts_esm_buff_phy_addr = 0, dts_esm_size_phy_addr = 0; void *dts_esm_buff_vir_addr = NULL, *dts_esm_size_vir_addr = NULL; u8 *esm_firm_vir_addr = NULL, *esm_data_vir_addr = NULL; hdcpParams_t hdcp; /*unsigned int boot_type; struct disp_device_config config;*/ #endif #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 LOG_TRACE(); pr_info("HDMI 2.0 driver init start!\n"); drv_global_value_init(); hdmi_drv = kmalloc(sizeof(struct hdmi_tx_drv), GFP_KERNEL); if (!hdmi_drv) { pr_err("%s:Could not allocated hdmi_tx_dev\n", FUNC_NAME); return -1; } memset(hdmi_drv, 0, sizeof(struct hdmi_tx_drv)); hdmi_drv->hdmi_core = kmalloc(sizeof(struct hdmi_tx_core), GFP_KERNEL); if (!hdmi_drv->hdmi_core) { pr_err("%s:Could not allocated hdmi_tx_core\n", __func__); goto free_mem; } memset(hdmi_drv->hdmi_core, 0, sizeof(struct hdmi_tx_core)); esm_firm_vir_addr = dma_alloc_coherent(&pdev->dev, HDCP22_FIRMWARE_SIZE, &hdcp.esm_firm_phy_addr, GFP_KERNEL | __GFP_ZERO); hdcp.esm_firm_vir_addr = (unsigned long)esm_firm_vir_addr; hdcp.esm_firm_size = HDCP22_FIRMWARE_SIZE; esm_data_vir_addr = dma_alloc_coherent(&pdev->dev, HDCP22_DATA_SIZE, &hdcp.esm_data_phy_addr, GFP_KERNEL | __GFP_ZERO); hdcp.esm_data_size = HDCP22_DATA_SIZE; hdcp.esm_data_vir_addr = (unsigned long)esm_data_vir_addr; hdmi_drv->hdmi_core->blacklist_sink = -1; #if defined(__LINUX_PLAT__) hdmi_drv->pdev = pdev; hdmi_drv->parent_dev = &pdev->dev; /*Read video booting params from disp device tree*/ value = disp_boot_para_parse("boot_disp"); /*To check if hdmi has been configured in uboot*/ 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; pr_info("boot_hdmi=%s\n", boot_hdmi ? "ture" : "false"); /*parse the booting video params*/ /*boot_type = output_type0; config.mode = output_mode0; config.format = value1 & 0xff; config.bits = (value1 >> 8) & 0xff; config.cs = (value1 >> 16) & 0xffff; config.eotf = value2 & 0xff;*/ /* iomap */ reg_base = (uintptr_t __force)of_iomap(pdev->dev.of_node, 0); if (0 == reg_base) { pr_err("unable to map hdmi registers\n"); ret = -EINVAL; goto free_mem; } #else /* iomap */ reg_base = disp_getprop_regbase("hdmi", "reg", 0); if (0 == reg_base) { pr_err("unable to map hdmi registers\n"); ret = -1; kfree(hdmi_drv); return -1; } #endif hdmi_core_set_base_addr(reg_base); hdmi_drv->reg_base = reg_base; hdcp.esm_hpi_base = reg_base + ESM_REG_BASE_OFFSET; pr_info("hdmi reg base:0x%llx esm_hpi_base:0x%llx\n", __pa(reg_base), __pa(hdcp.esm_hpi_base)); #if defined(__LINUX_PLAT__) /***********************get dts value configs*************************/ /*get ddc control gpio enable config*/ if (of_property_read_u32(pdev->dev.of_node, "ddc_en_io_ctrl", &hdmi_drv->ddc_ctrl_en)) pr_info("ERROR: can not get ddc_en_io_ctrl\n"); /*get cec config*/ if (of_property_read_u32(pdev->dev.of_node, "hdmi_cec_support", &hdmi_drv->cec_support)) pr_err("ERROR: can not get hdmi_cec_support node\n"); if (of_property_read_u32(pdev->dev.of_node, "hdmi_cec_super_standby", &hdmi_drv->cec_super_standby)) pr_err("ERROR: can not get hdmi_cec_super_standby node\n"); hdmi_drv->hdmi_core->cec_super_standby = hdmi_drv->cec_super_standby; /*get hdcp configs*/ if (of_property_read_u32_array(pdev->dev.of_node, "hdmi_hdcp_enable", (u32 *)&hdcp.use_hdcp, 1)) pr_info("ERROR: can not get hdmi_hdcp_enable\n"); if (of_property_read_u32_array(pdev->dev.of_node, "hdmi_hdcp22_enable", (u32 *)&hdcp.use_hdcp22, 1)) pr_info("ERROR: can not get hdmi_hdcp22_enable\n"); esm_np = of_find_node_by_name(NULL, "esm"); if (esm_np == NULL) { pr_info("hdmi2.0 error: could not find the node of esm\n"); } else if (hdcp.use_hdcp22) { if (!of_property_read_u32_array(esm_np, "esm_img_size_addr", &dts_esm_size_phy_addr, 1)) { /*obtain esm firmware size*/ dts_esm_size_vir_addr = __va(dts_esm_size_phy_addr); memcpy((void *)(&hdcp.esm_firm_size), dts_esm_size_vir_addr, 4); if (!of_property_read_u32_array(esm_np, "esm_img_buff_addr", &dts_esm_buff_phy_addr, 1)) { /*obtain esm firmware*/ dts_esm_buff_vir_addr = __va(dts_esm_buff_phy_addr); if (hdcp.esm_firm_size <= HDCP22_FIRMWARE_SIZE) memcpy(esm_firm_vir_addr, dts_esm_buff_vir_addr, hdcp.esm_firm_size); else pr_info("WARN: esm_firm_size is too big\n"); } else { pr_info("ERROR: Can not read esm_img_buff_addr\n"); } } else { pr_info("ERROR: Can not read esm_img_size_addr\n"); } } /******************get dts pin ctrl and gpio configs******************/ /*Get DCC GPIO*/ hdmi_drv->pctl = pinctrl_get(&pdev->dev); if (IS_ERR(hdmi_drv->pctl)) pr_err("ERROR: pinctrl_get for HDMI2.0 DDC fail\n"); hdmi_drv->ddc_ctrl.gpio = of_get_named_gpio_flags(pdev->dev.of_node, "ddc_io_ctrl", 0, (enum of_gpio_flags *)(&(hdmi_drv->ddc_ctrl))); if (0 != gpio_request(hdmi_drv->ddc_ctrl.gpio, NULL)) { pr_info("ddc ctrl gpio_request is failed\n"); return 0; } /**********************get dts clk configs****************************/ hdmi_drv->hdmi_clk = of_clk_get(pdev->dev.of_node, 0); if (IS_ERR(hdmi_drv->hdmi_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi\n"); goto free_mem; } hdmi_drv->hdmi_ddc_clk = of_clk_get(pdev->dev.of_node, 1); if (IS_ERR(hdmi_drv->hdmi_ddc_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi ddc\n"); goto free_mem; } hdmi_drv->hdmi_hdcp_clk = of_clk_get(pdev->dev.of_node, 2); if (IS_ERR(hdmi_drv->hdmi_hdcp_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi hdcp\n"); goto free_mem; } hdmi_drv->hdmi_cec_clk = of_clk_get(pdev->dev.of_node, 3); if (IS_ERR(hdmi_drv->hdmi_cec_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi ddc\n"); goto free_mem; } if (of_property_read_string(pdev->dev.of_node, "hdmi_power", &hdmi_power)) { pr_err("Error: get hdmi power node failed\n"); } else { hdmi_drv->power_use = 1; memcpy((void *)hdmi_drv->power, hdmi_power, strlen(hdmi_power) + 1); pr_info("get hdmi_power:%s successfully\n", hdmi_drv->power); hdmi_power_enable(hdmi_drv->power); } #else int node_offset = 0; char io_name[32]; disp_gpio_set_t gpio_info; node_offset = disp_fdt_nodeoffset("hdmi"); of_periph_clk_config_setup(node_offset); /* get clk */ hdmi_drv->hdmi_clk = of_clk_get(node_offset, 0); if (IS_ERR(hdmi_drv->hdmi_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi\n"); kfree(hdmi_drv); return -1; } hdmi_drv->hdmi_ddc_clk = of_clk_get(node_offset, 1); if (IS_ERR(hdmi_drv->hdmi_ddc_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi ddc\n"); kfree(hdmi_drv); return -1; } hdmi_drv->hdmi_hdcp_clk = of_clk_get(node_offset, 2); if (IS_ERR(hdmi_drv->hdmi_hdcp_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi hdcp\n"); kfree(hdmi_drv); return -1; } hdmi_drv->hdmi_cec_clk = of_clk_get(node_offset, 3); if (IS_ERR(hdmi_drv->hdmi_cec_clk)) { dev_err(&pdev->dev, "fail to get clk for hdmi cec\n"); kfree(hdmi_drv); return -1; } clk_set_rate(hdmi_drv->hdmi_hdcp_clk, 300000000); ret = clk_prepare_enable(hdmi_drv->hdmi_clk); if (ret != 0) pr_info("hdmi clk enable failed!\n"); ret = clk_prepare_enable(hdmi_drv->hdmi_ddc_clk); if (ret != 0) pr_info("hdmi ddc clk enable failed!\n"); ret = clk_prepare_enable(hdmi_drv->hdmi_cec_clk); if (ret != 0) pr_info("hdmi cec clk enable failed!\n"); ret = clk_prepare_enable(hdmi_drv->hdmi_hdcp_clk); if (ret != 0) pr_info("hdmi hdcp clk enable failed!\n"); sprintf(io_name, "ddc_scl"); ret = disp_sys_script_get_item("hdmi", io_name, (int *)&gpio_info, sizeof(disp_gpio_set_t)/sizeof(int)); if (ret == 3) { disp_sys_gpio_request_simple(&gpio_info, 1); pr_info("enable ddc_scl pin\n"); } memset(io_name, 0, 32); memset(&gpio_info, 0, sizeof(gpio_info)); sprintf(io_name, "ddc_sda"); ret = disp_sys_script_get_item("hdmi", io_name, (int *)&gpio_info, sizeof(disp_gpio_set_t)/sizeof(int)); if (ret == 3) { disp_sys_gpio_request_simple(&gpio_info, 1); pr_info("enable ddc_sda pin\n"); } /* cec pin */ memset(io_name, 0, 32); memset(&gpio_info, 0, sizeof(gpio_info)); sprintf(io_name, "cec_io"); ret = disp_sys_script_get_item("hdmi", io_name, (int *)&gpio_info, sizeof(disp_gpio_set_t)/sizeof(int)); if (ret == 3) { disp_sys_gpio_request_simple(&gpio_info, 1); pr_info("enable hmid cec pin\n"); } else { pr_info("hmid cec pin not config\n"); } hdmi_clk_enable_mask = 1; #endif #if defined(__LINUX_PLAT__) /*init hdmi params*/ /*set_static_config(&config);*/ #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) switch_dev_register(&hdmi_switch_dev); #endif /*Check if hdmi has been configured during booting*/ if (boot_hdmi) { VIDEO_INF("uboot has configured hdmi\n"); video_on = true; hdmi_clk_enable_mask = 1; hdmi_pin_config_mask = 1; hdmi_enable_mask = 1; #if !defined(CONFIG_COMMON_CLK_ENABLE_SYNCBOOT) hdmi_drv->hdmi_clk->enable_count = 1; hdmi_drv->hdmi_ddc_clk->enable_count = 1; hdmi_drv->hdmi_clk->prepare_count = 1; hdmi_drv->hdmi_ddc_clk->prepare_count = 1; hdmi_parent_clk = clk_get_parent(hdmi_drv->hdmi_clk); hdmi_parent_clk->enable_count++; hdmi_parent_clk->prepare_count++; ddc_parent_clk = clk_get_parent(hdmi_drv->hdmi_ddc_clk); ddc_parent_clk->enable_count++; ddc_parent_clk->prepare_count++; #endif if (hdmi_drv->hdmi_hdcp_clk != NULL) if (clk_prepare_enable(hdmi_drv->hdmi_hdcp_clk) != 0) pr_info("hdmi hdcp clk enable failed!\n"); if (hdmi_drv->hdmi_cec_clk != NULL) { struct clk *cec_clk_parent = NULL; cec_clk_parent = clk_get(NULL, "periph32k"); if (cec_clk_parent == NULL || IS_ERR(cec_clk_parent)) { pr_err("periph32k clk get failed\n"); } else { if (clk_set_parent(hdmi_drv->hdmi_cec_clk, cec_clk_parent) != 0) pr_err("hdmi ddc clk set parent clk 'periph32k' failed\n"); } if (clk_prepare_enable(hdmi_drv->hdmi_cec_clk) != 0) pr_info("hdmi cec clk enable failed!\n"); } } else { VIDEO_INF("uboot has not configured hdmi\n"); video_on = false; hdmi_clk_enable_mask = 0; hdmi_pin_config_mask = 0; hdmi_sys_source_configure(); } /*Init hdmi core and core params*/ if (hdmi_tx_core_init(hdmi_drv->hdmi_core, phy_model, NULL, NULL, &hdcp)) { pr_err("Application init failed\n"); goto free_mem; } resistor_calibration_core(hdmi_drv->hdmi_core, 0x10004, 0x80c00000); if (!boot_hdmi) /*enable hpd sense*/ hpd_sense_enbale_core(hdmi_drv->hdmi_core); #if defined(CONFIG_SWITCH) || defined(CONFIG_ANDROID_SWITCH) if (!(hdmi_hpd_mask & 0x100)) switch_set_state(&hdmi_switch_dev, hpd_state); #endif /*Now that everything is fine, let's add it to device list*/ list_add_tail(&hdmi_drv->devlist, &devlist_global); mutex_init(&hdmi_drv->ctrl_mutex); mutex_init(&hdmi_drv->hdcp_mutex); if (hdmi_drv->cec_super_standby) scene_lock_init(&hdmi_standby_lock, SCENE_HDMI_CEC_STANDBY, "hdmi_cec_standby"); /*Create hdmi thread to poll hpd and hdcp status and handle hdcp and hpd event*/ hdmi_drv->hdmi_task = kthread_create(hdmi_run_thread, (void *)0, "hdmi proc"); if (IS_ERR(hdmi_drv->hdmi_task)) { pr_info("Unable to start kernel thread %s.\n\n", "hdmi proc"); hdmi_drv->hdmi_task = NULL; goto free_mem; } wake_up_process(hdmi_drv->hdmi_task); cec_thread_init(hdmi_drv->parent_dev); #endif memset(&disp_func, 0, sizeof(struct disp_device_func)); disp_func.enable = hdmi_enable; disp_func.smooth_enable = hdmi_smooth_enable; disp_func.disable = hdmi_disable; #if defined(__LINUX_PLAT__) disp_func.mode_support = hdmi_mode_support; #endif disp_func.get_HPD_status = hdmi_get_HPD_status; disp_func.get_input_csc = hdmi_core_get_csc_type; disp_func.get_video_timing_info = hdmi_get_video_timming_info; #if defined(__LINUX_PLAT__) disp_func.suspend = hdmi_suspend; disp_func.resume = hdmi_resume; #endif disp_func.set_static_config = set_static_config; disp_func.get_static_config = get_static_config; #if defined(__LINUX_PLAT__) disp_func.set_dynamic_config = set_dynamic_config; disp_func.get_dynamic_config = get_dynamic_config; #endif disp_set_hdmi_func(&disp_func); #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); AUDIO_INF("audio_set_hdmi_func\n"); #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 VIDEO_INF("HDMI2.0 DRIVER PROBE END\n"); return ret; free_mem: kfree(hdmi_drv->hdmi_core); kfree(hdmi_drv); pr_info("Free core and drv memory\n"); return -1; } #if defined(__LINUX_PLAT__) static int hdmi_tx_exit(struct platform_device *pdev) { struct hdmi_tx_drv *dev; struct list_head *list; cec_thread_exit(); while (!list_empty(&devlist_global)) { list = devlist_global.next; list_del(list); dev = list_entry(list, struct hdmi_tx_drv, devlist); if (dev == NULL) continue; } if (hdmi_drv->hdmi_task) { kthread_stop(hdmi_drv->hdmi_task); hdmi_drv->hdmi_task = NULL; } scene_lock_destroy(&hdmi_standby_lock); if (hdmi_drv->power_use) hdmi_power_enable(hdmi_drv->power); hdmi_sys_source_release(); return 0; } /** * @short of_device_id structure */ static const struct of_device_id dw_hdmi_tx[] = { { .compatible = "allwinner,sunxi-hdmi" }, { } }; MODULE_DEVICE_TABLE(of, dw_hdmi_tx); /** * @short Platform driver structure */ static struct platform_driver __refdata dwc_hdmi_tx_pdrv = { .remove = hdmi_tx_exit, .probe = hdmi_tx_init, .driver = { .name = "allwinner,sunxi-hdmi", .owner = THIS_MODULE, .of_match_table = dw_hdmi_tx, }, }; 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 int __parse_dump_str(const char *buf, size_t size, unsigned long *start, unsigned long *end) { char *ptr = NULL; char *ptr2 = (char *)buf; int ret = 0, times = 0; /* Support single address mode, some time it haven't ',' */ next: /*Default dump only one register(*start =*end). If ptr is not NULL, we will cover the default value of end.*/ if (times == 1) *start = *end; if (!ptr2 || (ptr2 - buf) >= size) goto out; ptr = ptr2; ptr2 = strnchr(ptr, size - (ptr - buf), ','); if (ptr2) { *ptr2 = '\0'; ptr2++; } ptr = strim(ptr); if (!strlen(ptr)) goto next; ret = kstrtoul(ptr, 16, end); if (!ret) { times++; goto next; } else pr_warn("String syntax errors: \"%s\"\n", ptr); out: return ret; } static ssize_t hdmi_test_reg_read_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(count)] > hdmi_test_reg_read"); } ssize_t hdmi_test_reg_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long start_reg = 0; unsigned long read_count = 0; u32 i; u8 *separator; u32 data = 0; separator = strchr(buf, ','); if (separator != NULL) { if (__parse_dump_str(buf, count, &start_reg, &read_count)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("start_reg=0x%lx read_count=%ld\n", start_reg, read_count); for (i = 0; i < read_count; i++) { pr_info("hdmi_addr_offset: 0x%lx = 0x%x\n", start_reg, hdmitx_read(start_reg * 4)); start_reg++; } } else { separator = strchr(buf, ' '); if (separator != NULL) { start_reg = simple_strtoul(buf, NULL, 0); read_count = simple_strtoul(separator + 1, NULL, 0); for (i = 0; i < read_count; i += 4) { data = (u8)hdmitx_read((start_reg + i) * 4); data |= ((u8)hdmitx_read((start_reg + i + 1) * 4)) << 8; data |= ((u8)hdmitx_read((start_reg + i + 2) * 4)) << 16; data |= ((u8)hdmitx_read((start_reg + i + 3) * 4)) << 24; if ((i % 16) == 0) printk("\n0x%08lx: 0x%08x", (start_reg + i), data); else printk(" 0x%08x", data); } } else { start_reg = simple_strtoul(buf, NULL, 0); pr_info("hdmi_addr_offset: 0x%lx = 0x%x\n", start_reg, hdmitx_read(start_reg * 4)); } } pr_info("\n"); return count; } static DEVICE_ATTR(read, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_test_reg_read_show, hdmi_test_reg_read_store); static ssize_t hdmi_test_reg_write_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(value)] > hdmi_test_write"); } ssize_t hdmi_test_reg_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long reg_addr = 0; unsigned long value = 0; u8 *separator1 = NULL; u8 *separator2 = NULL; separator1 = strchr(buf, ','); separator2 = strchr(buf, ' '); if (separator1 != NULL) { if (__parse_dump_str(buf, count, ®_addr, &value)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("reg_addr=0x%lx write_value=0x%lx\n", reg_addr, value); hdmitx_write((reg_addr * 4), value); mdelay(1); pr_info("after write,red(%lx)=%x\n", reg_addr, hdmitx_read(reg_addr * 4)); } else if (separator2 != NULL) { reg_addr = simple_strtoul(buf, NULL, 0); value = simple_strtoul(separator2 + 1, NULL, 0); pr_info("reg_addr=0x%lx write_value=0x%lx\n", reg_addr, value); hdmitx_write((reg_addr * 4), value); mdelay(1); pr_info("after write,red(%lx)=%x\n", reg_addr, hdmitx_read(reg_addr * 4)); } else { pr_info("ERROR:error input\n"); } return count; } static DEVICE_ATTR(write, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_test_reg_write_show, hdmi_test_reg_write_store); static ssize_t phy_write_show(struct device *dev, struct device_attribute *attr, char *buf) { pr_info("OPMODE_PLLCFG-0x16\n"); pr_info("CKSYMTXCTRL-0x09\n"); pr_info("PLLCURRCTRL-0x10\n"); pr_info("VLEVCTRL-0x0E\n"); pr_info("PLLGMPCTRL-0x15\n"); pr_info("TXTERM-0x19\n"); return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(value)] > phy_write"); } static ssize_t phy_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 reg_addr = 0; u16 value = 0; struct hdmi_tx_core *core = NULL; core = hdmi_drv->hdmi_core; if (__parse_dump_str(buf, count, (unsigned long *)®_addr, (unsigned long *)&value)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("reg_addr=0x%x write_value=0x%x\n", (u32)reg_addr, (u32)value); core->dev_func.phy_write(reg_addr, value); return count; } static DEVICE_ATTR(phy_write, S_IRUGO|S_IWUSR|S_IWGRP, phy_write_show, phy_write_store); /*static DEVICE_ATTR(hdmi_test_print_core_structure, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_test_print_core_structure_show, NULL);*/ static ssize_t phy_read_show(struct device *dev, struct device_attribute *attr, char *buf) { pr_info("OPMODE_PLLCFG-0x16\n"); pr_info("CKSYMTXCTRL-0x09\n"); pr_info("PLLCURRCTRL-0x10\n"); pr_info("VLEVCTRL-0x0E\n"); pr_info("PLLGMPCTRL-0x15\n"); pr_info("TXTERM-0x19\n"); return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(count)] > phy_read"); } ssize_t phy_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 start_reg = 0; u16 value = 0; unsigned long read_count = 0; u32 i; struct hdmi_tx_core *core = get_platform(); if (__parse_dump_str(buf, count, (unsigned long *)&start_reg, &read_count)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("start_reg=0x%x read_count=%ld\n", (u32)start_reg, read_count); for (i = 0; i < read_count; i++) { core->dev_func.phy_read(start_reg, &value); pr_info("hdmi_addr_offset: 0x%x = 0x%x\n", (u32)start_reg, value); start_reg++; } pr_info("\n"); return count; } static DEVICE_ATTR(phy_read, S_IRUGO|S_IWUSR|S_IWGRP, phy_read_show, phy_read_store); static ssize_t scdc_read_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(count)] > scdc_read"); } ssize_t scdc_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 start_reg = 0; u8 value = 0; unsigned long read_count = 0; u32 i; struct hdmi_tx_core *core = get_platform(); if (__parse_dump_str(buf, count, (unsigned long *)&start_reg, &read_count)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("start_reg=0x%x read_count=%ld\n", (u32)start_reg, read_count); for (i = 0; i < read_count; i++) { core->dev_func.scdc_read(start_reg, 1, &value); pr_info("hdmi_addr_offset: 0x%x = 0x%x\n", (u32)start_reg, value); start_reg++; } pr_info("\n"); return count; } static DEVICE_ATTR(scdc_read, S_IRUGO|S_IWUSR|S_IWGRP, scdc_read_show, scdc_read_store); static ssize_t scdc_write_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(value)] > scdc_write"); } static ssize_t scdc_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 reg_addr = 0; u8 value = 0; struct hdmi_tx_core *core = NULL; core = hdmi_drv->hdmi_core; if (__parse_dump_str(buf, count, (unsigned long *)®_addr, (unsigned long *)&value)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("reg_addr=0x%x write_value=0x%x\n", reg_addr, value); core->dev_func.scdc_write(reg_addr, 1, &value); return count; } static DEVICE_ATTR(scdc_write, S_IRUGO|S_IWUSR|S_IWGRP, scdc_write_show, scdc_write_store); static ssize_t hdmi_debug_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "debug=%d\n", hdmi_printf); } 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, "9", 1) == 0) hdmi_printf = 9; else if (strnicmp(buf, "8", 1) == 0) hdmi_printf = 8; else if (strnicmp(buf, "7", 1) == 0) hdmi_printf = 7; else if (strnicmp(buf, "6", 1) == 0) hdmi_printf = 6; else if (strnicmp(buf, "5", 1) == 0) hdmi_printf = 5; else if (strnicmp(buf, "4", 1) == 0) hdmi_printf = 4; else if (strnicmp(buf, "3", 1) == 0) hdmi_printf = 3; else if (strnicmp(buf, "2", 1) == 0) hdmi_printf = 2; else if (strnicmp(buf, "1", 1) == 0) hdmi_printf = 1; else if (strnicmp(buf, "0", 1) == 0) hdmi_printf = 0; else pr_err("Error Input!\n"); pr_info("debug=%d\n", hdmi_printf); return count; } static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_debug_show, hdmi_debug_store); static ssize_t hdmi_one_touch_play_show(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t hdmi_one_touch_play_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_cec_send_one_touch_paly(); else pr_err("Error Input!\n"); return count; } static DEVICE_ATTR(one_touch_play, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_one_touch_play_show, hdmi_one_touch_play_store); static ssize_t hdmi_cec_local_standby_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = sprintf(buf, "%d", (unsigned int)cec_get_local_standby()); return ret; } static ssize_t hdmi_cec_local_standby_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) cec_set_local_standby(true); else if (strnicmp(buf, "1", 0) == 0) cec_set_local_standby(false); else pr_err("Error Input!\n"); return count; } static DEVICE_ATTR(cec_local_standby_mode, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_cec_local_standby_show, hdmi_cec_local_standby_store); static ssize_t hdmi_cec_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t n = 0; n += cec_dump_core(buf + n); return n; } static DEVICE_ATTR(cec_dump, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_cec_dump_show, NULL); static ssize_t hdmi_hdcp_type_show(struct device *dev, struct device_attribute *attr, char *buf) { char hdcp_type = (char)get_hdcp_type_core(hdmi_drv->hdmi_core); mutex_lock(&hdmi_drv->hdcp_mutex); memcpy(buf, &hdcp_type, 1); mutex_unlock(&hdmi_drv->hdcp_mutex); return 1; } static ssize_t hdmi_hdcp_type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return count; } static DEVICE_ATTR(hdcp_type, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_hdcp_type_show, hdmi_hdcp_type_store); static ssize_t hdmi_cec_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", hdmi_drv->cec_support); } static ssize_t hdmi_cec_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) { hdmi_drv->cec_support = 1; hdmi_cec_enable(1); } else { hdmi_drv->cec_support = 0; hdmi_cec_enable(0); } return count; } static DEVICE_ATTR(cec_enable, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_cec_enable_show, hdmi_cec_enable_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 = kstrtoul(buf, 16, &val); if (err) { pr_info("Invalid size\n"); return err; } pr_info("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) { u8 pedid[1024]; if ((hdmi_drv->hdmi_core->mode.edid != NULL) && (hdmi_drv->hdmi_core->mode.edid_ext != NULL)) { /*EDID_block0*/ memcpy(pedid, hdmi_drv->hdmi_core->mode.edid, 0x80); /*EDID_block1*/ memcpy(pedid+0x80, hdmi_drv->hdmi_core->mode.edid_ext, 0x380); memcpy(buf, pedid, 0x400); return 0x400; } else { return 0; } } 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_drv->hdmi_core->mode.pHdcp.hdcp_on); } 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; mutex_lock(&hdmi_drv->hdcp_mutex); if (strnicmp(buf, "1", 1) == 0) { set_hdcp_status(HDCP_ING); hdcp_enable_core(hdmi_drv->hdmi_core, 1); } else { hdcp_enable_core(hdmi_drv->hdmi_core, 0); set_hdcp_status(HDCP_DISABLE); } mutex_unlock(&hdmi_drv->hdcp_mutex); return count; } static DEVICE_ATTR(hdcp_enable, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_hdcp_enable_show, hdmi_hdcp_enable_store); static ssize_t hpi_write_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(value)] > hpi_write"); } static ssize_t hpi_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 reg_addr = 0; u32 value = 0; struct hdmi_tx_core *core = NULL; core = hdmi_drv->hdmi_core; if (__parse_dump_str(buf, count, (unsigned long *)®_addr, (unsigned long *)&value)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("reg_addr=0x%x write_value=0x%x\n", reg_addr, value); *((u32 *)(core->mode.pHdcp.esm_hpi_base + reg_addr)) = (u32)value; return count; } static DEVICE_ATTR(hpi_write, S_IRUGO|S_IWUSR|S_IWGRP, hpi_write_show, hpi_write_store); static ssize_t hpi_read_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "echo [0x(address offset), 0x(count)] > hpi_read"); } ssize_t hpi_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 start_reg = 0; u32 value = 0; unsigned long read_count = 0; u32 i; struct hdmi_tx_core *core = get_platform(); if (__parse_dump_str(buf, count, (unsigned long *)&start_reg, &read_count)) pr_err("%s,%d err, invalid para!\n", __func__, __LINE__); pr_info("start_reg=0x%x read_count=%ld\n", (u32)start_reg, read_count); for (i = 0; i < read_count; i++) { value = *((u32 *)(core->mode.pHdcp.esm_hpi_base + start_reg)); pr_info("hdmi_addr_offset: 0x%x = 0x%x\n", (u32)start_reg, value); start_reg++; } pr_info("\n"); return count; } static DEVICE_ATTR(hpi_read, S_IRUGO|S_IWUSR|S_IWGRP, hpi_read_show, hpi_read_store); static ssize_t hdcp_status_show(struct device *dev, struct device_attribute *attr, char *buf) { u32 count = sizeof(u8); mutex_lock(&hdmi_drv->hdcp_mutex); memcpy(buf, &hdcp_encrypt_status, count); mutex_unlock(&hdmi_drv->hdcp_mutex); return count; } static ssize_t hdcp_status_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) { } else { } return count; } static DEVICE_ATTR(hdcp_status, S_IRUGO|S_IWUSR|S_IWGRP, hdcp_status_show, hdcp_status_store); struct hdmi_debug_video_mode { int hdmi_mode;/* vic */ char *name; }; static struct hdmi_debug_video_mode debug_video_mode[] = { {HDMI_VIC_720x480I_4_3, "480I"}, {HDMI_VIC_720x480I_16_9, "480I"}, {HDMI_VIC_720x576I_4_3, "576I"}, {HDMI_VIC_720x576I_16_9, "576I"}, {HDMI_VIC_720x480P60_4_3, "720x480P"}, {HDMI_VIC_720x480P60_16_9, "720x480P"}, {HDMI_VIC_640x480P60, "640x480P"}, {HDMI_VIC_720x576P_4_3, "576P"}, {HDMI_VIC_720x576P_16_9, "576P"}, {HDMI_VIC_1280x720P24, "720P24"}, {HDMI_VIC_1280x720P25, "720P25"}, {HDMI_VIC_1280x720P30, "720P30"}, {HDMI_VIC_1280x720P50, "720P50"}, {HDMI_VIC_1280x720P60, "720P60"}, {HDMI_VIC_1920x1080I50, "1080I50"}, {HDMI_VIC_1920x1080I60, "1080I60"}, {HDMI_VIC_1920x1080P24, "1080P24"}, {HDMI_VIC_1920x1080P50, "1080P50"}, {HDMI_VIC_1920x1080P60, "1080P60"}, {HDMI_VIC_1920x1080P25, "1080P25"}, {HDMI_VIC_1920x1080P30, "1080P30"}, {HDMI_VIC_3840x2160P30, "2160P30"}, {HDMI_VIC_3840x2160P25, "2160PP25"}, {HDMI_VIC_3840x2160P24, "2160P24"}, {HDMI_VIC_4096x2160P24, "4096x2160P24"}, {HDMI_VIC_4096x2160P25, "4096x2160P25"}, {HDMI_VIC_4096x2160P30, "4096x2160P30"}, {HDMI_VIC_3840x2160P50, "2160P50"}, {HDMI_VIC_4096x2160P50, "4096x2160P50"}, {HDMI_VIC_3840x2160P60, "2160P60"}, {HDMI_VIC_4096x2160P60, "4096x2160P60"}, }; static char *hdmi_vic_name[] = { "2160P30", "2160P25", "2160P24", "4096x2160P24", }; static char *hdmi_audio_code_name[] = { "LPCM", "AC-3", "MPEG1", "MP3", "MPEG2", "AAC", "DTS", "ATRAC", "OneBitAudio", "DolbyDigital+", "DTS-HD", "MAT", "DST", "WMAPro", }; static char *debug_get_video_name(int hdmi_mode) { int i = 0; for (i = 0; i < sizeof(debug_video_mode)/sizeof(struct hdmi_debug_video_mode); i++) { if (debug_video_mode[i].hdmi_mode == hdmi_mode) return debug_video_mode[i].name; } return NULL; } static ssize_t hdmi_sink_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t n = 0; int i = 0; sink_edid_t *sink_cap = hdmi_drv->hdmi_core->mode.sink_cap; if (!sink_cap) { n += sprintf(buf+n, "%s\n", "Do not read edid from sink"); return n; } /* Video Data Block */ n += sprintf(buf+n, "\n\n%s", "Video Mode:"); for (i = 0; i < sink_cap->edid_mSvdIndex; i++) { if (sink_cap->edid_mSvd[i].mLimitedToYcc420 || sink_cap->edid_mSvd[i].mYcc420) continue; n += sprintf(buf+n, " %s", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); if (sink_cap->edid_mSvd[i].mNative) n += sprintf(buf+n, "%s", "(native)"); } for (i = 0; i < sink_cap->edid_mHdmivsdb.mHdmiVicCount; i++) { if (sink_cap->edid_mHdmivsdb.mHdmiVic[i] <= 0x4) { n += sprintf(buf+n, "%s", hdmi_vic_name[ sink_cap->edid_mHdmivsdb.mHdmiVic[i]-1]); } } /* YCC420 VDB */ n += sprintf(buf+n, "\n\n%s", "Only Support YUV420:"); for (i = 0; i < sink_cap->edid_mSvdIndex; i++) { if (sink_cap->edid_mSvd[i].mLimitedToYcc420) { n += sprintf(buf+n, " %s", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); } } /*YCC420 CMDB */ n += sprintf(buf+n, "\n\n%s", "Also Support YUV420:"); for (i = 0; i < sink_cap->edid_mSvdIndex; i++) { if (sink_cap->edid_mSvd[i].mYcc420) { n += sprintf(buf+n, " %s", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); } } /*video pixel format */ n += sprintf(buf+n, "\n\n%s", "Pixel Format: RGB"); if (sink_cap->edid_mYcc444Support) n += sprintf(buf+n, " %s", "YUV444"); if (sink_cap->edid_mYcc422Support) n += sprintf(buf+n, " %s", "YUV422"); /*deepcolor */ n += sprintf(buf+n, "\n\n%s", "Deep Color:"); if (sink_cap->edid_mHdmivsdb.mDeepColor30) { n += sprintf(buf+n, " %s", "RGB444_30bit"); if (sink_cap->edid_mHdmivsdb.mDeepColorY444) n += sprintf(buf+n, " %s", "YUV444_30bit"); } if (sink_cap->edid_mHdmivsdb.mDeepColor36) { n += sprintf(buf+n, " %s", "RGB444_36bit"); if (sink_cap->edid_mHdmivsdb.mDeepColorY444) n += sprintf(buf+n, " %s", "YUV444_36bit"); } if (sink_cap->edid_mHdmivsdb.mDeepColor48) { n += sprintf(buf+n, " %s", "RGB444_48bit"); if (sink_cap->edid_mHdmivsdb.mDeepColorY444) n += sprintf(buf+n, " %s", "YUV444_48bit"); } if (sink_cap->edid_mHdmiForumvsdb.mDC_30bit_420) n += sprintf(buf+n, " %s", "YUV420_30bit"); if (sink_cap->edid_mHdmiForumvsdb.mDC_36bit_420) n += sprintf(buf+n, " %s", "YUV420_36bit"); if (sink_cap->edid_mHdmiForumvsdb.mDC_48bit_420) n += sprintf(buf+n, " %s", "YUV420_48bit"); /*3D format */ if (sink_cap->edid_mHdmivsdb.m3dPresent) { n += sprintf(buf+n, "\n\n%s", "3D Mode:"); for (i = 0; i < 16; i++) { if (sink_cap->edid_mHdmivsdb.mVideo3dStruct[i][0] == 1 && i < sink_cap->edid_mSvdIndex) { n += sprintf(buf+n, " %s_FP", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); } if (sink_cap->edid_mHdmivsdb.mVideo3dStruct[i][6] == 1 && i < sink_cap->edid_mSvdIndex) { n += sprintf(buf+n, " %s_SBS", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); } if (sink_cap->edid_mHdmivsdb.mVideo3dStruct[i][8] == 1 && i < sink_cap->edid_mSvdIndex) { n += sprintf(buf+n, " %s_TAB", debug_get_video_name( (int)sink_cap->edid_mSvd[i].mCode)); } } } /*TMDS clk rate */ if (sink_cap->edid_mHdmiForumvsdb.mValid) { n += sprintf(buf+n, "\n\n%s", "MaxTmdsCharRate:"); n += sprintf(buf+n, " %d", sink_cap->edid_mHdmiForumvsdb.mMaxTmdsCharRate); } /*audio*/ n += sprintf(buf+n, "\n\n%s", "Basic Audio Support:"); n += sprintf(buf+n, " %s", sink_cap->edid_mBasicAudioSupport ? "YES" : "NO"); if (sink_cap->edid_mBasicAudioSupport) { n += sprintf(buf+n, "\n\n%s", "Audio Code:"); for (i = 0; i < sink_cap->edid_mSadIndex; i++) { n += sprintf(buf+n, " %s", hdmi_audio_code_name[sink_cap->edid_mSad[i].mFormat-1]); } } /*hdcp*/ n += sprintf(buf+n, "\n\n%s", "HDCP Tpye:"); n += sprintf(buf+n, "%c", '\n'); return n; } static DEVICE_ATTR(hdmi_sink, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_sink_show, NULL); static char *pixel_format_name[] = { "RGB", "YUV422", "YUV444", "YUV420", }; static char *colorimetry_name[] = { "NULL", "ITU601", "ITU709", "XV_YCC601", "XV_YCC709", "S_YCC601", "ADOBE_YCC601", "ADOBE_RGB", "BT2020_Yc_Cbc_Crc", "BT2020_Y_CB_CR", }; static ssize_t hdmi_source_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t n = 0; n += sprintf(buf+n, "\n%s%d\n", "HPD: ", hdmi_core_get_hpd_state()); n += sprintf(buf+n, "\n%s%d\n", "RxSense: ", hdmi_core_get_rxsense_state()); n += sprintf(buf+n, "\n%s%d\n", "PhyLock: ", hdmi_core_get_phy_pll_lock_state()); n += sprintf(buf+n, "\n%s%d\n", "PhyPower: ", hdmi_core_get_phy_power_state()); n += sprintf(buf+n, "\n%s%s\n", "TmdsMode: ", hdmi_core_get_tmds_mode() ? "HDMI" : "DVI"); n += sprintf(buf+n, "\n%s%d\n", "Scramble: ", hdmi_core_get_scramble_state()); n += sprintf(buf+n, "\n%s%d\n", "AvMute: ", hdmi_core_get_avmute_state()); n += sprintf(buf+n, "\n%s%d\n", "PixelRepetion: ", hdmi_core_get_pixelrepetion()); n += sprintf(buf+n, "\n%s%d\n", "BitDepth: ", hdmi_core_get_color_depth()); n += sprintf(buf+n, "\n%s%s\n", "PixelFormat: ", pixel_format_name[hdmi_core_get_pixel_format()]); n += sprintf(buf+n, "\n%s%s\n", "Colorimetry: ", colorimetry_name[hdmi_core_get_colorimetry()]); n += sprintf(buf+n, "\n%s%s\n", "VideoFormat: ", debug_get_video_name((int)hdmi_core_get_video_code())); n += sprintf(buf+n, "\n%s%d\n", "AudioLayout: ", hdmi_core_get_audio_layout()); n += sprintf(buf+n, "\n%s%d\n", "AudioChannelCnt: ", hdmi_core_get_audio_channel_count()); n += sprintf(buf+n, "\n%s%d\n", "AudioSamplingFreq: ", hdmi_core_get_audio_sample_freq()); n += sprintf(buf+n, "\n%s%d\n", "AudioSampleSize: ", hdmi_core_get_audio_sample_size()); n += sprintf(buf+n, "\n%s%d\n", "AudioNvalue: ", hdmi_core_get_audio_n()); return n; } static DEVICE_ATTR(hdmi_source, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_source_show, NULL); static ssize_t hdcp_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; ret += hdcp_dump_core(buf + ret); ret += sprintf(buf + ret, "\n"); ret += hdmi_drv->hdmi_core->dev_func.hdcp_config_dump(buf + ret); return ret; } static DEVICE_ATTR(hdcp_dump, S_IRUGO|S_IWUSR|S_IWGRP, hdcp_dump_show, NULL); static ssize_t hdmi_avmute_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n%s\n", "echo [value] > avmute", "-----value =1:avmute on; =0:avmute off"); } static ssize_t hdmi_avmute_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_core_avmute_enable(1); else hdmi_core_avmute_enable(0); return count; } static DEVICE_ATTR(avmute, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_avmute_show, hdmi_avmute_store); static ssize_t phy_power_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n%s\n", "echo [value] > phy_power", "-----value =1:phy power on; =0:phy power off"); } static ssize_t phy_power_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_core_phy_power_enable(1); else hdmi_core_phy_power_enable(0); return count; } static DEVICE_ATTR(phy_power, S_IRUGO|S_IWUSR|S_IWGRP, phy_power_show, phy_power_store); static ssize_t dvi_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t n = 0; n += sprintf(buf + n, "%s\n%s\n", "echo [value] > dvi_mode", "-----value =0:HDMI mode; =1:DVI mode"); return n; } static ssize_t dvi_mode_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_core_dvimode_enable(1); else hdmi_core_dvimode_enable(0); return count; } static DEVICE_ATTR(dvi_mode, S_IRUGO|S_IWUSR|S_IWGRP, dvi_mode_show, dvi_mode_store); static struct attribute *hdmi_attributes[] = { &dev_attr_read.attr, &dev_attr_write.attr, &dev_attr_phy_write.attr, &dev_attr_phy_read.attr, &dev_attr_scdc_read.attr, &dev_attr_scdc_write.attr, &dev_attr_hdcp_status.attr, &dev_attr_hpi_read.attr, &dev_attr_hpi_write.attr, &dev_attr_one_touch_play.attr, &dev_attr_cec_local_standby_mode.attr, &dev_attr_cec_dump.attr, &dev_attr_cec_enable.attr, /*&dev_attr_hdmi_test_print_core_structure.attr,*/ &dev_attr_debug.attr, &dev_attr_hdcp_type.attr, &dev_attr_hpd_mask.attr, &dev_attr_edid.attr, &dev_attr_hdcp_enable.attr, &dev_attr_hdmi_sink.attr, &dev_attr_hdmi_source.attr, &dev_attr_avmute.attr, &dev_attr_phy_power.attr, &dev_attr_dvi_mode.attr, &dev_attr_hdcp_dump.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; /*Create and add a character device*/ alloc_chrdev_region(&devid, 0, 1, "hdmi");/*corely for device number*/ hdmi_cdev = cdev_alloc(); cdev_init(hdmi_cdev, &hdmi_fops); hdmi_cdev->owner = THIS_MODULE; err = cdev_add(hdmi_cdev, devid, 1);/*/proc/device/hdmi*/ if (err) { pr_err("Error: hdmi cdev_add fail.\n"); return -1; } /*Create a path: sys/class/hdmi*/ hdmi_class = class_create(THIS_MODULE, "hdmi"); if (IS_ERR(hdmi_class)) { pr_err("Error:hdmi class_create fail\n"); return -1; } /*Create a path "sys/class/hdmi/hdmi"*/ hdev = device_create(hdmi_class, NULL, devid, NULL, "hdmi"); /*Create a path: sys/class/hdmi/hdmi/attr*/ ret = sysfs_create_group(&hdev->kobj, &hdmi_attribute_group); if (ret) pr_err("Error: hdmi sysfs_create_group failed!\n"); ret = platform_driver_register(&dwc_hdmi_tx_pdrv); if (ret) pr_err("Error: hdmi driver register fail\n"); pr_info("HDMI2.0 module init end\n"); return ret; } static void __exit hdmi_module_exit(void) { pr_info("hdmi_module_exit\n"); hdmi_tx_exit(hdmi_drv->pdev); dma_free_coherent(hdmi_drv->parent_dev, HDCP22_DATA_SIZE, &hdmi_drv->hdmi_core->mode.pHdcp.esm_data_phy_addr, GFP_KERNEL | __GFP_ZERO); dma_free_coherent(hdmi_drv->parent_dev, HDCP22_FIRMWARE_SIZE, &hdmi_drv->hdmi_core->mode.pHdcp.esm_firm_phy_addr, GFP_KERNEL | __GFP_ZERO); hdmi_core_exit(hdmi_drv->hdmi_core); kfree(hdmi_drv); platform_driver_unregister(&dwc_hdmi_tx_pdrv); sysfs_remove_group(&hdev->kobj, &hdmi_attribute_group); device_destroy(hdmi_class, devid); class_destroy(hdmi_class); cdev_del(hdmi_cdev); } late_initcall(hdmi_module_init); module_exit(hdmi_module_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("zhengwanyu"); MODULE_DESCRIPTION("HDMI_TX20 module driver"); MODULE_VERSION("1.0"); #endif