/* * Copyright (C) 2013 Gateworks Corporation * * 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 * * TODO * - add gpio reset pin * - add gpio pwrdn pin * - document devicetree bindings * - unload/reload module interrupts never fire (something not getting reset) */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Voltage regulators */ #define TDA1997X_VOLTAGE_DIGITAL_IO 3300000 #define TDA1997X_VOLTAGE_DIGITAL_CORE 1800000 #define TDA1997X_VOLTAGE_ANALOG 1800000 static struct regulator *dvddio_regulator; static struct regulator *dvdd_regulator; static struct regulator *avdd_regulator; /* Page 0x00 */ #define REG_VERSION 0x0000 #define REG_INPUT_SEL 0x0001 #define REG_SERVICE_MODE 0x0002 #define REG_HPD_MAN_CTRL 0x0003 #define REG_RT_MAN_CTRL 0x0004 #define REG_STANDBY_SOFT_RST 0x000A #define REG_HDMI_SOFT_RST 0x000B #define REG_HDMI_INFO_RST 0x000C #define REG_INT_FLG_CLR_TOP 0x000E #define REG_INT_FLG_CLR_SUS 0x000F #define REG_INT_FLG_CLR_DDC 0x0010 #define REG_INT_FLG_CLR_RATE 0x0011 #define REG_INT_FLG_CLR_MODE 0x0012 #define REG_INT_FLG_CLR_INFO 0x0013 #define REG_INT_FLG_CLR_AUDIO 0x0014 #define REG_INT_FLG_CLR_HDCP 0x0015 #define REG_INT_FLG_CLR_AFE 0x0016 #define REG_INT_MASK_TOP 0x0017 #define REG_INT_MASK_SUS 0x0018 #define REG_INT_MASK_DDC 0x0019 #define REG_INT_MASK_RATE 0x001A #define REG_INT_MASK_MODE 0x001B #define REG_INT_MASK_INFO 0x001C #define REG_INT_MASK_AUDIO 0x001D #define REG_INT_MASK_HDCP 0x001E #define REG_INT_MASK_AFE 0x001F #define REG_DETECT_5V 0x0020 #define REG_SUS_STATUS 0x0021 #define REG_V_PER 0x0022 #define REG_H_PER 0x0025 #define REG_HS_WIDTH 0x0027 #define REG_FMT_H_TOT 0x0029 #define REG_FMT_H_ACT 0x002b #define REG_FMT_H_FRONT 0x002d #define REG_FMT_H_SYNC 0x002f #define REG_FMT_H_BACK 0x0031 #define REG_FMT_V_TOT 0x0033 #define REG_FMT_V_ACT 0x0035 #define REG_FMT_V_FRONT_F1 0x0037 #define REG_FMT_V_FRONT_F2 0x0038 #define REG_FMT_V_SYNC 0x0039 #define REG_FMT_V_BACK_F1 0x003a #define REG_FMT_V_BACK_F2 0x003b #define REG_FMT_DE_ACT 0x003c #define REG_RATE_CTRL 0x0040 #define REG_CLK_MIN_RATE 0x0043 #define REG_CLK_MAX_RATE 0x0046 #define REG_CLK_A_STATUS 0x0049 #define REG_CLK_A_RATE 0x004A #define REG_DRIFT_CLK_A_REG 0x004D #define REG_CLK_B_STATUS 0x004E #define REG_CLK_B_RATE 0x004F #define REG_DRIFT_CLK_B_REG 0x0052 #define REG_HDCP_CTRL 0x0060 #define REG_HDCP_KDS 0x0061 #define REG_HDCP_BCAPS 0x0063 #define REG_HDCP_KEY_CTRL 0x0064 #define REG_INFO_CTRL 0x0076 #define REG_INFO_EXCEED 0x0077 #define REG_PIX_REPEAT 0x007B #define REG_AUDIO_PATH 0x007C #define REG_AUDIO_SEL 0x007D #define REG_AUDIO_OUT_ENABLE 0x007E #define REG_AUDIO_OUT_HIZ 0x007F #define REG_VDP_CTRL 0x0080 #define REG_VHREF_CTRL 0x00A0 #define REG_PXCNT_PR 0x00A2 #define REG_PXCNT_NPIX 0x00A4 #define REG_LCNT_PR 0x00A6 #define REG_LCNT_NLIN 0x00A8 #define REG_HREF_S 0x00AA #define REG_HREF_E 0x00AC #define REG_HS_S 0x00AE #define REG_HS_E 0x00B0 #define REG_VREF_F1_S 0x00B2 #define REG_VREF_F1_WIDTH 0x00B4 #define REG_VREF_F2_S 0x00B5 #define REG_VREF_F2_WIDTH 0x00B7 #define REG_VS_F1_LINE_S 0x00B8 #define REG_VS_F1_LINE_WIDTH 0x00BA #define REG_VS_F2_LINE_S 0x00BB #define REG_VS_F2_LINE_WIDTH 0x00BD #define REG_VS_F1_PIX_S 0x00BE #define REG_VS_F1_PIX_E 0x00C0 #define REG_VS_F2_PIX_S 0x00C2 #define REG_VS_F2_PIX_E 0x00C4 #define REG_FREF_F1_S 0x00C6 #define REG_FREF_F2_S 0x00C8 #define REG_FDW_S 0x00ca #define REG_FDW_E 0x00cc #define REG_BLK_GY 0x00da #define REG_BLK_BU 0x00dc #define REG_BLK_RV 0x00de #define REG_FILTERS_CTRL 0x00e0 #define REG_DITHERING_CTRL 0x00E9 #define REG_OF_CTRL 0x00EA #define REG_CLKOUT_CTRL 0x00EB #define REG_HS_HREF_SEL 0x00EC #define REG_VS_VREF_SEL 0x00ED #define REG_DE_FREF_SEL 0x00EE #define REG_VP35_32_CTRL 0x00EF #define REG_VP31_28_CTRL 0x00F0 #define REG_VP27_24_CTRL 0x00F1 #define REG_VP23_20_CTRL 0x00F2 #define REG_VP19_16_CTRL 0x00F3 #define REG_VP15_12_CTRL 0x00F4 #define REG_VP11_08_CTRL 0x00F5 #define REG_VP07_04_CTRL 0x00F6 #define REG_VP03_00_CTRL 0x00F7 #define REG_CURPAGE_00H 0xFF #define MASK_VPER 0x3fffff #define MASK_HPER 0x0fff #define MASK_HSWIDTH 0x03ff /* Page 0x01 */ #define REG_HDMI_FLAGS 0x0100 #define REG_DEEP_COLOR_MODE 0x0101 #define REG_AUDIO_FLAGS 0x0108 #define REG_AUDIO_FREQ 0x0109 #define REG_ACP_PACKET_TYPE 0x0141 #define REG_ISRC1_PACKET_TYPE 0x0161 #define REG_ISRC2_PACKET_TYPE 0x0181 #define REG_GBD_PACKET_TYPE 0x01a1 #define ISRC_PACKET_HDR_LEN 3 #define ISRC_PACKET_DAT_LEN 16 #define GDB_PACKET_HDR_LEN 3 #define GDB_PACKET_DAT_LEN 28 #define ACP_PACKET_HDR_LEN 3 #define ACP_PACKET_DAT_LEN 16 #define MASK_AUDIO_DST_RATE 0x80 #define MASK_AUDIO_FREQ 0x07 #define MASK_DC_PIXEL_PHASE 0xf0 #define MASK_DC_COLOR_DEPTH 0x0f /* Page 0x12 */ #define REG_CLK_CFG 0x1200 #define REG_CLK_OUT_CFG 0x1201 #define REG_CFG1 0x1202 #define REG_CFG2 0x1203 #define REG_WDL_CFG 0x1210 #define REG_DELOCK_DELAY 0x1212 #define REG_PON_OVR_EN 0x12A0 #define REG_PON_CBIAS 0x12A1 #define REG_PON_RESCAL 0x12A2 #define REG_PON_RES 0x12A3 #define REG_PON_CLK 0x12A4 #define REG_PON_PLL 0x12A5 #define REG_PON_EQ 0x12A6 #define REG_PON_DES 0x12A7 #define REG_PON_OUT 0x12A8 #define REG_PON_MUX 0x12A9 #define REG_MODE_RECOVER_CFG1 0x12F8 #define REG_MODE_RECOVER_CFG2 0x12F9 #define REG_MODE_RECOVER_STS 0x12FA #define REG_AUDIO_LAYOUT 0x12D0 /* Page 0x13 */ #define REG_DEEP_COLOR_CTRL 0x1300 #define REG_CGU_DEBUG_SEL 0x1305 #define REG_HDCP_DDC_ADDR 0x1310 #define REG_HDCP_KIDX 0x1316 #define REG_DEEP_PLL7 0x1347 #define REG_HDCP_DE_CTRL 0x1370 #define REG_HDCP_EP_FILT_CTRL 0x1371 #define REG_HDMI_CTRL 0x1377 #define REG_HMTP_CTRL 0x137a #define REG_TIMER_D 0x13CF #define REG_SUS_SET_RGB0 0x13E1 #define REG_SUS_SET_RGB1 0x13E2 #define REG_SUS_SET_RGB2 0x13E3 #define REG_SUS_SET_RGB3 0x13E4 #define REG_SUS_SET_RGB4 0x13E5 #define REG_MAN_SUS_HDMI_SEL 0x13E8 #define REG_MAN_HDMI_SET 0x13E9 #define REG_SUS_CLOCK_GOOD 0x13EF /* CGU_DEBUG_SEL bits */ #define CGU_DEBUG_CFG_CLK_MASK 0x18 #define CGU_DEBUG_XO_FRO_SEL (1<<2) #define CGU_DEBUG_VDP_CLK_SEL (1<<1) #define CGU_DEBUG_PIX_CLK_SEL (1<<0) /* REG_MAN_SUS_HDMI_SEL / REG_MAN_HDMI_SET bits */ #define MAN_DIS_OUT_BUF (1<<7) #define MAN_DIS_ANA_PATH (1<<6) #define MAN_DIS_HDCP (1<<5) #define MAN_DIS_TMDS_ENC (1<<4) #define MAN_DIS_TMDS_FLOW (1<<3) #define MAN_RST_HDCP (1<<2) #define MAN_RST_TMDS_ENC (1<<1) #define MAN_RST_TMDS_FLOW (1<<0) /* Page 0x14 */ #define REG_FIFO_LATENCY_VAL 0x1403 #define REG_AUDIO_CLOCK_MODE 0x1411 #define REG_TEST_NCTS_CTRL 0x1415 #define REG_TEST_AUDIO_FREQ 0x1426 #define REG_TEST_MODE 0x1437 /* Page 0x20 */ #define REG_EDID_IN_BYTE0 0x2000 /* EDID base */ #define REG_EDID_IN_VERSION 0x2080 #define REG_EDID_ENABLE 0x2081 #define REG_HPD_POWER 0x2084 #define REG_HPD_AUTO_CTRL 0x2085 #define REG_HPD_DURATION 0x2086 #define REG_RX_HPD_HEAC 0x2087 /* Page 0x21 */ #define REG_EDID_IN_BYTE128 0x2100 /* CEA Extension block */ #define REG_EDID_IN_SPA_SUB 0x2180 #define REG_EDID_IN_SPA_AB_A 0x2181 #define REG_EDID_IN_SPA_CD_A 0x2182 #define REG_EDID_IN_CKSUM_A 0x2183 #define REG_EDID_IN_SPA_AB_B 0x2184 #define REG_EDID_IN_SPA_CD_B 0x2185 #define REG_EDID_IN_CKSUM_B 0x2186 /* Page 0x30 */ #define REG_RT_AUTO_CTRL 0x3000 #define REG_EQ_MAN_CTRL0 0x3001 #define REG_EQ_MAN_CTRL1 0x3002 #define REG_OUTPUT_CFG 0x3003 #define REG_MUTE_CTRL 0x3004 #define REG_SLAVE_ADDR 0x3005 #define REG_CMTP_REG6 0x3006 #define REG_CMTP_REG7 0x3007 #define REG_CMTP_REG8 0x3008 #define REG_CMTP_REG9 0x3009 #define REG_CMTP_REGA 0x300A #define REG_CMTP_REGB 0x300B #define REG_CMTP_REGC 0x300C #define REG_CMTP_REGD 0x300D #define REG_CMTP_REGE 0x300E #define REG_CMTP_REGF 0x300F #define REG_CMTP_REG10 0x3010 #define REG_CMTP_REG11 0x3011 /* CEC */ #define REG_PWR_CONTROL 0x80F4 #define REG_OSC_DIVIDER 0x80F5 #define REG_EN_OSC_PERIOD_LSB 0x80F8 #define REG_CONTROL 0x80FF /* global interrupt flags (INT_FLG_CRL_TOP) */ #define INTERRUPT_AFE (1<<7) /* AFE module */ #define INTERRUPT_HDCP (1<<6) /* HDCP module */ #define INTERRUPT_AUDIO (1<<5) /* Audio module */ #define INTERRUPT_INFO (1<<4) /* Infoframe module */ #define INTERRUPT_MODE (1<<3) /* HDMI mode module */ #define INTERRUPT_RATE (1<<2) /* rate module */ #define INTERRUPT_DDC (1<<1) /* DDC module */ #define INTERRUPT_SUS (1<<0) /* SUS module */ /* INT_FLG_CLR_HDCP bits */ #define MASK_HDCP_MTP (1<<7) /* HDCP MTP busy */ #define MASK_HDCP_DLMTP (1<<4) /* HDCP end download MTP to SRAM */ #define MASK_HDCP_DLRAM (1<<3) /* HDCP end download keys from SRAM */ #define MASK_HDCP_ENC (1<<2) /* HDCP ENC */ #define MASK_STATE_C5 (1<<1) /* HDCP State C5 reached */ #define MASK_AKSV (1<<0) /* AKSV received (start of auth) */ /* INT_FLG_CLR_RATE bits */ #define MASK_RATE_B_DRIFT (1<<7) /* Rate measurement drifted */ #define MASK_RATE_B_ST (1<<6) /* Rate measurement stability change */ #define MASK_RATE_B_ACT (1<<5) /* Rate measurement activity change */ #define MASK_RATE_B_PST (1<<4) /* Rate measreument presence change */ #define MASK_RATE_A_DRIFT (1<<3) /* Rate measurement drifted */ #define MASK_RATE_A_ST (1<<2) /* Rate measurement stability change */ #define MASK_RATE_A_ACT (1<<1) /* Rate measurement presence change */ #define MASK_RATE_A_PST (1<<0) /* Rate measreument presence change */ /* INT_FLG_CLR_SUS (Start Up Sequencer) bits */ #define MASK_MPT_BIT (1<<7) /* Config MTP end of process */ #define MASK_FMT_BIT (1<<5) /* Video format changed */ #define MASK_RT_PULSE_BIT (1<<4) /* End of termination resistance pulse */ #define MASK_SUS_END_BIT (1<<3) /* SUS last state reached */ #define MASK_SUS_ACT_BIT (1<<2) /* Activity of selected input changed */ #define MASK_SUS_CH_BIT (1<<1) /* Selected input changed */ #define MASK_SUS_ST_BIT (1<<0) /* SUS state changed */ /* INT_FLG_CLR_DDC bits */ #define MASK_EDID_MTP (1<<7) /* EDID MTP end of process */ #define MASK_DDC_ERR (1<<6) /* master DDC error */ #define MASK_DDC_CMD_DONE (1<<5) /* master DDC cmd send correct */ #define MASK_READ_DONE (1<<4) /* End of down EDID read */ #define MASK_RX_DDC_SW (1<<3) /* Output DDC switching finished */ #define MASK_HDCP_DDC_SW (1<<2) /* HDCP DDC switching finished */ #define MASK_HDP_PULSE_END (1<<1) /* End of Hot Plug Detect pulse */ #define MASK_DET_5V (1<<0) /* Detection of +5V */ /* INT_FLG_CLR_MODE bits */ #define MASK_HDMI_FLG (1<<7) /* HDMI mode, avmute, encrypt-on, FIFO fail */ #define MASK_GAMUT (1<<6) /* Gamut packet */ #define MASK_ISRC2 (1<<5) /* ISRC2 packet */ #define MASK_ISRC1 (1<<4) /* ISRC1 packet */ #define MASK_ACP (1<<3) /* Audio Content Protection packet */ #define MASK_DC_NO_GCP (1<<2) /* GCP not recieved in 5 frames */ #define MASK_DC_PHASE (1<<1) /* deep color mode pixel phase needs update */ #define MASK_DC_MODE (1<<0) /* deep color mode color depth changed */ /* INT_FLG_CLR_INFO bits */ #define MASK_MPS_IF (1<<6) /* MPEG Source Product IF change */ #define MASK_AUD_IF (1<<5) /* Audio IF change */ #define MASK_SPD_IF (1<<4) /* Source Product Descriptor IF change */ #define MASK_AVI_IF (1<<3) /* Auxiliary Video information IF change */ #define MASK_VS_IF_OTHER_BK2 (1<<2) /* Vendor Specific IF (bank2) change */ #define MASK_VS_IF_OTHER_BK1 (1<<1) /* Vendor Specific IF (bank1) change */ #define MASK_VS_IF_HDMI (1<<0) /* Vendor Specific IF (with HDMI LLC reg code) change */ /* INT_FLG_CLR_AUDIO bits */ #define MASK_AUDIO_FREQ_FLG (1<<5) /* Audio freq change */ #define MASK_AUDIO_FLG (1<<4) /* DST, OBA, HBR, ASP change */ #define MASK_MUTE_FLG (1<<3) /* Audio Mute */ #define MASK_CH_STATE (1<<2) /* Channel status */ #define MASK_UNMUTE_FIFO (1<<1) /* Audio Unmute */ #define MASK_ERROR_FIFO_PT (1<<0) /* Audio FIFO pointer error */ /* INT_FLG_CLR_AFE bits */ #define MASK_AFE_WDL_UNLOCKED (1<<7) /* Wordlocker was unlocked */ #define MASK_AFE_GAIN_DONE (1<<6) /* Gain calibration done */ #define MASK_AFE_OFFSET_DONE (1<<5) /* Offset calibration done */ #define MASK_AFE_ACTIVITY_DET (1<<4) /* Activity detected on data */ #define MASK_AFE_PLL_LOCK (1<<3) /* TMDS PLL is locked */ #define MASK_AFE_TRMCAL_DONE (1<<2) /* Termination calibration done */ #define MASK_AFE_ASU_STATE (1<<1) /* ASU state is reached */ #define MASK_AFE_ASU_READY (1<<0) /* AFE calibration done: TMDS ready */ /* OF_CTRL bits */ #define VP_OUT (1<<7) /* enable VP[35:0], HS, VS, DE, V_CLK */ #define VP_HIZ (1<<6) /* unused VP pins Hi-Z */ #define VP_BLK (1<<4) /* Insert blanking code in data */ #define VP_TRC (1<<3) /* Insert timing code (SAV/EAV) in data*/ #define VP_FORMAT_SEL_MASK 0x7 /* format selection */ /* HDMI_SOFT_RST bits */ #define RESET_DC (1<<7) /* Reset deep color module */ #define RESET_HDCP (1<<6) /* Reset HDCP module */ #define RESET_KSV (1<<5) /* Reset KSV-FIFO */ #define RESET_SCFG (1<<4) /* Reset HDCP and repeater function */ #define RESET_HCFG (1<<3) /* Reset HDCP DDC part */ #define RESET_PA (1<<2) /* Reset polarity adjust */ #define RESET_EP (1<<1) /* Reset Error protection */ #define RESET_TMDS (1<<0) /* Reset TMDS (calib, encoding, flow) */ /* HDMI_INFO_RST bits */ #define NACK_HDCP (1<<7) /* No ACK on HDCP request */ #define RESET_FIFO (1<<4) /* Reset Audio FIFO control */ #define RESET_GAMUT (1<<3) /* Clear Gamut packet */ #define RESET_AI (1<<2) /* Clear ACP and ISRC packets */ #define RESET_IF (1<<1) /* Clear all Audio infoframe packets */ #define RESET_AUDIO (1<<0) /* Reset Audio FIFO control */ /* HDCP_BCAPS bits */ #define HDCP_HDMI (1<<7) /* HDCP suports HDMI (vs DVI only) */ #define HDCP_REPEATER (1<<6) /* HDCP supports repeater function */ #define HDCP_READY (1<<5) /* set by repeater function */ #define HDCP_FAST (1<<4) /* Up to 400kHz */ #define HDCP_11 (1<<1) /* HDCP 1.1 supported */ #define HDCP_FAST_REAUTH (1<<0) /* fast reauthentication suported */ /* masks for interrupt status registers */ #define MASK_SUS_STATE_VALUE 0x1F #define LAST_STATE_REACHED 0x1B #define MASK_CLK_STABLE 0x04 #define MASK_CLK_ACTIVE 0x02 #define MASK_SUS_STATE_BIT 0x10 #define MASK_SR_FIFO_FIFO_CTRL 0x30 #define MASK_AUDIO_FLAG 0x10 /* Power Control */ #define MASK_OF_CTRL_OUT_HIZ 0x80 #define MASK_AUDIO_PLL_PD 0x80 #define DC_PLL_PD 0x01 #define DC_PLL_PON 0x00 #define MASK_XTAL_OSC_PD 0x02 #define MASK_TMDS_CLK_DIS 0x08 #define CBIAS_PON 0x01 #define CBIAS_POFF 0x00 #define TMDS_AUTO_PON 0x00 #define TMDS_MAN_PON 0x01 #define MASK_LOW_PW_EDID 0x01 /* Rate measurement */ #define RATE_REFTIM_ENABLE 0x01 #define CLK_MIN_RATE 0x0057e4 #define CLK_MAX_RATE 0x0395f8 #define WDL_CFG_VAL 0x82 #define DC_FILTER_VAL 0x31 /* Infoframe */ #define VS_HDMI_IF_UPDATE 0x0200 #define VS_HDMI_IF_TYPE 0x0201 #define VS_BK1_IF_UPDATE 0x0220 #define VS_BK1_IF_TYPE 0x0221 #define VS_BK2_IF_UPDATE 0x0240 #define VS_BK2_IF_TYPE 0x0241 #define AVI_IF_UPDATE 0x0260 #define AVI_IF_TYPE 0x0261 #define AVI_IF_NB_DATA 17 #define SPD_IF_UPDATE 0x0280 #define SPD_IF_TYPE 0x0281 #define SPD_IF_NB_DATA 31 #define AUD_IF_UPDATE 0x02a0 #define AUD_IF_TYPE 0x02a1 #define AUD_IF_NB_DATA 14 #define MPS_IF_UPDATE 0x02c0 #define MPS_IF_TYPE 0x02c1 #define MPS_IF_NB_DATA 14 #define MAX_IF_DATA 40 #define VS_IF_NB 31 /* Input Selection */ #define MASK_DIG_INPUT 0x01 #define MASK_DIG_INPUT_VDPR_FMT 0x85 #define MASK_HDMIOUTMODE 0x02 #define FORMAT_RESET 0x80 /* Colorspace Conversion Registers */ #define MAT_OFFSET_NB 3 #define MAT_COEFF_NB 9 #define OFFSET_LOOP_NB 2 #define MIN_VAL_OFFSET -4096 #define MAX_VAL_OFFSET 4095 #define MIN_VAL_COEFF -16384 #define MAX_VAL_COEFF 16383 #define MASK_MAT_COEFF_LSB 0x00FF /* Blanking code values depend on output colorspace (RGB or YUV) */ typedef struct { s16 blankingCodeGy; s16 blankingCodeBu; s16 blankingCodeRv; } blankingcodes_t; blankingcodes_t RGBBlankingCode = {64, 64, 64}; blankingcodes_t YUVBlankingCode = {64, 512, 512}; /* Video Colorspace formats */ typedef enum { COLORSPACE_RGB, COLORSPACE_YCBCR_422, COLORSPACE_YCBCR_444, COLORSPACE_FUTURE, } tda1997x_colorspace_t; /* Video Colorimetry formats */ typedef enum { COLORIMETRY_NONE, COLORIMETRY_ITU601, COLORIMETRY_ITU709, COLORIMETRY_XVYCC, } tda1997x_colorimetry_t; /* Video colormode formats */ typedef enum { DEEPCOLORMODE_NOT_INDICATED = 0x00, DEEPCOLORMODE_24 = 0x04, DEEPCOLORMODE_30 = 0x05, DEEPCOLORMODE_36 = 0x06, DEEPCOLORMODE_48 = 0x07, } tda1997x_deepcolor_t; /* resolution type */ typedef enum { RESTYPE_SDTV, RESTYPE_HDTV, RESTYPE_PC, } tda1997x_restype_t; /* Video output port format */ const char *vidfmt_names[] = { "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */ "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */ "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */ }; static char *colorspace_names[] = { "RGB", "YUV422", "YUV444", "Future" }; static char *colorimetry_names[] = { "", "ITU601", "ITU709", "XVYCC" }; /* HDCP */ #define RX_SEED_TABLE_LEN 10 /* HDCP Seed */ typedef enum { HDCP_DECRYPTKEY_OFF = 0x00, HDCP_DECRYPTKEY_ON = 0x02 } hdcp_key_t; typedef enum { DISABLE = 0x00, ENABLE = 0x01 } enable_t; /* MTP */ typedef enum { MTP_START_DOWNLOAD, MTP_START_READ, } mtp_command_t; /* HPD modes */ typedef enum { HPD_LOW, /* HPD low and pulse of at least 100ms */ HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */ HPD_HIGH, /* HIGH */ HPD_HIGH_OTHER, HPD_PULSE, /* HPD low pulse */ } hpdmode_t; /** configure colorspace conversion matrix * The color conversion matrix will convert between the colorimetry of the * HDMI input to the desired output format RGB|YUV */ typedef enum { ITU709_RGBLimited, RGBLimited_ITU601, ITU601_RGBLimited, } colorconversion_t; /* Colorspace conversion matrix coefficients and offsets */ typedef struct { /* Input offsets */ s16 offInt1; s16 offInt2; s16 offInt3; /* Coeficients */ s16 P11Coef; s16 P12Coef; s16 P13Coef; s16 P21Coef; s16 P22Coef; s16 P23Coef; s16 P31Coef; s16 P32Coef; s16 P33Coef; /* Output offsets */ s16 offOut1; s16 offOut2; s16 offOut3; } colormatrixcoefs_t; /* Conversion matrixes */ colormatrixcoefs_t conversion_matrix[] = { /* ITU709 -> RGBLimited */ { -256, -2048, -2048, /*Input Offset*/ 4096, -1875, -750, 4096, 6307, 0, 4096, 0, 7431, 256, 256, 256 /*Output Offset*/ }, /* RGBLimited -> ITU601 */ { -256, -256, -256, /*Input Offset*/ 2404, 1225, 467, -1754, 2095, -341, -1388, -707, 2095, /*RGB limited range => ITU-601 YUV limited range */ 256, 2048, 2048 /*Output Offset*/ }, /* YUV601 -> RGBLimited */ { -256, -2048, -2048, /*Input Offset*/ 4096, -2860, -1378, 4096, 5615, 0, 4096, 0, 7097, /*ITU-601 YUV limited range => RGB limited range */ 256, 256, 256 /*Output Offset*/ } }; /* HDCP seed table, arranged as pairs of 16bit integrers: lookup val, seed val * If no table is programmed or KEY_SED in config file is null, HDCP will be * disabled */ typedef struct { u16 lookUpVal; u16 seedVal; } hdmi_cfg_seed_t; const hdmi_cfg_seed_t rx_seed_table[RX_SEED_TABLE_LEN] = { {0xF0, 0x1234}, {0xF1, 0xDBE6}, {0xF2, 0xDBE6}, {0, 0x1234}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; /** Video Input mode database * TODO: can I use something like modedb instead? * More recent kernels have some CEA data */ static char *restype_names[] = { "SDTV", "HDTV", "PC", }; typedef enum { VIDEORES_1280_720p_24HZ, VIDEORES_1280_720p_25HZ, VIDEORES_1280_720p_30HZ, VIDEORES_1920_1080p_24HZ, VIDEORES_1920_1080p_25HZ, VIDEORES_1920_1080p_30HZ, VIDEORES_720_480p_60HZ, VIDEORES_1280_720p_60HZ, VIDEORES_1920_1080i_60HZ, VIDEORES_720_480i_60HZ, VIDEORES_1920_1080p_60HZ, VIDEORES_720_576p_50HZ, VIDEORES_1280_720p_50HZ, VIDEORES_1920_1080i_50HZ, VIDEORES_720_576i_50HZ, VIDEORES_1920_1080p_50HZ, VIDEORES_640_480p_60HZ, /* VGA */ VIDEORES_800_600p_60HZ, /* SVGA */ VIDEORES_1024_768p_60HZ, /* XGA */ VIDEORES_1280_768p_60HZ, /* WXGA */ VIDEORES_1280_960p_60HZ, /* ???? */ VIDEORES_1280_1024p_60HZ, /* SXGA */ VIDEORES_1440_900p_60HZ, /* ???? */ VIDEORES_1600_1200p_60HZ, /* UGA */ VIDEORES_1680_1050p_60HZ_RB, /* WSXGA */ VIDEORES_1920_1200p_60HZ_RB, /* WUXGA */ VIDEORES_640_480p_75HZ, /* VGA */ VIDEORES_800_600p_75HZ, /* SVGA */ VIDEORES_1024_768p_75HZ, /* XGA */ VIDEORES_1280_768p_75HZ, /* WXGA */ VIDEORES_1280_1024p_75HZ, /* SXGA */ VIDEORES_640_480p_85HZ, /* VGA */ VIDEORES_800_600p_85HZ, /* SVGA */ VIDEORES_1024_768p_85HZ, /* XGA */ VIDEORES_1280_768p_85HZ, /* WXGA */ VIDEORES_1280_1024p_85HZ, /* SXGA */ VIDEORES_720_240p_60HZ_M1, /* 720(1440, 2880)x240p 60Hz mode 1 */ VIDEORES_720_240p_60HZ_M2, /* 720(1440, 2880)x240p 60Hz mode 2 */ VIDEORES_720_288p_50HZ_M1, /* 720(1440)x288p 50Hz mode 1 */ VIDEORES_720_288p_50HZ_M2, /* 720(1440)x288p 50Hz mode 1 */ VIDEORES_720_288p_50HZ_M3, /* 720(1440)x288p 50Hz mode 1 */ VIDEORES_1360_768p_60HZ, /* 1360x768p 60Hz (PC resolution) */ VIDEORES_1400_1050p_60HZ, /* 1400x1050p 60Hz (PC resolution) */ VIDEORES_1400_1050p_60HZ_RB, /* 1400x1050p 60Hz Reduced Blanking (PC) */ VIDEORES_1024_768p_70HZ, /* XGA */ VIDEORES_640_480p_72HZ, /* VGA */ VIDEORES_800_600p_72HZ, /* SVGA */ VIDEORES_640_350p_85HZ, /* 640x350p 85Hz (PC) */ VIDEORES_640_400p_85HZ, /* 640x400p 85Hz (PC) */ VIDEORES_720_400p_85HZ, /* 720x400p 85Hz (PC) */ VIDEORES_UNKNOWN } resolutionid_t; /* structure for video format measurements */ typedef struct { u8 videoFormat; /* 1=interlaced or 0=progressive */ u8 vsPolarity; /* 1=negative 0=positive */ u8 hsPolarity; /* 1=negative 0=positive */ u16 horizontalTotalPeriod; /* period of 1 line (pixel clocks) */ u16 horizontalVideoActiveWidth; /* period of 1 active line (pixel clocks) */ u16 horizontalFrontPorchWidth; /* width of front porch */ u16 horizontalBackPorchWidth; /* width of back porch */ u16 horizontalSyncWidthPixClk; u16 verticalTotalPeriod; /* period of a frame in line numbers */ u16 verticalVideoActiveWidth; u16 verticalFrontPorchWidthF1; /* vertical front porch width of field 1 */ u16 verticalFrontPorchWidthF2; /* vertical front porch width of field 2*/ u16 verticalSyncWidth; /* width of the VS in line numbers */ u16 verticalBackPorchWidthF1; /* vertical back porch width of field 1 */ u16 verticalBackPorchWidthF2; /* vertical back porch width of field 2 */ u16 dataEnablePresent; /* 1=DE signal present */ } videoFormatDetails; typedef struct { u8 resolutionID; u16 width; u16 height; u8 horizfreq; u8 interlaced; u32 verticalPeriodMin; /* = MCLK(27MHz) / VFreq minus 0.7% */ u32 verticalPeriodMax; /* same + 0.7% */ u16 horizontalPeriodMin; /* = MCLK(27MHz) / HFreq minus 1% */ u16 horizontalPeriodMax; u16 hsWidthMin; /* = MCLK(27MHz) / pixclk * hWidth minux ...% */ u16 hsWidthMax; } resolution_data_t; typedef struct { u16 href_start; u16 href_end; u16 vref_f1_start; u8 vref_f1_width; u16 vref_f2_start; u8 vref_f2_width; u16 fieldref_f1_start; u8 fieldPolarity; u16 fieldref_f2_start; } vhref_values_t; typedef struct { u8 resolutionID; u16 pixCountPreset; u16 pixCountNb; u16 lineCountPreset; u16 lineCountNb; vhref_values_t vhref_values; } resolution_timings_t; const resolution_timings_t resolution_timings[] = { /* Low TV */ {VIDEORES_1280_720p_24HZ, 1, 3300, 1, 750, {261, 1541, 745, 30, 0, 0, 1, 0, 0} }, {VIDEORES_1280_720p_25HZ, 1, 3960, 1, 750, {261, 1541, 745, 30, 0, 0, 1, 0, 0} }, {VIDEORES_1280_720p_30HZ, 1, 3300, 1, 750, {261, 1541, 745, 30, 0, 0, 1, 0, 0} }, {VIDEORES_1920_1080p_24HZ, 1, 2750, 1, 1125, {193, 2113, 1121, 45, 0, 0, 1, 0, 0} }, {VIDEORES_1920_1080p_25HZ, 1, 2640, 1, 1125, {193, 2113, 1121, 45, 0, 0, 1, 0, 0} }, {VIDEORES_1920_1080p_30HZ, 1, 2200, 1, 1125, {193, 2113, 1121, 45, 0, 0, 1, 0, 0} }, /* 60 Hz TV */ {VIDEORES_720_480p_60HZ, 1, 858, 1, 525, {123, 843, 516, 45, 0, 0, 1, 0, 0} }, {VIDEORES_1280_720p_60HZ, 1, 1650, 1, 750, {261, 1541, 745, 30, 0, 0, 1, 0, 0} }, {VIDEORES_1920_1080i_60HZ, 1, 2200, 1, 1125, {193, 2113, 1123, 22, 560, 23, 1, 0, 563} }, {VIDEORES_720_480i_60HZ, 1, 858, 1, 525, {120, 840, 521, 22, 258, 23, 1, 0, 263} }, {VIDEORES_1920_1080p_60HZ, 1, 2200, 1, 1125, {193, 2113, 1121, 45, 0, 0, 1, 0, 0} }, /* 50 Hz TV */ {VIDEORES_720_576p_50HZ, 1, 864, 1, 625, {133, 853, 620, 49, 0, 0, 1, 0, 0} }, {VIDEORES_1280_720p_50HZ, 1, 1980, 1, 750, {261, 1541, 745, 30, 0, 0, 1, 0, 0} }, {VIDEORES_1920_1080i_50HZ, 1, 2640, 1, 1125, {193, 2113, 1123, 22, 560, 23, 1, 0, 563} }, {VIDEORES_720_576i_50HZ, 1, 864, 1, 625, {133, 853, 623, 24, 310, 25, 1, 0, 313 } }, {VIDEORES_1920_1080p_50HZ, 1, 2640, 1, 1125, {193, 2113, 1121, 45, 0, 0, 1, 0, 0} }, /* 60 Hz PC */ {VIDEORES_640_480p_60HZ, 1, 800, 1, 525, {145, 785, 515, 45, 0, 0, 1, 0, 0} }, {VIDEORES_800_600p_60HZ, 1, 1056, 1, 628, {217, 1017, 627, 28, 0, 0, 0, 0, 0} }, {VIDEORES_1024_768p_60HZ, 1, 1344, 1, 806, {297, 1321, 803, 38, 0, 0, 0, 0, 0} }, {VIDEORES_1280_768p_60HZ, 1, 1440, 1, 790, {321, 1601, 795, 30, 0, 0, 0, 0, 0} }, {VIDEORES_1280_960p_60HZ, 1, 1800, 1, 1000, {425, 1705, 999, 40, 0, 0, 0, 0, 0} }, {VIDEORES_1280_1024p_60HZ, 1, 1688, 1, 1066, {361, 1641, 1065, 42, 0, 0, 0, 0, 0} }, {VIDEORES_1440_900p_60HZ, 1, 1904, 1, 934, {385, 1825, 931, 34, 0, 0, 0, 0, 0} }, {VIDEORES_1600_1200p_60HZ, 1, 2160, 1, 1250, {497, 2097, 1249, 50, 0, 0, 0, 0, 0} }, {VIDEORES_1680_1050p_60HZ_RB, 1, 1840, 1, 1080, {113, 1793, 1077, 30, 0, 0, 0, 0, 0} }, {VIDEORES_1920_1200p_60HZ_RB, 1, 2080, 1, 1235, {113, 2033, 1232, 35, 0, 0, 0, 0, 0} }, /* 75 HZ PC */ {VIDEORES_640_480p_75HZ, 1, 840, 1, 500, {185, 825, 499, 20, 0, 0, 1, 0, 0} }, {VIDEORES_800_600p_75HZ, 1, 1056, 1, 625, {241, 1041, 624, 25, 0, 0, 0, 0, 0} }, {VIDEORES_1024_768p_75HZ, 1, 1312, 1, 800, {273, 1297, 799, 32, 0, 0, 0, 0, 0} }, {VIDEORES_1280_768p_75HZ, 1, 1696, 1, 805, {337, 1617, 802, 37, 0, 0, 0, 0, 0} }, {VIDEORES_1280_1024p_75HZ, 1, 1688, 1, 1066, {393, 1673, 1065, 42, 0, 0, 0, 0, 0} }, /* 85 HZ PC */ {VIDEORES_640_480p_85HZ, 1, 832, 1, 509, {137, 777, 508, 29, 0, 0, 1, 0, 0} }, {VIDEORES_800_600p_85HZ, 1, 1048, 1, 631, {217, 1017, 630, 31, 0, 0, 0, 0, 0} }, {VIDEORES_1024_768p_85HZ, 1, 1376, 1, 808, {305, 1329, 807, 40, 0, 0, 0, 0, 0} }, {VIDEORES_1280_768p_85HZ, 1, 1712, 1, 908, {353, 1633, 905, 140, 0, 0, 0, 0, 0} }, {VIDEORES_1280_1024p_85HZ, 1, 1728, 1, 1072, {385, 1665, 1071, 48, 0, 0, 0, 0, 0} }, /* Other resolutions */ {VIDEORES_720_240p_60HZ_M1, 1, 858, 1, 262, {120, 840, 258, 22, 0, 0, 0, 0, 0} }, {VIDEORES_720_240p_60HZ_M2, 1, 858, 1, 263, {120, 840, 258, 23, 0, 0, 0, 0, 0} }, {VIDEORES_720_288p_50HZ_M1, 1, 864, 1, 312, {133, 853, 310, 24, 0, 0, 0, 0, 0} }, {VIDEORES_720_288p_50HZ_M2, 1, 864, 1, 313, {133, 853, 310, 25, 0, 0, 0, 0, 0} }, {VIDEORES_720_288p_50HZ_M3, 1, 864, 1, 314, {133, 853, 310, 26, 0, 0, 0, 0, 0} }, {VIDEORES_1360_768p_60HZ, 1, 1792, 1, 795, {369, 1729, 792, 27, 0, 0, 0, 0, 0} }, {VIDEORES_1400_1050p_60HZ, 1, 1864, 1, 1089, {377, 1777, 1086, 39, 0, 0, 0, 0, 0} }, {VIDEORES_1400_1050p_60HZ_RB, 1, 1560, 1, 1080, {113, 1513, 1077, 30, 0, 0, 0, 0, 0} }, {VIDEORES_1024_768p_70HZ, 1, 1328, 1, 806, {281, 1305, 803, 38, 0, 0, 0, 0, 0} }, {VIDEORES_640_480p_72HZ, 1, 832, 1, 520, {169, 809, 511, 40, 0, 0, 0, 0, 0} }, {VIDEORES_800_600p_72HZ, 1, 1040, 1, 666, {185, 985, 629, 66, 0, 0, 0, 0, 0} }, {VIDEORES_640_350p_85HZ, 1, 832, 1, 445, {161, 801, 413, 95, 0, 0, 0, 0, 0} }, {VIDEORES_640_400p_85HZ, 1, 832, 1, 445, {161, 801, 444, 45, 0, 0, 0, 0, 0} }, {VIDEORES_720_400p_85HZ, 1, 936, 1, 446, {181, 901, 445, 46, 0, 0, 0, 0, 0} } }; const resolution_data_t supported_res[] = { /* Low TV */ {VIDEORES_1280_720p_24HZ, 1280,720,24,0, 1117178, 1134065, 1488, 1513, 17, 19}, {VIDEORES_1280_720p_25HZ, 1280,720,25,0, 1072491, 1087614, 1428, 1451, 13, 15}, {VIDEORES_1280_720p_30HZ, 1280,720,30,0, 893742, 907252, 1190, 1210, 13, 15}, {VIDEORES_1920_1080p_24HZ, 1920,1080,24,0, 1117178, 1134065, 992, 1009, 14, 17}, {VIDEORES_1920_1080p_25HZ, 1920,1080,25,0, 1072491, 1087614, 952, 967, 14, 17}, {VIDEORES_1920_1080p_30HZ, 1920,1080,30,0, 893742, 907252, 794, 806, 14, 17}, /* 60 Hz TV */ {VIDEORES_720_480p_60HZ, 720,480,60,0, 446870, 453626, 850, 865, 60, 63}, {VIDEORES_1280_720p_60HZ, 1280,720,60,0, 446870, 453626, 594, 605, 13, 15}, {VIDEORES_1920_1080i_60HZ, 1920,1080,60,1, 446870, 453626, 793, 807, 14, 17}, {VIDEORES_720_480i_60HZ, 720,480,60,1, 446870, 453626, 1701, 1729, 122, 125}, {VIDEORES_1920_1080p_60HZ, 1920,1080,60,0, 446870, 453626, 396, 404, 6, 9}, /* 50 Hz TV */ {VIDEORES_720_576p_50HZ, 720,576,50,0, 536245, 543807, 856, 871, 62, 65}, {VIDEORES_1280_720p_50HZ, 1280,720,50,0, 536245, 543807, 713, 726, 13, 15}, {VIDEORES_1920_1080i_50HZ, 1920,1080,50,1, 536245, 543807, 952, 967, 14, 17}, {VIDEORES_720_576i_50HZ, 720,576,50,1, 536245, 543807, 1714, 1741, 124, 127}, {VIDEORES_1920_1080p_50HZ, 1920,1080,50,0, 536245, 543807, 475, 484, 6, 9}, /* 60 HZ PC */ {VIDEORES_640_480p_60HZ, 640,480,60,0, 446870, 453626, 850, 865, 101, 104}, {VIDEORES_800_600p_60HZ, 800,600,60,0, 444523, 450791, 708, 718, 84, 88}, {VIDEORES_1024_768p_60HZ, 1024,768,60,0, 446842, 453142, 554, 562, 54, 58}, {VIDEORES_1280_768p_60HZ, 1280,768,60,0, 447842, 454156, 561, 569, 41, 46}, {VIDEORES_1280_960p_60HZ, 1280,960,60,0, 446872, 453172, 447, 453, 26, 30}, {VIDEORES_1280_1024p_60HZ, 1280,1024,60,0, 446723, 453021, 419, 425, 26, 30}, {VIDEORES_1440_900p_60HZ, 1440,900,60,0, 446723, 453021, 478, 486, 35, 40}, {VIDEORES_1600_1200p_60HZ, 1600,1200,60,0, 446872, 453172, 357, 363, 30, 34}, {VIDEORES_1680_1050p_60HZ_RB, 1680,1050,60,0, 447745, 454058, 415, 420, 5, 9}, {VIDEORES_1920_1200p_60HZ_RB, 1920,1200,60,0, 447235, 453550, 362, 367, 4, 8}, /* 75 HZ PC */ {VIDEORES_640_480p_75HZ, 640,480,75,0, 357498, 362538, 715, 725, 53, 57}, {VIDEORES_800_600p_75HZ, 800,600,75,0, 357498, 362538, 572, 580, 42, 46}, {VIDEORES_1024_768p_75HZ, 1024,768,75,0, 357359, 362398, 447, 453, 31, 35}, {VIDEORES_1280_768p_75HZ, 1280,768,75,0, 357480, 362520, 444, 450, 32, 36}, {VIDEORES_1280_1024p_75HZ, 1280,1024,75,0, 357378, 362417, 335, 340, 27, 31}, /* 85 HZ PC */ {VIDEORES_640_480p_85HZ, 640,480,85,0, 315409, 319856, 620, 628, 40, 44}, {VIDEORES_800_600p_85HZ, 800,600,85,0, 315213, 319657, 500, 507, 29, 33}, {VIDEORES_1024_768p_85HZ, 1024,768,85,0, 315450, 319898, 390, 396, 25, 29}, {VIDEORES_1280_768p_85HZ, 1280,768,85,0, 315423, 319871, 391, 396, 29, 33}, {VIDEORES_1280_1024p_85HZ, 1280,1024,85,0, 315350, 319796, 294, 298, 26, 30}, /* Other resolutions */ {VIDEORES_720_240p_60HZ_M1, 720,240,60,0, 446017, 452305, 1702, 1726, 122, 126}, {VIDEORES_720_240p_60HZ_M2, 720,240,60,0, 447723, 454035, 1702, 1726, 122, 126}, {VIDEORES_720_288p_50HZ_M1, 720,288,50,0, 535390, 542938, 1716, 1740, 124, 128}, {VIDEORES_720_288p_50HZ_M2, 720,288,50,0, 537106, 544678, 1716, 1740, 124, 128}, {VIDEORES_720_288p_50HZ_M3, 720,288,50,0, 538822, 546419, 1716, 1740, 124, 128}, {VIDEORES_1360_768p_60HZ, 1360,768,60,0, 446760, 453059, 562, 570, 33, 37}, {VIDEORES_1400_1050p_60HZ, 1400,1050,60,0, 447036, 453338, 411, 416, 30, 34}, {VIDEORES_1400_1050p_60HZ_RB, 1400,1050,60,0, 447260, 453565, 414, 420, 7, 11}, {VIDEORES_1024_768p_70HZ, 1024,768,70,0, 382656, 388051, 475, 481, 47, 51}, {VIDEORES_640_480p_72HZ, 640,480,72,0, 368255, 373447, 708, 718, 32, 36}, {VIDEORES_800_600p_72HZ, 800,600,72,0, 371423, 376660, 558, 566, 63, 67}, {VIDEORES_640_350p_85HZ, 640,350,85,0, 315142, 319585, 708, 718, 53, 57}, {VIDEORES_640_400p_85HZ, 640,400,85,0, 315142, 319585, 708, 718, 53, 57}, {VIDEORES_720_400p_85HZ, 720,400,85,0, 315294, 319740, 707, 717, 53, 57} }; /* Platform Data */ struct tda1997x_platform_data { /* Misc */ char hdcp; /* enable HDCP */ char external_edid; /* use external EDID */ char ddc_slave; /* DDC i2c slave address */ /* Audio */ tda1997x_audiofmt_t audout_format; /* output data format */ tda1997x_audiosysclk_t audout_sysclk; /* clock config */ tda1997x_audiolayout_t audout_layout; /* physical bus layout */ bool audio_force_channel_assignment; /* use AUD IF info if unset */ bool audio_auto_mute; /* enable hardware audio auto-mute */ bool audout_invert_clk; /* data valid on rising edge of BCLK */ /* Video */ tda1997x_videofmt_t vidout_format; /* video output data format */ bool vidout_blc; /* insert blanking codes (SAV/EAV) */ bool vidout_trc; /* insert timing codes */ tda1997x_videoclkmode_t vidout_clkmode; /* clock mode */ /* pin polarity (1=invert) */ bool vidout_invert_de; bool vidout_invert_hs; bool vidout_invert_vs; /* clock delays (0=-8, 1=-7 ... 15=+7 pixels) */ char vidout_delay_hs; char vidout_delay_vs; char vidout_delay_de; char vidout_delay_clk; /* sync selections (controls how sync pins are derived) */ tda1997x_sync_output_hs_t vidout_sel_hs; tda1997x_sync_output_vs_t vidout_sel_vs; tda1997x_sync_output_de_t vidout_sel_de; /* Video port configs */ u8 vidout_port_config[9]; u8 vidout_port_config_no; /* Max pixel rate (MP/sec) */ int max_pixel_rate; }; /** * Maintains the information on the current state of the chip */ struct tda1997x_data { struct i2c_client *client; struct i2c_client *client_cec; char page; struct work_struct work; int irq; tda1997x_state_t state; tda1997x_input_t input; spinlock_t lock; struct tda1997x_platform_data *pdata; struct mutex page_lock; struct mutex cec_lock; /* detected info from chip */ int chip; int chip_version; int chip_revision; char eess_detected; char hdmi_detected; char hdcp_detected; char internal_edid; char port_30bit; char output_2p5; char tmdsb_clk; char tmdsb_soc; char cec_enabled; char cec_slave; /* status info */ char hdmi_status; char vsi_received; char mptrw_in_progress; char state_c5_reached; char activity_status_reg; char input_detect[2]; char vendor[12]; char product[18]; u16 key_decryption_seed; /* video source */ tda1997x_colorspace_t colorspace; tda1997x_colorimetry_t colorimetry; tda1997x_restype_t resolutiontype; /* video source and output format */ tda1997x_vidout_fmt_t video_mode; /* audio source */ u8 channel_assignment; int source_channels; /* audio output format */ tda1997x_audout_fmt_t audio_mode; }; #ifdef DEBUG /* kernel parameters */ static int debug = 0; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level"); #define DPRINTK(x, fmt, args...) { if (debug>=x) printk(KERN_DEBUG fmt, ## args); } /* HPD modes */ const char *hpd_names[] = { "HPD_LOW", "HPD_LOW_OTHER", "HPD_HIGH", "HPD_HIGH_OTHER", "HPD_PULSE", }; /* Audio output port layout (hardware pins) */ const char *audlayout_names[] = { "AUDIO_LAYOUT_AUTO", "AUDIO_LAYOUT_0", "AUDIO_LAYOUT_1", }; /* Audio format */ const char *audfmt_names[] = { "AUDIO_FMT_I2S16", "AUDIO_FMT_I2S32", "AUDIO_FMT_SPDIF", "AUDIO_FMT_OBA", "AUDIO_FMT_I2S16_HBR_STRAIGHT", "AUDIO_FMT_I2S16_HBR_DEMUX", "AUDIO_FMT_I2S32_HBR_DEMUX", "AUDIO_FMT_SPDIF_HBR_DEMUX", "AUDIO_FMT_DST", }; #else #define DPRINTK(x, fmt, args...) #endif static struct tda1997x_data tda1997x_data; static int tda1997x_manual_hpd(struct tda1997x_data *tda1997x, hpdmode_t mode); /** API for CEC */ int tda1997x_cec_read(u8 reg) { struct tda1997x_data *tda1997x = &tda1997x_data; int val; mutex_lock(&tda1997x->cec_lock); val = i2c_smbus_read_byte_data(tda1997x_data.client_cec, reg); if (val < 0) { dev_dbg(&tda1997x_data.client_cec->dev, "%s:read reg error: reg=%2x\n", __func__, reg); val = -1; } mutex_unlock(&tda1997x->cec_lock); return val; } EXPORT_SYMBOL(tda1997x_cec_read); int tda1997x_cec_write(u8 reg, u8 val) { struct tda1997x_data *tda1997x = &tda1997x_data; int ret = 0; mutex_unlock(&tda1997x->cec_lock); ret = i2c_smbus_write_byte_data(tda1997x_data.client_cec, reg, val); if (ret < 0) { dev_dbg(&tda1997x_data.client_cec->dev, "%s:write reg error:reg=%2x,val=%2x\n", __func__, reg, val); ret = -1; } mutex_unlock(&tda1997x->cec_lock); return ret; } EXPORT_SYMBOL(tda1997x_cec_write); /** API for MFD children */ /** get current state */ tda1997x_state_t tda1997x_get_state(void) { struct tda1997x_data *tda1997x = &tda1997x_data; tda1997x_state_t state; unsigned long flags; dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); spin_lock_irqsave(&tda1997x->lock, flags); state = tda1997x->state; spin_unlock_irqrestore(&tda1997x->lock, flags); return state; } EXPORT_SYMBOL(tda1997x_get_state); int tda1997x_get_vidout_fmt(tda1997x_vidout_fmt_t *fmt) { struct tda1997x_data *tda1997x = &tda1997x_data; unsigned long flags; dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); spin_lock_irqsave(&tda1997x->lock, flags); memcpy(fmt, &(tda1997x->video_mode), sizeof(*fmt)); fmt->sensor_vidfmt = tda1997x->pdata->vidout_format; fmt->sensor_clkmode = tda1997x->pdata->vidout_clkmode; spin_unlock_irqrestore(&tda1997x->lock, flags); return 0; } EXPORT_SYMBOL(tda1997x_get_vidout_fmt); int tda1997x_get_audout_fmt(tda1997x_audout_fmt_t *fmt) { struct tda1997x_data *tda1997x = &tda1997x_data; unsigned long flags; dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); spin_lock_irqsave(&tda1997x->lock, flags); memcpy(fmt, &(tda1997x->audio_mode), sizeof(*fmt)); spin_unlock_irqrestore(&tda1997x->lock, flags); return 0; } EXPORT_SYMBOL(tda1997x_get_audout_fmt); /*********************************************************************** * I2C transfer ***********************************************************************/ /** set the current page * * @param page number * @returns 0 if success, an error code otherwise. */ static int tda1997x_setpage(u8 page) { int ret; if (tda1997x_data.page != page) { ret = i2c_smbus_write_byte_data(tda1997x_data.client, REG_CURPAGE_00H, page); if (ret < 0) { dev_dbg(&tda1997x_data.client->dev, "%s:write reg error:reg=%2x,val=%2x\n", __func__, REG_CURPAGE_00H, page); return -1; } tda1997x_data.page = page; } return 0; } /** Read one register from a tda1997x i2c slave device. * * @param reg register in the device we wish to access. * high byte is page, low byte is reg * @returns 0 if success, an error code otherwise. */ static inline int io_read(u16 reg) { struct tda1997x_data *tda1997x = &tda1997x_data; int val; /* page 80 denotes CEC reg which needs to go out cec_client */ BUG_ON(reg>>8 == 0x80); mutex_lock(&tda1997x->page_lock); if (tda1997x_setpage(reg>>8)) { val = -1; goto out; } val = i2c_smbus_read_byte_data(tda1997x_data.client, reg&0xff); if (!(reg == REG_INT_FLG_CLR_TOP && val == 0x00)) { DPRINTK(3, "<< 0x%04x=0x%02x\n", reg, val); } if (val < 0) { dev_dbg(&tda1997x_data.client->dev, "%s:read reg error: reg=%2x\n", __func__, reg&0xff); val = -1; goto out; } out: mutex_unlock(&tda1997x->page_lock); return val; } /* 16bit read */ static inline long io_read16(u16 reg) { u8 val; long lval = 0; if ( (val = io_read(reg)) < 0) return -1; lval |= (val<<8); if ( (val = io_read(reg+1)) < 0) return -1; lval |= val; return lval; } /* 24bit read */ static inline long io_read24(u16 reg) { u8 val; long lval = 0; if ( (val = io_read(reg)) < 0) return -1; lval |= (val<<16); if ( (val = io_read(reg+1)) < 0) return -1; lval |= (val<<8); if ( (val = io_read(reg+2)) < 0) return -1; lval |= val; return lval; } /* n-byte read */ static unsigned int io_readn(u16 reg, u8 len, u8 *data) { int i; int sz = 0; u8 val; for (i = 0; i < len; i++) { if ( (val = io_read(reg + i)) < 0) break; data[i] = val; sz++; } return sz; } /** Write one register of a tda1997x i2c slave device. * * @param reg register in the device we wish to access. * high byte is page, low byte is reg * @returns 0 if success, an error code otherwise. */ static int io_write(u16 reg, u8 val) { struct tda1997x_data *tda1997x = &tda1997x_data; s32 ret = 0; /* page 80 denotes CEC reg which needs to go out cec_client */ BUG_ON(reg>>8 == 0x80); mutex_lock(&tda1997x->page_lock); if (tda1997x_setpage(reg>>8)) { ret = -1; goto out; } DPRINTK(3, ">> 0x%04x=0x%02x\n", reg, val); ret = i2c_smbus_write_byte_data(tda1997x_data.client, reg&0xff, val); if (ret < 0) { dev_dbg(&tda1997x_data.client->dev, "%s:write reg error:reg=%2x,val=%2x\n", __func__, reg&0xff, val); ret = -1; goto out; } out: mutex_unlock(&tda1997x->page_lock); return ret; } /* 16bit write */ static int io_write16(u16 reg, u16 val) { if (io_write(reg, (val>>8)&0xff) < 0) return -1; if (io_write(reg+1, val&0xff) < 0) return -1; return 0; } /* 24bit write */ static int io_write24(u16 reg, u32 val) { if (io_write(reg, (val>>16)&0xff) < 0) return -1; if (io_write(reg+1, (val>>8)&0xff) < 0) return -1; if (io_write(reg+2, val&0xff) < 0) return -1; return 0; } /* n-byte write */ static unsigned int io_writen(u16 reg, u8 len, u8 *data) { int i; int sz = 0; for (i = 0; i < len; i++) { io_write(reg + i, data[i]); sz++; } return sz; } /*********************************************************************** * EDID ***********************************************************************/ /* Modified from the NXP exapp71a application for Ventana */ u8 edid_block[256] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1e, 0xe3, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, 0x01, 0x17, 0x01, 0x03, 0xa2, 0x00, 0x00, 0x00, 0x02, 0xee, 0x95, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x47, 0x57, 0x20, 0x56, 0x65, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x0a, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x1e, 0x3c, 0x0f, 0x44, 0x09, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x31, 0x30, 0x38, 0x30, 0x70, 0x44, 0x43, 0x78, 0x76, 0x43, 0x33, 0x44, 0x0a, 0x01, 0x89, 0x02, 0x03, 0x21, 0xf0, 0x4c, 0x22, 0x20, 0x21, 0x04, 0x13, 0x03, 0x12, 0x05, 0x14, 0x07, 0x16, 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x67, 0x03, 0x0c, 0x00, 0x00, 0x00, 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x80, 0x38, 0x74, 0x00, 0x00, 0x3f, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x50, 0x55, 0x00, 0x00, 0xd0, 0x52, 0x00, 0x00, 0x39, 0xa0, 0x0f, 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, 0x00, 0x20, 0x58, 0x32, 0x00, 0x00, 0x3f, 0xd7, 0x09, 0x80, 0xa0, 0x20, 0xe0, 0x2d, 0x10, 0x08, 0x60, 0x22, 0x01, 0x80, 0xe0, 0x21, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, }; /* DDC config 0 (page 0x20) */ u8 ddc_config0[8] = { /* 0x80: EDID_VERSION */ 0x19, /* main=0x19 */ /* 0x81: EDID_ENABLE bits: * 7 - unused * 6 - edid_only * 5:2 - unused * 1 - edid_b_en * 0 - edid_a_en */ 0x41, /* enable EDID for A only */ /* 0x82: EDID_BLOCK_SELECT bits: * 7:6 - ddc_b_blk1_sel * 5:4 - ddc_b_blk0_sel * 3:2 - ddc_a_blk1_sel * 1:0 - ddc_a_blk0_sel */ 0x44, /* ddc_b_blk1_sel=1 ddc_a_blk1_sel=1 */ /* 0x83: empty */ 0x00, /* 0x84: HPD_POWER bits: * 7:5 - unused * 4:3 - hpd_bp * 2 - hpd_edid_only * 1:0 - unused */ 0x06, /* hpd_bp=1 hpd_edid_only=1 */ /* 0x85: HPD_AUTO_CTRL bits * 7 - read_edid * 6 - unused * 5 - hpd_f3tech * 4 - hpd_other * 3 - hpd_unsel * 2 - hpd_all_ch * 1 - hpd_prv_ch * 0 - hpd_new_ch */ 0x18, /* hpd_other=1, hdp_unsel=1 */ /* 0x86: HPD_DURATION */ 0x00, /* hpd_duration=0 */ /* 0x87: RX_HPD_HEAC bits * 7:2 - unused * 1 - heac_b_en * 0 - heac_a_en */ 0x00, }; /* RT config (page 0x30) */ u8 rt_config[6] = { /* 0x00: RT_AUTO_CTRL bits: * 7 - unused * 6 - rt_other * 5 - rt_hpd_low * 4 - rt_no_5v * 3 - rt_unsel * 2 - rt_all_ch * 1 - rt_prv_ch * 0 - rt_new_ch */ 0x78, /* 0x01: EQ_MAN_CTRL0 bits: * 7-3 - unused * 2-0 - ch0_man_gain */ 0x03, /* ch0_man_gain=3 */ /* 0x02: EQ_MAN_CTRL1 bits: * 7 - unused * 6-4 - ch1_man_gain * 3 - unused * 2-0 - ch2_man_gain */ 0x33, /* ch1_man_gain=3 ch2_man_gain=3 */ /* 0x03: OUTPUT_CFG bits * 7 - eq_cal_act * 6 - eq_cal_ch * 5 - eq_cal_5v * 4 - eq_cal_cond * 3 - unused * 2 - out_idle * 1 - ap_idle * 0 - vp_idle */ 0xf0, /* eq_cal_act=1 eq_cal_ch=1 eq_cal_5v=1 eq_cal_cond=1 */ /* 0x04: MUTE_CTRL bits * 7:3 - unused * 2 - mute_man * 1 - mute_pol * 0 - mute_ena */ 0x00, /* 0x05: SLAVE_ADDR bits * 7:6 - unused * 5 - i2c_cec_a1 * 4 - i2c_cec_a0 * 3 - unused * 2 - i2c_a2 * 1 - i2c_a1 * 0 - i2c_a0 */ 0x10, /* i2c_cec_a0=1 */ }; static u32 reg = 0; /** Loads EDID data into embedded EDID memory of receiver device * @edid - pointer to two block EDID (array of 256 bytes) common to both inputs * * A common EDID block is used for both inputs with the exception of the * SPA (Source Physical Address) used for CEC. Therefore, we locate the SPA * within the EDID passed and treating it as the SPA for InputA while adding * 1 to it for the SPA for InputB. The EDID is written into nvram as well * as the SPA offset, SPA's and adjusted checksums for both inputs. */ static int tda1997x_load_edid_data(u8 *edid) { u8 chksum, chksum_spa; int i, n, spa_offset = 0; u16 spa; DPRINTK(0,"%s\n", __func__); /* sanity check EDID data: base block, extensions, checksums */ if (!drm_edid_is_valid((struct edid *) edid)) { printk(KERN_ERR "edid data is invalid\n"); return -EINVAL; } if (edid[0x7e] != 1) { printk(KERN_ERR "edid requires a single CEA extension block\n"); return -EINVAL; } /* Find SPA offset within CEA extentsion */ for (i = 1; i <= edid[0x7e]; i++) { u8 *ext = edid + (i * EDID_LENGTH); /* look for CEA v3 extension */ if (ext[0] != 0x02 || ext[1] != 0x03) continue; /* make sure we have a DBC */ if (ext[2] < 5) continue; /* iterate through DBC's until we find the Vendor block */ for (n = 4; n < ext[2]; n++) { char type = (ext[n] & 0xe0) >> 5; char len = (ext[n] & 0x1f); if (type == 3) { if (ext[n+1] == 0x03 && ext[n+2] == 0x0c) { spa_offset = n+4; spa = ext[spa_offset] << 8 | ext[spa_offset+1]; } } n += len; } } if (!spa_offset) { printk(KERN_ERR "EDID requires an HDMI Vendor Specific Data Block\n"); return -EINVAL; } /* calculate ext block checksum w/o SPA */ chksum = 0; for (i = 0; i < 127; i++) if (i != spa_offset && i != (spa_offset + 1)) chksum += edid[i+128]; /* write base EDID */ for (i = 0; i < 128; i++) io_write(REG_EDID_IN_BYTE0 + i, edid[i]); /* write CEA Extension */ for (i = 0; i < 128; i++) io_write(REG_EDID_IN_BYTE128 + i, edid[i+128]); /* SPA for InputA */ io_write16(REG_EDID_IN_SPA_AB_A, spa); chksum_spa = (u8)((spa & 0xff00)>>8) + (u8)(spa & 0x00ff) + chksum; chksum_spa = (u8)((0xff - chksum_spa) + 0x01); /* 2's complement */ io_write(REG_EDID_IN_CKSUM_A, chksum_spa); /* SPA for InputB */ spa += 1; io_write16(REG_EDID_IN_SPA_AB_B, spa); chksum_spa = (u8)((spa & 0xff00)>>8) + (u8)(spa & 0x00ff) + chksum; chksum_spa = (u8)((0xff - chksum_spa) + 0x01); /* 2's comp */ io_write(REG_EDID_IN_CKSUM_B, chksum_spa); /* write source physical address subaddress offset */ io_write(REG_EDID_IN_SPA_SUB, spa_offset); return 0; } /* * sysfs hooks */ static ssize_t b_show(struct device *dev, struct device_attribute *attr, char *buf) { int rz = 0; const char *name = attr->attr.name; struct tda1997x_data *tda1997x = &tda1997x_data; unsigned long flags; const char *sname = ""; spin_lock_irqsave(&tda1997x->lock, flags); if (strcasecmp(name, "state") == 0) { switch(tda1997x->state) { case STATE_NOT_INITIALIZED: sname = "not initialized"; break; case STATE_INITIALIZED: sname = "initalized"; break; case STATE_LOCKED: sname = "locked"; break; case STATE_UNLOCKED: sname = "unlocked"; break; case STATE_CONFIGURED: sname = "configured"; break; } rz = sprintf(buf, "%s\n", sname); } else if (strcasecmp(name, "vidmode") == 0) { if (tda1997x->state == STATE_LOCKED) { rz = sprintf(buf, "%dx%d%c@%dHz\n", tda1997x->video_mode.width, tda1997x->video_mode.height, (tda1997x->video_mode.interlaced)?'i':'p', tda1997x->video_mode.fps); } else rz = sprintf(buf, "no signal\n"); } else if (strcasecmp(name, "colorspace") == 0) { rz = sprintf(buf, "%s %s\n", colorspace_names[tda1997x->colorspace], colorimetry_names[tda1997x->colorimetry]); } else if (strcasecmp(name, "audmode") == 0) { if (tda1997x->state == STATE_LOCKED) { rz = sprintf(buf, "%dHz\n", tda1997x->audio_mode.samplerate); } else rz = sprintf(buf, "no signal\n"); } else if (strcasecmp(name, "vendor") == 0) { rz = sprintf(buf, "%s\n", tda1997x->vendor); } else if (strcasecmp(name, "product") == 0) { rz = sprintf(buf, "%s\n", tda1997x->product); } else if (strcasecmp(name, "info") == 0) { rz = sprintf(buf, "%s%s%s\n", (tda1997x->hdmi_detected)?"HDMI ":"", (tda1997x->eess_detected)?"EESS ":"", (tda1997x->hdcp_detected)?"HDCP ":""); } else if (strcasecmp(name, "edid") == 0) { for (rz = 0; rz < sizeof(edid_block); rz++) buf[rz] = edid_block[rz]; } else if (strcasecmp(name, "reg") == 0) { rz = sprintf(buf, "%02x\n", io_read(reg) ); printk(KERN_INFO "TDA1997x-core: Register 0x%04x=%s\n", reg, buf); } else { rz = sprintf(buf, "invalid attr\n"); } spin_unlock_irqrestore(&tda1997x->lock, flags); return rz; } static ssize_t b_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 val = 0; const char *name = attr->attr.name; struct tda1997x_data *tda1997x = &tda1997x_data; int i; if (strcasecmp(name, "edid") == 0) { for (i = 0; i < count; i++) edid_block[i] = buf[i]; printk(KERN_INFO "TDA1997x-core: New EDID being loaded\n"); /* Set HPD low */ tda1997x_manual_hpd(tda1997x, HPD_LOW); tda1997x_load_edid_data(edid_block); /* Set HPD high (now that EDID is ready) */ tda1997x_manual_hpd(tda1997x, HPD_HIGH); } else if (strcasecmp(name, "reg") == 0) { i = sscanf(buf, "%x %x", ®, &val); if (i == 2) { io_write( (u16)reg, (u8)val); printk(KERN_INFO "TDA1997x-core: Register 0x%04x=0x%02x\n", reg, val); } else { printk(KERN_INFO "TDA1997x-core: Register 0x%04x\n", reg); } } else { printk(KERN_ERR "invalid name '%s'\n", attr->attr.name); } return count; } /* * Create a group of attributes so that we can create and destory them all * at once. */ static struct device_attribute attr_state = __ATTR(state, 0660, b_show, b_store); static struct device_attribute attr_vidmode = __ATTR(vidmode, 0660, b_show, NULL); static struct device_attribute attr_audmode = __ATTR(audmode, 0660, b_show, NULL); static struct device_attribute attr_vendor = __ATTR(vendor, 0660, b_show, NULL); static struct device_attribute attr_product = __ATTR(product, 0660, b_show, NULL); static struct device_attribute attr_info = __ATTR(info, 0660, b_show, NULL); static struct device_attribute attr_edid = __ATTR(edid, 0770, b_show, b_store); static struct device_attribute attr_colorspace = __ATTR(colorspace, 0660, b_show, NULL); static struct device_attribute attr_reg = __ATTR(reg, 0660, b_show, b_store); static struct attribute *tda1997x_attrs[] = { &attr_state.attr, &attr_vidmode.attr, &attr_audmode.attr, &attr_vendor.attr, &attr_product.attr, &attr_info.attr, &attr_edid.attr, &attr_colorspace.attr, &attr_reg.attr, NULL }; static struct attribute_group attr_group = { .attrs = tda1997x_attrs, }; /** Loads DDC and RT configuration data into embedded memory of receiver device * @ddc_config - pointer to the DDC block configuration (8 bytes) * @rt_config - pointer to the RT block configuration (6 bytes) */ static int tda1997x_load_config_data(struct tda1997x_data *tda1997x, const u8 *ddc, const u8 *rt) { int i; if (!tda1997x->internal_edid) return -EPERM; /* DDC to page 20h */ for (i = 0; i < 8; i++) io_write(REG_EDID_IN_VERSION + i, ddc[i]); /* RT to page 30h */ for (i = 0; i < 6; i++) io_write(REG_RT_AUTO_CTRL + i, rt[i]); return 0; } /*********************************************************************** * Signal Control ***********************************************************************/ /** manual HPD (Hot Plug Detect) control * @param hdp_mode * @returns 0 on success */ static int tda1997x_manual_hpd(struct tda1997x_data *tda1997x, hpdmode_t mode) { u8 hpd_auto, hpd_pwr, hpd_man; DPRINTK(0, "%s: %s\n", __func__, hpd_names[mode]); /* HPD_POWER bits: * 7:4 - unused * 3:2 - hpd_bp[1:0] * 1 - hpd_edid_only * 0 - unused * * HPD_AUTO_CTRL bits: * 7 - read_edid * 6 - unused * 5 - hpd_f3tech * 4 - hp_other * 3 - hpd_unsel * 2 - hpd_all_ch * 1 - hpd_prv_ch * 0 - hpd_new_ch * * HPD_MAN bits: * 7 - ? * 6-3 - unused * 2:0 - man_gain */ hpd_auto = io_read(REG_HPD_AUTO_CTRL); hpd_pwr = io_read(REG_HPD_POWER); hpd_man = io_read(REG_HPD_MAN_CTRL); hpd_man &= 0x87; switch(mode) { /* HPD low and pulse of at least 100ms */ case HPD_LOW: hpd_man &= ~0x03; /* man_gain=0 */ hpd_pwr &= ~0x0c; /* hpd_bp=0 */ io_write(REG_HPD_POWER, hpd_pwr); io_write(REG_HPD_MAN_CTRL, hpd_man); break; /* HPD high */ case HPD_HIGH: /* hpd_bp=1 */ hpd_pwr = (hpd_pwr & ~0x0c) | 0x04; io_write(REG_HPD_POWER, hpd_pwr); break; /* HPD low and pulse of at least 100ms */ case HPD_LOW_OTHER: hpd_man &= ~0x03; /* man_gain=0 */ hpd_auto &= ~0x10; /* hp_other=0 */ io_write(REG_HPD_AUTO_CTRL, hpd_auto); io_write(REG_HPD_MAN_CTRL, hpd_man); break; /* HPD high */ case HPD_HIGH_OTHER: hpd_auto |= 0x10; /* hp_other=1 */ io_write(REG_HPD_AUTO_CTRL, hpd_auto); break; /* HPD low pulse */ case HPD_PULSE: hpd_man &= ~0x03; /* man_gain=0 */ /* write HPD_MAN to have HPD low pulse */ io_write(REG_HPD_MAN_CTRL, hpd_man); break; } return 0; } /** configure the receiver with the new colorspace * @returns 0 on success * colorspace conversion depends on input format and output format * blanking codes depend on the output colorspace */ static int tda1997x_configure_conversion(struct tda1997x_data *tda1997x, tda1997x_colorspace_t colorspace, tda1997x_colorimetry_t colorimetry, tda1997x_videofmt_t vidout_format) { colormatrixcoefs_t *pCoefficients = NULL; blankingcodes_t *pBlankingCodes = NULL; u8 data[31]; printk(KERN_INFO "%s: %s %s => %s\n", KBUILD_MODNAME, colorspace_names[colorspace], (colorspace == COLORSPACE_RGB) ? "" : colorimetry_names[colorimetry], vidfmt_names[vidout_format]); switch (vidout_format) { /* RGB 4:4:4 output */ case VIDEOFMT_444: pBlankingCodes = &RGBBlankingCode; if (colorspace != COLORSPACE_RGB) { if (colorimetry == COLORIMETRY_ITU709) pCoefficients = &conversion_matrix[ITU709_RGBLimited]; else pCoefficients = &conversion_matrix[ITU601_RGBLimited]; } break; /* YUV422 output */ case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ pBlankingCodes = &YUVBlankingCode; if (colorspace == COLORSPACE_RGB) pCoefficients = &conversion_matrix[RGBLimited_ITU601]; break; } if (pCoefficients) { s16 *pTabCoeff = &(pCoefficients->offInt1); u8 i, j; /* test the range of the coefs */ for (i = 0; i < OFFSET_LOOP_NB; i++) { for (j = 0; j < MAT_OFFSET_NB; j++) { if ( (pTabCoeff[j] < MIN_VAL_OFFSET) || pTabCoeff[j] > MAX_VAL_OFFSET) return -EINVAL; } pTabCoeff = &(pCoefficients->offOut1); } pTabCoeff = &(pCoefficients->P11Coef); for (i = 0; i < MAT_COEFF_NB; i++) { if ( (pTabCoeff[i] < MIN_VAL_OFFSET) || pTabCoeff[i] > MAX_VAL_OFFSET) return -EINVAL; } /* enable matrix conversion by disabling bypass: mat_bp=0 */ data[0] = io_read(REG_VDP_CTRL); data[0] &= ~(1<<0); /* offset input 1 value */ data[1] = (u8) ((u16)(pCoefficients->offInt1) >> 8); data[2] = (u8) (pCoefficients->offInt1 & MASK_MAT_COEFF_LSB); /* offset input 2 value */ data[3] = (u8) ((u16)(pCoefficients->offInt2) >> 8); data[4] = (u8) (pCoefficients->offInt2 & MASK_MAT_COEFF_LSB); /* offset input 3 value */ data[5] = (u8) ((u16)(pCoefficients->offInt3) >> 8); data[6] = (u8) (pCoefficients->offInt3 & MASK_MAT_COEFF_LSB); /* Coefficient (1,1) value */ data[7] = (u8) ((u16)(pCoefficients->P11Coef) >> 8); data[8] = (u8) (pCoefficients->P11Coef & MASK_MAT_COEFF_LSB); /* Coefficient (1,2) value */ data[9] = (u8) ((u16)(pCoefficients->P12Coef) >> 8); data[10] = (u8) (pCoefficients->P12Coef & MASK_MAT_COEFF_LSB); /* Coefficient (1,3) value */ data[11] = (u8) ((u16)(pCoefficients->P13Coef) >> 8); data[12] = (u8) (pCoefficients->P13Coef & MASK_MAT_COEFF_LSB); /* Coefficient (2,1) value */ data[13] = (u8) ((u16)(pCoefficients->P21Coef) >> 8); data[14] = (u8) (pCoefficients->P21Coef & MASK_MAT_COEFF_LSB); /* Coefficient (2,2) value */ data[15] = (u8) ((u16)(pCoefficients->P22Coef) >> 8); data[16] = (u8) (pCoefficients->P22Coef & MASK_MAT_COEFF_LSB); /* Coefficient (2,3) value */ data[17] = (u8) ((u16)(pCoefficients->P23Coef) >> 8); data[18] = (u8) (pCoefficients->P23Coef & MASK_MAT_COEFF_LSB); /* Coefficient (3,1) value */ data[19] = (u8) ((u16)(pCoefficients->P31Coef) >> 8); data[20] = (u8) (pCoefficients->P31Coef & MASK_MAT_COEFF_LSB); /* Coefficient (3,2) value */ data[21] = (u8) ((u16)(pCoefficients->P32Coef) >> 8); data[22] = (u8) (pCoefficients->P32Coef & MASK_MAT_COEFF_LSB); /* Coefficient (3,3) value */ data[23] = (u8) ((u16)(pCoefficients->P33Coef) >> 8); data[24] = (u8) (pCoefficients->P33Coef & MASK_MAT_COEFF_LSB); /* Offset output 1 value */ data[25] = (u8) ((u16)(pCoefficients->offOut1) >> 8); data[26] = (u8) (pCoefficients->offOut1 & MASK_MAT_COEFF_LSB); /* Offset output 2 value */ data[27] = (u8) ((u16)(pCoefficients->offOut2) >> 8); data[28] = (u8) (pCoefficients->offOut2 & MASK_MAT_COEFF_LSB); /* Offset output 3 value */ data[29] = (u8) ((u16)(pCoefficients->offOut3) >> 8); data[30] = (u8) (pCoefficients->offOut3 & MASK_MAT_COEFF_LSB); io_writen(REG_VDP_CTRL, 31, data); } else { /* enable matrix conversion bypass (mat_b) */ data[0] = io_read(REG_VDP_CTRL); data[0] |= (1<<0); /* mat_bp=1 */ io_write(REG_VDP_CTRL, data[0]); } /* SetBlankingCodes */ if (pBlankingCodes) { io_write16(REG_BLK_GY, pBlankingCodes->blankingCodeGy); io_write16(REG_BLK_BU, pBlankingCodes->blankingCodeBu); io_write16(REG_BLK_RV, pBlankingCodes->blankingCodeRv); } return 0; } /** Configure the active input to the given resolution * @param resolution ID * @returns 0 on success */ static int tda1997x_configure_input_resolution(resolutionid_t resolution) { struct tda1997x_data *tda1997x = &tda1997x_data; u8 reg; const resolution_timings_t *timings = NULL; const vhref_values_t *vh; int i; DPRINTK(0,"%s\n", __func__); /* scan through resolution table looking for a match for timings */ for (i = 0; i < ARRAY_SIZE(resolution_timings); i++) { if (resolution == resolution_timings[i].resolutionID) { timings = &resolution_timings[i]; vh = &timings->vhref_values; break; } } if (!timings) { printk(KERN_INFO "%s: resolution not supported\n", KBUILD_MODNAME); return -1; } /* Configure Frame Detection Window: * define the horizontal area where the VHREF modules consider a VSYNC a * new frame (values typically depend on the video mode being processed */ /* start position of the frame detection window */ io_write16(REG_FDW_S, 0x2ef & 0x3fff); /* end position of the frame detection window */ io_write16(REG_FDW_E, 0x141 & 0x3fff); /* Set Pixel And Line Counters */ if (tda1997x->chip_revision == 0) /* add 1 line to lineCountPreset */ io_write16(REG_PXCNT_PR, (timings->pixCountPreset + 3) & 0x3fff); else io_write16(REG_PXCNT_PR, timings->pixCountPreset & 0x3fff); io_write16(REG_PXCNT_NPIX, timings->pixCountNb & 0x3fff); io_write16(REG_LCNT_PR, timings->lineCountPreset & 0x3fff); io_write16(REG_LCNT_NLIN, timings->lineCountNb & 0x3fff); /* Configure VHRef: * configure the VHRef timing generator responsible for rebuilding all * horiz and vert synch and ref signals from its input allowing automatic * detection algorithms and forcing predefined modes (480i & 576i) * * REG_VHREF_CTRL bits: * bit7 - interlaced_det - interlace detect method: 1=alternate,0=framefield * bit6:5 - vsync_type: 0=Auto,1=FDW,2=Even,3=Odd * bit4:3 - std_det: 0=PAL,1=NTSC,2=AUTO,3=OFF * bit2 - href_src - VREF source: 1=from standard, 0=manual * bit1 - href_src - HREF source: 1=from standard, 0=manual * bit0 - hsync_sel - HSYNC signal: 1=HS,0=VS */ reg = io_read(REG_VHREF_CTRL); io_write(REG_VHREF_CTRL, 0x3<<3 /* std_det=off */); /* Set VHRef: * configure the VHRef timing values. In case the VHREF generator has * been configured in manual mode, this will allow to manually set all horiz * and vert ref values (non-active pixel areas) of the generator * and allows setting the frame reference params. These values typically * depend on the video mode being processed */ /* horizontal reference start/end */ io_write16(REG_HREF_S, vh->href_start & 0x3fff); io_write16(REG_HREF_E, vh->href_end & 0x3fff); /* vertical reference f1 start/end */ io_write16(REG_VREF_F1_S, vh->vref_f1_start & 0x3fff); io_write (REG_VREF_F1_WIDTH, vh->vref_f1_width); /* vertical reference f2 start/end */ io_write16(REG_VREF_F2_S, vh->vref_f2_start & 0x3fff); io_write (REG_VREF_F2_WIDTH, vh->vref_f2_width); /* F1/F2 FREF, field polarity */ io_write16(REG_FREF_F1_S, (vh->fieldref_f1_start & 0x3fff) || (vh->fieldPolarity<<8)); io_write16(REG_FREF_F2_S, vh->fieldref_f2_start & 0x3fff); /* set the state machine */ tda1997x->state = STATE_CONFIGURED; return 0; } /** Configure Audio output formatter * @param pdata with audio output configuration * @param channel_assignment * @returns 0 on success */ static int tda1997x_configure_audio_formatter(struct tda1997x_platform_data *pdata, u8 channel_assignment) { u8 fifo_latency = 0x80; bool sp_used_by_fifo = 0; bool ws_active = 0; u8 reg; DPRINTK(0,"%s: %s %s channel_assignment=%d enable_auto_mute=%d\n", __func__, audlayout_names[pdata->audout_layout], audfmt_names[pdata->audout_format], channel_assignment, pdata->audio_auto_mute); /* AUDIO_PATH bits: * 7:0 - channel assignment (CEA-861-D Table 20) */ io_write(REG_AUDIO_PATH, channel_assignment); /* AUDIO_SEL bits: * 7 - aclk_inv - Polarity of clock signal on A_CLK pin (1=invert) * 6 - test_tone - Audio test tone generator (1=on) * 5 - i2s_spdif - Selection of audio output format (0=I2S, 1=SPDIF) * 4 - i2s_32 - Size of I2S samples (0=16bit, 1=32bit) * 3 - hwmute_ena - Automatic audio mute (1=enabled) * 2 - hbr_demux - High Bit Rate output mode: * 0:straight via AP0 * 1:demuxed via AP0,AP1,AP2,AP3 * 1:0 - audio_type - Selection of audio output packet mode * 00: Audio samples * 01: High-Bit Rate (HBR) * 10: One Bit Audio (OBA) * 11: Direct Stream Transfer (DST) */ reg = io_read(REG_AUDIO_SEL); if (pdata->audio_auto_mute) reg |= 1<<3; else reg &= ~(1<<3); switch(pdata->audout_format) { case AUDIO_FMT_I2S16: /* 16bit I2S mode, SP flag used by FIFO */ reg &= ~(1<<4); reg &= ~(1<<5); sp_used_by_fifo = 1; ws_active = 1; break; case AUDIO_FMT_I2S32: /* 32bit I2S mode, SP flag used by FIFO */ reg |= (1<<4); reg &= ~(1<<5); sp_used_by_fifo = 1; ws_active = 1; break; case AUDIO_FMT_OBA: /* One Bit Audio */ reg &= ~(1<<5); sp_used_by_fifo = 1; ws_active = 1; break; case AUDIO_FMT_SPDIF: /* SPDIF */ /* SPDIF mode, SP flag used by FIFO */ reg |= (1<<5); sp_used_by_fifo = 1; break; case AUDIO_FMT_I2S16_HBR_DEMUX: /* 16bit I2S High Bit Rate demux */ reg &= ~(1<<4); reg &= ~(1<<5); sp_used_by_fifo = 1; ws_active = 1; break; case AUDIO_FMT_I2S32_HBR_DEMUX: /* 32bit I2S High Bit Rate demux */ reg |= (1<<4); reg &= ~(1<<5); sp_used_by_fifo = 1; ws_active = 1; break; case AUDIO_FMT_DST: /* Direct Stream Transfer */ sp_used_by_fifo = 0; break; case AUDIO_FMT_SPDIF_HBR_DEMUX: /* SPDIF High Bit Rate demux */ reg |= (1<<5); sp_used_by_fifo = 0; break; default: break; } /* Clock polarity */ reg &= ~(1<<7); if (pdata->audout_invert_clk) reg |= 1<<7; io_write(REG_AUDIO_SEL, reg); /* AUDIO_LAYOUT bits: * 2 - sp_flag : * 0 - sp flag ignored by FIFO-control (4 subpackets written in FIFO) * 1 - sp flag used by FIFO-control (Present samples subpackets written in FIFO) * 1 - layout_man: * 0 - layout defined by audio packet header * 1 - manual control of layout * 0 - layout: value of layout in case of manual selection * 0 - layout0 * 1 - layout1 */ io_write(REG_AUDIO_LAYOUT, ((sp_used_by_fifo)?(1<<2):0) | pdata->audout_layout); /* FIFO Latency value */ io_write(REG_FIFO_LATENCY_VAL, fifo_latency); /* AUDIO_OUT_ENABLE bits: enable AP, ACLK and WS if needed * 5 - aclk_out - 1=Audio clock port active * 4 - ws_out - 1=Word selection port active * 3 - ap3_out - 1=AP0 active * 2 - ap2_out - 1=AP1 active * 1 - ap1_out - 1=AP2 active * 0 - ap0_out - 1=AP3 active */ if (!sp_used_by_fifo) { reg = 0x0f; } else { /* TODO: ensure audio out DAI allows AP1,2,3? */ reg = 0x01; /* AP0 always enabled */ if (channel_assignment >= 0x01) reg |= 2; /* >1 also need AP1 */ if (channel_assignment >= 0x04) reg |= 4; /* >4 also need AP2 */ if (channel_assignment >= 0x0C) reg |= 8; /* >12 also need AP3 */ /* specific cases where AP1 is not used */ if ((channel_assignment == 0x04) || (channel_assignment == 0x08) || (channel_assignment == 0x0c) || (channel_assignment == 0x10) || (channel_assignment == 0x14) || (channel_assignment == 0x18) || (channel_assignment == 0x1c)) reg &= ~2; /* specific cases where AP2 is not used */ if ((channel_assignment >= 0x14) && (channel_assignment <= 0x17)) reg &= ~4; } if (ws_active) reg |= 0x30; io_write(REG_AUDIO_OUT_ENABLE, reg); /* reset test mode to normal audio freq auto selection */ io_write(REG_TEST_MODE, 0x00); /* reset sw_ncts_en & ncts_en if i2s layout_0 */ if (reg & 0x18) { reg = io_read(REG_TEST_NCTS_CTRL); reg &= ~0x03; io_write(REG_TEST_NCTS_CTRL, reg); } return 0; } /** Selects HDMI input * here as well. * * @param input * @returns 0 on success */ int tda1997x_select_input(tda1997x_input_t input) { struct tda1997x_data *tda1997x = &tda1997x_data; u8 reg; DPRINTK(0,"%s: HDMI-%c\n", __func__, input?'B':'A'); reg = io_read(REG_INPUT_SEL); /* keep loop mode for TDA19973 */ if (tda1997x->chip == 19973) reg &= ~MASK_DIG_INPUT_VDPR_FMT; else reg &= ~(MASK_DIG_INPUT_VDPR_FMT | MASK_HDMIOUTMODE); reg |= 0x80; /* RESET_FTM - vdp reset */ reg |= input; reg |= FORMAT_RESET; /* Soft reset of format measurement timing */ /* update INPUT_SEL */ io_write(REG_INPUT_SEL, reg); return 0; } EXPORT_SYMBOL(tda1997x_select_input); /** configure video output format (output pins and sync config) * @param pdata with video output configuration * @returns 0 on success */ static int tda1997x_set_video_outputformat(struct tda1997x_platform_data *pdata) { u8 reg; DPRINTK(0,"%s: %s\n", __func__, vidfmt_names[pdata->vidout_format]); /* Configure pixel clock generator: * - delay and polarity from platform data * - clock per output format * * CLKOUT_CTRL bits: * 7 - unused * 6:4 - clkout_del - Delay of clock signal on V_CLK pin * 3 - unused * 2 - clkout_tog - polarity of clock signal on V_CLK pin (1 - invert) * 1:0 - clkout_sel: Selection of the clock signal output on CKP pin * 00: Pix_clock (and selection for Chroma/Luma in case of CCIR-656, so * clock for CCIR-656 DDR) * 01: Pix_clock_x2 (for 422_CCIR) * 10: Pix_clock_div2 * 11: Pix_clock_div4 */ reg = pdata->vidout_delay_clk << 4; if (pdata->vidout_clkmode == CLOCK_SINGLE_EDGE) { /* single edge */ switch (pdata->vidout_format) { case VIDEOFMT_444: /* RGB444/YUV444 */ case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ break; case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ reg |= 0x01; /* clk_x2 */ break; } } else { /* dual edge */ switch (pdata->vidout_format) { case VIDEOFMT_444: /* RGB444/YUV444 */ case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ reg |= 0x02; /* clk_div2 */ break; case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ break; } } io_write(REG_CLKOUT_CTRL, reg); /* Configure pre-filter: * * FILTERS_CTRL: * 3:2 - Bu Filter control * 00: Off * 01: 2 Taps * 10: 7 Taps * 11: 2/7 Taps * 1:0 - Rv Filter control * 00: Off * 01: 2 Taps * 10: 7 Taps * 11: 2/7 Taps */ reg = 0x00; /* filters off */ /* 4:2:2 mode requires conversion */ if ((pdata->vidout_format == VIDEOFMT_422_SMP) || (pdata->vidout_format == VIDEOFMT_422_CCIR)) reg = 0x0f; /* 27taps for Rv and Bu */ io_write(REG_FILTERS_CTRL, reg); /* Enable Blanking code and timing ref (EAV/SAV) insertion */ reg = VP_OUT | pdata->vidout_format; if (pdata->vidout_blc) reg |= VP_BLK; if (pdata->vidout_trc) reg |= VP_TRC; io_write(REG_OF_CTRL, reg); /* Configure bypass: * VDP_CTRL bits: * 5 - compdel_bp - 1:bypass compdel * 4 - formatter_bp - 1:bypass formatter * 3 - ? * 2 - ? * 1 - prefilter_bp - 1:bypass prefilter * 0 - mat_bp - 1:bypass matrix conversion */ reg = io_read(REG_VDP_CTRL); /* bypass pre-filter if not needed (REG_FILTERS_CTRL == 0) */ if ( (io_read(REG_FILTERS_CTRL) & 0x0f) == 0) reg |= (1<<1); /* disable pre-filter */ else reg &= ~(1<<1); /* enable pre-filter */ /* bypass formatter if not needed: * (444 mode and no insertion of timing/blanking codes */ if ( (pdata->vidout_format == VIDEOFMT_444) && !pdata->vidout_blc && !pdata->vidout_trc ) { reg |= (1<<4); /* disable formatter */ } else { /* enable formatter and compdel: needed for timing/blanking codes */ reg &= ~((1<<4)|(1<<5)); } /* activate compdel for small sync delays */ if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) { reg &= ~(1<<5); } io_write(REG_VDP_CTRL, reg); /* Configure DE output signal: * - delay, polarity, and source from platform data * * DE_FREF_SEL bits: * 7:4 - de_del - DE from HDMI delay (Latency datapath + [-8..+7] pixels) * 3 - de_pxq - DE pixel qualification (only when del_sel = 00) * 0: Timing codes are not signaled by DE * 1: Timing codes are signaled by DE * 2 - de_pol - Polarity of signal output on DE/FREF pin * 0: No specific action * 1: Invert signal * 1:0 - de_sel - Selection of signal output on DE/FREF pin * 00: DE from VHREF [HREF and not(VREF)] * 01: FREF from VHREF * 10: FREF from HDMI */ io_write(REG_DE_FREF_SEL, (pdata->vidout_delay_de << 4) | (pdata->vidout_invert_de << 2) | (pdata->vidout_sel_de)); /* HS_HREF_SEL bits: * 7:4 - hs_del - HS from HDMI delay (Latency datapath + [-8..+7] pixels) * 3 - href_pxq - HREF pixel qualifcation: * 0 - Timing codes are not signaled by HREF * 1 - Timing codes are signaled by HREF * 2 - hsync_pol: Polarity of signal output on HS/HREF pin * 0 - No specific action * 1 - Invert signal * 1:0 - hsync_sel - Selection of signal output on HS/HREF pin * 00: HS from VHREF (Select HS signal output from internal sync generator * on HS_HREF hardware pin) * 01: HREF from VHREF (Select HREF signal output from internal sync * generator on HS_HREF hardware pin) * 10: HREF from HDMI (Select HREF signal output from HDMI input sync * processor on HS_HREF hardware pin) */ io_write(REG_HS_HREF_SEL, (pdata->vidout_delay_hs << 4) | (pdata->vidout_invert_hs << 2) | (pdata->vidout_sel_hs)); /* VS_VREF_SEL bits: * 7:4 - vs_del - VS from HDMI delay (Latency datapath + [-8..+7] pixels) * 3 - unused * 2 - vsync_pol: Polarity of signal output on VS/VREF pin * 0 - No specific action * 1 - Invert signal * 1:0 - vsync_sel - Selection of signal output on VS/VREF pin * 00: VS from VHREF (Select VS signal output from internal sync generator * on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VSYNC_VHREF) * 01: VREF from VHREF (Select VREF signal output from internal sync * generator on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VREF_VHREF) * 10: VREF from HDMI (Select HREF signal output from HDMI input sync * processor on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VSYNC_HDMI) */ io_write(REG_VS_VREF_SEL, (pdata->vidout_delay_vs << 4) | (pdata->vidout_invert_vs << 2) | (pdata->vidout_sel_vs)); return 0; } /** Soft Reset of specific hdmi info * @param info_rst - reset to apply to HDMI_INFO_RST * @param bresetSus - reset start-up sequencer * @returns 0 on success */ static int tda1997x_hdmi_info_reset(u8 info_rst, bool bresetSus) { u8 reg; DPRINTK(0,"%s: 0x%02x %s\n", __func__, info_rst, bresetSus?"RESET_SUS":""); reg = io_read(REG_HDMI_INFO_RST); io_write(REG_HDMI_INFO_RST, info_rst); /* if IF has been reset, clear INT_FLG_MODE as all ITs are raided by the * reset IF */ if (reg & RESET_IF) { reg = io_read(REG_INT_FLG_CLR_MODE); io_write(REG_INT_FLG_CLR_MODE, reg); } /* Write SUS_RESET register (bit hdcp_dcc_man does not exist on TDA19972) */ if (!bresetSus) { reg = io_read(REG_RATE_CTRL); reg |= RATE_REFTIM_ENABLE; reg = io_write(REG_RATE_CTRL, reg); } else { reg = io_read(REG_RATE_CTRL); reg &= ~RATE_REFTIM_ENABLE; reg = io_write(REG_RATE_CTRL, reg); } return 0; } /* Configure HDCP: set basic configuration for HDCP module: * enable/disable, key encryption, DDC address, key description seed * * @param decrypt_keys - Key internal decryption (on/off) * @param hdcp_enable - Enable/disable HDCP function * @param i2c_addr - Display data channel I2C slave address * @param key_decryption_seed - Key decryption Seed * @returns 0 on success */ static int tda1997x_configure_hdcp(struct tda1997x_data *tda1997x, hdcp_key_t decrypt_keys, enable_t hdcp_enable, u8 ddc_i2c_addr, u16 key_decryption_seed) { u8 reg; u8 regs[3]; DPRINTK(0,"%s: enable=%d ddc=0x%02x seed=0x%04x\n", __func__, hdcp_enable, ddc_i2c_addr, key_decryption_seed); /* HDCP control */ regs[0] = decrypt_keys | hdcp_enable; /* keys description seed MSB */ regs[1] = key_decryption_seed >> 8; /* keys description seed LSB */ regs[2] = key_decryption_seed & 0x00ff; if (tda1997x->chip_revision == 0) { /* enable clock on TMDS PLL by using FRO */ io_write(REG_CLK_CFG, 0x03); io_write(REG_PON_CBIAS, 0x01); io_write(REG_PON_PLL, 0x01); io_write(REG_PON_OVR_EN, 0x01); } /* write HDCP_CTRL regs */ io_writen(REG_HDCP_CTRL, 3, regs); /* write i2c addr */ io_write(REG_HDCP_DDC_ADDR, ddc_i2c_addr); if (tda1997x->chip_revision == 0) { /* disable clock on TMDS PLL by using FRO */ io_write(REG_CLK_CFG, 0x00); io_write(REG_PON_OVR_EN, 0x00); /* Restore clock */ io_write(REG_PON_CBIAS, 0x00); io_write(REG_CGU_DEBUG_SEL, 0x08); /* Clear HDMI mode flag in BCAPS (for N1) */ io_write(REG_CLK_CFG, 0x03); io_write(REG_PON_OVR_EN, 0x01); io_write(REG_PON_CBIAS, 0x01); io_write(REG_PON_PLL, 0x01); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; reg |= 0x02; io_write(REG_MODE_RECOVER_CFG1, reg); io_write(REG_CLK_CFG, 0x00); io_write(REG_PON_OVR_EN, 0x00); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; io_write(REG_MODE_RECOVER_CFG1, reg); } /* clear HDCP interrupt status bits that may have been raised during this * process */ io_write(REG_INT_FLG_CLR_HDCP, 0x07); return 0; } /* Configure HDCP MTP * @param cmd - command to be sent (download or read) * @returns 0 on success */ static int tda1997x_configure_mtp(struct tda1997x_data *tda1997x, mtp_command_t cmd) { u8 reg; unsigned int timeout; switch (cmd) { case MTP_START_DOWNLOAD: DPRINTK(0,"%s: MTP_START_DOWNLOAD\n", __func__); if (tda1997x->chip_revision == 0) { /* disable termination and enable HDCP block */ io_write(REG_RT_MAN_CTRL, 0x00); io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); /* enable clock on TMDS PLL by using FRO */ io_write(REG_CLK_CFG, 0x03); io_write(REG_PON_CBIAS, 0x01); io_write(REG_PON_PLL, 0x01); io_write(REG_PON_OVR_EN, 0x01); io_write(REG_CGU_DEBUG_SEL, 0x00); /* reset HDCP block */ io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); io_write(REG_MAN_HDMI_SET, 0x04); io_write(REG_MAN_HDMI_SET, 0x00); /* remove force */ io_write(REG_PON_OVR_EN, 0x00); io_write(REG_CLK_CFG, 0x03); /* copy byte KEY_0(39) (0x4002) into private_area (0x425f) */ reg = io_read(0x4002 /*REG_MTP_KEY39_LSB*/); io_write(0x425f /*REG_MTP_PRIVATE_AREA*/, reg); } /* Enable HDCP */ io_write(REG_HDMI_INFO_RST, 0x00); io_write(REG_HDCP_CTRL, 0x03); /* clear flag hdcp_dlmtp: 0x0015:4 */ io_write(REG_INT_FLG_CLR_HDCP, 0x18); /* download key into HDCP engine */ io_write(REG_HDCP_KEY_CTRL, 0x01); /* check flag hdcp_dlram: 0x0015:3 */ timeout = jiffies + msecs_to_jiffies(100); do { reg = io_read(REG_INT_FLG_CLR_HDCP); } while ( (jiffies < timeout) & ((reg & 0x08) != 0x08)); /* download MTP into SRAM: hmtp_dl_all (0x137a) */ io_write(REG_HMTP_CTRL, 0x01); /* check flag hdcp_dlmtp: 0x0015:4 */ timeout = jiffies + msecs_to_jiffies(100); do { reg = io_read(REG_INT_FLG_CLR_HDCP); } while ( (jiffies < timeout) & ((reg & 0x10) != 0x10)); /* clear HDCP interrupt status bits that may have been raised */ io_write(REG_INT_FLG_CLR_HDCP, 0x07); break; case MTP_START_READ: DPRINTK(0,"%s: MTP_START_READ\n", __func__); /* clear flag hdcp_dlmtp */ io_write(REG_INT_FLG_CLR_HDCP, 0x10); /* Download MTP into SRAM: hmtp_dl_all */ io_write(REG_HMTP_CTRL, 0x01); /* check flag hdcp_dlmtp */ timeout = jiffies + msecs_to_jiffies(100); do { reg = io_read(REG_INT_FLG_CLR_HDCP); } while ( (jiffies < timeout) & ((reg & 0x10) != 0x10)); break; } return 0; } /** set power mode */ static void tda1997x_power_mode(struct tda1997x_data *tda1997x, bool bEnable) { u8 reg; dev_dbg(&tda1997x->client->dev, "%s %s\n", __func__, bEnable?"on":"off"); DPRINTK(0,"%s %s\n", __func__, bEnable?"on":"off"); if (bEnable) { /* Power on sequence */ #if 0 /* Disable low power mode */ reg = io_read(REG_EDID_POWER); reg &= ~MASK_LOW_PW_EDID; io_write(REG_EDID_POWER, reg); #endif /* Automatic control of TMDS */ io_write(REG_PON_OVR_EN, TMDS_AUTO_PON); /* Enable current bias unit */ io_write(REG_CFG1, CBIAS_PON); #if 0 /* Enable TMDS clock in the digital equalizer */ reg = io_read(REG_SUS_RESET); reg &= ~MASK_TMDS_CLK_DIS; io_write(REG_SUS_RESET, reg); /* Enable Xtal Osc */ reg = io_read(REG_XOSC_CFG); reg &= ~MASK_XTAL_OSC_PD; io_write(REG_XOSC_CFG, reg); #endif /* Enable deep color PLL */ io_write(REG_DEEP_PLL7, DC_PLL_PON); #if 0 /* Enable audio PLL */ reg = io_read(REG_CLOCKS_MODE); reg &= ~MASK_AUDIO_PLL_PD; io_write(REG_CLOCKS_MODE, reg); #endif /* Output buffers active */ reg = io_read(REG_OF_CTRL); reg &= ~MASK_OF_CTRL_OUT_HIZ; io_write(REG_OF_CTRL, reg); } else { /* Power down EDID mode sequence */ /* Output buffers in HiZ */ reg = io_read(REG_OF_CTRL); reg |= MASK_OF_CTRL_OUT_HIZ; io_write(REG_OF_CTRL, reg); #if 0 /* Disable audio PLL */ reg = io_read(REG_CLOCKS_MODE); reg |= MASK_AUDIO_PLL_PD; io_write(REG_CLOCKS_MODE, reg); #endif /* Disable deep color PLL */ io_write(REG_DEEP_PLL7, DC_PLL_PD); #if 0 /* Disable Xtal Osc */ reg = io_read(REG_XOSC_CFG); reg |= MASK_XTAL_OSC_PD; io_write(REG_XOSC_CFG, reg); /* Disable TMDS clock in the digital equalizer */ reg = io_read(REG_SUS_RESET); reg |= MASK_TMDS_CLK_DIS; io_write(REG_SUS_RESET, reg); #endif /* Disable current bias unit */ io_write(REG_CFG1, CBIAS_POFF); /* Manual control of TMDS */ io_write(REG_PON_OVR_EN, TMDS_MAN_PON); #if 0 /* Enable low power mode */ reg = io_read(REG_EDID_POWER); reg |= MASK_LOW_PW_EDID; io_write(REG_EDID_POWER, reg); #endif } } /* check the audio samplerate for change * @returns 0 on success */ static int tda1997x_get_audio_frequency(struct tda1997x_data *tda1997x) { u8 reg; long freq = 0; reg = io_read(REG_AUDIO_FREQ); switch(reg & MASK_AUDIO_FREQ) { case 0x00: break; case 0x01: freq= 32000; break; case 0x02: freq= 44100; break; case 0x03: freq= 48000; break; case 0x04: freq= 88200; break; case 0x05: freq= 96000; break; case 0x06: freq=176400; break; case 0x07: freq=192000; break; } DPRINTK(0, "REG_AUDIO_FREQ=0x%02x: %ldHz\n", reg, freq); tda1997x->audio_mode.samplerate = freq; return 0; } /** detect an appropriate video mode from: * video input verticalPeriod, horizontalPeriod, and hsWidth * @returns resolution_data * */ static const resolution_data_t * tda1997x_detect_resolution(struct tda1997x_data *tda1997x) { u32 verticalPeriod; u16 horizontalPeriod; u16 hsWidth; char vPerCmp = 0, hPerCmp = 0, hsWidthCmp = 0; const resolution_data_t *res; int i; /* Read the FMT registers */ verticalPeriod = io_read24(REG_V_PER) & MASK_VPER; horizontalPeriod = io_read16(REG_H_PER) & MASK_HPER; hsWidth = io_read16(REG_HS_WIDTH) & MASK_HSWIDTH; DPRINTK(0,"verticalPeriod=%d horizontalPeriod=%d hsWidth=%d\n", verticalPeriod, horizontalPeriod, hsWidth); #if 1 // more details { videoFormatDetails fmt; videoFormatDetails *pFMT = &fmt; /* read the FMT registers */ pFMT->vsPolarity = io_read(REG_V_PER) & 0x80; pFMT->hsPolarity = io_read(REG_H_PER) & 0x80; pFMT->videoFormat = io_read(REG_HS_WIDTH) & 0x80; pFMT->horizontalTotalPeriod = io_read16(REG_FMT_H_TOT)&0x3fff; pFMT->horizontalVideoActiveWidth = io_read16(REG_FMT_H_ACT)&0x3fff; pFMT->horizontalFrontPorchWidth = io_read16(REG_FMT_H_FRONT)&0x3fff; pFMT->horizontalSyncWidthPixClk = io_read16(REG_FMT_H_SYNC)&0x3fff; pFMT->horizontalBackPorchWidth = io_read16(REG_FMT_H_BACK)&0x3fff; pFMT->verticalTotalPeriod = io_read16(REG_FMT_V_TOT)&0x3fff; pFMT->verticalVideoActiveWidth = io_read16(REG_FMT_V_ACT)&0x3fff; pFMT->verticalFrontPorchWidthF1 = io_read(REG_FMT_V_FRONT_F1); pFMT->verticalFrontPorchWidthF2 = io_read(REG_FMT_V_FRONT_F2); pFMT->verticalSyncWidth = io_read(REG_FMT_V_SYNC); pFMT->verticalBackPorchWidthF1 = io_read(REG_FMT_V_BACK_F1); pFMT->verticalBackPorchWidthF2 = io_read(REG_FMT_V_BACK_F2); pFMT->dataEnablePresent = io_read(REG_FMT_DE_ACT)&0x01; DPRINTK(2,"vsPolarity=%d hsPolarity=%d videoFormat=%d\n" "horizTotalPeriod=%d horizVideoActiveWidth=%d horizFrontPorchWidth=%d\n" "horizSyncWidthPixClk=%d horizBackPorchWidth=%d\n" "vertTotalPeriod=%d vertVideoActiveWidth=%d vertFrontPorchWidthF1=%d" "vertFrontPorchWidthF2=%d\n" "vertBackPorchWidthF1=%d vertBackPorchWidthF1=%d dataEnablePresent=%d\n", pFMT->vsPolarity, pFMT->hsPolarity, pFMT->videoFormat, pFMT->horizontalTotalPeriod, pFMT->horizontalVideoActiveWidth, pFMT->horizontalFrontPorchWidth, pFMT->horizontalSyncWidthPixClk, pFMT->horizontalBackPorchWidth, pFMT->verticalTotalPeriod, pFMT->verticalVideoActiveWidth, pFMT->verticalFrontPorchWidthF1, pFMT->verticalFrontPorchWidthF2, pFMT->verticalBackPorchWidthF1, pFMT->verticalBackPorchWidthF2, pFMT->dataEnablePresent); } #endif /* iterate over list of supported resolutions and find best match */ for (i = 0; i < ARRAY_SIZE(supported_res); i++) { res = &supported_res[i]; vPerCmp = (char) ((verticalPeriod >= res->verticalPeriodMin) && (verticalPeriod <= res->verticalPeriodMax)); hPerCmp = (char) ((horizontalPeriod >= res->horizontalPeriodMin) && (horizontalPeriod <= res->horizontalPeriodMax)); hsWidthCmp = (char) ((hsWidth >= res->hsWidthMin) && (hsWidth <= res->hsWidthMax)); DPRINTK(1,"\t%02d: %dx%d@%d%c: %d/%d/%d\n", i, res->width, res->height, res->horizfreq, res->interlaced?'i':'p', vPerCmp, hPerCmp, hsWidthCmp); if (vPerCmp && hPerCmp && hsWidthCmp) { int pixrate; /* resolutiontype used to determine Default Colorimetry */ switch (res->height) { case 480: case 576: case 240: case 288: tda1997x->resolutiontype = RESTYPE_SDTV; break; case 720: case 1080: tda1997x->resolutiontype = RESTYPE_HDTV; break; default: tda1997x->resolutiontype = RESTYPE_PC; } printk(KERN_INFO "%s: matched resolution: %dx%d%c@%d %s\n", KBUILD_MODNAME, res->width, res->height, res->interlaced?'i':'p', res->horizfreq, restype_names[tda1997x->resolutiontype]); /* validate input mode */ pixrate = (res->width * res->height * res->horizfreq) / 1000000; if (res->interlaced) pixrate /= 2; switch(tda1997x->pdata->vidout_format) { case VIDEOFMT_444: case VIDEOFMT_422_SMP: /* FIXME: have not figured out how to get HS output asserted on 2nd field */ if (res->interlaced) { printk(KERN_INFO "%s: Error %s: interlaced not supported\n", KBUILD_MODNAME, vidfmt_names[tda1997x->pdata->vidout_format]); return NULL; } break; case VIDEOFMT_422_CCIR: /* BT656 requires 2-clocks per pixel */ pixrate *= 2; break; } if (tda1997x->pdata->max_pixel_rate && (pixrate > tda1997x->pdata->max_pixel_rate)) { printk(KERN_INFO "%s: Error: %dMP/s exceeds max of %dMP/s\n", KBUILD_MODNAME, pixrate, tda1997x->pdata->max_pixel_rate); return NULL; } return res; } } printk(KERN_ERR "%s: found no video resolution match for: %d/%d/%d\n", KBUILD_MODNAME, verticalPeriod, horizontalPeriod, hsWidth); return NULL; } /** read input activity registers * * @returns activity_sate bitmask: * 7: unused * 6: unused * 5: unused * 4: tmds_locked * 3: inputD_clock_stable (unsupported) * 2: inputC_clock_stable (unsupported) * 1: inputB_clock_stable * 0: inputA_clock_stable */ static u8 tda1997x_read_activity_status_regs(void) { u8 reg, status = 0; /* Activity detection must only be notified when stable_clk_x AND active_x * bits are set to 1. If only stable_clk_x bit is set to 1 but not * active_x, it means that the TMDS clock is not in the defined range, * so activity detection must not be notified * => regStatus must be set to 0 in that case. If stable_clk_x bit is * set to 0, regStatus must also be set to 0 */ /* Read CLK_A_STATUS register */ reg = io_read(REG_CLK_A_STATUS); /* when stable_clk_x is set to 1, check active_x bit */ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE) ) reg &= ~MASK_CLK_STABLE; status |= ((reg & MASK_CLK_STABLE) >> 2); /* Read CLK_B_STATUS register */ reg = io_read(REG_CLK_B_STATUS); /* when stable_clk_x is set to 1, check active_x bit */ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE) ) reg &= ~MASK_CLK_STABLE; status |= ((reg & MASK_CLK_STABLE) >> 1); /* Read the SUS_STATUS register */ reg = io_read(REG_SUS_STATUS); /* If state = 5 => TMDS is locked */ if ( (reg & MASK_SUS_STATE_VALUE) == LAST_STATE_REACHED) status |= MASK_SUS_STATE_BIT; else status &= ~MASK_SUS_STATE_BIT; return status; } /** parse an infoframe and do some sanity checks on it * * @type of inforframe * @returns 0 on success */ static unsigned int tda1997x_parse_infoframe(struct tda1997x_data *tda1997x, int type) { u8 d[MAX_IF_DATA]; u8 crc; int len = 0; int i; /* determine length based on type */ switch(type) { case MPS_IF_TYPE: len = MPS_IF_NB_DATA; break; case AUD_IF_TYPE: len = AUD_IF_NB_DATA; break; case SPD_IF_TYPE: len = SPD_IF_NB_DATA; break; case AVI_IF_TYPE: len = AVI_IF_NB_DATA; break; case VS_HDMI_IF_TYPE: case VS_BK1_IF_TYPE: case VS_BK2_IF_TYPE: len = VS_IF_NB; break; } /* read data */ if (io_readn(type, len, d) != len) { printk(KERN_ERR "infoframe type0x%02x failed read\n", type); return -1; } /* verify crc */ for (i = 0, crc = 0; i < len; i++) crc += d[i]; if (crc) { printk(KERN_ERR "infoframe type0x%02x failed CRC\n", type); return -2; } /* parse details */ switch(type) { case MPS_IF_TYPE: /* parse infoframe to get bitrate, fieldrepeat, MPEG_Frame */ DPRINTK(0,"\t\tbitrate=%d,%d,%d,%d fieldRepeat=%d MPEG_Frame=%d\n", d[4], d[5], d[6], d[7], d[8] & 0x10, d[8] & 0x03); break; /* Audio InfoFrame: see HDMI spec 8.2.2 */ case AUD_IF_TYPE: /* * CC0..CC2 - Channel Count. See CEA-861-D table 17 * CT0..CT3 - Coding Type. The CT bits shall always be 0 (use Stream Header) * SS0..SS1 - Sample Size. The SS bits shall always be 0 (use Stream Header) * SF0..SF2 - Sample Freq. See CEA-861-D table 18. * - For L-PCM and IEC 61937 compressed audio streams the SF bits * shall always be 0 (use Stream Header). * - For One Bit Audio and DST streams, the SF bits shall equal * the ACR fs value. * - For Super Audio CD, the SF bits are typically 0, 1, 0 * indicating a sample freq of 2.8224MSamples/s (64*44.1kHz) * CA0..CA7 - Channel/Speaker Allocation. See CEA-861-D Section 6.6.2 * - this is not valid for IEC 61937 compressed audio streams * LSV0..LSV3 - Level Shif Value (for downmixing). See CEA-861-D 6.6.2 * and CEA-861-D table 21 * DM_INH - Downmix inibit. See CEA-861-D sec 6.6.2 and table 22 * The DM_INH field is to be set only for DVD-Audio * * CT, SS, and SF values of 0 indicate that these items are carried in the * audio stream itself. */ DPRINTK(0,"\t\tcodingType=%d channelCount=%d samplefrequency=%d " "samplesize=%d dataByte3=%d channelAllocation=%d downmixInhibit=%d " "levelShiftValue=%d\n", (d[4] & 0xf0) >> 4, /* CT3, CT2, CT1, CT0 */ (d[4] & 0x07), /* CC2, CC1, CC0 */ (d[5] & 0x1c) >> 2, /* SF2, SF1, SF0 */ (d[5] & 0x03), /* SS1, SS0 */ d[6], d[7], /* CA7 .. CA0 */ (d[8] & 0x80) >> 7, /* DM_INH */ (d[8] & 0x78) >> 3); /* LSV3, LSV2, LSV1, LSV0 */ /* Channel Count */ tda1997x->source_channels = (d[4] & 0x07) + 1; DPRINTK(0, "Audio Channels: %d\n", tda1997x->source_channels); /* Channel Assignment */ if ( (d[7] <= 0x1f) /* MAX_CHANNEL_ALLOC */ && (d[7] != tda1997x->channel_assignment) && !tda1997x->pdata->audio_force_channel_assignment) { /* use the channel assignment from the audio infoframe */ DPRINTK(0, "channel assignment changed: %d\n", d[7]); tda1997x->channel_assignment = d[7]; /* configure audio output */ tda1997x_configure_audio_formatter(tda1997x->pdata, tda1997x->channel_assignment); /* reset the audio FIFO */ tda1997x_hdmi_info_reset(RESET_AUDIO, 0); //tda1997x_hdmi_info_reset(0x00, 0); } break; /* Source Product Descriptor information (SPD) */ case SPD_IF_TYPE: for (i = 0; i < 8; i++) tda1997x->vendor[i] = d[4 + i]; for (i = 0; i < 16; i++) tda1997x->product[i] = d[4 + 8 + i]; printk(KERN_INFO "%s: Source Product Descriptor: %s %s\n", KBUILD_MODNAME, tda1997x->vendor, tda1997x->product); break; /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ case AVI_IF_TYPE: { u8 pixel_repetitionfactor; u8 reg; if (d[0] != 0x82) { printk(KERN_ERR "INFOFRAME AVI: wrong packet type! (0x%02x)\n", d[0]); } DPRINTK(0,"\t\tcolorIndicator=%d activeInfoPresent=%d " "barInfomationDataValid=%d scanInformation=%d colorimetry=%d " "pictureAspectRatio=%d activeFormatAspectRation=%d " "nonUinformPictureScaling=%d videoFormatIdentificationCode=%d " "pixelRepetitionFactor=%d\n", (d[4] & 0x60) >> 5, /* colorspace: Y1, Y0 */ (d[4] & 0x10) >> 4, /* activeInfoPresent: A0 */ (d[4] & 0x0c) >> 2, /* barInformationValid: B1, B0 */ (d[4] & 0x03), /* scanInformation: S1, S0 */ (d[5] & 0xc0) >> 6, /* coloriemtry: C1, C0 */ (d[5] & 0x30) >> 4, /* pictureAspectRatio: M1, M0 */ (d[5] & 0x0f), /* activeFormatAspectRatio: R3, R2, R1, R0 */ (d[6] & 0x03), /* nonUniformPictureScaling: SC1, SC0 */ (d[7] & 0x7f), /* videoFormatID: VIC6, VIC5, VIC4 */ (d[8] & 0x0f) /* pixelRepetitionFactor: PR3, PR2, PR1, PR0 */ ); tda1997x->colorspace = (d[4] & 0x60) >> 5; /* Y1, Y0 */ tda1997x->colorimetry = (d[5] & 0xc0) >> 6; /* C1, C0 */ pixel_repetitionfactor = d[8] & 0x0f; /* PR3, PR2, PR1, PR0 */ /* If colorimetry not specified, conversion depends on resolutiontype: * - SDTV: ITU601 for SD (480/576/240/288 line resolution) * - HDTV: ITU709 for HD (720/1080 line resolution) * - PC: sRGB * see HDMI specification section 6.7 */ if ( (tda1997x->colorspace == COLORSPACE_YCBCR_422 || tda1997x->colorspace == COLORSPACE_YCBCR_444) && (tda1997x->colorimetry == COLORIMETRY_XVYCC || tda1997x->colorimetry == COLORIMETRY_NONE) ) { if (tda1997x->resolutiontype == RESTYPE_HDTV) tda1997x->colorimetry = COLORIMETRY_ITU709; else if (tda1997x->resolutiontype == RESTYPE_SDTV) tda1997x->colorimetry = COLORIMETRY_ITU601; else tda1997x->colorimetry = COLORIMETRY_NONE; dev_info(&tda1997x->client->dev, "invalid/undefined colorimetry defaulted to %s (%s)\n", colorimetry_names[tda1997x->colorimetry], restype_names[tda1997x->resolutiontype]); } /* configure upsampler per sample format */ /* ConfigureUpDownSampler: 0=bypass 1=repeatchroma 2=interpolate */ reg = io_read(REG_PIX_REPEAT); reg = (reg & ~0x30 /* MASK_UP_SEL */); if (tda1997x->colorspace == COLORSPACE_YCBCR_422) reg |= (1 << 4); /* repeatchroma */ io_write(REG_PIX_REPEAT, reg); /* ConfigurePixelRepeater - repeat n-times each pixel */ reg = io_read(REG_PIX_REPEAT); reg = (reg & ~0x0f /*MASK_PIX_REP*/) | pixel_repetitionfactor; io_write(REG_PIX_REPEAT, reg); /* configure the receiver with the new colorspace */ tda1997x_configure_conversion(tda1997x, tda1997x->colorspace, tda1997x->colorimetry, tda1997x->pdata->vidout_format); } break; case VS_HDMI_IF_TYPE: case VS_BK1_IF_TYPE: case VS_BK2_IF_TYPE: /* read update flag and store at the end */ d[VS_IF_NB] = io_read(type); if (type == VS_HDMI_IF_TYPE && d[VS_IF_NB] > 3) /* HDMI_INFO_EXCEED */ { DPRINTK(0,"HDMI_INFO_EXCEED\n"); return -3; } DPRINTK(0, "\t\tieee_id[0]=%d ieee_id[1]=%d ieee_id[2]=%d\n",d[0],d[1],d[2]); break; } return 0; } /* tda1997x_work - deferred work procedure for handling interrupt */ static void tda1997x_work(struct work_struct *work) { struct tda1997x_data *tda1997x = &tda1997x_data; u8 reg, interrupt_top_flags, source; do { /* read interrupt flags */ interrupt_top_flags = io_read(REG_INT_FLG_CLR_TOP); if (interrupt_top_flags == 0) break; DPRINTK(0,"interrupt:0x%02x\n", interrupt_top_flags); /* SUS interrupt source (Input activity events) */ if (interrupt_top_flags & INTERRUPT_SUS) { source = io_read(REG_INT_FLG_CLR_SUS); io_write(REG_INT_FLG_CLR_SUS, source); DPRINTK(0,"SUS: 0x%02x\n", source); if (source & MASK_MPT_BIT) { DPRINTK(0,"\tConfig MTP end of process\n"); /* reset MTP in use flag if set */ if (tda1997x->mptrw_in_progress) tda1997x->mptrw_in_progress = 0; } if (source & MASK_SUS_END_BIT) { /* reset audio FIFO */ reg = io_read(REG_HDMI_INFO_RST); reg |= MASK_SR_FIFO_FIFO_CTRL; io_write(REG_HDMI_INFO_RST, reg); reg &= ~MASK_SR_FIFO_FIFO_CTRL; io_write(REG_HDMI_INFO_RST, reg); DPRINTK(0,"\tRESET AUDIO SUS_END\n"); /* reset HDMI flags memory and vsi_received flag */ tda1997x->hdmi_status = 0; tda1997x->vsi_received = 0; } /* filter FMT interrupt based on SUS state */ reg = io_read(REG_SUS_STATUS); if ( ((reg & MASK_SUS_STATE_VALUE) != LAST_STATE_REACHED) || (source & MASK_MPT_BIT)) { DPRINTK(0,"sus_state_value=0x%02x filter video fmt changed\n", reg & MASK_SUS_STATE_VALUE); source &= ~MASK_FMT_BIT; } if (source & (MASK_FMT_BIT | MASK_SUS_END_BIT)) { const resolution_data_t *res; DPRINTK(0, "\tHDMI LOCKED\n"); reg = io_read(REG_SUS_STATUS); if ((reg & MASK_SUS_STATE_VALUE) != LAST_STATE_REACHED) { printk(KERN_ERR "%s: BAD SUS STATUS\n", KBUILD_MODNAME); continue; } /* There is a new activity, the status for HDCP repeater state */ tda1997x->state_c5_reached = 0; /* Detect the new resolution */ res = tda1997x_detect_resolution(tda1997x); if (res) { tda1997x->video_mode.width = res->width; tda1997x->video_mode.height = res->height; tda1997x->video_mode.fps = res->horizfreq; tda1997x->video_mode.interlaced = res->interlaced; tda1997x->video_mode.signal = 1; /* configure the active input to the given resolution */ tda1997x_configure_input_resolution(res->resolutionID); } else { tda1997x->video_mode.width = 0; tda1997x->video_mode.height = 0; tda1997x->video_mode.fps = 0; tda1997x->video_mode.interlaced = 0; tda1997x->video_mode.signal = 1; } /* on 'input locked' event, RGB colorspace is forced (the AVI infoframe * is not received yet at this moment) * if AVI infoframe is received later, the colorspace will be * reconfigured in the AVI infoframe handler */ tda1997x->colorspace = COLORSPACE_RGB; tda1997x->colorimetry = COLORIMETRY_NONE; /* bypass colorspace conversion */ io_write(REG_VDP_CTRL, io_read(REG_VDP_CTRL) | (1<<0)); /* SetBlankingCodes */ io_write16(REG_BLK_GY, RGBBlankingCode.blankingCodeGy); io_write16(REG_BLK_BU, RGBBlankingCode.blankingCodeBu); io_write16(REG_BLK_RV, RGBBlankingCode.blankingCodeRv); /* set the state machine */ tda1997x->state = STATE_LOCKED; } if (source & MASK_RT_PULSE_BIT) { DPRINTK(0,"\tEnd of termination resistance pulse\n"); } if (source & MASK_SUS_ACT_BIT) { DPRINTK(0,"\tActivity of selected input changed\n"); } if (source & MASK_SUS_CH_BIT) { DPRINTK(0,"\tSelected input changed\n"); } if (source & MASK_SUS_ST_BIT) { DPRINTK(0,"\tSUS state changed\n"); } } /* DDC interrupt source (Display Data Channel) */ else if (interrupt_top_flags & INTERRUPT_DDC ) { source = io_read(REG_INT_FLG_CLR_DDC ); io_write(REG_INT_FLG_CLR_DDC, source); DPRINTK(0,"DDC: 0x%02x\n", source); if (source & MASK_EDID_MTP) { DPRINTK(0,"\tEDID MTP end of process\n"); /* reset MTP in use flag if set */ if (tda1997x->mptrw_in_progress) tda1997x->mptrw_in_progress = 0; } /* we don't care about these */ if (source & MASK_DDC_ERR) { DPRINTK(0,"\tmaster DDC error\n"); } if (source & MASK_DDC_CMD_DONE) { DPRINTK(0,"\tmaster DDC cmd send correct\n"); } if (source & MASK_READ_DONE) { DPRINTK(0,"\tEnd of Down EDID read\n"); } if (source & MASK_RX_DDC_SW) { DPRINTK(0,"\tOutput DDC switching finished\n"); } if (source & MASK_HDCP_DDC_SW) { DPRINTK(0,"\tHDCP DDC switching finished\n"); } if (source & MASK_HDP_PULSE_END) { DPRINTK(0,"\tEnd of Hot Plug Detect pulse\n"); } if (source & MASK_DET_5V) { DPRINTK(0,"\tDetected +5V\n"); } } /* RATE interrupt source (Digital Input A/B activity: rate/presence/drift) */ else if (interrupt_top_flags & INTERRUPT_RATE) { u8 irq_status, last_irq_status; source = io_read(REG_INT_FLG_CLR_RATE); io_write(REG_INT_FLG_CLR_RATE, source); DPRINTK(0,"RATE: 0x%02x\n", source); /* read status regs */ last_irq_status = irq_status = tda1997x_read_activity_status_regs(); /* read clock status reg until INT_FLG_CLR_RATE is still 0 * after the read to make sure its the last one */ reg = source; while (reg != 0) { irq_status = tda1997x_read_activity_status_regs(); reg = io_read(REG_INT_FLG_CLR_RATE); io_write(REG_INT_FLG_CLR_RATE, reg); source |= reg; } /* we don't use these indicators */ #if 0 if (source & MASK_RATE_B_DRIFT) { DPRINTK(0, "\tRate measrement of input B drifted\n"); } if (source & MASK_RATE_B_ACT) { DPRINTK(0, "\tRate measurement of input B activity change\n"); } if (source & MASK_RATE_B_PST) { DPRINTK(0, "\tRate measurement of input B presence change\n"); } if (source & MASK_RATE_A_DRIFT) { DPRINTK(0, "\tRate measrement of input A drifted\n"); } if (source & MASK_RATE_A_ACT) { DPRINTK(0, "\tRate measurement of input A activity change\n"); } if (source & MASK_RATE_A_PST) { DPRINTK(0, "\tRate measurement of input A presence change\n"); } #endif /* we only pay attention to stability change events */ if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { int input = (source & MASK_RATE_A_ST)?0:1; u8 mask = 1<activity_status_reg); /* state change */ if ((irq_status & mask) != (tda1997x->activity_status_reg & mask)) { /* activity lost */ if ( (irq_status & mask) == 0) { printk(KERN_INFO "%s: HDMI-%c: Digital Activity Lost\n", KBUILD_MODNAME, input+'A'); tda1997x->state = STATE_UNLOCKED; tda1997x->input_detect[input] = 0; tda1997x->hdmi_status = 0; tda1997x->hdmi_detected = 0; tda1997x->hdcp_detected = 0; tda1997x->eess_detected = 0; /* ConfigureUpDownSampler: 0=bypass 1=repeatchroma 2=interpolate */ reg = io_read(REG_PIX_REPEAT); reg = (reg & ~0x30 /*MASK_UP_SEL*/) | (0 << 4); /* bypass */ io_write(REG_PIX_REPEAT, reg); /* ConfigurePixelRepeater - repeat n-times each pixel */ reg = io_read(REG_PIX_REPEAT); reg = (reg & ~0x0f /*MASK_PIX_REP*/) | 0; /* 0-times: disable */ io_write(REG_PIX_REPEAT, reg); if (tda1997x->chip_revision == 0) { /* Clear HDMI mode flag in BCAPS (for N1) */ io_write(REG_CLK_CFG, 0x03); io_write(REG_PON_OVR_EN, 0x01); io_write(REG_PON_CBIAS, 0x01); io_write(REG_PON_PLL, 0x01); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; reg |= 0x02; io_write(REG_MODE_RECOVER_CFG1, reg); io_write(REG_CLK_CFG, 0x00); io_write(REG_PON_OVR_EN, 0x00); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; io_write(REG_MODE_RECOVER_CFG1, reg); } tda1997x->video_mode.width = 0; tda1997x->video_mode.height = 0; tda1997x->video_mode.fps = 0; tda1997x->video_mode.interlaced = 0; tda1997x->video_mode.signal = 0; } else { printk(KERN_INFO "%s: HDMI-%c: Digital Activity Detected\n", KBUILD_MODNAME, input+'A'); tda1997x->input_detect[input] = 1; } /* hold onto current state */ tda1997x->activity_status_reg = (irq_status & mask); } } } /* MODE interrupt source (Gamut, ISRC2, ISRC1, ACP, GCP, Deep color flags) */ else if (interrupt_top_flags & INTERRUPT_MODE) { source = io_read(REG_INT_FLG_CLR_MODE); io_write(REG_INT_FLG_CLR_MODE, source); DPRINTK(0,"MODE: 0x%02x\n", source); /* special processing for HDMI Flag IT */ if (source & MASK_HDMI_FLG) { u8 statusChange; reg = io_read(REG_HDMI_FLAGS); reg &= 0x7c; /* ignore fifo_fail and fifo_warning bits */ statusChange = tda1997x->hdmi_status ^ reg; DPRINTK(0,"\tHDMI_FLAGS:0x%02x prev=0x%02x changed=0x%02x\n", reg, tda1997x->hdmi_status, statusChange); if (statusChange) { tda1997x->hdmi_status = reg; /* Get HDMI Status */ if (statusChange & 0x80) DPRINTK(0,"\t\tAUDIOFLAG - Audio packet in last videoframe\n"); if (statusChange & 0x40) DPRINTK(0,"\t\tHDMI mode detected\n"); tda1997x->hdmi_detected = 1; if (statusChange & 0x20) DPRINTK(0,"\t\tEESS mode detected\n"); tda1997x->eess_detected = 1; if (statusChange & 0x10) DPRINTK(0,"\t\tHDCP encryption detected\n"); tda1997x->hdcp_detected = 1; if (statusChange & 0x08) DPRINTK(0,"\t\tAVMUTE\n"); if (statusChange & 0x04) DPRINTK(0,"\t\tLayout status Audio Sample Packet\n"); if (statusChange & 0x02) DPRINTK(0,"\t\tFIFO read/write pointers are crossed\n"); if (statusChange & 0x01) DPRINTK(0,"\t\tFIFO read ptr closer than 2 samples from write ptr\n"); } } if (source & MASK_GAMUT) { u8 d[GDB_PACKET_HDR_LEN + GDB_PACKET_DAT_LEN]; io_readn(REG_GBD_PACKET_TYPE, sizeof(d), d); DPRINTK(0,"\tGamut packet: type=%d nextField=%d GBDProfile=%d " "affectedGamutSeqNum=%d noCrntGBD=%d packetSeq=%d curSeq=%d\n", d[0], /* type */ (d[1] & 0x80) >> 7, /* nextField */ (d[1] & 0x70) >> 4, /* GBDProfile */ (d[1] & 0x0f), /* affected Gamut SeqNum */ (d[2] & 0x80) >> 7, /* noCrntGBD */ (d[2] & 0x30) >> 4, /* packet SeqNum */ (d[2] & 0x0f) /* current SeqNum */ ); } #if 0 if (source & MASK_ISRC2) { DPRINTK(0,"\tISRC2 packet\n"); if (1) // untested { u8 d[ISRC_PACKET_HDR_LEN + ISRC_PACKET_DAT_LEN]; u8 crc = 0, i; io_readn(REG_ISRC2_PACKET_TYPE, sizeof(d), d); for (i = 0; i < sizeof(d); i++) crc += d[i]; if (crc) { printk(KERN_ERR "%s: ISRC2 packet CRC failed\n", KBUILD_MODNAME); } } } if (source & MASK_ISRC1) { DPRINTK(0,"\tISRC1 packet\n"); if (1) // untested { u8 d[ISRC_PACKET_HDR_LEN + ISRC_PACKET_DAT_LEN]; u8 crc = 0, i; io_readn(REG_ISRC1_PACKET_TYPE, sizeof(d), d); for (i = 0; i < sizeof(d); i++) crc += d[i]; if (crc) { printk(KERN_ERR "%s: ISRC1 packet CRC failed\n", KBUILD_MODNAME); } else { DPRINTK(0,"\tISRC1 packet: type=%d ISRCCont=%d ISRCValid=%d " "ISRCStatus=%d\n", d[0], (d[1] & 0x80) >> 7, (d[1] & 0x40) >> 6, (d[1] & 0x07) ); } } } if (source & MASK_ACP) { DPRINTK(0,"\tAudio Content Protection (ACP) Packet\n"); if (1) // untested { u8 d[ACP_PACKET_HDR_LEN + ACP_PACKET_DAT_LEN]; u8 crc = 0, i; io_readn(REG_ACP_PACKET_TYPE, sizeof(d), d); for (i = 0; i < sizeof(d); i++) crc += d[i]; if (crc) { printk(KERN_ERR "%s: ACP packet CRC failed\n", KBUILD_MODNAME); } } } #endif if (source & MASK_DC_NO_GCP) { DPRINTK(0,"\tGCP not received in 5 frames\n"); } if (source & MASK_DC_PHASE) { DPRINTK(0,"\tDeep color mode pixel phase needs update\n"); } if (source & MASK_DC_MODE) { reg = io_read(REG_DEEP_COLOR_MODE); DPRINTK(0,"\tDeep color mode color depth changed: " "pixelPackingPhase=0x%02x mode=%d\n", reg & MASK_DC_PIXEL_PHASE, reg & MASK_DC_COLOR_DEPTH); } } /* Infoframe change interrupt source */ else if (interrupt_top_flags & INTERRUPT_INFO ) { source = io_read(REG_INT_FLG_CLR_INFO); io_write(REG_INT_FLG_CLR_INFO, source); DPRINTK(0,"INFO: 0x%02x\n", source); /* Vendor-Specific Infoframe */ if (source & MASK_VS_IF_OTHER_BK2 || source & MASK_VS_IF_OTHER_BK1 || source & MASK_VS_IF_HDMI) { u8 VSIUpdate, VSOther1Update, VSOther2Update; /* Read the Update registers */ VSIUpdate = io_read(VS_HDMI_IF_UPDATE); VSOther1Update = io_read(VS_BK1_IF_UPDATE); VSOther2Update = io_read(VS_BK2_IF_UPDATE); /* discard ITs that are too old */ if (VSIUpdate >= 3) source &= ~MASK_VS_IF_HDMI; if (VSOther1Update >= 3) source &= ~MASK_VS_IF_OTHER_BK1; if (VSOther2Update >= 3) source &= ~MASK_VS_IF_OTHER_BK2; /* No new VSI has been received if these 3 registers are > 3 */ if (tda1997x->vsi_received && (VSIUpdate>=3) && (VSOther1Update>=3) && (VSOther2Update>=3)) { /* false VSI received */ tda1997x->vsi_received = 0; } else { tda1997x->vsi_received = 1; if (source & MASK_VS_IF_HDMI) { DPRINTK(0,"\tVendor-Specific InfoFrame: HDMI\n"); tda1997x_parse_infoframe(tda1997x, VS_HDMI_IF_TYPE); } if (source & MASK_VS_IF_OTHER_BK1) { DPRINTK(0,"\tVendor-Specific InfoFrame: BK1\n"); tda1997x_parse_infoframe(tda1997x, VS_BK1_IF_TYPE); } if (source & MASK_VS_IF_OTHER_BK2) { DPRINTK(0,"\tVendor-Specific InfoFrame: BK2\n"); tda1997x_parse_infoframe(tda1997x, VS_BK2_IF_TYPE); } } } /* MPEG Source Product infoframe */ if (source & MASK_MPS_IF) { DPRINTK(0, "\tMPEG Source Product InfoFrame content change\n"); tda1997x_parse_infoframe(tda1997x, MPS_IF_TYPE); } /* Audio infoframe */ if (source & MASK_AUD_IF) { DPRINTK(0, "\tAudio InfoFrame content change\n"); tda1997x_parse_infoframe(tda1997x, AUD_IF_TYPE); } /* Source Product Descriptor infoframe change */ if (source & MASK_SPD_IF) { DPRINTK(0, "\tSource Product Descriptor InfoFrame change\n"); tda1997x_parse_infoframe(tda1997x, SPD_IF_TYPE); } /* Auxillary Video Information infoframe */ if (source & MASK_AVI_IF) { DPRINTK(0, "\tAuxiliary Video information InfoFrame change\n"); tda1997x_parse_infoframe(tda1997x, AVI_IF_TYPE); } } /* Audio interrupt source: * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err */ else if (interrupt_top_flags & INTERRUPT_AUDIO ) { source = io_read(REG_INT_FLG_CLR_AUDIO); io_write(REG_INT_FLG_CLR_AUDIO, source); DPRINTK(0,"AUDIO: 0x%02x\n", source); if (source & MASK_ERROR_FIFO_PT || /* Audio FIFO pointer error */ source & MASK_MUTE_FLG) /* Audio mute */ { if (source & MASK_MUTE_FLG) { DPRINTK(0, "\tAudio MUTE\n"); } else { DPRINTK(0, "\tAudio FIFO error\n"); } /* audio reset audio FIFO */ reg = io_read(REG_SUS_STATUS); if ((reg & MASK_SUS_STATE_VALUE) == LAST_STATE_REACHED) { reg = io_read(REG_HDMI_INFO_RST); reg |= MASK_SR_FIFO_FIFO_CTRL; io_write(REG_HDMI_INFO_RST, reg); reg &= ~MASK_SR_FIFO_FIFO_CTRL; io_write(REG_HDMI_INFO_RST, reg); /* reset channel status IT if present */ source &= ~(MASK_CH_STATE); } } if (source & MASK_AUDIO_FREQ_FLG) { DPRINTK(0, "\tAudio freq change\n"); tda1997x_get_audio_frequency(tda1997x); printk(KERN_INFO "%s: Audio Frequency Change: %dHz\n", KBUILD_MODNAME, tda1997x->audio_mode.samplerate); } if (source & MASK_AUDIO_FLG) { reg = io_read(REG_AUDIO_FLAGS); DPRINTK(0, "\tAudio flag: 0x%02x\n", reg); if (reg & 0x08) { DPRINTK(0, "\t\tDTS packets detected\n"); } if (reg & 0x04) { DPRINTK(0, "\t\tOBA packets detected\n"); } if (reg & 0x02) { DPRINTK(0, "\t\tHBR packets detected\n"); } if (reg & 0x01) { DPRINTK(0, "\t\tAudio sample packets detected\n"); } } if (source & MASK_CH_STATE) { DPRINTK(0, "\tChannel status\n"); } if (source & MASK_UNMUTE_FIFO) { DPRINTK(0, "\tUnmute audio FIFO\n"); } } /* HDCP interrupt source (content protection) */ if (interrupt_top_flags & INTERRUPT_HDCP) { source = io_read(REG_INT_FLG_CLR_HDCP); io_write(REG_INT_FLG_CLR_HDCP, source); DPRINTK(0,"HDCP: 0x%02x\n", source); /* reset MTP in use flag if set */ if (source & MASK_HDCP_MTP) { DPRINTK(0, "\tHDCP MTP in use\n"); tda1997x->mptrw_in_progress = 0; } if (source & MASK_HDCP_DLMTP) { DPRINTK(0,"\tHDCP end download MTP to SRAM\n"); } if (source & MASK_HDCP_DLRAM) { DPRINTK(0,"\tHDCP end download keys from SRAM to HDCP core\n"); } if (source & MASK_HDCP_ENC) { DPRINTK(0,"\tHDCP_ENC\n"); } if (source & MASK_STATE_C5) { DPRINTK(0,"\tHDCP State C5 reached\n"); /* REPEATER: mask AUDIO and IF interrupts to avoid IF during auth */ reg = io_read(REG_INT_MASK_TOP); reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); io_write(REG_INT_MASK_TOP, reg); interrupt_top_flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); } if (source & MASK_AKSV) { DPRINTK(0,"\tAKSV received (Start of Authentication)\n"); } } /* AFE interrupt source */ else if (interrupt_top_flags & INTERRUPT_AFE ) { source = io_read(REG_INT_FLG_CLR_AFE); io_write(REG_INT_FLG_CLR_AFE, source); DPRINTK(0,"AFE: 0x%02x\n", source); } } while (interrupt_top_flags != 0); /* we handled all alerts; re-enable level-triggered IRQ */ enable_irq(tda1997x->irq); } /** tda1997x interrupt handler */ static irqreturn_t tda1997x_isr(int irq, void *d) { struct tda1997x_data *tda1997x = d; /* disable level-triggered IRQs until we handle them */ disable_irq_nosync(irq); schedule_work(&tda1997x->work); return IRQ_HANDLED; } /*********************************************************************** * I2C client and driver. ***********************************************************************/ /** * tda1997x I2C detach function. * Called on rmmod. * * @param *client struct i2c_client*. * @return Error code indicating success or failure. */ static int tda1997x_remove(struct i2c_client *client) { struct tda1997x_data *tda1997x = i2c_get_clientdata(client); dev_dbg(&tda1997x_data.client->dev, "%s:Removing tda1997x video decoder @ 0x%02X from adapter %s\n", __func__, client->addr << 1, client->adapter->name); tda1997x_power_mode(&tda1997x_data, 0); if (client->irq) { devm_free_irq(&client->dev, client->irq, &tda1997x_data); } /* if (tda1997x->vid_child) { platform_device_del(tda1997x->vid_child); } */ if (tda1997x->client_cec) i2c_unregister_device(tda1997x->client_cec); sysfs_remove_group(&client->dev.kobj, &attr_group); if (dvddio_regulator) regulator_disable(dvddio_regulator); if (dvdd_regulator) regulator_disable(dvdd_regulator); if (avdd_regulator) regulator_disable(avdd_regulator); return 0; } static int tda1997x_regulator_enable(struct device *dev, struct tda1997x_platform_data *pdata) { int ret = 0; dvddio_regulator = devm_regulator_get(dev, "DOVDD"); if (!IS_ERR(dvddio_regulator)) { regulator_set_voltage(dvddio_regulator, TDA1997X_VOLTAGE_DIGITAL_IO, TDA1997X_VOLTAGE_DIGITAL_IO); ret = regulator_enable(dvddio_regulator); if (ret) { dev_err(dev, "set io voltage failed\n"); return ret; } else { dev_dbg(dev, "set io voltage ok\n"); } } else { dev_warn(dev, "cannot get io voltage\n"); } dvdd_regulator = devm_regulator_get(dev, "DVDD"); if (!IS_ERR(dvdd_regulator)) { ret = regulator_set_voltage(dvdd_regulator, TDA1997X_VOLTAGE_DIGITAL_CORE, TDA1997X_VOLTAGE_DIGITAL_CORE); ret = regulator_enable(dvdd_regulator); if (ret) { dev_err(dev, "set core voltage failed\n"); return ret; } else { dev_dbg(dev, "set core voltage ok\n"); } } else { dev_warn(dev, "cannot get core voltage\n"); } avdd_regulator = devm_regulator_get(dev, "AVDD"); if (!IS_ERR(avdd_regulator)) { ret = regulator_set_voltage(avdd_regulator, TDA1997X_VOLTAGE_ANALOG, TDA1997X_VOLTAGE_ANALOG); ret = regulator_enable(avdd_regulator); if (ret) { dev_err(dev, "set analog voltage failed\n"); return ret; } else { dev_dbg(dev, "set analog voltage ok\n"); } } else { dev_warn(dev, "cannot get analog voltage\n"); } return ret; } static int parse_vidout_fmt(const char *mode) { int clkmode; if (!strcmp(mode, "444")) clkmode = VIDEOFMT_444; else if (!strcmp(mode, "422_smp")) clkmode = VIDEOFMT_422_SMP; else if (!strcmp(mode, "422_ccir")) clkmode = VIDEOFMT_422_CCIR; else clkmode = -EINVAL; return clkmode; } static int parse_vidout_clkmode(const char *mode) { int clkmode; if (!strcmp(mode, "single_edge")) clkmode = CLOCK_SINGLE_EDGE; else if (!strcmp(mode, "dual_edge")) clkmode = CLOCK_DUAL_EDGE; else if (!strcmp(mode, "single_edge_toggled")) clkmode = CLOCK_SINGLE_EDGE_TOGGLED; else if (!strcmp(mode, "dual_edge_toggled")) clkmode = CLOCK_DUAL_EDGE_TOGGLED; else clkmode = -EINVAL; return clkmode; } static int parse_audout_fmt(const char *mode) { int clkmode; if (!strcmp(mode, "i2s16")) clkmode = AUDIO_FMT_I2S16; else if (!strcmp(mode, "i2s32")) clkmode = AUDIO_FMT_I2S32; else if (!strcmp(mode, "spdif")) clkmode = AUDIO_FMT_SPDIF; else if (!strcmp(mode, "oba")) clkmode = AUDIO_FMT_OBA; else if (!strcmp(mode, "i2s16_hbr_straight")) clkmode = AUDIO_FMT_I2S16_HBR_STRAIGHT; else if (!strcmp(mode, "i2s16_hbr_demux")) clkmode = AUDIO_FMT_I2S16_HBR_DEMUX; else if (!strcmp(mode, "i2s32_hbr_demux")) clkmode = AUDIO_FMT_I2S32_HBR_DEMUX; else if (!strcmp(mode, "dst")) clkmode = AUDIO_FMT_DST; else clkmode = -EINVAL; return clkmode; } static int tda1997x_get_of_property(struct device *dev, struct tda1997x_platform_data *pdata) { struct device_node *np = dev->of_node; const char *vidout_fmt, *vidout_clk, *audout_fmt; u32 hdcp, ddc_slave, blc, trc; u32 port_configs; u32 audout_clk, audout_layout, max_pixel_rate = 0; int err; /* defaults (use inverted vs/hs/de) */ /* TODO: add of bindings for these */ pdata->vidout_sel_vs = 1; pdata->vidout_invert_vs =1; pdata->vidout_sel_hs = 1; pdata->vidout_invert_hs =1; pdata->vidout_sel_de = 1; pdata->vidout_invert_de =1; /* enable HDCP */ err = of_property_read_u32(np, "hdcp", &hdcp); if (err) { dev_dbg(dev, "get of property hdcp fail\n"); return err; } /* DDC Slave address */ err = of_property_read_u32(np, "ddc_slave", &ddc_slave); if (err) { dev_dbg(dev, "get of property ddc_slave fail\n"); return err; } /* Video output mode */ err = of_property_read_string(np, "vidout_fmt", &vidout_fmt); if (err) { dev_dbg(dev, "get of property vidout_fmt fail\n"); return err; } /* insert timing codes (SAV/EAV) in stream */ err = of_property_read_u32(np, "vidout_trc", &trc); if (err) { dev_dbg(dev, "get of property vidout_trc fail\n"); return err; } /* insert blanking codes in stream */ err = of_property_read_u32(np, "vidout_blc", &blc); if (err) { dev_dbg(dev, "get of property vidout_blc fail\n"); return err; } /* video output clock mode */ err = of_property_read_string(np, "vidout_clkmode", &vidout_clk); if (err) { dev_dbg(dev, "get of property vidout_clkmode fail\n"); return err; } /* video output port config */ of_find_property(np, "vidout_portcfg", &port_configs); err = of_property_read_u8_array(np, "vidout_portcfg", pdata->vidout_port_config, port_configs); if (err) { dev_dbg(dev, "get of property vidout_portcfg fail\n"); return err; } pdata->vidout_port_config_no = port_configs; /* max pixrate */ of_property_read_u32(np, "max-pixel-rate", &max_pixel_rate); /* audio output format */ err = of_property_read_string(np, "audout_fmt", &audout_fmt); if (err) { dev_dbg(dev, "get of property audout_fmt fail\n"); return err; } /* audio output sysclk */ err = of_property_read_u32(np, "audout_sysclk", &audout_clk); if (err) { dev_dbg(dev, "get of property audout_clkmode fail\n"); return err; } /* audio layout */ err = of_property_read_u32(np, "audout_layout", &audout_layout); if (err) { dev_dbg(dev, "get of property audout_layout fail\n"); return err; } pdata->hdcp = hdcp; pdata->ddc_slave = ddc_slave; pdata->vidout_format = parse_vidout_fmt(vidout_fmt); pdata->vidout_trc = trc; pdata->vidout_blc = blc; pdata->vidout_clkmode = parse_vidout_clkmode(vidout_clk); pdata->max_pixel_rate = max_pixel_rate; pdata->audout_layout = audout_layout; pdata->audout_format = parse_audout_fmt(audout_fmt); switch (audout_clk) { default: case 128: pdata->audout_sysclk = AUDIO_SYSCLK_128FS; break; case 256: pdata->audout_sysclk = AUDIO_SYSCLK_256FS; break; case 512: pdata->audout_sysclk = AUDIO_SYSCLK_512FS; break; } return err; } /** * tda1997x I2C probe function. * Function set in i2c_driver struct. * Called by insmod. * * @param *adapter I2C adapter descriptor. * * @return Error code indicating success or failure. */ static int tda1997x_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; int i; u8 reg; struct tda1997x_data *tda1997x = &tda1997x_data; struct tda1997x_platform_data *pdata; struct device *dev = &client->dev; dev_dbg(dev, "%s\n", __func__); pdata = devm_kzalloc(dev, sizeof(struct tda1997x_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; dev->platform_data = pdata; if (!client->irq) { dev_err(dev, "get tda1997x of interrupt fail\n"); return -EINVAL; } ret = tda1997x_get_of_property(dev, pdata); if (ret < 0) { dev_err(dev, "get tda1997x of property fail\n"); return ret; } /* probe chip */ /* ret = i2c_smbus_read_byte_data(client, REG_CMTP_REG10 & 0xff); if (ret < 0) return -ENODEV; */ tda1997x_regulator_enable(dev, pdata); memset(tda1997x, 0, sizeof(tda1997x_data)); i2c_set_clientdata(client, tda1997x); spin_lock_init(&tda1997x->lock); tda1997x->page = 0xff; tda1997x->client = client; tda1997x->irq = client->irq; tda1997x->pdata = pdata; tda1997x->internal_edid = !pdata->external_edid; INIT_WORK(&tda1997x->work, tda1997x_work); mutex_init(&tda1997x->page_lock); mutex_init(&tda1997x->cec_lock); switch(pdata->audout_layout) { case AUDIO_LAYOUT_FORCED_0: tda1997x->audio_mode.channels = 2; break; case AUDIO_LAYOUT_FORCED_1: tda1997x->audio_mode.channels = 8; break; default: tda1997x->audio_mode.channels = 8; break; } switch(pdata->audout_format) { case AUDIO_FMT_I2S16: tda1997x->audio_mode.samplesize = 2; break; case AUDIO_FMT_I2S32: tda1997x->audio_mode.samplesize = 2; break; default: tda1997x->audio_mode.samplesize = 0; } /* sysfs hooks */ ret = sysfs_create_group(&client->dev.kobj, &attr_group); if (ret) printk(KERN_ERR "%s: failed creating sysfs attrs\n", KBUILD_MODNAME); tda1997x->colorspace = COLORSPACE_RGB; tda1997x->colorimetry = COLORIMETRY_NONE; tda1997x->resolutiontype = RESTYPE_SDTV; /* disable/reset HDCP to get correct I2C access to Rx HDMI */ io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); /* Read chip configuration*/ reg = io_read(REG_CMTP_REG10); tda1997x->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ tda1997x->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ tda1997x->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ tda1997x->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ tda1997x->cec_enabled = (reg >> 0) & 0x01; /* CEC enabled */ switch((reg >> 4) & 0x03) { case 0x00: tda1997x->chip = 19971; tda1997x->input = INPUT_HDMI_A; break; case 0x01: tda1997x->chip = 19972; tda1997x->input = INPUT_AUTO_DIGITAL; break; case 0x02: case 0x03: tda1997x->chip = 19973; tda1997x->input = INPUT_HDMI_A; break; } /* read chip revision */ tda1997x->chip_revision = io_read(REG_CMTP_REG11); /* if N2 version, reset compdel_bp as it may generate some small pixel * shifts in case of embedded sync/or delay lower than 4 */ if (tda1997x->chip_revision != 0) { io_write(REG_MAN_SUS_HDMI_SEL, 0x00); io_write (REG_VDP_CTRL, 0x1f); } /* The CEC I2C address is not yet correct. We need to take into account * possible config setting in SLAVE_ADDR register, however the Hw I2C address */ tda1997x->cec_slave = 0x34 + ((io_read(REG_SLAVE_ADDR)>>4) & 0x03); pr_info("NXP TDA%d N%d detected: %dbit VP%s\n", tda1997x->chip, tda1997x->chip_revision + 1, (tda1997x->port_30bit)?30:24, (tda1997x->cec_enabled)?", CEC ":""); pr_info("video out format: %s\n", vidfmt_names[pdata->vidout_format]); if (tda1997x->cec_enabled) pr_info("CEC slave address 0x%02x\n", tda1997x->cec_slave); if (tda1997x->pdata->max_pixel_rate) pr_info("max pixel rate: %d MP/sec\n", pdata->max_pixel_rate); /* Attach a second dummy i2c_client for CEC register access */ tda1997x->client_cec = i2c_new_dummy(client->adapter, tda1997x->cec_slave); if (!tda1997x->client_cec) { printk(KERN_ERR "%s: failed to register CEC client\n", KBUILD_MODNAME); } /* disable HPD */ io_write(REG_HPD_AUTO_CTRL, 0x08); /* hpd_unsel */ if (tda1997x->chip_revision == 0) { io_write(REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); io_write(REG_CGU_DEBUG_SEL, 0x08); } /* reset infoframe at end of start-up-sequencer */ io_write(REG_SUS_SET_RGB2, 0x06); io_write(REG_SUS_SET_RGB3, 0x06); /* update page 0 */ io_write(REG_RT_MAN_CTRL, 0x43); /* CEC */ /* enable sync measurement timing */ tda1997x_cec_write(REG_PWR_CONTROL & 0xff, 0x04); /* adjust CEC clock divider */ tda1997x_cec_write(REG_OSC_DIVIDER & 0xff, 0x03); tda1997x_cec_write(REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); io_write(REG_TIMER_D, 0x54); /* enable power switch - SRAM content is always valid * (in case E-MTP is not or mis-programmed) */ reg = tda1997x_cec_read(REG_CONTROL & 0xff); reg |= 0x20; tda1997x_cec_write(REG_CONTROL & 0xff, reg); mdelay(50); /* read the chip version */ reg = io_read(REG_VERSION); /* get the chip configuration */ reg = io_read(REG_CMTP_REG10); /* init interrupt masks we care about */ io_write(REG_INT_MASK_TOP, 0x7f); /* hdcp,audio,info,mode,rate,ddc,sus */ io_write(REG_INT_MASK_SUS, 0xa8); /* config_mtp,fmt,sus_end,sus_st */ io_write(REG_INT_MASK_DDC, 0x00); /* none */ io_write(REG_INT_MASK_RATE, 0x44); /* rate_b_st, rate_a_st */ io_write(REG_INT_MASK_MODE, 0xf9); /* hdmi_flg,gamut,isrc2,isrc1,acp,dc_mode */ io_write(REG_INT_MASK_INFO, 0x7f); /* mps_if,aud_if,spd_if,avi_if, vs_if_other_bk2,vs_if_other_bk1, vs_if_hdmi */ io_write(REG_INT_MASK_AUDIO,0x3f); /* audio_freq,audio_flg,mute_flg, ch stat,unmount_fifo,fifo_err */ io_write(REG_INT_MASK_HDCP, 0x02); /* state_c5 */ io_write(REG_INT_MASK_AFE, 0x00); /* none */ /* clear all interrupts */ io_write(REG_INT_FLG_CLR_TOP, 0xff); io_write(REG_INT_FLG_CLR_SUS, 0xff); io_write(REG_INT_FLG_CLR_DDC, 0xff); io_write(REG_INT_FLG_CLR_RATE, 0xff); io_write(REG_INT_FLG_CLR_MODE, 0xff); io_write(REG_INT_FLG_CLR_INFO, 0xff); io_write(REG_INT_FLG_CLR_AUDIO, 0xff); io_write(REG_INT_FLG_CLR_HDCP, 0xff); io_write(REG_INT_FLG_CLR_AFE, 0xff); /* init TMDS equalizer */ if (tda1997x->chip_revision == 0) io_write(REG_CGU_DEBUG_SEL, 0x08); io_write24(REG_CLK_MIN_RATE, CLK_MIN_RATE); io_write24(REG_CLK_MAX_RATE, CLK_MAX_RATE); if (tda1997x->chip_revision == 0) io_write(REG_WDL_CFG, WDL_CFG_VAL); /* DC filter */ io_write(REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); /* disable test pattern */ io_write(REG_SERVICE_MODE, 0x00); /* update HDMI INFO CTRL */ io_write(REG_INFO_CTRL, 0xff); /* write HDMI INFO EXCEED value */ io_write(REG_INFO_EXCEED, 3); if (tda1997x->chip_revision == 0) { /* clear HDMI mode flag in BCAPS (for N1) */ io_write(REG_CLK_CFG, 0x03); io_write(REG_PON_OVR_EN, 0x01); io_write(REG_PON_CBIAS, 0x01); io_write(REG_PON_PLL, 0x01); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; reg |= 0x02; io_write(REG_MODE_RECOVER_CFG1, reg); io_write(REG_CLK_CFG, 0x00); io_write(REG_PON_OVR_EN, 0x00); reg = io_read(REG_MODE_RECOVER_CFG1); reg &= ~0x06; io_write(REG_MODE_RECOVER_CFG1, reg); } /* No HDCP acknowledge when HDCP is disabled * and reset SUS to force format detection */ tda1997x_hdmi_info_reset(NACK_HDCP, 1); //tda1997x_hdmi_info_reset(NACK_HDCP, 0); /* get key description seed in fuction of seed table if provded */ tda1997x_configure_mtp(tda1997x, MTP_START_READ); /* Start read */ reg = io_read(0x4000); /* read from MTP */ for (i = 0; i < RX_SEED_TABLE_LEN; i++) { if ((rx_seed_table[i].seedVal == 0) && (rx_seed_table[i].lookUpVal == 0)) break; else if (rx_seed_table[i].lookUpVal == reg) { /* MTP_DATA_LSB */ /* found seed replace seed in key_decryption_seed */ tda1997x->key_decryption_seed = rx_seed_table[i].seedVal; break; } } DPRINTK(0,"HDCP decryption seed:0x%04x\n", tda1997x->key_decryption_seed); /* disable HDCP */ tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_ON, DISABLE, tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); /* set the state machine */ tda1997x->state = STATE_INITIALIZED; /* Set HPD low */ tda1997x_manual_hpd(tda1997x, HPD_LOW); /* Configure receiver capabilities */ io_write(REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); /* Configure HDMI * * HDMI_CTRL bits: * 3:2 - mute_mode: * 00: use control packet * 01: mute off * 10: mute on * 1:0 - hdcp_mode: * 00: auto * 01: oess * 10: eess */ io_write(REG_HDMI_CTRL, 0x00); /* Auto HDCP mode, packet controlled mute */ /* Configure EDID * * EDID_ENABLE bits: * 7 - nack_off * 6 - edid_only * 1 - edid_b_en * 0 - edid_a_en */ reg = io_read(REG_EDID_ENABLE); if (!tda1997x->internal_edid) reg &= ~0x83; /* EDID Nack ON */ else reg |= 0x83; /* EDID Nack OFF */ io_write(REG_EDID_ENABLE, reg); /* HDCP Activation */ if (pdata->hdcp) { DPRINTK(0,"%s: Activating HDCP\n", __func__); /* No HDCP acknowledge when HDCP is disabled */ tda1997x_hdmi_info_reset(NACK_HDCP, 0); /* disable HDCP */ tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_ON, DISABLE, tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); /* index first secret key */ io_write(REG_HDCP_KIDX, 0x00); /* download MTP */ tda1997x_configure_mtp(tda1997x, MTP_START_DOWNLOAD); mdelay(20); /* enable HDCP */ tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_OFF, ENABLE, tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); /* Enable HDCP acknowledge when HDCP is enabled */ //tda1997x_hdmi_info_reset(0x00, 0); /* Configure HDCP error protection */ DPRINTK(0,"Configure HDCP error protection\n"); /* delockDelay - Delay before delocking the word locker */ io_write(REG_DELOCK_DELAY, 0x07); /* delockDelay (7=max) */ /* HDCP_DE_CTRL bits: * 7:6 - de_measurement_mode: * 00: 1_VDP * 01: 2_VDP * 10: 3_VDP * 11: 4_VDP * 5 - de_regen_mode: 1-enable * 4:3 - de_filter_sensitivity: * 2:0 - de_composition_mode: * 000: CH0 * 001: CH1 * 010: CH2 * 011: AND * 100: OR * 101: MIXED */ io_write(REG_HDCP_DE_CTRL, 3<<3); /* de_filter_sensitivity=3 */ /* HDCP_EP_FILT_CTRL bits: * 5:4 - ctl_filter_sensitivity * 3:2 - vs_filter_sensitivity * 1:0 - hs_filter_sensitivity */ /* ctl_filter_sentivity = vs_filter_sensitivity = hs_filter_sensitivity = 3 */ io_write(REG_HDCP_EP_FILT_CTRL, (0x3<<4) | (0x3<<2) | 0x3); } /* reset start-up-sequencer to force format detection */ tda1997x_hdmi_info_reset(0x00, 1); //tda1997x_hdmi_info_reset(0x00, 0); /* Set HPD high */ tda1997x_manual_hpd(tda1997x, HPD_HIGH_OTHER); /* Internal EDIDs are enabled - we can now load EDID */ if (tda1997x->internal_edid) { /* Load EDID into embedded memory */ tda1997x_load_edid_data(edid_block); /* Load DDC and RT data into embedded memory */ tda1997x_load_config_data(tda1997x, ddc_config0, rt_config); } /* Set HPD high (now that EDID is ready) */ tda1997x_manual_hpd(tda1997x, HPD_HIGH); /* Select input */ tda1997x_select_input(tda1997x->input); /* enable matrix conversion bypass (no conversion) */ io_write(REG_VDP_CTRL, io_read(REG_VDP_CTRL) | (1<<0)); /* set video output mode */ tda1997x_set_video_outputformat(tda1997x->pdata); for (i = 0; i < tda1997x->pdata->vidout_port_config_no; i++) io_write(REG_VP35_32_CTRL + i, tda1997x->pdata->vidout_port_config[i]); /* configure audio output mode */ tda1997x_configure_audio_formatter(tda1997x->pdata, 0); /* configure audio clock mode: * Audio clock generation from Xtal: 128fs, 256fs, 512fs and * Fs = 32kHz, 44.1kHz, 48kHz, 88.2kHz, 96kHz, 172.4kHz, 192kHz */ //io_write(REG_AUDIO_CLOCK_MODE, tda1997x->pdata->audio_sysclk); io_write(REG_AUDIO_CLOCK_MODE, AUDIO_SYSCLK_128FS); /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ tda1997x_hdmi_info_reset(RESET_AI, 0); //tda1997x_hdmi_info_reset(0x00, 0); /* reset infoframe */ tda1997x_hdmi_info_reset(RESET_IF, 0); //tda1997x_hdmi_info_reset(0x00, 0); /* reset audio infoframes */ tda1997x_hdmi_info_reset(RESET_AUDIO, 0); //tda1997x_hdmi_info_reset(0x00, 0); /* reset gamut */ tda1997x_hdmi_info_reset(RESET_GAMUT, 0); //tda1997x_hdmi_info_reset(0x00, 0); /* get initial HDMI status */ tda1997x->hdmi_status = io_read(REG_HDMI_FLAGS); /* get DEEP color mode */ reg = io_read(REG_DEEP_COLOR_MODE); /* register interrupt handler */ if (client->irq) { ret = devm_request_irq(&client->dev, client->irq, tda1997x_isr, 0, "tda1997x_irq", tda1997x); if (ret < 0) { pr_err("%s:interrupt gpio%d registration failed, error=%d \n", __func__, client->irq, ret); } pr_debug("registered irq%d\n", client->irq); } return ret; } static const struct i2c_device_id tda1997x_id[] = { {"tda1997x", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, tda1997x_id); //static const struct of_device_id tda1997x_dt_ids[] = { // { .compatible = "nxp,tda1997x", }, // { /* sentinel */ } //}; //MODULE_DEVICE_TABLE(of, tda1997x_dt_ids); static struct i2c_driver tda1997x_i2c_driver = { .driver = { .name = "tda1997x", .owner = THIS_MODULE, //.of_match_table = tda1997x_dt_ids, }, .probe = tda1997x_probe, .remove = tda1997x_remove, .id_table = tda1997x_id, }; /** * tda1997x init function. * Called on insmod. * * @return Error code indicating success or failure. */ static __init int tda1997x_init(void) { u8 err = 0; pr_debug("%s\n", __func__); /* Tells the i2c driver what functions to call for this driver. */ err = i2c_add_driver(&tda1997x_i2c_driver); if (err != 0) pr_err("%s:driver registration failed, error=%d \n", __func__, err); return err; } /** * tda1997x cleanup function. * Called on rmmod. * * @return Error code indicating success or failure. */ static void __exit tda1997x_clean(void) { dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); i2c_del_driver(&tda1997x_i2c_driver); } module_init(tda1997x_init); module_exit(tda1997x_clean); MODULE_DESCRIPTION("Core driver for TDA1997X HDMI Receiver"); MODULE_AUTHOR("Tim Harvey "); MODULE_LICENSE("GPL");