1969325a2SAndy Yan // SPDX-License-Identifier: GPL-2.0-only 2969325a2SAndy Yan /* 3969325a2SAndy Yan * Copyright (C) Rockchip Electronics Co., Ltd. 4969325a2SAndy Yan * Zheng Yang <zhengyang@rock-chips.com> 5969325a2SAndy Yan * Yakir Yang <ykk@rock-chips.com> 6969325a2SAndy Yan * Andy Yan <andyshrk@163.com> 7969325a2SAndy Yan */ 8969325a2SAndy Yan 9969325a2SAndy Yan #include <linux/irq.h> 10969325a2SAndy Yan #include <linux/clk.h> 11969325a2SAndy Yan #include <linux/delay.h> 12969325a2SAndy Yan #include <linux/err.h> 13969325a2SAndy Yan #include <linux/i2c.h> 14969325a2SAndy Yan #include <linux/hdmi.h> 15969325a2SAndy Yan #include <linux/mfd/syscon.h> 16969325a2SAndy Yan #include <linux/mod_devicetable.h> 17969325a2SAndy Yan #include <linux/module.h> 18969325a2SAndy Yan #include <linux/mutex.h> 19969325a2SAndy Yan #include <linux/platform_device.h> 20969325a2SAndy Yan #include <linux/regmap.h> 21969325a2SAndy Yan 22969325a2SAndy Yan #include <drm/bridge/inno_hdmi.h> 23969325a2SAndy Yan #include <drm/drm_atomic.h> 24969325a2SAndy Yan #include <drm/drm_atomic_helper.h> 25969325a2SAndy Yan #include <drm/drm_edid.h> 26969325a2SAndy Yan #include <drm/drm_of.h> 27969325a2SAndy Yan #include <drm/drm_print.h> 28969325a2SAndy Yan #include <drm/drm_probe_helper.h> 29969325a2SAndy Yan #include <drm/drm_simple_kms_helper.h> 30969325a2SAndy Yan 31969325a2SAndy Yan #include <drm/display/drm_hdmi_helper.h> 32969325a2SAndy Yan #include <drm/display/drm_hdmi_state_helper.h> 33969325a2SAndy Yan 34969325a2SAndy Yan #define INNO_HDMI_MIN_TMDS_CLOCK 25000000U 35969325a2SAndy Yan 36969325a2SAndy Yan #define DDC_SEGMENT_ADDR 0x30 37969325a2SAndy Yan 38969325a2SAndy Yan #define HDMI_SCL_RATE (100 * 1000) 39969325a2SAndy Yan 40969325a2SAndy Yan #define DDC_BUS_FREQ_L 0x4b 41969325a2SAndy Yan #define DDC_BUS_FREQ_H 0x4c 42969325a2SAndy Yan 43969325a2SAndy Yan #define HDMI_SYS_CTRL 0x00 44969325a2SAndy Yan #define m_RST_ANALOG BIT(6) 45969325a2SAndy Yan #define v_RST_ANALOG (0 << 6) 46969325a2SAndy Yan #define v_NOT_RST_ANALOG BIT(6) 47969325a2SAndy Yan #define m_RST_DIGITAL BIT(5) 48969325a2SAndy Yan #define v_RST_DIGITAL (0 << 5) 49969325a2SAndy Yan #define v_NOT_RST_DIGITAL BIT(5) 50969325a2SAndy Yan #define m_REG_CLK_INV BIT(4) 51969325a2SAndy Yan #define v_REG_CLK_NOT_INV (0 << 4) 52969325a2SAndy Yan #define v_REG_CLK_INV BIT(4) 53969325a2SAndy Yan #define m_VCLK_INV BIT(3) 54969325a2SAndy Yan #define v_VCLK_NOT_INV (0 << 3) 55969325a2SAndy Yan #define v_VCLK_INV BIT(3) 56969325a2SAndy Yan #define m_REG_CLK_SOURCE BIT(2) 57969325a2SAndy Yan #define v_REG_CLK_SOURCE_TMDS (0 << 2) 58969325a2SAndy Yan #define v_REG_CLK_SOURCE_SYS BIT(2) 59969325a2SAndy Yan #define m_POWER BIT(1) 60969325a2SAndy Yan #define v_PWR_ON (0 << 1) 61969325a2SAndy Yan #define v_PWR_OFF BIT(1) 62969325a2SAndy Yan #define m_INT_POL BIT(0) 63969325a2SAndy Yan #define v_INT_POL_HIGH 1 64969325a2SAndy Yan #define v_INT_POL_LOW 0 65969325a2SAndy Yan 66969325a2SAndy Yan #define HDMI_VIDEO_CONTRL1 0x01 67969325a2SAndy Yan #define m_VIDEO_INPUT_FORMAT (7 << 1) 68969325a2SAndy Yan #define m_DE_SOURCE BIT(0) 69969325a2SAndy Yan #define v_VIDEO_INPUT_FORMAT(n) ((n) << 1) 70969325a2SAndy Yan #define v_DE_EXTERNAL 1 71969325a2SAndy Yan #define v_DE_INTERNAL 0 72969325a2SAndy Yan enum { 73969325a2SAndy Yan VIDEO_INPUT_SDR_RGB444 = 0, 74969325a2SAndy Yan VIDEO_INPUT_DDR_RGB444 = 5, 75969325a2SAndy Yan VIDEO_INPUT_DDR_YCBCR422 = 6 76969325a2SAndy Yan }; 77969325a2SAndy Yan 78969325a2SAndy Yan #define HDMI_VIDEO_CONTRL2 0x02 79969325a2SAndy Yan #define m_VIDEO_OUTPUT_COLOR (3 << 6) 80969325a2SAndy Yan #define m_VIDEO_INPUT_BITS (3 << 4) 81969325a2SAndy Yan #define m_VIDEO_INPUT_CSP BIT(0) 82969325a2SAndy Yan #define v_VIDEO_OUTPUT_COLOR(n) (((n) & 0x3) << 6) 83969325a2SAndy Yan #define v_VIDEO_INPUT_BITS(n) ((n) << 4) 84969325a2SAndy Yan #define v_VIDEO_INPUT_CSP(n) ((n) << 0) 85969325a2SAndy Yan enum { 86969325a2SAndy Yan VIDEO_INPUT_12BITS = 0, 87969325a2SAndy Yan VIDEO_INPUT_10BITS = 1, 88969325a2SAndy Yan VIDEO_INPUT_REVERT = 2, 89969325a2SAndy Yan VIDEO_INPUT_8BITS = 3, 90969325a2SAndy Yan }; 91969325a2SAndy Yan 92969325a2SAndy Yan #define HDMI_VIDEO_CONTRL 0x03 93969325a2SAndy Yan #define m_VIDEO_AUTO_CSC BIT(7) 94969325a2SAndy Yan #define v_VIDEO_AUTO_CSC(n) ((n) << 7) 95969325a2SAndy Yan #define m_VIDEO_C0_C2_SWAP BIT(0) 96969325a2SAndy Yan #define v_VIDEO_C0_C2_SWAP(n) ((n) << 0) 97969325a2SAndy Yan enum { 98969325a2SAndy Yan C0_C2_CHANGE_ENABLE = 0, 99969325a2SAndy Yan C0_C2_CHANGE_DISABLE = 1, 100969325a2SAndy Yan AUTO_CSC_DISABLE = 0, 101969325a2SAndy Yan AUTO_CSC_ENABLE = 1, 102969325a2SAndy Yan }; 103969325a2SAndy Yan 104969325a2SAndy Yan #define HDMI_VIDEO_CONTRL3 0x04 105969325a2SAndy Yan #define m_COLOR_DEPTH_NOT_INDICATED BIT(4) 106969325a2SAndy Yan #define m_SOF BIT(3) 107969325a2SAndy Yan #define m_COLOR_RANGE BIT(2) 108969325a2SAndy Yan #define m_CSC BIT(0) 109969325a2SAndy Yan #define v_COLOR_DEPTH_NOT_INDICATED(n) ((n) << 4) 110969325a2SAndy Yan #define v_SOF_ENABLE (0 << 3) 111969325a2SAndy Yan #define v_SOF_DISABLE BIT(3) 112969325a2SAndy Yan #define v_COLOR_RANGE_FULL BIT(2) 113969325a2SAndy Yan #define v_COLOR_RANGE_LIMITED (0 << 2) 114969325a2SAndy Yan #define v_CSC_ENABLE 1 115969325a2SAndy Yan #define v_CSC_DISABLE 0 116969325a2SAndy Yan 117969325a2SAndy Yan #define HDMI_AV_MUTE 0x05 118969325a2SAndy Yan #define m_AVMUTE_CLEAR BIT(7) 119969325a2SAndy Yan #define m_AVMUTE_ENABLE BIT(6) 120969325a2SAndy Yan #define m_AUDIO_MUTE BIT(1) 121969325a2SAndy Yan #define m_VIDEO_BLACK BIT(0) 122969325a2SAndy Yan #define v_AVMUTE_CLEAR(n) ((n) << 7) 123969325a2SAndy Yan #define v_AVMUTE_ENABLE(n) ((n) << 6) 124969325a2SAndy Yan #define v_AUDIO_MUTE(n) ((n) << 1) 125969325a2SAndy Yan #define v_VIDEO_MUTE(n) ((n) << 0) 126969325a2SAndy Yan 127969325a2SAndy Yan #define HDMI_VIDEO_TIMING_CTL 0x08 128969325a2SAndy Yan #define v_HSYNC_POLARITY(n) ((n) << 3) 129969325a2SAndy Yan #define v_VSYNC_POLARITY(n) ((n) << 2) 130969325a2SAndy Yan #define v_INETLACE(n) ((n) << 1) 131969325a2SAndy Yan #define v_EXTERANL_VIDEO(n) ((n) << 0) 132969325a2SAndy Yan 133969325a2SAndy Yan #define HDMI_VIDEO_EXT_HTOTAL_L 0x09 134969325a2SAndy Yan #define HDMI_VIDEO_EXT_HTOTAL_H 0x0a 135969325a2SAndy Yan #define HDMI_VIDEO_EXT_HBLANK_L 0x0b 136969325a2SAndy Yan #define HDMI_VIDEO_EXT_HBLANK_H 0x0c 137969325a2SAndy Yan #define HDMI_VIDEO_EXT_HDELAY_L 0x0d 138969325a2SAndy Yan #define HDMI_VIDEO_EXT_HDELAY_H 0x0e 139969325a2SAndy Yan #define HDMI_VIDEO_EXT_HDURATION_L 0x0f 140969325a2SAndy Yan #define HDMI_VIDEO_EXT_HDURATION_H 0x10 141969325a2SAndy Yan #define HDMI_VIDEO_EXT_VTOTAL_L 0x11 142969325a2SAndy Yan #define HDMI_VIDEO_EXT_VTOTAL_H 0x12 143969325a2SAndy Yan #define HDMI_VIDEO_EXT_VBLANK 0x13 144969325a2SAndy Yan #define HDMI_VIDEO_EXT_VDELAY 0x14 145969325a2SAndy Yan #define HDMI_VIDEO_EXT_VDURATION 0x15 146969325a2SAndy Yan 147969325a2SAndy Yan #define HDMI_VIDEO_CSC_COEF 0x18 148969325a2SAndy Yan 149969325a2SAndy Yan #define HDMI_AUDIO_CTRL1 0x35 150969325a2SAndy Yan enum { 151969325a2SAndy Yan CTS_SOURCE_INTERNAL = 0, 152969325a2SAndy Yan CTS_SOURCE_EXTERNAL = 1, 153969325a2SAndy Yan }; 154969325a2SAndy Yan 155969325a2SAndy Yan #define v_CTS_SOURCE(n) ((n) << 7) 156969325a2SAndy Yan 157969325a2SAndy Yan enum { 158969325a2SAndy Yan DOWNSAMPLE_DISABLE = 0, 159969325a2SAndy Yan DOWNSAMPLE_1_2 = 1, 160969325a2SAndy Yan DOWNSAMPLE_1_4 = 2, 161969325a2SAndy Yan }; 162969325a2SAndy Yan 163969325a2SAndy Yan #define v_DOWN_SAMPLE(n) ((n) << 5) 164969325a2SAndy Yan 165969325a2SAndy Yan enum { 166969325a2SAndy Yan AUDIO_SOURCE_IIS = 0, 167969325a2SAndy Yan AUDIO_SOURCE_SPDIF = 1, 168969325a2SAndy Yan }; 169969325a2SAndy Yan 170969325a2SAndy Yan #define v_AUDIO_SOURCE(n) ((n) << 3) 171969325a2SAndy Yan 172969325a2SAndy Yan #define v_MCLK_ENABLE(n) ((n) << 2) 173969325a2SAndy Yan 174969325a2SAndy Yan enum { 175969325a2SAndy Yan MCLK_128FS = 0, 176969325a2SAndy Yan MCLK_256FS = 1, 177969325a2SAndy Yan MCLK_384FS = 2, 178969325a2SAndy Yan MCLK_512FS = 3, 179969325a2SAndy Yan }; 180969325a2SAndy Yan 181969325a2SAndy Yan #define v_MCLK_RATIO(n) (n) 182969325a2SAndy Yan 183969325a2SAndy Yan #define AUDIO_SAMPLE_RATE 0x37 184969325a2SAndy Yan 185969325a2SAndy Yan enum { 186969325a2SAndy Yan AUDIO_32K = 0x3, 187969325a2SAndy Yan AUDIO_441K = 0x0, 188969325a2SAndy Yan AUDIO_48K = 0x2, 189969325a2SAndy Yan AUDIO_882K = 0x8, 190969325a2SAndy Yan AUDIO_96K = 0xa, 191969325a2SAndy Yan AUDIO_1764K = 0xc, 192969325a2SAndy Yan AUDIO_192K = 0xe, 193969325a2SAndy Yan }; 194969325a2SAndy Yan 195969325a2SAndy Yan #define AUDIO_I2S_MODE 0x38 196969325a2SAndy Yan 197969325a2SAndy Yan enum { 198969325a2SAndy Yan I2S_CHANNEL_1_2 = 1, 199969325a2SAndy Yan I2S_CHANNEL_3_4 = 3, 200969325a2SAndy Yan I2S_CHANNEL_5_6 = 7, 201969325a2SAndy Yan I2S_CHANNEL_7_8 = 0xf 202969325a2SAndy Yan }; 203969325a2SAndy Yan 204969325a2SAndy Yan #define v_I2S_CHANNEL(n) ((n) << 2) 205969325a2SAndy Yan 206969325a2SAndy Yan enum { 207969325a2SAndy Yan I2S_STANDARD = 0, 208969325a2SAndy Yan I2S_LEFT_JUSTIFIED = 1, 209969325a2SAndy Yan I2S_RIGHT_JUSTIFIED = 2, 210969325a2SAndy Yan }; 211969325a2SAndy Yan 212969325a2SAndy Yan #define v_I2S_MODE(n) (n) 213969325a2SAndy Yan 214969325a2SAndy Yan #define AUDIO_I2S_MAP 0x39 215969325a2SAndy Yan #define AUDIO_I2S_SWAPS_SPDIF 0x3a 216969325a2SAndy Yan #define v_SPIDF_FREQ(n) (n) 217969325a2SAndy Yan 218969325a2SAndy Yan #define N_32K 0x1000 219969325a2SAndy Yan #define N_441K 0x1880 220969325a2SAndy Yan #define N_882K 0x3100 221969325a2SAndy Yan #define N_1764K 0x6200 222969325a2SAndy Yan #define N_48K 0x1800 223969325a2SAndy Yan #define N_96K 0x3000 224969325a2SAndy Yan #define N_192K 0x6000 225969325a2SAndy Yan 226969325a2SAndy Yan #define HDMI_AUDIO_CHANNEL_STATUS 0x3e 227969325a2SAndy Yan #define m_AUDIO_STATUS_NLPCM BIT(7) 228969325a2SAndy Yan #define m_AUDIO_STATUS_USE BIT(6) 229969325a2SAndy Yan #define m_AUDIO_STATUS_COPYRIGHT BIT(5) 230969325a2SAndy Yan #define m_AUDIO_STATUS_ADDITION (3 << 2) 231969325a2SAndy Yan #define m_AUDIO_STATUS_CLK_ACCURACY (2 << 0) 232969325a2SAndy Yan #define v_AUDIO_STATUS_NLPCM(n) (((n) & 1) << 7) 233969325a2SAndy Yan #define AUDIO_N_H 0x3f 234969325a2SAndy Yan #define AUDIO_N_M 0x40 235969325a2SAndy Yan #define AUDIO_N_L 0x41 236969325a2SAndy Yan 237969325a2SAndy Yan #define HDMI_AUDIO_CTS_H 0x45 238969325a2SAndy Yan #define HDMI_AUDIO_CTS_M 0x46 239969325a2SAndy Yan #define HDMI_AUDIO_CTS_L 0x47 240969325a2SAndy Yan 241969325a2SAndy Yan #define HDMI_DDC_CLK_L 0x4b 242969325a2SAndy Yan #define HDMI_DDC_CLK_H 0x4c 243969325a2SAndy Yan 244969325a2SAndy Yan #define HDMI_EDID_SEGMENT_POINTER 0x4d 245969325a2SAndy Yan #define HDMI_EDID_WORD_ADDR 0x4e 246969325a2SAndy Yan #define HDMI_EDID_FIFO_OFFSET 0x4f 247969325a2SAndy Yan #define HDMI_EDID_FIFO_ADDR 0x50 248969325a2SAndy Yan 249969325a2SAndy Yan #define HDMI_PACKET_SEND_MANUAL 0x9c 250969325a2SAndy Yan #define HDMI_PACKET_SEND_AUTO 0x9d 251969325a2SAndy Yan #define m_PACKET_GCP_EN BIT(7) 252969325a2SAndy Yan #define m_PACKET_MSI_EN BIT(6) 253969325a2SAndy Yan #define m_PACKET_SDI_EN BIT(5) 254969325a2SAndy Yan #define m_PACKET_VSI_EN BIT(4) 255969325a2SAndy Yan #define v_PACKET_GCP_EN(n) (((n) & 1) << 7) 256969325a2SAndy Yan #define v_PACKET_MSI_EN(n) (((n) & 1) << 6) 257969325a2SAndy Yan #define v_PACKET_SDI_EN(n) (((n) & 1) << 5) 258969325a2SAndy Yan #define v_PACKET_VSI_EN(n) (((n) & 1) << 4) 259969325a2SAndy Yan 260969325a2SAndy Yan #define HDMI_CONTROL_PACKET_BUF_INDEX 0x9f 261969325a2SAndy Yan 262969325a2SAndy Yan enum { 263969325a2SAndy Yan INFOFRAME_VSI = 0x05, 264969325a2SAndy Yan INFOFRAME_AVI = 0x06, 265969325a2SAndy Yan INFOFRAME_AAI = 0x08, 266969325a2SAndy Yan }; 267969325a2SAndy Yan 268969325a2SAndy Yan #define HDMI_CONTROL_PACKET_ADDR 0xa0 269969325a2SAndy Yan #define HDMI_MAXIMUM_INFO_FRAME_SIZE 0x11 270969325a2SAndy Yan 271969325a2SAndy Yan enum { 272969325a2SAndy Yan AVI_COLOR_MODE_RGB = 0, 273969325a2SAndy Yan AVI_COLOR_MODE_YCBCR422 = 1, 274969325a2SAndy Yan AVI_COLOR_MODE_YCBCR444 = 2, 275969325a2SAndy Yan AVI_COLORIMETRY_NO_DATA = 0, 276969325a2SAndy Yan 277969325a2SAndy Yan AVI_COLORIMETRY_SMPTE_170M = 1, 278969325a2SAndy Yan AVI_COLORIMETRY_ITU709 = 2, 279969325a2SAndy Yan AVI_COLORIMETRY_EXTENDED = 3, 280969325a2SAndy Yan 281969325a2SAndy Yan AVI_CODED_FRAME_ASPECT_NO_DATA = 0, 282969325a2SAndy Yan AVI_CODED_FRAME_ASPECT_4_3 = 1, 283969325a2SAndy Yan AVI_CODED_FRAME_ASPECT_16_9 = 2, 284969325a2SAndy Yan 285969325a2SAndy Yan ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08, 286969325a2SAndy Yan ACTIVE_ASPECT_RATE_4_3 = 0x09, 287969325a2SAndy Yan ACTIVE_ASPECT_RATE_16_9 = 0x0A, 288969325a2SAndy Yan ACTIVE_ASPECT_RATE_14_9 = 0x0B, 289969325a2SAndy Yan }; 290969325a2SAndy Yan 291969325a2SAndy Yan #define HDMI_HDCP_CTRL 0x52 292969325a2SAndy Yan #define m_HDMI_DVI BIT(1) 293969325a2SAndy Yan #define v_HDMI_DVI(n) ((n) << 1) 294969325a2SAndy Yan 295969325a2SAndy Yan #define HDMI_INTERRUPT_MASK1 0xc0 296969325a2SAndy Yan #define HDMI_INTERRUPT_STATUS1 0xc1 297969325a2SAndy Yan #define m_INT_ACTIVE_VSYNC BIT(5) 298969325a2SAndy Yan #define m_INT_EDID_READY BIT(2) 299969325a2SAndy Yan 300969325a2SAndy Yan #define HDMI_INTERRUPT_MASK2 0xc2 301969325a2SAndy Yan #define HDMI_INTERRUPT_STATUS2 0xc3 302969325a2SAndy Yan #define m_INT_HDCP_ERR BIT(7) 303969325a2SAndy Yan #define m_INT_BKSV_FLAG BIT(6) 304969325a2SAndy Yan #define m_INT_HDCP_OK BIT(4) 305969325a2SAndy Yan 306969325a2SAndy Yan #define HDMI_STATUS 0xc8 307969325a2SAndy Yan #define m_HOTPLUG BIT(7) 308969325a2SAndy Yan #define m_MASK_INT_HOTPLUG BIT(5) 309969325a2SAndy Yan #define m_INT_HOTPLUG BIT(1) 310969325a2SAndy Yan #define v_MASK_INT_HOTPLUG(n) (((n) & 0x1) << 5) 311969325a2SAndy Yan 312969325a2SAndy Yan #define HDMI_COLORBAR 0xc9 313969325a2SAndy Yan 314969325a2SAndy Yan #define HDMI_PHY_SYNC 0xce 315969325a2SAndy Yan #define HDMI_PHY_SYS_CTL 0xe0 316969325a2SAndy Yan #define m_TMDS_CLK_SOURCE BIT(5) 317969325a2SAndy Yan #define v_TMDS_FROM_PLL (0 << 5) 318969325a2SAndy Yan #define v_TMDS_FROM_GEN BIT(5) 319969325a2SAndy Yan #define m_PHASE_CLK BIT(4) 320969325a2SAndy Yan #define v_DEFAULT_PHASE (0 << 4) 321969325a2SAndy Yan #define v_SYNC_PHASE BIT(4) 322969325a2SAndy Yan #define m_TMDS_CURRENT_PWR BIT(3) 323969325a2SAndy Yan #define v_TURN_ON_CURRENT (0 << 3) 324969325a2SAndy Yan #define v_CAT_OFF_CURRENT BIT(3) 325969325a2SAndy Yan #define m_BANDGAP_PWR BIT(2) 326969325a2SAndy Yan #define v_BANDGAP_PWR_UP (0 << 2) 327969325a2SAndy Yan #define v_BANDGAP_PWR_DOWN BIT(2) 328969325a2SAndy Yan #define m_PLL_PWR BIT(1) 329969325a2SAndy Yan #define v_PLL_PWR_UP (0 << 1) 330969325a2SAndy Yan #define v_PLL_PWR_DOWN BIT(1) 331969325a2SAndy Yan #define m_TMDS_CHG_PWR BIT(0) 332969325a2SAndy Yan #define v_TMDS_CHG_PWR_UP (0 << 0) 333969325a2SAndy Yan #define v_TMDS_CHG_PWR_DOWN BIT(0) 334969325a2SAndy Yan 335969325a2SAndy Yan #define HDMI_PHY_CHG_PWR 0xe1 336969325a2SAndy Yan #define v_CLK_CHG_PWR(n) (((n) & 1) << 3) 337969325a2SAndy Yan #define v_DATA_CHG_PWR(n) (((n) & 7) << 0) 338969325a2SAndy Yan 339969325a2SAndy Yan #define HDMI_PHY_DRIVER 0xe2 340969325a2SAndy Yan #define v_CLK_MAIN_DRIVER(n) ((n) << 4) 341969325a2SAndy Yan #define v_DATA_MAIN_DRIVER(n) ((n) << 0) 342969325a2SAndy Yan 343969325a2SAndy Yan #define HDMI_PHY_PRE_EMPHASIS 0xe3 344969325a2SAndy Yan #define v_PRE_EMPHASIS(n) (((n) & 7) << 4) 345969325a2SAndy Yan #define v_CLK_PRE_DRIVER(n) (((n) & 3) << 2) 346969325a2SAndy Yan #define v_DATA_PRE_DRIVER(n) (((n) & 3) << 0) 347969325a2SAndy Yan 348969325a2SAndy Yan #define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW 0xe7 349969325a2SAndy Yan #define v_FEEDBACK_DIV_LOW(n) ((n) & 0xff) 350969325a2SAndy Yan #define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8 351969325a2SAndy Yan #define v_FEEDBACK_DIV_HIGH(n) ((n) & 1) 352969325a2SAndy Yan 353969325a2SAndy Yan #define HDMI_PHY_PRE_DIV_RATIO 0xed 354969325a2SAndy Yan #define v_PRE_DIV_RATIO(n) ((n) & 0x1f) 355969325a2SAndy Yan 356969325a2SAndy Yan #define HDMI_CEC_CTRL 0xd0 357969325a2SAndy Yan #define m_ADJUST_FOR_HISENSE BIT(6) 358969325a2SAndy Yan #define m_REJECT_RX_BROADCAST BIT(5) 359969325a2SAndy Yan #define m_BUSFREETIME_ENABLE BIT(2) 360969325a2SAndy Yan #define m_REJECT_RX BIT(1) 361969325a2SAndy Yan #define m_START_TX BIT(0) 362969325a2SAndy Yan 363969325a2SAndy Yan #define HDMI_CEC_DATA 0xd1 364969325a2SAndy Yan #define HDMI_CEC_TX_OFFSET 0xd2 365969325a2SAndy Yan #define HDMI_CEC_RX_OFFSET 0xd3 366969325a2SAndy Yan #define HDMI_CEC_CLK_H 0xd4 367969325a2SAndy Yan #define HDMI_CEC_CLK_L 0xd5 368969325a2SAndy Yan #define HDMI_CEC_TX_LENGTH 0xd6 369969325a2SAndy Yan #define HDMI_CEC_RX_LENGTH 0xd7 370969325a2SAndy Yan #define HDMI_CEC_TX_INT_MASK 0xd8 371969325a2SAndy Yan #define m_TX_DONE BIT(3) 372969325a2SAndy Yan #define m_TX_NOACK BIT(2) 373969325a2SAndy Yan #define m_TX_BROADCAST_REJ BIT(1) 374969325a2SAndy Yan #define m_TX_BUSNOTFREE BIT(0) 375969325a2SAndy Yan 376969325a2SAndy Yan #define HDMI_CEC_RX_INT_MASK 0xd9 377969325a2SAndy Yan #define m_RX_LA_ERR BIT(4) 378969325a2SAndy Yan #define m_RX_GLITCH BIT(3) 379969325a2SAndy Yan #define m_RX_DONE BIT(0) 380969325a2SAndy Yan 381969325a2SAndy Yan #define HDMI_CEC_TX_INT 0xda 382969325a2SAndy Yan #define HDMI_CEC_RX_INT 0xdb 383969325a2SAndy Yan #define HDMI_CEC_BUSFREETIME_L 0xdc 384969325a2SAndy Yan #define HDMI_CEC_BUSFREETIME_H 0xdd 385969325a2SAndy Yan #define HDMI_CEC_LOGICADDR 0xde 386969325a2SAndy Yan 387969325a2SAndy Yan struct inno_hdmi_i2c { 388969325a2SAndy Yan struct i2c_adapter adap; 389969325a2SAndy Yan 390969325a2SAndy Yan u8 ddc_addr; 391969325a2SAndy Yan u8 segment_addr; 392969325a2SAndy Yan 393969325a2SAndy Yan struct mutex lock; 394969325a2SAndy Yan struct completion cmp; 395969325a2SAndy Yan }; 396969325a2SAndy Yan 397969325a2SAndy Yan struct inno_hdmi { 398969325a2SAndy Yan struct device *dev; 399969325a2SAndy Yan struct drm_bridge bridge; 400969325a2SAndy Yan struct clk *pclk; 401969325a2SAndy Yan struct clk *refclk; 402969325a2SAndy Yan void __iomem *regs; 403969325a2SAndy Yan struct regmap *grf; 404969325a2SAndy Yan 405969325a2SAndy Yan struct inno_hdmi_i2c *i2c; 406969325a2SAndy Yan struct i2c_adapter *ddc; 407969325a2SAndy Yan const struct inno_hdmi_plat_data *plat_data; 408969325a2SAndy Yan }; 409969325a2SAndy Yan 410969325a2SAndy Yan enum { 411969325a2SAndy Yan CSC_RGB_0_255_TO_ITU601_16_235_8BIT, 412969325a2SAndy Yan CSC_RGB_0_255_TO_ITU709_16_235_8BIT, 413969325a2SAndy Yan CSC_RGB_0_255_TO_RGB_16_235_8BIT, 414969325a2SAndy Yan }; 415969325a2SAndy Yan 416969325a2SAndy Yan static const char coeff_csc[][24] = { 417969325a2SAndy Yan /* 418969325a2SAndy Yan * RGB2YUV:601 SD mode: 419969325a2SAndy Yan * Cb = -0.291G - 0.148R + 0.439B + 128 420969325a2SAndy Yan * Y = 0.504G + 0.257R + 0.098B + 16 421969325a2SAndy Yan * Cr = -0.368G + 0.439R - 0.071B + 128 422969325a2SAndy Yan */ 423969325a2SAndy Yan { 424969325a2SAndy Yan 0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80, 425969325a2SAndy Yan 0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e, 426969325a2SAndy Yan 0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80 427969325a2SAndy Yan }, 428969325a2SAndy Yan /* 429969325a2SAndy Yan * RGB2YUV:709 HD mode: 430969325a2SAndy Yan * Cb = - 0.338G - 0.101R + 0.439B + 128 431969325a2SAndy Yan * Y = 0.614G + 0.183R + 0.062B + 16 432969325a2SAndy Yan * Cr = - 0.399G + 0.439R - 0.040B + 128 433969325a2SAndy Yan */ 434969325a2SAndy Yan { 435969325a2SAndy Yan 0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80, 436969325a2SAndy Yan 0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10, 437969325a2SAndy Yan 0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80 438969325a2SAndy Yan }, 439969325a2SAndy Yan /* 440969325a2SAndy Yan * RGB[0:255]2RGB[16:235]: 441969325a2SAndy Yan * R' = R x (235-16)/255 + 16; 442969325a2SAndy Yan * G' = G x (235-16)/255 + 16; 443969325a2SAndy Yan * B' = B x (235-16)/255 + 16; 444969325a2SAndy Yan */ 445969325a2SAndy Yan { 446969325a2SAndy Yan 0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10, 447969325a2SAndy Yan 0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 448969325a2SAndy Yan 0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10 449969325a2SAndy Yan }, 450969325a2SAndy Yan }; 451969325a2SAndy Yan 452969325a2SAndy Yan static struct inno_hdmi *bridge_to_inno_hdmi(struct drm_bridge *bridge) 453969325a2SAndy Yan { 454969325a2SAndy Yan return container_of(bridge, struct inno_hdmi, bridge); 455969325a2SAndy Yan } 456969325a2SAndy Yan 457969325a2SAndy Yan static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi, 458969325a2SAndy Yan unsigned long pixelclk) 459969325a2SAndy Yan { 460969325a2SAndy Yan const struct inno_hdmi_phy_config *phy_configs = hdmi->plat_data->phy_configs; 461969325a2SAndy Yan int i; 462969325a2SAndy Yan 463969325a2SAndy Yan for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) { 464969325a2SAndy Yan if (pixelclk <= phy_configs[i].pixelclock) 465969325a2SAndy Yan return i; 466969325a2SAndy Yan } 467969325a2SAndy Yan 468969325a2SAndy Yan DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %lu\n", 469969325a2SAndy Yan pixelclk); 470969325a2SAndy Yan 471969325a2SAndy Yan return -EINVAL; 472969325a2SAndy Yan } 473969325a2SAndy Yan 474969325a2SAndy Yan static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset) 475969325a2SAndy Yan { 476969325a2SAndy Yan return readl_relaxed(hdmi->regs + (offset) * 0x04); 477969325a2SAndy Yan } 478969325a2SAndy Yan 479969325a2SAndy Yan static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val) 480969325a2SAndy Yan { 481969325a2SAndy Yan writel_relaxed(val, hdmi->regs + (offset) * 0x04); 482969325a2SAndy Yan } 483969325a2SAndy Yan 484969325a2SAndy Yan static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset, 485969325a2SAndy Yan u32 msk, u32 val) 486969325a2SAndy Yan { 487969325a2SAndy Yan u8 temp = hdmi_readb(hdmi, offset) & ~msk; 488969325a2SAndy Yan 489969325a2SAndy Yan temp |= val & msk; 490969325a2SAndy Yan hdmi_writeb(hdmi, offset, temp); 491969325a2SAndy Yan } 492969325a2SAndy Yan 493969325a2SAndy Yan static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi, unsigned long long rate) 494969325a2SAndy Yan { 495969325a2SAndy Yan unsigned long long ddc_bus_freq = rate >> 2; 496969325a2SAndy Yan 497969325a2SAndy Yan do_div(ddc_bus_freq, HDMI_SCL_RATE); 498969325a2SAndy Yan 499969325a2SAndy Yan hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); 500969325a2SAndy Yan hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); 501969325a2SAndy Yan 502969325a2SAndy Yan /* Clear the EDID interrupt flag and mute the interrupt */ 503969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); 504969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); 505969325a2SAndy Yan } 506969325a2SAndy Yan 507969325a2SAndy Yan static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable) 508969325a2SAndy Yan { 509969325a2SAndy Yan if (enable) 510969325a2SAndy Yan hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON); 511969325a2SAndy Yan else 512969325a2SAndy Yan hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF); 513969325a2SAndy Yan } 514969325a2SAndy Yan 515969325a2SAndy Yan static void inno_hdmi_standby(struct inno_hdmi *hdmi) 516969325a2SAndy Yan { 517969325a2SAndy Yan inno_hdmi_sys_power(hdmi, false); 518969325a2SAndy Yan 519969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00); 520969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00); 521969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00); 522969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); 523969325a2SAndy Yan }; 524969325a2SAndy Yan 525969325a2SAndy Yan static void inno_hdmi_power_up(struct inno_hdmi *hdmi, 526969325a2SAndy Yan unsigned long mpixelclock) 527969325a2SAndy Yan { 528969325a2SAndy Yan struct inno_hdmi_phy_config *phy_config; 529969325a2SAndy Yan int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock); 530969325a2SAndy Yan 531969325a2SAndy Yan if (ret < 0) { 532969325a2SAndy Yan phy_config = hdmi->plat_data->default_phy_config; 533969325a2SAndy Yan DRM_DEV_ERROR(hdmi->dev, 534969325a2SAndy Yan "Using default phy configuration for TMDS rate %lu", 535969325a2SAndy Yan mpixelclock); 536969325a2SAndy Yan } else { 537969325a2SAndy Yan phy_config = &hdmi->plat_data->phy_configs[ret]; 538969325a2SAndy Yan } 539969325a2SAndy Yan 540969325a2SAndy Yan inno_hdmi_sys_power(hdmi, false); 541969325a2SAndy Yan 542969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis); 543969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control); 544969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); 545969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14); 546969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10); 547969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f); 548969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00); 549969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01); 550969325a2SAndy Yan 551969325a2SAndy Yan inno_hdmi_sys_power(hdmi, true); 552969325a2SAndy Yan }; 553969325a2SAndy Yan 554969325a2SAndy Yan static void inno_hdmi_init_hw(struct inno_hdmi *hdmi) 555969325a2SAndy Yan { 556969325a2SAndy Yan u32 val; 557969325a2SAndy Yan u32 msk; 558969325a2SAndy Yan 559969325a2SAndy Yan hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL); 560969325a2SAndy Yan usleep_range(100, 150); 561969325a2SAndy Yan 562969325a2SAndy Yan hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG); 563969325a2SAndy Yan usleep_range(100, 150); 564969325a2SAndy Yan 565969325a2SAndy Yan msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL; 566969325a2SAndy Yan val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH; 567969325a2SAndy Yan hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val); 568969325a2SAndy Yan 569969325a2SAndy Yan inno_hdmi_standby(hdmi); 570969325a2SAndy Yan 571969325a2SAndy Yan /* 572969325a2SAndy Yan * When the controller isn't configured to an accurate 573969325a2SAndy Yan * video timing and there is no reference clock available, 574969325a2SAndy Yan * then the TMDS clock source would be switched to PCLK_HDMI, 575969325a2SAndy Yan * so we need to init the TMDS rate to PCLK rate, and 576969325a2SAndy Yan * reconfigure the DDC clock. 577969325a2SAndy Yan */ 578969325a2SAndy Yan if (hdmi->refclk) 579969325a2SAndy Yan inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->refclk)); 580969325a2SAndy Yan else 581969325a2SAndy Yan inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->pclk)); 582969325a2SAndy Yan 583969325a2SAndy Yan /* Unmute hotplug interrupt */ 584969325a2SAndy Yan hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1)); 585969325a2SAndy Yan } 586969325a2SAndy Yan 587*b626b1a1SDmitry Baryshkov static int inno_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge) 588969325a2SAndy Yan { 589969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 590969325a2SAndy Yan 591969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); 592969325a2SAndy Yan 593969325a2SAndy Yan return 0; 594969325a2SAndy Yan } 595969325a2SAndy Yan 596*b626b1a1SDmitry Baryshkov static int inno_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge, 597969325a2SAndy Yan const u8 *buffer, size_t len) 598969325a2SAndy Yan { 599969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 600969325a2SAndy Yan ssize_t i; 601969325a2SAndy Yan 602*b626b1a1SDmitry Baryshkov inno_hdmi_bridge_clear_avi_infoframe(bridge); 603969325a2SAndy Yan 604969325a2SAndy Yan for (i = 0; i < len; i++) 605969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]); 606969325a2SAndy Yan 607969325a2SAndy Yan return 0; 608969325a2SAndy Yan } 609969325a2SAndy Yan 610*b626b1a1SDmitry Baryshkov static int inno_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge) 611*b626b1a1SDmitry Baryshkov { 612*b626b1a1SDmitry Baryshkov drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n"); 613*b626b1a1SDmitry Baryshkov 614*b626b1a1SDmitry Baryshkov return 0; 615*b626b1a1SDmitry Baryshkov } 616*b626b1a1SDmitry Baryshkov 617*b626b1a1SDmitry Baryshkov static int inno_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge, 618*b626b1a1SDmitry Baryshkov const u8 *buffer, size_t len) 619*b626b1a1SDmitry Baryshkov { 620*b626b1a1SDmitry Baryshkov drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n"); 621*b626b1a1SDmitry Baryshkov 622*b626b1a1SDmitry Baryshkov return 0; 623*b626b1a1SDmitry Baryshkov } 624*b626b1a1SDmitry Baryshkov 625969325a2SAndy Yan static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi, 626969325a2SAndy Yan struct drm_connector *connector, 627969325a2SAndy Yan struct drm_display_mode *mode) 628969325a2SAndy Yan { 629969325a2SAndy Yan struct drm_connector_state *conn_state = connector->state; 630969325a2SAndy Yan int c0_c2_change = 0; 631969325a2SAndy Yan int csc_enable = 0; 632969325a2SAndy Yan int csc_mode = 0; 633969325a2SAndy Yan int auto_csc = 0; 634969325a2SAndy Yan int value; 635969325a2SAndy Yan int i; 636969325a2SAndy Yan int colorimetry; 637969325a2SAndy Yan u8 vic = drm_match_cea_mode(mode); 638969325a2SAndy Yan 639969325a2SAndy Yan if (vic == 6 || vic == 7 || vic == 21 || vic == 22 || 640969325a2SAndy Yan vic == 2 || vic == 3 || vic == 17 || vic == 18) 641969325a2SAndy Yan colorimetry = HDMI_COLORIMETRY_ITU_601; 642969325a2SAndy Yan else 643969325a2SAndy Yan colorimetry = HDMI_COLORIMETRY_ITU_709; 644969325a2SAndy Yan 645969325a2SAndy Yan 646969325a2SAndy Yan /* Input video mode is SDR RGB24bit, data enable signal from external */ 647969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL | 648969325a2SAndy Yan v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444)); 649969325a2SAndy Yan 650969325a2SAndy Yan /* Input color hardcode to RGB, and output color hardcode to RGB888 */ 651969325a2SAndy Yan value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | 652969325a2SAndy Yan v_VIDEO_OUTPUT_COLOR(0) | 653969325a2SAndy Yan v_VIDEO_INPUT_CSP(0); 654969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value); 655969325a2SAndy Yan 656969325a2SAndy Yan if (conn_state->hdmi.output_format == HDMI_COLORSPACE_RGB) { 657969325a2SAndy Yan if (conn_state->hdmi.is_limited_range) { 658969325a2SAndy Yan csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT; 659969325a2SAndy Yan auto_csc = AUTO_CSC_DISABLE; 660969325a2SAndy Yan c0_c2_change = C0_C2_CHANGE_DISABLE; 661969325a2SAndy Yan csc_enable = v_CSC_ENABLE; 662969325a2SAndy Yan 663969325a2SAndy Yan } else { 664969325a2SAndy Yan value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1); 665969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value); 666969325a2SAndy Yan 667969325a2SAndy Yan hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, 668969325a2SAndy Yan m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP, 669969325a2SAndy Yan v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) | 670969325a2SAndy Yan v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE)); 671969325a2SAndy Yan return 0; 672969325a2SAndy Yan } 673969325a2SAndy Yan } else { 674969325a2SAndy Yan if (colorimetry == HDMI_COLORIMETRY_ITU_601) { 675969325a2SAndy Yan if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) { 676969325a2SAndy Yan csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT; 677969325a2SAndy Yan auto_csc = AUTO_CSC_DISABLE; 678969325a2SAndy Yan c0_c2_change = C0_C2_CHANGE_DISABLE; 679969325a2SAndy Yan csc_enable = v_CSC_ENABLE; 680969325a2SAndy Yan } 681969325a2SAndy Yan } else { 682969325a2SAndy Yan if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) { 683969325a2SAndy Yan csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT; 684969325a2SAndy Yan auto_csc = AUTO_CSC_DISABLE; 685969325a2SAndy Yan c0_c2_change = C0_C2_CHANGE_DISABLE; 686969325a2SAndy Yan csc_enable = v_CSC_ENABLE; 687969325a2SAndy Yan } 688969325a2SAndy Yan } 689969325a2SAndy Yan } 690969325a2SAndy Yan 691969325a2SAndy Yan for (i = 0; i < 24; i++) 692969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i, coeff_csc[csc_mode][i]); 693969325a2SAndy Yan 694969325a2SAndy Yan value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1); 695969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value); 696969325a2SAndy Yan hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC | 697969325a2SAndy Yan m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) | 698969325a2SAndy Yan v_VIDEO_C0_C2_SWAP(c0_c2_change)); 699969325a2SAndy Yan 700969325a2SAndy Yan return 0; 701969325a2SAndy Yan } 702969325a2SAndy Yan 703969325a2SAndy Yan static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, 704969325a2SAndy Yan struct drm_display_mode *mode) 705969325a2SAndy Yan { 706969325a2SAndy Yan const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops; 707969325a2SAndy Yan u32 value; 708969325a2SAndy Yan 709969325a2SAndy Yan if (plat_ops && plat_ops->enable) 710969325a2SAndy Yan plat_ops->enable(hdmi->dev, mode); 711969325a2SAndy Yan 712969325a2SAndy Yan /* Set detail external video timing polarity and interlace mode */ 713969325a2SAndy Yan value = v_EXTERANL_VIDEO(1); 714969325a2SAndy Yan value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? 715969325a2SAndy Yan v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0); 716969325a2SAndy Yan value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? 717969325a2SAndy Yan v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0); 718969325a2SAndy Yan value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 719969325a2SAndy Yan v_INETLACE(1) : v_INETLACE(0); 720969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value); 721969325a2SAndy Yan 722969325a2SAndy Yan /* Set detail external video timing */ 723969325a2SAndy Yan value = mode->htotal; 724969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF); 725969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); 726969325a2SAndy Yan 727969325a2SAndy Yan value = mode->htotal - mode->hdisplay; 728969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF); 729969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); 730969325a2SAndy Yan 731969325a2SAndy Yan value = mode->htotal - mode->hsync_start; 732969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF); 733969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); 734969325a2SAndy Yan 735969325a2SAndy Yan value = mode->hsync_end - mode->hsync_start; 736969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF); 737969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); 738969325a2SAndy Yan 739969325a2SAndy Yan value = mode->vtotal; 740969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF); 741969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); 742969325a2SAndy Yan 743969325a2SAndy Yan value = mode->vtotal - mode->vdisplay; 744969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF); 745969325a2SAndy Yan 746969325a2SAndy Yan value = mode->vtotal - mode->vsync_start; 747969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF); 748969325a2SAndy Yan 749969325a2SAndy Yan value = mode->vsync_end - mode->vsync_start; 750969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF); 751969325a2SAndy Yan 752969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e); 753969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c); 754969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01); 755969325a2SAndy Yan 756969325a2SAndy Yan return 0; 757969325a2SAndy Yan } 758969325a2SAndy Yan 759969325a2SAndy Yan static int inno_hdmi_setup(struct inno_hdmi *hdmi, struct drm_atomic_state *state) 760969325a2SAndy Yan { 761969325a2SAndy Yan struct drm_bridge *bridge = &hdmi->bridge; 762969325a2SAndy Yan struct drm_connector *connector; 763969325a2SAndy Yan struct drm_display_info *info; 764969325a2SAndy Yan struct drm_connector_state *new_conn_state; 765969325a2SAndy Yan struct drm_crtc_state *new_crtc_state; 766969325a2SAndy Yan 767969325a2SAndy Yan connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); 768969325a2SAndy Yan 769969325a2SAndy Yan new_conn_state = drm_atomic_get_new_connector_state(state, connector); 770969325a2SAndy Yan if (WARN_ON(!new_conn_state)) 771969325a2SAndy Yan return -EINVAL; 772969325a2SAndy Yan 773969325a2SAndy Yan new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); 774969325a2SAndy Yan if (WARN_ON(!new_crtc_state)) 775969325a2SAndy Yan return -EINVAL; 776969325a2SAndy Yan 777969325a2SAndy Yan info = &connector->display_info; 778969325a2SAndy Yan 779969325a2SAndy Yan /* Mute video and audio output */ 780969325a2SAndy Yan hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, 781969325a2SAndy Yan v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); 782969325a2SAndy Yan 783969325a2SAndy Yan /* Set HDMI Mode */ 784969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_HDCP_CTRL, v_HDMI_DVI(info->is_hdmi)); 785969325a2SAndy Yan 786969325a2SAndy Yan inno_hdmi_config_video_timing(hdmi, &new_crtc_state->adjusted_mode); 787969325a2SAndy Yan 788969325a2SAndy Yan inno_hdmi_config_video_csc(hdmi, connector, &new_crtc_state->adjusted_mode); 789969325a2SAndy Yan 790969325a2SAndy Yan drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); 791969325a2SAndy Yan 792969325a2SAndy Yan /* 793969325a2SAndy Yan * When IP controller have configured to an accurate video 794969325a2SAndy Yan * timing, then the TMDS clock source would be switched to 795969325a2SAndy Yan * DCLK_LCDC, so we need to init the TMDS rate to mode pixel 796969325a2SAndy Yan * clock rate, and reconfigure the DDC clock. 797969325a2SAndy Yan */ 798969325a2SAndy Yan inno_hdmi_i2c_init(hdmi, new_conn_state->hdmi.tmds_char_rate); 799969325a2SAndy Yan 800969325a2SAndy Yan /* Unmute video and audio output */ 801969325a2SAndy Yan hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, 802969325a2SAndy Yan v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); 803969325a2SAndy Yan 804969325a2SAndy Yan inno_hdmi_power_up(hdmi, new_conn_state->hdmi.tmds_char_rate); 805969325a2SAndy Yan 806969325a2SAndy Yan return 0; 807969325a2SAndy Yan } 808969325a2SAndy Yan 809969325a2SAndy Yan static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridge, 810969325a2SAndy Yan const struct drm_display_info *info, 811969325a2SAndy Yan const struct drm_display_mode *mode) 812969325a2SAndy Yan { 813969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 814969325a2SAndy Yan unsigned long mpixelclk, max_tolerance; 815969325a2SAndy Yan long rounded_refclk; 816969325a2SAndy Yan 817969325a2SAndy Yan /* No support for double-clock modes */ 818969325a2SAndy Yan if (mode->flags & DRM_MODE_FLAG_DBLCLK) 819969325a2SAndy Yan return MODE_BAD; 820969325a2SAndy Yan 821969325a2SAndy Yan mpixelclk = mode->clock * 1000; 822969325a2SAndy Yan 823969325a2SAndy Yan if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) 824969325a2SAndy Yan return MODE_CLOCK_LOW; 825969325a2SAndy Yan 826969325a2SAndy Yan if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0) 827969325a2SAndy Yan return MODE_CLOCK_HIGH; 828969325a2SAndy Yan 829969325a2SAndy Yan if (hdmi->refclk) { 830969325a2SAndy Yan rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk); 831969325a2SAndy Yan if (rounded_refclk < 0) 832969325a2SAndy Yan return MODE_BAD; 833969325a2SAndy Yan 834969325a2SAndy Yan /* Vesa DMT standard mentions +/- 0.5% max tolerance */ 835969325a2SAndy Yan max_tolerance = mpixelclk / 200; 836969325a2SAndy Yan if (abs_diff((unsigned long)rounded_refclk, mpixelclk) > max_tolerance) 837969325a2SAndy Yan return MODE_NOCLOCK; 838969325a2SAndy Yan } 839969325a2SAndy Yan 840969325a2SAndy Yan return MODE_OK; 841969325a2SAndy Yan } 842969325a2SAndy Yan 843969325a2SAndy Yan static enum drm_connector_status 844969325a2SAndy Yan inno_hdmi_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) 845969325a2SAndy Yan { 846969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 847969325a2SAndy Yan 848969325a2SAndy Yan return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ? 849969325a2SAndy Yan connector_status_connected : connector_status_disconnected; 850969325a2SAndy Yan } 851969325a2SAndy Yan 852969325a2SAndy Yan static const struct drm_edid * 853969325a2SAndy Yan inno_hdmi_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) 854969325a2SAndy Yan { 855969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 856969325a2SAndy Yan const struct drm_edid *drm_edid; 857969325a2SAndy Yan 858969325a2SAndy Yan drm_edid = drm_edid_read_ddc(connector, bridge->ddc); 859969325a2SAndy Yan if (!drm_edid) 860969325a2SAndy Yan dev_dbg(hdmi->dev, "failed to get edid\n"); 861969325a2SAndy Yan 862969325a2SAndy Yan return drm_edid; 863969325a2SAndy Yan } 864969325a2SAndy Yan 865969325a2SAndy Yan static void inno_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, 866969325a2SAndy Yan struct drm_atomic_state *state) 867969325a2SAndy Yan { 868969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 869969325a2SAndy Yan 870969325a2SAndy Yan inno_hdmi_setup(hdmi, state); 871969325a2SAndy Yan } 872969325a2SAndy Yan 873969325a2SAndy Yan static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, 874969325a2SAndy Yan struct drm_atomic_state *state) 875969325a2SAndy Yan { 876969325a2SAndy Yan struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); 877969325a2SAndy Yan 878969325a2SAndy Yan inno_hdmi_standby(hdmi); 879969325a2SAndy Yan } 880969325a2SAndy Yan 881969325a2SAndy Yan static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = { 882969325a2SAndy Yan .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 883969325a2SAndy Yan .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 884969325a2SAndy Yan .atomic_reset = drm_atomic_helper_bridge_reset, 885969325a2SAndy Yan .atomic_enable = inno_hdmi_bridge_atomic_enable, 886969325a2SAndy Yan .atomic_disable = inno_hdmi_bridge_atomic_disable, 887969325a2SAndy Yan .detect = inno_hdmi_bridge_detect, 888969325a2SAndy Yan .edid_read = inno_hdmi_bridge_edid_read, 889*b626b1a1SDmitry Baryshkov .hdmi_clear_avi_infoframe = inno_hdmi_bridge_clear_avi_infoframe, 890*b626b1a1SDmitry Baryshkov .hdmi_write_avi_infoframe = inno_hdmi_bridge_write_avi_infoframe, 891*b626b1a1SDmitry Baryshkov .hdmi_clear_hdmi_infoframe = inno_hdmi_bridge_clear_hdmi_infoframe, 892*b626b1a1SDmitry Baryshkov .hdmi_write_hdmi_infoframe = inno_hdmi_bridge_write_hdmi_infoframe, 893969325a2SAndy Yan .mode_valid = inno_hdmi_bridge_mode_valid, 894969325a2SAndy Yan }; 895969325a2SAndy Yan 896969325a2SAndy Yan static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi) 897969325a2SAndy Yan { 898969325a2SAndy Yan struct inno_hdmi_i2c *i2c = hdmi->i2c; 899969325a2SAndy Yan u8 stat; 900969325a2SAndy Yan 901969325a2SAndy Yan stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1); 902969325a2SAndy Yan if (!(stat & m_INT_EDID_READY)) 903969325a2SAndy Yan return IRQ_NONE; 904969325a2SAndy Yan 905969325a2SAndy Yan /* Clear HDMI EDID interrupt flag */ 906969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); 907969325a2SAndy Yan 908969325a2SAndy Yan complete(&i2c->cmp); 909969325a2SAndy Yan 910969325a2SAndy Yan return IRQ_HANDLED; 911969325a2SAndy Yan } 912969325a2SAndy Yan 913969325a2SAndy Yan static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id) 914969325a2SAndy Yan { 915969325a2SAndy Yan struct inno_hdmi *hdmi = dev_id; 916969325a2SAndy Yan irqreturn_t ret = IRQ_NONE; 917969325a2SAndy Yan u8 interrupt; 918969325a2SAndy Yan 919969325a2SAndy Yan if (hdmi->i2c) 920969325a2SAndy Yan ret = inno_hdmi_i2c_irq(hdmi); 921969325a2SAndy Yan 922969325a2SAndy Yan interrupt = hdmi_readb(hdmi, HDMI_STATUS); 923969325a2SAndy Yan if (interrupt & m_INT_HOTPLUG) { 924969325a2SAndy Yan hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG); 925969325a2SAndy Yan ret = IRQ_WAKE_THREAD; 926969325a2SAndy Yan } 927969325a2SAndy Yan 928969325a2SAndy Yan return ret; 929969325a2SAndy Yan } 930969325a2SAndy Yan 931969325a2SAndy Yan static irqreturn_t inno_hdmi_irq(int irq, void *dev_id) 932969325a2SAndy Yan { 933969325a2SAndy Yan struct inno_hdmi *hdmi = dev_id; 934969325a2SAndy Yan 935969325a2SAndy Yan drm_helper_hpd_irq_event(hdmi->bridge.dev); 936969325a2SAndy Yan 937969325a2SAndy Yan return IRQ_HANDLED; 938969325a2SAndy Yan } 939969325a2SAndy Yan 940969325a2SAndy Yan static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs) 941969325a2SAndy Yan { 942969325a2SAndy Yan int length = msgs->len; 943969325a2SAndy Yan u8 *buf = msgs->buf; 944969325a2SAndy Yan int ret; 945969325a2SAndy Yan 946969325a2SAndy Yan ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10); 947969325a2SAndy Yan if (!ret) 948969325a2SAndy Yan return -EAGAIN; 949969325a2SAndy Yan 950969325a2SAndy Yan while (length--) 951969325a2SAndy Yan *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR); 952969325a2SAndy Yan 953969325a2SAndy Yan return 0; 954969325a2SAndy Yan } 955969325a2SAndy Yan 956969325a2SAndy Yan static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs) 957969325a2SAndy Yan { 958969325a2SAndy Yan /* 959969325a2SAndy Yan * The DDC module only support read EDID message, so 960969325a2SAndy Yan * we assume that each word write to this i2c adapter 961969325a2SAndy Yan * should be the offset of EDID word address. 962969325a2SAndy Yan */ 963969325a2SAndy Yan if (msgs->len != 1 || (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) 964969325a2SAndy Yan return -EINVAL; 965969325a2SAndy Yan 966969325a2SAndy Yan reinit_completion(&hdmi->i2c->cmp); 967969325a2SAndy Yan 968969325a2SAndy Yan if (msgs->addr == DDC_SEGMENT_ADDR) 969969325a2SAndy Yan hdmi->i2c->segment_addr = msgs->buf[0]; 970969325a2SAndy Yan if (msgs->addr == DDC_ADDR) 971969325a2SAndy Yan hdmi->i2c->ddc_addr = msgs->buf[0]; 972969325a2SAndy Yan 973969325a2SAndy Yan /* Set edid fifo first addr */ 974969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00); 975969325a2SAndy Yan 976969325a2SAndy Yan /* Set edid word address 0x00/0x80 */ 977969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); 978969325a2SAndy Yan 979969325a2SAndy Yan /* Set edid segment pointer */ 980969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr); 981969325a2SAndy Yan 982969325a2SAndy Yan return 0; 983969325a2SAndy Yan } 984969325a2SAndy Yan 985969325a2SAndy Yan static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap, 986969325a2SAndy Yan struct i2c_msg *msgs, int num) 987969325a2SAndy Yan { 988969325a2SAndy Yan struct inno_hdmi *hdmi = i2c_get_adapdata(adap); 989969325a2SAndy Yan struct inno_hdmi_i2c *i2c = hdmi->i2c; 990969325a2SAndy Yan int i, ret = 0; 991969325a2SAndy Yan 992969325a2SAndy Yan mutex_lock(&i2c->lock); 993969325a2SAndy Yan 994969325a2SAndy Yan /* Clear the EDID interrupt flag and unmute the interrupt */ 995969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY); 996969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); 997969325a2SAndy Yan 998969325a2SAndy Yan for (i = 0; i < num; i++) { 999969325a2SAndy Yan DRM_DEV_DEBUG(hdmi->dev, 1000969325a2SAndy Yan "xfer: num: %d/%d, len: %d, flags: %#x\n", 1001969325a2SAndy Yan i + 1, num, msgs[i].len, msgs[i].flags); 1002969325a2SAndy Yan 1003969325a2SAndy Yan if (msgs[i].flags & I2C_M_RD) 1004969325a2SAndy Yan ret = inno_hdmi_i2c_read(hdmi, &msgs[i]); 1005969325a2SAndy Yan else 1006969325a2SAndy Yan ret = inno_hdmi_i2c_write(hdmi, &msgs[i]); 1007969325a2SAndy Yan 1008969325a2SAndy Yan if (ret < 0) 1009969325a2SAndy Yan break; 1010969325a2SAndy Yan } 1011969325a2SAndy Yan 1012969325a2SAndy Yan if (!ret) 1013969325a2SAndy Yan ret = num; 1014969325a2SAndy Yan 1015969325a2SAndy Yan /* Mute HDMI EDID interrupt */ 1016969325a2SAndy Yan hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); 1017969325a2SAndy Yan 1018969325a2SAndy Yan mutex_unlock(&i2c->lock); 1019969325a2SAndy Yan 1020969325a2SAndy Yan return ret; 1021969325a2SAndy Yan } 1022969325a2SAndy Yan 1023969325a2SAndy Yan static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter) 1024969325a2SAndy Yan { 1025969325a2SAndy Yan return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 1026969325a2SAndy Yan } 1027969325a2SAndy Yan 1028969325a2SAndy Yan static const struct i2c_algorithm inno_hdmi_algorithm = { 1029969325a2SAndy Yan .master_xfer = inno_hdmi_i2c_xfer, 1030969325a2SAndy Yan .functionality = inno_hdmi_i2c_func, 1031969325a2SAndy Yan }; 1032969325a2SAndy Yan 1033969325a2SAndy Yan static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi) 1034969325a2SAndy Yan { 1035969325a2SAndy Yan struct i2c_adapter *adap; 1036969325a2SAndy Yan struct inno_hdmi_i2c *i2c; 1037969325a2SAndy Yan int ret; 1038969325a2SAndy Yan 1039969325a2SAndy Yan i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); 1040969325a2SAndy Yan if (!i2c) 1041969325a2SAndy Yan return ERR_PTR(-ENOMEM); 1042969325a2SAndy Yan 1043969325a2SAndy Yan mutex_init(&i2c->lock); 1044969325a2SAndy Yan init_completion(&i2c->cmp); 1045969325a2SAndy Yan 1046969325a2SAndy Yan adap = &i2c->adap; 1047969325a2SAndy Yan adap->owner = THIS_MODULE; 1048969325a2SAndy Yan adap->dev.parent = hdmi->dev; 1049969325a2SAndy Yan adap->dev.of_node = hdmi->dev->of_node; 1050969325a2SAndy Yan adap->algo = &inno_hdmi_algorithm; 1051969325a2SAndy Yan strscpy(adap->name, "Inno HDMI", sizeof(adap->name)); 1052969325a2SAndy Yan i2c_set_adapdata(adap, hdmi); 1053969325a2SAndy Yan 1054969325a2SAndy Yan ret = devm_i2c_add_adapter(hdmi->dev, adap); 1055969325a2SAndy Yan if (ret) { 1056969325a2SAndy Yan dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); 1057969325a2SAndy Yan return ERR_PTR(ret); 1058969325a2SAndy Yan } 1059969325a2SAndy Yan 1060969325a2SAndy Yan hdmi->i2c = i2c; 1061969325a2SAndy Yan 1062969325a2SAndy Yan DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name); 1063969325a2SAndy Yan 1064969325a2SAndy Yan return adap; 1065969325a2SAndy Yan } 1066969325a2SAndy Yan 1067969325a2SAndy Yan struct inno_hdmi *inno_hdmi_bind(struct device *dev, 1068969325a2SAndy Yan struct drm_encoder *encoder, 1069969325a2SAndy Yan const struct inno_hdmi_plat_data *plat_data) 1070969325a2SAndy Yan { 1071969325a2SAndy Yan struct platform_device *pdev = to_platform_device(dev); 1072969325a2SAndy Yan struct inno_hdmi *hdmi; 1073969325a2SAndy Yan int irq; 1074969325a2SAndy Yan int ret; 1075969325a2SAndy Yan 1076969325a2SAndy Yan if (!plat_data->phy_configs || !plat_data->default_phy_config) { 1077969325a2SAndy Yan dev_err(dev, "Missing platform PHY ops\n"); 1078969325a2SAndy Yan return ERR_PTR(-ENODEV); 1079969325a2SAndy Yan } 1080969325a2SAndy Yan 1081969325a2SAndy Yan hdmi = devm_drm_bridge_alloc(dev, struct inno_hdmi, bridge, &inno_hdmi_bridge_funcs); 1082969325a2SAndy Yan if (IS_ERR(hdmi)) 1083969325a2SAndy Yan return ERR_CAST(hdmi); 1084969325a2SAndy Yan 1085969325a2SAndy Yan hdmi->dev = dev; 1086969325a2SAndy Yan hdmi->plat_data = plat_data; 1087969325a2SAndy Yan 1088969325a2SAndy Yan hdmi->regs = devm_platform_ioremap_resource(pdev, 0); 1089969325a2SAndy Yan if (IS_ERR(hdmi->regs)) 1090969325a2SAndy Yan return ERR_CAST(hdmi->regs); 1091969325a2SAndy Yan 1092969325a2SAndy Yan hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk"); 1093969325a2SAndy Yan if (IS_ERR(hdmi->pclk)) { 1094969325a2SAndy Yan dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n"); 1095969325a2SAndy Yan return ERR_CAST(hdmi->pclk); 1096969325a2SAndy Yan } 1097969325a2SAndy Yan 1098969325a2SAndy Yan hdmi->refclk = devm_clk_get_optional_enabled(hdmi->dev, "ref"); 1099969325a2SAndy Yan if (IS_ERR(hdmi->refclk)) { 1100969325a2SAndy Yan dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n"); 1101969325a2SAndy Yan return ERR_CAST(hdmi->refclk); 1102969325a2SAndy Yan } 1103969325a2SAndy Yan 1104969325a2SAndy Yan inno_hdmi_init_hw(hdmi); 1105969325a2SAndy Yan 1106969325a2SAndy Yan irq = platform_get_irq(pdev, 0); 1107969325a2SAndy Yan if (irq < 0) 1108969325a2SAndy Yan return ERR_PTR(irq); 1109969325a2SAndy Yan 1110969325a2SAndy Yan ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq, 1111969325a2SAndy Yan inno_hdmi_irq, IRQF_SHARED, 1112969325a2SAndy Yan dev_name(dev), hdmi); 1113969325a2SAndy Yan if (ret) 1114969325a2SAndy Yan return ERR_PTR(ret); 1115969325a2SAndy Yan 1116969325a2SAndy Yan hdmi->bridge.driver_private = hdmi; 1117969325a2SAndy Yan hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | 1118969325a2SAndy Yan DRM_BRIDGE_OP_EDID | 1119969325a2SAndy Yan DRM_BRIDGE_OP_HDMI | 1120969325a2SAndy Yan DRM_BRIDGE_OP_HPD; 1121969325a2SAndy Yan hdmi->bridge.of_node = pdev->dev.of_node; 1122969325a2SAndy Yan hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; 1123969325a2SAndy Yan hdmi->bridge.vendor = "Inno"; 1124969325a2SAndy Yan hdmi->bridge.product = "Inno HDMI"; 1125969325a2SAndy Yan 1126969325a2SAndy Yan hdmi->bridge.ddc = inno_hdmi_i2c_adapter(hdmi); 1127969325a2SAndy Yan if (IS_ERR(hdmi->bridge.ddc)) 1128969325a2SAndy Yan return ERR_CAST(hdmi->bridge.ddc); 1129969325a2SAndy Yan 1130969325a2SAndy Yan ret = devm_drm_bridge_add(dev, &hdmi->bridge); 1131969325a2SAndy Yan if (ret) 1132969325a2SAndy Yan return ERR_PTR(ret); 1133969325a2SAndy Yan 1134969325a2SAndy Yan ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); 1135969325a2SAndy Yan if (ret) 1136969325a2SAndy Yan return ERR_PTR(ret); 1137969325a2SAndy Yan 1138969325a2SAndy Yan return hdmi; 1139969325a2SAndy Yan } 1140969325a2SAndy Yan EXPORT_SYMBOL_GPL(inno_hdmi_bind); 1141969325a2SAndy Yan MODULE_AUTHOR("Andy Yan <andyshrk@163.com>"); 1142969325a2SAndy Yan MODULE_DESCRIPTION("INNOSILICON HDMI transmitter library"); 1143969325a2SAndy Yan MODULE_LICENSE("GPL"); 1144