xref: /linux/drivers/gpu/drm/bridge/inno-hdmi.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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