diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/accessory | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/accessory')
-rw-r--r-- | drivers/accessory/30pin_con.c | 737 | ||||
-rw-r--r-- | drivers/accessory/Kconfig | 38 | ||||
-rw-r--r-- | drivers/accessory/MHD_SiI9234.c | 543 | ||||
-rw-r--r-- | drivers/accessory/MHD_SiI9234.h | 78 | ||||
-rw-r--r-- | drivers/accessory/Makefile | 7 | ||||
-rw-r--r-- | drivers/accessory/sec_keyboard.c | 500 | ||||
-rw-r--r-- | drivers/accessory/sec_keyboard.h | 208 | ||||
-rw-r--r-- | drivers/accessory/sii9234.c | 339 | ||||
-rw-r--r-- | drivers/accessory/sii9234.h | 191 | ||||
-rw-r--r-- | drivers/accessory/sii9234_tpi_regs.h | 381 |
10 files changed, 3022 insertions, 0 deletions
diff --git a/drivers/accessory/30pin_con.c b/drivers/accessory/30pin_con.c new file mode 100644 index 0000000..7052ed2 --- /dev/null +++ b/drivers/accessory/30pin_con.c @@ -0,0 +1,737 @@ + + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/30pin_con.h> +#include <linux/switch.h> +#include <linux/wakelock.h> +#include <plat/adc.h> +#include <linux/earlysuspend.h> + +#include <asm/irq.h> +#include <linux/mfd/tps6586x.h> + +#ifdef CONFIG_REGULATOR +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#endif + +#ifdef CONFIG_MHL_SII9234 +#include "sii9234.h" +#endif + +#define SUBJECT "ACCESSORY" + +#define ACC_CONDEV_DBG(format, ...) \ + pr_info("[ "SUBJECT " (%s,%d) ] " format "\n", \ + __func__, __LINE__, ## __VA_ARGS__); + +#define DETECTION_INTR_DELAY (get_jiffies_64() + (HZ*15)) /* 20s */ + +enum accessory_type { + ACCESSORY_NONE = 0, + ACCESSORY_OTG, + ACCESSORY_LINEOUT, + ACCESSORY_CARMOUNT, + ACCESSORY_UNKNOWN, +}; + +enum dock_type { + DOCK_NONE = 0, + DOCK_DESK, + DOCK_KEYBOARD, +}; + +enum uevent_dock_type { + UEVENT_DOCK_NONE = 0, + UEVENT_DOCK_DESK, + UEVENT_DOCK_CAR, + UEVENT_DOCK_KEYBOARD = 9, +}; + +struct acc_con_info { + struct device *acc_dev; + struct acc_con_platform_data *pdata; + struct delayed_work acc_dwork; + struct delayed_work acc_id_dwork; + struct switch_dev dock_switch; + struct switch_dev ear_jack_switch; + struct wake_lock wake_lock; + struct s3c_adc_client *padc; + enum accessory_type current_accessory; + enum dock_type current_dock; + int accessory_irq; + int dock_irq; +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + int mhl_irq; + bool mhl_pwr_state; +#endif + struct early_suspend early_suspend; + struct delayed_work acc_con_work; + struct mutex lock; +}; + +#if defined(CONFIG_STMPE811_ADC) +#ifdef CONFIG_MACH_P4NOTE +#define ACCESSORY_ID_ADC_CH 7 +#else +#define ACCESSORY_ID_ADC_CH 0 +#endif +#else +#define ACCESSORY_ID_ADC_CH 4 +#endif + +#ifdef CONFIG_SAMSUNG_MHL_9290 +static BLOCKING_NOTIFIER_HEAD(acc_notifier); + +int acc_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acc_notifier, nb); +} + +int acc_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acc_notifier, nb); +} + +static int acc_notify(int event) +{ + pr_info("notifier: mhl callback\n"); + return blocking_notifier_call_chain(&acc_notifier, event, NULL); +} +#endif +static int acc_get_adc_accessroy_id(struct s3c_adc_client *padc) +{ + int adc_data; + +#if defined(CONFIG_STMPE811_ADC) + adc_data = stmpe811_get_adc_data(ACCESSORY_ID_ADC_CH); +#else + adc_data = s3c_adc_read(padc, ACCESSORY_ID_ADC_CH); +#endif +/* ACC_CONDEV_DBG("[ACC] adc_data = %d..\n",adc_data); */ + return adc_data; +} + +static int acc_get_accessory_id(struct acc_con_info *acc) +{ + int i; + u32 adc = 0, adc_sum = 0; + u32 adc_buff[5] = {0}; + u32 adc_val = 0; + u32 adc_min = 0; + u32 adc_max = 0; + + if (!acc) { + pr_err("adc client is not registered!\n"); + return -1; + } + + for (i = 0; i < 5; i++) { + adc_val = acc_get_adc_accessroy_id(acc->padc); + adc_buff[i] = adc_val; + adc_sum += adc_buff[i]; + if (i == 0) { + adc_min = adc_buff[0]; + adc_max = adc_buff[0]; + } else { + if (adc_max < adc_buff[i]) + adc_max = adc_buff[i]; + else if (adc_min > adc_buff[i]) + adc_min = adc_buff[i]; + } + msleep(20); + } + adc = (adc_sum - adc_max - adc_min)/3; + ACC_CONDEV_DBG("ACCESSORY_ID ADC value = %d", adc); + return (int)adc; +} + +#ifdef CONFIG_MHL_SII9234 +static ssize_t MHD_check_read(struct class *class, + struct class_attribute *attr, char *buf) +{ + int count; + int res; + /*TVout_LDO_ctrl(true);*/ + if (!MHD_HW_IsOn()) { + sii9234_tpi_init(); + res = MHD_Read_deviceID(); + MHD_HW_Off(); + } else { + sii9234_tpi_init(); + res = MHD_Read_deviceID(); + } + + count = sprintf(buf, "%d\n", res); + /*TVout_LDO_ctrl(false);*/ + return count; +} + +static ssize_t MHD_check_write(struct class *class, + struct class_attribute *attr, const char *buf, size_t size) +{ + printk(KERN_INFO"input data --> %s\n", buf); + + return size; +} + +static CLASS_ATTR(test_result, S_IRUGO, MHD_check_read, MHD_check_write); +#endif /* CONFIG_MHL_SII9234 */ + +static ssize_t acc_read_acc_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acc_con_info *acc = dev_get_drvdata(dev); + int adc_val = 0 ; + int jig_uart_off = 0 ; + int count = 0 ; + adc_val = acc_get_accessory_id(acc); + + if ((3540 < adc_val) && (adc_val < 3920)) + jig_uart_off = 28 ; + else + jig_uart_off = 0 ; + + ACC_CONDEV_DBG("jig_uart_off : %d", jig_uart_off); + + count = sprintf(buf, "%x\n", jig_uart_off); + + return count; +} + +static DEVICE_ATTR(adc, S_IRUGO, acc_read_acc_id, NULL); + + +void acc_accessory_uevent(struct acc_con_info *acc, int acc_adc) +{ + char *env_ptr; + char *stat_ptr; + char *envp[3]; + + /* value is changed for PxLTE + 3 pole earjack 1.00 V ( 0.90~1.10V) adc: 797~1002 + Car mount 1.38 V (1.24~1.45V) adc: 1134~1352 + 4 pole earjack just bundles is supported . adc :1360~1449 + OTG 2.2 V (2.00~2.35V) adc: 1903~2248 */ + + if (acc_adc != false) { + if ((1100 < acc_adc) && (1400 > acc_adc)) { + /* 3 pole earjack 1220 */ + env_ptr = "ACCESSORY=lineout"; + acc->current_accessory = ACCESSORY_LINEOUT; + switch_set_state(&acc->ear_jack_switch, 1); +#if 0 + } else if ((1134 < acc_adc) && (1352 > acc_adc)) { + /* car mount */ + env_ptr = "ACCESSORY=carmount"; + acc->current_accessory = ACCESSORY_CARMOUNT; +#endif + } else if ((1800 < acc_adc) && (2350 > acc_adc)) { + /* 4 pole earjack, No warranty 2000 */ + env_ptr = "ACCESSORY=lineout"; + acc->current_accessory = ACCESSORY_LINEOUT; + switch_set_state(&acc->ear_jack_switch, 1); + } else if ((2450 < acc_adc) && (2850 > acc_adc)) { + /* otg 2730 */ + env_ptr = "ACCESSORY=OTG"; + acc->current_accessory = ACCESSORY_OTG; + } else { + env_ptr = "ACCESSORY=unknown"; + acc->current_accessory = ACCESSORY_UNKNOWN; + } + + stat_ptr = "STATE=online"; + envp[0] = env_ptr; + envp[1] = stat_ptr; + envp[2] = NULL; + if (acc->current_accessory == ACCESSORY_OTG) { + if (acc->pdata->usb_ldo_en) + acc->pdata->usb_ldo_en(1); + if (acc->pdata->otg_en) + acc->pdata->otg_en(1); + } + kobject_uevent_env(&acc->acc_dev->kobj, KOBJ_CHANGE, envp); + ACC_CONDEV_DBG("%s : %s", env_ptr, stat_ptr); + } else { + if (acc->current_accessory == ACCESSORY_OTG) + env_ptr = "ACCESSORY=OTG"; + else if (acc->current_accessory == ACCESSORY_LINEOUT) { + env_ptr = "ACCESSORY=lineout"; + switch_set_state(&acc->ear_jack_switch, + UEVENT_DOCK_NONE); + } else if (acc->current_accessory == ACCESSORY_CARMOUNT) + env_ptr = "ACCESSORY=carmount"; + else + env_ptr = "ACCESSORY=unknown"; + + stat_ptr = "STATE=offline"; + envp[0] = env_ptr; + envp[1] = stat_ptr; + envp[2] = NULL; + kobject_uevent_env(&acc->acc_dev->kobj, KOBJ_CHANGE, envp); + if ((acc->current_accessory == ACCESSORY_OTG) && + acc->pdata->otg_en) + acc->pdata->otg_en(0); + + acc->current_accessory = ACCESSORY_NONE; + ACC_CONDEV_DBG("%s : %s", env_ptr, stat_ptr); + } +} + +static void acc_dock_uevent(struct acc_con_info *acc, bool connected) +{ + char *env_ptr; + char *stat_ptr; + char *envp[3]; + + if (acc->current_dock == DOCK_KEYBOARD) + env_ptr = "DOCK=keyboard"; + else if (acc->current_dock == DOCK_DESK) + env_ptr = "DOCK=desk"; + else + env_ptr = "DOCK=unknown"; + + if (!connected) { + stat_ptr = "STATE=offline"; + acc->current_dock = DOCK_NONE; + } else { + stat_ptr = "STATE=online"; + } + + envp[0] = env_ptr; + envp[1] = stat_ptr; + envp[2] = NULL; + kobject_uevent_env(&acc->acc_dev->kobj, KOBJ_CHANGE, envp); + ACC_CONDEV_DBG("%s : %s", env_ptr, stat_ptr); +} + + +static void acc_check_dock_detection(struct acc_con_info *acc) +{ + if (NULL == acc->pdata->get_dock_state) { + ACC_CONDEV_DBG("[30PIN] failed to get acc state!!!"); + return; + } + + if (!acc->pdata->get_dock_state()) { +#ifdef CONFIG_SEC_KEYBOARD_DOCK + if (acc->pdata->check_keyboard && + acc->pdata->check_keyboard(true)) { + acc->current_dock = DOCK_KEYBOARD; + ACC_CONDEV_DBG + ("[30PIN] keyboard dock station attached!!!"); + switch_set_state(&acc->dock_switch, + UEVENT_DOCK_KEYBOARD); + } else +#endif + { + ACC_CONDEV_DBG + ("[30PIN] desktop dock station attached!!!"); + switch_set_state(&acc->dock_switch, UEVENT_DOCK_DESK); + acc->current_dock = DOCK_DESK; + +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + mutex_lock(&acc->lock); + if (!acc->mhl_pwr_state) { +#if defined(CONFIG_MHL_SII9234) + sii9234_tpi_init(); +#elif defined(CONFIG_SAMSUNG_MHL_9290) + acc_notify(1); +#endif + acc->mhl_pwr_state = true; + } + mutex_unlock(&acc->lock); +#endif + } + acc_dock_uevent(acc, true); + } else { + + ACC_CONDEV_DBG("[30PIN] dock station detached!!!"); + + switch_set_state(&acc->dock_switch, UEVENT_DOCK_NONE); +#ifdef CONFIG_SEC_KEYBOARD_DOCK + if (acc->pdata->check_keyboard) + acc->pdata->check_keyboard(false); +#endif +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + /*call MHL deinit */ + if (acc->mhl_pwr_state) { +#if defined(CONFIG_MHL_SII9234) + MHD_HW_Off(); +#elif defined(CONFIG_SAMSUNG_MHL_9290) + acc_notify(0); +#endif + acc->mhl_pwr_state = false; + } +#endif + /*TVout_LDO_ctrl(false); */ + acc_dock_uevent(acc, false); + + } +} + +static irqreturn_t acc_dock_isr(int irq, void *ptr) +{ + struct acc_con_info *acc = ptr; + wake_lock(&acc->wake_lock); + acc_check_dock_detection(acc); + wake_unlock(&acc->wake_lock); + return IRQ_HANDLED; +} + +#define DETECTION_DELAY_MS 200 + +static irqreturn_t acc_accessory_isr(int irq, void *dev_id) +{ + struct acc_con_info *acc = (struct acc_con_info *)dev_id; + ACC_CONDEV_DBG(""); + cancel_delayed_work_sync(&acc->acc_id_dwork); + schedule_delayed_work(&acc->acc_id_dwork, + msecs_to_jiffies(DETECTION_DELAY_MS)); + return IRQ_HANDLED; +} + +static int acc_init_dock_int(struct acc_con_info *acc) +{ + int ret = 0; + acc->accessory_irq = gpio_to_irq(acc->pdata->accessory_irq_gpio); + ret = request_threaded_irq(acc->accessory_irq, NULL, acc_dock_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "accessory_detect", acc); + if (ret) + ACC_CONDEV_DBG("request_irq(accessory_irq) return : %d\n", ret); + + ret = enable_irq_wake(acc->accessory_irq); + if (ret) + ACC_CONDEV_DBG("enable_irq_wake(accessory_irq) return : %d\n", + ret); + + return ret; +} + +static int acc_init_accessory_int(struct acc_con_info *acc) +{ + int ret = 0; + acc->dock_irq = gpio_to_irq(acc->pdata->dock_irq_gpio); + ret = request_threaded_irq(acc->dock_irq, NULL, acc_accessory_isr, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "dock_detect", acc); + if (ret) + ACC_CONDEV_DBG("request_irq(dock_irq) return : %d\n", ret); + + ret = enable_irq_wake(acc->dock_irq); + if (ret) + ACC_CONDEV_DBG("enable_irq_wake(dock_irq) return : %d\n", ret); + + return ret; +} + +static void acc_dwork_int_init(struct work_struct *work) +{ + struct acc_con_info *acc = container_of(work, + struct acc_con_info, acc_dwork.work); + int retval; + + ACC_CONDEV_DBG(""); + + retval = acc_init_dock_int(acc); + if (retval) { + ACC_CONDEV_DBG("failed to initialize dock_int irq"); + goto err_irq_dock; + } + + retval = acc_init_accessory_int(acc); + if (retval) { + ACC_CONDEV_DBG(" failed to initialize accessory_int irq"); + goto err_irq_acc; + } + + if (acc->pdata->get_dock_state) { + if (!acc->pdata->get_dock_state()) + acc_check_dock_detection(acc); + } + + if (acc->pdata->get_acc_state) { + if (!acc->pdata->get_acc_state()) + schedule_delayed_work(&acc->acc_id_dwork, + msecs_to_jiffies(DETECTION_DELAY_MS)); + } + + return ; + +err_irq_acc: + free_irq(acc->accessory_irq, acc); +err_irq_dock: + switch_dev_unregister(&acc->ear_jack_switch); + return ; +} + +static void acc_dwork_accessory_detect(struct work_struct *work) +{ + struct acc_con_info *acc = container_of(work, + struct acc_con_info, acc_id_dwork.work); + + int adc_val = 0; + int acc_state = 0; + + acc_state = acc->pdata->get_acc_state(); + + if (acc_state) { + ACC_CONDEV_DBG("Accessory detached"); + acc_accessory_uevent(acc, false); + } else { + ACC_CONDEV_DBG("Accessory attached"); + adc_val = acc_get_accessory_id(acc); + acc_accessory_uevent(acc, adc_val); + } +} + +static int acc_con_probe(struct platform_device *pdev) +{ + struct acc_con_info *acc; + struct acc_con_platform_data *pdata = pdev->dev.platform_data; + struct regulator *vadc_regulator; + + int retval; +#ifdef CONFIG_MHL_SII9234 + struct class *sec_mhl; +#endif + ACC_CONDEV_DBG(""); + + if (pdata == NULL) { + pr_err("%s: no pdata\n", __func__); + return -ENODEV; + } + +#ifdef CONFIG_REGULATOR +#ifndef CONFIG_MACH_P4NOTE + /* LDO1 regulator ON */ + vadc_regulator = regulator_get(&pdev->dev, "vadc_3.3v"); + if (IS_ERR(vadc_regulator)) { + printk(KERN_ERR "failed to get resource %s\n", + "vadc_3.3v"); + return PTR_ERR(vadc_regulator); + } + regulator_enable(vadc_regulator); +#endif +#endif + + acc = kzalloc(sizeof(*acc), GFP_KERNEL); + if (!acc) + return -ENOMEM; + + acc->pdata = pdata; + acc->current_dock = DOCK_NONE; + acc->current_accessory = ACCESSORY_NONE; +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + acc->mhl_irq = gpio_to_irq(pdata->mhl_irq_gpio); + acc->mhl_pwr_state = false; +#endif + + mutex_init(&acc->lock); + dev_set_drvdata(&pdev->dev, acc); + + acc->acc_dev = &pdev->dev; + + /* Register adc client */ +#if defined(CONFIG_STMPE811_ADC) + /* Do nothing */ +#elif defined(CONFIG_S3C_ADC) + acc->padc = s3c_adc_register(pdev, NULL, NULL, 0); +#endif + +#ifdef CONFIG_MHL_SII9234 + retval = i2c_add_driver(&SII9234A_i2c_driver); + if (retval) { + pr_err("[MHL SII9234A] can't add i2c driver\n"); + goto err_i2c_a; + } else { + pr_info("[MHL SII9234A] add i2c driver\n"); + } + + retval = i2c_add_driver(&SII9234B_i2c_driver); + if (retval) { + pr_err("[MHL SII9234B] can't add i2c driver\n"); + goto err_i2c_b; + } else { + pr_info("[MHL SII9234B] add i2c driver\n"); + } + + retval = i2c_add_driver(&SII9234C_i2c_driver); + if (retval) { + pr_err("[MHL SII9234C] can't add i2c driver\n"); + goto err_i2c_c; + } else { + pr_info("[MHL SII9234C] add i2c driver\n"); + } + + retval = i2c_add_driver(&SII9234_i2c_driver); + if (retval) { + pr_err("[MHL SII9234] can't add i2c driver\n"); + goto err_i2c; + } else { + pr_info("[MHL SII9234] add i2c driver\n"); + } +#endif + acc->dock_switch.name = "dock"; + retval = switch_dev_register(&acc->dock_switch); + if (retval < 0) + goto err_sw_dock; + + acc->ear_jack_switch.name = "usb_audio"; + retval = switch_dev_register(&acc->ear_jack_switch); + if (retval < 0) + goto err_sw_jack; + + wake_lock_init(&acc->wake_lock, WAKE_LOCK_SUSPEND, "30pin_con"); + + INIT_DELAYED_WORK(&acc->acc_dwork, acc_dwork_int_init); + schedule_delayed_work(&acc->acc_dwork, msecs_to_jiffies(10000)); + INIT_DELAYED_WORK(&acc->acc_id_dwork, acc_dwork_accessory_detect); +#ifdef CONFIG_MHL_SII9234 + sec_mhl = class_create(THIS_MODULE, "mhl"); + if (IS_ERR(sec_mhl)) { + pr_err("Failed to create class(sec_mhl)!\n"); + retval = -ENOMEM; + } + + retval = class_create_file(sec_mhl, &class_attr_test_result); + if (retval) { + pr_err("Failed to create device file in sysfs entries!\n"); + retval = -ENOMEM; + } +#endif + +#ifndef CONFIG_MACH_P10 + if (device_create_file(sec_switch_dev, &dev_attr_adc) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_adc .attr.name); + dev_set_drvdata(sec_switch_dev, acc); +#endif + + return 0; + +#ifndef CONFIG_SEC_KEYBOARD_DOCK +err_irq_acc: + free_irq(acc->accessory_irq, acc); +err_irq_dock: + switch_dev_unregister(&acc->ear_jack_switch); +#endif +err_sw_jack: + switch_dev_unregister(&acc->dock_switch); +err_sw_dock: +#ifdef CONFIG_MHL_SII9234 + i2c_del_driver(&SII9234_i2c_driver); +err_i2c: + i2c_del_driver(&SII9234C_i2c_driver); +err_i2c_c: + i2c_del_driver(&SII9234B_i2c_driver); +err_i2c_b: + i2c_del_driver(&SII9234A_i2c_driver); +err_i2c_a: +#endif + + kfree(acc); + + return retval; +} + +static int acc_con_remove(struct platform_device *pdev) +{ + struct acc_con_info *acc = platform_get_drvdata(pdev); + ACC_CONDEV_DBG(""); + + free_irq(acc->accessory_irq, acc); + free_irq(acc->dock_irq, acc); +#ifdef CONFIG_MHL_SII9234 + i2c_del_driver(&SII9234A_i2c_driver); + i2c_del_driver(&SII9234B_i2c_driver); + i2c_del_driver(&SII9234C_i2c_driver); + i2c_del_driver(&SII9234_i2c_driver); +#endif + + switch_dev_unregister(&acc->dock_switch); + switch_dev_unregister(&acc->ear_jack_switch); + kfree(acc); + return 0; +} + +static int acc_con_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct acc_con_info *acc = platform_get_drvdata(pdev); + ACC_CONDEV_DBG(""); +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + if (acc->mhl_pwr_state) { + pr_err("%s::MHL off\n", __func__); +#if defined(CONFIG_MHL_SII9234) + MHD_HW_Off(); +#elif defined(CONFIG_SAMSUNG_MHL_9290) + acc_notify(0); +#endif + acc->mhl_pwr_state = false; + } +#endif + return 0; +} + +static int acc_con_resume(struct platform_device *pdev) +{ + struct acc_con_info *acc = platform_get_drvdata(pdev); + ACC_CONDEV_DBG(""); + + mutex_lock(&acc->lock); +#if defined(CONFIG_MHL_SII9234) || defined(CONFIG_SAMSUNG_MHL_9290) + if (acc->current_dock == DOCK_DESK && !acc->mhl_pwr_state) { + pr_err("%s::MHL init\n", __func__); +#if defined(CONFIG_MHL_SII9234) + sii9234_tpi_init(); /* call MHL init */ +#elif defined(CONFIG_SAMSUNG_MHL_9290) + acc_notify(1); +#endif + acc->mhl_pwr_state = true; + } +#endif + mutex_unlock(&acc->lock); + + return 0; +} + +static struct platform_driver acc_con_driver = { + .probe = acc_con_probe, + .remove = acc_con_remove, + .suspend = acc_con_suspend, + .resume = acc_con_resume, + .driver = { + .name = "acc_con", + .owner = THIS_MODULE, + }, +}; + +static int __init acc_con_init(void) +{ + ACC_CONDEV_DBG(""); + + return platform_driver_register(&acc_con_driver); +} + +static void __exit acc_con_exit(void) +{ + platform_driver_unregister(&acc_con_driver); +} + +late_initcall(acc_con_init); +module_exit(acc_con_exit); + +MODULE_AUTHOR("Kyungrok Min <gyoungrok.min@samsung.com>"); +MODULE_DESCRIPTION("acc connector driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/accessory/Kconfig b/drivers/accessory/Kconfig new file mode 100644 index 0000000..1bd8af0 --- /dev/null +++ b/drivers/accessory/Kconfig @@ -0,0 +1,38 @@ +# +# Accessory Configuration +# + +menuconfig ACCESSORY + bool "Accessory devices" + default y + ---help--- + Say Y here to get to see options for Samsung Accessories + If you say N, all options in this submenu will be skipped and disabled. + +if ACCESSORY + +config 30PIN_CONN + tristate "Accessory detection driver" + depends on I2C + help + Provides support for detecting Accessory,such as TA, Keyboard + +config MHL_SII9234 + tristate "SiI9234 MHL(Mobile HD Link) Transmitter support" + depends on I2C + ---help--- + Mobile HD Link Transmitter driver for SiI9234 + +config SEC_KEYBOARD_DOCK + tristate "sec keyboard dock support" + +config HPD_PULL + tristate "HPD pull up by PMIC LDO" + +config SAMSUNG_MHL_9290 + tristate "support 9290 dongle and 9292 dongle" + +endif # ACCESSORY + + + diff --git a/drivers/accessory/MHD_SiI9234.c b/drivers/accessory/MHD_SiI9234.c new file mode 100644 index 0000000..bb3effd --- /dev/null +++ b/drivers/accessory/MHD_SiI9234.c @@ -0,0 +1,543 @@ +/* + * MHD_SiI9234.c - Driver for Silicon Image MHD SiI9234 Transmitter driver + * + * Copyright 2010 Philju Lee (Daniel Lee) + * + * Based on preview driver from Silicon Image. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +static int MYDRV_MAJOR; + +static int MHDDRV_MAJOR; + +static bool tclkStable; +static bool mobileHdCableConnected; +static bool hdmiCableConnected; +static bool dsRxPoweredUp; +static byte tmdsPoweredUp; +static byte txPowerState; +static bool checkTclkStable; + + +/*===========================================================================*/ + +static void InitCBusRegs(void); +static byte ReadIndexedRegister(byte PageNum, byte Offset); +void MHD_HW_Reset(void); +void MHD_HW_Off(void); +void MHD_OUT_EN(void); +void MHD_INT_clear(void); + +static void I2C_WriteByte(byte deviceID, byte offset, byte value); +static byte I2C_ReadByte(byte deviceID, byte offset); +static byte ReadByteTPI(byte Offset); +static void WriteByteTPI(byte Offset, byte Data); +static void ReadModifyWriteTPI(byte Offset, byte Mask, byte Data); +static void WriteIndexedRegister(byte PageNum, byte Offset, byte Data); +static void sii9234_initializeStateVariables(void); + +static void sii9234_enable_interrupts(void); +static byte ReadByteCBUS(byte Offset); +static void WriteByteCBUS(byte Offset, byte Data); +static byte ReadIndexedRegister(byte PageNum, byte Offset); +static void ReadModifyWriteIndexedRegister(byte PageNum, byte Offset, + byte Mask, byte Data); +static void TxPowerStateD3(void); +static void TxPowerStateD0(void); +static void CheckTxFifoStable(void); +static void HotPlugService(void); +static void ReadModifyWriteCBUS(byte Offset, byte Mask, byte Value); +static void OnMHDCableConnected(void) ; +static void OnDownstreamRxPoweredDown(void); +static void OnHdmiCableDisconnected(void); +static void OnDownstreamRxPoweredUp(void); +static void OnHdmiCableConnected(void); + +void sii9234_tpi_init(void); +static void sii9234_register_init(void); +static void mhd_tx_fifo_stable(void); +int MHD_Read_deviceID(void); + +/*===========================================================================*/ + + +bool delay_ms(int msec) +{ + mdelay(msec); + return 0; +} + +void MHD_HW_Reset(void) +{ + pr_info("[SIMG]MHD_HW_Reset == Start ==\n"); + SII9234_HW_Reset(SII9234_i2c_client); + pr_info("[SIMG] MHD_HW_Reset == End ==\n"); +} +EXPORT_SYMBOL(MHD_HW_Reset); + +void MHD_HW_Off(void) +{ + SII9234_HW_Off(SII9234_i2c_client); +#ifdef CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE + mhl_hpd_handler(false); +#endif +} +EXPORT_SYMBOL(MHD_HW_Off); + +int MHD_HW_IsOn(void) +{ + return SII9234_HW_IsOn(); +} +EXPORT_SYMBOL(MHD_HW_IsOn); + +void MHD_OUT_EN(void) +{ + byte state , int_stat; + int_stat = ReadIndexedRegister(INDEXED_PAGE_0, 0x74); + pr_info("[HDMI]MHD_OUT_EN INT register value is: 0x%02x\n", int_stat); + state = ReadIndexedRegister(INDEXED_PAGE_0, 0x81); + pr_info("[HDMI]MHD_OUT_EN register 0x81 value is: 0x%02x\n", state); + + if ((state & 0x02) && (int_stat & 0x01)) { + pr_info("[HDMI]MHD_OUT_EN :: enable output\n"); + ReadModifyWriteIndexedRegister(INDEXED_PAGE_0, 0x80, SI_BIT_4, + 0x0); + msleep(20); + ReadModifyWriteIndexedRegister(INDEXED_PAGE_0, 0x80, SI_BIT_4, + SI_BIT_4); + msleep(60); + /* set mhd power active mode */ + ReadModifyWriteTPI(TPI_DEVICE_POWER_STATE_CTRL_REG, + TX_POWER_STATE_MASK, 0x00); + + mhd_tx_fifo_stable(); /*fifo clear*/ + } + MHD_INT_clear(); +} +EXPORT_SYMBOL(MHD_OUT_EN); + +void MHD_INT_clear(void) +{ + byte Int_state; + Int_state = ReadIndexedRegister(INDEXED_PAGE_0, 0x74); + WriteIndexedRegister(INDEXED_PAGE_0, 0x74, Int_state); +} +EXPORT_SYMBOL(MHD_INT_clear); + +void I2C_WriteByte(byte deviceID, byte offset, byte value) +{ + if (deviceID == 0x72) + SII9234_i2c_write(SII9234_i2c_client, offset, value); + else if (deviceID == 0x7A) + SII9234_i2c_write(SII9234A_i2c_client, offset, value); + else if (deviceID == 0x92) + SII9234_i2c_write(SII9234B_i2c_client, offset, value); + else if (deviceID == 0xC8) + SII9234_i2c_write(SII9234C_i2c_client, offset, value); + else + pr_err("[MHL]I2C_WriteByte error %x\n", deviceID); +} + +byte I2C_ReadByte(byte deviceID, byte offset) +{ + byte number = 0; + /*pr_err("[MHL]I2C_ReadByte called ID%x Offset%x\n",deviceID,offset);*/ + if (deviceID == 0x72) + number = SII9234_i2c_read(SII9234_i2c_client, offset); + else if (deviceID == 0x7A) + number = SII9234_i2c_read(SII9234A_i2c_client, offset); + else if (deviceID == 0x92) + number = SII9234_i2c_read(SII9234B_i2c_client, offset); + else if (deviceID == 0xC8) + number = SII9234_i2c_read(SII9234C_i2c_client, offset); + else + pr_err("[MHL]I2C_ReadByte error %x\n", deviceID); + /*pr_err("[MHL]I2C_ReadByte ID:%x Offset:%x data:%x\n", + deviceID,offset,number); */ + return number; +} + +byte ReadByteTPI(byte Offset) +{ + return I2C_ReadByte(TPI_SLAVE_ADDR, Offset); +} + +void WriteByteTPI(byte Offset, byte Data) +{ + I2C_WriteByte(TPI_SLAVE_ADDR, Offset, Data); +} + +void ReadModifyWriteTPI(byte Offset, byte Mask, byte Data) +{ + + byte Temp; + + Temp = ReadByteTPI(Offset); + /* Read the current value of the register.*/ + Temp &= ~Mask; + /*Clear the bits that are set in Mask.*/ + Temp |= (Data & Mask); + /*OR in new value. Apply Mask to Value for safety.*/ + WriteByteTPI(Offset, Temp); + /*Write new value back to register.*/ +} + + +void WriteIndexedRegister(byte PageNum, byte Offset, byte Data) +{ + WriteByteTPI(TPI_INDEXED_PAGE_REG, PageNum); /*Indexed page*/ + WriteByteTPI(TPI_INDEXED_OFFSET_REG, Offset); /*Indexed register*/ + WriteByteTPI(TPI_INDEXED_VALUE_REG, Data); /*Write value*/ +} + + +static void sii9234_initializeStateVariables(void) +{ + + tclkStable = FALSE; + checkTclkStable = TRUE; + tmdsPoweredUp = FALSE; + mobileHdCableConnected = FALSE; + hdmiCableConnected = FALSE; + dsRxPoweredUp = FALSE; +} + +static void InitCBusRegs(void) +{ + I2C_WriteByte(0xC8, 0x1F, 0x02); + /*Heartbeat Max Fail Enable*/ + I2C_WriteByte(0xC8, 0x07, DDC_XLTN_TIMEOUT_MAX_VAL | 0x06); + /*Increase DDC translation layer timer*/ + I2C_WriteByte(0xC8, 0x40, 0x03); + /*CBUS Drive Strength*/ + I2C_WriteByte(0xC8, 0x42, 0x06); + /*CBUS DDC interface ignore segment pointer*/ + I2C_WriteByte(0xC8, 0x36, 0x0C); + /*I2C_WriteByte(0xC8, 0x44, 0x02);*/ + I2C_WriteByte(0xC8, 0x3D, 0xFD); + I2C_WriteByte(0xC8, 0x1C, 0x00); + I2C_WriteByte(0xC8, 0x44, 0x00); + /*I2C_WriteByte(0xC8, 0x09, 0x60);*/ + /* Enable PVC Xfer aborted / follower aborted*/ +} + +void sii9234_enable_interrupts(void) +{ + ReadModifyWriteTPI(TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT_MASK, + HOT_PLUG_EVENT_MASK); + WriteIndexedRegister(INDEXED_PAGE_0, 0x75, SI_BIT_5); /* Enable */ +} + +static byte ReadByteCBUS(byte Offset) +{ + return I2C_ReadByte(CBUS_SLAVE_ADDR, Offset); +} + + +static void WriteByteCBUS(byte Offset, byte Data) +{ + I2C_WriteByte(CBUS_SLAVE_ADDR, Offset, Data); +} + +static byte ReadIndexedRegister(byte PageNum, byte Offset) +{ + WriteByteTPI(TPI_INDEXED_PAGE_REG, PageNum); /* Indexed page */ + WriteByteTPI(TPI_INDEXED_OFFSET_REG, Offset); /* Indexed register */ + return ReadByteTPI(TPI_INDEXED_VALUE_REG); /* Return read value */ +} + +static void ReadModifyWriteIndexedRegister(byte PageNum, byte Offset, + byte Mask, byte Data) +{ + byte Temp; + + /* Read the current value of the register. */ + Temp = ReadIndexedRegister(PageNum, Offset); + /* Clear the bits that are set in Mask. */ + Temp &= ~Mask; + /* OR in new value. Apply Mask to Value for safety. */ + Temp |= (Data & Mask); + /* Write new value back to register. */ + WriteByteTPI(TPI_INDEXED_VALUE_REG, Temp); +} + +static void TxPowerStateD3(void) +{ + + ReadModifyWriteIndexedRegister(INDEXED_PAGE_1, 0x3D, SI_BIT_0, 0x00); + pr_info("[SIMG] TX Power State D3\n"); + txPowerState = TX_POWER_STATE_D3; +} + +static void TxPowerStateD0(void) +{ + ReadModifyWriteTPI(TPI_DEVICE_POWER_STATE_CTRL_REG, + TX_POWER_STATE_MASK, 0x00); + TPI_DEBUG_PRINT(("[SIMG] TX Power State D0\n")); + txPowerState = TX_POWER_STATE_D0; +} + +static void CheckTxFifoStable(void) +{ + byte bTemp; + + bTemp = ReadIndexedRegister(INDEXED_PAGE_0, 0x3E); + if ((bTemp & (SI_BIT_7 | SI_BIT_6)) != 0x00) { + TPI_DEBUG_PRINT(("[SIMG] FIFO Overrun / Underrun\n")); + /* Assert MHD FIFO Reset */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x05, + SI_BIT_4 | ASR_VALUE); + mdelay(1); + /* Deassert MHD FIFO Reset */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x05, ASR_VALUE); + } +} + +static void HotPlugService(void) +{ + /* disable interrupts */ + ReadModifyWriteTPI(TPI_INTERRUPT_ENABLE_REG, + RECEIVER_SENSE_EVENT_MASK, 0x00); + + /* enable TMDS */ + pr_info("[SIMG] TMDS -> Enabled\n"); + ReadModifyWriteTPI(TPI_SYSTEM_CONTROL_DATA_REG, + TMDS_OUTPUT_CONTROL_MASK, TMDS_OUTPUT_CONTROL_ACTIVE); + tmdsPoweredUp = TRUE; + + TxPowerStateD0(); + + /* enable interrupts */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x78, 0x01); + + CheckTxFifoStable(); +} + +static void ReadModifyWriteCBUS(byte Offset, byte Mask, byte Value) +{ + byte Temp = ReadByteCBUS(Offset); + Temp &= ~Mask; + Temp |= (Value & Mask); + WriteByteCBUS(Offset, Temp); +} + +static void OnMHDCableConnected(void) +{ + + TPI_DEBUG_PRINT(("[SIMG] MHD Connected\n")); + + if (txPowerState == TX_POWER_STATE_D3) { + /* start tpi */ + WriteByteTPI(TPI_ENABLE, 0x00); /* Write "0" to 72:C7 to + start HW TPI mode */ + /* enable interrupts */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x78, 0x01); + + TxPowerStateD0(); + } + + mobileHdCableConnected = TRUE; + + WriteIndexedRegister(INDEXED_PAGE_0, 0xA0, 0x10); + + TPI_DEBUG_PRINT(("[SIMG] Setting DDC Burst Mode\n")); + /* Increase DDC translation layer timer (burst mode) */ + WriteByteCBUS(0x07, DDC_XLTN_TIMEOUT_MAX_VAL | 0x0E); + WriteByteCBUS(0x47, 0x03); + WriteByteCBUS(0x21, 0x01); /* Heartbeat Disable */ +} + +void OnDownstreamRxPoweredDown(void) +{ + + TPI_DEBUG_PRINT(("[SIMG] DSRX -> Powered Down\n")); + + dsRxPoweredUp = FALSE; + + /* disable TMDS */ + TPI_DEBUG_PRINT(("[SIMG] TMDS -> Disabled\n")); + ReadModifyWriteTPI(TPI_SYSTEM_CONTROL_DATA_REG, + TMDS_OUTPUT_CONTROL_MASK, + TMDS_OUTPUT_CONTROL_POWER_DOWN); + tmdsPoweredUp = FALSE; +} + +void OnHdmiCableDisconnected(void) +{ + + TPI_DEBUG_PRINT(("[SIMG] HDMI Disconnected\n")); + + hdmiCableConnected = FALSE; + OnDownstreamRxPoweredDown(); +} + + +void sii9234_tpi_init(void) +{ + MHD_HW_Reset(); + + pr_info("[HDMI]9234 init ++\n"); + + sii9234_register_init(); + + /* start tpi */ + WriteByteTPI(TPI_ENABLE, 0x00); /* Write "0" to 72:C7 to + start HW TPI mode */ + + /* enable interrupts */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x78, 0x01); + + /* mhd rx connected */ + WriteIndexedRegister(INDEXED_PAGE_0, + 0xA0, 0x10); /* TX termination enable */ + WriteByteCBUS(0x07, DDC_XLTN_TIMEOUT_MAX_VAL | + 0x0E); /* Increase DDC translation layer timer (burst mode) */ + WriteByteCBUS(0x47, 0x03); + WriteByteCBUS(0x21, 0x01); /* Heartbeat Disable */ + + /* enable mhd tx */ + ReadModifyWriteTPI(TPI_SYSTEM_CONTROL_DATA_REG, + TMDS_OUTPUT_CONTROL_MASK, TMDS_OUTPUT_CONTROL_ACTIVE); + + /* set mhd power active mode */ + ReadModifyWriteTPI(TPI_DEVICE_POWER_STATE_CTRL_REG, + TX_POWER_STATE_MASK, 0x00); + + mhd_tx_fifo_stable(); /*fifo clear*/ +#ifdef CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE + mhl_hpd_handler(true); +#endif + pr_info("[HDMI]9234 init --\n"); +} +EXPORT_SYMBOL(sii9234_tpi_init); + + +int MHD_Read_deviceID(void) +{ + + byte devID; + word wID; + + devID = ReadIndexedRegister(0x00, 0x03); + wID = devID << 8; + devID = ReadIndexedRegister(0x00, 0x02); + wID |= devID; + + devID = ReadByteTPI(TPI_DEVICE_ID); + + pr_err("SiI %04X\n", (int) wID); + + if (devID == SiI_DEVICE_ID) + return TRUE; + + pr_err("Unsupported TX\n"); + return FALSE; + +} +EXPORT_SYMBOL(MHD_Read_deviceID); + + +void mhd_tx_fifo_stable(void) +{ + byte tmp = ReadIndexedRegister(INDEXED_PAGE_0, 0x3E); + if ((tmp & (SI_BIT_7 | SI_BIT_6)) != 0x00) { + /* Assert Mobile HD FIFO Reset */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x05, + SI_BIT_4 | ASR_VALUE); + mdelay(1); + /* Deassert Mobile HD FIFO Reset */ + WriteIndexedRegister(INDEXED_PAGE_0, 0x05, ASR_VALUE); + } +} + + + +static void sii9234_register_init(void) +{ + /*Power Up*/ + I2C_WriteByte(0x7A, 0x3D, 0x3F); /* Power up CVCC 1.2V core */ + I2C_WriteByte(0x92, 0x11, 0x01); /* Enable TxPLL Clock*/ + I2C_WriteByte(0x92, 0x12, 0x15); /* Enable Tx Clock Path & Equalizer*/ + I2C_WriteByte(0x72, 0x08, 0x35); /* Power Up TMDS Tx Core*/ + + I2C_WriteByte(0x92, 0x00, 0x00); /* SIMG: correcting HW default*/ + I2C_WriteByte(0x92, 0x13, 0x60); /* SIMG: Set termination value*/ + I2C_WriteByte(0x92, 0x14, 0xF0); /* SIMG: Change CKDT level*/ + I2C_WriteByte(0x92, 0x4B, 0x06); /* SIMG: Correcting HW default*/ + + /*Analog PLL Control*/ + I2C_WriteByte(0x92, 0x17, 0x07); /* SIMG: PLL Calrefsel*/ + I2C_WriteByte(0x92, 0x1A, 0x20); /* VCO Cal*/ + I2C_WriteByte(0x92, 0x22, 0xE0); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x23, 0xC0); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x24, 0xA0); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x25, 0x80); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x26, 0x60); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x27, 0x40); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x28, 0x20); /* SIMG: Auto EQ*/ + I2C_WriteByte(0x92, 0x29, 0x00); /* SIMG: Auto EQ*/ + + /*I2C_WriteByte(0x92, 0x10, 0xF1);*/ + I2C_WriteByte(0x92, 0x4D, 0x02); /* SIMG: PLL Mode Value (order is important)*/ + /*I2C_WriteByte(0x92, 0x4D, 0x00);*/ + I2C_WriteByte(0x92, 0x4C, 0xA0); /* Manual zone control*/ + + /*I2C_WriteByte(0x72, 0x80, 0x14);*/ /* Enable Rx PLL Clock Value*/ + I2C_WriteByte(0x72, 0x80, 0x34); + + I2C_WriteByte(0x92, 0x31, 0x0B); /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz*/ + I2C_WriteByte(0x92, 0x45, 0x06); /* SIMG: DPLL Mode*/ + I2C_WriteByte(0x72, 0xA0, 0xD0); /* SIMG: Term mode*/ + I2C_WriteByte(0x72, 0xA1, 0xFC); /* Disable internal Mobile HD driver*/ + + + I2C_WriteByte(0x72, 0xA3, 0xEB); /* SIMG: Output Swing default EB*/ + I2C_WriteByte(0x72, 0xA6, 0x00); /* SIMG: Swing Offset*/ + + I2C_WriteByte(0x72, 0x2B, 0x01); /* Enable HDCP Compliance workaround*/ + + /*CBUS & Discovery*/ + ReadModifyWriteTPI(0x90, SI_BIT_3 | SI_BIT_2, SI_BIT_3);/* CBUS discovery cycle time for each drive and float = 150us*/ + + I2C_WriteByte(0x72, 0x91, 0xE5); /* Skip RGND detection*/ + + I2C_WriteByte(0x72, 0x94, 0x66); /* 1.8V CBUS VTH & GND threshold*/ + + /*set bit 2 and 3, which is Initiator Timeout*/ + I2C_WriteByte(CBUS_SLAVE_ADDR, 0x31, I2C_ReadByte(CBUS_SLAVE_ADDR, 0x31) | 0x0c); + + /*original 3x config*/ + I2C_WriteByte(0x72, 0xA5, 0x80); /* SIMG: RGND Hysterisis, 3x mode for Beast*/ + I2C_WriteByte(0x72, 0x95, 0x31); /* RGND & single discovery attempt (RGND blocking)*/ + I2C_WriteByte(0x72, 0x96, 0x22); /* use 1K and 2K setting*/ + + ReadModifyWriteTPI(0x95, SI_BIT_6, SI_BIT_6); /* Force USB ID switch to open*/ + + WriteByteTPI(0x92, 0x46); /* Force MHD mode*/ + WriteByteTPI(0x93, 0xDC); /* Disable CBUS pull-up during RGND measurement*/ + + ReadModifyWriteTPI(0x79, SI_BIT_1 | SI_BIT_2, 0); /*daniel test...MHL_INT*/ + + mdelay(25); + ReadModifyWriteTPI(0x95, SI_BIT_6, 0x00); /* Release USB ID switch*/ + + I2C_WriteByte(0x72, 0x90, 0x27); /* Enable CBUS discovery*/ + + InitCBusRegs(); + + I2C_WriteByte(0x72, 0x05, ASR_VALUE); /* Enable Auto soft reset on SCDT = 0*/ + + I2C_WriteByte(0x72, 0x0D, 0x1C); /* HDMI Transcode mode enable*/ +} + diff --git a/drivers/accessory/MHD_SiI9234.h b/drivers/accessory/MHD_SiI9234.h new file mode 100644 index 0000000..3bd52da --- /dev/null +++ b/drivers/accessory/MHD_SiI9234.h @@ -0,0 +1,78 @@ +/* +*/ + + +#ifndef __MHD_SiI9234_H +#define __MHD_SiI9234_H + + +/*typedef unsigned char bool;*/ +typedef unsigned char byte; +typedef unsigned short word; + + + +#define DRV_NAME "MHD_sii9234" +#define DRV_VERSION "0.1" +#define MHD_MAX_LENGTH 4096 +#define GPIO_LOW 0 +#define GPIO_HIGH 1 +#define TX_HW_RESET_PERIOD 10 +#define TPI_SLAVE_ADDR 0x72 + +#define ENABLE_AUTO_SOFT_RESET 0x04 +#define ASR_VALUE ENABLE_AUTO_SOFT_RESET + +/*TPI Identification Registers*/ +/*=============================*/ + +#define TPI_DEVICE_ID (0x1B) +#define TPI_DEVICE_REV_ID (0x1C) +#define TPI_RESERVED2 (0x1D) + +#define SiI_DEVICE_ID 0xB0 + +#define RSEN 0x04 + + +/*Indexed access defines*/ +#define TPI_INDEXED_PAGE_REG 0xBC +#define TPI_INDEXED_OFFSET_REG 0xBD +#define TPI_INDEXED_VALUE_REG 0xBE + +/*Generic Masks*/ +#define SI_ZERO 0x00 +#define SI_BIT_0 0x01 +#define SI_BIT_1 0x02 +#define SI_BIT_2 0x04 +#define SI_BIT_3 0x08 +#define SI_BIT_4 0x10 +#define SI_BIT_5 0x20 +#define SI_BIT_6 0x40 +#define SI_BIT_7 0x80 + +#define DDC_XLTN_TIMEOUT_MAX_VAL 0x30 + +/*Indexed register access*/ + +#define INDEXED_PAGE_0 0x01 +#define INDEXED_PAGE_1 0x02 +#define INDEXED_PAGE_2 0x03 + + +#define NON_MASKABLE_INT 0xFF +#define CBUS_SLAVE_ADDR 0xC8 +#define CEC_SLAVE_ADDR 0xC0 + +#define FALSE 0 +#define TRUE 1 + +typedef enum { + TX_HW_RESET, + GPIO_MAX +} GPIO_SignalType; + +#define TPI_DEBUG_PRINT(x) printk x + + +#endif/*__MHD_SiI9234_H*/ diff --git a/drivers/accessory/Makefile b/drivers/accessory/Makefile new file mode 100644 index 0000000..aa8a336 --- /dev/null +++ b/drivers/accessory/Makefile @@ -0,0 +1,7 @@ +# +# makefile for accessory +# + +obj-$(CONFIG_30PIN_CONN) += 30pin_con.o +obj-$(CONFIG_MHL_SII9234) += sii9234.o +obj-$(CONFIG_SEC_KEYBOARD_DOCK) += sec_keyboard.o
\ No newline at end of file diff --git a/drivers/accessory/sec_keyboard.c b/drivers/accessory/sec_keyboard.c new file mode 100644 index 0000000..1fbcc65 --- /dev/null +++ b/drivers/accessory/sec_keyboard.c @@ -0,0 +1,500 @@ + +#include "sec_keyboard.h" + +static void sec_keyboard_tx(struct sec_keyboard_drvdata *data, u8 cmd) +{ + if (data->pre_connected && data->tx_ready) + serio_write(data->serio, cmd); +} + +static void sec_keyboard_power(struct work_struct *work) +{ + struct sec_keyboard_drvdata *data = container_of(work, + struct sec_keyboard_drvdata, power_dwork.work); + + if (UNKOWN_KEYLAYOUT == data->kl) { + data->acc_power(1, false); + data->pre_connected = false; + + if (data->check_uart_path) + data->check_uart_path(false); + } +} + +static void forced_wakeup(struct sec_keyboard_drvdata *data) +{ + input_report_key(data->input_dev, + KEY_WAKEUP, 1); + input_report_key(data->input_dev, + KEY_WAKEUP, 0); + input_sync(data->input_dev); +} + +static void sec_keyboard_remapkey(struct work_struct *work) +{ + unsigned int keycode = 0; + struct sec_keyboard_drvdata *data = container_of(work, + struct sec_keyboard_drvdata, remap_dwork.work); + + if (data->pressed[0x45] || data->pressed[0x48]) { + keycode = data->keycode[data->remap_key]; + input_report_key(data->input_dev, keycode, 1); + input_sync(data->input_dev); + } + data->remap_key = 0; +} + +static void release_all_keys(struct sec_keyboard_drvdata *data) +{ + int i; + printk(KERN_DEBUG "[Keyboard] Release the pressed keys.\n"); + for (i = 0; i < KEYBOARD_MAX; i++) { + if (data->pressed[i]) { + input_report_key(data->input_dev, data->keycode[i], 0); + data->pressed[i] = false; + } + input_sync(data->input_dev); + } +} + +static void sec_keyboard_process_data( + struct sec_keyboard_drvdata *data, u8 scan_code) +{ + bool press; + unsigned int keycode; + + /* keyboard driver need the contry code*/ + if (data->kl == UNKOWN_KEYLAYOUT) { + switch (scan_code) { + case US_KEYBOARD: + data->kl = US_KEYLAYOUT; + data->keycode[49] = KEY_BACKSLASH; + /* for the wakeup state*/ + data->pre_kl = data->kl; + printk(KERN_DEBUG "[Keyboard] US keyboard is attacted.\n"); + break; + + case UK_KEYBOARD: + data->kl = UK_KEYLAYOUT; + data->keycode[49] = KEY_NUMERIC_POUND; + /* for the wakeup state*/ + data->pre_kl = data->kl; + printk(KERN_DEBUG "[Keyboard] UK keyboard is attacted.\n"); + break; + + default: + printk(KERN_DEBUG "[Keyboard] Unkown layout : %x\n", + scan_code); + break; + } + } else { + switch (scan_code) { + case 0x0: + release_all_keys(data); + break; + + case 0xca: /* Caps lock on */ + case 0xcb: /* Caps lock off */ + case 0xeb: /* US keyboard */ + case 0xec: /* UK keyboard */ + break; /* Ignore */ + + case 0x45: + case 0x48: + data->remap_key = scan_code; + data->pressed[scan_code] = true; + schedule_delayed_work(&data->remap_dwork, HZ/3); + break; + + case 0xc5: + case 0xc8: + keycode = (scan_code & 0x7f); + data->pressed[keycode] = false; + if (0 == data->remap_key) { + input_report_key(data->input_dev, + data->keycode[keycode], 0); + input_sync(data->input_dev); + } else { + cancel_delayed_work_sync(&data->remap_dwork); + if (0x48 == keycode) + keycode = KEY_NEXTSONG; + else + keycode = KEY_PREVIOUSSONG; + + input_report_key(data->input_dev, + keycode, 1); + input_report_key(data->input_dev, + keycode, 0); + input_sync(data->input_dev); + } + break; + + default: + keycode = (scan_code & 0x7f); + press = ((scan_code & 0x80) != 0x80); + + if (keycode >= KEYBOARD_MIN + || keycode <= KEYBOARD_MAX) { + data->pressed[keycode] = press; + input_report_key(data->input_dev, + data->keycode[keycode], press); + input_sync(data->input_dev); + } + break; + } + } +} + +static int check_keyboard_dock(struct sec_keyboard_callbacks *cb, bool val) +{ + struct sec_keyboard_drvdata *data = + container_of(cb, struct sec_keyboard_drvdata, callbacks); + int try_cnt = 0; + int max_cnt = 14; + + if (NULL == data->serio) + return 0; + + if (!val) + data->dockconnected = false; + else { + cancel_delayed_work_sync(&data->power_dwork); + /* wakeup by keyboard dock */ + if (data->pre_connected) { + if (UNKOWN_KEYLAYOUT != data->pre_kl) { + data->kl = data->pre_kl; + data->acc_power(1, true); + forced_wakeup(data); + printk(KERN_DEBUG "[Keyboard] kl : %d\n", + data->pre_kl); + return 1; + } + } else + data->pre_kl = UNKOWN_KEYLAYOUT; + + data->pre_connected = true; + + /* to prevent the over current issue */ + data->acc_power(0, false); + + if (data->check_uart_path) + data->check_uart_path(true); + + msleep(200); + data->acc_power(1, true); + + /* try to get handshake data */ + for (try_cnt = 0; try_cnt < max_cnt; try_cnt++) { + msleep(50); + if (data->kl != UNKOWN_KEYLAYOUT) { + data->dockconnected = true; + break; + } + if (gpio_get_value(data->acc_int_gpio)) { + printk(KERN_DEBUG "[Keyboard] acc is disconnected.\n"); + break; + } + } + } + + if (data->dockconnected) { + forced_wakeup(data); + return 1; + } else { + if (data->pre_connected) { + data->dockconnected = false; + schedule_delayed_work(&data->power_dwork, HZ/2); + + data->kl = UNKOWN_KEYLAYOUT; + release_all_keys(data); + } + return 0; + } +} + +static int sec_keyboard_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct sec_keyboard_drvdata *data = input_get_drvdata(dev); + + switch (type) { + case EV_LED: + if (value) + sec_keyboard_tx(data, 0xca); + else + sec_keyboard_tx(data, 0xcb); + return 0; + } + return -1; +} + +static ssize_t check_keyboard_connection(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sec_keyboard_drvdata *ddata = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ddata->dockconnected); +} + +static DEVICE_ATTR(attached, S_IRUGO, check_keyboard_connection, NULL); + +static struct attribute *sec_keyboard_attributes[] = { + &dev_attr_attached.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = sec_keyboard_attributes, +}; + +static irqreturn_t sec_keyboard_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct sec_keyboard_drvdata *ddata = serio_get_drvdata(serio); + if (ddata->pre_connected) + sec_keyboard_process_data(ddata, data); + return IRQ_HANDLED; +} + +static int sec_keyboard_connect(struct serio *serio, struct serio_driver *drv) +{ + struct sec_keyboard_drvdata *data = container_of(drv, + struct sec_keyboard_drvdata, serio_driver); + printk(KERN_DEBUG "[Keyboard] %s", __func__); + data->serio = serio; + serio_set_drvdata(serio, data); + if (serio_open(serio, drv)) + printk(KERN_ERR "[Keyboard] failed to open serial port\n"); + else + data->tx_ready = true; + return 0; +} + +static void sec_keyboard_disconnect(struct serio *serio) +{ + struct sec_keyboard_drvdata *data = serio_get_drvdata(serio); + printk(KERN_DEBUG "[Keyboard] %s", __func__); + data->tx_ready = false; + serio_close(serio); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void keyboard_early_suspend(struct early_suspend *early_sus) +{ + struct sec_keyboard_drvdata *data = container_of(early_sus, + struct sec_keyboard_drvdata, early_suspend); + + if (data->kl != UNKOWN_KEYLAYOUT) { + /* + if the command of the caps lock off is needed, + this command should be sent. + sec_keyboard_tx(0xcb); + msleep(20); + */ + sec_keyboard_tx(data, 0x10); /* the idle mode */ + } +} + +static void keyboard_late_resume(struct early_suspend *early_sus) +{ + struct sec_keyboard_drvdata *data = container_of(early_sus, + struct sec_keyboard_drvdata, early_suspend); + + if (data->kl != UNKOWN_KEYLAYOUT) + printk(KERN_DEBUG "[Keyboard] %s\n", __func__); + +} +#endif +static int __devinit sec_keyboard_probe(struct platform_device *pdev) +{ + struct sec_keyboard_platform_data *pdata = pdev->dev.platform_data; + struct sec_keyboard_drvdata *ddata; + struct input_dev *input; + int i, error; + + if (pdata == NULL) { + printk(KERN_ERR "%s: no pdata\n", __func__); + return -ENODEV; + } + + ddata = kzalloc(sizeof(struct sec_keyboard_drvdata), GFP_KERNEL); + if (NULL == ddata) { + error = -ENOMEM; + goto err_free_mem; + } + + input = input_allocate_device(); + if (NULL == input) { + printk(KERN_ERR "[Keyboard] failed to allocate input device.\n"); + error = -ENOMEM; + goto err_free_mem; + } + + ddata->input_dev = input; + ddata->acc_power = pdata->acc_power; + ddata->check_uart_path = pdata->check_uart_path; + ddata->acc_int_gpio = pdata->accessory_irq_gpio; + ddata->led_on = false; + ddata->dockconnected = false; + ddata->pre_connected = false; + ddata->remap_key = 0; + ddata->kl = UNKOWN_KEYLAYOUT; + ddata->callbacks.check_keyboard_dock = check_keyboard_dock; + if (pdata->register_cb) + pdata->register_cb(&ddata->callbacks); + + memcpy(ddata->keycode, sec_keycodes, sizeof(sec_keycodes)); + + INIT_DELAYED_WORK(&ddata->remap_dwork, sec_keyboard_remapkey); + INIT_DELAYED_WORK(&ddata->power_dwork, sec_keyboard_power); + + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); + + input->name = pdev->name; + input->dev.parent = &pdev->dev; + input->id.bustype = BUS_RS232; + input->event = sec_keyboard_event; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_LED, input->evbit); + __set_bit(LED_CAPSL, input->ledbit); + /* framework doesn't use repeat event */ + /* __set_bit(EV_REP, input->evbit); */ + + for (i = 0; i < KEYBOARD_SIZE; i++) { + if (KEY_RESERVED != ddata->keycode[i]) + input_set_capability(input, EV_KEY, ddata->keycode[i]); + } + + /* for the UK keyboard */ + input_set_capability(input, EV_KEY, KEY_NUMERIC_POUND); + + /* for the remaped keys */ + input_set_capability(input, EV_KEY, KEY_NEXTSONG); + input_set_capability(input, EV_KEY, KEY_PREVIOUSSONG); + + /* for the wakeup key */ + input_set_capability(input, EV_KEY, KEY_WAKEUP); + + error = input_register_device(input); + if (error < 0) { + printk(KERN_ERR "[Keyboard] failed to register input device.\n"); + error = -ENOMEM; + goto err_input_allocate_device; + } + + ddata->serio_driver.driver.name = pdev->name; + ddata->serio_driver.id_table = sec_serio_ids; + ddata->serio_driver.interrupt = sec_keyboard_interrupt, + ddata->serio_driver.connect = sec_keyboard_connect, + ddata->serio_driver.disconnect = sec_keyboard_disconnect, + + error = serio_register_driver(&ddata->serio_driver); + if (error < 0) { + printk(KERN_ERR "[Keyboard] failed to register serio\n"); + error = -ENOMEM; + goto err_reg_serio; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ddata->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ddata->early_suspend.suspend = keyboard_early_suspend; + ddata->early_suspend.resume = keyboard_late_resume; + register_early_suspend(&ddata->early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + ddata->keyboard_dev = device_create(sec_class, NULL, 0, + ddata, "sec_keyboard"); + if (IS_ERR(ddata->keyboard_dev)) { + printk(KERN_ERR "[Keyboard] failed to create device for the sysfs\n"); + error = -ENODEV; + goto err_sysfs_create_group; + } + + error = sysfs_create_group(&ddata->keyboard_dev->kobj, &attr_group); + if (error) { + printk(KERN_ERR "[Keyboard] failed to create sysfs group\n"); + goto err_sysfs_create_group; + } + + return 0; + +err_sysfs_create_group: +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ddata->early_suspend); +#endif + serio_unregister_driver(&ddata->serio_driver); +err_reg_serio: +err_input_allocate_device: + input_free_device(input); + del_timer_sync(&ddata->remap_dwork.timer); + del_timer_sync(&ddata->power_dwork.timer); +err_free_mem: + kfree(ddata); + return error; + +} + +static int __devexit sec_keyboard_remove(struct platform_device *pdev) +{ + struct sec_keyboard_drvdata *data = platform_get_drvdata(pdev); + input_unregister_device(data->input_dev); + serio_unregister_driver(&data->serio_driver); + return 0; +} + +#ifndef CONFIG_HAS_EARLYSUSPEND +static int sec_keyboard_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct sec_keyboard_drvdata *data = platform_get_drvdata(pdev); + + if (data->kl != UNKOWN_KEYLAYOUT) + sec_keyboard_tx(data, 0x10); + + return 0; +} + +static int sec_keyboard_resume(struct platform_device *pdev) +{ + struct sec_keyboard_platform_data *pdata = pdev->dev.platform_data; + struct sec_keyboard_drvdata *data = platform_get_drvdata(pdev); + if (pdata->wakeup_key) { + if (KEY_WAKEUP == pdata->wakeup_key()) + forced_wakeup(data); + } + + return 0; +} +#endif + +static struct platform_driver sec_keyboard_driver = { + .probe = sec_keyboard_probe, + .remove = __devexit_p(sec_keyboard_remove), +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = sec_keyboard_suspend, + .resume = sec_keyboard_resume, +#endif + .driver = { + .name = "sec_keyboard", + .owner = THIS_MODULE, + } +}; + +static int __init sec_keyboard_init(void) +{ + return platform_driver_register(&sec_keyboard_driver); +} + +static void __exit sec_keyboard_exit(void) +{ + platform_driver_unregister(&sec_keyboard_driver); +} + +module_init(sec_keyboard_init); +module_exit(sec_keyboard_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SEC Keyboard Dock driver"); diff --git a/drivers/accessory/sec_keyboard.h b/drivers/accessory/sec_keyboard.h new file mode 100644 index 0000000..7a30b58 --- /dev/null +++ b/drivers/accessory/sec_keyboard.h @@ -0,0 +1,208 @@ + +#ifndef _SEC_KEYBOARD_H_ +#define _SEC_KEYBOARD_H_ + +#include <linux/input.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/earlysuspend.h> +#include <linux/vmalloc.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/30pin_con.h> +#include <linux/serio.h> + +#define KEYBOARD_SIZE 128 +#define US_KEYBOARD 0xeb +#define UK_KEYBOARD 0xec + +#define KEYBOARD_MIN 0x4 +#define KEYBOARD_MAX 0x7f + +enum KEY_LAYOUT { + UNKOWN_KEYLAYOUT = 0, + US_KEYLAYOUT, + UK_KEYLAYOUT, +}; + +extern struct class *sec_class; + +static struct serio_device_id sec_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = 0x3c, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, sec_serio_ids); + +struct sec_keyboard_drvdata { + struct input_dev *input_dev; + struct device *keyboard_dev; + struct delayed_work remap_dwork; + struct delayed_work power_dwork; + struct sec_keyboard_callbacks callbacks; + struct serio *serio; + struct serio_driver serio_driver; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + void (*acc_power)(u8 token, bool active); + void (*check_uart_path)(bool en); + bool led_on; + bool dockconnected; + bool pre_connected; + bool pressed[KEYBOARD_SIZE]; + bool pre_uart_path; + bool tx_ready; + int acc_int_gpio; + unsigned int remap_key; + unsigned int kl; + unsigned int pre_kl; + unsigned short keycode[KEYBOARD_SIZE]; + unsigned long connected_time; + unsigned long disconnected_time; +}; + +static const unsigned short sec_keycodes[KEYBOARD_SIZE] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + KEY_ENTER, + KEY_BACK, + KEY_BACKSPACE, + KEY_TAB, + KEY_SPACE, + KEY_MINUS, + KEY_EQUAL, + KEY_LEFTBRACE, + KEY_RIGHTBRACE, + KEY_HOME, + KEY_RESERVED, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_GRAVE, + KEY_COMMA, + KEY_DOT, + KEY_SLASH, + KEY_CAPSLOCK, + KEY_TIME, + KEY_F3, + KEY_WWW, + KEY_EMAIL, + KEY_SCREENLOCK, + KEY_BRIGHTNESSDOWN, + KEY_BRIGHTNESSUP, + KEY_MUTE, + KEY_VOLUMEDOWN, + KEY_VOLUMEUP, + KEY_PLAY, + KEY_REWIND, + KEY_F15, + KEY_RESERVED, + KEY_FASTFORWARD, + KEY_MENU, + KEY_RESERVED, + KEY_RESERVED, + KEY_DELETE, + KEY_RESERVED, + KEY_RESERVED, + KEY_RIGHT, + KEY_LEFT, + KEY_DOWN, + KEY_UP, + KEY_NUMLOCK, + KEY_KPSLASH, + KEY_APOSTROPHE, + KEY_KPMINUS, + KEY_KPPLUS, + KEY_KPENTER, + KEY_KP1, + KEY_KP2, + KEY_KP3, + KEY_KP4, + KEY_KP5, + KEY_KP6, + KEY_KP7, + KEY_KP8, + KEY_KP9, + KEY_KPDOT, + KEY_RESERVED, + KEY_BACKSLASH, + KEY_F22, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_HANGEUL, + KEY_HANJA, + KEY_LEFTCTRL, + KEY_LEFTSHIFT, + KEY_F20, + KEY_SEARCH, + KEY_RIGHTALT, + KEY_RIGHTSHIFT, + KEY_F21, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_F17, +}; +#endif /*_SEC_KEYBOARD_H_*/ + diff --git a/drivers/accessory/sii9234.c b/drivers/accessory/sii9234.c new file mode 100644 index 0000000..d8c0408 --- /dev/null +++ b/drivers/accessory/sii9234.c @@ -0,0 +1,339 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <asm/irq.h> +#include <linux/delay.h> +#include <linux/mhd9234.h> + +#include "MHD_SiI9234.h" +#include "sii9234_tpi_regs.h" + + +#include <linux/syscalls.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> + +#include <linux/slab.h> + + +#define SUBJECT "MHL_DRIVER" + +#define SII_DEV_DBG(format, ...)\ + pr_info("[ "SUBJECT " (%s,%d) ] " format "\n",\ + __func__, __LINE__, ## __VA_ARGS__); + +struct i2c_client *SII9234_i2c_client; +struct i2c_client *SII9234A_i2c_client; +struct i2c_client *SII9234B_i2c_client; +struct i2c_client *SII9234C_i2c_client; + +static struct i2c_device_id SII9234_id[] = { + {"SII9234", 0}, + {} +}; + +static struct i2c_device_id SII9234A_id[] = { + {"SII9234A", 0}, + {} +}; + +static struct i2c_device_id SII9234B_id[] = { + {"SII9234B", 0}, + {} +}; + +static struct i2c_device_id SII9234C_id[] = { + {"SII9234C", 0}, + {} +}; + +struct SII9234_state { + struct i2c_client *client; +}; + +static struct timer_list MHL_reg_check; + +static u8 SII9234_i2c_read(struct i2c_client *client, u8 reg) +{ + u8 ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + SII_DEV_DBG("i2c read fail"); + return -EIO; + } + return ret; + +} + + +static int SII9234_i2c_write(struct i2c_client *client, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(client, reg, data); +} + +void SII9234_HW_Reset(struct i2c_client *client) +{ + struct sii9234_platform_data *pdata = client->dev.platform_data; + SII_DEV_DBG(""); + pdata->hw_reset(); +} + +void SII9234_HW_Off(struct i2c_client *client) +{ + struct sii9234_platform_data *pdata = client->dev.platform_data; + SII_DEV_DBG(""); + pdata->hw_off(); +} + +int SII9234_HW_IsOn(void) +{ +/* #warning - this needs fixing */ +/* int IsOn = gpio_get_value(GPIO_HDMI_EN1); */ +/* if(IsOn) */ + return true; +/* else */ + return false; +} + +#include "MHD_SiI9234.c" + +void sii_9234_monitor(unsigned long arg) +{ + SII_DEV_DBG(""); + /*sii9234_polling(); */ + ReadIndexedRegister(INDEXED_PAGE_0, 0x81); +#if 0 + pr_info("SII9234_i2c_read INDEXED_PAGE_0: 0x%02x\n", data); + + MHL_reg_check.expires = get_jiffies_64() + (HZ*3); + add_timer(&MHL_reg_check); +#endif +} + +static void check_HDMI_signal(unsigned long arg) +{ + SII_DEV_DBG(""); + +#if 0 + u8 data; + + MHL_HW_Reset(); + sii9234_initial_registers_set(); + startTPI(); + mhl_output_enable(); +#endif + sii9234_tpi_init(); + + MHL_reg_check.function = sii_9234_monitor; + MHL_reg_check.expires = get_jiffies_64() + (HZ*3); + add_timer(&MHL_reg_check); +#if 0 + data = ReadIndexedRegister(INDEXED_PAGE_0, 0x81); + pr_info("SII9234_i2c_read INDEXED_PAGE_0: 0x%02x\n", data); +#endif +} + + + +#if 0 +static DECLARE_DELAYED_WORK(init_sii9234, sii92324_init_sequance); +#endif + +static int SII9234_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct SII9234_state *state; + + SII_DEV_DBG(""); + + state = kzalloc(sizeof(struct SII9234_state), GFP_KERNEL); + if (!state) { + pr_err("failed to allocate memory\n"); + return -ENOMEM; + } + + state->client = client; + i2c_set_clientdata(client, state); + + /* rest of the initialisation goes here. */ + + pr_info("SII9234 attach success!!!\n"); + + SII9234_i2c_client = client; + +#if 0 + schedule_delayed_work(&init_sii9234, 5000); + + init_timer(&MHL_reg_check); + MHL_reg_check.function = check_HDMI_signal; + MHL_reg_check.expires = get_jiffies_64() + (HZ*10); + add_timer(&MHL_reg_check); + + MHL_HW_Reset(); + sii9234_initial_registers_set(); + startTPI(); + mhl_output_enable(); +#endif + + return 0; +} + + + +static int __devexit SII9234_remove(struct i2c_client *client) +{ + struct SII9234_state *state = i2c_get_clientdata(client); + kfree(state); + + return 0; +} + +static int SII9234A_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct SII9234_state *state; + + SII_DEV_DBG(""); + + state = kzalloc(sizeof(struct SII9234_state), GFP_KERNEL); + if (!state) { + pr_err("failed to allocate memory\n"); + return -ENOMEM; + } + + state->client = client; + i2c_set_clientdata(client, state); + + /* rest of the initialisation goes here. */ + + pr_info("SII9234A attach success!!!\n"); + + SII9234A_i2c_client = client; + + return 0; + +} + + + +static int __devexit SII9234A_remove(struct i2c_client *client) +{ + struct SII9234_state *state = i2c_get_clientdata(client); + kfree(state); + return 0; +} + +static int SII9234B_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct SII9234_state *state; + + SII_DEV_DBG(""); + + state = kzalloc(sizeof(struct SII9234_state), GFP_KERNEL); + if (!state) { + pr_err("failed to allocate memory\n"); + return -ENOMEM; + } + + state->client = client; + i2c_set_clientdata(client, state); + + /* rest of the initialisation goes here. */ + + pr_info("SII9234B attach success!!!\n"); + + SII9234B_i2c_client = client; + + return 0; +} + + + +static int __devexit SII9234B_remove(struct i2c_client *client) +{ + struct SII9234_state *state = i2c_get_clientdata(client); + kfree(state); + return 0; +} + +static int SII9234C_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct SII9234_state *state; + + SII_DEV_DBG(""); + + state = kzalloc(sizeof(struct SII9234_state), GFP_KERNEL); + if (!state) { + pr_err("failed to allocate memory\n"); + return -ENOMEM; + } + + state->client = client; + i2c_set_clientdata(client, state); + + /* rest of the initialisation goes here. */ + pr_info("SII9234C attach success!!!\n"); + + SII9234C_i2c_client = client; + + return 0; + +} + + + +static int __devexit SII9234C_remove(struct i2c_client *client) +{ + struct SII9234_state *state = i2c_get_clientdata(client); + kfree(state); + return 0; +} + + +struct i2c_driver SII9234_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "SII9234", + }, + .id_table = SII9234_id, + .probe = SII9234_i2c_probe, + .remove = __devexit_p(SII9234_remove), + .command = NULL, +}; + +struct i2c_driver SII9234A_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "SII9234A", + }, + .id_table = SII9234A_id, + .probe = SII9234A_i2c_probe, + .remove = __devexit_p(SII9234A_remove), + .command = NULL, +}; + +struct i2c_driver SII9234B_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "SII9234B", + }, + .id_table = SII9234B_id, + .probe = SII9234B_i2c_probe, + .remove = __devexit_p(SII9234B_remove), + .command = NULL, +}; + +struct i2c_driver SII9234C_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "SII9234C", + }, + .id_table = SII9234C_id, + .probe = SII9234C_i2c_probe, + .remove = __devexit_p(SII9234C_remove), + .command = NULL, +}; + + diff --git a/drivers/accessory/sii9234.h b/drivers/accessory/sii9234.h new file mode 100644 index 0000000..faa1227 --- /dev/null +++ b/drivers/accessory/sii9234.h @@ -0,0 +1,191 @@ +/* + * Silicon Image MHL(Mobile HD Link) Transmitter device driver + * + * Copyright (c) by Dongsoo Kim <dongsoo45.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <linux/types.h> + +#define SII9234_I2C_CHECK_RETRY 50 +#define SII9234_SEC(a) ((a*HZ/10) * 10) + +#define TRUE 1 +#define FALSE 0 + +/* + * regset item mode ID + */ +#define SII_W 0x01 +#define SII_R 0x02 +#define SII_M 0x03 +#define SII_D 0x04 + +/* + * Local register define + */ +#define TX_SLAVE_ADDR (0x72 >> 1) +#define SII9234_ES_P1 (0x7A >> 1) +#define SII9234_ES_P2 (0x92 >> 1) +#define CBUS_SLAVE_ADDR (0xC8 >> 1) + +/* BIT MASK */ +#define BIT_0 0x01 +#define BIT_1 0x02 +#define BIT_2 0x04 +#define BIT_3 0x08 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 + +/* power state */ +#define TX_POWER_STATE_D0 0x00 +#define TX_POWER_STATE_D1 0x01 +#define TX_POWER_STATE_D2 0x02 +#define TX_POWER_STATE_D3 0x03 + +/* + * @sii9234_regset + * mode : 0x01(write), 0x02(read), 0x03(mask) + * addr : TX_SLAVE_ADDR, SII9234_ES_P1, + * SII9234_ES_P2, CBUS_SLAVE_ADDR + * reg : address of register + * val : expecting value + */ +struct sii9234_regset { + unsigned char mode; + unsigned char addr; + unsigned char reg; + unsigned char val; +}; + +/* TPI System Control Register ================= */ + +#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A) + +#define LINK_INTEGRITY_MODE_MASK (BIT_6) +#define LINK_INTEGRITY_STATIC (0x00) +#define LINK_INTEGRITY_DYNAMIC (0x40) + +#define TMDS_OUTPUT_CONTROL_MASK (BIT_4) +#define TMDS_OUTPUT_CONTROL_ACTIVE (0x00) +#define TMDS_OUTPUT_CONTROL_POWER_DOWN (0x10) + +#define AV_MUTE_MASK (BIT_3) +#define AV_MUTE_NORMAL (0x00) +#define AV_MUTE_MUTED (0x08) + +#define DDC_BUS_REQUEST_MASK (BIT_2) +#define DDC_BUS_REQUEST_NOT_USING (0x00) +#define DDC_BUS_REQUEST_REQUESTED (0x04) + +#define DDC_BUS_GRANT_MASK (BIT_1) +#define DDC_BUS_GRANT_NOT_AVAILABLE (0x00) +#define DDC_BUS_GRANT_GRANTED (0x02) + +#define OUTPUT_MODE_MASK (BIT_0) +#define OUTPUT_MODE_DVI (0x00) +#define OUTPUT_MODE_HDMI (0x01) + +/* Interrupt Enable Register =================== */ + +#define TPI_INTERRUPT_ENABLE_REG (0x3C) + +#define HDCP_AUTH_STATUS_CHANGE_EN_MASK (BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_DISABLE (0x00) +#define HDCP_AUTH_STATUS_CHANGE_ENABLE (0x80) + +#define HDCP_VPRIME_VALUE_READY_EN_MASK (BIT_6) +#define HDCP_VPRIME_VALUE_READY_DISABLE (0x00) +#define HDCP_VPRIME_VALUE_READY_ENABLE (0x40) + +#define HDCP_SECURITY_CHANGE_EN_MASK (BIT_5) +#define HDCP_SECURITY_CHANGE_DISABLE (0x00) +#define HDCP_SECURITY_CHANGE_ENABLE (0x20) + +#define AUDIO_ERROR_EVENT_EN_MASK (BIT_4) +#define AUDIO_ERROR_EVENT_DISABLE (0x00) +#define AUDIO_ERROR_EVENT_ENABLE (0x10) + +#define CPI_EVENT_NO_RX_SENSE_MASK (BIT_3) +#define CPI_EVENT_NO_RX_SENSE_DISABLE (0x00) +#define CPI_EVENT_NO_RX_SENSE_ENABLE (0x08) + +#define RECEIVER_SENSE_EVENT_EN_MASK (BIT_1) +#define RECEIVER_SENSE_EVENT_DISABLE (0x00) +#define RECEIVER_SENSE_EVENT_ENABLE (0x02) + +#define HOT_PLUG_EVENT_EN_MASK (BIT_0) +#define HOT_PLUG_EVENT_DISABLE (0x00) +#define HOT_PLUG_EVENT_ENABLE (0x01) + +/* Interrupt status register ==================== */ +#define TPI_INTERRUPT_STATUS_REG (0x3D) + +#define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK (BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_NO (0x00) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_YES (0x80) + +#define HDCP_VPRIME_VALUE_READY_EVENT_MASK (BIT_6) +#define HDCP_VPRIME_VALUE_READY_EVENT_NO (0x00) +#define HDCP_VPRIME_VALUE_READY_EVENT_YES (0x40) + +#define HDCP_SECURITY_CHANGE_EVENT_MASK (BIT_5) +#define HDCP_SECURITY_CHANGE_EVENT_NO (0x00) +#define HDCP_SECURITY_CHANGE_EVENT_YES (0x20) + +#define AUDIO_ERROR_EVENT_MASK (BIT_4) +#define AUDIO_ERROR_EVENT_NO (0x00) +#define AUDIO_ERROR_EVENT_YES (0x10) + +#define CPI_EVENT_MASK (BIT_3) +#define CPI_EVENT_NO (0x00) +#define CPI_EVENT_YES (0x08) +/* This bit is dual purpose depending on the value of 0x3C[3] */ +#define RX_SENSE_MASK (BIT_3) +#define RX_SENSE_NOT_ATTACHED (0x00) +#define RX_SENSE_ATTACHED (0x08) + +#define HOT_PLUG_PIN_STATE_MASK (BIT_2) +#define HOT_PLUG_PIN_STATE_LOW (0x00) +#define HOT_PLUG_PIN_STATE_HIGH (0x04) + +#define RECEIVER_SENSE_EVENT_MASK (BIT_1) +#define RECEIVER_SENSE_EVENT_NO (0x00) +#define RECEIVER_SENSE_EVENT_YES (0x02) + +#define HOT_PLUG_EVENT_MASK (BIT_0) +#define HOT_PLUG_EVENT_NO (0x00) +#define HOT_PLUG_EVENT_YES (0x01) + +/* ===================================================== */ + +extern void sii9234_tpi_init(void); +extern void MHD_HW_Reset(void); +extern void MHD_HW_Off(void); +extern int MHD_HW_IsOn(void); +extern int MHD_Read_deviceID(void); +extern void MHD_INT_clear(void); +extern void MHD_OUT_EN(void); + +/*I2C driver add 20100614 kyungrok */ +extern struct i2c_driver SII9234_i2c_driver; +extern struct i2c_driver SII9234A_i2c_driver; +extern struct i2c_driver SII9234B_i2c_driver; +extern struct i2c_driver SII9234C_i2c_driver; + + diff --git a/drivers/accessory/sii9234_tpi_regs.h b/drivers/accessory/sii9234_tpi_regs.h new file mode 100644 index 0000000..7e0a71f --- /dev/null +++ b/drivers/accessory/sii9234_tpi_regs.h @@ -0,0 +1,381 @@ +/* + * MHD_SiI9234.c - Driver for Silicon Image MHD SiI9234 Transmitter driver + * + * Copyright 2010 Philju Lee (Daniel Lee) + * + * Based on preview driver from Silicon Image. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/*TPI Video Mode Data*/ + +#define TPI_PIX_CLK_LSB (0x00) +#define TPI_PIX_CLK_MSB (0x01) + +#define TPI_VERT_FREQ_LSB (0x02) +#define TPI_VERT_FREQ_MSB (0x03) + +#define TPI_TOTAL_PIX_LSB (0x04) +#define TPI_TOTAL_PIX_MSB (0x05) + +#define TPI_TOTAL_LINES_LSB (0x06) +#define TPI_TOTAL_LINES_MSB (0x07) + +/*Pixel Repetition Data*/ + +#define TPI_PIX_REPETITION (0x08) + +/*TPI AVI Input and Output Format Data*/ + +/* AVI Input Format Data =====================================*/ + +#define TPI_INPUT_FORMAT_REG (0x09) + +#define INPUT_COLOR_SPACE_MASK (SI_BIT_1 | SI_BIT_0) +#define INPUT_COLOR_SPACE_RGB (SI_ZERO) +#define INPUT_COLOR_SPACE_YCBCR444 (SI_BIT_0) +#define INPUT_COLOR_SPACE_YCBCR422 (SI_BIT_1) +#define INPUT_COLOR_SPACE_BLACK_MODE (SI_BIT_1 | SI_BIT_0) + +/* AVI Output Format Data =============================================== */ + +#define TPI_OUTPUT_FORMAT_REG (0x0A) + +#define TPI_YC_Input_Mode (0x0B) + +/*TPI AVI InfoFrame Data*/ + +#define TPI_AVI_BYTE_0 (0x0C) +#define TPI_AVI_BYTE_1 (0x0D) +#define TPI_AVI_BYTE_2 (0x0E) +#define TPI_AVI_BYTE_3 (0x0F) +#define TPI_AVI_BYTE_4 (0x10) +#define TPI_AVI_BYTE_5 (0x11) + +#define TPI_AUDIO_BYTE_0 (0xBF) + +#define TPI_END_TOP_BAR_LSB (0x12) +#define TPI_END_TOP_BAR_MSB (0x13) + +#define TPI_START_BTM_BAR_LSB (0x14) +#define TPI_START_BTM_BAR_MSB (0x15) + +#define TPI_END_LEFT_BAR_LSB (0x16) +#define TPI_END_LEFT_BAR_MSB (0x17) + +#define TPI_END_RIGHT_BAR_LSB (0x18) +#define TPI_END_RIGHT_BAR_MSB (0x19) + +/* Colorimetry*/ + +#define SET_EX_COLORIMETRY 0x0C +/*Set TPI_AVI_BYTE_2 to extended colorimetry and use*/ + + +/*===================================================== */ + +#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A) + +#define LINK_INTEGRITY_MODE_MASK (SI_BIT_6) +#define LINK_INTEGRITY_STATIC (SI_ZERO) +#define LINK_INTEGRITY_DYNAMIC (SI_BIT_6) + +#define TMDS_OUTPUT_CONTROL_MASK (SI_BIT_4) +#define TMDS_OUTPUT_CONTROL_ACTIVE (SI_ZERO) +#define TMDS_OUTPUT_CONTROL_POWER_DOWN (SI_BIT_4) + +#define AV_MUTE_MASK (SI_BIT_3) +#define AV_MUTE_NORMAL (SI_ZERO) +#define AV_MUTE_MUTED (SI_BIT_3) + +#define DDC_BUS_REQUEST_MASK (SI_BIT_2) +#define DDC_BUS_REQUEST_NOT_USING (SI_ZERO) +#define DDC_BUS_REQUEST_REQUESTED (SI_BIT_2) + +#define DDC_BUS_GRANT_MASK (SI_BIT_1) +#define DDC_BUS_GRANT_NOT_AVAILABLE (SI_ZERO) +#define DDC_BUS_GRANT_GRANTED (SI_BIT_1) + +#define OUTPUT_MODE_MASK (SI_BIT_0) +#define OUTPUT_MODE_DVI (SI_ZERO) +#define OUTPUT_MODE_HDMI (SI_BIT_0) + + +/*TPI Identification Registers*/ +/*=============================*/ + +#define TPI_DEVICE_ID (0x1B) +#define TPI_DEVICE_REV_ID (0x1C) +#define TPI_RESERVED2 (0x1D) + +/* ============================================== */ + +#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E) + +#define CTRL_PIN_CONTROL_MASK (SI_BIT_4) +#define CTRL_PIN_TRISTATE (SI_ZERO) +#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10) + +#define TX_POWER_STATE_MASK (SI_BIT_1 | SI_BIT_0) +#define TX_POWER_STATE_D0 (SI_ZERO) +#define TX_POWER_STATE_D1 (SI_BIT_0) +#define TX_POWER_STATE_D2 (SI_BIT_1) +#define TX_POWER_STATE_D3 (SI_BIT_1 | SI_BIT_0) + +/*Configuration of I2S Interface*/ + +#define TPI_I2S_EN (0x1F) +#define TPI_I2S_IN_CFG (0x20) + +/* Available only when TPI 0x26[7:6]=10 to select I2S input*/ +#define TPI_I2S_CHST_0 (0x21) +#define TPI_I2S_CHST_1 (0x22) +#define TPI_I2S_CHST_2 (0x23) +#define TPI_I2S_CHST_3 (0x24) +#define TPI_I2S_CHST_4 (0x25) + +/* Available only when 0x26[7:6]=01*/ +#define TPI_SPDIF_HEADER (0x24) +#define TPI_AUDIO_HANDLING (0x25) + + +/*Audio Configuration Regiaters*/ +#define TPI_AUDIO_INTERFACE_REG (0x26) + + +#define AUDIO_MUTE_MASK (SI_BIT_4) +#define AUDIO_MUTE_NORMAL (SI_ZERO) +#define AUDIO_MUTE_MUTED (SI_BIT_4) + +#define TPI_AUDIO_SAMPLE_CTRL (0x27) + +#define TPI_SPEAKER_CFG (0xC7) +#define TPI_CHANNEL_COUNT (0xC4) + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* +* HDCP Implementation +* +* HDCP link security logic is implemented in certain transmitters; unique +* keys are embedded in each chip as part of the solution. The security +* scheme is fully automatic and handled completely by the hardware. +*/ + +/* HDCP Query Data Register ============================================== */ + +#define TPI_HDCP_QUERY_DATA_REG (0x29) + +#define EXTENDED_LINK_PROTECTION_MASK (SI_BIT_7) +#define EXTENDED_LINK_PROTECTION_NONE (SI_ZERO) +#define EXTENDED_LINK_PROTECTION_SECURE (SI_BIT_7) + +#define LOCAL_LINK_PROTECTION_MASK (SI_BIT_6) +#define LOCAL_LINK_PROTECTION_NONE (SI_ZERO) +#define LOCAL_LINK_PROTECTION_SECURE (SI_BIT_6) + +#define LINK_STATUS_MASK (SI_BIT_5 | SI_BIT_4) +#define LINK_STATUS_NORMAL (SI_ZERO) +#define LINK_STATUS_LINK_LOST (SI_BIT_4) +#define LINK_STATUS_RENEGOTIATION_REQ (SI_BIT_5) +#define LINK_STATUS_LINK_SUSPENDED (SI_BIT_5 | SI_BIT_4) + +#define HDCP_REPEATER_MASK (SI_BIT_3) +#define HDCP_REPEATER_NO (SI_ZERO) +#define HDCP_REPEATER_YES (SI_BIT_3) + +#define CONNECTOR_TYPE_MASK (SI_BIT_2 | SI_BIT_0) +#define CONNECTOR_TYPE_DVI (SI_ZERO) +#define CONNECTOR_TYPE_RSVD (SI_BIT_0) +#define CONNECTOR_TYPE_HDMI (SI_BIT_2) +#define CONNECTOR_TYPE_FUTURE (SI_BIT_2 | SI_BIT_0) + +#define PROTECTION_TYPE_MASK (SI_BIT_1) +#define PROTECTION_TYPE_NONE (SI_ZERO) +#define PROTECTION_TYPE_HDCP (SI_BIT_1) + +/*HDCP Control Data Register ============================================ */ + +#define TPI_HDCP_CONTROL_DATA_REG (0x2A) + +#define PROTECTION_LEVEL_MASK (SI_BIT_0) +#define PROTECTION_LEVEL_MIN (0x00) +#define PROTECTION_LEVEL_MAX (0x01) + +/*HDCP BKSV Registers =================================================== */ + +#define TPI_BKSV_1_REG (0x2B) +#define TPI_BKSV_2_REG (0x2C) +#define TPI_BKSV_3_REG (0x2D) +#define TPI_BKSV_4_REG (0x2E) +#define TPI_BKSV_5_REG (0x2F) + +/* HDCP Revision Data Register =========================================== */ + +#define TPI_HDCP_REVISION_DATA_REG (0x30) + +#define HDCP_MAJOR_REVISION_MASK (SI_BIT_7 | SI_BIT_6 | SI_BIT_5 | SI_BIT_4) +#define HDCP_MAJOR_REVISION_VALUE (0x10) + +#define HDCP_MINOR_REVISION_MASK (SI_BIT_3 | SI_BIT_2 | SI_BIT_1 | SI_BIT_0) +#define HDCP_MINOR_REVISION_VALUE (0x02) + +/* HDCP KSV and V' Value Data Register =================================== */ + +#define TPI_V_PRIME_SELECTOR_REG (0x31) + +/* V' Value Readback Registers =========================================== */ + +#define TPI_V_PRIME_7_0_REG (0x32) +#define TPI_V_PRIME_15_9_REG (0x33) +#define TPI_V_PRIME_23_16_REG (0x34) +#define TPI_V_PRIME_31_24_REG (0x35) + +/* HDCP AKSV Registers =================================================== */ + +#define TPI_AKSV_1_REG (0x36) +#define TPI_AKSV_2_REG (0x37) +#define TPI_AKSV_3_REG (0x38) +#define TPI_AKSV_4_REG (0x39) +#define TPI_AKSV_5_REG (0x3A) + +/* +* Interrupt Service +* +* TPI can be configured to generate an interrupt to the host to notify it of +* various events. The host can either poll for activity or use an interrupt +* handler routine. TPI generates on a single interrupt (INT) to the host. +*/ + +/* Interrupt Enable Register ============================================= */ + +#define TPI_INTERRUPT_ENABLE_REG (0x3C) + +#define HDCP_AUTH_STATUS_CHANGE_EN_MASK (SI_BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_DISABLE (0x00) +#define HDCP_AUTH_STATUS_CHANGE_ENABLE (0x80) + +#define HDCP_VPRIME_VALUE_READY_EN_MASK (SI_BIT_6) +#define HDCP_VPRIME_VALUE_READY_DISABLE (0x00) +#define HDCP_VPRIME_VALUE_READY_ENABLE (0x40) + +#define HDCP_SECURITY_CHANGE_EN_MASK (SI_BIT_5) +#define HDCP_SECURITY_CHANGE_DISABLE (0x00) +#define HDCP_SECURITY_CHANGE_ENABLE (0x20) + +#define AUDIO_ERROR_EVENT_EN_MASK (SI_BIT_4) +#define AUDIO_ERROR_EVENT_DISABLE (0x00) +#define AUDIO_ERROR_EVENT_ENABLE (0x10) + +#define CPI_EVENT_NO_RX_SENSE_MASK (SI_BIT_3) +#define CPI_EVENT_NO_RX_SENSE_DISABLE (0x00) +#define CPI_EVENT_NO_RX_SENSE_ENABLE (0x08) + +#define RECEIVER_SENSE_EVENT_EN_MASK (SI_BIT_1) +#define RECEIVER_SENSE_EVENT_DISABLE (0x00) +#define RECEIVER_SENSE_EVENT_ENABLE (0x02) + +#define HOT_PLUG_EVENT_EN_MASK (SI_BIT_0) +#define HOT_PLUG_EVENT_DISABLE (0x00) +#define HOT_PLUG_EVENT_ENABLE (0x01) + +/* Interrupt Status Register ============================================= */ + +#define TPI_INTERRUPT_STATUS_REG (0x3D) + +#define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK (SI_BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_NO (0x00) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_YES (0x80) + +#define HDCP_VPRIME_VALUE_READY_EVENT_MASK (SI_BIT_6) +#define HDCP_VPRIME_VALUE_READY_EVENT_NO (0x00) +#define HDCP_VPRIME_VALUE_READY_EVENT_YES (0x40) + +#define HDCP_SECURITY_CHANGE_EVENT_MASK (SI_BIT_5) +#define HDCP_SECURITY_CHANGE_EVENT_NO (0x00) +#define HDCP_SECURITY_CHANGE_EVENT_YES (0x20) + +#define AUDIO_ERROR_EVENT_MASK (SI_BIT_4) +#define AUDIO_ERROR_EVENT_NO (0x00) +#define AUDIO_ERROR_EVENT_YES (0x10) + +#define CPI_EVENT_MASK (SI_BIT_3) +#define CPI_EVENT_NO (0x00) +#define CPI_EVENT_YES (0x08) +#define RX_SENSE_MASK (SI_BIT_3) +#define RX_SENSE_NOT_ATTACHED (0x00) +#define RX_SENSE_ATTACHED (0x08) + +#define HOT_PLUG_PIN_STATE_MASK (SI_BIT_2) +#define HOT_PLUG_PIN_STATE_LOW (0x00) +#define HOT_PLUG_PIN_STATE_HIGH (0x04) + +#define RECEIVER_SENSE_EVENT_MASK (SI_BIT_1) +#define RECEIVER_SENSE_EVENT_NO (0x00) +#define RECEIVER_SENSE_EVENT_YES (0x02) + +#define HOT_PLUG_EVENT_MASK (SI_BIT_0) +#define HOT_PLUG_EVENT_NO (0x00) +#define HOT_PLUG_EVENT_YES (0x01) + + +/* Sync Register Configuration and Sync Monitoring Registers*/ +/*==========================================================*/ + +#define TPI_SYNC_GEN_CTRL (0x60) +#define TPI_SYNC_POLAR_DETECT (0x61) + +/*Explicit Sync DE Generator Registers (TPI 0x60[7]=0)*/ +/*=====================================================*/ + +#define TPI_DE_DLY (0x62) +#define TPI_DE_CTRL (0x63) +#define TPI_DE_TOP (0x64) + +#define TPI_RESERVED4 (0x65) + +#define TPI_DE_CNT_7_0 (0x66) +#define TPI_DE_CNT_11_8 (0x67) + +#define TPI_DE_LIN_7_0 (0x68) +#define TPI_DE_LIN_10_8 (0x69) + +#define TPI_DE_H_RES_7_0 (0x6A) +#define TPI_DE_H_RES_10_8 (0x6B) + +#define TPI_DE_V_RES_7_0 (0x6C) +#define TPI_DE_V_RES_10_8 (0x6D) + +/* Embedded Sync Register Set (TPI 0x60[7]=1)*/ +/*===========================================*/ + +#define TPI_HBIT_TO_HSYNC_7_0 (0x62) +#define TPI_HBIT_TO_HSYNC_9_8 (0x63) +#define TPI_FIELD_2_OFFSET_7_0 (0x64) +#define TPI_FIELD_2_OFFSET_11_8 (0x65) +#define TPI_HWIDTH_7_0 (0x66) +#define TPI_HWIDTH_8_9 (0x67) +#define TPI_VBIT_TO_VSYNC (0x68) +#define TPI_VWIDTH (0x69) + +/*TPI Enable Register*/ + + +#define TPI_ENABLE (0xC7) + +/* Misc InfoFrames*/ +#define MISC_INFO_FRAMES_CTRL (0xBF) +#define MISC_INFO_FRAMES_TYPE (0xC0) +#define MISC_INFO_FRAMES_VER (0xC1) +#define MISC_INFO_FRAMES_LEN (0xC2) +#define MISC_INFO_FRAMES_CHKSUM (0xC3) |