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); 2417d255ddbSMiaoqian 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 284*09847723SAdrián Larumbe priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs; 285*09847723SAdrián Larumbe 28672317eaaSNeil Armstrong return 0; 28772317eaaSNeil Armstrong } 288*09847723SAdrián Larumbe 289*09847723SAdrián Larumbe void meson_encoder_cvbs_remove(struct meson_drm *priv) 290*09847723SAdrián Larumbe { 291*09847723SAdrián Larumbe struct meson_encoder_cvbs *meson_encoder_cvbs; 292*09847723SAdrián Larumbe 293*09847723SAdrián Larumbe if (priv->encoders[MESON_ENC_CVBS]) { 294*09847723SAdrián Larumbe meson_encoder_cvbs = priv->encoders[MESON_ENC_CVBS]; 295*09847723SAdrián Larumbe drm_bridge_remove(&meson_encoder_cvbs->bridge); 296*09847723SAdrián Larumbe drm_bridge_remove(meson_encoder_cvbs->next_bridge); 297*09847723SAdrián Larumbe } 298*09847723SAdrián Larumbe } 299