xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/bridge/chrontel-ch7033.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Chrontel CH7033 Video Encoder Driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2019,2020 Lubomir Rintel
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/regmap.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
13*4882a593Smuzhiyun #include <drm/drm_bridge.h>
14*4882a593Smuzhiyun #include <drm/drm_edid.h>
15*4882a593Smuzhiyun #include <drm/drm_of.h>
16*4882a593Smuzhiyun #include <drm/drm_print.h>
17*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* Page 0, Register 0x07 */
20*4882a593Smuzhiyun enum {
21*4882a593Smuzhiyun 	DRI_PD		= BIT(3),
22*4882a593Smuzhiyun 	IO_PD		= BIT(5),
23*4882a593Smuzhiyun };
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* Page 0, Register 0x08 */
26*4882a593Smuzhiyun enum {
27*4882a593Smuzhiyun 	DRI_PDDRI	= GENMASK(7, 4),
28*4882a593Smuzhiyun 	PDDAC		= GENMASK(3, 1),
29*4882a593Smuzhiyun 	PANEN		= BIT(0),
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /* Page 0, Register 0x09 */
33*4882a593Smuzhiyun enum {
34*4882a593Smuzhiyun 	DPD		= BIT(7),
35*4882a593Smuzhiyun 	GCKOFF		= BIT(6),
36*4882a593Smuzhiyun 	TV_BP		= BIT(5),
37*4882a593Smuzhiyun 	SCLPD		= BIT(4),
38*4882a593Smuzhiyun 	SDPD		= BIT(3),
39*4882a593Smuzhiyun 	VGA_PD		= BIT(2),
40*4882a593Smuzhiyun 	HDBKPD		= BIT(1),
41*4882a593Smuzhiyun 	HDMI_PD		= BIT(0),
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* Page 0, Register 0x0a */
45*4882a593Smuzhiyun enum {
46*4882a593Smuzhiyun 	MEMINIT		= BIT(7),
47*4882a593Smuzhiyun 	MEMIDLE		= BIT(6),
48*4882a593Smuzhiyun 	MEMPD		= BIT(5),
49*4882a593Smuzhiyun 	STOP		= BIT(4),
50*4882a593Smuzhiyun 	LVDS_PD		= BIT(3),
51*4882a593Smuzhiyun 	HD_DVIB		= BIT(2),
52*4882a593Smuzhiyun 	HDCP_PD		= BIT(1),
53*4882a593Smuzhiyun 	MCU_PD		= BIT(0),
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun /* Page 0, Register 0x18 */
57*4882a593Smuzhiyun enum {
58*4882a593Smuzhiyun 	IDF		= GENMASK(7, 4),
59*4882a593Smuzhiyun 	INTEN		= BIT(3),
60*4882a593Smuzhiyun 	SWAP		= GENMASK(2, 0),
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun enum {
64*4882a593Smuzhiyun 	BYTE_SWAP_RGB	= 0,
65*4882a593Smuzhiyun 	BYTE_SWAP_RBG	= 1,
66*4882a593Smuzhiyun 	BYTE_SWAP_GRB	= 2,
67*4882a593Smuzhiyun 	BYTE_SWAP_GBR	= 3,
68*4882a593Smuzhiyun 	BYTE_SWAP_BRG	= 4,
69*4882a593Smuzhiyun 	BYTE_SWAP_BGR	= 5,
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun /* Page 0, Register 0x19 */
73*4882a593Smuzhiyun enum {
74*4882a593Smuzhiyun 	HPO_I		= BIT(5),
75*4882a593Smuzhiyun 	VPO_I		= BIT(4),
76*4882a593Smuzhiyun 	DEPO_I		= BIT(3),
77*4882a593Smuzhiyun 	CRYS_EN		= BIT(2),
78*4882a593Smuzhiyun 	GCLKFREQ	= GENMASK(2, 0),
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /* Page 0, Register 0x2e */
82*4882a593Smuzhiyun enum {
83*4882a593Smuzhiyun 	HFLIP		= BIT(7),
84*4882a593Smuzhiyun 	VFLIP		= BIT(6),
85*4882a593Smuzhiyun 	DEPO_O		= BIT(5),
86*4882a593Smuzhiyun 	HPO_O		= BIT(4),
87*4882a593Smuzhiyun 	VPO_O		= BIT(3),
88*4882a593Smuzhiyun 	TE		= GENMASK(2, 0),
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun /* Page 0, Register 0x2b */
92*4882a593Smuzhiyun enum {
93*4882a593Smuzhiyun 	SWAPS		= GENMASK(7, 4),
94*4882a593Smuzhiyun 	VFMT		= GENMASK(3, 0),
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun /* Page 0, Register 0x54 */
98*4882a593Smuzhiyun enum {
99*4882a593Smuzhiyun 	COMP_BP		= BIT(7),
100*4882a593Smuzhiyun 	DAC_EN_T	= BIT(6),
101*4882a593Smuzhiyun 	HWO_HDMI_HI	= GENMASK(5, 3),
102*4882a593Smuzhiyun 	HOO_HDMI_HI	= GENMASK(2, 0),
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun /* Page 0, Register 0x57 */
106*4882a593Smuzhiyun enum {
107*4882a593Smuzhiyun 	FLDSEN		= BIT(7),
108*4882a593Smuzhiyun 	VWO_HDMI_HI	= GENMASK(5, 3),
109*4882a593Smuzhiyun 	VOO_HDMI_HI	= GENMASK(2, 0),
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun /* Page 0, Register 0x7e */
113*4882a593Smuzhiyun enum {
114*4882a593Smuzhiyun 	HDMI_LVDS_SEL	= BIT(7),
115*4882a593Smuzhiyun 	DE_GEN		= BIT(6),
116*4882a593Smuzhiyun 	PWM_INDEX_HI	= BIT(5),
117*4882a593Smuzhiyun 	USE_DE		= BIT(4),
118*4882a593Smuzhiyun 	R_INT		= GENMASK(3, 0),
119*4882a593Smuzhiyun };
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun /* Page 1, Register 0x07 */
122*4882a593Smuzhiyun enum {
123*4882a593Smuzhiyun 	BPCKSEL		= BIT(7),
124*4882a593Smuzhiyun 	DRI_CMFB_EN	= BIT(6),
125*4882a593Smuzhiyun 	CEC_PUEN	= BIT(5),
126*4882a593Smuzhiyun 	CEC_T		= BIT(3),
127*4882a593Smuzhiyun 	CKINV		= BIT(2),
128*4882a593Smuzhiyun 	CK_TVINV	= BIT(1),
129*4882a593Smuzhiyun 	DRI_CKS2	= BIT(0),
130*4882a593Smuzhiyun };
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun /* Page 1, Register 0x08 */
133*4882a593Smuzhiyun enum {
134*4882a593Smuzhiyun 	DACG		= BIT(6),
135*4882a593Smuzhiyun 	DACKTST		= BIT(5),
136*4882a593Smuzhiyun 	DEDGEB		= BIT(4),
137*4882a593Smuzhiyun 	SYO		= BIT(3),
138*4882a593Smuzhiyun 	DRI_IT_LVDS	= GENMASK(2, 1),
139*4882a593Smuzhiyun 	DISPON		= BIT(0),
140*4882a593Smuzhiyun };
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun /* Page 1, Register 0x0c */
143*4882a593Smuzhiyun enum {
144*4882a593Smuzhiyun 	DRI_PLL_CP	= GENMASK(7, 6),
145*4882a593Smuzhiyun 	DRI_PLL_DIVSEL	= BIT(5),
146*4882a593Smuzhiyun 	DRI_PLL_N1_1	= BIT(4),
147*4882a593Smuzhiyun 	DRI_PLL_N1_0	= BIT(3),
148*4882a593Smuzhiyun 	DRI_PLL_N3_1	= BIT(2),
149*4882a593Smuzhiyun 	DRI_PLL_N3_0	= BIT(1),
150*4882a593Smuzhiyun 	DRI_PLL_CKTSTEN = BIT(0),
151*4882a593Smuzhiyun };
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun /* Page 1, Register 0x6b */
154*4882a593Smuzhiyun enum {
155*4882a593Smuzhiyun 	VCO3CS		= GENMASK(7, 6),
156*4882a593Smuzhiyun 	ICPGBK2_0	= GENMASK(5, 3),
157*4882a593Smuzhiyun 	DRI_VCO357SC	= BIT(2),
158*4882a593Smuzhiyun 	PDPLL2		= BIT(1),
159*4882a593Smuzhiyun 	DRI_PD_SER	= BIT(0),
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun /* Page 1, Register 0x6c */
163*4882a593Smuzhiyun enum {
164*4882a593Smuzhiyun 	PLL2N11		= GENMASK(7, 4),
165*4882a593Smuzhiyun 	PLL2N5_4	= BIT(3),
166*4882a593Smuzhiyun 	PLL2N5_TOP	= BIT(2),
167*4882a593Smuzhiyun 	DRI_PLL_PD	= BIT(1),
168*4882a593Smuzhiyun 	PD_I2CM		= BIT(0),
169*4882a593Smuzhiyun };
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun /* Page 3, Register 0x28 */
172*4882a593Smuzhiyun enum {
173*4882a593Smuzhiyun 	DIFF_EN		= GENMASK(7, 6),
174*4882a593Smuzhiyun 	CORREC_EN	= GENMASK(5, 4),
175*4882a593Smuzhiyun 	VGACLK_BP	= BIT(3),
176*4882a593Smuzhiyun 	HM_LV_SEL	= BIT(2),
177*4882a593Smuzhiyun 	HD_VGA_SEL	= BIT(1),
178*4882a593Smuzhiyun };
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun /* Page 3, Register 0x2a */
181*4882a593Smuzhiyun enum {
182*4882a593Smuzhiyun 	LVDSCLK_BP	= BIT(7),
183*4882a593Smuzhiyun 	HDTVCLK_BP	= BIT(6),
184*4882a593Smuzhiyun 	HDMICLK_BP	= BIT(5),
185*4882a593Smuzhiyun 	HDTV_BP		= BIT(4),
186*4882a593Smuzhiyun 	HDMI_BP		= BIT(3),
187*4882a593Smuzhiyun 	THRWL		= GENMASK(2, 0),
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun /* Page 4, Register 0x52 */
191*4882a593Smuzhiyun enum {
192*4882a593Smuzhiyun 	PGM_ARSTB	= BIT(7),
193*4882a593Smuzhiyun 	MCU_ARSTB	= BIT(6),
194*4882a593Smuzhiyun 	MCU_RETB	= BIT(2),
195*4882a593Smuzhiyun 	RESETIB		= BIT(1),
196*4882a593Smuzhiyun 	RESETDB		= BIT(0),
197*4882a593Smuzhiyun };
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun struct ch7033_priv {
200*4882a593Smuzhiyun 	struct regmap *regmap;
201*4882a593Smuzhiyun 	struct drm_bridge *next_bridge;
202*4882a593Smuzhiyun 	struct drm_bridge bridge;
203*4882a593Smuzhiyun 	struct drm_connector connector;
204*4882a593Smuzhiyun };
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun #define conn_to_ch7033_priv(x) \
207*4882a593Smuzhiyun 	container_of(x, struct ch7033_priv, connector)
208*4882a593Smuzhiyun #define bridge_to_ch7033_priv(x) \
209*4882a593Smuzhiyun 	container_of(x, struct ch7033_priv, bridge)
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 
ch7033_connector_detect(struct drm_connector * connector,bool force)212*4882a593Smuzhiyun static enum drm_connector_status ch7033_connector_detect(
213*4882a593Smuzhiyun 	struct drm_connector *connector, bool force)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	struct ch7033_priv *priv = conn_to_ch7033_priv(connector);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return drm_bridge_detect(priv->next_bridge);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun static const struct drm_connector_funcs ch7033_connector_funcs = {
221*4882a593Smuzhiyun 	.reset = drm_atomic_helper_connector_reset,
222*4882a593Smuzhiyun 	.fill_modes = drm_helper_probe_single_connector_modes,
223*4882a593Smuzhiyun 	.detect = ch7033_connector_detect,
224*4882a593Smuzhiyun 	.destroy = drm_connector_cleanup,
225*4882a593Smuzhiyun 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
226*4882a593Smuzhiyun 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
227*4882a593Smuzhiyun };
228*4882a593Smuzhiyun 
ch7033_connector_get_modes(struct drm_connector * connector)229*4882a593Smuzhiyun static int ch7033_connector_get_modes(struct drm_connector *connector)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	struct ch7033_priv *priv = conn_to_ch7033_priv(connector);
232*4882a593Smuzhiyun 	struct edid *edid;
233*4882a593Smuzhiyun 	int ret;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	edid = drm_bridge_get_edid(priv->next_bridge, connector);
236*4882a593Smuzhiyun 	drm_connector_update_edid_property(connector, edid);
237*4882a593Smuzhiyun 	if (edid) {
238*4882a593Smuzhiyun 		ret = drm_add_edid_modes(connector, edid);
239*4882a593Smuzhiyun 		kfree(edid);
240*4882a593Smuzhiyun 	} else {
241*4882a593Smuzhiyun 		ret = drm_add_modes_noedid(connector, 1920, 1080);
242*4882a593Smuzhiyun 		drm_set_preferred_mode(connector, 1024, 768);
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	return ret;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
ch7033_connector_best_encoder(struct drm_connector * connector)248*4882a593Smuzhiyun static struct drm_encoder *ch7033_connector_best_encoder(
249*4882a593Smuzhiyun 			struct drm_connector *connector)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun 	struct ch7033_priv *priv = conn_to_ch7033_priv(connector);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	return priv->bridge.encoder;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun static const struct drm_connector_helper_funcs ch7033_connector_helper_funcs = {
257*4882a593Smuzhiyun 	.get_modes = ch7033_connector_get_modes,
258*4882a593Smuzhiyun 	.best_encoder = ch7033_connector_best_encoder,
259*4882a593Smuzhiyun };
260*4882a593Smuzhiyun 
ch7033_hpd_event(void * arg,enum drm_connector_status status)261*4882a593Smuzhiyun static void ch7033_hpd_event(void *arg, enum drm_connector_status status)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	struct ch7033_priv *priv = arg;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (priv->bridge.dev)
266*4882a593Smuzhiyun 		drm_helper_hpd_irq_event(priv->connector.dev);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
ch7033_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)269*4882a593Smuzhiyun static int ch7033_bridge_attach(struct drm_bridge *bridge,
270*4882a593Smuzhiyun 				enum drm_bridge_attach_flags flags)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun 	struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
273*4882a593Smuzhiyun 	struct drm_connector *connector = &priv->connector;
274*4882a593Smuzhiyun 	int ret;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	ret = drm_bridge_attach(bridge->encoder, priv->next_bridge, bridge,
277*4882a593Smuzhiyun 				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
278*4882a593Smuzhiyun 	if (ret)
279*4882a593Smuzhiyun 		return ret;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
282*4882a593Smuzhiyun 		return 0;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	if (priv->next_bridge->ops & DRM_BRIDGE_OP_DETECT) {
285*4882a593Smuzhiyun 		connector->polled = DRM_CONNECTOR_POLL_HPD;
286*4882a593Smuzhiyun 	} else {
287*4882a593Smuzhiyun 		connector->polled = DRM_CONNECTOR_POLL_CONNECT |
288*4882a593Smuzhiyun 				    DRM_CONNECTOR_POLL_DISCONNECT;
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
292*4882a593Smuzhiyun 		drm_bridge_hpd_enable(priv->next_bridge, ch7033_hpd_event,
293*4882a593Smuzhiyun 				      priv);
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	drm_connector_helper_add(connector,
297*4882a593Smuzhiyun 				 &ch7033_connector_helper_funcs);
298*4882a593Smuzhiyun 	ret = drm_connector_init_with_ddc(bridge->dev, &priv->connector,
299*4882a593Smuzhiyun 					  &ch7033_connector_funcs,
300*4882a593Smuzhiyun 					  priv->next_bridge->type,
301*4882a593Smuzhiyun 					  priv->next_bridge->ddc);
302*4882a593Smuzhiyun 	if (ret) {
303*4882a593Smuzhiyun 		DRM_ERROR("Failed to initialize connector\n");
304*4882a593Smuzhiyun 		return ret;
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	return drm_connector_attach_encoder(&priv->connector, bridge->encoder);
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun 
ch7033_bridge_detach(struct drm_bridge * bridge)310*4882a593Smuzhiyun static void ch7033_bridge_detach(struct drm_bridge *bridge)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD)
315*4882a593Smuzhiyun 		drm_bridge_hpd_disable(priv->next_bridge);
316*4882a593Smuzhiyun 	drm_connector_cleanup(&priv->connector);
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
ch7033_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)319*4882a593Smuzhiyun static enum drm_mode_status ch7033_bridge_mode_valid(struct drm_bridge *bridge,
320*4882a593Smuzhiyun 				     const struct drm_display_info *info,
321*4882a593Smuzhiyun 				     const struct drm_display_mode *mode)
322*4882a593Smuzhiyun {
323*4882a593Smuzhiyun 	if (mode->clock > 165000)
324*4882a593Smuzhiyun 		return MODE_CLOCK_HIGH;
325*4882a593Smuzhiyun 	if (mode->hdisplay >= 1920)
326*4882a593Smuzhiyun 		return MODE_BAD_HVALUE;
327*4882a593Smuzhiyun 	if (mode->vdisplay >= 1080)
328*4882a593Smuzhiyun 		return MODE_BAD_VVALUE;
329*4882a593Smuzhiyun 	return MODE_OK;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
ch7033_bridge_disable(struct drm_bridge * bridge)332*4882a593Smuzhiyun static void ch7033_bridge_disable(struct drm_bridge *bridge)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x04);
337*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x52, RESETDB, 0x00);
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun 
ch7033_bridge_enable(struct drm_bridge * bridge)340*4882a593Smuzhiyun static void ch7033_bridge_enable(struct drm_bridge *bridge)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x04);
345*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x52, RESETDB, RESETDB);
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
ch7033_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)348*4882a593Smuzhiyun static void ch7033_bridge_mode_set(struct drm_bridge *bridge,
349*4882a593Smuzhiyun 				   const struct drm_display_mode *mode,
350*4882a593Smuzhiyun 				   const struct drm_display_mode *adjusted_mode)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun 	struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
353*4882a593Smuzhiyun 	int hbporch = mode->hsync_start - mode->hdisplay;
354*4882a593Smuzhiyun 	int hsynclen = mode->hsync_end - mode->hsync_start;
355*4882a593Smuzhiyun 	int vbporch = mode->vsync_start - mode->vdisplay;
356*4882a593Smuzhiyun 	int vsynclen = mode->vsync_end - mode->vsync_start;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	/*
359*4882a593Smuzhiyun 	 * Page 4
360*4882a593Smuzhiyun 	 */
361*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x04);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	/* Turn everything off to set all the registers to their defaults. */
364*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x52, 0x00);
365*4882a593Smuzhiyun 	/* Bring I/O block up. */
366*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x52, RESETIB);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	/*
369*4882a593Smuzhiyun 	 * Page 0
370*4882a593Smuzhiyun 	 */
371*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x00);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	/* Bring up parts we need from the power down. */
374*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x07, DRI_PD | IO_PD, 0);
375*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x08, DRI_PDDRI | PDDAC | PANEN, 0);
376*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x09, DPD | GCKOFF |
377*4882a593Smuzhiyun 					       HDMI_PD | VGA_PD, 0);
378*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x0a, HD_DVIB, 0);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	/* Horizontal input timing. */
381*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x0b, (mode->htotal >> 8) << 3 |
382*4882a593Smuzhiyun 					 (mode->hdisplay >> 8));
383*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x0c, mode->hdisplay);
384*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x0d, mode->htotal);
385*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x0e, (hsynclen >> 8) << 3 |
386*4882a593Smuzhiyun 					 (hbporch >> 8));
387*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x0f, hbporch);
388*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x10, hsynclen);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	/* Vertical input timing. */
391*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x11, (mode->vtotal >> 8) << 3 |
392*4882a593Smuzhiyun 					 (mode->vdisplay >> 8));
393*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x12, mode->vdisplay);
394*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x13, mode->vtotal);
395*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x14, ((vsynclen >> 8) << 3) |
396*4882a593Smuzhiyun 					 (vbporch >> 8));
397*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x15, vbporch);
398*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x16, vsynclen);
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	/* Input color swap. */
401*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x18, SWAP, BYTE_SWAP_BGR);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	/* Input clock and sync polarity. */
404*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x19, 0x1, mode->clock >> 16);
405*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x19, HPO_I | VPO_I | GCLKFREQ,
406*4882a593Smuzhiyun 			   (mode->flags & DRM_MODE_FLAG_PHSYNC) ? HPO_I : 0 |
407*4882a593Smuzhiyun 			   (mode->flags & DRM_MODE_FLAG_PVSYNC) ? VPO_I : 0 |
408*4882a593Smuzhiyun 			   mode->clock >> 16);
409*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x1a, mode->clock >> 8);
410*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x1b, mode->clock);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	/* Horizontal output timing. */
413*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x1f, (mode->htotal >> 8) << 3 |
414*4882a593Smuzhiyun 					 (mode->hdisplay >> 8));
415*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x20, mode->hdisplay);
416*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x21, mode->htotal);
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	/* Vertical output timing. */
419*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x25, (mode->vtotal >> 8) << 3 |
420*4882a593Smuzhiyun 					 (mode->vdisplay >> 8));
421*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x26, mode->vdisplay);
422*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x27, mode->vtotal);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	/* VGA channel bypass */
425*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x2b, VFMT, 9);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	/* Output sync polarity. */
428*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x2e, HPO_O | VPO_O,
429*4882a593Smuzhiyun 			   (mode->flags & DRM_MODE_FLAG_PHSYNC) ? HPO_O : 0 |
430*4882a593Smuzhiyun 			   (mode->flags & DRM_MODE_FLAG_PVSYNC) ? VPO_O : 0);
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/* HDMI horizontal output timing. */
433*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x54, HWO_HDMI_HI | HOO_HDMI_HI,
434*4882a593Smuzhiyun 					       (hsynclen >> 8) << 3 |
435*4882a593Smuzhiyun 					       (hbporch >> 8));
436*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x55, hbporch);
437*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x56, hsynclen);
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	/* HDMI vertical output timing. */
440*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x57, VWO_HDMI_HI | VOO_HDMI_HI,
441*4882a593Smuzhiyun 					       (vsynclen >> 8) << 3 |
442*4882a593Smuzhiyun 					       (vbporch >> 8));
443*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x58, vbporch);
444*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x59, vsynclen);
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	/* Pick HDMI, not LVDS. */
447*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x7e, HDMI_LVDS_SEL, HDMI_LVDS_SEL);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	/*
450*4882a593Smuzhiyun 	 * Page 1
451*4882a593Smuzhiyun 	 */
452*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x01);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	/* No idea what these do, but VGA is wobbly and blinky without them. */
455*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x07, CKINV, CKINV);
456*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x08, DISPON, DISPON);
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	/* DRI PLL */
459*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_DIVSEL, DRI_PLL_DIVSEL);
460*4882a593Smuzhiyun 	if (mode->clock <= 40000) {
461*4882a593Smuzhiyun 		regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 |
462*4882a593Smuzhiyun 						       DRI_PLL_N1_0 |
463*4882a593Smuzhiyun 						       DRI_PLL_N3_1 |
464*4882a593Smuzhiyun 						       DRI_PLL_N3_0,
465*4882a593Smuzhiyun 						       0);
466*4882a593Smuzhiyun 	} else if (mode->clock < 80000) {
467*4882a593Smuzhiyun 		regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 |
468*4882a593Smuzhiyun 						       DRI_PLL_N1_0 |
469*4882a593Smuzhiyun 						       DRI_PLL_N3_1 |
470*4882a593Smuzhiyun 						       DRI_PLL_N3_0,
471*4882a593Smuzhiyun 						       DRI_PLL_N3_0 |
472*4882a593Smuzhiyun 						       DRI_PLL_N1_0);
473*4882a593Smuzhiyun 	} else {
474*4882a593Smuzhiyun 		regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 |
475*4882a593Smuzhiyun 						       DRI_PLL_N1_0 |
476*4882a593Smuzhiyun 						       DRI_PLL_N3_1 |
477*4882a593Smuzhiyun 						       DRI_PLL_N3_0,
478*4882a593Smuzhiyun 						       DRI_PLL_N3_1 |
479*4882a593Smuzhiyun 						       DRI_PLL_N1_1);
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	/* This seems to be color calibration for VGA. */
483*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x64, 0x29); /* LSB Blue */
484*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x65, 0x29); /* LSB Green */
485*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x66, 0x29); /* LSB Red */
486*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x67, 0x00); /* MSB Blue */
487*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x68, 0x00); /* MSB Green */
488*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x69, 0x00); /* MSB Red */
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x6b, DRI_PD_SER, 0x00);
491*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x6c, DRI_PLL_PD, 0x00);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	/*
494*4882a593Smuzhiyun 	 * Page 3
495*4882a593Smuzhiyun 	 */
496*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x03);
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	/* More bypasses and apparently another HDMI/LVDS selector. */
499*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x28, VGACLK_BP | HM_LV_SEL,
500*4882a593Smuzhiyun 					       VGACLK_BP | HM_LV_SEL);
501*4882a593Smuzhiyun 	regmap_update_bits(priv->regmap, 0x2a, HDMICLK_BP | HDMI_BP,
502*4882a593Smuzhiyun 					       HDMICLK_BP | HDMI_BP);
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	/*
505*4882a593Smuzhiyun 	 * Page 4
506*4882a593Smuzhiyun 	 */
507*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x04);
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	/* Output clock. */
510*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x10, mode->clock >> 16);
511*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x11, mode->clock >> 8);
512*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x12, mode->clock);
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun static const struct drm_bridge_funcs ch7033_bridge_funcs = {
516*4882a593Smuzhiyun 	.attach = ch7033_bridge_attach,
517*4882a593Smuzhiyun 	.detach = ch7033_bridge_detach,
518*4882a593Smuzhiyun 	.mode_valid = ch7033_bridge_mode_valid,
519*4882a593Smuzhiyun 	.disable = ch7033_bridge_disable,
520*4882a593Smuzhiyun 	.enable = ch7033_bridge_enable,
521*4882a593Smuzhiyun 	.mode_set = ch7033_bridge_mode_set,
522*4882a593Smuzhiyun };
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun static const struct regmap_config ch7033_regmap_config = {
525*4882a593Smuzhiyun 	.reg_bits = 8,
526*4882a593Smuzhiyun 	.val_bits = 8,
527*4882a593Smuzhiyun 	.max_register = 0x7f,
528*4882a593Smuzhiyun };
529*4882a593Smuzhiyun 
ch7033_probe(struct i2c_client * client,const struct i2c_device_id * id)530*4882a593Smuzhiyun static int ch7033_probe(struct i2c_client *client,
531*4882a593Smuzhiyun 			const struct i2c_device_id *id)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun 	struct device *dev = &client->dev;
534*4882a593Smuzhiyun 	struct ch7033_priv *priv;
535*4882a593Smuzhiyun 	unsigned int val;
536*4882a593Smuzhiyun 	int ret;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
539*4882a593Smuzhiyun 	if (!priv)
540*4882a593Smuzhiyun 		return -ENOMEM;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	dev_set_drvdata(dev, priv);
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL,
545*4882a593Smuzhiyun 					  &priv->next_bridge);
546*4882a593Smuzhiyun 	if (ret)
547*4882a593Smuzhiyun 		return ret;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	priv->regmap = devm_regmap_init_i2c(client, &ch7033_regmap_config);
550*4882a593Smuzhiyun 	if (IS_ERR(priv->regmap)) {
551*4882a593Smuzhiyun 		dev_err(&client->dev, "regmap init failed\n");
552*4882a593Smuzhiyun 		return PTR_ERR(priv->regmap);
553*4882a593Smuzhiyun 	}
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	ret = regmap_read(priv->regmap, 0x00, &val);
556*4882a593Smuzhiyun 	if (ret < 0) {
557*4882a593Smuzhiyun 		dev_err(&client->dev, "error reading the model id: %d\n", ret);
558*4882a593Smuzhiyun 		return ret;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 	if ((val & 0xf7) != 0x56) {
561*4882a593Smuzhiyun 		dev_err(&client->dev, "the device is not a ch7033\n");
562*4882a593Smuzhiyun 		return -ENODEV;
563*4882a593Smuzhiyun 	}
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	regmap_write(priv->regmap, 0x03, 0x04);
566*4882a593Smuzhiyun 	ret = regmap_read(priv->regmap, 0x51, &val);
567*4882a593Smuzhiyun 	if (ret < 0) {
568*4882a593Smuzhiyun 		dev_err(&client->dev, "error reading the model id: %d\n", ret);
569*4882a593Smuzhiyun 		return ret;
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun 	if ((val & 0x0f) != 3) {
572*4882a593Smuzhiyun 		dev_err(&client->dev, "unknown revision %u\n", val);
573*4882a593Smuzhiyun 		return -ENODEV;
574*4882a593Smuzhiyun 	}
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	INIT_LIST_HEAD(&priv->bridge.list);
577*4882a593Smuzhiyun 	priv->bridge.funcs = &ch7033_bridge_funcs;
578*4882a593Smuzhiyun 	priv->bridge.of_node = dev->of_node;
579*4882a593Smuzhiyun 	drm_bridge_add(&priv->bridge);
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	dev_info(dev, "Chrontel CH7033 Video Encoder\n");
582*4882a593Smuzhiyun 	return 0;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun 
ch7033_remove(struct i2c_client * client)585*4882a593Smuzhiyun static int ch7033_remove(struct i2c_client *client)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun 	struct device *dev = &client->dev;
588*4882a593Smuzhiyun 	struct ch7033_priv *priv = dev_get_drvdata(dev);
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	drm_bridge_remove(&priv->bridge);
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	return 0;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun static const struct of_device_id ch7033_dt_ids[] = {
596*4882a593Smuzhiyun 	{ .compatible = "chrontel,ch7033", },
597*4882a593Smuzhiyun 	{ }
598*4882a593Smuzhiyun };
599*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, ch7033_dt_ids);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun static const struct i2c_device_id ch7033_ids[] = {
602*4882a593Smuzhiyun 	{ "ch7033", 0 },
603*4882a593Smuzhiyun 	{ }
604*4882a593Smuzhiyun };
605*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, ch7033_ids);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun static struct i2c_driver ch7033_driver = {
608*4882a593Smuzhiyun 	.probe = ch7033_probe,
609*4882a593Smuzhiyun 	.remove = ch7033_remove,
610*4882a593Smuzhiyun 	.driver = {
611*4882a593Smuzhiyun 		.name = "ch7033",
612*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(ch7033_dt_ids),
613*4882a593Smuzhiyun 	},
614*4882a593Smuzhiyun 	.id_table = ch7033_ids,
615*4882a593Smuzhiyun };
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun module_i2c_driver(ch7033_driver);
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
620*4882a593Smuzhiyun MODULE_DESCRIPTION("Chrontel CH7033 Video Encoder Driver");
621*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
622