openrex-linux-3.14/drivers/mfd/tda1997x-core.c
2016-03-04 07:09:26 -08:00

4606 lines
137 KiB
C

/*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/i2c.h>
#include <linux/sysfs.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/fsl_devices.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/mfd/tda1997x-core.h>
#include <linux/time.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc.h>
/* 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", &reg, &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<<input;
DPRINTK(0, "\tHDMI-%c: Rate measurement stability change\n", input+'A');
DPRINTK(0, "\tirq_status=0x%02x/0x%02x/0x%02x\n",
irq_status, last_irq_status, tda1997x->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 <tharvey@gateworks.com>");
MODULE_LICENSE("GPL");