| /* |
| * drivers/amlogic/media/vin/tvin/hdmirx_ext/hw_sii9135/sii9135_iface.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * 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. |
| * |
| */ |
| |
| /* implement of functions of hw_ops of hdmirx_ext_dev_s. */ |
| |
| #include "../hdmirx_ext_drv.h" |
| #include "../platform_iface.h" |
| #include "SiITypeDefs.h" |
| #include "SiIRXDefs.h" |
| #include "SiIGlob.h" |
| #include "SiIRXIO.h" |
| #include "SiIRX_API.h" |
| #include "sii9135_iface.h" |
| |
| /* #include "io.h" */ |
| /* #include "debug.h" */ |
| |
| #define SII9135_PULLUP_I2C_ADDR 0x31 |
| #define SII9135_PULLDOWN_I2C_ADDR 0x30 |
| #define SII9135_CHIP_NAME "SII9135" |
| |
| unsigned int i2c_write_byte(unsigned char address, |
| unsigned char offset, unsigned char byteno, |
| unsigned char *p_data, unsigned char device) |
| { |
| int ret = 0; |
| |
| ret = __plat_i2c_write_block(address, offset, p_data, byteno); |
| |
| return (ret > 0) ? 0 : 1; |
| } |
| |
| unsigned int i2c_read_byte(unsigned char address, |
| unsigned char offset, unsigned char byteno, |
| unsigned char *p_data, unsigned char device) |
| { |
| int ret = 0; |
| |
| ret = __plat_i2c_read_block(address, offset, p_data, byteno); |
| |
| return (ret > 0) ? 0 : 1; |
| } |
| |
| |
| /* signal stauts */ |
| static int __sii9135_get_cable_status(void) |
| { |
| if (siiRX_CheckCableHPD()) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int __sii9135_get_signal_status(void) |
| { |
| RXEXTPR("%s: sm_bVideo=%d\n", __func__, SiI_Ctrl.sm_bVideo); |
| #if 0 |
| if (SiI_Ctrl.sm_bVideo == SiI_RX_VS_VideoOn) |
| return 1; |
| #endif |
| if (siiIsVideoOn()) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int __sii9135_get_input_port(void) |
| { |
| if (SiI_Ctrl.bVidInChannel == SiI_RX_VInCh1) |
| return 1; |
| else |
| return 2; |
| |
| return 0; |
| } |
| |
| static void __sii9135_set_input_port(int port) |
| { |
| if (port == 2) |
| SiI_RX_SetVideoInput(SiI_RX_VInCh2); |
| else |
| SiI_RX_SetVideoInput(SiI_RX_VInCh1); |
| RXEXTPR("%s: %d\n", __func__, port); |
| } |
| |
| /* signal timming */ |
| static int __sii9135_get_video_timming(struct video_timming_s *ptimming) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| unsigned char tmp[4]; |
| unsigned char i2c_addr; |
| int ret = 0; |
| /* |
| * CurTMDSCLK = (int)ucTMDSClk; |
| * CurVTiming.PCLK = (int)PCLK; |
| * CurVTiming.HActive = HActive; |
| * CurVTiming.HTotal = HTotal; |
| * CurVTiming.HFrontPorch = HFP; |
| * CurVTiming.HSyncWidth = HSYNCW; |
| * CurVTiming.HBackPorch = HTotal - HActive - HFP - HSYNCW; |
| * CurVTiming.VActive = VActive; |
| * CurVTiming.VTotal = VTotal; |
| * CurVTiming.VFrontPorch = VFP; |
| * CurVTiming.VSyncWidth = VSYNCW; |
| * CurVTiming.VBackPorch = VTotal - VActive - VFP - VSYNCW; |
| * CurVTiming.ScanMode = (InterLaced)&0x01; |
| * CurVTiming.VPolarity = (VSyncPol)&0x01; |
| * CurVTiming.HPolarity = (HSyncPol)&0x01; |
| */ |
| /* |
| * ptimming->h_active = CurVTiming.HActive; |
| * ptimming->h_total = CurVTiming.HTotal; |
| * ptimming->hs_frontporch = CurVTiming.HFrontPorch; |
| * ptimming->hs_width = CurVTiming.HSyncWidth; |
| * ptimming->hs_backporch = CurVTiming.HBackPorch; |
| * ptimming->v_active = CurVTiming.VActive; |
| * ptimming->v_total = CurVTiming.VTotal; |
| * ptimming->vs_frontporch = CurVTiming.VFrontPorch; |
| * ptimming->vs_width = CurVTiming.VSyncWidth; |
| * ptimming->vs_backporch = CurVTiming.VBackPorch; |
| */ |
| i2c_addr = hdrv->hw.i2c_addr; |
| |
| ret = i2c_read_byte(i2c_addr, 0x4e, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x4e error\n"); |
| ptimming->h_active = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| |
| ret = i2c_read_byte(i2c_addr, 0x50, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x50 error\n"); |
| ptimming->v_active = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| |
| ret = i2c_read_byte(i2c_addr, 0x3a, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x3a error\n"); |
| ptimming->h_total = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| |
| ret = i2c_read_byte(i2c_addr, 0x3c, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x3c error\n"); |
| ptimming->v_total = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| |
| ret = i2c_read_byte(i2c_addr, 0x5b, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x5b error\n"); |
| ptimming->hs_width = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| ret = i2c_read_byte(i2c_addr, 0x59, 2, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x59 error\n"); |
| ptimming->hs_frontporch = (((unsigned int)tmp[1]) << 8) + tmp[0]; |
| ptimming->hs_backporch = ptimming->h_total - ptimming->hs_width - |
| ptimming->hs_frontporch - ptimming->h_active; |
| |
| ret = i2c_read_byte(i2c_addr, 0x53, 1, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x53 error\n"); |
| ptimming->vs_frontporch = tmp[0]; |
| ptimming->vs_width = 5; |
| ret = i2c_read_byte(i2c_addr, 0x52, 1, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x52 error\n"); |
| ptimming->vs_backporch = tmp[0] - ptimming->vs_width; |
| |
| ret = i2c_read_byte(i2c_addr, 0x55, 1, tmp, 0); |
| if (ret) |
| RXEXTERR("i2c read 0x52 error\n"); |
| ptimming->mode = (tmp[0] >> 2) & 1; |
| ptimming->hs_pol = (tmp[0] >> 0) & 1; |
| ptimming->vs_pol = (tmp[0] >> 1) & 1; |
| return 0; |
| } |
| |
| void __sii9135_dump_video_timming(void) |
| { |
| int h_active, v_active, h_total, v_total; |
| int hs_fp, hs_width, hs_bp; |
| int vs_fp, vs_width, vs_bp; |
| int hs_pol, vs_pol, mode; |
| struct video_timming_s timming; |
| |
| if (__sii9135_get_video_timming(&timming) == -1) { |
| RXEXTERR("%s: get video timming failed\n", __func__); |
| return; |
| } |
| |
| h_active = timming.h_active; |
| v_active = timming.v_active; |
| |
| h_total = timming.h_total; |
| v_total = timming.v_total; |
| |
| hs_fp = timming.hs_frontporch; |
| hs_width = timming.hs_width; |
| hs_bp = timming.hs_backporch; |
| |
| vs_fp = timming.vs_frontporch; |
| vs_width = timming.vs_width; |
| vs_bp = timming.vs_backporch; |
| |
| hs_pol = timming.hs_pol; |
| vs_pol = timming.vs_pol; |
| mode = timming.mode; |
| |
| RXEXTPR("sii9135 video info:\n" |
| "h * v active = %4d x %4d\n" |
| "h * v total = %4d x %4d\n" |
| "h_sync(fp, ws, bp) = %4d, %4d, %4d\n" |
| "v_sync(fp, ws, bp) = %4d, %4d, %4d\n" |
| "sync_pol(hs, vs) = %s[%d], %s[%d]\n" |
| "video_mode = %s[%d]\n", |
| h_active, v_active, h_total, v_total, |
| hs_fp, hs_width, hs_bp, |
| vs_fp, vs_width, vs_bp, |
| ((hs_pol) ? "positive" : "negative"), hs_pol, |
| ((vs_pol) ? "positive" : "negative"), vs_pol, |
| ((mode) ? "interlaced" : "progressive"), mode); |
| } |
| |
| static int __sii9135_is_hdmi_mode(void) |
| { |
| if (siiIsHDMI_Mode()) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int __sii9135_get_audio_sample_rate(void) |
| { |
| BYTE bRegVal; |
| |
| bRegVal = siiReadByteHDMIRXP1(RX_FREQ_SVAL_ADDR) & 0x0F; /*reg 102 */ |
| |
| return (int)bRegVal; |
| } |
| |
| static int __print_buf(char *buf, int length) |
| { |
| int i = 0; |
| |
| if (buf == NULL) |
| return -1; |
| |
| for (i = 0; i < length; i++) |
| pr_info("[0x%.2x] = %.2X\n", i, buf[i]); |
| |
| return 0; |
| } |
| |
| static int __sii9135_debug(char *buf) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| int argn = 0; |
| char *p = NULL, *para = NULL, *argv[4] = {NULL, NULL, NULL, NULL}; |
| char str[3] = {' ', '\n', '\0'}; |
| unsigned long cmd = 0, dev_addr = 0, reg_start = 0, reg_end = 0; |
| unsigned long length = 0, value = 0xff; |
| char i2c_buf[2] = {0, 0}; |
| char *tmp = NULL; |
| unsigned long type = 255, count = 0; |
| int ret = 0; |
| int i; |
| |
| p = kstrdup(buf, GFP_KERNEL); |
| while (1) { |
| para = strsep(&p, str); |
| if (para == NULL) |
| break; |
| if (*para == '\0') |
| continue; |
| argv[argn++] = para; |
| } |
| |
| /* pr_info("get para: %s %s %s %s!\n",argv[0],argv[1],argv[2],argv[3]);*/ |
| |
| if (!strcmp(argv[0], "r")) |
| cmd = 0; |
| else if (!strcmp(argv[0], "w")) |
| cmd = 1; |
| else if (!strcmp(argv[0], "dump")) |
| cmd = 2; |
| else if (!strcmp(argv[0], "vinfo")) |
| cmd = 3; |
| else if (!strcmp(argv[0], "help")) |
| cmd = 4; |
| else if (!strncmp(argv[0], "reset", strlen("reset"))) |
| cmd = 5; |
| else if (!strncmp(argv[0], "tt", strlen("tt"))) |
| cmd = 6; |
| else {/* invalid command format, take it as "help" */ |
| pr_info("invalid cmd = %s\n", argv[0]); |
| cmd = 4; |
| } |
| |
| pr_info(" cmd = %ld - \"%s\"\n", cmd, argv[0]); |
| if ((argn < 1) || ((cmd == 0) && (argn != 3)) || |
| ((cmd == 1) && (argn != 4)) || ((cmd == 2) && (argn != 4))) { |
| pr_info("invalid command format!\n"); |
| cmd = 4; |
| } |
| |
| switch (cmd) { |
| case 0: /* r device_address register_address*/ |
| ret = kstrtoul(argv[1], 16, &dev_addr); |
| ret = kstrtoul(argv[2], 16, ®_start); |
| |
| ret = i2c_read_byte(dev_addr, reg_start, 1, i2c_buf, 0); |
| pr_info("reg read dev:0x%x[0x%x] = 0x%x, ret = %d\n", |
| (unsigned int)dev_addr, (unsigned int)reg_start, |
| i2c_buf[0], ret); |
| break; |
| case 1: /* w device_address register_address value */ |
| ret = kstrtoul(argv[1], 16, &dev_addr); |
| ret = kstrtoul(argv[2], 16, ®_start); |
| ret = kstrtoul(argv[3], 16, &value); |
| |
| i2c_buf[0] = value; |
| |
| ret = i2c_write_byte(dev_addr, reg_start, 1, i2c_buf, 0); |
| pr_info("i2c write dev: 0x%x[0x%x] = 0x%x, ret = %d\n", |
| (unsigned int)dev_addr, (unsigned int)reg_start, |
| (unsigned int)value, ret); |
| ret = i2c_read_byte(dev_addr, reg_start, 1, i2c_buf, 0); |
| pr_info("i2c read back ret = %d, value = 0x%x\n", |
| ret, i2c_buf[0]); |
| break; |
| case 2: /* dump device_address register */ |
| ret = kstrtoul(argv[1], 16, &dev_addr); |
| ret = kstrtoul(argv[2], 16, ®_start); |
| ret = kstrtoul(argv[3], 16, ®_end); |
| length = reg_end - reg_start + 1; |
| |
| pr_info("reg dump dev = 0x%x, start = 0x%x, end = 0x%x\n", |
| (unsigned int)dev_addr, (unsigned int)reg_start, |
| (unsigned int)reg_end); |
| |
| tmp = kzalloc((sizeof(char) * length), GFP_KERNEL); |
| if (tmp != NULL) { |
| ret = i2c_read_byte(dev_addr, reg_start, |
| length, tmp, 0); |
| pr_info("sii9135 dev=0x%.2x regs=0x%.2x:0x%.2x:\n", |
| (unsigned int)dev_addr, (unsigned int)reg_start, |
| (unsigned int)(reg_start+length-1)); |
| __print_buf(tmp, length); |
| |
| kfree(tmp); |
| } |
| break; |
| case 3: /* vinfo */ |
| pr_info("begin dump hdmi-in video info:\n"); |
| __sii9135_dump_video_timming(); |
| break; |
| case 5: /* reset, for test reset_gpio */ |
| ret = kstrtoul(argv[1], 10, &value); |
| value = (value == 0) ? 0 : 1; |
| |
| __plat_gpio_output(&hdrv->hw.reset_gpio, value); |
| pr_info("reset_gpio is set to %d\n", (unsigned int)value); |
| break; |
| case 6: /* tt, for loop test of hdmirx_ext i2c */ |
| ret = kstrtoul(argv[1], 10, &type); |
| ret = kstrtoul(argv[2], 10, &count); |
| pr_info("i2c stability test: type = %ld, count = %ld\n", |
| type, count); |
| |
| if (type == 0) { /*write*/ |
| dev_addr = 0x60; |
| reg_start = 0x08; |
| value = 0x05; |
| i2c_buf[0] = value; |
| for (i = 0; i < count; i++) { |
| ret = i2c_write_byte(dev_addr, reg_start, |
| 1, i2c_buf, 0); |
| pr_info("i2c_w: 0x%x[0x%x]=0x%x, ret=%d\n", |
| (unsigned int)dev_addr, |
| (unsigned int)reg_start, |
| (unsigned int)value, ret); |
| udelay(50); |
| } |
| } else { /*read*/ |
| for (i = 0; i < count; i++) { |
| dev_addr = 0x60; |
| reg_start = 0x08; |
| ret = i2c_read_byte(dev_addr, reg_start, 1, |
| i2c_buf, 0); |
| pr_info("i2c_r: 0x%x[0x%x]=0x%x, ret=%d\n", |
| (unsigned int)dev_addr, |
| (unsigned int)reg_start, |
| i2c_buf[0], ret); |
| udelay(50); |
| } |
| } |
| break; |
| case 4: /* help */ |
| default: |
| pr_info("sii9135 debug command format:\n"); |
| pr_info(" read register: r device_address register_address\n"); |
| pr_info(" write register: w device_address register_address value\n"); |
| pr_info(" dump register: dump device_address register_address_start register_address_end\n"); |
| pr_info(" video timming: vinfo\n"); |
| break; |
| } |
| |
| kfree(p); |
| return 0; |
| } |
| |
| static char *__sii9135_get_chip_version(void) |
| { |
| #if 0 |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| unsigned char tmp[3]; |
| unsigned char i2c_addr; |
| int ret; |
| |
| i2c_addr = hdrv->hw.i2c_addr; |
| ret = i2c_read_byte(i2c_addr, RX_DEV_IDL_ADDR, 3, tmp, 0); |
| siiReadBlockHDMIRXP0(RX_DEV_IDL_ADDR, 3, pbChipVer); |
| #endif |
| return SII9135_CHIP_NAME; |
| } |
| |
| static void __sii9135_hw_reset(int flag) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| int val_on, val_off; |
| |
| if (hdrv->hw.reset_gpio.flag == 0) { |
| RXEXTPR("%s: invalid reset_gpio\n", __func__); |
| return; |
| } |
| RXEXTPR("%s: %d\n", __func__, flag); |
| |
| if (hdrv->hw.reset_gpio.val_on != -1) |
| val_on = hdrv->hw.reset_gpio.val_on; |
| else |
| val_on = 0; |
| if (hdrv->hw.reset_gpio.val_off != -1) |
| val_off = hdrv->hw.reset_gpio.val_off; |
| else |
| val_off = 1; |
| if (flag) |
| __plat_gpio_output(&hdrv->hw.reset_gpio, val_on); |
| else |
| __plat_gpio_output(&hdrv->hw.reset_gpio, val_off); |
| } |
| |
| static void __sii9135_hw_en(int flag) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| int val_on, val_off; |
| |
| if (hdrv->hw.en_gpio.flag == 0) { |
| RXEXTPR("%s: invalid en_gpio\n", __func__); |
| return; |
| } |
| RXEXTPR("%s: %d\n", __func__, flag); |
| |
| if (hdrv->hw.en_gpio.val_on != -1) |
| val_on = hdrv->hw.en_gpio.val_on; |
| else |
| val_on = 1; |
| if (hdrv->hw.en_gpio.val_off != -1) |
| val_off = hdrv->hw.en_gpio.val_off; |
| else |
| val_off = 0; |
| if (flag) |
| __plat_gpio_output(&hdrv->hw.en_gpio, val_on); |
| else |
| __plat_gpio_output(&hdrv->hw.en_gpio, val_off); |
| } |
| |
| static int __sii9135_task_handler(void *data) |
| { |
| sii9135_main(); |
| |
| return 0; |
| } |
| |
| static int __sii9135_init(void) |
| { |
| RXEXTPR("%s\n", __func__); |
| |
| return 0; |
| } |
| |
| static int __sii9135_enable(void) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| unsigned char i2c_addr = SII9135_PULLDOWN_I2C_ADDR; |
| |
| if (!hdrv) |
| return -1; |
| |
| RXEXTPR("%s\n", __func__); |
| |
| __sii9135_hw_en(1); |
| __plat_msleep(100); |
| |
| i2c_addr = hdrv->hw.i2c_addr; |
| |
| __plat_i2c_init(&(hdrv->hw), i2c_addr); |
| |
| __sii9135_hw_reset(1); |
| __plat_msleep(200); |
| __sii9135_hw_reset(0); |
| __plat_msleep(200); |
| |
| __plat_create_thread(__sii9135_task_handler, hdrv, HDMIRX_EXT_DRV_NAME); |
| |
| return 0; |
| } |
| |
| static void __sii9135_disable(void) |
| { |
| struct hdmirx_ext_drv_s *hdrv = hdmirx_ext_get_driver(); |
| |
| if (!hdrv) |
| return; |
| |
| RXEXTPR("%s\n", __func__); |
| |
| __sii9135_hw_reset(1); |
| __sii9135_hw_en(0); |
| } |
| |
| void hdmirx_ext_register_hw_sii9135(struct hdmirx_ext_drv_s *hdrv) |
| { |
| if (!hdrv) |
| return; |
| |
| RXEXTPR("%s\n", __func__); |
| hdrv->hw.get_cable_status = __sii9135_get_cable_status; |
| hdrv->hw.get_signal_status = __sii9135_get_signal_status; |
| hdrv->hw.get_input_port = __sii9135_get_input_port; |
| hdrv->hw.set_input_port = __sii9135_set_input_port; |
| hdrv->hw.get_video_timming = __sii9135_get_video_timming; |
| hdrv->hw.is_hdmi_mode = __sii9135_is_hdmi_mode; |
| hdrv->hw.get_audio_sample_rate = __sii9135_get_audio_sample_rate; |
| hdrv->hw.debug = __sii9135_debug; |
| hdrv->hw.get_chip_version = __sii9135_get_chip_version; |
| hdrv->hw.init = __sii9135_init; |
| hdrv->hw.enable = __sii9135_enable; |
| hdrv->hw.disable = __sii9135_disable; |
| } |
| |