1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * DesignWare High-Definition Multimedia Interface (HDMI) driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2013-2015 Mentor Graphics Inc.
6*4882a593Smuzhiyun * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
7*4882a593Smuzhiyun * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/err.h>
12*4882a593Smuzhiyun #include <linux/extcon.h>
13*4882a593Smuzhiyun #include <linux/extcon-provider.h>
14*4882a593Smuzhiyun #include <linux/hdmi.h>
15*4882a593Smuzhiyun #include <linux/irq.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/mutex.h>
18*4882a593Smuzhiyun #include <linux/of_device.h>
19*4882a593Smuzhiyun #include <linux/pinctrl/consumer.h>
20*4882a593Smuzhiyun #include <linux/regmap.h>
21*4882a593Smuzhiyun #include <linux/dma-mapping.h>
22*4882a593Smuzhiyun #include <linux/spinlock.h>
23*4882a593Smuzhiyun #include <linux/pinctrl/consumer.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <media/cec-notifier.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #include <uapi/linux/media-bus-format.h>
28*4882a593Smuzhiyun #include <uapi/linux/videodev2.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <drm/bridge/dw_hdmi.h>
31*4882a593Smuzhiyun #include <drm/drm_atomic.h>
32*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
33*4882a593Smuzhiyun #include <drm/drm_bridge.h>
34*4882a593Smuzhiyun #include <drm/drm_edid.h>
35*4882a593Smuzhiyun #include <drm/drm_of.h>
36*4882a593Smuzhiyun #include <drm/drm_print.h>
37*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
38*4882a593Smuzhiyun #include <drm/drm_scdc_helper.h>
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #include "dw-hdmi-audio.h"
41*4882a593Smuzhiyun #include "dw-hdmi-cec.h"
42*4882a593Smuzhiyun #include "dw-hdmi-hdcp.h"
43*4882a593Smuzhiyun #include "dw-hdmi.h"
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #define DDC_CI_ADDR 0x37
46*4882a593Smuzhiyun #define DDC_SEGMENT_ADDR 0x30
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun #define HDMI_EDID_LEN 512
49*4882a593Smuzhiyun #define HDMI_EDID_BLOCK_LEN 128
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
52*4882a593Smuzhiyun #define SCDC_MIN_SOURCE_VERSION 0x1
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun #define HDMI14_MAX_TMDSCLK 340000000
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun static const unsigned int dw_hdmi_cable[] = {
57*4882a593Smuzhiyun EXTCON_DISP_HDMI,
58*4882a593Smuzhiyun EXTCON_NONE,
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun enum hdmi_datamap {
62*4882a593Smuzhiyun RGB444_8B = 0x01,
63*4882a593Smuzhiyun RGB444_10B = 0x03,
64*4882a593Smuzhiyun RGB444_12B = 0x05,
65*4882a593Smuzhiyun RGB444_16B = 0x07,
66*4882a593Smuzhiyun YCbCr444_8B = 0x09,
67*4882a593Smuzhiyun YCbCr444_10B = 0x0B,
68*4882a593Smuzhiyun YCbCr444_12B = 0x0D,
69*4882a593Smuzhiyun YCbCr444_16B = 0x0F,
70*4882a593Smuzhiyun YCbCr422_8B = 0x16,
71*4882a593Smuzhiyun YCbCr422_10B = 0x14,
72*4882a593Smuzhiyun YCbCr422_12B = 0x12,
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun * Unless otherwise noted, entries in this table are 100% optimization.
77*4882a593Smuzhiyun * Values can be obtained from hdmi_compute_n() but that function is
78*4882a593Smuzhiyun * slow so we pre-compute values we expect to see.
79*4882a593Smuzhiyun *
80*4882a593Smuzhiyun * All 32k and 48k values are expected to be the same (due to the way
81*4882a593Smuzhiyun * the math works) for any rate that's an exact kHz.
82*4882a593Smuzhiyun */
83*4882a593Smuzhiyun static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
84*4882a593Smuzhiyun { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
85*4882a593Smuzhiyun { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
86*4882a593Smuzhiyun { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
87*4882a593Smuzhiyun { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
88*4882a593Smuzhiyun { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
89*4882a593Smuzhiyun { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
90*4882a593Smuzhiyun { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
91*4882a593Smuzhiyun { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
92*4882a593Smuzhiyun { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
93*4882a593Smuzhiyun { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
94*4882a593Smuzhiyun { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
95*4882a593Smuzhiyun { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
96*4882a593Smuzhiyun { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
97*4882a593Smuzhiyun { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
98*4882a593Smuzhiyun { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
99*4882a593Smuzhiyun { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
100*4882a593Smuzhiyun { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
101*4882a593Smuzhiyun { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
102*4882a593Smuzhiyun { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
103*4882a593Smuzhiyun { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
104*4882a593Smuzhiyun { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
105*4882a593Smuzhiyun { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
106*4882a593Smuzhiyun { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
107*4882a593Smuzhiyun { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
108*4882a593Smuzhiyun { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
109*4882a593Smuzhiyun { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
110*4882a593Smuzhiyun { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
111*4882a593Smuzhiyun { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
112*4882a593Smuzhiyun { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
113*4882a593Smuzhiyun { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
114*4882a593Smuzhiyun { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
115*4882a593Smuzhiyun { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
116*4882a593Smuzhiyun { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
117*4882a593Smuzhiyun { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
118*4882a593Smuzhiyun { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
119*4882a593Smuzhiyun { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
120*4882a593Smuzhiyun { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* For 297 MHz+ HDMI spec have some other rule for setting N */
123*4882a593Smuzhiyun { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
124*4882a593Smuzhiyun { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* End of table */
127*4882a593Smuzhiyun { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static const u16 csc_coeff_default[3][4] = {
131*4882a593Smuzhiyun { 0x2000, 0x0000, 0x0000, 0x0000 },
132*4882a593Smuzhiyun { 0x0000, 0x2000, 0x0000, 0x0000 },
133*4882a593Smuzhiyun { 0x0000, 0x0000, 0x2000, 0x0000 }
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
137*4882a593Smuzhiyun { 0x2000, 0x6926, 0x74fd, 0x010e },
138*4882a593Smuzhiyun { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
139*4882a593Smuzhiyun { 0x2000, 0x0000, 0x38b4, 0x7e3b }
140*4882a593Smuzhiyun };
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
143*4882a593Smuzhiyun { 0x2000, 0x7106, 0x7a02, 0x00a7 },
144*4882a593Smuzhiyun { 0x2000, 0x3264, 0x0000, 0x7e6d },
145*4882a593Smuzhiyun { 0x2000, 0x0000, 0x3b61, 0x7e25 }
146*4882a593Smuzhiyun };
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
149*4882a593Smuzhiyun { 0x2591, 0x1322, 0x074b, 0x0000 },
150*4882a593Smuzhiyun { 0x6535, 0x2000, 0x7acc, 0x0200 },
151*4882a593Smuzhiyun { 0x6acd, 0x7534, 0x2000, 0x0200 }
152*4882a593Smuzhiyun };
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
155*4882a593Smuzhiyun { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
156*4882a593Smuzhiyun { 0x62f0, 0x2000, 0x7d11, 0x0200 },
157*4882a593Smuzhiyun { 0x6756, 0x78ab, 0x2000, 0x0200 }
158*4882a593Smuzhiyun };
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
161*4882a593Smuzhiyun { 0x1b7c, 0x0000, 0x0000, 0x0020 },
162*4882a593Smuzhiyun { 0x0000, 0x1b7c, 0x0000, 0x0020 },
163*4882a593Smuzhiyun { 0x0000, 0x0000, 0x1b7c, 0x0020 }
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun static const struct drm_display_mode dw_hdmi_default_modes[] = {
167*4882a593Smuzhiyun /* 4 - 1280x720@60Hz 16:9 */
168*4882a593Smuzhiyun { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
169*4882a593Smuzhiyun 1430, 1650, 0, 720, 725, 730, 750, 0,
170*4882a593Smuzhiyun DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
171*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
172*4882a593Smuzhiyun /* 16 - 1920x1080@60Hz 16:9 */
173*4882a593Smuzhiyun { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
174*4882a593Smuzhiyun 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
175*4882a593Smuzhiyun DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
176*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
177*4882a593Smuzhiyun /* 31 - 1920x1080@50Hz 16:9 */
178*4882a593Smuzhiyun { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
179*4882a593Smuzhiyun 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
180*4882a593Smuzhiyun DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
181*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
182*4882a593Smuzhiyun /* 19 - 1280x720@50Hz 16:9 */
183*4882a593Smuzhiyun { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
184*4882a593Smuzhiyun 1760, 1980, 0, 720, 725, 730, 750, 0,
185*4882a593Smuzhiyun DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
186*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
187*4882a593Smuzhiyun /* 17 - 720x576@50Hz 4:3 */
188*4882a593Smuzhiyun { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
189*4882a593Smuzhiyun 796, 864, 0, 576, 581, 586, 625, 0,
190*4882a593Smuzhiyun DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
191*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
192*4882a593Smuzhiyun /* 2 - 720x480@60Hz 4:3 */
193*4882a593Smuzhiyun { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
194*4882a593Smuzhiyun 798, 858, 0, 480, 489, 495, 525, 0,
195*4882a593Smuzhiyun DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
196*4882a593Smuzhiyun .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
197*4882a593Smuzhiyun };
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun struct hdmi_vmode {
200*4882a593Smuzhiyun bool mdataenablepolarity;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun unsigned int previous_pixelclock;
203*4882a593Smuzhiyun unsigned int mpixelclock;
204*4882a593Smuzhiyun unsigned int mpixelrepetitioninput;
205*4882a593Smuzhiyun unsigned int mpixelrepetitionoutput;
206*4882a593Smuzhiyun unsigned int previous_tmdsclock;
207*4882a593Smuzhiyun unsigned int mtmdsclock;
208*4882a593Smuzhiyun };
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun struct hdmi_data_info {
211*4882a593Smuzhiyun unsigned int enc_in_bus_format;
212*4882a593Smuzhiyun unsigned int enc_out_bus_format;
213*4882a593Smuzhiyun unsigned int enc_in_encoding;
214*4882a593Smuzhiyun unsigned int enc_out_encoding;
215*4882a593Smuzhiyun unsigned int quant_range;
216*4882a593Smuzhiyun unsigned int pix_repet_factor;
217*4882a593Smuzhiyun struct hdmi_vmode video_mode;
218*4882a593Smuzhiyun bool rgb_limited_range;
219*4882a593Smuzhiyun };
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun struct dw_hdmi_i2c {
222*4882a593Smuzhiyun struct i2c_adapter adap;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun struct mutex lock; /* used to serialize data transfers */
225*4882a593Smuzhiyun struct completion cmp;
226*4882a593Smuzhiyun u8 stat;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun u8 slave_reg;
229*4882a593Smuzhiyun bool is_regaddr;
230*4882a593Smuzhiyun bool is_segment;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun unsigned int scl_high_ns;
233*4882a593Smuzhiyun unsigned int scl_low_ns;
234*4882a593Smuzhiyun };
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun struct dw_hdmi_phy_data {
237*4882a593Smuzhiyun enum dw_hdmi_phy_type type;
238*4882a593Smuzhiyun const char *name;
239*4882a593Smuzhiyun unsigned int gen;
240*4882a593Smuzhiyun bool has_svsret;
241*4882a593Smuzhiyun int (*configure)(struct dw_hdmi *hdmi,
242*4882a593Smuzhiyun const struct dw_hdmi_plat_data *pdata,
243*4882a593Smuzhiyun unsigned long mpixelclock);
244*4882a593Smuzhiyun };
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun struct dw_hdmi {
247*4882a593Smuzhiyun struct drm_connector connector;
248*4882a593Smuzhiyun struct drm_bridge bridge;
249*4882a593Smuzhiyun struct drm_bridge *next_bridge;
250*4882a593Smuzhiyun struct platform_device *hdcp_dev;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun unsigned int version;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun struct platform_device *audio;
255*4882a593Smuzhiyun struct platform_device *cec;
256*4882a593Smuzhiyun struct device *dev;
257*4882a593Smuzhiyun struct clk *isfr_clk;
258*4882a593Smuzhiyun struct clk *iahb_clk;
259*4882a593Smuzhiyun struct clk *cec_clk;
260*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun struct hdmi_data_info hdmi_data;
263*4882a593Smuzhiyun const struct dw_hdmi_plat_data *plat_data;
264*4882a593Smuzhiyun const struct dw_hdmi_cec_wake_ops *cec_ops;
265*4882a593Smuzhiyun struct dw_hdcp *hdcp;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun int vic;
268*4882a593Smuzhiyun int irq;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun u8 edid[HDMI_EDID_LEN];
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun struct {
273*4882a593Smuzhiyun const struct dw_hdmi_phy_ops *ops;
274*4882a593Smuzhiyun const char *name;
275*4882a593Smuzhiyun void *data;
276*4882a593Smuzhiyun bool enabled;
277*4882a593Smuzhiyun } phy;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun struct drm_display_mode previous_mode;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun struct i2c_adapter *ddc;
282*4882a593Smuzhiyun void __iomem *regs;
283*4882a593Smuzhiyun bool sink_is_hdmi;
284*4882a593Smuzhiyun bool sink_has_audio;
285*4882a593Smuzhiyun bool hpd_state;
286*4882a593Smuzhiyun bool support_hdmi;
287*4882a593Smuzhiyun bool force_logo;
288*4882a593Smuzhiyun int force_output;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun struct delayed_work work;
291*4882a593Smuzhiyun struct workqueue_struct *workqueue;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun struct pinctrl *pinctrl;
294*4882a593Smuzhiyun struct pinctrl_state *default_state;
295*4882a593Smuzhiyun struct pinctrl_state *unwedge_state;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun struct mutex mutex; /* for state below and previous_mode */
298*4882a593Smuzhiyun enum drm_connector_force force; /* mutex-protected force state */
299*4882a593Smuzhiyun struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
300*4882a593Smuzhiyun bool disabled; /* DRM has disabled our bridge */
301*4882a593Smuzhiyun bool bridge_is_on; /* indicates the bridge is on */
302*4882a593Smuzhiyun bool rxsense; /* rxsense state */
303*4882a593Smuzhiyun u8 phy_mask; /* desired phy int mask settings */
304*4882a593Smuzhiyun u8 mc_clkdis; /* clock disable register */
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun spinlock_t audio_lock;
307*4882a593Smuzhiyun struct mutex audio_mutex;
308*4882a593Smuzhiyun struct dentry *debugfs_dir;
309*4882a593Smuzhiyun unsigned int sample_rate;
310*4882a593Smuzhiyun unsigned int audio_cts;
311*4882a593Smuzhiyun unsigned int audio_n;
312*4882a593Smuzhiyun bool audio_enable;
313*4882a593Smuzhiyun bool scramble_low_rates;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun struct extcon_dev *extcon;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun unsigned int reg_shift;
318*4882a593Smuzhiyun struct regmap *regm;
319*4882a593Smuzhiyun void (*enable_audio)(struct dw_hdmi *hdmi);
320*4882a593Smuzhiyun void (*disable_audio)(struct dw_hdmi *hdmi);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun struct mutex cec_notifier_mutex;
323*4882a593Smuzhiyun struct cec_notifier *cec_notifier;
324*4882a593Smuzhiyun struct cec_adapter *cec_adap;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun hdmi_codec_plugged_cb plugged_cb;
327*4882a593Smuzhiyun struct device *codec_dev;
328*4882a593Smuzhiyun enum drm_connector_status last_connector_result;
329*4882a593Smuzhiyun bool initialized; /* hdmi is enabled before bind */
330*4882a593Smuzhiyun bool logo_plug_out; /* hdmi is plug out when kernel logo */
331*4882a593Smuzhiyun bool update;
332*4882a593Smuzhiyun bool hdr2sdr; /* from hdr to sdr */
333*4882a593Smuzhiyun };
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun #define HDMI_IH_PHY_STAT0_RX_SENSE \
336*4882a593Smuzhiyun (HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
337*4882a593Smuzhiyun HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun #define HDMI_PHY_RX_SENSE \
340*4882a593Smuzhiyun (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
341*4882a593Smuzhiyun HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
342*4882a593Smuzhiyun
hdmi_writeb(struct dw_hdmi * hdmi,u8 val,int offset)343*4882a593Smuzhiyun static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun regmap_write(hdmi->regm, offset << hdmi->reg_shift, val);
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
hdmi_readb(struct dw_hdmi * hdmi,int offset)348*4882a593Smuzhiyun static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun unsigned int val = 0;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun regmap_read(hdmi->regm, offset << hdmi->reg_shift, &val);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun return val;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
handle_plugged_change(struct dw_hdmi * hdmi,bool plugged)357*4882a593Smuzhiyun static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun if (hdmi->plugged_cb && hdmi->codec_dev)
360*4882a593Smuzhiyun hdmi->plugged_cb(hdmi->codec_dev, plugged);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun
dw_hdmi_set_plugged_cb(struct dw_hdmi * hdmi,hdmi_codec_plugged_cb fn,struct device * codec_dev)363*4882a593Smuzhiyun int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
364*4882a593Smuzhiyun struct device *codec_dev)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun bool plugged;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
369*4882a593Smuzhiyun hdmi->plugged_cb = fn;
370*4882a593Smuzhiyun hdmi->codec_dev = codec_dev;
371*4882a593Smuzhiyun plugged = hdmi->last_connector_result == connector_status_connected;
372*4882a593Smuzhiyun handle_plugged_change(hdmi, plugged);
373*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun return 0;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb);
378*4882a593Smuzhiyun
hdmi_modb(struct dw_hdmi * hdmi,u8 data,u8 mask,unsigned reg)379*4882a593Smuzhiyun static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
hdmi_mask_writeb(struct dw_hdmi * hdmi,u8 data,unsigned int reg,u8 shift,u8 mask)384*4882a593Smuzhiyun static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
385*4882a593Smuzhiyun u8 shift, u8 mask)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun hdmi_modb(hdmi, data << shift, mask, reg);
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
dw_hdmi_check_output_type_changed(struct dw_hdmi * hdmi)390*4882a593Smuzhiyun static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun bool sink_hdmi;
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun sink_hdmi = hdmi->sink_is_hdmi;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun if (hdmi->force_output == 1)
397*4882a593Smuzhiyun hdmi->sink_is_hdmi = true;
398*4882a593Smuzhiyun else if (hdmi->force_output == 2)
399*4882a593Smuzhiyun hdmi->sink_is_hdmi = false;
400*4882a593Smuzhiyun else
401*4882a593Smuzhiyun hdmi->sink_is_hdmi = hdmi->support_hdmi;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun if (sink_hdmi != hdmi->sink_is_hdmi)
404*4882a593Smuzhiyun return true;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun return false;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
repo_hpd_event(struct work_struct * p_work)409*4882a593Smuzhiyun static void repo_hpd_event(struct work_struct *p_work)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work);
412*4882a593Smuzhiyun enum drm_connector_status status = hdmi->hpd_state ?
413*4882a593Smuzhiyun connector_status_connected : connector_status_disconnected;
414*4882a593Smuzhiyun u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
417*4882a593Smuzhiyun if (!(phy_stat & HDMI_PHY_RX_SENSE))
418*4882a593Smuzhiyun hdmi->rxsense = false;
419*4882a593Smuzhiyun if (phy_stat & HDMI_PHY_HPD)
420*4882a593Smuzhiyun hdmi->rxsense = true;
421*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun if (hdmi->bridge.dev) {
424*4882a593Smuzhiyun bool change;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun change = drm_helper_hpd_irq_event(hdmi->bridge.dev);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun if (change && hdmi->cec_adap &&
429*4882a593Smuzhiyun hdmi->cec_adap->devnode.registered)
430*4882a593Smuzhiyun cec_queue_pin_hpd_event(hdmi->cec_adap,
431*4882a593Smuzhiyun hdmi->hpd_state,
432*4882a593Smuzhiyun ktime_get());
433*4882a593Smuzhiyun drm_bridge_hpd_notify(&hdmi->bridge, status);
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
check_hdmi_irq(struct dw_hdmi * hdmi,int intr_stat,int phy_int_pol)437*4882a593Smuzhiyun static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat,
438*4882a593Smuzhiyun int phy_int_pol)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun int msecs;
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun /* To determine whether interrupt type is HPD */
443*4882a593Smuzhiyun if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD))
444*4882a593Smuzhiyun return false;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun if (phy_int_pol & HDMI_PHY_HPD) {
447*4882a593Smuzhiyun dev_dbg(hdmi->dev, "dw hdmi plug in\n");
448*4882a593Smuzhiyun msecs = 150;
449*4882a593Smuzhiyun hdmi->hpd_state = true;
450*4882a593Smuzhiyun } else {
451*4882a593Smuzhiyun dev_dbg(hdmi->dev, "dw hdmi plug out\n");
452*4882a593Smuzhiyun msecs = 20;
453*4882a593Smuzhiyun hdmi->hpd_state = false;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun return true;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun
init_hpd_work(struct dw_hdmi * hdmi)460*4882a593Smuzhiyun static void init_hpd_work(struct dw_hdmi *hdmi)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun hdmi->workqueue = create_workqueue("hpd_queue");
463*4882a593Smuzhiyun INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
dw_hdmi_i2c_set_divs(struct dw_hdmi * hdmi)466*4882a593Smuzhiyun static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun unsigned long clk_rate_khz;
469*4882a593Smuzhiyun unsigned long low_ns, high_ns;
470*4882a593Smuzhiyun unsigned long div_low, div_high;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun /* Standard-mode */
473*4882a593Smuzhiyun if (hdmi->i2c->scl_high_ns < 4000)
474*4882a593Smuzhiyun high_ns = 4708;
475*4882a593Smuzhiyun else
476*4882a593Smuzhiyun high_ns = hdmi->i2c->scl_high_ns;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun if (hdmi->i2c->scl_low_ns < 4700)
479*4882a593Smuzhiyun low_ns = 4916;
480*4882a593Smuzhiyun else
481*4882a593Smuzhiyun low_ns = hdmi->i2c->scl_low_ns;
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun /* Adjust to avoid overflow */
484*4882a593Smuzhiyun clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000);
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun div_low = (clk_rate_khz * low_ns) / 1000000;
487*4882a593Smuzhiyun if ((clk_rate_khz * low_ns) % 1000000)
488*4882a593Smuzhiyun div_low++;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun div_high = (clk_rate_khz * high_ns) / 1000000;
491*4882a593Smuzhiyun if ((clk_rate_khz * high_ns) % 1000000)
492*4882a593Smuzhiyun div_high++;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /* Maximum divider supported by hw is 0xffff */
495*4882a593Smuzhiyun if (div_low > 0xffff)
496*4882a593Smuzhiyun div_low = 0xffff;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun if (div_high > 0xffff)
499*4882a593Smuzhiyun div_high = 0xffff;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
502*4882a593Smuzhiyun hdmi_writeb(hdmi, (div_high >> 8) & 0xff,
503*4882a593Smuzhiyun HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
504*4882a593Smuzhiyun hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
505*4882a593Smuzhiyun hdmi_writeb(hdmi, (div_low >> 8) & 0xff,
506*4882a593Smuzhiyun HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
dw_hdmi_i2c_init(struct dw_hdmi * hdmi)509*4882a593Smuzhiyun static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
512*4882a593Smuzhiyun HDMI_PHY_I2CM_INT_ADDR);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
515*4882a593Smuzhiyun HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
516*4882a593Smuzhiyun HDMI_PHY_I2CM_CTLINT_ADDR);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun /* Software reset */
519*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun /* Set Standard Mode speed (determined to be 100KHz on iMX6) */
522*4882a593Smuzhiyun hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE,
523*4882a593Smuzhiyun HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV);
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun /* Set done, not acknowledged and arbitration interrupt polarities */
526*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
527*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
528*4882a593Smuzhiyun HDMI_I2CM_CTLINT);
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun /* Clear DONE and ERROR interrupts */
531*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
532*4882a593Smuzhiyun HDMI_IH_I2CM_STAT0);
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun /* Mute DONE and ERROR interrupts */
535*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
536*4882a593Smuzhiyun HDMI_IH_MUTE_I2CM_STAT0);
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun /* set SDA high level holding time */
539*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun dw_hdmi_i2c_set_divs(hdmi);
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
dw_hdmi_i2c_unwedge(struct dw_hdmi * hdmi)544*4882a593Smuzhiyun static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun /* If no unwedge state then give up */
547*4882a593Smuzhiyun if (!hdmi->unwedge_state)
548*4882a593Smuzhiyun return false;
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun dev_info(hdmi->dev, "Attempting to unwedge stuck i2c bus\n");
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun /*
553*4882a593Smuzhiyun * This is a huge hack to workaround a problem where the dw_hdmi i2c
554*4882a593Smuzhiyun * bus could sometimes get wedged. Once wedged there doesn't appear
555*4882a593Smuzhiyun * to be any way to unwedge it (including the HDMI_I2CM_SOFTRSTZ)
556*4882a593Smuzhiyun * other than pulsing the SDA line.
557*4882a593Smuzhiyun *
558*4882a593Smuzhiyun * We appear to be able to pulse the SDA line (in the eyes of dw_hdmi)
559*4882a593Smuzhiyun * by:
560*4882a593Smuzhiyun * 1. Remux the pin as a GPIO output, driven low.
561*4882a593Smuzhiyun * 2. Wait a little while. 1 ms seems to work, but we'll do 10.
562*4882a593Smuzhiyun * 3. Immediately jump to remux the pin as dw_hdmi i2c again.
563*4882a593Smuzhiyun *
564*4882a593Smuzhiyun * At the moment of remuxing, the line will still be low due to its
565*4882a593Smuzhiyun * recent stint as an output, but then it will be pulled high by the
566*4882a593Smuzhiyun * (presumed) external pullup. dw_hdmi seems to see this as a rising
567*4882a593Smuzhiyun * edge and that seems to get it out of its jam.
568*4882a593Smuzhiyun *
569*4882a593Smuzhiyun * This wedging was only ever seen on one TV, and only on one of
570*4882a593Smuzhiyun * its HDMI ports. It happened when the TV was powered on while the
571*4882a593Smuzhiyun * device was plugged in. A scope trace shows the TV bringing both SDA
572*4882a593Smuzhiyun * and SCL low, then bringing them both back up at roughly the same
573*4882a593Smuzhiyun * time. Presumably this confuses dw_hdmi because it saw activity but
574*4882a593Smuzhiyun * no real STOP (maybe it thinks there's another master on the bus?).
575*4882a593Smuzhiyun * Giving it a clean rising edge of SDA while SCL is already high
576*4882a593Smuzhiyun * presumably makes dw_hdmi see a STOP which seems to bring dw_hdmi out
577*4882a593Smuzhiyun * of its stupor.
578*4882a593Smuzhiyun *
579*4882a593Smuzhiyun * Note that after coming back alive, transfers seem to immediately
580*4882a593Smuzhiyun * resume, so if we unwedge due to a timeout we should wait a little
581*4882a593Smuzhiyun * longer for our transfer to finish, since it might have just started
582*4882a593Smuzhiyun * now.
583*4882a593Smuzhiyun */
584*4882a593Smuzhiyun pinctrl_select_state(hdmi->pinctrl, hdmi->unwedge_state);
585*4882a593Smuzhiyun msleep(10);
586*4882a593Smuzhiyun pinctrl_select_state(hdmi->pinctrl, hdmi->default_state);
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun return true;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun
dw_hdmi_i2c_wait(struct dw_hdmi * hdmi)591*4882a593Smuzhiyun static int dw_hdmi_i2c_wait(struct dw_hdmi *hdmi)
592*4882a593Smuzhiyun {
593*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c = hdmi->i2c;
594*4882a593Smuzhiyun int stat;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
597*4882a593Smuzhiyun if (!stat) {
598*4882a593Smuzhiyun /* If we can't unwedge, return timeout */
599*4882a593Smuzhiyun if (!dw_hdmi_i2c_unwedge(hdmi))
600*4882a593Smuzhiyun return -EAGAIN;
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun /* We tried to unwedge; give it another chance */
603*4882a593Smuzhiyun stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
604*4882a593Smuzhiyun if (!stat)
605*4882a593Smuzhiyun return -EAGAIN;
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun /* Check for error condition on the bus */
609*4882a593Smuzhiyun if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
610*4882a593Smuzhiyun return -EIO;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun return 0;
613*4882a593Smuzhiyun }
614*4882a593Smuzhiyun
dw_hdmi_i2c_read(struct dw_hdmi * hdmi,unsigned char * buf,unsigned int length)615*4882a593Smuzhiyun static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
616*4882a593Smuzhiyun unsigned char *buf, unsigned int length)
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c = hdmi->i2c;
619*4882a593Smuzhiyun int ret, retry, i;
620*4882a593Smuzhiyun bool read_edid = false;
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun if (!i2c->is_regaddr) {
623*4882a593Smuzhiyun dev_dbg(hdmi->dev, "set read register address to 0\n");
624*4882a593Smuzhiyun i2c->slave_reg = 0x00;
625*4882a593Smuzhiyun i2c->is_regaddr = true;
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun /* edid reads are in 128 bytes. scdc reads are in 1 byte */
629*4882a593Smuzhiyun if (length == HDMI_EDID_BLOCK_LEN)
630*4882a593Smuzhiyun read_edid = true;
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun while (length > 0) {
633*4882a593Smuzhiyun retry = 100;
634*4882a593Smuzhiyun hdmi_writeb(hdmi, i2c->slave_reg, HDMI_I2CM_ADDRESS);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun if (read_edid) {
637*4882a593Smuzhiyun i2c->slave_reg += 8;
638*4882a593Smuzhiyun length -= 8;
639*4882a593Smuzhiyun } else {
640*4882a593Smuzhiyun i2c->slave_reg++;
641*4882a593Smuzhiyun length--;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun while (retry > 0) {
645*4882a593Smuzhiyun if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)) {
646*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun dev_dbg(hdmi->dev, "hdmi disconnect, stop ddc read\n");
649*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
650*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(data, false);
651*4882a593Smuzhiyun return -EPERM;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun if (i2c->is_segment) {
655*4882a593Smuzhiyun if (read_edid)
656*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8_EXT,
657*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
658*4882a593Smuzhiyun else
659*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
660*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
661*4882a593Smuzhiyun } else {
662*4882a593Smuzhiyun if (read_edid)
663*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8,
664*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
665*4882a593Smuzhiyun else
666*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
667*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
668*4882a593Smuzhiyun }
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun ret = dw_hdmi_i2c_wait(hdmi);
671*4882a593Smuzhiyun if (ret == -EAGAIN) {
672*4882a593Smuzhiyun dev_dbg(hdmi->dev, "ddc read time out\n");
673*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
674*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
675*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
676*4882a593Smuzhiyun retry -= 10;
677*4882a593Smuzhiyun continue;
678*4882a593Smuzhiyun } else if (ret == -EIO) {
679*4882a593Smuzhiyun dev_dbg(hdmi->dev, "ddc read err\n");
680*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
681*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
682*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
683*4882a593Smuzhiyun retry--;
684*4882a593Smuzhiyun usleep_range(10000, 11000);
685*4882a593Smuzhiyun continue;
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun /* read success */
688*4882a593Smuzhiyun break;
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun if (retry <= 0) {
691*4882a593Smuzhiyun dev_err(hdmi->dev, "ddc read failed\n");
692*4882a593Smuzhiyun return -EIO;
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun if (read_edid)
696*4882a593Smuzhiyun for (i = 0; i < 8; i++)
697*4882a593Smuzhiyun *buf++ = hdmi_readb(hdmi, HDMI_I2CM_READ_BUFF0 + i);
698*4882a593Smuzhiyun else
699*4882a593Smuzhiyun *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
700*4882a593Smuzhiyun }
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun i2c->is_segment = false;
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun return 0;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun
dw_hdmi_i2c_write(struct dw_hdmi * hdmi,unsigned char * buf,unsigned int length)707*4882a593Smuzhiyun static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi,
708*4882a593Smuzhiyun unsigned char *buf, unsigned int length)
709*4882a593Smuzhiyun {
710*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c = hdmi->i2c;
711*4882a593Smuzhiyun int ret, retry;
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun if (!i2c->is_regaddr) {
714*4882a593Smuzhiyun /* Use the first write byte as register address */
715*4882a593Smuzhiyun i2c->slave_reg = buf[0];
716*4882a593Smuzhiyun length--;
717*4882a593Smuzhiyun buf++;
718*4882a593Smuzhiyun i2c->is_regaddr = true;
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun while (length--) {
722*4882a593Smuzhiyun retry = 100;
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
725*4882a593Smuzhiyun hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun while (retry > 0) {
728*4882a593Smuzhiyun if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)) {
729*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun dev_dbg(hdmi->dev, "hdmi disconnect, stop ddc write\n");
732*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
733*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(data, false);
734*4882a593Smuzhiyun return -EPERM;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun
737*4882a593Smuzhiyun reinit_completion(&i2c->cmp);
738*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE,
739*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun ret = dw_hdmi_i2c_wait(hdmi);
742*4882a593Smuzhiyun if (ret == -EAGAIN) {
743*4882a593Smuzhiyun dev_dbg(hdmi->dev, "ddc write time out\n");
744*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
745*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
746*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
747*4882a593Smuzhiyun retry -= 10;
748*4882a593Smuzhiyun continue;
749*4882a593Smuzhiyun } else if (ret == -EIO) {
750*4882a593Smuzhiyun dev_dbg(hdmi->dev, "ddc write err\n");
751*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
752*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
753*4882a593Smuzhiyun HDMI_I2CM_OPERATION);
754*4882a593Smuzhiyun retry--;
755*4882a593Smuzhiyun usleep_range(10000, 11000);
756*4882a593Smuzhiyun continue;
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun /* write success */
760*4882a593Smuzhiyun break;
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun if (retry <= 0) {
764*4882a593Smuzhiyun dev_err(hdmi->dev, "ddc write failed\n");
765*4882a593Smuzhiyun return -EIO;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun return 0;
770*4882a593Smuzhiyun }
771*4882a593Smuzhiyun
dw_hdmi_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)772*4882a593Smuzhiyun static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
773*4882a593Smuzhiyun struct i2c_msg *msgs, int num)
774*4882a593Smuzhiyun {
775*4882a593Smuzhiyun struct dw_hdmi *hdmi = i2c_get_adapdata(adap);
776*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c = hdmi->i2c;
777*4882a593Smuzhiyun u8 addr = msgs[0].addr;
778*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
779*4882a593Smuzhiyun int i, ret = 0;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun if (addr == DDC_CI_ADDR)
782*4882a593Smuzhiyun /*
783*4882a593Smuzhiyun * The internal I2C controller does not support the multi-byte
784*4882a593Smuzhiyun * read and write operations needed for DDC/CI.
785*4882a593Smuzhiyun * TOFIX: Blacklist the DDC/CI address until we filter out
786*4882a593Smuzhiyun * unsupported I2C operations.
787*4882a593Smuzhiyun */
788*4882a593Smuzhiyun return -EOPNOTSUPP;
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun for (i = 0; i < num; i++) {
793*4882a593Smuzhiyun if (msgs[i].len == 0) {
794*4882a593Smuzhiyun dev_dbg(hdmi->dev,
795*4882a593Smuzhiyun "unsupported transfer %d/%d, no data\n",
796*4882a593Smuzhiyun i + 1, num);
797*4882a593Smuzhiyun return -EOPNOTSUPP;
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun mutex_lock(&i2c->lock);
802*4882a593Smuzhiyun
803*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
804*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(data, true);
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
807*4882a593Smuzhiyun udelay(100);
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun /* Unmute DONE and ERROR interrupts */
810*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun /* Set slave device address taken from the first I2C message */
813*4882a593Smuzhiyun if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
814*4882a593Smuzhiyun addr = DDC_ADDR;
815*4882a593Smuzhiyun hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun /* Set slave device register address on transfer */
818*4882a593Smuzhiyun i2c->is_regaddr = false;
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun /* Set segment pointer for I2C extended read mode operation */
821*4882a593Smuzhiyun i2c->is_segment = false;
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun for (i = 0; i < num; i++) {
824*4882a593Smuzhiyun dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
825*4882a593Smuzhiyun i + 1, num, msgs[i].len, msgs[i].flags);
826*4882a593Smuzhiyun if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
827*4882a593Smuzhiyun i2c->is_segment = true;
828*4882a593Smuzhiyun hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
829*4882a593Smuzhiyun hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
830*4882a593Smuzhiyun } else {
831*4882a593Smuzhiyun if (msgs[i].flags & I2C_M_RD)
832*4882a593Smuzhiyun ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
833*4882a593Smuzhiyun msgs[i].len);
834*4882a593Smuzhiyun else
835*4882a593Smuzhiyun ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
836*4882a593Smuzhiyun msgs[i].len);
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun if (ret < 0)
839*4882a593Smuzhiyun break;
840*4882a593Smuzhiyun }
841*4882a593Smuzhiyun
842*4882a593Smuzhiyun if (!ret)
843*4882a593Smuzhiyun ret = num;
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun /* Mute DONE and ERROR interrupts */
846*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
847*4882a593Smuzhiyun HDMI_IH_MUTE_I2CM_STAT0);
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun mutex_unlock(&i2c->lock);
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun return ret;
852*4882a593Smuzhiyun }
853*4882a593Smuzhiyun
dw_hdmi_i2c_func(struct i2c_adapter * adapter)854*4882a593Smuzhiyun static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter)
855*4882a593Smuzhiyun {
856*4882a593Smuzhiyun return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun static const struct i2c_algorithm dw_hdmi_algorithm = {
860*4882a593Smuzhiyun .master_xfer = dw_hdmi_i2c_xfer,
861*4882a593Smuzhiyun .functionality = dw_hdmi_i2c_func,
862*4882a593Smuzhiyun };
863*4882a593Smuzhiyun
dw_hdmi_i2c_adapter(struct dw_hdmi * hdmi)864*4882a593Smuzhiyun static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun struct i2c_adapter *adap;
867*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c;
868*4882a593Smuzhiyun int ret;
869*4882a593Smuzhiyun
870*4882a593Smuzhiyun i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
871*4882a593Smuzhiyun if (!i2c)
872*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun mutex_init(&i2c->lock);
875*4882a593Smuzhiyun init_completion(&i2c->cmp);
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun adap = &i2c->adap;
878*4882a593Smuzhiyun adap->class = I2C_CLASS_DDC;
879*4882a593Smuzhiyun adap->owner = THIS_MODULE;
880*4882a593Smuzhiyun adap->dev.parent = hdmi->dev;
881*4882a593Smuzhiyun adap->algo = &dw_hdmi_algorithm;
882*4882a593Smuzhiyun strlcpy(adap->name, "DesignWare HDMI", sizeof(adap->name));
883*4882a593Smuzhiyun i2c_set_adapdata(adap, hdmi);
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun ret = i2c_add_adapter(adap);
886*4882a593Smuzhiyun if (ret) {
887*4882a593Smuzhiyun dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
888*4882a593Smuzhiyun devm_kfree(hdmi->dev, i2c);
889*4882a593Smuzhiyun return ERR_PTR(ret);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun hdmi->i2c = i2c;
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun return adap;
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun
hdmi_set_cts_n(struct dw_hdmi * hdmi,unsigned int cts,unsigned int n)899*4882a593Smuzhiyun static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
900*4882a593Smuzhiyun unsigned int n)
901*4882a593Smuzhiyun {
902*4882a593Smuzhiyun /* Must be set/cleared first */
903*4882a593Smuzhiyun hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun /* nshift factor = 0 */
906*4882a593Smuzhiyun hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun /* Use automatic CTS generation mode when CTS is not set */
909*4882a593Smuzhiyun if (cts)
910*4882a593Smuzhiyun hdmi_writeb(hdmi, ((cts >> 16) &
911*4882a593Smuzhiyun HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
912*4882a593Smuzhiyun HDMI_AUD_CTS3_CTS_MANUAL,
913*4882a593Smuzhiyun HDMI_AUD_CTS3);
914*4882a593Smuzhiyun else
915*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3);
916*4882a593Smuzhiyun hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
917*4882a593Smuzhiyun hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
920*4882a593Smuzhiyun hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
921*4882a593Smuzhiyun hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun
hdmi_match_tmds_n_table(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long freq)924*4882a593Smuzhiyun static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
925*4882a593Smuzhiyun unsigned long pixel_clk,
926*4882a593Smuzhiyun unsigned long freq)
927*4882a593Smuzhiyun {
928*4882a593Smuzhiyun const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
929*4882a593Smuzhiyun const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
930*4882a593Smuzhiyun int i;
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun if (plat_data->tmds_n_table) {
933*4882a593Smuzhiyun for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
934*4882a593Smuzhiyun if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
935*4882a593Smuzhiyun tmds_n = &plat_data->tmds_n_table[i];
936*4882a593Smuzhiyun break;
937*4882a593Smuzhiyun }
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun if (tmds_n == NULL) {
942*4882a593Smuzhiyun for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
943*4882a593Smuzhiyun if (pixel_clk == common_tmds_n_table[i].tmds) {
944*4882a593Smuzhiyun tmds_n = &common_tmds_n_table[i];
945*4882a593Smuzhiyun break;
946*4882a593Smuzhiyun }
947*4882a593Smuzhiyun }
948*4882a593Smuzhiyun }
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun if (tmds_n == NULL)
951*4882a593Smuzhiyun return -ENOENT;
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun switch (freq) {
954*4882a593Smuzhiyun case 32000:
955*4882a593Smuzhiyun return tmds_n->n_32k;
956*4882a593Smuzhiyun case 44100:
957*4882a593Smuzhiyun case 88200:
958*4882a593Smuzhiyun case 176400:
959*4882a593Smuzhiyun return (freq / 44100) * tmds_n->n_44k1;
960*4882a593Smuzhiyun case 48000:
961*4882a593Smuzhiyun case 96000:
962*4882a593Smuzhiyun case 192000:
963*4882a593Smuzhiyun return (freq / 48000) * tmds_n->n_48k;
964*4882a593Smuzhiyun default:
965*4882a593Smuzhiyun return -ENOENT;
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun }
968*4882a593Smuzhiyun
hdmi_audio_math_diff(unsigned int freq,unsigned int n,unsigned int pixel_clk)969*4882a593Smuzhiyun static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
970*4882a593Smuzhiyun unsigned int pixel_clk)
971*4882a593Smuzhiyun {
972*4882a593Smuzhiyun u64 final, diff;
973*4882a593Smuzhiyun u64 cts;
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun final = (u64)pixel_clk * n;
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun cts = final;
978*4882a593Smuzhiyun do_div(cts, 128 * freq);
979*4882a593Smuzhiyun
980*4882a593Smuzhiyun diff = final - (u64)cts * (128 * freq);
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun return diff;
983*4882a593Smuzhiyun }
984*4882a593Smuzhiyun
hdmi_compute_n(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long freq)985*4882a593Smuzhiyun static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi,
986*4882a593Smuzhiyun unsigned long pixel_clk,
987*4882a593Smuzhiyun unsigned long freq)
988*4882a593Smuzhiyun {
989*4882a593Smuzhiyun unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
990*4882a593Smuzhiyun unsigned int max_n = (128 * freq) / 300;
991*4882a593Smuzhiyun unsigned int ideal_n = (128 * freq) / 1000;
992*4882a593Smuzhiyun unsigned int best_n_distance = ideal_n;
993*4882a593Smuzhiyun unsigned int best_n = 0;
994*4882a593Smuzhiyun u64 best_diff = U64_MAX;
995*4882a593Smuzhiyun int n;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun /* If the ideal N could satisfy the audio math, then just take it */
998*4882a593Smuzhiyun if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
999*4882a593Smuzhiyun return ideal_n;
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun for (n = min_n; n <= max_n; n++) {
1002*4882a593Smuzhiyun u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun if (diff < best_diff || (diff == best_diff &&
1005*4882a593Smuzhiyun abs(n - ideal_n) < best_n_distance)) {
1006*4882a593Smuzhiyun best_n = n;
1007*4882a593Smuzhiyun best_diff = diff;
1008*4882a593Smuzhiyun best_n_distance = abs(best_n - ideal_n);
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun
1011*4882a593Smuzhiyun /*
1012*4882a593Smuzhiyun * The best N already satisfy the audio math, and also be
1013*4882a593Smuzhiyun * the closest value to ideal N, so just cut the loop.
1014*4882a593Smuzhiyun */
1015*4882a593Smuzhiyun if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
1016*4882a593Smuzhiyun break;
1017*4882a593Smuzhiyun }
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun return best_n;
1020*4882a593Smuzhiyun }
1021*4882a593Smuzhiyun
hdmi_find_n(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long sample_rate)1022*4882a593Smuzhiyun static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
1023*4882a593Smuzhiyun unsigned long sample_rate)
1024*4882a593Smuzhiyun {
1025*4882a593Smuzhiyun int n;
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyun n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
1028*4882a593Smuzhiyun if (n > 0)
1029*4882a593Smuzhiyun return n;
1030*4882a593Smuzhiyun
1031*4882a593Smuzhiyun dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n",
1032*4882a593Smuzhiyun pixel_clk);
1033*4882a593Smuzhiyun
1034*4882a593Smuzhiyun return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
1035*4882a593Smuzhiyun }
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyun /*
1038*4882a593Smuzhiyun * When transmitting IEC60958 linear PCM audio, these registers allow to
1039*4882a593Smuzhiyun * configure the channel status information of all the channel status
1040*4882a593Smuzhiyun * bits in the IEC60958 frame. For the moment this configuration is only
1041*4882a593Smuzhiyun * used when the I2S audio interface, General Purpose Audio (GPA),
1042*4882a593Smuzhiyun * or AHB audio DMA (AHBAUDDMA) interface is active
1043*4882a593Smuzhiyun * (for S/PDIF interface this information comes from the stream).
1044*4882a593Smuzhiyun */
dw_hdmi_set_channel_status(struct dw_hdmi * hdmi,u8 * channel_status)1045*4882a593Smuzhiyun void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi,
1046*4882a593Smuzhiyun u8 *channel_status)
1047*4882a593Smuzhiyun {
1048*4882a593Smuzhiyun /*
1049*4882a593Smuzhiyun * Set channel status register for frequency and word length.
1050*4882a593Smuzhiyun * Use default values for other registers.
1051*4882a593Smuzhiyun */
1052*4882a593Smuzhiyun hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7);
1053*4882a593Smuzhiyun hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8);
1054*4882a593Smuzhiyun }
1055*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status);
1056*4882a593Smuzhiyun
hdmi_set_clk_regenerator(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned int sample_rate)1057*4882a593Smuzhiyun static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
1058*4882a593Smuzhiyun unsigned long pixel_clk, unsigned int sample_rate)
1059*4882a593Smuzhiyun {
1060*4882a593Smuzhiyun unsigned long ftdms = pixel_clk;
1061*4882a593Smuzhiyun unsigned int n, cts;
1062*4882a593Smuzhiyun u8 config3;
1063*4882a593Smuzhiyun u64 tmp;
1064*4882a593Smuzhiyun
1065*4882a593Smuzhiyun n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
1066*4882a593Smuzhiyun
1067*4882a593Smuzhiyun config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun /* Only compute CTS when using internal AHB audio */
1070*4882a593Smuzhiyun if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
1071*4882a593Smuzhiyun /*
1072*4882a593Smuzhiyun * Compute the CTS value from the N value. Note that CTS and N
1073*4882a593Smuzhiyun * can be up to 20 bits in total, so we need 64-bit math. Also
1074*4882a593Smuzhiyun * note that our TDMS clock is not fully accurate; it is
1075*4882a593Smuzhiyun * accurate to kHz. This can introduce an unnecessary remainder
1076*4882a593Smuzhiyun * in the calculation below, so we don't try to warn about that.
1077*4882a593Smuzhiyun */
1078*4882a593Smuzhiyun tmp = (u64)ftdms * n;
1079*4882a593Smuzhiyun do_div(tmp, 128 * sample_rate);
1080*4882a593Smuzhiyun cts = tmp;
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
1083*4882a593Smuzhiyun __func__, sample_rate,
1084*4882a593Smuzhiyun ftdms / 1000000, (ftdms / 1000) % 1000,
1085*4882a593Smuzhiyun n, cts);
1086*4882a593Smuzhiyun } else {
1087*4882a593Smuzhiyun cts = 0;
1088*4882a593Smuzhiyun }
1089*4882a593Smuzhiyun
1090*4882a593Smuzhiyun spin_lock_irq(&hdmi->audio_lock);
1091*4882a593Smuzhiyun hdmi->audio_n = n;
1092*4882a593Smuzhiyun hdmi->audio_cts = cts;
1093*4882a593Smuzhiyun hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
1094*4882a593Smuzhiyun spin_unlock_irq(&hdmi->audio_lock);
1095*4882a593Smuzhiyun }
1096*4882a593Smuzhiyun
hdmi_init_clk_regenerator(struct dw_hdmi * hdmi)1097*4882a593Smuzhiyun static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
1098*4882a593Smuzhiyun {
1099*4882a593Smuzhiyun mutex_lock(&hdmi->audio_mutex);
1100*4882a593Smuzhiyun hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
1101*4882a593Smuzhiyun mutex_unlock(&hdmi->audio_mutex);
1102*4882a593Smuzhiyun }
1103*4882a593Smuzhiyun
hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi * hdmi)1104*4882a593Smuzhiyun static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
1105*4882a593Smuzhiyun {
1106*4882a593Smuzhiyun mutex_lock(&hdmi->audio_mutex);
1107*4882a593Smuzhiyun hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1108*4882a593Smuzhiyun hdmi->sample_rate);
1109*4882a593Smuzhiyun mutex_unlock(&hdmi->audio_mutex);
1110*4882a593Smuzhiyun }
1111*4882a593Smuzhiyun
dw_hdmi_set_sample_rate(struct dw_hdmi * hdmi,unsigned int rate)1112*4882a593Smuzhiyun void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
1113*4882a593Smuzhiyun {
1114*4882a593Smuzhiyun mutex_lock(&hdmi->audio_mutex);
1115*4882a593Smuzhiyun hdmi->sample_rate = rate;
1116*4882a593Smuzhiyun hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1117*4882a593Smuzhiyun hdmi->sample_rate);
1118*4882a593Smuzhiyun mutex_unlock(&hdmi->audio_mutex);
1119*4882a593Smuzhiyun }
1120*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
1121*4882a593Smuzhiyun
dw_hdmi_set_channel_count(struct dw_hdmi * hdmi,unsigned int cnt)1122*4882a593Smuzhiyun void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
1123*4882a593Smuzhiyun {
1124*4882a593Smuzhiyun u8 layout;
1125*4882a593Smuzhiyun
1126*4882a593Smuzhiyun mutex_lock(&hdmi->audio_mutex);
1127*4882a593Smuzhiyun
1128*4882a593Smuzhiyun /*
1129*4882a593Smuzhiyun * For >2 channel PCM audio, we need to select layout 1
1130*4882a593Smuzhiyun * and set an appropriate channel map.
1131*4882a593Smuzhiyun */
1132*4882a593Smuzhiyun if (cnt > 2)
1133*4882a593Smuzhiyun layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1;
1134*4882a593Smuzhiyun else
1135*4882a593Smuzhiyun layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0;
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK,
1138*4882a593Smuzhiyun HDMI_FC_AUDSCONF);
1139*4882a593Smuzhiyun
1140*4882a593Smuzhiyun /* Set the audio infoframes channel count */
1141*4882a593Smuzhiyun hdmi_modb(hdmi, (cnt - 1) << HDMI_FC_AUDICONF0_CC_OFFSET,
1142*4882a593Smuzhiyun HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0);
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun mutex_unlock(&hdmi->audio_mutex);
1145*4882a593Smuzhiyun }
1146*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count);
1147*4882a593Smuzhiyun
dw_hdmi_set_channel_allocation(struct dw_hdmi * hdmi,unsigned int ca)1148*4882a593Smuzhiyun void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca)
1149*4882a593Smuzhiyun {
1150*4882a593Smuzhiyun mutex_lock(&hdmi->audio_mutex);
1151*4882a593Smuzhiyun
1152*4882a593Smuzhiyun hdmi_writeb(hdmi, ca, HDMI_FC_AUDICONF2);
1153*4882a593Smuzhiyun
1154*4882a593Smuzhiyun mutex_unlock(&hdmi->audio_mutex);
1155*4882a593Smuzhiyun }
1156*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_allocation);
1157*4882a593Smuzhiyun
hdmi_enable_audio_clk(struct dw_hdmi * hdmi,bool enable)1158*4882a593Smuzhiyun static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
1159*4882a593Smuzhiyun {
1160*4882a593Smuzhiyun if (enable)
1161*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
1162*4882a593Smuzhiyun else
1163*4882a593Smuzhiyun hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE;
1164*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
1165*4882a593Smuzhiyun }
1166*4882a593Smuzhiyun
hdmi_audio_get_eld(struct dw_hdmi * hdmi)1167*4882a593Smuzhiyun static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
1168*4882a593Smuzhiyun {
1169*4882a593Smuzhiyun if (!hdmi->curr_conn)
1170*4882a593Smuzhiyun return NULL;
1171*4882a593Smuzhiyun
1172*4882a593Smuzhiyun return hdmi->curr_conn->eld;
1173*4882a593Smuzhiyun }
1174*4882a593Smuzhiyun
dw_hdmi_ahb_audio_enable(struct dw_hdmi * hdmi)1175*4882a593Smuzhiyun static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
1176*4882a593Smuzhiyun {
1177*4882a593Smuzhiyun hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
1178*4882a593Smuzhiyun }
1179*4882a593Smuzhiyun
dw_hdmi_ahb_audio_disable(struct dw_hdmi * hdmi)1180*4882a593Smuzhiyun static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
1181*4882a593Smuzhiyun {
1182*4882a593Smuzhiyun hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
1183*4882a593Smuzhiyun }
1184*4882a593Smuzhiyun
dw_hdmi_i2s_audio_enable(struct dw_hdmi * hdmi)1185*4882a593Smuzhiyun static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
1186*4882a593Smuzhiyun {
1187*4882a593Smuzhiyun hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
1188*4882a593Smuzhiyun hdmi_enable_audio_clk(hdmi, true);
1189*4882a593Smuzhiyun }
1190*4882a593Smuzhiyun
dw_hdmi_i2s_audio_disable(struct dw_hdmi * hdmi)1191*4882a593Smuzhiyun static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
1192*4882a593Smuzhiyun {
1193*4882a593Smuzhiyun hdmi_enable_audio_clk(hdmi, false);
1194*4882a593Smuzhiyun }
1195*4882a593Smuzhiyun
dw_hdmi_audio_enable(struct dw_hdmi * hdmi)1196*4882a593Smuzhiyun void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
1197*4882a593Smuzhiyun {
1198*4882a593Smuzhiyun unsigned long flags;
1199*4882a593Smuzhiyun
1200*4882a593Smuzhiyun spin_lock_irqsave(&hdmi->audio_lock, flags);
1201*4882a593Smuzhiyun hdmi->audio_enable = true;
1202*4882a593Smuzhiyun if (hdmi->enable_audio)
1203*4882a593Smuzhiyun hdmi->enable_audio(hdmi);
1204*4882a593Smuzhiyun spin_unlock_irqrestore(&hdmi->audio_lock, flags);
1205*4882a593Smuzhiyun }
1206*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
1207*4882a593Smuzhiyun
dw_hdmi_audio_disable(struct dw_hdmi * hdmi)1208*4882a593Smuzhiyun void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
1209*4882a593Smuzhiyun {
1210*4882a593Smuzhiyun unsigned long flags;
1211*4882a593Smuzhiyun
1212*4882a593Smuzhiyun spin_lock_irqsave(&hdmi->audio_lock, flags);
1213*4882a593Smuzhiyun hdmi->audio_enable = false;
1214*4882a593Smuzhiyun if (hdmi->disable_audio)
1215*4882a593Smuzhiyun hdmi->disable_audio(hdmi);
1216*4882a593Smuzhiyun spin_unlock_irqrestore(&hdmi->audio_lock, flags);
1217*4882a593Smuzhiyun }
1218*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
1219*4882a593Smuzhiyun
hdmi_bus_fmt_is_rgb(unsigned int bus_format)1220*4882a593Smuzhiyun static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
1221*4882a593Smuzhiyun {
1222*4882a593Smuzhiyun switch (bus_format) {
1223*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB888_1X24:
1224*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB101010_1X30:
1225*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB121212_1X36:
1226*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB161616_1X48:
1227*4882a593Smuzhiyun return true;
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun default:
1230*4882a593Smuzhiyun return false;
1231*4882a593Smuzhiyun }
1232*4882a593Smuzhiyun }
1233*4882a593Smuzhiyun
hdmi_bus_fmt_is_yuv444(unsigned int bus_format)1234*4882a593Smuzhiyun static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
1235*4882a593Smuzhiyun {
1236*4882a593Smuzhiyun switch (bus_format) {
1237*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV8_1X24:
1238*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV10_1X30:
1239*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV12_1X36:
1240*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV16_1X48:
1241*4882a593Smuzhiyun return true;
1242*4882a593Smuzhiyun
1243*4882a593Smuzhiyun default:
1244*4882a593Smuzhiyun return false;
1245*4882a593Smuzhiyun }
1246*4882a593Smuzhiyun }
1247*4882a593Smuzhiyun
hdmi_bus_fmt_is_yuv422(unsigned int bus_format)1248*4882a593Smuzhiyun static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
1249*4882a593Smuzhiyun {
1250*4882a593Smuzhiyun switch (bus_format) {
1251*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY8_1X16:
1252*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY10_1X20:
1253*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY12_1X24:
1254*4882a593Smuzhiyun return true;
1255*4882a593Smuzhiyun
1256*4882a593Smuzhiyun default:
1257*4882a593Smuzhiyun return false;
1258*4882a593Smuzhiyun }
1259*4882a593Smuzhiyun }
1260*4882a593Smuzhiyun
hdmi_bus_fmt_is_yuv420(unsigned int bus_format)1261*4882a593Smuzhiyun static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
1262*4882a593Smuzhiyun {
1263*4882a593Smuzhiyun switch (bus_format) {
1264*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1265*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1266*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
1267*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
1268*4882a593Smuzhiyun return true;
1269*4882a593Smuzhiyun
1270*4882a593Smuzhiyun default:
1271*4882a593Smuzhiyun return false;
1272*4882a593Smuzhiyun }
1273*4882a593Smuzhiyun }
1274*4882a593Smuzhiyun
hdmi_bus_fmt_color_depth(unsigned int bus_format)1275*4882a593Smuzhiyun static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
1276*4882a593Smuzhiyun {
1277*4882a593Smuzhiyun switch (bus_format) {
1278*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB888_1X24:
1279*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV8_1X24:
1280*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY8_1X16:
1281*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1282*4882a593Smuzhiyun return 8;
1283*4882a593Smuzhiyun
1284*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB101010_1X30:
1285*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV10_1X30:
1286*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY10_1X20:
1287*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1288*4882a593Smuzhiyun return 10;
1289*4882a593Smuzhiyun
1290*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB121212_1X36:
1291*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV12_1X36:
1292*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY12_1X24:
1293*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
1294*4882a593Smuzhiyun return 12;
1295*4882a593Smuzhiyun
1296*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB161616_1X48:
1297*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV16_1X48:
1298*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
1299*4882a593Smuzhiyun return 16;
1300*4882a593Smuzhiyun
1301*4882a593Smuzhiyun default:
1302*4882a593Smuzhiyun return 0;
1303*4882a593Smuzhiyun }
1304*4882a593Smuzhiyun }
1305*4882a593Smuzhiyun
1306*4882a593Smuzhiyun /*
1307*4882a593Smuzhiyun * this submodule is responsible for the video data synchronization.
1308*4882a593Smuzhiyun * for example, for RGB 4:4:4 input, the data map is defined as
1309*4882a593Smuzhiyun * pin{47~40} <==> R[7:0]
1310*4882a593Smuzhiyun * pin{31~24} <==> G[7:0]
1311*4882a593Smuzhiyun * pin{15~8} <==> B[7:0]
1312*4882a593Smuzhiyun */
hdmi_video_sample(struct dw_hdmi * hdmi)1313*4882a593Smuzhiyun static void hdmi_video_sample(struct dw_hdmi *hdmi)
1314*4882a593Smuzhiyun {
1315*4882a593Smuzhiyun int color_format = 0;
1316*4882a593Smuzhiyun u8 val;
1317*4882a593Smuzhiyun
1318*4882a593Smuzhiyun switch (hdmi->hdmi_data.enc_in_bus_format) {
1319*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB888_1X24:
1320*4882a593Smuzhiyun color_format = 0x01;
1321*4882a593Smuzhiyun break;
1322*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB101010_1X30:
1323*4882a593Smuzhiyun color_format = 0x03;
1324*4882a593Smuzhiyun break;
1325*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB121212_1X36:
1326*4882a593Smuzhiyun color_format = 0x05;
1327*4882a593Smuzhiyun break;
1328*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB161616_1X48:
1329*4882a593Smuzhiyun color_format = 0x07;
1330*4882a593Smuzhiyun break;
1331*4882a593Smuzhiyun
1332*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV8_1X24:
1333*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1334*4882a593Smuzhiyun color_format = 0x09;
1335*4882a593Smuzhiyun break;
1336*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV10_1X30:
1337*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1338*4882a593Smuzhiyun color_format = 0x0B;
1339*4882a593Smuzhiyun break;
1340*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV12_1X36:
1341*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
1342*4882a593Smuzhiyun color_format = 0x0D;
1343*4882a593Smuzhiyun break;
1344*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV16_1X48:
1345*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
1346*4882a593Smuzhiyun color_format = 0x0F;
1347*4882a593Smuzhiyun break;
1348*4882a593Smuzhiyun
1349*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY8_1X16:
1350*4882a593Smuzhiyun color_format = 0x16;
1351*4882a593Smuzhiyun break;
1352*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY10_1X20:
1353*4882a593Smuzhiyun color_format = 0x14;
1354*4882a593Smuzhiyun break;
1355*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY12_1X24:
1356*4882a593Smuzhiyun color_format = 0x12;
1357*4882a593Smuzhiyun break;
1358*4882a593Smuzhiyun
1359*4882a593Smuzhiyun default:
1360*4882a593Smuzhiyun return;
1361*4882a593Smuzhiyun }
1362*4882a593Smuzhiyun
1363*4882a593Smuzhiyun val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
1364*4882a593Smuzhiyun ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
1365*4882a593Smuzhiyun HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
1366*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
1367*4882a593Smuzhiyun
1368*4882a593Smuzhiyun /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
1369*4882a593Smuzhiyun val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
1370*4882a593Smuzhiyun HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
1371*4882a593Smuzhiyun HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
1372*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
1373*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
1374*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
1375*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
1376*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
1377*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
1378*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
1379*4882a593Smuzhiyun }
1380*4882a593Smuzhiyun
is_color_space_conversion(struct dw_hdmi * hdmi)1381*4882a593Smuzhiyun static int is_color_space_conversion(struct dw_hdmi *hdmi)
1382*4882a593Smuzhiyun {
1383*4882a593Smuzhiyun struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
1384*4882a593Smuzhiyun bool is_input_rgb, is_output_rgb;
1385*4882a593Smuzhiyun
1386*4882a593Smuzhiyun is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format);
1387*4882a593Smuzhiyun is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format);
1388*4882a593Smuzhiyun
1389*4882a593Smuzhiyun return (is_input_rgb != is_output_rgb) ||
1390*4882a593Smuzhiyun (is_input_rgb && is_output_rgb && hdmi_data->rgb_limited_range);
1391*4882a593Smuzhiyun }
1392*4882a593Smuzhiyun
is_color_space_decimation(struct dw_hdmi * hdmi)1393*4882a593Smuzhiyun static int is_color_space_decimation(struct dw_hdmi *hdmi)
1394*4882a593Smuzhiyun {
1395*4882a593Smuzhiyun if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
1396*4882a593Smuzhiyun return 0;
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) ||
1399*4882a593Smuzhiyun hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format))
1400*4882a593Smuzhiyun return 1;
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun return 0;
1403*4882a593Smuzhiyun }
1404*4882a593Smuzhiyun
is_color_space_interpolation(struct dw_hdmi * hdmi)1405*4882a593Smuzhiyun static int is_color_space_interpolation(struct dw_hdmi *hdmi)
1406*4882a593Smuzhiyun {
1407*4882a593Smuzhiyun if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format))
1408*4882a593Smuzhiyun return 0;
1409*4882a593Smuzhiyun
1410*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1411*4882a593Smuzhiyun hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
1412*4882a593Smuzhiyun return 1;
1413*4882a593Smuzhiyun
1414*4882a593Smuzhiyun return 0;
1415*4882a593Smuzhiyun }
1416*4882a593Smuzhiyun
is_csc_needed(struct dw_hdmi * hdmi)1417*4882a593Smuzhiyun static bool is_csc_needed(struct dw_hdmi *hdmi)
1418*4882a593Smuzhiyun {
1419*4882a593Smuzhiyun return is_color_space_conversion(hdmi) ||
1420*4882a593Smuzhiyun is_color_space_decimation(hdmi) ||
1421*4882a593Smuzhiyun is_color_space_interpolation(hdmi);
1422*4882a593Smuzhiyun }
1423*4882a593Smuzhiyun
is_rgb_full_to_limited_needed(struct dw_hdmi * hdmi)1424*4882a593Smuzhiyun static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi)
1425*4882a593Smuzhiyun {
1426*4882a593Smuzhiyun if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
1427*4882a593Smuzhiyun (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range))
1428*4882a593Smuzhiyun return true;
1429*4882a593Smuzhiyun
1430*4882a593Smuzhiyun return false;
1431*4882a593Smuzhiyun }
1432*4882a593Smuzhiyun
dw_hdmi_update_csc_coeffs(struct dw_hdmi * hdmi)1433*4882a593Smuzhiyun static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
1434*4882a593Smuzhiyun {
1435*4882a593Smuzhiyun const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
1436*4882a593Smuzhiyun bool is_input_rgb, is_output_rgb;
1437*4882a593Smuzhiyun unsigned i;
1438*4882a593Smuzhiyun u32 csc_scale = 1;
1439*4882a593Smuzhiyun
1440*4882a593Smuzhiyun is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
1441*4882a593Smuzhiyun is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun if (!is_input_rgb && is_output_rgb) {
1444*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601)
1445*4882a593Smuzhiyun csc_coeff = &csc_coeff_rgb_out_eitu601;
1446*4882a593Smuzhiyun else
1447*4882a593Smuzhiyun csc_coeff = &csc_coeff_rgb_out_eitu709;
1448*4882a593Smuzhiyun } else if (is_input_rgb && !is_output_rgb) {
1449*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601)
1450*4882a593Smuzhiyun csc_coeff = &csc_coeff_rgb_in_eitu601;
1451*4882a593Smuzhiyun else
1452*4882a593Smuzhiyun csc_coeff = &csc_coeff_rgb_in_eitu709;
1453*4882a593Smuzhiyun csc_scale = 0;
1454*4882a593Smuzhiyun } else if (is_input_rgb && is_output_rgb &&
1455*4882a593Smuzhiyun is_rgb_full_to_limited_needed(hdmi)) {
1456*4882a593Smuzhiyun csc_coeff = &csc_coeff_rgb_full_to_rgb_limited;
1457*4882a593Smuzhiyun }
1458*4882a593Smuzhiyun
1459*4882a593Smuzhiyun /* The CSC registers are sequential, alternating MSB then LSB */
1460*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
1461*4882a593Smuzhiyun u16 coeff_a = (*csc_coeff)[0][i];
1462*4882a593Smuzhiyun u16 coeff_b = (*csc_coeff)[1][i];
1463*4882a593Smuzhiyun u16 coeff_c = (*csc_coeff)[2][i];
1464*4882a593Smuzhiyun
1465*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
1466*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
1467*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
1468*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
1469*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
1470*4882a593Smuzhiyun hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
1471*4882a593Smuzhiyun }
1472*4882a593Smuzhiyun
1473*4882a593Smuzhiyun hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
1474*4882a593Smuzhiyun HDMI_CSC_SCALE);
1475*4882a593Smuzhiyun }
1476*4882a593Smuzhiyun
hdmi_video_csc(struct dw_hdmi * hdmi)1477*4882a593Smuzhiyun static void hdmi_video_csc(struct dw_hdmi *hdmi)
1478*4882a593Smuzhiyun {
1479*4882a593Smuzhiyun int color_depth = 0;
1480*4882a593Smuzhiyun int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
1481*4882a593Smuzhiyun int decimation = 0;
1482*4882a593Smuzhiyun
1483*4882a593Smuzhiyun /* YCC422 interpolation to 444 mode */
1484*4882a593Smuzhiyun if (is_color_space_interpolation(hdmi))
1485*4882a593Smuzhiyun interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
1486*4882a593Smuzhiyun else if (is_color_space_decimation(hdmi))
1487*4882a593Smuzhiyun decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
1490*4882a593Smuzhiyun case 8:
1491*4882a593Smuzhiyun color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
1492*4882a593Smuzhiyun break;
1493*4882a593Smuzhiyun case 10:
1494*4882a593Smuzhiyun color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
1495*4882a593Smuzhiyun break;
1496*4882a593Smuzhiyun case 12:
1497*4882a593Smuzhiyun color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
1498*4882a593Smuzhiyun break;
1499*4882a593Smuzhiyun case 16:
1500*4882a593Smuzhiyun color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
1501*4882a593Smuzhiyun break;
1502*4882a593Smuzhiyun
1503*4882a593Smuzhiyun default:
1504*4882a593Smuzhiyun return;
1505*4882a593Smuzhiyun }
1506*4882a593Smuzhiyun
1507*4882a593Smuzhiyun /* Configure the CSC registers */
1508*4882a593Smuzhiyun hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
1509*4882a593Smuzhiyun hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
1510*4882a593Smuzhiyun HDMI_CSC_SCALE);
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun dw_hdmi_update_csc_coeffs(hdmi);
1513*4882a593Smuzhiyun }
1514*4882a593Smuzhiyun
1515*4882a593Smuzhiyun /*
1516*4882a593Smuzhiyun * HDMI video packetizer is used to packetize the data.
1517*4882a593Smuzhiyun * for example, if input is YCC422 mode or repeater is used,
1518*4882a593Smuzhiyun * data should be repacked this module can be bypassed.
1519*4882a593Smuzhiyun */
hdmi_video_packetize(struct dw_hdmi * hdmi)1520*4882a593Smuzhiyun static void hdmi_video_packetize(struct dw_hdmi *hdmi)
1521*4882a593Smuzhiyun {
1522*4882a593Smuzhiyun unsigned int color_depth = 0;
1523*4882a593Smuzhiyun unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
1524*4882a593Smuzhiyun unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
1525*4882a593Smuzhiyun struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
1526*4882a593Smuzhiyun u8 val, vp_conf;
1527*4882a593Smuzhiyun
1528*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1529*4882a593Smuzhiyun hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
1530*4882a593Smuzhiyun hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1531*4882a593Smuzhiyun switch (hdmi_bus_fmt_color_depth(
1532*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format)) {
1533*4882a593Smuzhiyun case 8:
1534*4882a593Smuzhiyun color_depth = 0;
1535*4882a593Smuzhiyun output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1536*4882a593Smuzhiyun break;
1537*4882a593Smuzhiyun case 10:
1538*4882a593Smuzhiyun color_depth = 5;
1539*4882a593Smuzhiyun break;
1540*4882a593Smuzhiyun case 12:
1541*4882a593Smuzhiyun color_depth = 6;
1542*4882a593Smuzhiyun break;
1543*4882a593Smuzhiyun case 16:
1544*4882a593Smuzhiyun color_depth = 7;
1545*4882a593Smuzhiyun break;
1546*4882a593Smuzhiyun default:
1547*4882a593Smuzhiyun output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1548*4882a593Smuzhiyun }
1549*4882a593Smuzhiyun } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
1550*4882a593Smuzhiyun switch (hdmi_bus_fmt_color_depth(
1551*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format)) {
1552*4882a593Smuzhiyun case 0:
1553*4882a593Smuzhiyun case 8:
1554*4882a593Smuzhiyun remap_size = HDMI_VP_REMAP_YCC422_16bit;
1555*4882a593Smuzhiyun break;
1556*4882a593Smuzhiyun case 10:
1557*4882a593Smuzhiyun remap_size = HDMI_VP_REMAP_YCC422_20bit;
1558*4882a593Smuzhiyun break;
1559*4882a593Smuzhiyun case 12:
1560*4882a593Smuzhiyun remap_size = HDMI_VP_REMAP_YCC422_24bit;
1561*4882a593Smuzhiyun break;
1562*4882a593Smuzhiyun
1563*4882a593Smuzhiyun default:
1564*4882a593Smuzhiyun return;
1565*4882a593Smuzhiyun }
1566*4882a593Smuzhiyun output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
1567*4882a593Smuzhiyun } else {
1568*4882a593Smuzhiyun return;
1569*4882a593Smuzhiyun }
1570*4882a593Smuzhiyun
1571*4882a593Smuzhiyun /* set the packetizer registers */
1572*4882a593Smuzhiyun val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
1573*4882a593Smuzhiyun HDMI_VP_PR_CD_COLOR_DEPTH_MASK;
1574*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
1575*4882a593Smuzhiyun
1576*4882a593Smuzhiyun hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
1577*4882a593Smuzhiyun HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
1578*4882a593Smuzhiyun
1579*4882a593Smuzhiyun /* Data from pixel repeater block */
1580*4882a593Smuzhiyun if (hdmi_data->pix_repet_factor > 0) {
1581*4882a593Smuzhiyun vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
1582*4882a593Smuzhiyun HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
1583*4882a593Smuzhiyun } else { /* data from packetizer block */
1584*4882a593Smuzhiyun vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
1585*4882a593Smuzhiyun HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
1586*4882a593Smuzhiyun }
1587*4882a593Smuzhiyun
1588*4882a593Smuzhiyun hdmi_modb(hdmi, vp_conf,
1589*4882a593Smuzhiyun HDMI_VP_CONF_PR_EN_MASK |
1590*4882a593Smuzhiyun HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
1591*4882a593Smuzhiyun
1592*4882a593Smuzhiyun hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
1593*4882a593Smuzhiyun hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
1594*4882a593Smuzhiyun
1595*4882a593Smuzhiyun if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
1596*4882a593Smuzhiyun vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1597*4882a593Smuzhiyun HDMI_VP_CONF_PP_EN_ENABLE |
1598*4882a593Smuzhiyun HDMI_VP_CONF_YCC422_EN_DISABLE;
1599*4882a593Smuzhiyun } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
1600*4882a593Smuzhiyun vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1601*4882a593Smuzhiyun HDMI_VP_CONF_PP_EN_DISABLE |
1602*4882a593Smuzhiyun HDMI_VP_CONF_YCC422_EN_ENABLE;
1603*4882a593Smuzhiyun } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
1604*4882a593Smuzhiyun vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
1605*4882a593Smuzhiyun HDMI_VP_CONF_PP_EN_DISABLE |
1606*4882a593Smuzhiyun HDMI_VP_CONF_YCC422_EN_DISABLE;
1607*4882a593Smuzhiyun } else {
1608*4882a593Smuzhiyun return;
1609*4882a593Smuzhiyun }
1610*4882a593Smuzhiyun
1611*4882a593Smuzhiyun hdmi_modb(hdmi, vp_conf,
1612*4882a593Smuzhiyun HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
1613*4882a593Smuzhiyun HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
1614*4882a593Smuzhiyun
1615*4882a593Smuzhiyun hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
1616*4882a593Smuzhiyun HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
1617*4882a593Smuzhiyun HDMI_VP_STUFF_PP_STUFFING_MASK |
1618*4882a593Smuzhiyun HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
1619*4882a593Smuzhiyun
1620*4882a593Smuzhiyun hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
1621*4882a593Smuzhiyun HDMI_VP_CONF);
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
1625*4882a593Smuzhiyun * Synopsys PHY Handling
1626*4882a593Smuzhiyun */
1627*4882a593Smuzhiyun
hdmi_phy_test_clear(struct dw_hdmi * hdmi,unsigned char bit)1628*4882a593Smuzhiyun static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
1629*4882a593Smuzhiyun unsigned char bit)
1630*4882a593Smuzhiyun {
1631*4882a593Smuzhiyun hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
1632*4882a593Smuzhiyun HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
1633*4882a593Smuzhiyun }
1634*4882a593Smuzhiyun
hdmi_phy_wait_i2c_done(struct dw_hdmi * hdmi,int msec)1635*4882a593Smuzhiyun static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
1636*4882a593Smuzhiyun {
1637*4882a593Smuzhiyun u32 val;
1638*4882a593Smuzhiyun
1639*4882a593Smuzhiyun while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
1640*4882a593Smuzhiyun if (msec-- == 0)
1641*4882a593Smuzhiyun return false;
1642*4882a593Smuzhiyun udelay(1000);
1643*4882a593Smuzhiyun }
1644*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
1645*4882a593Smuzhiyun
1646*4882a593Smuzhiyun return true;
1647*4882a593Smuzhiyun }
1648*4882a593Smuzhiyun
dw_hdmi_phy_i2c_write(struct dw_hdmi * hdmi,unsigned short data,unsigned char addr)1649*4882a593Smuzhiyun void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
1650*4882a593Smuzhiyun unsigned char addr)
1651*4882a593Smuzhiyun {
1652*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
1653*4882a593Smuzhiyun hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
1654*4882a593Smuzhiyun hdmi_writeb(hdmi, (unsigned char)(data >> 8),
1655*4882a593Smuzhiyun HDMI_PHY_I2CM_DATAO_1_ADDR);
1656*4882a593Smuzhiyun hdmi_writeb(hdmi, (unsigned char)(data >> 0),
1657*4882a593Smuzhiyun HDMI_PHY_I2CM_DATAO_0_ADDR);
1658*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
1659*4882a593Smuzhiyun HDMI_PHY_I2CM_OPERATION_ADDR);
1660*4882a593Smuzhiyun hdmi_phy_wait_i2c_done(hdmi, 1000);
1661*4882a593Smuzhiyun }
1662*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
1663*4882a593Smuzhiyun
1664*4882a593Smuzhiyun /* Filter out invalid setups to avoid configuring SCDC and scrambling */
dw_hdmi_support_scdc(struct dw_hdmi * hdmi,const struct drm_display_info * display)1665*4882a593Smuzhiyun static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
1666*4882a593Smuzhiyun const struct drm_display_info *display)
1667*4882a593Smuzhiyun {
1668*4882a593Smuzhiyun /* Completely disable SCDC support for older controllers */
1669*4882a593Smuzhiyun if (hdmi->version < 0x200a)
1670*4882a593Smuzhiyun return false;
1671*4882a593Smuzhiyun
1672*4882a593Smuzhiyun /* Disable if no DDC bus */
1673*4882a593Smuzhiyun if (!hdmi->ddc)
1674*4882a593Smuzhiyun return false;
1675*4882a593Smuzhiyun
1676*4882a593Smuzhiyun /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */
1677*4882a593Smuzhiyun if (!display->hdmi.scdc.supported ||
1678*4882a593Smuzhiyun !display->hdmi.scdc.scrambling.supported)
1679*4882a593Smuzhiyun return false;
1680*4882a593Smuzhiyun
1681*4882a593Smuzhiyun /*
1682*4882a593Smuzhiyun * Disable if display only support low TMDS rates and scrambling
1683*4882a593Smuzhiyun * for low rates is not supported either
1684*4882a593Smuzhiyun */
1685*4882a593Smuzhiyun if (!display->hdmi.scdc.scrambling.low_rates &&
1686*4882a593Smuzhiyun display->max_tmds_clock <= 340000)
1687*4882a593Smuzhiyun return false;
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun return true;
1690*4882a593Smuzhiyun }
1691*4882a593Smuzhiyun
hdmi_phy_i2c_read(struct dw_hdmi * hdmi,unsigned char addr)1692*4882a593Smuzhiyun static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
1693*4882a593Smuzhiyun {
1694*4882a593Smuzhiyun int val;
1695*4882a593Smuzhiyun
1696*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
1697*4882a593Smuzhiyun hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
1698*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
1699*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
1700*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
1701*4882a593Smuzhiyun HDMI_PHY_I2CM_OPERATION_ADDR);
1702*4882a593Smuzhiyun hdmi_phy_wait_i2c_done(hdmi, 1000);
1703*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
1704*4882a593Smuzhiyun val = (val & 0xff) << 8;
1705*4882a593Smuzhiyun val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
1706*4882a593Smuzhiyun return val;
1707*4882a593Smuzhiyun }
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun /*
1710*4882a593Smuzhiyun * HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
1711*4882a593Smuzhiyun * - The Source shall suspend transmission of the TMDS clock and data
1712*4882a593Smuzhiyun * - The Source shall write to the TMDS_Bit_Clock_Ratio bit to change it
1713*4882a593Smuzhiyun * from a 0 to a 1 or from a 1 to a 0
1714*4882a593Smuzhiyun * - The Source shall allow a minimum of 1 ms and a maximum of 100 ms from
1715*4882a593Smuzhiyun * the time the TMDS_Bit_Clock_Ratio bit is written until resuming
1716*4882a593Smuzhiyun * transmission of TMDS clock and data
1717*4882a593Smuzhiyun *
1718*4882a593Smuzhiyun * To respect the 100ms maximum delay, the dw_hdmi_set_high_tmds_clock_ratio()
1719*4882a593Smuzhiyun * helper should called right before enabling the TMDS Clock and Data in
1720*4882a593Smuzhiyun * the PHY configuration callback.
1721*4882a593Smuzhiyun */
dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi * hdmi,const struct drm_display_info * display)1722*4882a593Smuzhiyun void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
1723*4882a593Smuzhiyun const struct drm_display_info *display)
1724*4882a593Smuzhiyun {
1725*4882a593Smuzhiyun unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
1726*4882a593Smuzhiyun
1727*4882a593Smuzhiyun /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
1728*4882a593Smuzhiyun if (dw_hdmi_support_scdc(hdmi, display)) {
1729*4882a593Smuzhiyun if (mtmdsclock > HDMI14_MAX_TMDSCLK)
1730*4882a593Smuzhiyun drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1);
1731*4882a593Smuzhiyun else
1732*4882a593Smuzhiyun drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 0);
1733*4882a593Smuzhiyun }
1734*4882a593Smuzhiyun }
1735*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_high_tmds_clock_ratio);
1736*4882a593Smuzhiyun
dw_hdmi_phy_enable_powerdown(struct dw_hdmi * hdmi,bool enable)1737*4882a593Smuzhiyun static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
1738*4882a593Smuzhiyun {
1739*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
1740*4882a593Smuzhiyun HDMI_PHY_CONF0_PDZ_OFFSET,
1741*4882a593Smuzhiyun HDMI_PHY_CONF0_PDZ_MASK);
1742*4882a593Smuzhiyun }
1743*4882a593Smuzhiyun
dw_hdmi_phy_enable_tmds(struct dw_hdmi * hdmi,u8 enable)1744*4882a593Smuzhiyun static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
1745*4882a593Smuzhiyun {
1746*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1747*4882a593Smuzhiyun HDMI_PHY_CONF0_ENTMDS_OFFSET,
1748*4882a593Smuzhiyun HDMI_PHY_CONF0_ENTMDS_MASK);
1749*4882a593Smuzhiyun }
1750*4882a593Smuzhiyun
dw_hdmi_phy_enable_svsret(struct dw_hdmi * hdmi,u8 enable)1751*4882a593Smuzhiyun static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
1752*4882a593Smuzhiyun {
1753*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1754*4882a593Smuzhiyun HDMI_PHY_CONF0_SVSRET_OFFSET,
1755*4882a593Smuzhiyun HDMI_PHY_CONF0_SVSRET_MASK);
1756*4882a593Smuzhiyun }
1757*4882a593Smuzhiyun
dw_hdmi_phy_gen2_pddq(struct dw_hdmi * hdmi,u8 enable)1758*4882a593Smuzhiyun void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
1759*4882a593Smuzhiyun {
1760*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1761*4882a593Smuzhiyun HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
1762*4882a593Smuzhiyun HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
1763*4882a593Smuzhiyun }
1764*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_pddq);
1765*4882a593Smuzhiyun
dw_hdmi_phy_gen2_txpwron(struct dw_hdmi * hdmi,u8 enable)1766*4882a593Smuzhiyun void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
1767*4882a593Smuzhiyun {
1768*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1769*4882a593Smuzhiyun HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
1770*4882a593Smuzhiyun HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
1771*4882a593Smuzhiyun }
1772*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_txpwron);
1773*4882a593Smuzhiyun
dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi * hdmi,u8 enable)1774*4882a593Smuzhiyun static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
1775*4882a593Smuzhiyun {
1776*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1777*4882a593Smuzhiyun HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
1778*4882a593Smuzhiyun HDMI_PHY_CONF0_SELDATAENPOL_MASK);
1779*4882a593Smuzhiyun }
1780*4882a593Smuzhiyun
dw_hdmi_phy_sel_interface_control(struct dw_hdmi * hdmi,u8 enable)1781*4882a593Smuzhiyun static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
1782*4882a593Smuzhiyun {
1783*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
1784*4882a593Smuzhiyun HDMI_PHY_CONF0_SELDIPIF_OFFSET,
1785*4882a593Smuzhiyun HDMI_PHY_CONF0_SELDIPIF_MASK);
1786*4882a593Smuzhiyun }
1787*4882a593Smuzhiyun
dw_hdmi_phy_reset(struct dw_hdmi * hdmi)1788*4882a593Smuzhiyun void dw_hdmi_phy_reset(struct dw_hdmi *hdmi)
1789*4882a593Smuzhiyun {
1790*4882a593Smuzhiyun /* PHY reset. The reset signal is active high on Gen2 PHYs. */
1791*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
1792*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
1793*4882a593Smuzhiyun }
1794*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset);
1795*4882a593Smuzhiyun
dw_hdmi_phy_i2c_set_addr(struct dw_hdmi * hdmi,u8 address)1796*4882a593Smuzhiyun void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)
1797*4882a593Smuzhiyun {
1798*4882a593Smuzhiyun hdmi_phy_test_clear(hdmi, 1);
1799*4882a593Smuzhiyun hdmi_writeb(hdmi, address, HDMI_PHY_I2CM_SLAVE_ADDR);
1800*4882a593Smuzhiyun hdmi_phy_test_clear(hdmi, 0);
1801*4882a593Smuzhiyun }
1802*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_set_addr);
1803*4882a593Smuzhiyun
dw_hdmi_phy_power_off(struct dw_hdmi * hdmi)1804*4882a593Smuzhiyun static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
1805*4882a593Smuzhiyun {
1806*4882a593Smuzhiyun const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
1807*4882a593Smuzhiyun unsigned int i;
1808*4882a593Smuzhiyun u16 val;
1809*4882a593Smuzhiyun
1810*4882a593Smuzhiyun if (phy->gen == 1) {
1811*4882a593Smuzhiyun dw_hdmi_phy_enable_tmds(hdmi, 0);
1812*4882a593Smuzhiyun dw_hdmi_phy_enable_powerdown(hdmi, true);
1813*4882a593Smuzhiyun return;
1814*4882a593Smuzhiyun }
1815*4882a593Smuzhiyun
1816*4882a593Smuzhiyun dw_hdmi_phy_gen2_txpwron(hdmi, 0);
1817*4882a593Smuzhiyun
1818*4882a593Smuzhiyun /*
1819*4882a593Smuzhiyun * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went
1820*4882a593Smuzhiyun * to low power mode.
1821*4882a593Smuzhiyun */
1822*4882a593Smuzhiyun for (i = 0; i < 5; ++i) {
1823*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_PHY_STAT0);
1824*4882a593Smuzhiyun if (!(val & HDMI_PHY_TX_PHY_LOCK))
1825*4882a593Smuzhiyun break;
1826*4882a593Smuzhiyun
1827*4882a593Smuzhiyun usleep_range(1000, 2000);
1828*4882a593Smuzhiyun }
1829*4882a593Smuzhiyun
1830*4882a593Smuzhiyun if (val & HDMI_PHY_TX_PHY_LOCK)
1831*4882a593Smuzhiyun dev_warn(hdmi->dev, "PHY failed to power down\n");
1832*4882a593Smuzhiyun else
1833*4882a593Smuzhiyun dev_dbg(hdmi->dev, "PHY powered down in %u iterations\n", i);
1834*4882a593Smuzhiyun
1835*4882a593Smuzhiyun dw_hdmi_phy_gen2_pddq(hdmi, 1);
1836*4882a593Smuzhiyun }
1837*4882a593Smuzhiyun
dw_hdmi_phy_power_on(struct dw_hdmi * hdmi)1838*4882a593Smuzhiyun static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
1839*4882a593Smuzhiyun {
1840*4882a593Smuzhiyun const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
1841*4882a593Smuzhiyun unsigned int i;
1842*4882a593Smuzhiyun u8 val;
1843*4882a593Smuzhiyun
1844*4882a593Smuzhiyun if (phy->gen == 1) {
1845*4882a593Smuzhiyun dw_hdmi_phy_enable_powerdown(hdmi, false);
1846*4882a593Smuzhiyun
1847*4882a593Smuzhiyun /* Toggle TMDS enable. */
1848*4882a593Smuzhiyun dw_hdmi_phy_enable_tmds(hdmi, 0);
1849*4882a593Smuzhiyun dw_hdmi_phy_enable_tmds(hdmi, 1);
1850*4882a593Smuzhiyun return 0;
1851*4882a593Smuzhiyun }
1852*4882a593Smuzhiyun
1853*4882a593Smuzhiyun dw_hdmi_phy_gen2_txpwron(hdmi, 1);
1854*4882a593Smuzhiyun dw_hdmi_phy_gen2_pddq(hdmi, 0);
1855*4882a593Smuzhiyun
1856*4882a593Smuzhiyun /* Wait for PHY PLL lock */
1857*4882a593Smuzhiyun for (i = 0; i < 5; ++i) {
1858*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
1859*4882a593Smuzhiyun if (val)
1860*4882a593Smuzhiyun break;
1861*4882a593Smuzhiyun
1862*4882a593Smuzhiyun usleep_range(1000, 2000);
1863*4882a593Smuzhiyun }
1864*4882a593Smuzhiyun
1865*4882a593Smuzhiyun if (!val) {
1866*4882a593Smuzhiyun dev_err(hdmi->dev, "PHY PLL failed to lock\n");
1867*4882a593Smuzhiyun return -ETIMEDOUT;
1868*4882a593Smuzhiyun }
1869*4882a593Smuzhiyun
1870*4882a593Smuzhiyun dev_dbg(hdmi->dev, "PHY PLL locked %u iterations\n", i);
1871*4882a593Smuzhiyun return 0;
1872*4882a593Smuzhiyun }
1873*4882a593Smuzhiyun
1874*4882a593Smuzhiyun /*
1875*4882a593Smuzhiyun * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available
1876*4882a593Smuzhiyun * information the DWC MHL PHY has the same register layout and is thus also
1877*4882a593Smuzhiyun * supported by this function.
1878*4882a593Smuzhiyun */
hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi * hdmi,const struct dw_hdmi_plat_data * pdata,unsigned long mpixelclock)1879*4882a593Smuzhiyun static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
1880*4882a593Smuzhiyun const struct dw_hdmi_plat_data *pdata,
1881*4882a593Smuzhiyun unsigned long mpixelclock)
1882*4882a593Smuzhiyun {
1883*4882a593Smuzhiyun const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
1884*4882a593Smuzhiyun const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
1885*4882a593Smuzhiyun const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
1886*4882a593Smuzhiyun unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
1887*4882a593Smuzhiyun unsigned int depth =
1888*4882a593Smuzhiyun hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
1889*4882a593Smuzhiyun
1890*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
1891*4882a593Smuzhiyun pdata->mpll_cfg_420)
1892*4882a593Smuzhiyun mpll_config = pdata->mpll_cfg_420;
1893*4882a593Smuzhiyun
1894*4882a593Smuzhiyun /* TOFIX Will need 420 specific PHY configuration tables */
1895*4882a593Smuzhiyun
1896*4882a593Smuzhiyun /* PLL/MPLL Cfg - always match on final entry */
1897*4882a593Smuzhiyun for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
1898*4882a593Smuzhiyun if (mpixelclock <= mpll_config->mpixelclock)
1899*4882a593Smuzhiyun break;
1900*4882a593Smuzhiyun
1901*4882a593Smuzhiyun for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
1902*4882a593Smuzhiyun if (tmdsclock <= curr_ctrl->mpixelclock)
1903*4882a593Smuzhiyun break;
1904*4882a593Smuzhiyun
1905*4882a593Smuzhiyun for (; phy_config->mpixelclock != ~0UL; phy_config++)
1906*4882a593Smuzhiyun if (tmdsclock <= phy_config->mpixelclock)
1907*4882a593Smuzhiyun break;
1908*4882a593Smuzhiyun
1909*4882a593Smuzhiyun if (mpll_config->mpixelclock == ~0UL ||
1910*4882a593Smuzhiyun curr_ctrl->mpixelclock == ~0UL ||
1911*4882a593Smuzhiyun phy_config->mpixelclock == ~0UL)
1912*4882a593Smuzhiyun return -EINVAL;
1913*4882a593Smuzhiyun
1914*4882a593Smuzhiyun if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
1915*4882a593Smuzhiyun depth = fls(depth - 8);
1916*4882a593Smuzhiyun else
1917*4882a593Smuzhiyun depth = 0;
1918*4882a593Smuzhiyun if (depth)
1919*4882a593Smuzhiyun depth--;
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
1922*4882a593Smuzhiyun HDMI_3D_TX_PHY_CPCE_CTRL);
1923*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
1924*4882a593Smuzhiyun HDMI_3D_TX_PHY_GMPCTRL);
1925*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
1926*4882a593Smuzhiyun HDMI_3D_TX_PHY_CURRCTRL);
1927*4882a593Smuzhiyun
1928*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
1929*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
1930*4882a593Smuzhiyun HDMI_3D_TX_PHY_MSM_CTRL);
1931*4882a593Smuzhiyun
1932*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
1933*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
1934*4882a593Smuzhiyun HDMI_3D_TX_PHY_CKSYMTXCTRL);
1935*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
1936*4882a593Smuzhiyun HDMI_3D_TX_PHY_VLEVCTRL);
1937*4882a593Smuzhiyun
1938*4882a593Smuzhiyun return 0;
1939*4882a593Smuzhiyun }
1940*4882a593Smuzhiyun
hdmi_phy_configure(struct dw_hdmi * hdmi,const struct drm_display_info * display)1941*4882a593Smuzhiyun static int hdmi_phy_configure(struct dw_hdmi *hdmi,
1942*4882a593Smuzhiyun const struct drm_display_info *display)
1943*4882a593Smuzhiyun {
1944*4882a593Smuzhiyun const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
1945*4882a593Smuzhiyun const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
1946*4882a593Smuzhiyun unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
1947*4882a593Smuzhiyun unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
1948*4882a593Smuzhiyun int ret;
1949*4882a593Smuzhiyun
1950*4882a593Smuzhiyun dw_hdmi_phy_power_off(hdmi);
1951*4882a593Smuzhiyun
1952*4882a593Smuzhiyun dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
1953*4882a593Smuzhiyun
1954*4882a593Smuzhiyun /* Leave low power consumption mode by asserting SVSRET. */
1955*4882a593Smuzhiyun if (phy->has_svsret)
1956*4882a593Smuzhiyun dw_hdmi_phy_enable_svsret(hdmi, 1);
1957*4882a593Smuzhiyun
1958*4882a593Smuzhiyun dw_hdmi_phy_reset(hdmi);
1959*4882a593Smuzhiyun
1960*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
1961*4882a593Smuzhiyun
1962*4882a593Smuzhiyun dw_hdmi_phy_i2c_set_addr(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
1963*4882a593Smuzhiyun
1964*4882a593Smuzhiyun /* Write to the PHY as configured by the platform */
1965*4882a593Smuzhiyun if (pdata->configure_phy)
1966*4882a593Smuzhiyun ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
1967*4882a593Smuzhiyun else
1968*4882a593Smuzhiyun ret = phy->configure(hdmi, pdata, mpixelclock);
1969*4882a593Smuzhiyun if (ret) {
1970*4882a593Smuzhiyun dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
1971*4882a593Smuzhiyun mpixelclock);
1972*4882a593Smuzhiyun return ret;
1973*4882a593Smuzhiyun }
1974*4882a593Smuzhiyun
1975*4882a593Smuzhiyun /* Wait for resuming transmission of TMDS clock and data */
1976*4882a593Smuzhiyun if (mtmdsclock > HDMI14_MAX_TMDSCLK)
1977*4882a593Smuzhiyun msleep(100);
1978*4882a593Smuzhiyun
1979*4882a593Smuzhiyun return dw_hdmi_phy_power_on(hdmi);
1980*4882a593Smuzhiyun }
1981*4882a593Smuzhiyun
dw_hdmi_phy_init(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)1982*4882a593Smuzhiyun static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
1983*4882a593Smuzhiyun const struct drm_display_info *display,
1984*4882a593Smuzhiyun const struct drm_display_mode *mode)
1985*4882a593Smuzhiyun {
1986*4882a593Smuzhiyun int i, ret;
1987*4882a593Smuzhiyun
1988*4882a593Smuzhiyun /* HDMI Phy spec says to do the phy initialization sequence twice */
1989*4882a593Smuzhiyun for (i = 0; i < 2; i++) {
1990*4882a593Smuzhiyun dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
1991*4882a593Smuzhiyun dw_hdmi_phy_sel_interface_control(hdmi, 0);
1992*4882a593Smuzhiyun
1993*4882a593Smuzhiyun ret = hdmi_phy_configure(hdmi, display);
1994*4882a593Smuzhiyun if (ret)
1995*4882a593Smuzhiyun return ret;
1996*4882a593Smuzhiyun }
1997*4882a593Smuzhiyun
1998*4882a593Smuzhiyun return 0;
1999*4882a593Smuzhiyun }
2000*4882a593Smuzhiyun
dw_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)2001*4882a593Smuzhiyun static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
2002*4882a593Smuzhiyun {
2003*4882a593Smuzhiyun dw_hdmi_phy_power_off(hdmi);
2004*4882a593Smuzhiyun }
2005*4882a593Smuzhiyun
dw_hdmi_phy_read_hpd(struct dw_hdmi * hdmi,void * data)2006*4882a593Smuzhiyun enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
2007*4882a593Smuzhiyun void *data)
2008*4882a593Smuzhiyun {
2009*4882a593Smuzhiyun return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
2010*4882a593Smuzhiyun connector_status_connected : connector_status_disconnected;
2011*4882a593Smuzhiyun }
2012*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_read_hpd);
2013*4882a593Smuzhiyun
dw_hdmi_phy_update_hpd(struct dw_hdmi * hdmi,void * data,bool force,bool disabled,bool rxsense)2014*4882a593Smuzhiyun void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
2015*4882a593Smuzhiyun bool force, bool disabled, bool rxsense)
2016*4882a593Smuzhiyun {
2017*4882a593Smuzhiyun u8 old_mask = hdmi->phy_mask;
2018*4882a593Smuzhiyun
2019*4882a593Smuzhiyun if (force || disabled || !rxsense)
2020*4882a593Smuzhiyun hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
2021*4882a593Smuzhiyun else
2022*4882a593Smuzhiyun hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
2023*4882a593Smuzhiyun
2024*4882a593Smuzhiyun if (old_mask != hdmi->phy_mask)
2025*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
2026*4882a593Smuzhiyun }
2027*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_update_hpd);
2028*4882a593Smuzhiyun
dw_hdmi_phy_setup_hpd(struct dw_hdmi * hdmi,void * data)2029*4882a593Smuzhiyun void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
2030*4882a593Smuzhiyun {
2031*4882a593Smuzhiyun /*
2032*4882a593Smuzhiyun * Configure the PHY RX SENSE and HPD interrupts polarities and clear
2033*4882a593Smuzhiyun * any pending interrupt.
2034*4882a593Smuzhiyun */
2035*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
2036*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
2037*4882a593Smuzhiyun HDMI_IH_PHY_STAT0);
2038*4882a593Smuzhiyun
2039*4882a593Smuzhiyun if (!hdmi->next_bridge) {
2040*4882a593Smuzhiyun /* Enable cable hot plug irq. */
2041*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
2042*4882a593Smuzhiyun
2043*4882a593Smuzhiyun /* Clear and unmute interrupts. */
2044*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
2045*4882a593Smuzhiyun HDMI_IH_PHY_STAT0);
2046*4882a593Smuzhiyun hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
2047*4882a593Smuzhiyun HDMI_IH_MUTE_PHY_STAT0);
2048*4882a593Smuzhiyun }
2049*4882a593Smuzhiyun }
2050*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
2051*4882a593Smuzhiyun
2052*4882a593Smuzhiyun static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
2053*4882a593Smuzhiyun .init = dw_hdmi_phy_init,
2054*4882a593Smuzhiyun .disable = dw_hdmi_phy_disable,
2055*4882a593Smuzhiyun .read_hpd = dw_hdmi_phy_read_hpd,
2056*4882a593Smuzhiyun .update_hpd = dw_hdmi_phy_update_hpd,
2057*4882a593Smuzhiyun .setup_hpd = dw_hdmi_phy_setup_hpd,
2058*4882a593Smuzhiyun };
2059*4882a593Smuzhiyun
2060*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
2061*4882a593Smuzhiyun * HDMI TX Setup
2062*4882a593Smuzhiyun */
2063*4882a593Smuzhiyun
hdmi_tx_hdcp_config(struct dw_hdmi * hdmi,const struct drm_display_mode * mode)2064*4882a593Smuzhiyun static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
2065*4882a593Smuzhiyun const struct drm_display_mode *mode)
2066*4882a593Smuzhiyun {
2067*4882a593Smuzhiyun struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
2068*4882a593Smuzhiyun u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
2069*4882a593Smuzhiyun
2070*4882a593Smuzhiyun /* Configure the video polarity */
2071*4882a593Smuzhiyun vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
2072*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
2073*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
2074*4882a593Smuzhiyun hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
2075*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
2076*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
2077*4882a593Smuzhiyun data_pol = vmode->mdataenablepolarity ?
2078*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH :
2079*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
2080*4882a593Smuzhiyun hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
2081*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
2082*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
2083*4882a593Smuzhiyun HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
2084*4882a593Smuzhiyun HDMI_A_VIDPOLCFG);
2085*4882a593Smuzhiyun
2086*4882a593Smuzhiyun /* Config the display mode */
2087*4882a593Smuzhiyun hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
2088*4882a593Smuzhiyun HDMI_A_HDCPCFG0_HDMIDVI_DVI;
2089*4882a593Smuzhiyun hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
2090*4882a593Smuzhiyun HDMI_A_HDCPCFG0);
2091*4882a593Smuzhiyun
2092*4882a593Smuzhiyun if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
2093*4882a593Smuzhiyun hdmi->hdcp->hdcp_start(hdmi->hdcp);
2094*4882a593Smuzhiyun }
2095*4882a593Smuzhiyun
hdmi_config_AVI(struct dw_hdmi * hdmi,const struct drm_connector * connector,const struct drm_display_mode * mode)2096*4882a593Smuzhiyun static void hdmi_config_AVI(struct dw_hdmi *hdmi,
2097*4882a593Smuzhiyun const struct drm_connector *connector,
2098*4882a593Smuzhiyun const struct drm_display_mode *mode)
2099*4882a593Smuzhiyun {
2100*4882a593Smuzhiyun struct hdmi_avi_infoframe frame;
2101*4882a593Smuzhiyun u8 val;
2102*4882a593Smuzhiyun bool is_hdmi2;
2103*4882a593Smuzhiyun const struct drm_display_info *info = &connector->display_info;
2104*4882a593Smuzhiyun
2105*4882a593Smuzhiyun is_hdmi2 = info->hdmi.scdc.supported || (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
2106*4882a593Smuzhiyun
2107*4882a593Smuzhiyun /* Initialise info frame from DRM mode */
2108*4882a593Smuzhiyun drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
2109*4882a593Smuzhiyun
2110*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
2111*4882a593Smuzhiyun /* default range */
2112*4882a593Smuzhiyun if (!hdmi->hdmi_data.quant_range)
2113*4882a593Smuzhiyun drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
2114*4882a593Smuzhiyun hdmi->hdmi_data.rgb_limited_range ?
2115*4882a593Smuzhiyun HDMI_QUANTIZATION_RANGE_LIMITED :
2116*4882a593Smuzhiyun HDMI_QUANTIZATION_RANGE_FULL);
2117*4882a593Smuzhiyun else
2118*4882a593Smuzhiyun drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
2119*4882a593Smuzhiyun hdmi->hdmi_data.quant_range);
2120*4882a593Smuzhiyun } else {
2121*4882a593Smuzhiyun frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
2122*4882a593Smuzhiyun frame.ycc_quantization_range =
2123*4882a593Smuzhiyun HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
2124*4882a593Smuzhiyun }
2125*4882a593Smuzhiyun
2126*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
2127*4882a593Smuzhiyun frame.colorspace = HDMI_COLORSPACE_YUV444;
2128*4882a593Smuzhiyun else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
2129*4882a593Smuzhiyun frame.colorspace = HDMI_COLORSPACE_YUV422;
2130*4882a593Smuzhiyun else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
2131*4882a593Smuzhiyun frame.colorspace = HDMI_COLORSPACE_YUV420;
2132*4882a593Smuzhiyun else
2133*4882a593Smuzhiyun frame.colorspace = HDMI_COLORSPACE_RGB;
2134*4882a593Smuzhiyun
2135*4882a593Smuzhiyun /* Set up colorimetry */
2136*4882a593Smuzhiyun if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
2137*4882a593Smuzhiyun switch (hdmi->hdmi_data.enc_out_encoding) {
2138*4882a593Smuzhiyun case V4L2_YCBCR_ENC_601:
2139*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
2140*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
2141*4882a593Smuzhiyun else
2142*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
2143*4882a593Smuzhiyun frame.extended_colorimetry =
2144*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
2145*4882a593Smuzhiyun break;
2146*4882a593Smuzhiyun case V4L2_YCBCR_ENC_709:
2147*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709)
2148*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
2149*4882a593Smuzhiyun else
2150*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
2151*4882a593Smuzhiyun frame.extended_colorimetry =
2152*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
2153*4882a593Smuzhiyun break;
2154*4882a593Smuzhiyun case V4L2_YCBCR_ENC_BT2020:
2155*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020)
2156*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
2157*4882a593Smuzhiyun else
2158*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
2159*4882a593Smuzhiyun frame.extended_colorimetry =
2160*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_BT2020;
2161*4882a593Smuzhiyun break;
2162*4882a593Smuzhiyun default: /* Carries no data */
2163*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
2164*4882a593Smuzhiyun frame.extended_colorimetry =
2165*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
2166*4882a593Smuzhiyun break;
2167*4882a593Smuzhiyun }
2168*4882a593Smuzhiyun frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
2169*4882a593Smuzhiyun } else {
2170*4882a593Smuzhiyun if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
2171*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
2172*4882a593Smuzhiyun frame.extended_colorimetry =
2173*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_BT2020;
2174*4882a593Smuzhiyun } else {
2175*4882a593Smuzhiyun frame.colorimetry = HDMI_COLORIMETRY_NONE;
2176*4882a593Smuzhiyun frame.extended_colorimetry =
2177*4882a593Smuzhiyun HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
2178*4882a593Smuzhiyun }
2179*4882a593Smuzhiyun
2180*4882a593Smuzhiyun if (is_hdmi2 && frame.quantization_range == HDMI_QUANTIZATION_RANGE_FULL)
2181*4882a593Smuzhiyun frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_FULL;
2182*4882a593Smuzhiyun else
2183*4882a593Smuzhiyun frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
2184*4882a593Smuzhiyun }
2185*4882a593Smuzhiyun
2186*4882a593Smuzhiyun /*
2187*4882a593Smuzhiyun * The Designware IP uses a different byte format from standard
2188*4882a593Smuzhiyun * AVI info frames, though generally the bits are in the correct
2189*4882a593Smuzhiyun * bytes.
2190*4882a593Smuzhiyun */
2191*4882a593Smuzhiyun
2192*4882a593Smuzhiyun /*
2193*4882a593Smuzhiyun * AVI data byte 1 differences: Colorspace in bits 0,1 rather than 5,6,
2194*4882a593Smuzhiyun * scan info in bits 4,5 rather than 0,1 and active aspect present in
2195*4882a593Smuzhiyun * bit 6 rather than 4.
2196*4882a593Smuzhiyun */
2197*4882a593Smuzhiyun val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3);
2198*4882a593Smuzhiyun if (frame.active_aspect & 15)
2199*4882a593Smuzhiyun val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
2200*4882a593Smuzhiyun if (frame.top_bar || frame.bottom_bar)
2201*4882a593Smuzhiyun val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
2202*4882a593Smuzhiyun if (frame.left_bar || frame.right_bar)
2203*4882a593Smuzhiyun val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
2204*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
2205*4882a593Smuzhiyun
2206*4882a593Smuzhiyun /* AVI data byte 2 differences: none */
2207*4882a593Smuzhiyun val = ((frame.colorimetry & 0x3) << 6) |
2208*4882a593Smuzhiyun ((frame.picture_aspect & 0x3) << 4) |
2209*4882a593Smuzhiyun (frame.active_aspect & 0xf);
2210*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
2211*4882a593Smuzhiyun
2212*4882a593Smuzhiyun /* AVI data byte 3 differences: none */
2213*4882a593Smuzhiyun val = ((frame.extended_colorimetry & 0x7) << 4) |
2214*4882a593Smuzhiyun ((frame.quantization_range & 0x3) << 2) |
2215*4882a593Smuzhiyun (frame.nups & 0x3);
2216*4882a593Smuzhiyun if (frame.itc)
2217*4882a593Smuzhiyun val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
2218*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
2219*4882a593Smuzhiyun
2220*4882a593Smuzhiyun /* AVI data byte 4 differences: none */
2221*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
2222*4882a593Smuzhiyun hdmi->connector.display_info.hdmi.scdc.supported)
2223*4882a593Smuzhiyun val = hdmi->vic;
2224*4882a593Smuzhiyun else
2225*4882a593Smuzhiyun val = frame.video_code & 0x7f;
2226*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
2227*4882a593Smuzhiyun
2228*4882a593Smuzhiyun /* AVI Data Byte 5- set up input and output pixel repetition */
2229*4882a593Smuzhiyun val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
2230*4882a593Smuzhiyun HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
2231*4882a593Smuzhiyun HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
2232*4882a593Smuzhiyun ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
2233*4882a593Smuzhiyun HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
2234*4882a593Smuzhiyun HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
2235*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
2236*4882a593Smuzhiyun
2237*4882a593Smuzhiyun /*
2238*4882a593Smuzhiyun * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
2239*4882a593Smuzhiyun * ycc range in bits 2,3 rather than 6,7
2240*4882a593Smuzhiyun */
2241*4882a593Smuzhiyun val = ((frame.ycc_quantization_range & 0x3) << 2) |
2242*4882a593Smuzhiyun (frame.content_type & 0x3);
2243*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
2244*4882a593Smuzhiyun
2245*4882a593Smuzhiyun /* AVI Data Bytes 6-13 */
2246*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
2247*4882a593Smuzhiyun hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
2248*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
2249*4882a593Smuzhiyun hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
2250*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
2251*4882a593Smuzhiyun hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
2252*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
2253*4882a593Smuzhiyun hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
2254*4882a593Smuzhiyun }
2255*4882a593Smuzhiyun
hdmi_config_vendor_specific_infoframe(struct dw_hdmi * hdmi,const struct drm_connector * connector,const struct drm_display_mode * mode)2256*4882a593Smuzhiyun static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
2257*4882a593Smuzhiyun const struct drm_connector *connector,
2258*4882a593Smuzhiyun const struct drm_display_mode *mode)
2259*4882a593Smuzhiyun {
2260*4882a593Smuzhiyun struct hdmi_vendor_infoframe frame;
2261*4882a593Smuzhiyun u8 buffer[10];
2262*4882a593Smuzhiyun ssize_t err;
2263*4882a593Smuzhiyun
2264*4882a593Smuzhiyun /* if sink support hdmi2.0, don't send vsi */
2265*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
2266*4882a593Smuzhiyun hdmi->connector.display_info.hdmi.scdc.supported) {
2267*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
2268*4882a593Smuzhiyun HDMI_FC_DATAUTO0_VSD_MASK);
2269*4882a593Smuzhiyun return;
2270*4882a593Smuzhiyun }
2271*4882a593Smuzhiyun
2272*4882a593Smuzhiyun err = drm_hdmi_vendor_infoframe_from_display_mode(&frame,
2273*4882a593Smuzhiyun &hdmi->connector,
2274*4882a593Smuzhiyun mode);
2275*4882a593Smuzhiyun if (err < 0)
2276*4882a593Smuzhiyun /*
2277*4882a593Smuzhiyun * Going into that statement does not means vendor infoframe
2278*4882a593Smuzhiyun * fails. It just informed us that vendor infoframe is not
2279*4882a593Smuzhiyun * needed for the selected mode. Only 4k or stereoscopic 3D
2280*4882a593Smuzhiyun * mode requires vendor infoframe. So just simply return.
2281*4882a593Smuzhiyun */
2282*4882a593Smuzhiyun return;
2283*4882a593Smuzhiyun
2284*4882a593Smuzhiyun err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
2285*4882a593Smuzhiyun if (err < 0) {
2286*4882a593Smuzhiyun dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
2287*4882a593Smuzhiyun err);
2288*4882a593Smuzhiyun return;
2289*4882a593Smuzhiyun }
2290*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
2291*4882a593Smuzhiyun HDMI_FC_DATAUTO0_VSD_MASK);
2292*4882a593Smuzhiyun
2293*4882a593Smuzhiyun /* Set the length of HDMI vendor specific InfoFrame payload */
2294*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE);
2295*4882a593Smuzhiyun
2296*4882a593Smuzhiyun /* Set 24bit IEEE Registration Identifier */
2297*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0);
2298*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1);
2299*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2);
2300*4882a593Smuzhiyun
2301*4882a593Smuzhiyun /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */
2302*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0);
2303*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1);
2304*4882a593Smuzhiyun
2305*4882a593Smuzhiyun if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
2306*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2);
2307*4882a593Smuzhiyun
2308*4882a593Smuzhiyun /* Packet frame interpolation */
2309*4882a593Smuzhiyun hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1);
2310*4882a593Smuzhiyun
2311*4882a593Smuzhiyun /* Auto packets per frame and line spacing */
2312*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2);
2313*4882a593Smuzhiyun
2314*4882a593Smuzhiyun /* Configures the Frame Composer On RDRB mode */
2315*4882a593Smuzhiyun hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
2316*4882a593Smuzhiyun HDMI_FC_DATAUTO0_VSD_MASK);
2317*4882a593Smuzhiyun }
2318*4882a593Smuzhiyun
hdmi_config_drm_infoframe(struct dw_hdmi * hdmi,const struct drm_connector * connector)2319*4882a593Smuzhiyun static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
2320*4882a593Smuzhiyun const struct drm_connector *connector)
2321*4882a593Smuzhiyun {
2322*4882a593Smuzhiyun const struct drm_connector_state *conn_state = connector->state;
2323*4882a593Smuzhiyun struct hdr_output_metadata *hdr_metadata;
2324*4882a593Smuzhiyun struct hdmi_drm_infoframe frame;
2325*4882a593Smuzhiyun u8 buffer[30];
2326*4882a593Smuzhiyun ssize_t err;
2327*4882a593Smuzhiyun int i;
2328*4882a593Smuzhiyun
2329*4882a593Smuzhiyun /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
2330*4882a593Smuzhiyun if (hdmi->version < 0x211a) {
2331*4882a593Smuzhiyun DRM_ERROR("Not support DRM Infoframe\n");
2332*4882a593Smuzhiyun return;
2333*4882a593Smuzhiyun }
2334*4882a593Smuzhiyun
2335*4882a593Smuzhiyun if (!hdmi->plat_data->use_drm_infoframe)
2336*4882a593Smuzhiyun return;
2337*4882a593Smuzhiyun
2338*4882a593Smuzhiyun hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
2339*4882a593Smuzhiyun HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
2340*4882a593Smuzhiyun
2341*4882a593Smuzhiyun if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
2342*4882a593Smuzhiyun DRM_DEBUG("No need to set HDR metadata in infoframe\n");
2343*4882a593Smuzhiyun return;
2344*4882a593Smuzhiyun }
2345*4882a593Smuzhiyun
2346*4882a593Smuzhiyun if (!conn_state->hdr_output_metadata) {
2347*4882a593Smuzhiyun DRM_DEBUG("source metadata not set yet\n");
2348*4882a593Smuzhiyun return;
2349*4882a593Smuzhiyun }
2350*4882a593Smuzhiyun
2351*4882a593Smuzhiyun hdr_metadata = (struct hdr_output_metadata *)
2352*4882a593Smuzhiyun conn_state->hdr_output_metadata->data;
2353*4882a593Smuzhiyun
2354*4882a593Smuzhiyun if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf &
2355*4882a593Smuzhiyun BIT(hdr_metadata->hdmi_metadata_type1.eotf))) {
2356*4882a593Smuzhiyun DRM_ERROR("Not support EOTF %d\n",
2357*4882a593Smuzhiyun hdr_metadata->hdmi_metadata_type1.eotf);
2358*4882a593Smuzhiyun return;
2359*4882a593Smuzhiyun }
2360*4882a593Smuzhiyun
2361*4882a593Smuzhiyun err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
2362*4882a593Smuzhiyun if (err < 0)
2363*4882a593Smuzhiyun return;
2364*4882a593Smuzhiyun
2365*4882a593Smuzhiyun err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer));
2366*4882a593Smuzhiyun if (err < 0) {
2367*4882a593Smuzhiyun dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err);
2368*4882a593Smuzhiyun return;
2369*4882a593Smuzhiyun }
2370*4882a593Smuzhiyun
2371*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0);
2372*4882a593Smuzhiyun hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1);
2373*4882a593Smuzhiyun
2374*4882a593Smuzhiyun for (i = 0; i < frame.length; i++)
2375*4882a593Smuzhiyun hdmi_writeb(hdmi, buffer[4 + i], HDMI_FC_DRM_PB0 + i);
2376*4882a593Smuzhiyun
2377*4882a593Smuzhiyun hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP);
2378*4882a593Smuzhiyun /*
2379*4882a593Smuzhiyun * avi and hdr infoframe cannot be sent at the same time
2380*4882a593Smuzhiyun * for compatibility with Huawei TV
2381*4882a593Smuzhiyun */
2382*4882a593Smuzhiyun msleep(300);
2383*4882a593Smuzhiyun hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE,
2384*4882a593Smuzhiyun HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
2385*4882a593Smuzhiyun
2386*4882a593Smuzhiyun DRM_DEBUG("%s eotf %d end\n", __func__,
2387*4882a593Smuzhiyun hdr_metadata->hdmi_metadata_type1.eotf);
2388*4882a593Smuzhiyun }
2389*4882a593Smuzhiyun
2390*4882a593Smuzhiyun static unsigned int
hdmi_get_tmdsclock(struct dw_hdmi * hdmi,unsigned long mpixelclock)2391*4882a593Smuzhiyun hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
2392*4882a593Smuzhiyun {
2393*4882a593Smuzhiyun unsigned int tmdsclock = mpixelclock;
2394*4882a593Smuzhiyun unsigned int depth =
2395*4882a593Smuzhiyun hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
2396*4882a593Smuzhiyun
2397*4882a593Smuzhiyun if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
2398*4882a593Smuzhiyun switch (depth) {
2399*4882a593Smuzhiyun case 16:
2400*4882a593Smuzhiyun tmdsclock = mpixelclock * 2;
2401*4882a593Smuzhiyun break;
2402*4882a593Smuzhiyun case 12:
2403*4882a593Smuzhiyun tmdsclock = mpixelclock * 3 / 2;
2404*4882a593Smuzhiyun break;
2405*4882a593Smuzhiyun case 10:
2406*4882a593Smuzhiyun tmdsclock = mpixelclock * 5 / 4;
2407*4882a593Smuzhiyun break;
2408*4882a593Smuzhiyun default:
2409*4882a593Smuzhiyun break;
2410*4882a593Smuzhiyun }
2411*4882a593Smuzhiyun }
2412*4882a593Smuzhiyun
2413*4882a593Smuzhiyun return tmdsclock;
2414*4882a593Smuzhiyun }
2415*4882a593Smuzhiyun
hdmi_av_composer(struct dw_hdmi * hdmi,const struct drm_display_info * display,const struct drm_display_mode * mode)2416*4882a593Smuzhiyun static void hdmi_av_composer(struct dw_hdmi *hdmi,
2417*4882a593Smuzhiyun const struct drm_display_info *display,
2418*4882a593Smuzhiyun const struct drm_display_mode *mode)
2419*4882a593Smuzhiyun {
2420*4882a593Smuzhiyun u8 inv_val, bytes;
2421*4882a593Smuzhiyun const struct drm_hdmi_info *hdmi_info = &display->hdmi;
2422*4882a593Smuzhiyun struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
2423*4882a593Smuzhiyun int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
2424*4882a593Smuzhiyun unsigned int vdisplay, hdisplay;
2425*4882a593Smuzhiyun
2426*4882a593Smuzhiyun vmode->previous_pixelclock = vmode->mpixelclock;
2427*4882a593Smuzhiyun vmode->mpixelclock = mode->crtc_clock * 1000;
2428*4882a593Smuzhiyun if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
2429*4882a593Smuzhiyun DRM_MODE_FLAG_3D_FRAME_PACKING)
2430*4882a593Smuzhiyun vmode->mpixelclock *= 2;
2431*4882a593Smuzhiyun dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
2432*4882a593Smuzhiyun
2433*4882a593Smuzhiyun vmode->previous_tmdsclock = vmode->mtmdsclock;
2434*4882a593Smuzhiyun vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
2435*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
2436*4882a593Smuzhiyun vmode->mtmdsclock /= 2;
2437*4882a593Smuzhiyun dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
2438*4882a593Smuzhiyun
2439*4882a593Smuzhiyun if (hdmi->update)
2440*4882a593Smuzhiyun return;
2441*4882a593Smuzhiyun
2442*4882a593Smuzhiyun /* Set up HDMI_FC_INVIDCONF
2443*4882a593Smuzhiyun * Some display equipments require that the interval
2444*4882a593Smuzhiyun * between Video Data and Data island must be at least 58 pixels,
2445*4882a593Smuzhiyun * and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement.
2446*4882a593Smuzhiyun */
2447*4882a593Smuzhiyun inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE;
2448*4882a593Smuzhiyun
2449*4882a593Smuzhiyun inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
2450*4882a593Smuzhiyun HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
2451*4882a593Smuzhiyun HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
2452*4882a593Smuzhiyun
2453*4882a593Smuzhiyun inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
2454*4882a593Smuzhiyun HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
2455*4882a593Smuzhiyun HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
2456*4882a593Smuzhiyun
2457*4882a593Smuzhiyun inv_val |= (vmode->mdataenablepolarity ?
2458*4882a593Smuzhiyun HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
2459*4882a593Smuzhiyun HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
2460*4882a593Smuzhiyun
2461*4882a593Smuzhiyun if (hdmi->vic == 39)
2462*4882a593Smuzhiyun inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
2463*4882a593Smuzhiyun else
2464*4882a593Smuzhiyun inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
2465*4882a593Smuzhiyun HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
2466*4882a593Smuzhiyun HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
2467*4882a593Smuzhiyun
2468*4882a593Smuzhiyun inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
2469*4882a593Smuzhiyun HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
2470*4882a593Smuzhiyun HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
2471*4882a593Smuzhiyun
2472*4882a593Smuzhiyun inv_val |= hdmi->sink_is_hdmi ?
2473*4882a593Smuzhiyun HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
2474*4882a593Smuzhiyun HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
2475*4882a593Smuzhiyun
2476*4882a593Smuzhiyun hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
2477*4882a593Smuzhiyun
2478*4882a593Smuzhiyun hdisplay = mode->hdisplay;
2479*4882a593Smuzhiyun hblank = mode->htotal - mode->hdisplay;
2480*4882a593Smuzhiyun h_de_hs = mode->hsync_start - mode->hdisplay;
2481*4882a593Smuzhiyun hsync_len = mode->hsync_end - mode->hsync_start;
2482*4882a593Smuzhiyun
2483*4882a593Smuzhiyun /*
2484*4882a593Smuzhiyun * When we're setting a YCbCr420 mode, we need
2485*4882a593Smuzhiyun * to adjust the horizontal timing to suit.
2486*4882a593Smuzhiyun */
2487*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
2488*4882a593Smuzhiyun hdisplay /= 2;
2489*4882a593Smuzhiyun hblank /= 2;
2490*4882a593Smuzhiyun h_de_hs /= 2;
2491*4882a593Smuzhiyun hsync_len /= 2;
2492*4882a593Smuzhiyun }
2493*4882a593Smuzhiyun
2494*4882a593Smuzhiyun vdisplay = mode->vdisplay;
2495*4882a593Smuzhiyun vblank = mode->vtotal - mode->vdisplay;
2496*4882a593Smuzhiyun v_de_vs = mode->vsync_start - mode->vdisplay;
2497*4882a593Smuzhiyun vsync_len = mode->vsync_end - mode->vsync_start;
2498*4882a593Smuzhiyun
2499*4882a593Smuzhiyun /*
2500*4882a593Smuzhiyun * When we're setting an interlaced mode, we need
2501*4882a593Smuzhiyun * to adjust the vertical timing to suit.
2502*4882a593Smuzhiyun */
2503*4882a593Smuzhiyun if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
2504*4882a593Smuzhiyun vdisplay /= 2;
2505*4882a593Smuzhiyun vblank /= 2;
2506*4882a593Smuzhiyun v_de_vs /= 2;
2507*4882a593Smuzhiyun vsync_len /= 2;
2508*4882a593Smuzhiyun }
2509*4882a593Smuzhiyun
2510*4882a593Smuzhiyun /* Scrambling Control */
2511*4882a593Smuzhiyun if (dw_hdmi_support_scdc(hdmi, display)) {
2512*4882a593Smuzhiyun if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
2513*4882a593Smuzhiyun (hdmi_info->scdc.scrambling.low_rates &&
2514*4882a593Smuzhiyun hdmi->scramble_low_rates)) {
2515*4882a593Smuzhiyun /*
2516*4882a593Smuzhiyun * HDMI2.0 Specifies the following procedure:
2517*4882a593Smuzhiyun * After the Source Device has determined that
2518*4882a593Smuzhiyun * SCDC_Present is set (=1), the Source Device should
2519*4882a593Smuzhiyun * write the accurate Version of the Source Device
2520*4882a593Smuzhiyun * to the Source Version field in the SCDCS.
2521*4882a593Smuzhiyun * Source Devices compliant shall set the
2522*4882a593Smuzhiyun * Source Version = 1.
2523*4882a593Smuzhiyun */
2524*4882a593Smuzhiyun drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION,
2525*4882a593Smuzhiyun &bytes);
2526*4882a593Smuzhiyun drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION,
2527*4882a593Smuzhiyun min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION));
2528*4882a593Smuzhiyun
2529*4882a593Smuzhiyun /* Enabled Scrambling in the Sink */
2530*4882a593Smuzhiyun drm_scdc_set_scrambling(hdmi->ddc, 1);
2531*4882a593Smuzhiyun
2532*4882a593Smuzhiyun /*
2533*4882a593Smuzhiyun * To activate the scrambler feature, you must ensure
2534*4882a593Smuzhiyun * that the quasi-static configuration bit
2535*4882a593Smuzhiyun * fc_invidconf.HDCP_keepout is set at configuration
2536*4882a593Smuzhiyun * time, before the required mc_swrstzreq.tmdsswrst_req
2537*4882a593Smuzhiyun * reset request is issued.
2538*4882a593Smuzhiyun */
2539*4882a593Smuzhiyun hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
2540*4882a593Smuzhiyun HDMI_MC_SWRSTZ);
2541*4882a593Smuzhiyun hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL);
2542*4882a593Smuzhiyun } else {
2543*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
2544*4882a593Smuzhiyun hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
2545*4882a593Smuzhiyun HDMI_MC_SWRSTZ);
2546*4882a593Smuzhiyun drm_scdc_set_scrambling(hdmi->ddc, 0);
2547*4882a593Smuzhiyun }
2548*4882a593Smuzhiyun } else {
2549*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
2550*4882a593Smuzhiyun }
2551*4882a593Smuzhiyun
2552*4882a593Smuzhiyun /* Set up horizontal active pixel width */
2553*4882a593Smuzhiyun hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
2554*4882a593Smuzhiyun hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
2555*4882a593Smuzhiyun
2556*4882a593Smuzhiyun /* Set up vertical active lines */
2557*4882a593Smuzhiyun hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
2558*4882a593Smuzhiyun hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
2559*4882a593Smuzhiyun
2560*4882a593Smuzhiyun /* Set up horizontal blanking pixel region width */
2561*4882a593Smuzhiyun hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
2562*4882a593Smuzhiyun hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
2563*4882a593Smuzhiyun
2564*4882a593Smuzhiyun /* Set up vertical blanking pixel region width */
2565*4882a593Smuzhiyun hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
2566*4882a593Smuzhiyun
2567*4882a593Smuzhiyun /* Set up HSYNC active edge delay width (in pixel clks) */
2568*4882a593Smuzhiyun hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
2569*4882a593Smuzhiyun hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
2570*4882a593Smuzhiyun
2571*4882a593Smuzhiyun /* Set up VSYNC active edge delay (in lines) */
2572*4882a593Smuzhiyun hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
2573*4882a593Smuzhiyun
2574*4882a593Smuzhiyun /* Set up HSYNC active pulse width (in pixel clks) */
2575*4882a593Smuzhiyun hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
2576*4882a593Smuzhiyun hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
2577*4882a593Smuzhiyun
2578*4882a593Smuzhiyun /* Set up VSYNC active edge delay (in lines) */
2579*4882a593Smuzhiyun hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
2580*4882a593Smuzhiyun }
2581*4882a593Smuzhiyun
2582*4882a593Smuzhiyun /* HDMI Initialization Step B.4 */
dw_hdmi_enable_video_path(struct dw_hdmi * hdmi)2583*4882a593Smuzhiyun static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
2584*4882a593Smuzhiyun {
2585*4882a593Smuzhiyun /* control period minimum duration */
2586*4882a593Smuzhiyun hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
2587*4882a593Smuzhiyun hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
2588*4882a593Smuzhiyun hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
2589*4882a593Smuzhiyun
2590*4882a593Smuzhiyun /* Set to fill TMDS data channels */
2591*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
2592*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
2593*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
2594*4882a593Smuzhiyun
2595*4882a593Smuzhiyun /* Enable pixel clock and tmds data path */
2596*4882a593Smuzhiyun
2597*4882a593Smuzhiyun if (!hdmi->update)
2598*4882a593Smuzhiyun hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
2599*4882a593Smuzhiyun HDMI_MC_CLKDIS_CSCCLK_DISABLE |
2600*4882a593Smuzhiyun HDMI_MC_CLKDIS_AUDCLK_DISABLE |
2601*4882a593Smuzhiyun HDMI_MC_CLKDIS_PREPCLK_DISABLE |
2602*4882a593Smuzhiyun HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
2603*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
2604*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
2605*4882a593Smuzhiyun
2606*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
2607*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
2608*4882a593Smuzhiyun
2609*4882a593Smuzhiyun /* Enable pixel repetition path */
2610*4882a593Smuzhiyun if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
2611*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
2612*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
2613*4882a593Smuzhiyun }
2614*4882a593Smuzhiyun
2615*4882a593Smuzhiyun /* Enable csc path */
2616*4882a593Smuzhiyun if (is_csc_needed(hdmi)) {
2617*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
2618*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
2619*4882a593Smuzhiyun
2620*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
2621*4882a593Smuzhiyun HDMI_MC_FLOWCTRL);
2622*4882a593Smuzhiyun } else {
2623*4882a593Smuzhiyun hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CSCCLK_DISABLE;
2624*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
2625*4882a593Smuzhiyun
2626*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
2627*4882a593Smuzhiyun HDMI_MC_FLOWCTRL);
2628*4882a593Smuzhiyun }
2629*4882a593Smuzhiyun }
2630*4882a593Smuzhiyun
2631*4882a593Smuzhiyun /* Workaround to clear the overflow condition */
dw_hdmi_clear_overflow(struct dw_hdmi * hdmi)2632*4882a593Smuzhiyun static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
2633*4882a593Smuzhiyun {
2634*4882a593Smuzhiyun unsigned int count;
2635*4882a593Smuzhiyun unsigned int i;
2636*4882a593Smuzhiyun u8 val;
2637*4882a593Smuzhiyun
2638*4882a593Smuzhiyun /*
2639*4882a593Smuzhiyun * Under some circumstances the Frame Composer arithmetic unit can miss
2640*4882a593Smuzhiyun * an FC register write due to being busy processing the previous one.
2641*4882a593Smuzhiyun * The issue can be worked around by issuing a TMDS software reset and
2642*4882a593Smuzhiyun * then write one of the FC registers several times.
2643*4882a593Smuzhiyun *
2644*4882a593Smuzhiyun * The number of iterations matters and depends on the HDMI TX revision
2645*4882a593Smuzhiyun * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
2646*4882a593Smuzhiyun * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
2647*4882a593Smuzhiyun * as needing the workaround, with 4 iterations for v1.30a and 1
2648*4882a593Smuzhiyun * iteration for others.
2649*4882a593Smuzhiyun * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
2650*4882a593Smuzhiyun * the workaround with a single iteration.
2651*4882a593Smuzhiyun * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have
2652*4882a593Smuzhiyun * been identified as needing the workaround with a single iteration.
2653*4882a593Smuzhiyun */
2654*4882a593Smuzhiyun
2655*4882a593Smuzhiyun switch (hdmi->version) {
2656*4882a593Smuzhiyun case 0x130a:
2657*4882a593Smuzhiyun count = 4;
2658*4882a593Smuzhiyun break;
2659*4882a593Smuzhiyun case 0x131a:
2660*4882a593Smuzhiyun case 0x132a:
2661*4882a593Smuzhiyun case 0x200a:
2662*4882a593Smuzhiyun case 0x201a:
2663*4882a593Smuzhiyun case 0x211a:
2664*4882a593Smuzhiyun case 0x212a:
2665*4882a593Smuzhiyun count = 1;
2666*4882a593Smuzhiyun break;
2667*4882a593Smuzhiyun default:
2668*4882a593Smuzhiyun return;
2669*4882a593Smuzhiyun }
2670*4882a593Smuzhiyun
2671*4882a593Smuzhiyun /* TMDS software reset */
2672*4882a593Smuzhiyun hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
2673*4882a593Smuzhiyun
2674*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
2675*4882a593Smuzhiyun for (i = 0; i < count; i++)
2676*4882a593Smuzhiyun hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
2677*4882a593Smuzhiyun }
2678*4882a593Smuzhiyun
hdmi_disable_overflow_interrupts(struct dw_hdmi * hdmi)2679*4882a593Smuzhiyun static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
2680*4882a593Smuzhiyun {
2681*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
2682*4882a593Smuzhiyun HDMI_IH_MUTE_FC_STAT2);
2683*4882a593Smuzhiyun }
2684*4882a593Smuzhiyun
dw_hdmi_force_output_pattern(struct dw_hdmi * hdmi,const struct drm_display_mode * mode)2685*4882a593Smuzhiyun static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, const struct drm_display_mode *mode)
2686*4882a593Smuzhiyun {
2687*4882a593Smuzhiyun /* force output black */
2688*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
2689*4882a593Smuzhiyun enum hdmi_quantization_range rgb_quant_range = drm_default_rgb_quant_range(mode);
2690*4882a593Smuzhiyun
2691*4882a593Smuzhiyun if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_FULL) {
2692*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS2); /*R*/
2693*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS1); /*G*/
2694*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS0); /*B*/
2695*4882a593Smuzhiyun } else if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) {
2696*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS2); /*R*/
2697*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*G*/
2698*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS0); /*B*/
2699*4882a593Smuzhiyun } else if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_DEFAULT) {
2700*4882a593Smuzhiyun if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_FULL) {
2701*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS2); /*R*/
2702*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS1); /*G*/
2703*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS0); /*B*/
2704*4882a593Smuzhiyun } else if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) {
2705*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS2); /*R*/
2706*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*G*/
2707*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS0); /*B*/
2708*4882a593Smuzhiyun }
2709*4882a593Smuzhiyun }
2710*4882a593Smuzhiyun } else {
2711*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x80, HDMI_FC_DBGTMDS2); /*Cr*/
2712*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*Y*/
2713*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x80, HDMI_FC_DBGTMDS0); /*Cb*/
2714*4882a593Smuzhiyun }
2715*4882a593Smuzhiyun }
2716*4882a593Smuzhiyun
dw_hdmi_setup(struct dw_hdmi * hdmi,const struct drm_connector * connector,const struct drm_display_mode * mode)2717*4882a593Smuzhiyun static int dw_hdmi_setup(struct dw_hdmi *hdmi,
2718*4882a593Smuzhiyun const struct drm_connector *connector,
2719*4882a593Smuzhiyun const struct drm_display_mode *mode)
2720*4882a593Smuzhiyun {
2721*4882a593Smuzhiyun int ret;
2722*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
2723*4882a593Smuzhiyun
2724*4882a593Smuzhiyun hdmi_disable_overflow_interrupts(hdmi);
2725*4882a593Smuzhiyun
2726*4882a593Smuzhiyun hdmi->vic = drm_match_cea_mode(mode);
2727*4882a593Smuzhiyun
2728*4882a593Smuzhiyun if (!hdmi->vic) {
2729*4882a593Smuzhiyun dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
2730*4882a593Smuzhiyun } else {
2731*4882a593Smuzhiyun dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
2732*4882a593Smuzhiyun }
2733*4882a593Smuzhiyun
2734*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_out_encoding)
2735*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_encoding =
2736*4882a593Smuzhiyun hdmi->plat_data->get_enc_out_encoding(data);
2737*4882a593Smuzhiyun else if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
2738*4882a593Smuzhiyun (hdmi->vic == 21) || (hdmi->vic == 22) ||
2739*4882a593Smuzhiyun (hdmi->vic == 2) || (hdmi->vic == 3) ||
2740*4882a593Smuzhiyun (hdmi->vic == 17) || (hdmi->vic == 18))
2741*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
2742*4882a593Smuzhiyun else
2743*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
2744*4882a593Smuzhiyun
2745*4882a593Smuzhiyun if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
2746*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
2747*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
2748*4882a593Smuzhiyun } else {
2749*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
2750*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
2751*4882a593Smuzhiyun }
2752*4882a593Smuzhiyun /* TOFIX: Get input format from plat data or fallback to RGB888 */
2753*4882a593Smuzhiyun if (hdmi->plat_data->get_input_bus_format)
2754*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
2755*4882a593Smuzhiyun hdmi->plat_data->get_input_bus_format(data);
2756*4882a593Smuzhiyun else if (hdmi->plat_data->input_bus_format)
2757*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
2758*4882a593Smuzhiyun hdmi->plat_data->input_bus_format;
2759*4882a593Smuzhiyun else
2760*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
2761*4882a593Smuzhiyun MEDIA_BUS_FMT_RGB888_1X24;
2762*4882a593Smuzhiyun
2763*4882a593Smuzhiyun /* TOFIX: Default to RGB888 output format */
2764*4882a593Smuzhiyun if (hdmi->plat_data->get_output_bus_format)
2765*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
2766*4882a593Smuzhiyun hdmi->plat_data->get_output_bus_format(data);
2767*4882a593Smuzhiyun else
2768*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
2769*4882a593Smuzhiyun MEDIA_BUS_FMT_RGB888_1X24;
2770*4882a593Smuzhiyun
2771*4882a593Smuzhiyun if (hdmi->plat_data->set_prev_bus_format)
2772*4882a593Smuzhiyun hdmi->plat_data->set_prev_bus_format(data, hdmi->hdmi_data.enc_out_bus_format);
2773*4882a593Smuzhiyun
2774*4882a593Smuzhiyun /* TOFIX: Get input encoding from plat data or fallback to none */
2775*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_in_encoding)
2776*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_encoding =
2777*4882a593Smuzhiyun hdmi->plat_data->get_enc_in_encoding(data);
2778*4882a593Smuzhiyun else if (hdmi->plat_data->input_bus_encoding)
2779*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_encoding =
2780*4882a593Smuzhiyun hdmi->plat_data->input_bus_encoding;
2781*4882a593Smuzhiyun else
2782*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
2783*4882a593Smuzhiyun
2784*4882a593Smuzhiyun
2785*4882a593Smuzhiyun if (hdmi->plat_data->get_quant_range)
2786*4882a593Smuzhiyun hdmi->hdmi_data.quant_range =
2787*4882a593Smuzhiyun hdmi->plat_data->get_quant_range(data);
2788*4882a593Smuzhiyun
2789*4882a593Smuzhiyun hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi &&
2790*4882a593Smuzhiyun drm_default_rgb_quant_range(mode) ==
2791*4882a593Smuzhiyun HDMI_QUANTIZATION_RANGE_LIMITED;
2792*4882a593Smuzhiyun
2793*4882a593Smuzhiyun if (!hdmi->sink_is_hdmi)
2794*4882a593Smuzhiyun hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL;
2795*4882a593Smuzhiyun
2796*4882a593Smuzhiyun /*
2797*4882a593Smuzhiyun * According to the dw-hdmi specification 6.4.2
2798*4882a593Smuzhiyun * vp_pr_cd[3:0]:
2799*4882a593Smuzhiyun * 0000b: No pixel repetition (pixel sent only once)
2800*4882a593Smuzhiyun * 0001b: Pixel sent two times (pixel repeated once)
2801*4882a593Smuzhiyun */
2802*4882a593Smuzhiyun hdmi->hdmi_data.pix_repet_factor =
2803*4882a593Smuzhiyun (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
2804*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
2805*4882a593Smuzhiyun
2806*4882a593Smuzhiyun dw_hdmi_force_output_pattern(hdmi, mode);
2807*4882a593Smuzhiyun
2808*4882a593Smuzhiyun /* HDMI Initialization Step B.1 */
2809*4882a593Smuzhiyun hdmi_av_composer(hdmi, &connector->display_info, mode);
2810*4882a593Smuzhiyun
2811*4882a593Smuzhiyun /* HDMI Initialization Step B.3 */
2812*4882a593Smuzhiyun dw_hdmi_enable_video_path(hdmi);
2813*4882a593Smuzhiyun
2814*4882a593Smuzhiyun if (hdmi->sink_has_audio) {
2815*4882a593Smuzhiyun dev_dbg(hdmi->dev, "sink has audio support\n");
2816*4882a593Smuzhiyun
2817*4882a593Smuzhiyun /* HDMI Initialization Step E - Configure audio */
2818*4882a593Smuzhiyun hdmi_clk_regenerator_update_pixel_clock(hdmi);
2819*4882a593Smuzhiyun hdmi_enable_audio_clk(hdmi, hdmi->audio_enable);
2820*4882a593Smuzhiyun }
2821*4882a593Smuzhiyun
2822*4882a593Smuzhiyun /* not for DVI mode */
2823*4882a593Smuzhiyun if (hdmi->sink_is_hdmi) {
2824*4882a593Smuzhiyun dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
2825*4882a593Smuzhiyun
2826*4882a593Smuzhiyun /* HDMI Initialization Step F - Configure AVI InfoFrame */
2827*4882a593Smuzhiyun hdmi_config_AVI(hdmi, connector, mode);
2828*4882a593Smuzhiyun hdmi_config_vendor_specific_infoframe(hdmi, connector, mode);
2829*4882a593Smuzhiyun hdmi_config_drm_infoframe(hdmi, connector);
2830*4882a593Smuzhiyun } else {
2831*4882a593Smuzhiyun dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
2832*4882a593Smuzhiyun }
2833*4882a593Smuzhiyun
2834*4882a593Smuzhiyun hdmi_video_packetize(hdmi);
2835*4882a593Smuzhiyun hdmi_video_csc(hdmi);
2836*4882a593Smuzhiyun hdmi_video_sample(hdmi);
2837*4882a593Smuzhiyun hdmi_tx_hdcp_config(hdmi, mode);
2838*4882a593Smuzhiyun
2839*4882a593Smuzhiyun /* HDMI Enable phy output */
2840*4882a593Smuzhiyun if (!hdmi->phy.enabled ||
2841*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.previous_pixelclock !=
2842*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelclock ||
2843*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.previous_tmdsclock !=
2844*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mtmdsclock) {
2845*4882a593Smuzhiyun ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
2846*4882a593Smuzhiyun &connector->display_info,
2847*4882a593Smuzhiyun &hdmi->previous_mode);
2848*4882a593Smuzhiyun if (ret)
2849*4882a593Smuzhiyun return ret;
2850*4882a593Smuzhiyun hdmi->phy.enabled = true;
2851*4882a593Smuzhiyun }
2852*4882a593Smuzhiyun
2853*4882a593Smuzhiyun dw_hdmi_clear_overflow(hdmi);
2854*4882a593Smuzhiyun
2855*4882a593Smuzhiyun /*
2856*4882a593Smuzhiyun * konka tv should switch pattern after set to yuv420 10bit or
2857*4882a593Smuzhiyun * the TV might not recognize the signal.
2858*4882a593Smuzhiyun */
2859*4882a593Smuzhiyun if (!hdmi->update) {
2860*4882a593Smuzhiyun hdmi_writeb(hdmi, 1, HDMI_FC_DBGFORCE);
2861*4882a593Smuzhiyun msleep(50);
2862*4882a593Smuzhiyun hdmi_writeb(hdmi, 0, HDMI_FC_DBGFORCE);
2863*4882a593Smuzhiyun }
2864*4882a593Smuzhiyun
2865*4882a593Smuzhiyun return 0;
2866*4882a593Smuzhiyun }
2867*4882a593Smuzhiyun
initialize_hdmi_ih_mutes(struct dw_hdmi * hdmi)2868*4882a593Smuzhiyun static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
2869*4882a593Smuzhiyun {
2870*4882a593Smuzhiyun u8 ih_mute;
2871*4882a593Smuzhiyun
2872*4882a593Smuzhiyun /*
2873*4882a593Smuzhiyun * Boot up defaults are:
2874*4882a593Smuzhiyun * HDMI_IH_MUTE = 0x03 (disabled)
2875*4882a593Smuzhiyun * HDMI_IH_MUTE_* = 0x00 (enabled)
2876*4882a593Smuzhiyun *
2877*4882a593Smuzhiyun * Disable top level interrupt bits in HDMI block
2878*4882a593Smuzhiyun */
2879*4882a593Smuzhiyun ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
2880*4882a593Smuzhiyun HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
2881*4882a593Smuzhiyun HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
2882*4882a593Smuzhiyun
2883*4882a593Smuzhiyun hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
2884*4882a593Smuzhiyun
2885*4882a593Smuzhiyun /* by default mask all interrupts */
2886*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
2887*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
2888*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
2889*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
2890*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
2891*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
2892*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
2893*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
2894*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
2895*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
2896*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
2897*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
2898*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
2899*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
2900*4882a593Smuzhiyun
2901*4882a593Smuzhiyun /* Disable interrupts in the IH_MUTE_* registers */
2902*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
2903*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
2904*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
2905*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
2906*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
2907*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
2908*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
2909*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
2910*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
2911*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
2912*4882a593Smuzhiyun
2913*4882a593Smuzhiyun /* Enable top level interrupt bits in HDMI block */
2914*4882a593Smuzhiyun ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
2915*4882a593Smuzhiyun HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
2916*4882a593Smuzhiyun hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
2917*4882a593Smuzhiyun }
2918*4882a593Smuzhiyun
dw_hdmi_poweron(struct dw_hdmi * hdmi)2919*4882a593Smuzhiyun static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
2920*4882a593Smuzhiyun {
2921*4882a593Smuzhiyun hdmi->bridge_is_on = true;
2922*4882a593Smuzhiyun
2923*4882a593Smuzhiyun /*
2924*4882a593Smuzhiyun * The curr_conn field is guaranteed to be valid here, as this function
2925*4882a593Smuzhiyun * is only be called when !hdmi->disabled.
2926*4882a593Smuzhiyun */
2927*4882a593Smuzhiyun dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
2928*4882a593Smuzhiyun }
2929*4882a593Smuzhiyun
dw_hdmi_poweroff(struct dw_hdmi * hdmi)2930*4882a593Smuzhiyun static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
2931*4882a593Smuzhiyun {
2932*4882a593Smuzhiyun if (hdmi->phy.enabled) {
2933*4882a593Smuzhiyun hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
2934*4882a593Smuzhiyun hdmi->phy.enabled = false;
2935*4882a593Smuzhiyun }
2936*4882a593Smuzhiyun
2937*4882a593Smuzhiyun if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
2938*4882a593Smuzhiyun hdmi->hdcp->hdcp_stop(hdmi->hdcp);
2939*4882a593Smuzhiyun hdmi->bridge_is_on = false;
2940*4882a593Smuzhiyun }
2941*4882a593Smuzhiyun
dw_hdmi_update_power(struct dw_hdmi * hdmi)2942*4882a593Smuzhiyun static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
2943*4882a593Smuzhiyun {
2944*4882a593Smuzhiyun int force = hdmi->force;
2945*4882a593Smuzhiyun
2946*4882a593Smuzhiyun if (hdmi->disabled) {
2947*4882a593Smuzhiyun force = DRM_FORCE_OFF;
2948*4882a593Smuzhiyun } else if (force == DRM_FORCE_UNSPECIFIED) {
2949*4882a593Smuzhiyun if (hdmi->rxsense)
2950*4882a593Smuzhiyun force = DRM_FORCE_ON;
2951*4882a593Smuzhiyun else
2952*4882a593Smuzhiyun force = DRM_FORCE_OFF;
2953*4882a593Smuzhiyun }
2954*4882a593Smuzhiyun
2955*4882a593Smuzhiyun if (force == DRM_FORCE_OFF) {
2956*4882a593Smuzhiyun if (hdmi->initialized) {
2957*4882a593Smuzhiyun hdmi->initialized = false;
2958*4882a593Smuzhiyun hdmi->disabled = true;
2959*4882a593Smuzhiyun hdmi->logo_plug_out = true;
2960*4882a593Smuzhiyun }
2961*4882a593Smuzhiyun if (hdmi->bridge_is_on)
2962*4882a593Smuzhiyun dw_hdmi_poweroff(hdmi);
2963*4882a593Smuzhiyun } else {
2964*4882a593Smuzhiyun if (!hdmi->bridge_is_on)
2965*4882a593Smuzhiyun dw_hdmi_poweron(hdmi);
2966*4882a593Smuzhiyun }
2967*4882a593Smuzhiyun }
2968*4882a593Smuzhiyun
2969*4882a593Smuzhiyun /*
2970*4882a593Smuzhiyun * Adjust the detection of RXSENSE according to whether we have a forced
2971*4882a593Smuzhiyun * connection mode enabled, or whether we have been disabled. There is
2972*4882a593Smuzhiyun * no point processing RXSENSE interrupts if we have a forced connection
2973*4882a593Smuzhiyun * state, or DRM has us disabled.
2974*4882a593Smuzhiyun *
2975*4882a593Smuzhiyun * We also disable rxsense interrupts when we think we're disconnected
2976*4882a593Smuzhiyun * to avoid floating TDMS signals giving false rxsense interrupts.
2977*4882a593Smuzhiyun *
2978*4882a593Smuzhiyun * Note: we still need to listen for HPD interrupts even when DRM has us
2979*4882a593Smuzhiyun * disabled so that we can detect a connect event.
2980*4882a593Smuzhiyun */
dw_hdmi_update_phy_mask(struct dw_hdmi * hdmi)2981*4882a593Smuzhiyun static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
2982*4882a593Smuzhiyun {
2983*4882a593Smuzhiyun if (hdmi->phy.ops->update_hpd)
2984*4882a593Smuzhiyun hdmi->phy.ops->update_hpd(hdmi, hdmi->phy.data,
2985*4882a593Smuzhiyun hdmi->force, hdmi->disabled,
2986*4882a593Smuzhiyun hdmi->rxsense);
2987*4882a593Smuzhiyun }
2988*4882a593Smuzhiyun
dw_hdmi_detect(struct dw_hdmi * hdmi)2989*4882a593Smuzhiyun static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
2990*4882a593Smuzhiyun {
2991*4882a593Smuzhiyun enum drm_connector_status result;
2992*4882a593Smuzhiyun
2993*4882a593Smuzhiyun if (!hdmi->force_logo) {
2994*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
2995*4882a593Smuzhiyun hdmi->force = DRM_FORCE_UNSPECIFIED;
2996*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
2997*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
2998*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
2999*4882a593Smuzhiyun }
3000*4882a593Smuzhiyun
3001*4882a593Smuzhiyun result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
3002*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
3003*4882a593Smuzhiyun if (result != hdmi->last_connector_result) {
3004*4882a593Smuzhiyun dev_dbg(hdmi->dev, "read_hpd result: %d", result);
3005*4882a593Smuzhiyun handle_plugged_change(hdmi,
3006*4882a593Smuzhiyun result == connector_status_connected);
3007*4882a593Smuzhiyun hdmi->last_connector_result = result;
3008*4882a593Smuzhiyun }
3009*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
3010*4882a593Smuzhiyun
3011*4882a593Smuzhiyun if (result == connector_status_connected)
3012*4882a593Smuzhiyun extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
3013*4882a593Smuzhiyun else
3014*4882a593Smuzhiyun extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
3015*4882a593Smuzhiyun
3016*4882a593Smuzhiyun return result;
3017*4882a593Smuzhiyun }
3018*4882a593Smuzhiyun
dw_hdmi_get_edid(struct dw_hdmi * hdmi,struct drm_connector * connector)3019*4882a593Smuzhiyun static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
3020*4882a593Smuzhiyun struct drm_connector *connector)
3021*4882a593Smuzhiyun {
3022*4882a593Smuzhiyun struct edid *edid;
3023*4882a593Smuzhiyun
3024*4882a593Smuzhiyun if (!hdmi->ddc)
3025*4882a593Smuzhiyun return NULL;
3026*4882a593Smuzhiyun
3027*4882a593Smuzhiyun edid = drm_get_edid(connector, hdmi->ddc);
3028*4882a593Smuzhiyun if (!edid) {
3029*4882a593Smuzhiyun dev_dbg(hdmi->dev, "failed to get edid\n");
3030*4882a593Smuzhiyun return NULL;
3031*4882a593Smuzhiyun }
3032*4882a593Smuzhiyun
3033*4882a593Smuzhiyun dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
3034*4882a593Smuzhiyun edid->width_cm, edid->height_cm);
3035*4882a593Smuzhiyun
3036*4882a593Smuzhiyun hdmi->support_hdmi = drm_detect_hdmi_monitor(edid);
3037*4882a593Smuzhiyun hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
3038*4882a593Smuzhiyun
3039*4882a593Smuzhiyun return edid;
3040*4882a593Smuzhiyun }
3041*4882a593Smuzhiyun
3042*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
3043*4882a593Smuzhiyun * DRM Connector Operations
3044*4882a593Smuzhiyun */
3045*4882a593Smuzhiyun
3046*4882a593Smuzhiyun static enum drm_connector_status
dw_hdmi_connector_detect(struct drm_connector * connector,bool force)3047*4882a593Smuzhiyun dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
3048*4882a593Smuzhiyun {
3049*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3050*4882a593Smuzhiyun connector);
3051*4882a593Smuzhiyun return dw_hdmi_detect(hdmi);
3052*4882a593Smuzhiyun }
3053*4882a593Smuzhiyun
3054*4882a593Smuzhiyun static int
dw_hdmi_update_hdr_property(struct drm_connector * connector)3055*4882a593Smuzhiyun dw_hdmi_update_hdr_property(struct drm_connector *connector)
3056*4882a593Smuzhiyun {
3057*4882a593Smuzhiyun struct drm_device *dev = connector->dev;
3058*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3059*4882a593Smuzhiyun connector);
3060*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3061*4882a593Smuzhiyun const struct hdr_static_metadata *metadata =
3062*4882a593Smuzhiyun &connector->hdr_sink_metadata.hdmi_type1;
3063*4882a593Smuzhiyun size_t size = sizeof(*metadata);
3064*4882a593Smuzhiyun struct drm_property *property;
3065*4882a593Smuzhiyun struct drm_property_blob *blob;
3066*4882a593Smuzhiyun int ret;
3067*4882a593Smuzhiyun
3068*4882a593Smuzhiyun if (hdmi->plat_data->get_hdr_property)
3069*4882a593Smuzhiyun property = hdmi->plat_data->get_hdr_property(data);
3070*4882a593Smuzhiyun else
3071*4882a593Smuzhiyun return -EINVAL;
3072*4882a593Smuzhiyun
3073*4882a593Smuzhiyun if (hdmi->plat_data->get_hdr_blob)
3074*4882a593Smuzhiyun blob = hdmi->plat_data->get_hdr_blob(data);
3075*4882a593Smuzhiyun else
3076*4882a593Smuzhiyun return -EINVAL;
3077*4882a593Smuzhiyun
3078*4882a593Smuzhiyun ret = drm_property_replace_global_blob(dev, &blob, size, metadata,
3079*4882a593Smuzhiyun &connector->base, property);
3080*4882a593Smuzhiyun return ret;
3081*4882a593Smuzhiyun }
3082*4882a593Smuzhiyun
dw_hdmi_connector_get_modes(struct drm_connector * connector)3083*4882a593Smuzhiyun static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
3084*4882a593Smuzhiyun {
3085*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3086*4882a593Smuzhiyun connector);
3087*4882a593Smuzhiyun struct hdr_static_metadata *metedata =
3088*4882a593Smuzhiyun &connector->hdr_sink_metadata.hdmi_type1;
3089*4882a593Smuzhiyun struct edid *edid;
3090*4882a593Smuzhiyun struct drm_display_mode *mode;
3091*4882a593Smuzhiyun struct drm_display_info *info = &connector->display_info;
3092*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3093*4882a593Smuzhiyun int i, ret = 0;
3094*4882a593Smuzhiyun
3095*4882a593Smuzhiyun memset(metedata, 0, sizeof(*metedata));
3096*4882a593Smuzhiyun edid = dw_hdmi_get_edid(hdmi, connector);
3097*4882a593Smuzhiyun if (edid) {
3098*4882a593Smuzhiyun int vic = 0;
3099*4882a593Smuzhiyun
3100*4882a593Smuzhiyun dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
3101*4882a593Smuzhiyun edid->width_cm, edid->height_cm);
3102*4882a593Smuzhiyun drm_connector_update_edid_property(connector, edid);
3103*4882a593Smuzhiyun cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
3104*4882a593Smuzhiyun ret = drm_add_edid_modes(connector, edid);
3105*4882a593Smuzhiyun if (hdmi->plat_data->get_color_changed)
3106*4882a593Smuzhiyun hdmi->plat_data->get_yuv422_format(connector, edid);
3107*4882a593Smuzhiyun if (hdmi->plat_data->get_colorimetry)
3108*4882a593Smuzhiyun hdmi->plat_data->get_colorimetry(data, edid);
3109*4882a593Smuzhiyun
3110*4882a593Smuzhiyun list_for_each_entry(mode, &connector->probed_modes, head) {
3111*4882a593Smuzhiyun vic = drm_match_cea_mode(mode);
3112*4882a593Smuzhiyun
3113*4882a593Smuzhiyun if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_NONE) {
3114*4882a593Smuzhiyun if (vic >= 93 && vic <= 95)
3115*4882a593Smuzhiyun mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
3116*4882a593Smuzhiyun else if (vic == 98)
3117*4882a593Smuzhiyun mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135;
3118*4882a593Smuzhiyun }
3119*4882a593Smuzhiyun }
3120*4882a593Smuzhiyun
3121*4882a593Smuzhiyun kfree(edid);
3122*4882a593Smuzhiyun } else {
3123*4882a593Smuzhiyun hdmi->support_hdmi = true;
3124*4882a593Smuzhiyun hdmi->sink_has_audio = true;
3125*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) {
3126*4882a593Smuzhiyun const struct drm_display_mode *ptr =
3127*4882a593Smuzhiyun &dw_hdmi_default_modes[i];
3128*4882a593Smuzhiyun
3129*4882a593Smuzhiyun mode = drm_mode_duplicate(connector->dev, ptr);
3130*4882a593Smuzhiyun if (mode) {
3131*4882a593Smuzhiyun if (!i)
3132*4882a593Smuzhiyun mode->type = DRM_MODE_TYPE_PREFERRED;
3133*4882a593Smuzhiyun drm_mode_probed_add(connector, mode);
3134*4882a593Smuzhiyun ret++;
3135*4882a593Smuzhiyun }
3136*4882a593Smuzhiyun }
3137*4882a593Smuzhiyun info->edid_hdmi_dc_modes = 0;
3138*4882a593Smuzhiyun info->hdmi.y420_dc_modes = 0;
3139*4882a593Smuzhiyun info->color_formats = 0;
3140*4882a593Smuzhiyun
3141*4882a593Smuzhiyun dev_info(hdmi->dev, "failed to get edid\n");
3142*4882a593Smuzhiyun }
3143*4882a593Smuzhiyun dw_hdmi_update_hdr_property(connector);
3144*4882a593Smuzhiyun dw_hdmi_check_output_type_changed(hdmi);
3145*4882a593Smuzhiyun
3146*4882a593Smuzhiyun return ret;
3147*4882a593Smuzhiyun }
3148*4882a593Smuzhiyun
3149*4882a593Smuzhiyun static struct drm_encoder *
dw_hdmi_connector_best_encoder(struct drm_connector * connector)3150*4882a593Smuzhiyun dw_hdmi_connector_best_encoder(struct drm_connector *connector)
3151*4882a593Smuzhiyun {
3152*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3153*4882a593Smuzhiyun connector);
3154*4882a593Smuzhiyun
3155*4882a593Smuzhiyun return hdmi->bridge.encoder;
3156*4882a593Smuzhiyun }
3157*4882a593Smuzhiyun
dw_hdmi_color_changed(struct drm_connector * connector)3158*4882a593Smuzhiyun static bool dw_hdmi_color_changed(struct drm_connector *connector)
3159*4882a593Smuzhiyun {
3160*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3161*4882a593Smuzhiyun connector);
3162*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3163*4882a593Smuzhiyun bool ret = false;
3164*4882a593Smuzhiyun
3165*4882a593Smuzhiyun if (hdmi->plat_data->get_color_changed)
3166*4882a593Smuzhiyun ret = hdmi->plat_data->get_color_changed(data);
3167*4882a593Smuzhiyun
3168*4882a593Smuzhiyun return ret;
3169*4882a593Smuzhiyun }
3170*4882a593Smuzhiyun
hdr_metadata_equal(struct dw_hdmi * hdmi,const struct drm_connector_state * old_state,const struct drm_connector_state * new_state)3171*4882a593Smuzhiyun static bool hdr_metadata_equal(struct dw_hdmi *hdmi, const struct drm_connector_state *old_state,
3172*4882a593Smuzhiyun const struct drm_connector_state *new_state)
3173*4882a593Smuzhiyun {
3174*4882a593Smuzhiyun struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
3175*4882a593Smuzhiyun struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
3176*4882a593Smuzhiyun int i, ret;
3177*4882a593Smuzhiyun u8 *data;
3178*4882a593Smuzhiyun
3179*4882a593Smuzhiyun hdmi->hdr2sdr = false;
3180*4882a593Smuzhiyun
3181*4882a593Smuzhiyun if (!old_blob && !new_blob)
3182*4882a593Smuzhiyun return true;
3183*4882a593Smuzhiyun
3184*4882a593Smuzhiyun if (!old_blob) {
3185*4882a593Smuzhiyun data = (u8 *)new_blob->data;
3186*4882a593Smuzhiyun
3187*4882a593Smuzhiyun for (i = 0; i < new_blob->length; i++)
3188*4882a593Smuzhiyun if (data[i])
3189*4882a593Smuzhiyun return false;
3190*4882a593Smuzhiyun
3191*4882a593Smuzhiyun return true;
3192*4882a593Smuzhiyun }
3193*4882a593Smuzhiyun
3194*4882a593Smuzhiyun if (!new_blob) {
3195*4882a593Smuzhiyun data = (u8 *)old_blob->data;
3196*4882a593Smuzhiyun
3197*4882a593Smuzhiyun for (i = 0; i < old_blob->length; i++)
3198*4882a593Smuzhiyun if (data[i])
3199*4882a593Smuzhiyun return false;
3200*4882a593Smuzhiyun
3201*4882a593Smuzhiyun return true;
3202*4882a593Smuzhiyun }
3203*4882a593Smuzhiyun
3204*4882a593Smuzhiyun if (old_blob->length != new_blob->length)
3205*4882a593Smuzhiyun return false;
3206*4882a593Smuzhiyun
3207*4882a593Smuzhiyun ret = !memcmp(old_blob->data, new_blob->data, old_blob->length);
3208*4882a593Smuzhiyun
3209*4882a593Smuzhiyun if (!ret && new_blob) {
3210*4882a593Smuzhiyun data = (u8 *)new_blob->data;
3211*4882a593Smuzhiyun
3212*4882a593Smuzhiyun for (i = 0; i < new_blob->length; i++)
3213*4882a593Smuzhiyun if (data[i])
3214*4882a593Smuzhiyun break;
3215*4882a593Smuzhiyun
3216*4882a593Smuzhiyun if (i == new_blob->length)
3217*4882a593Smuzhiyun hdmi->hdr2sdr = true;
3218*4882a593Smuzhiyun }
3219*4882a593Smuzhiyun
3220*4882a593Smuzhiyun return ret;
3221*4882a593Smuzhiyun }
3222*4882a593Smuzhiyun
check_hdr_color_change(struct drm_connector_state * old_state,struct drm_connector_state * new_state,struct dw_hdmi * hdmi)3223*4882a593Smuzhiyun static bool check_hdr_color_change(struct drm_connector_state *old_state,
3224*4882a593Smuzhiyun struct drm_connector_state *new_state,
3225*4882a593Smuzhiyun struct dw_hdmi *hdmi)
3226*4882a593Smuzhiyun {
3227*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3228*4882a593Smuzhiyun
3229*4882a593Smuzhiyun if (!hdr_metadata_equal(hdmi, old_state, new_state)) {
3230*4882a593Smuzhiyun hdmi->plat_data->check_hdr_color_change(new_state, data);
3231*4882a593Smuzhiyun return true;
3232*4882a593Smuzhiyun }
3233*4882a593Smuzhiyun
3234*4882a593Smuzhiyun return false;
3235*4882a593Smuzhiyun }
3236*4882a593Smuzhiyun
dw_hdmi_connector_atomic_check(struct drm_connector * connector,struct drm_atomic_state * state)3237*4882a593Smuzhiyun static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
3238*4882a593Smuzhiyun struct drm_atomic_state *state)
3239*4882a593Smuzhiyun {
3240*4882a593Smuzhiyun struct drm_connector_state *old_state =
3241*4882a593Smuzhiyun drm_atomic_get_old_connector_state(state, connector);
3242*4882a593Smuzhiyun struct drm_connector_state *new_state =
3243*4882a593Smuzhiyun drm_atomic_get_new_connector_state(state, connector);
3244*4882a593Smuzhiyun struct drm_crtc *crtc = new_state->crtc;
3245*4882a593Smuzhiyun struct drm_crtc_state *crtc_state;
3246*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3247*4882a593Smuzhiyun connector);
3248*4882a593Smuzhiyun struct drm_display_mode *mode = NULL;
3249*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3250*4882a593Smuzhiyun struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
3251*4882a593Smuzhiyun
3252*4882a593Smuzhiyun if (!crtc)
3253*4882a593Smuzhiyun return 0;
3254*4882a593Smuzhiyun
3255*4882a593Smuzhiyun crtc_state = drm_atomic_get_crtc_state(state, crtc);
3256*4882a593Smuzhiyun if (IS_ERR(crtc_state))
3257*4882a593Smuzhiyun return PTR_ERR(crtc_state);
3258*4882a593Smuzhiyun
3259*4882a593Smuzhiyun mode = &crtc_state->mode;
3260*4882a593Smuzhiyun
3261*4882a593Smuzhiyun /*
3262*4882a593Smuzhiyun * If HDMI is enabled in uboot, it's need to record
3263*4882a593Smuzhiyun * drm_display_mode and set phy status to enabled.
3264*4882a593Smuzhiyun */
3265*4882a593Smuzhiyun if (!vmode->mpixelclock) {
3266*4882a593Smuzhiyun u8 val;
3267*4882a593Smuzhiyun
3268*4882a593Smuzhiyun hdmi->curr_conn = connector;
3269*4882a593Smuzhiyun
3270*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_in_encoding)
3271*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_encoding =
3272*4882a593Smuzhiyun hdmi->plat_data->get_enc_in_encoding(data);
3273*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_out_encoding)
3274*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_encoding =
3275*4882a593Smuzhiyun hdmi->plat_data->get_enc_out_encoding(data);
3276*4882a593Smuzhiyun if (hdmi->plat_data->get_input_bus_format)
3277*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3278*4882a593Smuzhiyun hdmi->plat_data->get_input_bus_format(data);
3279*4882a593Smuzhiyun if (hdmi->plat_data->get_output_bus_format)
3280*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
3281*4882a593Smuzhiyun hdmi->plat_data->get_output_bus_format(data);
3282*4882a593Smuzhiyun
3283*4882a593Smuzhiyun memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
3284*4882a593Smuzhiyun vmode->mpixelclock = mode->crtc_clock * 1000;
3285*4882a593Smuzhiyun vmode->previous_pixelclock = mode->clock * 1000;
3286*4882a593Smuzhiyun vmode->previous_tmdsclock = mode->clock * 1000;
3287*4882a593Smuzhiyun vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi,
3288*4882a593Smuzhiyun vmode->mpixelclock);
3289*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
3290*4882a593Smuzhiyun vmode->mtmdsclock /= 2;
3291*4882a593Smuzhiyun
3292*4882a593Smuzhiyun dw_hdmi_force_output_pattern(hdmi, mode);
3293*4882a593Smuzhiyun drm_scdc_readb(hdmi->ddc, SCDC_TMDS_CONFIG, &val);
3294*4882a593Smuzhiyun
3295*4882a593Smuzhiyun /* if plug out before hdmi bind, reset hdmi */
3296*4882a593Smuzhiyun if (vmode->mtmdsclock >= 340000000 && !(val & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40))
3297*4882a593Smuzhiyun hdmi->logo_plug_out = true;
3298*4882a593Smuzhiyun }
3299*4882a593Smuzhiyun
3300*4882a593Smuzhiyun if (check_hdr_color_change(old_state, new_state, hdmi) || hdmi->logo_plug_out ||
3301*4882a593Smuzhiyun dw_hdmi_color_changed(connector)) {
3302*4882a593Smuzhiyun u32 mtmdsclk;
3303*4882a593Smuzhiyun
3304*4882a593Smuzhiyun if (hdmi->plat_data->update_color_format)
3305*4882a593Smuzhiyun hdmi->plat_data->update_color_format(new_state, data);
3306*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_in_encoding)
3307*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_encoding =
3308*4882a593Smuzhiyun hdmi->plat_data->get_enc_in_encoding(data);
3309*4882a593Smuzhiyun if (hdmi->plat_data->get_enc_out_encoding)
3310*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_encoding =
3311*4882a593Smuzhiyun hdmi->plat_data->get_enc_out_encoding(data);
3312*4882a593Smuzhiyun if (hdmi->plat_data->get_input_bus_format)
3313*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3314*4882a593Smuzhiyun hdmi->plat_data->get_input_bus_format(data);
3315*4882a593Smuzhiyun if (hdmi->plat_data->get_output_bus_format)
3316*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
3317*4882a593Smuzhiyun hdmi->plat_data->get_output_bus_format(data);
3318*4882a593Smuzhiyun
3319*4882a593Smuzhiyun mtmdsclk = hdmi_get_tmdsclock(hdmi, mode->clock);
3320*4882a593Smuzhiyun
3321*4882a593Smuzhiyun if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
3322*4882a593Smuzhiyun mtmdsclk /= 2;
3323*4882a593Smuzhiyun
3324*4882a593Smuzhiyun if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD))
3325*4882a593Smuzhiyun return 0;
3326*4882a593Smuzhiyun
3327*4882a593Smuzhiyun if (hdmi->hdmi_data.video_mode.mpixelclock == (mode->clock * 1000) &&
3328*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mtmdsclock == (mtmdsclk * 1000) &&
3329*4882a593Smuzhiyun !hdmi->logo_plug_out && !hdmi->disabled) {
3330*4882a593Smuzhiyun hdmi->update = true;
3331*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
3332*4882a593Smuzhiyun mdelay(180);
3333*4882a593Smuzhiyun handle_plugged_change(hdmi, false);
3334*4882a593Smuzhiyun } else {
3335*4882a593Smuzhiyun hdmi->update = false;
3336*4882a593Smuzhiyun crtc_state->mode_changed = true;
3337*4882a593Smuzhiyun hdmi->logo_plug_out = false;
3338*4882a593Smuzhiyun }
3339*4882a593Smuzhiyun }
3340*4882a593Smuzhiyun
3341*4882a593Smuzhiyun return 0;
3342*4882a593Smuzhiyun }
3343*4882a593Smuzhiyun
3344*4882a593Smuzhiyun static int
dw_hdmi_atomic_connector_set_property(struct drm_connector * connector,struct drm_connector_state * state,struct drm_property * property,uint64_t val)3345*4882a593Smuzhiyun dw_hdmi_atomic_connector_set_property(struct drm_connector *connector,
3346*4882a593Smuzhiyun struct drm_connector_state *state,
3347*4882a593Smuzhiyun struct drm_property *property,
3348*4882a593Smuzhiyun uint64_t val)
3349*4882a593Smuzhiyun {
3350*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3351*4882a593Smuzhiyun connector);
3352*4882a593Smuzhiyun const struct dw_hdmi_property_ops *ops =
3353*4882a593Smuzhiyun hdmi->plat_data->property_ops;
3354*4882a593Smuzhiyun
3355*4882a593Smuzhiyun if (ops && ops->set_property)
3356*4882a593Smuzhiyun return ops->set_property(connector, state, property,
3357*4882a593Smuzhiyun val, hdmi->plat_data->phy_data);
3358*4882a593Smuzhiyun else
3359*4882a593Smuzhiyun return -EINVAL;
3360*4882a593Smuzhiyun }
3361*4882a593Smuzhiyun
3362*4882a593Smuzhiyun static int
dw_hdmi_atomic_connector_get_property(struct drm_connector * connector,const struct drm_connector_state * state,struct drm_property * property,uint64_t * val)3363*4882a593Smuzhiyun dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
3364*4882a593Smuzhiyun const struct drm_connector_state *state,
3365*4882a593Smuzhiyun struct drm_property *property,
3366*4882a593Smuzhiyun uint64_t *val)
3367*4882a593Smuzhiyun {
3368*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3369*4882a593Smuzhiyun connector);
3370*4882a593Smuzhiyun const struct dw_hdmi_property_ops *ops =
3371*4882a593Smuzhiyun hdmi->plat_data->property_ops;
3372*4882a593Smuzhiyun
3373*4882a593Smuzhiyun if (ops && ops->get_property)
3374*4882a593Smuzhiyun return ops->get_property(connector, state, property,
3375*4882a593Smuzhiyun val, hdmi->plat_data->phy_data);
3376*4882a593Smuzhiyun else
3377*4882a593Smuzhiyun return -EINVAL;
3378*4882a593Smuzhiyun }
3379*4882a593Smuzhiyun
3380*4882a593Smuzhiyun static int
dw_hdmi_connector_set_property(struct drm_connector * connector,struct drm_property * property,uint64_t val)3381*4882a593Smuzhiyun dw_hdmi_connector_set_property(struct drm_connector *connector,
3382*4882a593Smuzhiyun struct drm_property *property, uint64_t val)
3383*4882a593Smuzhiyun {
3384*4882a593Smuzhiyun return dw_hdmi_atomic_connector_set_property(connector, NULL,
3385*4882a593Smuzhiyun property, val);
3386*4882a593Smuzhiyun }
3387*4882a593Smuzhiyun
dw_hdmi_connector_atomic_commit(struct drm_connector * connector,struct drm_connector_state * state)3388*4882a593Smuzhiyun static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector,
3389*4882a593Smuzhiyun struct drm_connector_state *state)
3390*4882a593Smuzhiyun {
3391*4882a593Smuzhiyun struct dw_hdmi *hdmi =
3392*4882a593Smuzhiyun container_of(connector, struct dw_hdmi, connector);
3393*4882a593Smuzhiyun
3394*4882a593Smuzhiyun if (hdmi->update) {
3395*4882a593Smuzhiyun dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
3396*4882a593Smuzhiyun mdelay(50);
3397*4882a593Smuzhiyun handle_plugged_change(hdmi, true);
3398*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
3399*4882a593Smuzhiyun hdmi->update = false;
3400*4882a593Smuzhiyun }
3401*4882a593Smuzhiyun }
3402*4882a593Smuzhiyun
dw_hdmi_set_quant_range(struct dw_hdmi * hdmi)3403*4882a593Smuzhiyun void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi)
3404*4882a593Smuzhiyun {
3405*4882a593Smuzhiyun if (!hdmi->bridge_is_on)
3406*4882a593Smuzhiyun return;
3407*4882a593Smuzhiyun
3408*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
3409*4882a593Smuzhiyun dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
3410*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
3411*4882a593Smuzhiyun }
3412*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range);
3413*4882a593Smuzhiyun
dw_hdmi_set_output_type(struct dw_hdmi * hdmi,u64 val)3414*4882a593Smuzhiyun void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val)
3415*4882a593Smuzhiyun {
3416*4882a593Smuzhiyun hdmi->force_output = val;
3417*4882a593Smuzhiyun
3418*4882a593Smuzhiyun if (!dw_hdmi_check_output_type_changed(hdmi))
3419*4882a593Smuzhiyun return;
3420*4882a593Smuzhiyun
3421*4882a593Smuzhiyun if (!hdmi->bridge_is_on)
3422*4882a593Smuzhiyun return;
3423*4882a593Smuzhiyun
3424*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
3425*4882a593Smuzhiyun dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
3426*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
3427*4882a593Smuzhiyun }
3428*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type);
3429*4882a593Smuzhiyun
dw_hdmi_get_output_whether_hdmi(struct dw_hdmi * hdmi)3430*4882a593Smuzhiyun bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi)
3431*4882a593Smuzhiyun {
3432*4882a593Smuzhiyun return hdmi->sink_is_hdmi;
3433*4882a593Smuzhiyun }
3434*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi);
3435*4882a593Smuzhiyun
dw_hdmi_get_output_type_cap(struct dw_hdmi * hdmi)3436*4882a593Smuzhiyun int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi)
3437*4882a593Smuzhiyun {
3438*4882a593Smuzhiyun return hdmi->support_hdmi;
3439*4882a593Smuzhiyun }
3440*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap);
3441*4882a593Smuzhiyun
dw_hdmi_set_hpd_wake(struct dw_hdmi * hdmi)3442*4882a593Smuzhiyun void dw_hdmi_set_hpd_wake(struct dw_hdmi *hdmi)
3443*4882a593Smuzhiyun {
3444*4882a593Smuzhiyun if (!hdmi->cec)
3445*4882a593Smuzhiyun return;
3446*4882a593Smuzhiyun
3447*4882a593Smuzhiyun if (!hdmi->cec_ops)
3448*4882a593Smuzhiyun return;
3449*4882a593Smuzhiyun
3450*4882a593Smuzhiyun if (hdmi->cec_ops->hpd_wake_up)
3451*4882a593Smuzhiyun hdmi->cec_ops->hpd_wake_up(hdmi->cec);
3452*4882a593Smuzhiyun }
3453*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_hpd_wake);
3454*4882a593Smuzhiyun
dw_hdmi_connector_force(struct drm_connector * connector)3455*4882a593Smuzhiyun static void dw_hdmi_connector_force(struct drm_connector *connector)
3456*4882a593Smuzhiyun {
3457*4882a593Smuzhiyun struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
3458*4882a593Smuzhiyun connector);
3459*4882a593Smuzhiyun
3460*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
3461*4882a593Smuzhiyun
3462*4882a593Smuzhiyun if (hdmi->force != connector->force) {
3463*4882a593Smuzhiyun if (!hdmi->disabled && connector->force == DRM_FORCE_OFF)
3464*4882a593Smuzhiyun extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
3465*4882a593Smuzhiyun false);
3466*4882a593Smuzhiyun else if (hdmi->disabled && connector->force == DRM_FORCE_ON)
3467*4882a593Smuzhiyun extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
3468*4882a593Smuzhiyun true);
3469*4882a593Smuzhiyun }
3470*4882a593Smuzhiyun
3471*4882a593Smuzhiyun hdmi->force = connector->force;
3472*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
3473*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
3474*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
3475*4882a593Smuzhiyun }
3476*4882a593Smuzhiyun
3477*4882a593Smuzhiyun static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
3478*4882a593Smuzhiyun .fill_modes = drm_helper_probe_single_connector_modes,
3479*4882a593Smuzhiyun .detect = dw_hdmi_connector_detect,
3480*4882a593Smuzhiyun .destroy = drm_connector_cleanup,
3481*4882a593Smuzhiyun .force = dw_hdmi_connector_force,
3482*4882a593Smuzhiyun .reset = drm_atomic_helper_connector_reset,
3483*4882a593Smuzhiyun .set_property = dw_hdmi_connector_set_property,
3484*4882a593Smuzhiyun .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
3485*4882a593Smuzhiyun .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
3486*4882a593Smuzhiyun .atomic_set_property = dw_hdmi_atomic_connector_set_property,
3487*4882a593Smuzhiyun .atomic_get_property = dw_hdmi_atomic_connector_get_property,
3488*4882a593Smuzhiyun };
3489*4882a593Smuzhiyun
3490*4882a593Smuzhiyun static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
3491*4882a593Smuzhiyun .get_modes = dw_hdmi_connector_get_modes,
3492*4882a593Smuzhiyun .best_encoder = dw_hdmi_connector_best_encoder,
3493*4882a593Smuzhiyun .atomic_check = dw_hdmi_connector_atomic_check,
3494*4882a593Smuzhiyun .atomic_commit = dw_hdmi_connector_atomic_commit,
3495*4882a593Smuzhiyun };
3496*4882a593Smuzhiyun
dw_hdmi_attach_properties(struct dw_hdmi * hdmi)3497*4882a593Smuzhiyun static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi)
3498*4882a593Smuzhiyun {
3499*4882a593Smuzhiyun unsigned int color = MEDIA_BUS_FMT_RGB888_1X24;
3500*4882a593Smuzhiyun int video_mapping, colorspace;
3501*4882a593Smuzhiyun enum drm_connector_status connect_status =
3502*4882a593Smuzhiyun hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
3503*4882a593Smuzhiyun const struct dw_hdmi_property_ops *ops =
3504*4882a593Smuzhiyun hdmi->plat_data->property_ops;
3505*4882a593Smuzhiyun
3506*4882a593Smuzhiyun if (connect_status == connector_status_connected) {
3507*4882a593Smuzhiyun video_mapping = (hdmi_readb(hdmi, HDMI_TX_INVID0) &
3508*4882a593Smuzhiyun HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
3509*4882a593Smuzhiyun colorspace = (hdmi_readb(hdmi, HDMI_FC_AVICONF0) &
3510*4882a593Smuzhiyun HDMI_FC_AVICONF0_PIX_FMT_MASK);
3511*4882a593Smuzhiyun switch (video_mapping) {
3512*4882a593Smuzhiyun case 0x01:
3513*4882a593Smuzhiyun color = MEDIA_BUS_FMT_RGB888_1X24;
3514*4882a593Smuzhiyun break;
3515*4882a593Smuzhiyun case 0x03:
3516*4882a593Smuzhiyun color = MEDIA_BUS_FMT_RGB101010_1X30;
3517*4882a593Smuzhiyun break;
3518*4882a593Smuzhiyun case 0x09:
3519*4882a593Smuzhiyun if (colorspace == HDMI_COLORSPACE_YUV420)
3520*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
3521*4882a593Smuzhiyun else if (colorspace == HDMI_COLORSPACE_YUV422)
3522*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYVY8_1X16;
3523*4882a593Smuzhiyun else
3524*4882a593Smuzhiyun color = MEDIA_BUS_FMT_YUV8_1X24;
3525*4882a593Smuzhiyun break;
3526*4882a593Smuzhiyun case 0x0b:
3527*4882a593Smuzhiyun if (colorspace == HDMI_COLORSPACE_YUV420)
3528*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
3529*4882a593Smuzhiyun else if (colorspace == HDMI_COLORSPACE_YUV422)
3530*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYVY10_1X20;
3531*4882a593Smuzhiyun else
3532*4882a593Smuzhiyun color = MEDIA_BUS_FMT_YUV10_1X30;
3533*4882a593Smuzhiyun break;
3534*4882a593Smuzhiyun case 0x14:
3535*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYVY10_1X20;
3536*4882a593Smuzhiyun break;
3537*4882a593Smuzhiyun case 0x16:
3538*4882a593Smuzhiyun color = MEDIA_BUS_FMT_UYVY8_1X16;
3539*4882a593Smuzhiyun break;
3540*4882a593Smuzhiyun default:
3541*4882a593Smuzhiyun color = MEDIA_BUS_FMT_RGB888_1X24;
3542*4882a593Smuzhiyun dev_err(hdmi->dev, "unexpected mapping: 0x%x\n",
3543*4882a593Smuzhiyun video_mapping);
3544*4882a593Smuzhiyun }
3545*4882a593Smuzhiyun
3546*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format = color;
3547*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format = color;
3548*4882a593Smuzhiyun /*
3549*4882a593Smuzhiyun * input format will be set as yuv444 when output
3550*4882a593Smuzhiyun * format is yuv420
3551*4882a593Smuzhiyun */
3552*4882a593Smuzhiyun if (color == MEDIA_BUS_FMT_UYVY10_1X20)
3553*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3554*4882a593Smuzhiyun MEDIA_BUS_FMT_YUV10_1X30;
3555*4882a593Smuzhiyun else if (color == MEDIA_BUS_FMT_UYVY8_1X16)
3556*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3557*4882a593Smuzhiyun MEDIA_BUS_FMT_YUV8_1X24;
3558*4882a593Smuzhiyun }
3559*4882a593Smuzhiyun
3560*4882a593Smuzhiyun if (ops && ops->attach_properties)
3561*4882a593Smuzhiyun return ops->attach_properties(&hdmi->connector,
3562*4882a593Smuzhiyun color, hdmi->version,
3563*4882a593Smuzhiyun hdmi->plat_data->phy_data, 0);
3564*4882a593Smuzhiyun }
3565*4882a593Smuzhiyun
dw_hdmi_destroy_properties(struct dw_hdmi * hdmi)3566*4882a593Smuzhiyun static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi)
3567*4882a593Smuzhiyun {
3568*4882a593Smuzhiyun const struct dw_hdmi_property_ops *ops =
3569*4882a593Smuzhiyun hdmi->plat_data->property_ops;
3570*4882a593Smuzhiyun
3571*4882a593Smuzhiyun if (ops && ops->destroy_properties)
3572*4882a593Smuzhiyun return ops->destroy_properties(&hdmi->connector,
3573*4882a593Smuzhiyun hdmi->plat_data->phy_data);
3574*4882a593Smuzhiyun }
3575*4882a593Smuzhiyun
dw_hdmi_connector_create(struct dw_hdmi * hdmi)3576*4882a593Smuzhiyun static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
3577*4882a593Smuzhiyun {
3578*4882a593Smuzhiyun struct drm_connector *connector = &hdmi->connector;
3579*4882a593Smuzhiyun struct cec_connector_info conn_info;
3580*4882a593Smuzhiyun struct cec_notifier *notifier;
3581*4882a593Smuzhiyun
3582*4882a593Smuzhiyun if (hdmi->version >= 0x200a)
3583*4882a593Smuzhiyun connector->ycbcr_420_allowed =
3584*4882a593Smuzhiyun hdmi->plat_data->ycbcr_420_allowed;
3585*4882a593Smuzhiyun else
3586*4882a593Smuzhiyun connector->ycbcr_420_allowed = false;
3587*4882a593Smuzhiyun
3588*4882a593Smuzhiyun connector->interlace_allowed = 1;
3589*4882a593Smuzhiyun connector->polled = DRM_CONNECTOR_POLL_HPD;
3590*4882a593Smuzhiyun
3591*4882a593Smuzhiyun drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
3592*4882a593Smuzhiyun
3593*4882a593Smuzhiyun drm_connector_init_with_ddc(hdmi->bridge.dev, connector,
3594*4882a593Smuzhiyun &dw_hdmi_connector_funcs,
3595*4882a593Smuzhiyun DRM_MODE_CONNECTOR_HDMIA,
3596*4882a593Smuzhiyun hdmi->ddc);
3597*4882a593Smuzhiyun
3598*4882a593Smuzhiyun /*
3599*4882a593Smuzhiyun * drm_connector_attach_max_bpc_property() requires the
3600*4882a593Smuzhiyun * connector to have a state.
3601*4882a593Smuzhiyun */
3602*4882a593Smuzhiyun drm_atomic_helper_connector_reset(connector);
3603*4882a593Smuzhiyun
3604*4882a593Smuzhiyun drm_connector_attach_max_bpc_property(connector, 8, 16);
3605*4882a593Smuzhiyun
3606*4882a593Smuzhiyun if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
3607*4882a593Smuzhiyun drm_object_attach_property(&connector->base,
3608*4882a593Smuzhiyun connector->dev->mode_config.hdr_output_metadata_property, 0);
3609*4882a593Smuzhiyun
3610*4882a593Smuzhiyun drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
3611*4882a593Smuzhiyun
3612*4882a593Smuzhiyun dw_hdmi_attach_properties(hdmi);
3613*4882a593Smuzhiyun
3614*4882a593Smuzhiyun cec_fill_conn_info_from_drm(&conn_info, connector);
3615*4882a593Smuzhiyun
3616*4882a593Smuzhiyun notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
3617*4882a593Smuzhiyun if (!notifier)
3618*4882a593Smuzhiyun return -ENOMEM;
3619*4882a593Smuzhiyun
3620*4882a593Smuzhiyun mutex_lock(&hdmi->cec_notifier_mutex);
3621*4882a593Smuzhiyun hdmi->cec_notifier = notifier;
3622*4882a593Smuzhiyun mutex_unlock(&hdmi->cec_notifier_mutex);
3623*4882a593Smuzhiyun
3624*4882a593Smuzhiyun return 0;
3625*4882a593Smuzhiyun }
3626*4882a593Smuzhiyun
3627*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
3628*4882a593Smuzhiyun * DRM Bridge Operations
3629*4882a593Smuzhiyun */
3630*4882a593Smuzhiyun
3631*4882a593Smuzhiyun /*
3632*4882a593Smuzhiyun * Possible output formats :
3633*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
3634*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
3635*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
3636*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
3637*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV16_1X48,
3638*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB161616_1X48,
3639*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY12_1X24,
3640*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV12_1X36,
3641*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB121212_1X36,
3642*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY10_1X20,
3643*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV10_1X30,
3644*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB101010_1X30,
3645*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY8_1X16,
3646*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV8_1X24,
3647*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB888_1X24,
3648*4882a593Smuzhiyun */
3649*4882a593Smuzhiyun
3650*4882a593Smuzhiyun /* Can return a maximum of 11 possible output formats for a mode/connector */
3651*4882a593Smuzhiyun #define MAX_OUTPUT_SEL_FORMATS 11
3652*4882a593Smuzhiyun
dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state,unsigned int * num_output_fmts)3653*4882a593Smuzhiyun static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
3654*4882a593Smuzhiyun struct drm_bridge_state *bridge_state,
3655*4882a593Smuzhiyun struct drm_crtc_state *crtc_state,
3656*4882a593Smuzhiyun struct drm_connector_state *conn_state,
3657*4882a593Smuzhiyun unsigned int *num_output_fmts)
3658*4882a593Smuzhiyun {
3659*4882a593Smuzhiyun struct drm_connector *conn = conn_state->connector;
3660*4882a593Smuzhiyun struct drm_display_info *info = &conn->display_info;
3661*4882a593Smuzhiyun struct drm_display_mode *mode = &crtc_state->mode;
3662*4882a593Smuzhiyun u8 max_bpc = conn_state->max_requested_bpc;
3663*4882a593Smuzhiyun bool is_hdmi2_sink = info->hdmi.scdc.supported ||
3664*4882a593Smuzhiyun (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
3665*4882a593Smuzhiyun u32 *output_fmts;
3666*4882a593Smuzhiyun unsigned int i = 0;
3667*4882a593Smuzhiyun
3668*4882a593Smuzhiyun *num_output_fmts = 0;
3669*4882a593Smuzhiyun
3670*4882a593Smuzhiyun output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
3671*4882a593Smuzhiyun GFP_KERNEL);
3672*4882a593Smuzhiyun if (!output_fmts)
3673*4882a593Smuzhiyun return NULL;
3674*4882a593Smuzhiyun
3675*4882a593Smuzhiyun /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */
3676*4882a593Smuzhiyun if (list_is_singular(&bridge->encoder->bridge_chain) ||
3677*4882a593Smuzhiyun list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) {
3678*4882a593Smuzhiyun *num_output_fmts = 1;
3679*4882a593Smuzhiyun output_fmts[0] = MEDIA_BUS_FMT_FIXED;
3680*4882a593Smuzhiyun
3681*4882a593Smuzhiyun return output_fmts;
3682*4882a593Smuzhiyun }
3683*4882a593Smuzhiyun
3684*4882a593Smuzhiyun /*
3685*4882a593Smuzhiyun * If the current mode enforces 4:2:0, force the output but format
3686*4882a593Smuzhiyun * to 4:2:0 and do not add the YUV422/444/RGB formats
3687*4882a593Smuzhiyun */
3688*4882a593Smuzhiyun if (conn->ycbcr_420_allowed &&
3689*4882a593Smuzhiyun (drm_mode_is_420_only(info, mode) ||
3690*4882a593Smuzhiyun (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
3691*4882a593Smuzhiyun
3692*4882a593Smuzhiyun /* Order bus formats from 16bit to 8bit if supported */
3693*4882a593Smuzhiyun if (max_bpc >= 16 && info->bpc == 16 &&
3694*4882a593Smuzhiyun (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
3695*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
3696*4882a593Smuzhiyun
3697*4882a593Smuzhiyun if (max_bpc >= 12 && info->bpc >= 12 &&
3698*4882a593Smuzhiyun (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
3699*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
3700*4882a593Smuzhiyun
3701*4882a593Smuzhiyun if (max_bpc >= 10 && info->bpc >= 10 &&
3702*4882a593Smuzhiyun (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
3703*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
3704*4882a593Smuzhiyun
3705*4882a593Smuzhiyun /* Default 8bit fallback */
3706*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
3707*4882a593Smuzhiyun
3708*4882a593Smuzhiyun *num_output_fmts = i;
3709*4882a593Smuzhiyun
3710*4882a593Smuzhiyun return output_fmts;
3711*4882a593Smuzhiyun }
3712*4882a593Smuzhiyun
3713*4882a593Smuzhiyun /*
3714*4882a593Smuzhiyun * Order bus formats from 16bit to 8bit and from YUV422 to RGB
3715*4882a593Smuzhiyun * if supported. In any case the default RGB888 format is added
3716*4882a593Smuzhiyun */
3717*4882a593Smuzhiyun
3718*4882a593Smuzhiyun /* Default 8bit RGB fallback */
3719*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
3720*4882a593Smuzhiyun
3721*4882a593Smuzhiyun if (max_bpc >= 16 && info->bpc == 16) {
3722*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
3723*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
3724*4882a593Smuzhiyun
3725*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
3726*4882a593Smuzhiyun }
3727*4882a593Smuzhiyun
3728*4882a593Smuzhiyun if (max_bpc >= 12 && info->bpc >= 12) {
3729*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
3730*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
3731*4882a593Smuzhiyun
3732*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
3733*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
3734*4882a593Smuzhiyun
3735*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
3736*4882a593Smuzhiyun }
3737*4882a593Smuzhiyun
3738*4882a593Smuzhiyun if (max_bpc >= 10 && info->bpc >= 10) {
3739*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
3740*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
3741*4882a593Smuzhiyun
3742*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
3743*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
3744*4882a593Smuzhiyun
3745*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
3746*4882a593Smuzhiyun }
3747*4882a593Smuzhiyun
3748*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
3749*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
3750*4882a593Smuzhiyun
3751*4882a593Smuzhiyun if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
3752*4882a593Smuzhiyun output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
3753*4882a593Smuzhiyun
3754*4882a593Smuzhiyun *num_output_fmts = i;
3755*4882a593Smuzhiyun
3756*4882a593Smuzhiyun return output_fmts;
3757*4882a593Smuzhiyun }
3758*4882a593Smuzhiyun
3759*4882a593Smuzhiyun /*
3760*4882a593Smuzhiyun * Possible input formats :
3761*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB888_1X24
3762*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV8_1X24
3763*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY8_1X16
3764*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
3765*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB101010_1X30
3766*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV10_1X30
3767*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY10_1X20
3768*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
3769*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB121212_1X36
3770*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV12_1X36
3771*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYVY12_1X24
3772*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
3773*4882a593Smuzhiyun * - MEDIA_BUS_FMT_RGB161616_1X48
3774*4882a593Smuzhiyun * - MEDIA_BUS_FMT_YUV16_1X48
3775*4882a593Smuzhiyun * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
3776*4882a593Smuzhiyun */
3777*4882a593Smuzhiyun
3778*4882a593Smuzhiyun /* Can return a maximum of 3 possible input formats for an output format */
3779*4882a593Smuzhiyun #define MAX_INPUT_SEL_FORMATS 3
3780*4882a593Smuzhiyun
dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state,u32 output_fmt,unsigned int * num_input_fmts)3781*4882a593Smuzhiyun static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
3782*4882a593Smuzhiyun struct drm_bridge_state *bridge_state,
3783*4882a593Smuzhiyun struct drm_crtc_state *crtc_state,
3784*4882a593Smuzhiyun struct drm_connector_state *conn_state,
3785*4882a593Smuzhiyun u32 output_fmt,
3786*4882a593Smuzhiyun unsigned int *num_input_fmts)
3787*4882a593Smuzhiyun {
3788*4882a593Smuzhiyun u32 *input_fmts;
3789*4882a593Smuzhiyun unsigned int i = 0;
3790*4882a593Smuzhiyun
3791*4882a593Smuzhiyun *num_input_fmts = 0;
3792*4882a593Smuzhiyun
3793*4882a593Smuzhiyun input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
3794*4882a593Smuzhiyun GFP_KERNEL);
3795*4882a593Smuzhiyun if (!input_fmts)
3796*4882a593Smuzhiyun return NULL;
3797*4882a593Smuzhiyun
3798*4882a593Smuzhiyun switch (output_fmt) {
3799*4882a593Smuzhiyun /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */
3800*4882a593Smuzhiyun case MEDIA_BUS_FMT_FIXED:
3801*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
3802*4882a593Smuzhiyun break;
3803*4882a593Smuzhiyun /* 8bit */
3804*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB888_1X24:
3805*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
3806*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
3807*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
3808*4882a593Smuzhiyun break;
3809*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV8_1X24:
3810*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
3811*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
3812*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
3813*4882a593Smuzhiyun break;
3814*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY8_1X16:
3815*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
3816*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
3817*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
3818*4882a593Smuzhiyun break;
3819*4882a593Smuzhiyun
3820*4882a593Smuzhiyun /* 10bit */
3821*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB101010_1X30:
3822*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
3823*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
3824*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
3825*4882a593Smuzhiyun break;
3826*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV10_1X30:
3827*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
3828*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
3829*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
3830*4882a593Smuzhiyun break;
3831*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY10_1X20:
3832*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
3833*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
3834*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
3835*4882a593Smuzhiyun break;
3836*4882a593Smuzhiyun
3837*4882a593Smuzhiyun /* 12bit */
3838*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB121212_1X36:
3839*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
3840*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
3841*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
3842*4882a593Smuzhiyun break;
3843*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV12_1X36:
3844*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
3845*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
3846*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
3847*4882a593Smuzhiyun break;
3848*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYVY12_1X24:
3849*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
3850*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
3851*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
3852*4882a593Smuzhiyun break;
3853*4882a593Smuzhiyun
3854*4882a593Smuzhiyun /* 16bit */
3855*4882a593Smuzhiyun case MEDIA_BUS_FMT_RGB161616_1X48:
3856*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
3857*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
3858*4882a593Smuzhiyun break;
3859*4882a593Smuzhiyun case MEDIA_BUS_FMT_YUV16_1X48:
3860*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
3861*4882a593Smuzhiyun input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
3862*4882a593Smuzhiyun break;
3863*4882a593Smuzhiyun
3864*4882a593Smuzhiyun /*YUV 4:2:0 */
3865*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
3866*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
3867*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
3868*4882a593Smuzhiyun case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
3869*4882a593Smuzhiyun input_fmts[i++] = output_fmt;
3870*4882a593Smuzhiyun break;
3871*4882a593Smuzhiyun }
3872*4882a593Smuzhiyun
3873*4882a593Smuzhiyun *num_input_fmts = i;
3874*4882a593Smuzhiyun
3875*4882a593Smuzhiyun if (*num_input_fmts == 0) {
3876*4882a593Smuzhiyun kfree(input_fmts);
3877*4882a593Smuzhiyun input_fmts = NULL;
3878*4882a593Smuzhiyun }
3879*4882a593Smuzhiyun
3880*4882a593Smuzhiyun return input_fmts;
3881*4882a593Smuzhiyun }
3882*4882a593Smuzhiyun
dw_hdmi_bridge_atomic_check(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)3883*4882a593Smuzhiyun static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
3884*4882a593Smuzhiyun struct drm_bridge_state *bridge_state,
3885*4882a593Smuzhiyun struct drm_crtc_state *crtc_state,
3886*4882a593Smuzhiyun struct drm_connector_state *conn_state)
3887*4882a593Smuzhiyun {
3888*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3889*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3890*4882a593Smuzhiyun
3891*4882a593Smuzhiyun if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) {
3892*4882a593Smuzhiyun if (hdmi->plat_data->get_output_bus_format)
3893*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
3894*4882a593Smuzhiyun hdmi->plat_data->get_output_bus_format(data);
3895*4882a593Smuzhiyun else
3896*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
3897*4882a593Smuzhiyun MEDIA_BUS_FMT_RGB888_1X24;
3898*4882a593Smuzhiyun
3899*4882a593Smuzhiyun if (hdmi->plat_data->get_input_bus_format)
3900*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3901*4882a593Smuzhiyun hdmi->plat_data->get_input_bus_format(data);
3902*4882a593Smuzhiyun else if (hdmi->plat_data->input_bus_format)
3903*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3904*4882a593Smuzhiyun hdmi->plat_data->input_bus_format;
3905*4882a593Smuzhiyun else
3906*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3907*4882a593Smuzhiyun MEDIA_BUS_FMT_RGB888_1X24;
3908*4882a593Smuzhiyun } else {
3909*4882a593Smuzhiyun hdmi->hdmi_data.enc_out_bus_format =
3910*4882a593Smuzhiyun bridge_state->output_bus_cfg.format;
3911*4882a593Smuzhiyun
3912*4882a593Smuzhiyun hdmi->hdmi_data.enc_in_bus_format =
3913*4882a593Smuzhiyun bridge_state->input_bus_cfg.format;
3914*4882a593Smuzhiyun
3915*4882a593Smuzhiyun dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
3916*4882a593Smuzhiyun bridge_state->input_bus_cfg.format,
3917*4882a593Smuzhiyun bridge_state->output_bus_cfg.format);
3918*4882a593Smuzhiyun }
3919*4882a593Smuzhiyun
3920*4882a593Smuzhiyun return 0;
3921*4882a593Smuzhiyun }
3922*4882a593Smuzhiyun
dw_hdmi_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)3923*4882a593Smuzhiyun static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
3924*4882a593Smuzhiyun enum drm_bridge_attach_flags flags)
3925*4882a593Smuzhiyun {
3926*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3927*4882a593Smuzhiyun int ret;
3928*4882a593Smuzhiyun
3929*4882a593Smuzhiyun if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
3930*4882a593Smuzhiyun return 0;
3931*4882a593Smuzhiyun
3932*4882a593Smuzhiyun if (hdmi->next_bridge) {
3933*4882a593Smuzhiyun hdmi->next_bridge->encoder = bridge->encoder;
3934*4882a593Smuzhiyun ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags);
3935*4882a593Smuzhiyun if (ret) {
3936*4882a593Smuzhiyun DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
3937*4882a593Smuzhiyun return ret;
3938*4882a593Smuzhiyun }
3939*4882a593Smuzhiyun
3940*4882a593Smuzhiyun return 0;
3941*4882a593Smuzhiyun }
3942*4882a593Smuzhiyun
3943*4882a593Smuzhiyun return dw_hdmi_connector_create(hdmi);
3944*4882a593Smuzhiyun }
3945*4882a593Smuzhiyun
dw_hdmi_bridge_detach(struct drm_bridge * bridge)3946*4882a593Smuzhiyun static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
3947*4882a593Smuzhiyun {
3948*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3949*4882a593Smuzhiyun
3950*4882a593Smuzhiyun mutex_lock(&hdmi->cec_notifier_mutex);
3951*4882a593Smuzhiyun cec_notifier_conn_unregister(hdmi->cec_notifier);
3952*4882a593Smuzhiyun hdmi->cec_notifier = NULL;
3953*4882a593Smuzhiyun mutex_unlock(&hdmi->cec_notifier_mutex);
3954*4882a593Smuzhiyun }
3955*4882a593Smuzhiyun
3956*4882a593Smuzhiyun static enum drm_mode_status
dw_hdmi_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)3957*4882a593Smuzhiyun dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
3958*4882a593Smuzhiyun const struct drm_display_info *info,
3959*4882a593Smuzhiyun const struct drm_display_mode *mode)
3960*4882a593Smuzhiyun {
3961*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3962*4882a593Smuzhiyun const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
3963*4882a593Smuzhiyun enum drm_mode_status mode_status = MODE_OK;
3964*4882a593Smuzhiyun
3965*4882a593Smuzhiyun if (hdmi->next_bridge)
3966*4882a593Smuzhiyun return MODE_OK;
3967*4882a593Smuzhiyun
3968*4882a593Smuzhiyun if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD) && hdmi->hdr2sdr)
3969*4882a593Smuzhiyun return MODE_OK;
3970*4882a593Smuzhiyun
3971*4882a593Smuzhiyun if (pdata->mode_valid)
3972*4882a593Smuzhiyun mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
3973*4882a593Smuzhiyun mode);
3974*4882a593Smuzhiyun
3975*4882a593Smuzhiyun return mode_status;
3976*4882a593Smuzhiyun }
3977*4882a593Smuzhiyun
dw_hdmi_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * orig_mode,const struct drm_display_mode * mode)3978*4882a593Smuzhiyun static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
3979*4882a593Smuzhiyun const struct drm_display_mode *orig_mode,
3980*4882a593Smuzhiyun const struct drm_display_mode *mode)
3981*4882a593Smuzhiyun {
3982*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3983*4882a593Smuzhiyun
3984*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
3985*4882a593Smuzhiyun
3986*4882a593Smuzhiyun /* Store the display mode for plugin/DKMS poweron events */
3987*4882a593Smuzhiyun memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
3988*4882a593Smuzhiyun
3989*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
3990*4882a593Smuzhiyun }
3991*4882a593Smuzhiyun
dw_hdmi_bridge_atomic_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_state)3992*4882a593Smuzhiyun static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
3993*4882a593Smuzhiyun struct drm_bridge_state *old_state)
3994*4882a593Smuzhiyun {
3995*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
3996*4882a593Smuzhiyun void *data = hdmi->plat_data->phy_data;
3997*4882a593Smuzhiyun
3998*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
3999*4882a593Smuzhiyun hdmi->disabled = true;
4000*4882a593Smuzhiyun handle_plugged_change(hdmi, false);
4001*4882a593Smuzhiyun hdmi->curr_conn = NULL;
4002*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
4003*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
4004*4882a593Smuzhiyun if (hdmi->plat_data->dclk_set)
4005*4882a593Smuzhiyun hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, false, 0);
4006*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
4007*4882a593Smuzhiyun
4008*4882a593Smuzhiyun mutex_lock(&hdmi->i2c->lock);
4009*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
4010*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(data, false);
4011*4882a593Smuzhiyun mutex_unlock(&hdmi->i2c->lock);
4012*4882a593Smuzhiyun }
4013*4882a593Smuzhiyun
dw_hdmi_bridge_atomic_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_state)4014*4882a593Smuzhiyun static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
4015*4882a593Smuzhiyun struct drm_bridge_state *old_state)
4016*4882a593Smuzhiyun {
4017*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
4018*4882a593Smuzhiyun struct drm_atomic_state *state = old_state->base.state;
4019*4882a593Smuzhiyun struct drm_connector *connector;
4020*4882a593Smuzhiyun
4021*4882a593Smuzhiyun connector = drm_atomic_get_new_connector_for_encoder(state,
4022*4882a593Smuzhiyun bridge->encoder);
4023*4882a593Smuzhiyun
4024*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
4025*4882a593Smuzhiyun hdmi->disabled = false;
4026*4882a593Smuzhiyun hdmi->curr_conn = connector;
4027*4882a593Smuzhiyun if (hdmi->plat_data->dclk_set)
4028*4882a593Smuzhiyun hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0);
4029*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
4030*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
4031*4882a593Smuzhiyun handle_plugged_change(hdmi, true);
4032*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
4033*4882a593Smuzhiyun }
4034*4882a593Smuzhiyun
dw_hdmi_bridge_detect(struct drm_bridge * bridge)4035*4882a593Smuzhiyun static enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge)
4036*4882a593Smuzhiyun {
4037*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
4038*4882a593Smuzhiyun
4039*4882a593Smuzhiyun return dw_hdmi_detect(hdmi);
4040*4882a593Smuzhiyun }
4041*4882a593Smuzhiyun
dw_hdmi_bridge_get_edid(struct drm_bridge * bridge,struct drm_connector * connector)4042*4882a593Smuzhiyun static struct edid *dw_hdmi_bridge_get_edid(struct drm_bridge *bridge,
4043*4882a593Smuzhiyun struct drm_connector *connector)
4044*4882a593Smuzhiyun {
4045*4882a593Smuzhiyun struct dw_hdmi *hdmi = bridge->driver_private;
4046*4882a593Smuzhiyun
4047*4882a593Smuzhiyun return dw_hdmi_get_edid(hdmi, connector);
4048*4882a593Smuzhiyun }
4049*4882a593Smuzhiyun
4050*4882a593Smuzhiyun static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
4051*4882a593Smuzhiyun .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
4052*4882a593Smuzhiyun .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
4053*4882a593Smuzhiyun .atomic_reset = drm_atomic_helper_bridge_reset,
4054*4882a593Smuzhiyun .attach = dw_hdmi_bridge_attach,
4055*4882a593Smuzhiyun .detach = dw_hdmi_bridge_detach,
4056*4882a593Smuzhiyun .atomic_check = dw_hdmi_bridge_atomic_check,
4057*4882a593Smuzhiyun .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
4058*4882a593Smuzhiyun .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
4059*4882a593Smuzhiyun .atomic_enable = dw_hdmi_bridge_atomic_enable,
4060*4882a593Smuzhiyun .atomic_disable = dw_hdmi_bridge_atomic_disable,
4061*4882a593Smuzhiyun .mode_set = dw_hdmi_bridge_mode_set,
4062*4882a593Smuzhiyun .mode_valid = dw_hdmi_bridge_mode_valid,
4063*4882a593Smuzhiyun .detect = dw_hdmi_bridge_detect,
4064*4882a593Smuzhiyun .get_edid = dw_hdmi_bridge_get_edid,
4065*4882a593Smuzhiyun };
4066*4882a593Smuzhiyun
dw_hdmi_set_cec_adap(struct dw_hdmi * hdmi,struct cec_adapter * adap)4067*4882a593Smuzhiyun void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap)
4068*4882a593Smuzhiyun {
4069*4882a593Smuzhiyun hdmi->cec_adap = adap;
4070*4882a593Smuzhiyun }
4071*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap);
4072*4882a593Smuzhiyun
4073*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
4074*4882a593Smuzhiyun * IRQ Handling
4075*4882a593Smuzhiyun */
4076*4882a593Smuzhiyun
dw_hdmi_i2c_irq(struct dw_hdmi * hdmi)4077*4882a593Smuzhiyun static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
4078*4882a593Smuzhiyun {
4079*4882a593Smuzhiyun struct dw_hdmi_i2c *i2c = hdmi->i2c;
4080*4882a593Smuzhiyun unsigned int stat;
4081*4882a593Smuzhiyun
4082*4882a593Smuzhiyun stat = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
4083*4882a593Smuzhiyun if (!stat)
4084*4882a593Smuzhiyun return IRQ_NONE;
4085*4882a593Smuzhiyun
4086*4882a593Smuzhiyun hdmi_writeb(hdmi, stat, HDMI_IH_I2CM_STAT0);
4087*4882a593Smuzhiyun
4088*4882a593Smuzhiyun i2c->stat = stat;
4089*4882a593Smuzhiyun
4090*4882a593Smuzhiyun complete(&i2c->cmp);
4091*4882a593Smuzhiyun
4092*4882a593Smuzhiyun return IRQ_HANDLED;
4093*4882a593Smuzhiyun }
4094*4882a593Smuzhiyun
dw_hdmi_hardirq(int irq,void * dev_id)4095*4882a593Smuzhiyun static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
4096*4882a593Smuzhiyun {
4097*4882a593Smuzhiyun struct dw_hdmi *hdmi = dev_id;
4098*4882a593Smuzhiyun u8 intr_stat, hdcp_stat;
4099*4882a593Smuzhiyun irqreturn_t ret = IRQ_NONE;
4100*4882a593Smuzhiyun
4101*4882a593Smuzhiyun if (hdmi->i2c)
4102*4882a593Smuzhiyun ret = dw_hdmi_i2c_irq(hdmi);
4103*4882a593Smuzhiyun
4104*4882a593Smuzhiyun intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
4105*4882a593Smuzhiyun if (intr_stat) {
4106*4882a593Smuzhiyun hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
4107*4882a593Smuzhiyun return IRQ_WAKE_THREAD;
4108*4882a593Smuzhiyun }
4109*4882a593Smuzhiyun
4110*4882a593Smuzhiyun hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
4111*4882a593Smuzhiyun if (hdcp_stat) {
4112*4882a593Smuzhiyun dev_dbg(hdmi->dev, "HDCP irq %#x\n", hdcp_stat);
4113*4882a593Smuzhiyun hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
4114*4882a593Smuzhiyun return IRQ_WAKE_THREAD;
4115*4882a593Smuzhiyun }
4116*4882a593Smuzhiyun
4117*4882a593Smuzhiyun return ret;
4118*4882a593Smuzhiyun }
4119*4882a593Smuzhiyun
dw_hdmi_setup_rx_sense(struct dw_hdmi * hdmi,bool hpd,bool rx_sense)4120*4882a593Smuzhiyun void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense)
4121*4882a593Smuzhiyun {
4122*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
4123*4882a593Smuzhiyun
4124*4882a593Smuzhiyun if (!hdmi->force && !hdmi->force_logo) {
4125*4882a593Smuzhiyun /*
4126*4882a593Smuzhiyun * If the RX sense status indicates we're disconnected,
4127*4882a593Smuzhiyun * clear the software rxsense status.
4128*4882a593Smuzhiyun */
4129*4882a593Smuzhiyun if (!rx_sense)
4130*4882a593Smuzhiyun hdmi->rxsense = false;
4131*4882a593Smuzhiyun
4132*4882a593Smuzhiyun /*
4133*4882a593Smuzhiyun * Only set the software rxsense status when both
4134*4882a593Smuzhiyun * rxsense and hpd indicates we're connected.
4135*4882a593Smuzhiyun * This avoids what seems to be bad behaviour in
4136*4882a593Smuzhiyun * at least iMX6S versions of the phy.
4137*4882a593Smuzhiyun */
4138*4882a593Smuzhiyun if (hpd)
4139*4882a593Smuzhiyun hdmi->rxsense = true;
4140*4882a593Smuzhiyun
4141*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
4142*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
4143*4882a593Smuzhiyun }
4144*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
4145*4882a593Smuzhiyun }
4146*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense);
4147*4882a593Smuzhiyun
dw_hdmi_irq(int irq,void * dev_id)4148*4882a593Smuzhiyun static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
4149*4882a593Smuzhiyun {
4150*4882a593Smuzhiyun struct dw_hdmi *hdmi = dev_id;
4151*4882a593Smuzhiyun u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat;
4152*4882a593Smuzhiyun
4153*4882a593Smuzhiyun intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
4154*4882a593Smuzhiyun phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
4155*4882a593Smuzhiyun phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
4156*4882a593Smuzhiyun
4157*4882a593Smuzhiyun phy_pol_mask = 0;
4158*4882a593Smuzhiyun if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
4159*4882a593Smuzhiyun phy_pol_mask |= HDMI_PHY_HPD;
4160*4882a593Smuzhiyun if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
4161*4882a593Smuzhiyun phy_pol_mask |= HDMI_PHY_RX_SENSE0;
4162*4882a593Smuzhiyun if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
4163*4882a593Smuzhiyun phy_pol_mask |= HDMI_PHY_RX_SENSE1;
4164*4882a593Smuzhiyun if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
4165*4882a593Smuzhiyun phy_pol_mask |= HDMI_PHY_RX_SENSE2;
4166*4882a593Smuzhiyun if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
4167*4882a593Smuzhiyun phy_pol_mask |= HDMI_PHY_RX_SENSE3;
4168*4882a593Smuzhiyun
4169*4882a593Smuzhiyun if (phy_pol_mask)
4170*4882a593Smuzhiyun hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
4171*4882a593Smuzhiyun
4172*4882a593Smuzhiyun /*
4173*4882a593Smuzhiyun * RX sense tells us whether the TDMS transmitters are detecting
4174*4882a593Smuzhiyun * load - in other words, there's something listening on the
4175*4882a593Smuzhiyun * other end of the link. Use this to decide whether we should
4176*4882a593Smuzhiyun * power on the phy as HPD may be toggled by the sink to merely
4177*4882a593Smuzhiyun * ask the source to re-read the EDID.
4178*4882a593Smuzhiyun */
4179*4882a593Smuzhiyun if (intr_stat &
4180*4882a593Smuzhiyun (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
4181*4882a593Smuzhiyun dw_hdmi_setup_rx_sense(hdmi,
4182*4882a593Smuzhiyun phy_stat & HDMI_PHY_HPD,
4183*4882a593Smuzhiyun phy_stat & HDMI_PHY_RX_SENSE);
4184*4882a593Smuzhiyun
4185*4882a593Smuzhiyun if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) {
4186*4882a593Smuzhiyun mutex_lock(&hdmi->cec_notifier_mutex);
4187*4882a593Smuzhiyun cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
4188*4882a593Smuzhiyun mutex_unlock(&hdmi->cec_notifier_mutex);
4189*4882a593Smuzhiyun }
4190*4882a593Smuzhiyun }
4191*4882a593Smuzhiyun
4192*4882a593Smuzhiyun check_hdmi_irq(hdmi, intr_stat, phy_int_pol);
4193*4882a593Smuzhiyun
4194*4882a593Smuzhiyun hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
4195*4882a593Smuzhiyun if (!hdmi->next_bridge)
4196*4882a593Smuzhiyun hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
4197*4882a593Smuzhiyun HDMI_IH_PHY_STAT0_RX_SENSE),
4198*4882a593Smuzhiyun HDMI_IH_MUTE_PHY_STAT0);
4199*4882a593Smuzhiyun
4200*4882a593Smuzhiyun hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
4201*4882a593Smuzhiyun if (hdcp_stat) {
4202*4882a593Smuzhiyun if (hdmi->hdcp)
4203*4882a593Smuzhiyun hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
4204*4882a593Smuzhiyun hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
4205*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
4206*4882a593Smuzhiyun }
4207*4882a593Smuzhiyun return IRQ_HANDLED;
4208*4882a593Smuzhiyun }
4209*4882a593Smuzhiyun
4210*4882a593Smuzhiyun static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
4211*4882a593Smuzhiyun {
4212*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
4213*4882a593Smuzhiyun .name = "DWC HDMI TX PHY",
4214*4882a593Smuzhiyun .gen = 1,
4215*4882a593Smuzhiyun }, {
4216*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
4217*4882a593Smuzhiyun .name = "DWC MHL PHY + HEAC PHY",
4218*4882a593Smuzhiyun .gen = 2,
4219*4882a593Smuzhiyun .has_svsret = true,
4220*4882a593Smuzhiyun .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
4221*4882a593Smuzhiyun }, {
4222*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_MHL_PHY,
4223*4882a593Smuzhiyun .name = "DWC MHL PHY",
4224*4882a593Smuzhiyun .gen = 2,
4225*4882a593Smuzhiyun .has_svsret = true,
4226*4882a593Smuzhiyun .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
4227*4882a593Smuzhiyun }, {
4228*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
4229*4882a593Smuzhiyun .name = "DWC HDMI 3D TX PHY + HEAC PHY",
4230*4882a593Smuzhiyun .gen = 2,
4231*4882a593Smuzhiyun .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
4232*4882a593Smuzhiyun }, {
4233*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
4234*4882a593Smuzhiyun .name = "DWC HDMI 3D TX PHY",
4235*4882a593Smuzhiyun .gen = 2,
4236*4882a593Smuzhiyun .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
4237*4882a593Smuzhiyun }, {
4238*4882a593Smuzhiyun .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
4239*4882a593Smuzhiyun .name = "DWC HDMI 2.0 TX PHY",
4240*4882a593Smuzhiyun .gen = 2,
4241*4882a593Smuzhiyun .has_svsret = true,
4242*4882a593Smuzhiyun .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
4243*4882a593Smuzhiyun }, {
4244*4882a593Smuzhiyun .type = DW_HDMI_PHY_VENDOR_PHY,
4245*4882a593Smuzhiyun .name = "Vendor PHY",
4246*4882a593Smuzhiyun }
4247*4882a593Smuzhiyun };
4248*4882a593Smuzhiyun
dw_hdmi_detect_phy(struct dw_hdmi * hdmi)4249*4882a593Smuzhiyun static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
4250*4882a593Smuzhiyun {
4251*4882a593Smuzhiyun unsigned int i;
4252*4882a593Smuzhiyun u8 phy_type;
4253*4882a593Smuzhiyun
4254*4882a593Smuzhiyun phy_type = hdmi->plat_data->phy_force_vendor ?
4255*4882a593Smuzhiyun DW_HDMI_PHY_VENDOR_PHY :
4256*4882a593Smuzhiyun hdmi_readb(hdmi, HDMI_CONFIG2_ID);
4257*4882a593Smuzhiyun
4258*4882a593Smuzhiyun if (phy_type == DW_HDMI_PHY_VENDOR_PHY) {
4259*4882a593Smuzhiyun /* Vendor PHYs require support from the glue layer. */
4260*4882a593Smuzhiyun if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) {
4261*4882a593Smuzhiyun dev_err(hdmi->dev,
4262*4882a593Smuzhiyun "Vendor HDMI PHY not supported by glue layer\n");
4263*4882a593Smuzhiyun return -ENODEV;
4264*4882a593Smuzhiyun }
4265*4882a593Smuzhiyun
4266*4882a593Smuzhiyun hdmi->phy.ops = hdmi->plat_data->phy_ops;
4267*4882a593Smuzhiyun hdmi->phy.data = hdmi->plat_data->phy_data;
4268*4882a593Smuzhiyun hdmi->phy.name = hdmi->plat_data->phy_name;
4269*4882a593Smuzhiyun return 0;
4270*4882a593Smuzhiyun }
4271*4882a593Smuzhiyun
4272*4882a593Smuzhiyun /* Synopsys PHYs are handled internally. */
4273*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
4274*4882a593Smuzhiyun if (dw_hdmi_phys[i].type == phy_type) {
4275*4882a593Smuzhiyun hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
4276*4882a593Smuzhiyun hdmi->phy.name = dw_hdmi_phys[i].name;
4277*4882a593Smuzhiyun hdmi->phy.data = (void *)&dw_hdmi_phys[i];
4278*4882a593Smuzhiyun
4279*4882a593Smuzhiyun if (!dw_hdmi_phys[i].configure &&
4280*4882a593Smuzhiyun !hdmi->plat_data->configure_phy) {
4281*4882a593Smuzhiyun dev_err(hdmi->dev, "%s requires platform support\n",
4282*4882a593Smuzhiyun hdmi->phy.name);
4283*4882a593Smuzhiyun return -ENODEV;
4284*4882a593Smuzhiyun }
4285*4882a593Smuzhiyun
4286*4882a593Smuzhiyun return 0;
4287*4882a593Smuzhiyun }
4288*4882a593Smuzhiyun }
4289*4882a593Smuzhiyun
4290*4882a593Smuzhiyun dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type);
4291*4882a593Smuzhiyun return -ENODEV;
4292*4882a593Smuzhiyun }
4293*4882a593Smuzhiyun
dw_hdmi_cec_enable(struct dw_hdmi * hdmi)4294*4882a593Smuzhiyun static void dw_hdmi_cec_enable(struct dw_hdmi *hdmi)
4295*4882a593Smuzhiyun {
4296*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
4297*4882a593Smuzhiyun hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
4298*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
4299*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
4300*4882a593Smuzhiyun }
4301*4882a593Smuzhiyun
dw_hdmi_cec_disable(struct dw_hdmi * hdmi)4302*4882a593Smuzhiyun static void dw_hdmi_cec_disable(struct dw_hdmi *hdmi)
4303*4882a593Smuzhiyun {
4304*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
4305*4882a593Smuzhiyun hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
4306*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
4307*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
4308*4882a593Smuzhiyun }
4309*4882a593Smuzhiyun
4310*4882a593Smuzhiyun static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
4311*4882a593Smuzhiyun .write = hdmi_writeb,
4312*4882a593Smuzhiyun .read = hdmi_readb,
4313*4882a593Smuzhiyun .mod = hdmi_modb,
4314*4882a593Smuzhiyun .enable = dw_hdmi_cec_enable,
4315*4882a593Smuzhiyun .disable = dw_hdmi_cec_disable,
4316*4882a593Smuzhiyun };
4317*4882a593Smuzhiyun
4318*4882a593Smuzhiyun static const struct regmap_config hdmi_regmap_8bit_config = {
4319*4882a593Smuzhiyun .reg_bits = 32,
4320*4882a593Smuzhiyun .val_bits = 8,
4321*4882a593Smuzhiyun .reg_stride = 1,
4322*4882a593Smuzhiyun .max_register = HDMI_I2CM_SCDC_UPDATE1,
4323*4882a593Smuzhiyun };
4324*4882a593Smuzhiyun
4325*4882a593Smuzhiyun static const struct regmap_config hdmi_regmap_32bit_config = {
4326*4882a593Smuzhiyun .reg_bits = 32,
4327*4882a593Smuzhiyun .val_bits = 32,
4328*4882a593Smuzhiyun .reg_stride = 4,
4329*4882a593Smuzhiyun .max_register = HDMI_I2CM_SCDC_UPDATE1 << 2,
4330*4882a593Smuzhiyun };
4331*4882a593Smuzhiyun
dw_hdmi_init_hw(struct dw_hdmi * hdmi)4332*4882a593Smuzhiyun static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
4333*4882a593Smuzhiyun {
4334*4882a593Smuzhiyun initialize_hdmi_ih_mutes(hdmi);
4335*4882a593Smuzhiyun
4336*4882a593Smuzhiyun /*
4337*4882a593Smuzhiyun * Reset HDMI DDC I2C master controller and mute I2CM interrupts.
4338*4882a593Smuzhiyun * Even if we are using a separate i2c adapter doing this doesn't
4339*4882a593Smuzhiyun * hurt.
4340*4882a593Smuzhiyun */
4341*4882a593Smuzhiyun if (hdmi->i2c)
4342*4882a593Smuzhiyun dw_hdmi_i2c_init(hdmi);
4343*4882a593Smuzhiyun
4344*4882a593Smuzhiyun if (hdmi->phy.ops->setup_hpd)
4345*4882a593Smuzhiyun hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
4346*4882a593Smuzhiyun }
4347*4882a593Smuzhiyun
dw_hdmi_status_show(struct seq_file * s,void * v)4348*4882a593Smuzhiyun static int dw_hdmi_status_show(struct seq_file *s, void *v)
4349*4882a593Smuzhiyun {
4350*4882a593Smuzhiyun struct dw_hdmi *hdmi = s->private;
4351*4882a593Smuzhiyun u32 val;
4352*4882a593Smuzhiyun
4353*4882a593Smuzhiyun seq_puts(s, "PHY: ");
4354*4882a593Smuzhiyun if (!hdmi->phy.enabled) {
4355*4882a593Smuzhiyun seq_puts(s, "disabled\n");
4356*4882a593Smuzhiyun return 0;
4357*4882a593Smuzhiyun }
4358*4882a593Smuzhiyun seq_puts(s, "enabled\t\t\tMode: ");
4359*4882a593Smuzhiyun if (hdmi->sink_is_hdmi)
4360*4882a593Smuzhiyun seq_puts(s, "HDMI\n");
4361*4882a593Smuzhiyun else
4362*4882a593Smuzhiyun seq_puts(s, "DVI\n");
4363*4882a593Smuzhiyun if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
4364*4882a593Smuzhiyun val = hdmi->hdmi_data.video_mode.mtmdsclock / 4;
4365*4882a593Smuzhiyun else
4366*4882a593Smuzhiyun val = hdmi->hdmi_data.video_mode.mtmdsclock;
4367*4882a593Smuzhiyun seq_printf(s, "Pixel Clk: %uHz\t\tTMDS Clk: %uHz\n",
4368*4882a593Smuzhiyun hdmi->hdmi_data.video_mode.mpixelclock, val);
4369*4882a593Smuzhiyun seq_puts(s, "Color Format: ");
4370*4882a593Smuzhiyun if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format))
4371*4882a593Smuzhiyun seq_puts(s, "RGB");
4372*4882a593Smuzhiyun else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
4373*4882a593Smuzhiyun seq_puts(s, "YUV444");
4374*4882a593Smuzhiyun else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
4375*4882a593Smuzhiyun seq_puts(s, "YUV422");
4376*4882a593Smuzhiyun else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
4377*4882a593Smuzhiyun seq_puts(s, "YUV420");
4378*4882a593Smuzhiyun else
4379*4882a593Smuzhiyun seq_puts(s, "UNKNOWN");
4380*4882a593Smuzhiyun val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
4381*4882a593Smuzhiyun seq_printf(s, "\t\tColor Depth: %d bit\n", val);
4382*4882a593Smuzhiyun seq_puts(s, "Colorimetry: ");
4383*4882a593Smuzhiyun switch (hdmi->hdmi_data.enc_out_encoding) {
4384*4882a593Smuzhiyun case V4L2_YCBCR_ENC_601:
4385*4882a593Smuzhiyun seq_puts(s, "ITU.BT601");
4386*4882a593Smuzhiyun break;
4387*4882a593Smuzhiyun case V4L2_YCBCR_ENC_709:
4388*4882a593Smuzhiyun seq_puts(s, "ITU.BT709");
4389*4882a593Smuzhiyun break;
4390*4882a593Smuzhiyun case V4L2_YCBCR_ENC_BT2020:
4391*4882a593Smuzhiyun seq_puts(s, "ITU.BT2020");
4392*4882a593Smuzhiyun break;
4393*4882a593Smuzhiyun default: /* Carries no data */
4394*4882a593Smuzhiyun seq_puts(s, "ITU.BT601");
4395*4882a593Smuzhiyun break;
4396*4882a593Smuzhiyun }
4397*4882a593Smuzhiyun
4398*4882a593Smuzhiyun seq_puts(s, "\t\tEOTF: ");
4399*4882a593Smuzhiyun
4400*4882a593Smuzhiyun if (hdmi->version < 0x211a) {
4401*4882a593Smuzhiyun seq_puts(s, "Unsupported\n");
4402*4882a593Smuzhiyun return 0;
4403*4882a593Smuzhiyun }
4404*4882a593Smuzhiyun
4405*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN);
4406*4882a593Smuzhiyun if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) {
4407*4882a593Smuzhiyun seq_puts(s, "Off\n");
4408*4882a593Smuzhiyun return 0;
4409*4882a593Smuzhiyun }
4410*4882a593Smuzhiyun
4411*4882a593Smuzhiyun switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) {
4412*4882a593Smuzhiyun case HDMI_EOTF_TRADITIONAL_GAMMA_SDR:
4413*4882a593Smuzhiyun seq_puts(s, "SDR");
4414*4882a593Smuzhiyun break;
4415*4882a593Smuzhiyun case HDMI_EOTF_TRADITIONAL_GAMMA_HDR:
4416*4882a593Smuzhiyun seq_puts(s, "HDR");
4417*4882a593Smuzhiyun break;
4418*4882a593Smuzhiyun case HDMI_EOTF_SMPTE_ST2084:
4419*4882a593Smuzhiyun seq_puts(s, "ST2084");
4420*4882a593Smuzhiyun break;
4421*4882a593Smuzhiyun case HDMI_EOTF_BT_2100_HLG:
4422*4882a593Smuzhiyun seq_puts(s, "HLG");
4423*4882a593Smuzhiyun break;
4424*4882a593Smuzhiyun default:
4425*4882a593Smuzhiyun seq_puts(s, "Not Defined\n");
4426*4882a593Smuzhiyun return 0;
4427*4882a593Smuzhiyun }
4428*4882a593Smuzhiyun
4429*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB3) << 8;
4430*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB2);
4431*4882a593Smuzhiyun seq_printf(s, "\nx0: %d", val);
4432*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB5) << 8;
4433*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB4);
4434*4882a593Smuzhiyun seq_printf(s, "\t\t\t\ty0: %d\n", val);
4435*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB7) << 8;
4436*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB6);
4437*4882a593Smuzhiyun seq_printf(s, "x1: %d", val);
4438*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB9) << 8;
4439*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB8);
4440*4882a593Smuzhiyun seq_printf(s, "\t\t\t\ty1: %d\n", val);
4441*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB11) << 8;
4442*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB10);
4443*4882a593Smuzhiyun seq_printf(s, "x2: %d", val);
4444*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB13) << 8;
4445*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB12);
4446*4882a593Smuzhiyun seq_printf(s, "\t\t\t\ty2: %d\n", val);
4447*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB15) << 8;
4448*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB14);
4449*4882a593Smuzhiyun seq_printf(s, "white x: %d", val);
4450*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB17) << 8;
4451*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB16);
4452*4882a593Smuzhiyun seq_printf(s, "\t\t\twhite y: %d\n", val);
4453*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB19) << 8;
4454*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB18);
4455*4882a593Smuzhiyun seq_printf(s, "max lum: %d", val);
4456*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB21) << 8;
4457*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB20);
4458*4882a593Smuzhiyun seq_printf(s, "\t\t\tmin lum: %d\n", val);
4459*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB23) << 8;
4460*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB22);
4461*4882a593Smuzhiyun seq_printf(s, "max cll: %d", val);
4462*4882a593Smuzhiyun val = hdmi_readb(hdmi, HDMI_FC_DRM_PB25) << 8;
4463*4882a593Smuzhiyun val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB24);
4464*4882a593Smuzhiyun seq_printf(s, "\t\t\tmax fall: %d\n", val);
4465*4882a593Smuzhiyun return 0;
4466*4882a593Smuzhiyun }
4467*4882a593Smuzhiyun
dw_hdmi_status_open(struct inode * inode,struct file * file)4468*4882a593Smuzhiyun static int dw_hdmi_status_open(struct inode *inode, struct file *file)
4469*4882a593Smuzhiyun {
4470*4882a593Smuzhiyun return single_open(file, dw_hdmi_status_show, inode->i_private);
4471*4882a593Smuzhiyun }
4472*4882a593Smuzhiyun
4473*4882a593Smuzhiyun static const struct file_operations dw_hdmi_status_fops = {
4474*4882a593Smuzhiyun .owner = THIS_MODULE,
4475*4882a593Smuzhiyun .open = dw_hdmi_status_open,
4476*4882a593Smuzhiyun .read = seq_read,
4477*4882a593Smuzhiyun .llseek = seq_lseek,
4478*4882a593Smuzhiyun .release = single_release,
4479*4882a593Smuzhiyun };
4480*4882a593Smuzhiyun
4481*4882a593Smuzhiyun #include <linux/fs.h>
4482*4882a593Smuzhiyun #include <linux/debugfs.h>
4483*4882a593Smuzhiyun #include <linux/seq_file.h>
4484*4882a593Smuzhiyun
4485*4882a593Smuzhiyun struct dw_hdmi_reg_table {
4486*4882a593Smuzhiyun int reg_base;
4487*4882a593Smuzhiyun int reg_end;
4488*4882a593Smuzhiyun };
4489*4882a593Smuzhiyun
4490*4882a593Smuzhiyun static const struct dw_hdmi_reg_table hdmi_reg_table[] = {
4491*4882a593Smuzhiyun {HDMI_DESIGN_ID, HDMI_CONFIG3_ID},
4492*4882a593Smuzhiyun {HDMI_IH_FC_STAT0, HDMI_IH_MUTE},
4493*4882a593Smuzhiyun {HDMI_TX_INVID0, HDMI_TX_BCBDATA1},
4494*4882a593Smuzhiyun {HDMI_VP_STATUS, HDMI_VP_POL},
4495*4882a593Smuzhiyun {HDMI_FC_INVIDCONF, HDMI_FC_DBGTMDS2},
4496*4882a593Smuzhiyun {HDMI_PHY_CONF0, HDMI_PHY_POL0},
4497*4882a593Smuzhiyun {HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR},
4498*4882a593Smuzhiyun {HDMI_AUD_CONF0, 0x3624},
4499*4882a593Smuzhiyun {HDMI_MC_SFRDIV, HDMI_MC_HEACPHY_RST},
4500*4882a593Smuzhiyun {HDMI_CSC_CFG, HDMI_CSC_COEF_C4_LSB},
4501*4882a593Smuzhiyun {HDMI_A_HDCPCFG0, 0x52bb},
4502*4882a593Smuzhiyun {0x7800, 0x7818},
4503*4882a593Smuzhiyun {0x7900, 0x790e},
4504*4882a593Smuzhiyun {HDMI_CEC_CTRL, HDMI_CEC_WKUPCTRL},
4505*4882a593Smuzhiyun {HDMI_I2CM_SLAVE, 0x7e31},
4506*4882a593Smuzhiyun };
4507*4882a593Smuzhiyun
dw_hdmi_ctrl_show(struct seq_file * s,void * v)4508*4882a593Smuzhiyun static int dw_hdmi_ctrl_show(struct seq_file *s, void *v)
4509*4882a593Smuzhiyun {
4510*4882a593Smuzhiyun struct dw_hdmi *hdmi = s->private;
4511*4882a593Smuzhiyun u32 i = 0, j = 0, val = 0;
4512*4882a593Smuzhiyun
4513*4882a593Smuzhiyun seq_puts(s, "\n>>>hdmi_ctl reg ");
4514*4882a593Smuzhiyun for (i = 0; i < 16; i++)
4515*4882a593Smuzhiyun seq_printf(s, " %2x", i);
4516*4882a593Smuzhiyun seq_puts(s, "\n---------------------------------------------------");
4517*4882a593Smuzhiyun
4518*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
4519*4882a593Smuzhiyun for (j = hdmi_reg_table[i].reg_base;
4520*4882a593Smuzhiyun j <= hdmi_reg_table[i].reg_end; j++) {
4521*4882a593Smuzhiyun val = hdmi_readb(hdmi, j);
4522*4882a593Smuzhiyun if ((j - hdmi_reg_table[i].reg_base) % 16 == 0)
4523*4882a593Smuzhiyun seq_printf(s, "\n>>>hdmi_ctl %04x:", j);
4524*4882a593Smuzhiyun seq_printf(s, " %02x", val);
4525*4882a593Smuzhiyun }
4526*4882a593Smuzhiyun }
4527*4882a593Smuzhiyun seq_puts(s, "\n---------------------------------------------------\n");
4528*4882a593Smuzhiyun
4529*4882a593Smuzhiyun return 0;
4530*4882a593Smuzhiyun }
4531*4882a593Smuzhiyun
dw_hdmi_ctrl_open(struct inode * inode,struct file * file)4532*4882a593Smuzhiyun static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file)
4533*4882a593Smuzhiyun {
4534*4882a593Smuzhiyun return single_open(file, dw_hdmi_ctrl_show, inode->i_private);
4535*4882a593Smuzhiyun }
4536*4882a593Smuzhiyun
4537*4882a593Smuzhiyun static ssize_t
dw_hdmi_ctrl_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)4538*4882a593Smuzhiyun dw_hdmi_ctrl_write(struct file *file, const char __user *buf,
4539*4882a593Smuzhiyun size_t count, loff_t *ppos)
4540*4882a593Smuzhiyun {
4541*4882a593Smuzhiyun struct dw_hdmi *hdmi =
4542*4882a593Smuzhiyun ((struct seq_file *)file->private_data)->private;
4543*4882a593Smuzhiyun u32 reg, val;
4544*4882a593Smuzhiyun char kbuf[25];
4545*4882a593Smuzhiyun
4546*4882a593Smuzhiyun if (copy_from_user(kbuf, buf, count))
4547*4882a593Smuzhiyun return -EFAULT;
4548*4882a593Smuzhiyun if (sscanf(kbuf, "%x%x", ®, &val) == -1)
4549*4882a593Smuzhiyun return -EFAULT;
4550*4882a593Smuzhiyun if (reg > HDMI_I2CM_FS_SCL_LCNT_0_ADDR) {
4551*4882a593Smuzhiyun dev_err(hdmi->dev, "it is no a hdmi register\n");
4552*4882a593Smuzhiyun return count;
4553*4882a593Smuzhiyun }
4554*4882a593Smuzhiyun dev_info(hdmi->dev, "/**********hdmi register config******/");
4555*4882a593Smuzhiyun dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
4556*4882a593Smuzhiyun hdmi_writeb(hdmi, val, reg);
4557*4882a593Smuzhiyun return count;
4558*4882a593Smuzhiyun }
4559*4882a593Smuzhiyun
4560*4882a593Smuzhiyun static const struct file_operations dw_hdmi_ctrl_fops = {
4561*4882a593Smuzhiyun .owner = THIS_MODULE,
4562*4882a593Smuzhiyun .open = dw_hdmi_ctrl_open,
4563*4882a593Smuzhiyun .read = seq_read,
4564*4882a593Smuzhiyun .write = dw_hdmi_ctrl_write,
4565*4882a593Smuzhiyun .llseek = seq_lseek,
4566*4882a593Smuzhiyun .release = single_release,
4567*4882a593Smuzhiyun };
4568*4882a593Smuzhiyun
dw_hdmi_phy_show(struct seq_file * s,void * v)4569*4882a593Smuzhiyun static int dw_hdmi_phy_show(struct seq_file *s, void *v)
4570*4882a593Smuzhiyun {
4571*4882a593Smuzhiyun struct dw_hdmi *hdmi = s->private;
4572*4882a593Smuzhiyun u32 i;
4573*4882a593Smuzhiyun
4574*4882a593Smuzhiyun seq_puts(s, "\n>>>hdmi_phy reg ");
4575*4882a593Smuzhiyun for (i = 0; i < 0x28; i++)
4576*4882a593Smuzhiyun seq_printf(s, "regs %02x val %04x\n",
4577*4882a593Smuzhiyun i, hdmi_phy_i2c_read(hdmi, i));
4578*4882a593Smuzhiyun return 0;
4579*4882a593Smuzhiyun }
4580*4882a593Smuzhiyun
dw_hdmi_phy_open(struct inode * inode,struct file * file)4581*4882a593Smuzhiyun static int dw_hdmi_phy_open(struct inode *inode, struct file *file)
4582*4882a593Smuzhiyun {
4583*4882a593Smuzhiyun return single_open(file, dw_hdmi_phy_show, inode->i_private);
4584*4882a593Smuzhiyun }
4585*4882a593Smuzhiyun
4586*4882a593Smuzhiyun static ssize_t
dw_hdmi_phy_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)4587*4882a593Smuzhiyun dw_hdmi_phy_write(struct file *file, const char __user *buf,
4588*4882a593Smuzhiyun size_t count, loff_t *ppos)
4589*4882a593Smuzhiyun {
4590*4882a593Smuzhiyun struct dw_hdmi *hdmi =
4591*4882a593Smuzhiyun ((struct seq_file *)file->private_data)->private;
4592*4882a593Smuzhiyun u32 reg, val;
4593*4882a593Smuzhiyun char kbuf[25];
4594*4882a593Smuzhiyun
4595*4882a593Smuzhiyun if (copy_from_user(kbuf, buf, count))
4596*4882a593Smuzhiyun return -EFAULT;
4597*4882a593Smuzhiyun if (sscanf(kbuf, "%x%x", ®, &val) == -1)
4598*4882a593Smuzhiyun return -EFAULT;
4599*4882a593Smuzhiyun if (reg > 0x28) {
4600*4882a593Smuzhiyun dev_err(hdmi->dev, "it is not a hdmi phy register\n");
4601*4882a593Smuzhiyun return count;
4602*4882a593Smuzhiyun }
4603*4882a593Smuzhiyun dev_info(hdmi->dev, "/*******hdmi phy register config******/");
4604*4882a593Smuzhiyun dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
4605*4882a593Smuzhiyun dw_hdmi_phy_i2c_write(hdmi, val, reg);
4606*4882a593Smuzhiyun return count;
4607*4882a593Smuzhiyun }
4608*4882a593Smuzhiyun
4609*4882a593Smuzhiyun static const struct file_operations dw_hdmi_phy_fops = {
4610*4882a593Smuzhiyun .owner = THIS_MODULE,
4611*4882a593Smuzhiyun .open = dw_hdmi_phy_open,
4612*4882a593Smuzhiyun .read = seq_read,
4613*4882a593Smuzhiyun .write = dw_hdmi_phy_write,
4614*4882a593Smuzhiyun .llseek = seq_lseek,
4615*4882a593Smuzhiyun .release = single_release,
4616*4882a593Smuzhiyun };
4617*4882a593Smuzhiyun
dw_hdmi_register_debugfs(struct device * dev,struct dw_hdmi * hdmi)4618*4882a593Smuzhiyun static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi)
4619*4882a593Smuzhiyun {
4620*4882a593Smuzhiyun hdmi->debugfs_dir = debugfs_create_dir("dw-hdmi", NULL);
4621*4882a593Smuzhiyun if (IS_ERR(hdmi->debugfs_dir)) {
4622*4882a593Smuzhiyun dev_err(dev, "failed to create debugfs dir!\n");
4623*4882a593Smuzhiyun return;
4624*4882a593Smuzhiyun }
4625*4882a593Smuzhiyun debugfs_create_file("status", 0400, hdmi->debugfs_dir,
4626*4882a593Smuzhiyun hdmi, &dw_hdmi_status_fops);
4627*4882a593Smuzhiyun debugfs_create_file("ctrl", 0400, hdmi->debugfs_dir,
4628*4882a593Smuzhiyun hdmi, &dw_hdmi_ctrl_fops);
4629*4882a593Smuzhiyun debugfs_create_file("phy", 0400, hdmi->debugfs_dir,
4630*4882a593Smuzhiyun hdmi, &dw_hdmi_phy_fops);
4631*4882a593Smuzhiyun }
4632*4882a593Smuzhiyun
dw_hdmi_register_hdcp(struct device * dev,struct dw_hdmi * hdmi,u32 val,bool hdcp1x_enable)4633*4882a593Smuzhiyun static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi,
4634*4882a593Smuzhiyun u32 val, bool hdcp1x_enable)
4635*4882a593Smuzhiyun {
4636*4882a593Smuzhiyun struct dw_hdcp hdmi_hdcp = {
4637*4882a593Smuzhiyun .hdmi = hdmi,
4638*4882a593Smuzhiyun .write = hdmi_writeb,
4639*4882a593Smuzhiyun .read = hdmi_readb,
4640*4882a593Smuzhiyun .regs = hdmi->regs,
4641*4882a593Smuzhiyun .reg_io_width = val,
4642*4882a593Smuzhiyun .enable = hdcp1x_enable,
4643*4882a593Smuzhiyun };
4644*4882a593Smuzhiyun struct platform_device_info hdcp_device_info = {
4645*4882a593Smuzhiyun .parent = dev,
4646*4882a593Smuzhiyun .id = PLATFORM_DEVID_AUTO,
4647*4882a593Smuzhiyun .res = NULL,
4648*4882a593Smuzhiyun .num_res = 0,
4649*4882a593Smuzhiyun .name = DW_HDCP_DRIVER_NAME,
4650*4882a593Smuzhiyun .data = &hdmi_hdcp,
4651*4882a593Smuzhiyun .size_data = sizeof(hdmi_hdcp),
4652*4882a593Smuzhiyun .dma_mask = DMA_BIT_MASK(32),
4653*4882a593Smuzhiyun };
4654*4882a593Smuzhiyun
4655*4882a593Smuzhiyun hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
4656*4882a593Smuzhiyun if (IS_ERR(hdmi->hdcp_dev))
4657*4882a593Smuzhiyun dev_err(dev, "failed to register hdcp!\n");
4658*4882a593Smuzhiyun else
4659*4882a593Smuzhiyun hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
4660*4882a593Smuzhiyun }
4661*4882a593Smuzhiyun
get_force_logo_property(struct dw_hdmi * hdmi)4662*4882a593Smuzhiyun static int get_force_logo_property(struct dw_hdmi *hdmi)
4663*4882a593Smuzhiyun {
4664*4882a593Smuzhiyun struct device_node *dss;
4665*4882a593Smuzhiyun struct device_node *route;
4666*4882a593Smuzhiyun struct device_node *route_hdmi;
4667*4882a593Smuzhiyun
4668*4882a593Smuzhiyun dss = of_find_node_by_name(NULL, "display-subsystem");
4669*4882a593Smuzhiyun if (!dss) {
4670*4882a593Smuzhiyun dev_err(hdmi->dev, "can't find display-subsystem\n");
4671*4882a593Smuzhiyun return -ENODEV;
4672*4882a593Smuzhiyun }
4673*4882a593Smuzhiyun
4674*4882a593Smuzhiyun route = of_find_node_by_name(dss, "route");
4675*4882a593Smuzhiyun if (!route) {
4676*4882a593Smuzhiyun dev_err(hdmi->dev, "can't find route\n");
4677*4882a593Smuzhiyun of_node_put(dss);
4678*4882a593Smuzhiyun return -ENODEV;
4679*4882a593Smuzhiyun }
4680*4882a593Smuzhiyun of_node_put(dss);
4681*4882a593Smuzhiyun
4682*4882a593Smuzhiyun route_hdmi = of_find_node_by_name(route, "route-hdmi");
4683*4882a593Smuzhiyun if (!route_hdmi) {
4684*4882a593Smuzhiyun dev_err(hdmi->dev, "can't find route-hdmi\n");
4685*4882a593Smuzhiyun of_node_put(route);
4686*4882a593Smuzhiyun return -ENODEV;
4687*4882a593Smuzhiyun }
4688*4882a593Smuzhiyun of_node_put(route);
4689*4882a593Smuzhiyun
4690*4882a593Smuzhiyun hdmi->force_logo =
4691*4882a593Smuzhiyun of_property_read_bool(route_hdmi, "force-output");
4692*4882a593Smuzhiyun
4693*4882a593Smuzhiyun of_node_put(route_hdmi);
4694*4882a593Smuzhiyun
4695*4882a593Smuzhiyun return 0;
4696*4882a593Smuzhiyun }
4697*4882a593Smuzhiyun
4698*4882a593Smuzhiyun void
dw_hdmi_cec_wake_ops_register(struct dw_hdmi * hdmi,const struct dw_hdmi_cec_wake_ops * cec_ops)4699*4882a593Smuzhiyun dw_hdmi_cec_wake_ops_register(struct dw_hdmi *hdmi, const struct dw_hdmi_cec_wake_ops *cec_ops)
4700*4882a593Smuzhiyun {
4701*4882a593Smuzhiyun if (!cec_ops || !hdmi)
4702*4882a593Smuzhiyun return;
4703*4882a593Smuzhiyun
4704*4882a593Smuzhiyun hdmi->cec_ops = cec_ops;
4705*4882a593Smuzhiyun }
4706*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_cec_wake_ops_register);
4707*4882a593Smuzhiyun
4708*4882a593Smuzhiyun
4709*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
4710*4882a593Smuzhiyun * Probe/remove API, used from platforms based on the DRM bridge API.
4711*4882a593Smuzhiyun */
dw_hdmi_probe(struct platform_device * pdev,const struct dw_hdmi_plat_data * plat_data)4712*4882a593Smuzhiyun struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
4713*4882a593Smuzhiyun const struct dw_hdmi_plat_data *plat_data)
4714*4882a593Smuzhiyun {
4715*4882a593Smuzhiyun struct device *dev = &pdev->dev;
4716*4882a593Smuzhiyun struct device_node *np = dev->of_node;
4717*4882a593Smuzhiyun struct device_node *endpoint;
4718*4882a593Smuzhiyun struct platform_device_info pdevinfo;
4719*4882a593Smuzhiyun struct device_node *ddc_node;
4720*4882a593Smuzhiyun struct dw_hdmi_cec_data cec;
4721*4882a593Smuzhiyun struct dw_hdmi *hdmi;
4722*4882a593Smuzhiyun struct resource *iores = NULL;
4723*4882a593Smuzhiyun int irq;
4724*4882a593Smuzhiyun int ret;
4725*4882a593Smuzhiyun u32 val = 1;
4726*4882a593Smuzhiyun u8 prod_id0;
4727*4882a593Smuzhiyun u8 prod_id1;
4728*4882a593Smuzhiyun u8 config0;
4729*4882a593Smuzhiyun u8 config3;
4730*4882a593Smuzhiyun bool hdcp1x_enable = 0;
4731*4882a593Smuzhiyun
4732*4882a593Smuzhiyun hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
4733*4882a593Smuzhiyun if (!hdmi)
4734*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
4735*4882a593Smuzhiyun
4736*4882a593Smuzhiyun hdmi->connector.stereo_allowed = 1;
4737*4882a593Smuzhiyun hdmi->plat_data = plat_data;
4738*4882a593Smuzhiyun hdmi->dev = dev;
4739*4882a593Smuzhiyun hdmi->sample_rate = 48000;
4740*4882a593Smuzhiyun hdmi->disabled = true;
4741*4882a593Smuzhiyun hdmi->rxsense = true;
4742*4882a593Smuzhiyun hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
4743*4882a593Smuzhiyun hdmi->mc_clkdis = 0x7f;
4744*4882a593Smuzhiyun hdmi->last_connector_result = connector_status_disconnected;
4745*4882a593Smuzhiyun
4746*4882a593Smuzhiyun mutex_init(&hdmi->mutex);
4747*4882a593Smuzhiyun mutex_init(&hdmi->audio_mutex);
4748*4882a593Smuzhiyun mutex_init(&hdmi->cec_notifier_mutex);
4749*4882a593Smuzhiyun spin_lock_init(&hdmi->audio_lock);
4750*4882a593Smuzhiyun
4751*4882a593Smuzhiyun ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
4752*4882a593Smuzhiyun if (ddc_node) {
4753*4882a593Smuzhiyun hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
4754*4882a593Smuzhiyun of_node_put(ddc_node);
4755*4882a593Smuzhiyun if (!hdmi->ddc) {
4756*4882a593Smuzhiyun dev_dbg(hdmi->dev, "failed to read ddc node\n");
4757*4882a593Smuzhiyun return ERR_PTR(-EPROBE_DEFER);
4758*4882a593Smuzhiyun }
4759*4882a593Smuzhiyun
4760*4882a593Smuzhiyun } else {
4761*4882a593Smuzhiyun dev_dbg(hdmi->dev, "no ddc property found\n");
4762*4882a593Smuzhiyun }
4763*4882a593Smuzhiyun
4764*4882a593Smuzhiyun if (!plat_data->regm) {
4765*4882a593Smuzhiyun const struct regmap_config *reg_config;
4766*4882a593Smuzhiyun
4767*4882a593Smuzhiyun of_property_read_u32(np, "reg-io-width", &val);
4768*4882a593Smuzhiyun switch (val) {
4769*4882a593Smuzhiyun case 4:
4770*4882a593Smuzhiyun reg_config = &hdmi_regmap_32bit_config;
4771*4882a593Smuzhiyun hdmi->reg_shift = 2;
4772*4882a593Smuzhiyun break;
4773*4882a593Smuzhiyun case 1:
4774*4882a593Smuzhiyun reg_config = &hdmi_regmap_8bit_config;
4775*4882a593Smuzhiyun break;
4776*4882a593Smuzhiyun default:
4777*4882a593Smuzhiyun dev_err(dev, "reg-io-width must be 1 or 4\n");
4778*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
4779*4882a593Smuzhiyun }
4780*4882a593Smuzhiyun
4781*4882a593Smuzhiyun iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4782*4882a593Smuzhiyun hdmi->regs = devm_ioremap_resource(dev, iores);
4783*4882a593Smuzhiyun if (IS_ERR(hdmi->regs)) {
4784*4882a593Smuzhiyun ret = PTR_ERR(hdmi->regs);
4785*4882a593Smuzhiyun goto err_res;
4786*4882a593Smuzhiyun }
4787*4882a593Smuzhiyun
4788*4882a593Smuzhiyun hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config);
4789*4882a593Smuzhiyun if (IS_ERR(hdmi->regm)) {
4790*4882a593Smuzhiyun dev_err(dev, "Failed to configure regmap\n");
4791*4882a593Smuzhiyun ret = PTR_ERR(hdmi->regm);
4792*4882a593Smuzhiyun goto err_res;
4793*4882a593Smuzhiyun }
4794*4882a593Smuzhiyun } else {
4795*4882a593Smuzhiyun hdmi->regm = plat_data->regm;
4796*4882a593Smuzhiyun }
4797*4882a593Smuzhiyun
4798*4882a593Smuzhiyun hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
4799*4882a593Smuzhiyun if (IS_ERR(hdmi->isfr_clk)) {
4800*4882a593Smuzhiyun ret = PTR_ERR(hdmi->isfr_clk);
4801*4882a593Smuzhiyun dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
4802*4882a593Smuzhiyun goto err_res;
4803*4882a593Smuzhiyun }
4804*4882a593Smuzhiyun
4805*4882a593Smuzhiyun ret = clk_prepare_enable(hdmi->isfr_clk);
4806*4882a593Smuzhiyun if (ret) {
4807*4882a593Smuzhiyun dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
4808*4882a593Smuzhiyun goto err_res;
4809*4882a593Smuzhiyun }
4810*4882a593Smuzhiyun
4811*4882a593Smuzhiyun hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
4812*4882a593Smuzhiyun if (IS_ERR(hdmi->iahb_clk)) {
4813*4882a593Smuzhiyun ret = PTR_ERR(hdmi->iahb_clk);
4814*4882a593Smuzhiyun dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
4815*4882a593Smuzhiyun goto err_isfr;
4816*4882a593Smuzhiyun }
4817*4882a593Smuzhiyun
4818*4882a593Smuzhiyun ret = clk_prepare_enable(hdmi->iahb_clk);
4819*4882a593Smuzhiyun if (ret) {
4820*4882a593Smuzhiyun dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
4821*4882a593Smuzhiyun goto err_isfr;
4822*4882a593Smuzhiyun }
4823*4882a593Smuzhiyun
4824*4882a593Smuzhiyun hdmi->cec_clk = devm_clk_get(hdmi->dev, "cec");
4825*4882a593Smuzhiyun if (PTR_ERR(hdmi->cec_clk) == -ENOENT) {
4826*4882a593Smuzhiyun hdmi->cec_clk = NULL;
4827*4882a593Smuzhiyun } else if (IS_ERR(hdmi->cec_clk)) {
4828*4882a593Smuzhiyun ret = PTR_ERR(hdmi->cec_clk);
4829*4882a593Smuzhiyun if (ret != -EPROBE_DEFER)
4830*4882a593Smuzhiyun dev_err(hdmi->dev, "Cannot get HDMI cec clock: %d\n",
4831*4882a593Smuzhiyun ret);
4832*4882a593Smuzhiyun
4833*4882a593Smuzhiyun hdmi->cec_clk = NULL;
4834*4882a593Smuzhiyun goto err_iahb;
4835*4882a593Smuzhiyun } else {
4836*4882a593Smuzhiyun ret = clk_prepare_enable(hdmi->cec_clk);
4837*4882a593Smuzhiyun if (ret) {
4838*4882a593Smuzhiyun dev_err(hdmi->dev, "Cannot enable HDMI cec clock: %d\n",
4839*4882a593Smuzhiyun ret);
4840*4882a593Smuzhiyun goto err_iahb;
4841*4882a593Smuzhiyun }
4842*4882a593Smuzhiyun }
4843*4882a593Smuzhiyun
4844*4882a593Smuzhiyun /* Product and revision IDs */
4845*4882a593Smuzhiyun hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
4846*4882a593Smuzhiyun | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
4847*4882a593Smuzhiyun prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0);
4848*4882a593Smuzhiyun prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1);
4849*4882a593Smuzhiyun
4850*4882a593Smuzhiyun if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX ||
4851*4882a593Smuzhiyun (prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) {
4852*4882a593Smuzhiyun dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n",
4853*4882a593Smuzhiyun hdmi->version, prod_id0, prod_id1);
4854*4882a593Smuzhiyun ret = -ENODEV;
4855*4882a593Smuzhiyun goto err_iahb;
4856*4882a593Smuzhiyun }
4857*4882a593Smuzhiyun
4858*4882a593Smuzhiyun ret = dw_hdmi_detect_phy(hdmi);
4859*4882a593Smuzhiyun if (ret < 0)
4860*4882a593Smuzhiyun goto err_iahb;
4861*4882a593Smuzhiyun
4862*4882a593Smuzhiyun dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n",
4863*4882a593Smuzhiyun hdmi->version >> 12, hdmi->version & 0xfff,
4864*4882a593Smuzhiyun prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
4865*4882a593Smuzhiyun hdmi->phy.name);
4866*4882a593Smuzhiyun
4867*4882a593Smuzhiyun ret = get_force_logo_property(hdmi);
4868*4882a593Smuzhiyun if (ret)
4869*4882a593Smuzhiyun goto err_iahb;
4870*4882a593Smuzhiyun
4871*4882a593Smuzhiyun hdmi->logo_plug_out = false;
4872*4882a593Smuzhiyun hdmi->initialized = false;
4873*4882a593Smuzhiyun ret = hdmi_readb(hdmi, HDMI_PHY_STAT0);
4874*4882a593Smuzhiyun if (((ret & HDMI_PHY_TX_PHY_LOCK) && (ret & HDMI_PHY_HPD) &&
4875*4882a593Smuzhiyun hdmi_readb(hdmi, HDMI_FC_EXCTRLDUR)) || hdmi->force_logo) {
4876*4882a593Smuzhiyun hdmi->mc_clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS);
4877*4882a593Smuzhiyun hdmi->disabled = false;
4878*4882a593Smuzhiyun hdmi->bridge_is_on = true;
4879*4882a593Smuzhiyun hdmi->phy.enabled = true;
4880*4882a593Smuzhiyun hdmi->initialized = true;
4881*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
4882*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(hdmi->plat_data->phy_data, true);
4883*4882a593Smuzhiyun if (hdmi->plat_data->dclk_set)
4884*4882a593Smuzhiyun hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0);
4885*4882a593Smuzhiyun } else if (ret & HDMI_PHY_TX_PHY_LOCK) {
4886*4882a593Smuzhiyun hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
4887*4882a593Smuzhiyun if (hdmi->plat_data->set_ddc_io)
4888*4882a593Smuzhiyun hdmi->plat_data->set_ddc_io(hdmi->plat_data->phy_data, false);
4889*4882a593Smuzhiyun }
4890*4882a593Smuzhiyun
4891*4882a593Smuzhiyun init_hpd_work(hdmi);
4892*4882a593Smuzhiyun
4893*4882a593Smuzhiyun irq = platform_get_irq(pdev, 0);
4894*4882a593Smuzhiyun if (irq < 0) {
4895*4882a593Smuzhiyun ret = irq;
4896*4882a593Smuzhiyun goto err_iahb;
4897*4882a593Smuzhiyun }
4898*4882a593Smuzhiyun
4899*4882a593Smuzhiyun hdmi->irq = irq;
4900*4882a593Smuzhiyun ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
4901*4882a593Smuzhiyun dw_hdmi_irq, IRQF_SHARED | IRQF_ONESHOT,
4902*4882a593Smuzhiyun dev_name(dev), hdmi);
4903*4882a593Smuzhiyun if (ret)
4904*4882a593Smuzhiyun goto err_iahb;
4905*4882a593Smuzhiyun
4906*4882a593Smuzhiyun /*
4907*4882a593Smuzhiyun * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
4908*4882a593Smuzhiyun * N and cts values before enabling phy
4909*4882a593Smuzhiyun */
4910*4882a593Smuzhiyun hdmi_init_clk_regenerator(hdmi);
4911*4882a593Smuzhiyun
4912*4882a593Smuzhiyun /* If DDC bus is not specified, try to register HDMI I2C bus */
4913*4882a593Smuzhiyun if (!hdmi->ddc) {
4914*4882a593Smuzhiyun /* Look for (optional) stuff related to unwedging */
4915*4882a593Smuzhiyun hdmi->pinctrl = devm_pinctrl_get(dev);
4916*4882a593Smuzhiyun if (!IS_ERR(hdmi->pinctrl)) {
4917*4882a593Smuzhiyun hdmi->unwedge_state =
4918*4882a593Smuzhiyun pinctrl_lookup_state(hdmi->pinctrl, "unwedge");
4919*4882a593Smuzhiyun hdmi->default_state =
4920*4882a593Smuzhiyun pinctrl_lookup_state(hdmi->pinctrl, "default");
4921*4882a593Smuzhiyun
4922*4882a593Smuzhiyun if (IS_ERR(hdmi->default_state) ||
4923*4882a593Smuzhiyun IS_ERR(hdmi->unwedge_state)) {
4924*4882a593Smuzhiyun if (!IS_ERR(hdmi->unwedge_state))
4925*4882a593Smuzhiyun dev_warn(dev,
4926*4882a593Smuzhiyun "Unwedge requires default pinctrl\n");
4927*4882a593Smuzhiyun hdmi->default_state = NULL;
4928*4882a593Smuzhiyun hdmi->unwedge_state = NULL;
4929*4882a593Smuzhiyun }
4930*4882a593Smuzhiyun }
4931*4882a593Smuzhiyun
4932*4882a593Smuzhiyun hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
4933*4882a593Smuzhiyun if (IS_ERR(hdmi->ddc))
4934*4882a593Smuzhiyun hdmi->ddc = NULL;
4935*4882a593Smuzhiyun /*
4936*4882a593Smuzhiyun * Read high and low time from device tree. If not available use
4937*4882a593Smuzhiyun * the default timing scl clock rate is about 99.6KHz.
4938*4882a593Smuzhiyun */
4939*4882a593Smuzhiyun if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns",
4940*4882a593Smuzhiyun &hdmi->i2c->scl_high_ns))
4941*4882a593Smuzhiyun hdmi->i2c->scl_high_ns = 4708;
4942*4882a593Smuzhiyun if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns",
4943*4882a593Smuzhiyun &hdmi->i2c->scl_low_ns))
4944*4882a593Smuzhiyun hdmi->i2c->scl_low_ns = 4916;
4945*4882a593Smuzhiyun }
4946*4882a593Smuzhiyun
4947*4882a593Smuzhiyun dw_hdmi_init_hw(hdmi);
4948*4882a593Smuzhiyun
4949*4882a593Smuzhiyun hdmi->bridge.driver_private = hdmi;
4950*4882a593Smuzhiyun hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
4951*4882a593Smuzhiyun hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
4952*4882a593Smuzhiyun | DRM_BRIDGE_OP_HPD;
4953*4882a593Smuzhiyun #ifdef CONFIG_OF
4954*4882a593Smuzhiyun hdmi->bridge.of_node = pdev->dev.of_node;
4955*4882a593Smuzhiyun #endif
4956*4882a593Smuzhiyun
4957*4882a593Smuzhiyun endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, 1, -1);
4958*4882a593Smuzhiyun if (endpoint && of_device_is_available(endpoint)) {
4959*4882a593Smuzhiyun struct device_node *remote;
4960*4882a593Smuzhiyun
4961*4882a593Smuzhiyun remote = of_graph_get_remote_port_parent(endpoint);
4962*4882a593Smuzhiyun of_node_put(endpoint);
4963*4882a593Smuzhiyun if (!remote || !of_device_is_available(remote)) {
4964*4882a593Smuzhiyun of_node_put(remote);
4965*4882a593Smuzhiyun ret = -ENODEV;
4966*4882a593Smuzhiyun goto err_iahb;
4967*4882a593Smuzhiyun }
4968*4882a593Smuzhiyun
4969*4882a593Smuzhiyun hdmi->next_bridge = of_drm_find_bridge(remote);
4970*4882a593Smuzhiyun of_node_put(remote);
4971*4882a593Smuzhiyun if (!hdmi->next_bridge) {
4972*4882a593Smuzhiyun dev_err(hdmi->dev, "can't find next bridge\n");
4973*4882a593Smuzhiyun ret = -EPROBE_DEFER;
4974*4882a593Smuzhiyun goto err_iahb;
4975*4882a593Smuzhiyun }
4976*4882a593Smuzhiyun
4977*4882a593Smuzhiyun hdmi->sink_is_hdmi = true;
4978*4882a593Smuzhiyun hdmi->sink_has_audio = true;
4979*4882a593Smuzhiyun }
4980*4882a593Smuzhiyun
4981*4882a593Smuzhiyun memset(&pdevinfo, 0, sizeof(pdevinfo));
4982*4882a593Smuzhiyun pdevinfo.parent = dev;
4983*4882a593Smuzhiyun pdevinfo.id = PLATFORM_DEVID_AUTO;
4984*4882a593Smuzhiyun
4985*4882a593Smuzhiyun config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID);
4986*4882a593Smuzhiyun config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
4987*4882a593Smuzhiyun
4988*4882a593Smuzhiyun if (iores && config3 & HDMI_CONFIG3_AHBAUDDMA) {
4989*4882a593Smuzhiyun struct dw_hdmi_audio_data audio;
4990*4882a593Smuzhiyun
4991*4882a593Smuzhiyun audio.phys = iores->start;
4992*4882a593Smuzhiyun audio.base = hdmi->regs;
4993*4882a593Smuzhiyun audio.irq = irq;
4994*4882a593Smuzhiyun audio.hdmi = hdmi;
4995*4882a593Smuzhiyun audio.get_eld = hdmi_audio_get_eld;
4996*4882a593Smuzhiyun hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
4997*4882a593Smuzhiyun hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
4998*4882a593Smuzhiyun
4999*4882a593Smuzhiyun pdevinfo.name = "dw-hdmi-ahb-audio";
5000*4882a593Smuzhiyun pdevinfo.data = &audio;
5001*4882a593Smuzhiyun pdevinfo.size_data = sizeof(audio);
5002*4882a593Smuzhiyun pdevinfo.dma_mask = DMA_BIT_MASK(32);
5003*4882a593Smuzhiyun hdmi->audio = platform_device_register_full(&pdevinfo);
5004*4882a593Smuzhiyun } else if (config0 & HDMI_CONFIG0_I2S) {
5005*4882a593Smuzhiyun struct dw_hdmi_i2s_audio_data audio;
5006*4882a593Smuzhiyun
5007*4882a593Smuzhiyun audio.hdmi = hdmi;
5008*4882a593Smuzhiyun audio.get_eld = hdmi_audio_get_eld;
5009*4882a593Smuzhiyun audio.write = hdmi_writeb;
5010*4882a593Smuzhiyun audio.read = hdmi_readb;
5011*4882a593Smuzhiyun hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
5012*4882a593Smuzhiyun hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
5013*4882a593Smuzhiyun
5014*4882a593Smuzhiyun pdevinfo.name = "dw-hdmi-i2s-audio";
5015*4882a593Smuzhiyun pdevinfo.data = &audio;
5016*4882a593Smuzhiyun pdevinfo.size_data = sizeof(audio);
5017*4882a593Smuzhiyun pdevinfo.dma_mask = DMA_BIT_MASK(32);
5018*4882a593Smuzhiyun hdmi->audio = platform_device_register_full(&pdevinfo);
5019*4882a593Smuzhiyun }
5020*4882a593Smuzhiyun
5021*4882a593Smuzhiyun if (config0 & HDMI_CONFIG0_CEC) {
5022*4882a593Smuzhiyun cec.hdmi = hdmi;
5023*4882a593Smuzhiyun cec.ops = &dw_hdmi_cec_ops;
5024*4882a593Smuzhiyun cec.irq = irq;
5025*4882a593Smuzhiyun
5026*4882a593Smuzhiyun irq = platform_get_irq(pdev, 1);
5027*4882a593Smuzhiyun if (irq < 0)
5028*4882a593Smuzhiyun dev_dbg(hdmi->dev, "can't get cec wake up irq\n");
5029*4882a593Smuzhiyun
5030*4882a593Smuzhiyun cec.wake_irq = irq;
5031*4882a593Smuzhiyun
5032*4882a593Smuzhiyun pdevinfo.name = "dw-hdmi-cec";
5033*4882a593Smuzhiyun pdevinfo.data = &cec;
5034*4882a593Smuzhiyun pdevinfo.size_data = sizeof(cec);
5035*4882a593Smuzhiyun pdevinfo.dma_mask = 0;
5036*4882a593Smuzhiyun
5037*4882a593Smuzhiyun hdmi->cec = platform_device_register_full(&pdevinfo);
5038*4882a593Smuzhiyun }
5039*4882a593Smuzhiyun
5040*4882a593Smuzhiyun hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable);
5041*4882a593Smuzhiyun if (IS_ERR(hdmi->extcon)) {
5042*4882a593Smuzhiyun ret = PTR_ERR(hdmi->extcon);
5043*4882a593Smuzhiyun dev_err(hdmi->dev, "allocate extcon failed: %d\n", ret);
5044*4882a593Smuzhiyun goto err_iahb;
5045*4882a593Smuzhiyun }
5046*4882a593Smuzhiyun
5047*4882a593Smuzhiyun ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon);
5048*4882a593Smuzhiyun if (ret) {
5049*4882a593Smuzhiyun dev_err(hdmi->dev, "failed to register extcon: %d\n",
5050*4882a593Smuzhiyun ret);
5051*4882a593Smuzhiyun goto err_iahb;
5052*4882a593Smuzhiyun }
5053*4882a593Smuzhiyun
5054*4882a593Smuzhiyun ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI,
5055*4882a593Smuzhiyun EXTCON_PROP_DISP_HPD);
5056*4882a593Smuzhiyun if (ret) {
5057*4882a593Smuzhiyun dev_err(hdmi->dev,
5058*4882a593Smuzhiyun "failed to set USB property capability: %d\n",
5059*4882a593Smuzhiyun ret);
5060*4882a593Smuzhiyun goto err_iahb;
5061*4882a593Smuzhiyun }
5062*4882a593Smuzhiyun
5063*4882a593Smuzhiyun drm_bridge_add(&hdmi->bridge);
5064*4882a593Smuzhiyun
5065*4882a593Smuzhiyun dw_hdmi_register_debugfs(dev, hdmi);
5066*4882a593Smuzhiyun
5067*4882a593Smuzhiyun if (of_property_read_bool(np, "scramble-low-rates"))
5068*4882a593Smuzhiyun hdmi->scramble_low_rates = true;
5069*4882a593Smuzhiyun
5070*4882a593Smuzhiyun if (of_property_read_bool(np, "hdcp1x-enable"))
5071*4882a593Smuzhiyun hdcp1x_enable = 1;
5072*4882a593Smuzhiyun dw_hdmi_register_hdcp(dev, hdmi, val, hdcp1x_enable);
5073*4882a593Smuzhiyun
5074*4882a593Smuzhiyun return hdmi;
5075*4882a593Smuzhiyun
5076*4882a593Smuzhiyun err_iahb:
5077*4882a593Smuzhiyun clk_disable_unprepare(hdmi->iahb_clk);
5078*4882a593Smuzhiyun if (hdmi->cec_clk)
5079*4882a593Smuzhiyun clk_disable_unprepare(hdmi->cec_clk);
5080*4882a593Smuzhiyun err_isfr:
5081*4882a593Smuzhiyun clk_disable_unprepare(hdmi->isfr_clk);
5082*4882a593Smuzhiyun err_res:
5083*4882a593Smuzhiyun if (hdmi->i2c)
5084*4882a593Smuzhiyun i2c_del_adapter(&hdmi->i2c->adap);
5085*4882a593Smuzhiyun else
5086*4882a593Smuzhiyun i2c_put_adapter(hdmi->ddc);
5087*4882a593Smuzhiyun
5088*4882a593Smuzhiyun return ERR_PTR(ret);
5089*4882a593Smuzhiyun }
5090*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_probe);
5091*4882a593Smuzhiyun
dw_hdmi_remove(struct dw_hdmi * hdmi)5092*4882a593Smuzhiyun void dw_hdmi_remove(struct dw_hdmi *hdmi)
5093*4882a593Smuzhiyun {
5094*4882a593Smuzhiyun if (hdmi->irq)
5095*4882a593Smuzhiyun disable_irq(hdmi->irq);
5096*4882a593Smuzhiyun
5097*4882a593Smuzhiyun cancel_delayed_work(&hdmi->work);
5098*4882a593Smuzhiyun flush_workqueue(hdmi->workqueue);
5099*4882a593Smuzhiyun destroy_workqueue(hdmi->workqueue);
5100*4882a593Smuzhiyun
5101*4882a593Smuzhiyun debugfs_remove_recursive(hdmi->debugfs_dir);
5102*4882a593Smuzhiyun
5103*4882a593Smuzhiyun drm_bridge_remove(&hdmi->bridge);
5104*4882a593Smuzhiyun
5105*4882a593Smuzhiyun if (hdmi->audio && !IS_ERR(hdmi->audio))
5106*4882a593Smuzhiyun platform_device_unregister(hdmi->audio);
5107*4882a593Smuzhiyun if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev))
5108*4882a593Smuzhiyun platform_device_unregister(hdmi->hdcp_dev);
5109*4882a593Smuzhiyun if (!IS_ERR(hdmi->cec))
5110*4882a593Smuzhiyun platform_device_unregister(hdmi->cec);
5111*4882a593Smuzhiyun
5112*4882a593Smuzhiyun /* Disable all interrupts */
5113*4882a593Smuzhiyun hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
5114*4882a593Smuzhiyun
5115*4882a593Smuzhiyun if (!hdmi->next_bridge) {
5116*4882a593Smuzhiyun dw_hdmi_destroy_properties(hdmi);
5117*4882a593Smuzhiyun hdmi->connector.funcs->destroy(&hdmi->connector);
5118*4882a593Smuzhiyun }
5119*4882a593Smuzhiyun
5120*4882a593Smuzhiyun if (hdmi->bridge.encoder)
5121*4882a593Smuzhiyun hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
5122*4882a593Smuzhiyun
5123*4882a593Smuzhiyun clk_disable_unprepare(hdmi->iahb_clk);
5124*4882a593Smuzhiyun clk_disable_unprepare(hdmi->isfr_clk);
5125*4882a593Smuzhiyun if (hdmi->cec_clk)
5126*4882a593Smuzhiyun clk_disable_unprepare(hdmi->cec_clk);
5127*4882a593Smuzhiyun
5128*4882a593Smuzhiyun if (hdmi->i2c)
5129*4882a593Smuzhiyun i2c_del_adapter(&hdmi->i2c->adap);
5130*4882a593Smuzhiyun else
5131*4882a593Smuzhiyun i2c_put_adapter(hdmi->ddc);
5132*4882a593Smuzhiyun }
5133*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_remove);
5134*4882a593Smuzhiyun
5135*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
5136*4882a593Smuzhiyun * Bind/unbind API, used from platforms based on the component framework.
5137*4882a593Smuzhiyun */
dw_hdmi_bind(struct platform_device * pdev,struct drm_encoder * encoder,struct dw_hdmi_plat_data * plat_data)5138*4882a593Smuzhiyun struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
5139*4882a593Smuzhiyun struct drm_encoder *encoder,
5140*4882a593Smuzhiyun struct dw_hdmi_plat_data *plat_data)
5141*4882a593Smuzhiyun {
5142*4882a593Smuzhiyun struct dw_hdmi *hdmi;
5143*4882a593Smuzhiyun int ret;
5144*4882a593Smuzhiyun
5145*4882a593Smuzhiyun hdmi = dw_hdmi_probe(pdev, plat_data);
5146*4882a593Smuzhiyun if (IS_ERR(hdmi))
5147*4882a593Smuzhiyun return hdmi;
5148*4882a593Smuzhiyun
5149*4882a593Smuzhiyun ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
5150*4882a593Smuzhiyun if (ret) {
5151*4882a593Smuzhiyun dw_hdmi_remove(hdmi);
5152*4882a593Smuzhiyun DRM_ERROR("Failed to initialize bridge with drm\n");
5153*4882a593Smuzhiyun return ERR_PTR(ret);
5154*4882a593Smuzhiyun }
5155*4882a593Smuzhiyun
5156*4882a593Smuzhiyun if (!hdmi->next_bridge)
5157*4882a593Smuzhiyun plat_data->connector = &hdmi->connector;
5158*4882a593Smuzhiyun
5159*4882a593Smuzhiyun return hdmi;
5160*4882a593Smuzhiyun }
5161*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_bind);
5162*4882a593Smuzhiyun
dw_hdmi_unbind(struct dw_hdmi * hdmi)5163*4882a593Smuzhiyun void dw_hdmi_unbind(struct dw_hdmi *hdmi)
5164*4882a593Smuzhiyun {
5165*4882a593Smuzhiyun dw_hdmi_remove(hdmi);
5166*4882a593Smuzhiyun }
5167*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
5168*4882a593Smuzhiyun
dw_hdmi_reg_initial(struct dw_hdmi * hdmi)5169*4882a593Smuzhiyun static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi)
5170*4882a593Smuzhiyun {
5171*4882a593Smuzhiyun if (hdmi_readb(hdmi, HDMI_IH_MUTE)) {
5172*4882a593Smuzhiyun initialize_hdmi_ih_mutes(hdmi);
5173*4882a593Smuzhiyun /* unmute cec irq */
5174*4882a593Smuzhiyun hdmi_writeb(hdmi, 0x68, HDMI_IH_MUTE_CEC_STAT0);
5175*4882a593Smuzhiyun
5176*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
5177*4882a593Smuzhiyun HDMI_PHY_I2CM_INT_ADDR);
5178*4882a593Smuzhiyun
5179*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
5180*4882a593Smuzhiyun HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
5181*4882a593Smuzhiyun HDMI_PHY_I2CM_CTLINT_ADDR);
5182*4882a593Smuzhiyun
5183*4882a593Smuzhiyun if (!hdmi->next_bridge) {
5184*4882a593Smuzhiyun hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
5185*4882a593Smuzhiyun HDMI_PHY_POL0);
5186*4882a593Smuzhiyun hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
5187*4882a593Smuzhiyun hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
5188*4882a593Smuzhiyun HDMI_IH_PHY_STAT0_RX_SENSE),
5189*4882a593Smuzhiyun HDMI_IH_MUTE_PHY_STAT0);
5190*4882a593Smuzhiyun }
5191*4882a593Smuzhiyun }
5192*4882a593Smuzhiyun }
5193*4882a593Smuzhiyun
dw_hdmi_suspend(struct dw_hdmi * hdmi)5194*4882a593Smuzhiyun void dw_hdmi_suspend(struct dw_hdmi *hdmi)
5195*4882a593Smuzhiyun {
5196*4882a593Smuzhiyun if (!hdmi)
5197*4882a593Smuzhiyun return;
5198*4882a593Smuzhiyun
5199*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
5200*4882a593Smuzhiyun
5201*4882a593Smuzhiyun /*
5202*4882a593Smuzhiyun * When system shutdown, hdmi should be disabled.
5203*4882a593Smuzhiyun * When system suspend, dw_hdmi_bridge_disable will disable hdmi first.
5204*4882a593Smuzhiyun * To prevent duplicate operation, we should determine whether hdmi
5205*4882a593Smuzhiyun * has been disabled.
5206*4882a593Smuzhiyun */
5207*4882a593Smuzhiyun if (!hdmi->disabled) {
5208*4882a593Smuzhiyun hdmi->disabled = true;
5209*4882a593Smuzhiyun dw_hdmi_update_power(hdmi);
5210*4882a593Smuzhiyun dw_hdmi_update_phy_mask(hdmi);
5211*4882a593Smuzhiyun }
5212*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
5213*4882a593Smuzhiyun
5214*4882a593Smuzhiyun if (hdmi->irq)
5215*4882a593Smuzhiyun disable_irq(hdmi->irq);
5216*4882a593Smuzhiyun cancel_delayed_work(&hdmi->work);
5217*4882a593Smuzhiyun flush_workqueue(hdmi->workqueue);
5218*4882a593Smuzhiyun pinctrl_pm_select_sleep_state(hdmi->dev);
5219*4882a593Smuzhiyun }
5220*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
5221*4882a593Smuzhiyun
dw_hdmi_resume(struct dw_hdmi * hdmi)5222*4882a593Smuzhiyun void dw_hdmi_resume(struct dw_hdmi *hdmi)
5223*4882a593Smuzhiyun {
5224*4882a593Smuzhiyun if (!hdmi)
5225*4882a593Smuzhiyun return;
5226*4882a593Smuzhiyun
5227*4882a593Smuzhiyun pinctrl_pm_select_default_state(hdmi->dev);
5228*4882a593Smuzhiyun mutex_lock(&hdmi->mutex);
5229*4882a593Smuzhiyun dw_hdmi_reg_initial(hdmi);
5230*4882a593Smuzhiyun if (hdmi->i2c)
5231*4882a593Smuzhiyun dw_hdmi_i2c_init(hdmi);
5232*4882a593Smuzhiyun if (hdmi->irq)
5233*4882a593Smuzhiyun enable_irq(hdmi->irq);
5234*4882a593Smuzhiyun /*
5235*4882a593Smuzhiyun * HDMI status maybe incorrect in the following condition:
5236*4882a593Smuzhiyun * HDMI plug in -> system sleep -> HDMI plug out -> system wake up.
5237*4882a593Smuzhiyun * At this time, cat /sys/class/drm/card 0-HDMI-A-1/status is connected.
5238*4882a593Smuzhiyun * There is no hpd interrupt, because HDMI is powerdown during suspend.
5239*4882a593Smuzhiyun * So we need check the current HDMI status in this case.
5240*4882a593Smuzhiyun */
5241*4882a593Smuzhiyun if (hdmi->connector.status == connector_status_connected) {
5242*4882a593Smuzhiyun if (hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data) ==
5243*4882a593Smuzhiyun connector_status_disconnected) {
5244*4882a593Smuzhiyun hdmi->hpd_state = false;
5245*4882a593Smuzhiyun mod_delayed_work(hdmi->workqueue, &hdmi->work,
5246*4882a593Smuzhiyun msecs_to_jiffies(20));
5247*4882a593Smuzhiyun }
5248*4882a593Smuzhiyun }
5249*4882a593Smuzhiyun mutex_unlock(&hdmi->mutex);
5250*4882a593Smuzhiyun }
5251*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dw_hdmi_resume);
5252*4882a593Smuzhiyun
5253*4882a593Smuzhiyun MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
5254*4882a593Smuzhiyun MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
5255*4882a593Smuzhiyun MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
5256*4882a593Smuzhiyun MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
5257*4882a593Smuzhiyun MODULE_DESCRIPTION("DW HDMI transmitter driver");
5258*4882a593Smuzhiyun MODULE_LICENSE("GPL");
5259*4882a593Smuzhiyun MODULE_ALIAS("platform:dw-hdmi");
5260