172317eaaSNeil Armstrong // SPDX-License-Identifier: GPL-2.0-or-later
272317eaaSNeil Armstrong /*
372317eaaSNeil Armstrong  * Copyright (C) 2016 BayLibre, SAS
472317eaaSNeil Armstrong  * Author: Neil Armstrong <narmstrong@baylibre.com>
572317eaaSNeil Armstrong  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
672317eaaSNeil Armstrong  * Copyright (C) 2014 Endless Mobile
772317eaaSNeil Armstrong  *
872317eaaSNeil Armstrong  * Written by:
972317eaaSNeil Armstrong  *     Jasper St. Pierre <jstpierre@mecheye.net>
1072317eaaSNeil Armstrong  */
1172317eaaSNeil Armstrong 
1272317eaaSNeil Armstrong #include <linux/export.h>
1372317eaaSNeil Armstrong #include <linux/of_graph.h>
1472317eaaSNeil Armstrong 
1572317eaaSNeil Armstrong #include <drm/drm_atomic_helper.h>
16318ba02cSNeil Armstrong #include <drm/drm_bridge.h>
17318ba02cSNeil Armstrong #include <drm/drm_bridge_connector.h>
1872317eaaSNeil Armstrong #include <drm/drm_device.h>
1972317eaaSNeil Armstrong #include <drm/drm_edid.h>
2072317eaaSNeil Armstrong #include <drm/drm_probe_helper.h>
21318ba02cSNeil Armstrong #include <drm/drm_simple_kms_helper.h>
2272317eaaSNeil Armstrong 
2372317eaaSNeil Armstrong #include "meson_registers.h"
2472317eaaSNeil Armstrong #include "meson_vclk.h"
2572317eaaSNeil Armstrong #include "meson_encoder_cvbs.h"
2672317eaaSNeil Armstrong 
2772317eaaSNeil Armstrong /* HHI VDAC Registers */
2872317eaaSNeil Armstrong #define HHI_VDAC_CNTL0		0x2F4 /* 0xbd offset in data sheet */
2972317eaaSNeil Armstrong #define HHI_VDAC_CNTL0_G12A	0x2EC /* 0xbd offset in data sheet */
3072317eaaSNeil Armstrong #define HHI_VDAC_CNTL1		0x2F8 /* 0xbe offset in data sheet */
3172317eaaSNeil Armstrong #define HHI_VDAC_CNTL1_G12A	0x2F0 /* 0xbe offset in data sheet */
3272317eaaSNeil Armstrong 
3372317eaaSNeil Armstrong struct meson_encoder_cvbs {
3472317eaaSNeil Armstrong 	struct drm_encoder	encoder;
35318ba02cSNeil Armstrong 	struct drm_bridge	bridge;
36318ba02cSNeil Armstrong 	struct drm_bridge	*next_bridge;
3772317eaaSNeil Armstrong 	struct meson_drm	*priv;
3872317eaaSNeil Armstrong };
3972317eaaSNeil Armstrong 
40318ba02cSNeil Armstrong #define bridge_to_meson_encoder_cvbs(x) \
41318ba02cSNeil Armstrong 	container_of(x, struct meson_encoder_cvbs, bridge)
4272317eaaSNeil Armstrong 
4372317eaaSNeil Armstrong /* Supported Modes */
4472317eaaSNeil Armstrong 
4572317eaaSNeil Armstrong struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
4672317eaaSNeil Armstrong 	{ /* PAL */
4772317eaaSNeil Armstrong 		.enci = &meson_cvbs_enci_pal,
4872317eaaSNeil Armstrong 		.mode = {
4972317eaaSNeil Armstrong 			DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
5072317eaaSNeil Armstrong 				 720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
5172317eaaSNeil Armstrong 				 DRM_MODE_FLAG_INTERLACE),
5272317eaaSNeil Armstrong 			.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
5372317eaaSNeil Armstrong 		},
5472317eaaSNeil Armstrong 	},
5572317eaaSNeil Armstrong 	{ /* NTSC */
5672317eaaSNeil Armstrong 		.enci = &meson_cvbs_enci_ntsc,
5772317eaaSNeil Armstrong 		.mode = {
5872317eaaSNeil Armstrong 			DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
5972317eaaSNeil Armstrong 				720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
6072317eaaSNeil Armstrong 				DRM_MODE_FLAG_INTERLACE),
6172317eaaSNeil Armstrong 			.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
6272317eaaSNeil Armstrong 		},
6372317eaaSNeil Armstrong 	},
6472317eaaSNeil Armstrong };
6572317eaaSNeil Armstrong 
6672317eaaSNeil Armstrong static const struct meson_cvbs_mode *
6772317eaaSNeil Armstrong meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
6872317eaaSNeil Armstrong {
6972317eaaSNeil Armstrong 	int i;
7072317eaaSNeil Armstrong 
7172317eaaSNeil Armstrong 	for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
7272317eaaSNeil Armstrong 		struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
7372317eaaSNeil Armstrong 
7472317eaaSNeil Armstrong 		if (drm_mode_match(req_mode, &meson_mode->mode,
7572317eaaSNeil Armstrong 				   DRM_MODE_MATCH_TIMINGS |
7672317eaaSNeil Armstrong 				   DRM_MODE_MATCH_CLOCK |
7772317eaaSNeil Armstrong 				   DRM_MODE_MATCH_FLAGS |
7872317eaaSNeil Armstrong 				   DRM_MODE_MATCH_3D_FLAGS))
7972317eaaSNeil Armstrong 			return meson_mode;
8072317eaaSNeil Armstrong 	}
8172317eaaSNeil Armstrong 
8272317eaaSNeil Armstrong 	return NULL;
8372317eaaSNeil Armstrong }
8472317eaaSNeil Armstrong 
85318ba02cSNeil Armstrong static int meson_encoder_cvbs_attach(struct drm_bridge *bridge,
86318ba02cSNeil Armstrong 				     enum drm_bridge_attach_flags flags)
8772317eaaSNeil Armstrong {
88318ba02cSNeil Armstrong 	struct meson_encoder_cvbs *meson_encoder_cvbs =
89318ba02cSNeil Armstrong 					bridge_to_meson_encoder_cvbs(bridge);
90318ba02cSNeil Armstrong 
91318ba02cSNeil Armstrong 	return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge,
92318ba02cSNeil Armstrong 				 &meson_encoder_cvbs->bridge, flags);
9372317eaaSNeil Armstrong }
9472317eaaSNeil Armstrong 
95318ba02cSNeil Armstrong static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge,
96318ba02cSNeil Armstrong 					struct drm_connector *connector)
9772317eaaSNeil Armstrong {
98318ba02cSNeil Armstrong 	struct meson_encoder_cvbs *meson_encoder_cvbs =
99318ba02cSNeil Armstrong 					bridge_to_meson_encoder_cvbs(bridge);
100318ba02cSNeil Armstrong 	struct meson_drm *priv = meson_encoder_cvbs->priv;
10172317eaaSNeil Armstrong 	struct drm_display_mode *mode;
10272317eaaSNeil Armstrong 	int i;
10372317eaaSNeil Armstrong 
10472317eaaSNeil Armstrong 	for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
10572317eaaSNeil Armstrong 		struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
10672317eaaSNeil Armstrong 
107318ba02cSNeil Armstrong 		mode = drm_mode_duplicate(priv->drm, &meson_mode->mode);
10872317eaaSNeil Armstrong 		if (!mode) {
109318ba02cSNeil Armstrong 			dev_err(priv->dev, "Failed to create a new display mode\n");
11072317eaaSNeil Armstrong 			return 0;
11172317eaaSNeil Armstrong 		}
11272317eaaSNeil Armstrong 
11372317eaaSNeil Armstrong 		drm_mode_probed_add(connector, mode);
11472317eaaSNeil Armstrong 	}
11572317eaaSNeil Armstrong 
11672317eaaSNeil Armstrong 	return i;
11772317eaaSNeil Armstrong }
11872317eaaSNeil Armstrong 
119318ba02cSNeil Armstrong static int meson_encoder_cvbs_mode_valid(struct drm_bridge *bridge,
120318ba02cSNeil Armstrong 					const struct drm_display_info *display_info,
121318ba02cSNeil Armstrong 					const struct drm_display_mode *mode)
12272317eaaSNeil Armstrong {
123318ba02cSNeil Armstrong 	if (meson_cvbs_get_mode(mode))
12472317eaaSNeil Armstrong 		return MODE_OK;
125318ba02cSNeil Armstrong 
126318ba02cSNeil Armstrong 	return MODE_BAD;
12772317eaaSNeil Armstrong }
12872317eaaSNeil Armstrong 
129318ba02cSNeil Armstrong static int meson_encoder_cvbs_atomic_check(struct drm_bridge *bridge,
130318ba02cSNeil Armstrong 					struct drm_bridge_state *bridge_state,
13172317eaaSNeil Armstrong 					struct drm_crtc_state *crtc_state,
13272317eaaSNeil Armstrong 					struct drm_connector_state *conn_state)
13372317eaaSNeil Armstrong {
13472317eaaSNeil Armstrong 	if (meson_cvbs_get_mode(&crtc_state->mode))
13572317eaaSNeil Armstrong 		return 0;
13672317eaaSNeil Armstrong 
13772317eaaSNeil Armstrong 	return -EINVAL;
13872317eaaSNeil Armstrong }
13972317eaaSNeil Armstrong 
140318ba02cSNeil Armstrong static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge,
141318ba02cSNeil Armstrong 					     struct drm_bridge_state *bridge_state)
14272317eaaSNeil Armstrong {
143318ba02cSNeil Armstrong 	struct meson_encoder_cvbs *encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge);
144318ba02cSNeil Armstrong 	struct drm_atomic_state *state = bridge_state->base.state;
145318ba02cSNeil Armstrong 	struct meson_drm *priv = encoder_cvbs->priv;
146318ba02cSNeil Armstrong 	const struct meson_cvbs_mode *meson_mode;
147318ba02cSNeil Armstrong 	struct drm_connector_state *conn_state;
148318ba02cSNeil Armstrong 	struct drm_crtc_state *crtc_state;
149318ba02cSNeil Armstrong 	struct drm_connector *connector;
15072317eaaSNeil Armstrong 
151318ba02cSNeil Armstrong 	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
152318ba02cSNeil Armstrong 	if (WARN_ON(!connector))
153318ba02cSNeil Armstrong 		return;
15472317eaaSNeil Armstrong 
155318ba02cSNeil Armstrong 	conn_state = drm_atomic_get_new_connector_state(state, connector);
156318ba02cSNeil Armstrong 	if (WARN_ON(!conn_state))
157318ba02cSNeil Armstrong 		return;
158318ba02cSNeil Armstrong 
159318ba02cSNeil Armstrong 	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
160318ba02cSNeil Armstrong 	if (WARN_ON(!crtc_state))
161318ba02cSNeil Armstrong 		return;
162318ba02cSNeil Armstrong 
163318ba02cSNeil Armstrong 	meson_mode = meson_cvbs_get_mode(&crtc_state->adjusted_mode);
164318ba02cSNeil Armstrong 	if (WARN_ON(!meson_mode))
165318ba02cSNeil Armstrong 		return;
166318ba02cSNeil Armstrong 
167318ba02cSNeil Armstrong 	meson_venci_cvbs_mode_set(priv, meson_mode->enci);
168318ba02cSNeil Armstrong 
169318ba02cSNeil Armstrong 	/* Setup 27MHz vclk2 for ENCI and VDAC */
170318ba02cSNeil Armstrong 	meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
171318ba02cSNeil Armstrong 			 MESON_VCLK_CVBS, MESON_VCLK_CVBS,
172318ba02cSNeil Armstrong 			 MESON_VCLK_CVBS, MESON_VCLK_CVBS,
173318ba02cSNeil Armstrong 			 true);
17472317eaaSNeil Armstrong 
17572317eaaSNeil Armstrong 	/* VDAC0 source is not from ATV */
17672317eaaSNeil Armstrong 	writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
17772317eaaSNeil Armstrong 			    priv->io_base + _REG(VENC_VDAC_DACSEL0));
17872317eaaSNeil Armstrong 
17972317eaaSNeil Armstrong 	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
18072317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
18172317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
18272317eaaSNeil Armstrong 	} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
18372317eaaSNeil Armstrong 		 meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
18472317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
18572317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
18672317eaaSNeil Armstrong 	} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
18772317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
18872317eaaSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
18972317eaaSNeil Armstrong 	}
19072317eaaSNeil Armstrong }
19172317eaaSNeil Armstrong 
192318ba02cSNeil Armstrong static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge,
193318ba02cSNeil Armstrong 					      struct drm_bridge_state *bridge_state)
19472317eaaSNeil Armstrong {
19572317eaaSNeil Armstrong 	struct meson_encoder_cvbs *meson_encoder_cvbs =
196318ba02cSNeil Armstrong 					bridge_to_meson_encoder_cvbs(bridge);
19772317eaaSNeil Armstrong 	struct meson_drm *priv = meson_encoder_cvbs->priv;
19872317eaaSNeil Armstrong 
199318ba02cSNeil Armstrong 	/* Disable CVBS VDAC */
200318ba02cSNeil Armstrong 	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
201318ba02cSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
202318ba02cSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
203318ba02cSNeil Armstrong 	} else {
204318ba02cSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
205318ba02cSNeil Armstrong 		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
20672317eaaSNeil Armstrong 	}
20772317eaaSNeil Armstrong }
20872317eaaSNeil Armstrong 
209318ba02cSNeil Armstrong static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
210318ba02cSNeil Armstrong 	.attach = meson_encoder_cvbs_attach,
211318ba02cSNeil Armstrong 	.mode_valid = meson_encoder_cvbs_mode_valid,
212318ba02cSNeil Armstrong 	.get_modes = meson_encoder_cvbs_get_modes,
213318ba02cSNeil Armstrong 	.atomic_enable = meson_encoder_cvbs_atomic_enable,
214318ba02cSNeil Armstrong 	.atomic_disable = meson_encoder_cvbs_atomic_disable,
215318ba02cSNeil Armstrong 	.atomic_check = meson_encoder_cvbs_atomic_check,
216318ba02cSNeil Armstrong 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
217318ba02cSNeil Armstrong 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
218318ba02cSNeil Armstrong 	.atomic_reset = drm_atomic_helper_bridge_reset,
21972317eaaSNeil Armstrong };
22072317eaaSNeil Armstrong 
22172317eaaSNeil Armstrong int meson_encoder_cvbs_init(struct meson_drm *priv)
22272317eaaSNeil Armstrong {
22372317eaaSNeil Armstrong 	struct drm_device *drm = priv->drm;
22472317eaaSNeil Armstrong 	struct meson_encoder_cvbs *meson_encoder_cvbs;
22572317eaaSNeil Armstrong 	struct drm_connector *connector;
226318ba02cSNeil Armstrong 	struct device_node *remote;
22772317eaaSNeil Armstrong 	int ret;
22872317eaaSNeil Armstrong 
229318ba02cSNeil Armstrong 	meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL);
230318ba02cSNeil Armstrong 	if (!meson_encoder_cvbs)
231318ba02cSNeil Armstrong 		return -ENOMEM;
232318ba02cSNeil Armstrong 
233318ba02cSNeil Armstrong 	/* CVBS Connector Bridge */
234318ba02cSNeil Armstrong 	remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0);
235318ba02cSNeil Armstrong 	if (!remote) {
23672317eaaSNeil Armstrong 		dev_info(drm->dev, "CVBS Output connector not available\n");
23772317eaaSNeil Armstrong 		return 0;
23872317eaaSNeil Armstrong 	}
23972317eaaSNeil Armstrong 
240318ba02cSNeil Armstrong 	meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
241*7d255ddbSMiaoqian Lin 	of_node_put(remote);
242318ba02cSNeil Armstrong 	if (!meson_encoder_cvbs->next_bridge) {
243318ba02cSNeil Armstrong 		dev_err(priv->dev, "Failed to find CVBS Connector bridge\n");
244318ba02cSNeil Armstrong 		return -EPROBE_DEFER;
245318ba02cSNeil Armstrong 	}
246318ba02cSNeil Armstrong 
247318ba02cSNeil Armstrong 	/* CVBS Encoder Bridge */
248318ba02cSNeil Armstrong 	meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs;
249318ba02cSNeil Armstrong 	meson_encoder_cvbs->bridge.of_node = priv->dev->of_node;
250318ba02cSNeil Armstrong 	meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite;
251318ba02cSNeil Armstrong 	meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES;
252318ba02cSNeil Armstrong 	meson_encoder_cvbs->bridge.interlace_allowed = true;
253318ba02cSNeil Armstrong 
254318ba02cSNeil Armstrong 	drm_bridge_add(&meson_encoder_cvbs->bridge);
25572317eaaSNeil Armstrong 
25672317eaaSNeil Armstrong 	meson_encoder_cvbs->priv = priv;
25772317eaaSNeil Armstrong 
25872317eaaSNeil Armstrong 	/* Encoder */
259318ba02cSNeil Armstrong 	ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
260318ba02cSNeil Armstrong 				      DRM_MODE_ENCODER_TVDAC);
26172317eaaSNeil Armstrong 	if (ret) {
262318ba02cSNeil Armstrong 		dev_err(priv->dev, "Failed to init CVBS encoder: %d\n", ret);
26372317eaaSNeil Armstrong 		return ret;
26472317eaaSNeil Armstrong 	}
26572317eaaSNeil Armstrong 
266318ba02cSNeil Armstrong 	meson_encoder_cvbs->encoder.possible_crtcs = BIT(0);
26772317eaaSNeil Armstrong 
268318ba02cSNeil Armstrong 	/* Attach CVBS Encoder Bridge to Encoder */
269318ba02cSNeil Armstrong 	ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL,
270318ba02cSNeil Armstrong 				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
271318ba02cSNeil Armstrong 	if (ret) {
272318ba02cSNeil Armstrong 		dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
273318ba02cSNeil Armstrong 		return ret;
274318ba02cSNeil Armstrong 	}
275318ba02cSNeil Armstrong 
276318ba02cSNeil Armstrong 	/* Initialize & attach Bridge Connector */
277318ba02cSNeil Armstrong 	connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder);
278318ba02cSNeil Armstrong 	if (IS_ERR(connector)) {
279318ba02cSNeil Armstrong 		dev_err(priv->dev, "Unable to create CVBS bridge connector\n");
280318ba02cSNeil Armstrong 		return PTR_ERR(connector);
281318ba02cSNeil Armstrong 	}
282318ba02cSNeil Armstrong 	drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder);
28372317eaaSNeil Armstrong 
28472317eaaSNeil Armstrong 	return 0;
28572317eaaSNeil Armstrong }
286