Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/meson: encoder_cvbs: switch to bridge with ATTACH_NO_CONNECTOR

Drop the local connector and move all callback to bridge funcs in order
to leverage the generic CVBS display connector.

This will also permit adding custom cvbs connectors for ADC based HPD
detection on some Amlogic SoC based boards.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
[narmstrong: fixed diplay typo in commit log]
Link: https://patchwork.freedesktop.org/patch/msgid/20211020123947.2585572-7-narmstrong@baylibre.com

+122 -131
+122 -131
drivers/gpu/drm/meson/meson_encoder_cvbs.c
··· 13 13 #include <linux/of_graph.h> 14 14 15 15 #include <drm/drm_atomic_helper.h> 16 + #include <drm/drm_bridge.h> 17 + #include <drm/drm_bridge_connector.h> 16 18 #include <drm/drm_device.h> 17 19 #include <drm/drm_edid.h> 18 20 #include <drm/drm_probe_helper.h> 19 - #include <drm/drm_print.h> 21 + #include <drm/drm_simple_kms_helper.h> 20 22 21 23 #include "meson_registers.h" 22 24 #include "meson_vclk.h" ··· 32 30 33 31 struct meson_encoder_cvbs { 34 32 struct drm_encoder encoder; 35 - struct drm_connector connector; 33 + struct drm_bridge bridge; 34 + struct drm_bridge *next_bridge; 36 35 struct meson_drm *priv; 37 36 }; 38 - #define encoder_to_meson_encoder_cvbs(x) \ 39 - container_of(x, struct meson_encoder_cvbs, encoder) 40 37 41 - #define connector_to_meson_encoder_cvbs(x) \ 42 - container_of(x, struct meson_encoder_cvbs, connector) 38 + #define bridge_to_meson_encoder_cvbs(x) \ 39 + container_of(x, struct meson_encoder_cvbs, bridge) 43 40 44 41 /* Supported Modes */ 45 42 ··· 82 81 return NULL; 83 82 } 84 83 85 - /* Connector */ 86 - 87 - static void meson_cvbs_connector_destroy(struct drm_connector *connector) 84 + static int meson_encoder_cvbs_attach(struct drm_bridge *bridge, 85 + enum drm_bridge_attach_flags flags) 88 86 { 89 - drm_connector_cleanup(connector); 87 + struct meson_encoder_cvbs *meson_encoder_cvbs = 88 + bridge_to_meson_encoder_cvbs(bridge); 89 + 90 + return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge, 91 + &meson_encoder_cvbs->bridge, flags); 90 92 } 91 93 92 - static enum drm_connector_status 93 - meson_cvbs_connector_detect(struct drm_connector *connector, bool force) 94 + static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge, 95 + struct drm_connector *connector) 94 96 { 95 - /* FIXME: Add load-detect or jack-detect if possible */ 96 - return connector_status_connected; 97 - } 98 - 99 - static int meson_cvbs_connector_get_modes(struct drm_connector *connector) 100 - { 101 - struct drm_device *dev = connector->dev; 97 + struct meson_encoder_cvbs *meson_encoder_cvbs = 98 + bridge_to_meson_encoder_cvbs(bridge); 99 + struct meson_drm *priv = meson_encoder_cvbs->priv; 102 100 struct drm_display_mode *mode; 103 101 int i; 104 102 105 103 for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { 106 104 struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; 107 105 108 - mode = drm_mode_duplicate(dev, &meson_mode->mode); 106 + mode = drm_mode_duplicate(priv->drm, &meson_mode->mode); 109 107 if (!mode) { 110 - DRM_ERROR("Failed to create a new display mode\n"); 108 + dev_err(priv->dev, "Failed to create a new display mode\n"); 111 109 return 0; 112 110 } 113 111 ··· 116 116 return i; 117 117 } 118 118 119 - static int meson_cvbs_connector_mode_valid(struct drm_connector *connector, 120 - struct drm_display_mode *mode) 119 + static int meson_encoder_cvbs_mode_valid(struct drm_bridge *bridge, 120 + const struct drm_display_info *display_info, 121 + const struct drm_display_mode *mode) 121 122 { 122 - /* Validate the modes added in get_modes */ 123 - return MODE_OK; 123 + if (meson_cvbs_get_mode(mode)) 124 + return MODE_OK; 125 + 126 + return MODE_BAD; 124 127 } 125 128 126 - static const struct drm_connector_funcs meson_cvbs_connector_funcs = { 127 - .detect = meson_cvbs_connector_detect, 128 - .fill_modes = drm_helper_probe_single_connector_modes, 129 - .destroy = meson_cvbs_connector_destroy, 130 - .reset = drm_atomic_helper_connector_reset, 131 - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 132 - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 133 - }; 134 - 135 - static const 136 - struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = { 137 - .get_modes = meson_cvbs_connector_get_modes, 138 - .mode_valid = meson_cvbs_connector_mode_valid, 139 - }; 140 - 141 - /* Encoder */ 142 - 143 - static void meson_encoder_cvbs_encoder_destroy(struct drm_encoder *encoder) 144 - { 145 - drm_encoder_cleanup(encoder); 146 - } 147 - 148 - static const struct drm_encoder_funcs meson_encoder_cvbs_encoder_funcs = { 149 - .destroy = meson_encoder_cvbs_encoder_destroy, 150 - }; 151 - 152 - static int meson_encoder_cvbs_encoder_atomic_check(struct drm_encoder *encoder, 129 + static int meson_encoder_cvbs_atomic_check(struct drm_bridge *bridge, 130 + struct drm_bridge_state *bridge_state, 153 131 struct drm_crtc_state *crtc_state, 154 132 struct drm_connector_state *conn_state) 155 133 { ··· 137 159 return -EINVAL; 138 160 } 139 161 140 - static void meson_encoder_cvbs_encoder_disable(struct drm_encoder *encoder) 162 + static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, 163 + struct drm_bridge_state *bridge_state) 141 164 { 142 - struct meson_encoder_cvbs *meson_encoder_cvbs = 143 - encoder_to_meson_encoder_cvbs(encoder); 144 - struct meson_drm *priv = meson_encoder_cvbs->priv; 165 + struct meson_encoder_cvbs *encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge); 166 + struct drm_atomic_state *state = bridge_state->base.state; 167 + struct meson_drm *priv = encoder_cvbs->priv; 168 + const struct meson_cvbs_mode *meson_mode; 169 + struct drm_connector_state *conn_state; 170 + struct drm_crtc_state *crtc_state; 171 + struct drm_connector *connector; 145 172 146 - /* Disable CVBS VDAC */ 147 - if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { 148 - regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); 149 - regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); 150 - } else { 151 - regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); 152 - regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); 153 - } 154 - } 173 + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); 174 + if (WARN_ON(!connector)) 175 + return; 155 176 156 - static void meson_encoder_cvbs_encoder_enable(struct drm_encoder *encoder) 157 - { 158 - struct meson_encoder_cvbs *meson_encoder_cvbs = 159 - encoder_to_meson_encoder_cvbs(encoder); 160 - struct meson_drm *priv = meson_encoder_cvbs->priv; 177 + conn_state = drm_atomic_get_new_connector_state(state, connector); 178 + if (WARN_ON(!conn_state)) 179 + return; 180 + 181 + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); 182 + if (WARN_ON(!crtc_state)) 183 + return; 184 + 185 + meson_mode = meson_cvbs_get_mode(&crtc_state->adjusted_mode); 186 + if (WARN_ON(!meson_mode)) 187 + return; 188 + 189 + meson_venci_cvbs_mode_set(priv, meson_mode->enci); 190 + 191 + /* Setup 27MHz vclk2 for ENCI and VDAC */ 192 + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, 193 + MESON_VCLK_CVBS, MESON_VCLK_CVBS, 194 + MESON_VCLK_CVBS, MESON_VCLK_CVBS, 195 + true); 161 196 162 197 /* VDAC0 source is not from ATV */ 163 198 writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0, ··· 189 198 } 190 199 } 191 200 192 - static void meson_encoder_cvbs_encoder_mode_set(struct drm_encoder *encoder, 193 - struct drm_display_mode *mode, 194 - struct drm_display_mode *adjusted_mode) 201 + static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge, 202 + struct drm_bridge_state *bridge_state) 195 203 { 196 - const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode); 197 204 struct meson_encoder_cvbs *meson_encoder_cvbs = 198 - encoder_to_meson_encoder_cvbs(encoder); 205 + bridge_to_meson_encoder_cvbs(bridge); 199 206 struct meson_drm *priv = meson_encoder_cvbs->priv; 200 207 201 - if (meson_mode) { 202 - meson_venci_cvbs_mode_set(priv, meson_mode->enci); 203 - 204 - /* Setup 27MHz vclk2 for ENCI and VDAC */ 205 - meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, 206 - MESON_VCLK_CVBS, MESON_VCLK_CVBS, 207 - MESON_VCLK_CVBS, MESON_VCLK_CVBS, 208 - true); 208 + /* Disable CVBS VDAC */ 209 + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { 210 + regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); 211 + regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); 212 + } else { 213 + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); 214 + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); 209 215 } 210 216 } 211 217 212 - static const struct drm_encoder_helper_funcs 213 - meson_encoder_cvbs_encoder_helper_funcs = { 214 - .atomic_check = meson_encoder_cvbs_encoder_atomic_check, 215 - .disable = meson_encoder_cvbs_encoder_disable, 216 - .enable = meson_encoder_cvbs_encoder_enable, 217 - .mode_set = meson_encoder_cvbs_encoder_mode_set, 218 + static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { 219 + .attach = meson_encoder_cvbs_attach, 220 + .mode_valid = meson_encoder_cvbs_mode_valid, 221 + .get_modes = meson_encoder_cvbs_get_modes, 222 + .atomic_enable = meson_encoder_cvbs_atomic_enable, 223 + .atomic_disable = meson_encoder_cvbs_atomic_disable, 224 + .atomic_check = meson_encoder_cvbs_atomic_check, 225 + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 226 + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 227 + .atomic_reset = drm_atomic_helper_bridge_reset, 218 228 }; 219 - 220 - static bool meson_encoder_cvbs_connector_is_available(struct meson_drm *priv) 221 - { 222 - struct device_node *remote; 223 - 224 - remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); 225 - if (!remote) 226 - return false; 227 - 228 - of_node_put(remote); 229 - return true; 230 - } 231 229 232 230 int meson_encoder_cvbs_init(struct meson_drm *priv) 233 231 { 234 232 struct drm_device *drm = priv->drm; 235 233 struct meson_encoder_cvbs *meson_encoder_cvbs; 236 234 struct drm_connector *connector; 237 - struct drm_encoder *encoder; 235 + struct device_node *remote; 238 236 int ret; 239 237 240 - if (!meson_encoder_cvbs_connector_is_available(priv)) { 238 + meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL); 239 + if (!meson_encoder_cvbs) 240 + return -ENOMEM; 241 + 242 + /* CVBS Connector Bridge */ 243 + remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); 244 + if (!remote) { 241 245 dev_info(drm->dev, "CVBS Output connector not available\n"); 242 246 return 0; 243 247 } 244 248 245 - meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), 246 - GFP_KERNEL); 247 - if (!meson_encoder_cvbs) 248 - return -ENOMEM; 249 + meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote); 250 + if (!meson_encoder_cvbs->next_bridge) { 251 + dev_err(priv->dev, "Failed to find CVBS Connector bridge\n"); 252 + return -EPROBE_DEFER; 253 + } 254 + 255 + /* CVBS Encoder Bridge */ 256 + meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs; 257 + meson_encoder_cvbs->bridge.of_node = priv->dev->of_node; 258 + meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite; 259 + meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES; 260 + meson_encoder_cvbs->bridge.interlace_allowed = true; 261 + 262 + drm_bridge_add(&meson_encoder_cvbs->bridge); 249 263 250 264 meson_encoder_cvbs->priv = priv; 251 - encoder = &meson_encoder_cvbs->encoder; 252 - connector = &meson_encoder_cvbs->connector; 253 - 254 - /* Connector */ 255 - 256 - drm_connector_helper_add(connector, 257 - &meson_cvbs_connector_helper_funcs); 258 - 259 - ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs, 260 - DRM_MODE_CONNECTOR_Composite); 261 - if (ret) { 262 - dev_err(priv->dev, "Failed to init CVBS connector\n"); 263 - return ret; 264 - } 265 - 266 - connector->interlace_allowed = 1; 267 265 268 266 /* Encoder */ 269 - 270 - drm_encoder_helper_add(encoder, &meson_encoder_cvbs_encoder_helper_funcs); 271 - 272 - ret = drm_encoder_init(drm, encoder, &meson_encoder_cvbs_encoder_funcs, 273 - DRM_MODE_ENCODER_TVDAC, "meson_encoder_cvbs"); 267 + ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder, 268 + DRM_MODE_ENCODER_TVDAC); 274 269 if (ret) { 275 - dev_err(priv->dev, "Failed to init CVBS encoder\n"); 270 + dev_err(priv->dev, "Failed to init CVBS encoder: %d\n", ret); 276 271 return ret; 277 272 } 278 273 279 - encoder->possible_crtcs = BIT(0); 274 + meson_encoder_cvbs->encoder.possible_crtcs = BIT(0); 280 275 281 - drm_connector_attach_encoder(connector, encoder); 276 + /* Attach CVBS Encoder Bridge to Encoder */ 277 + ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL, 278 + DRM_BRIDGE_ATTACH_NO_CONNECTOR); 279 + if (ret) { 280 + dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); 281 + return ret; 282 + } 283 + 284 + /* Initialize & attach Bridge Connector */ 285 + connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder); 286 + if (IS_ERR(connector)) { 287 + dev_err(priv->dev, "Unable to create CVBS bridge connector\n"); 288 + return PTR_ERR(connector); 289 + } 290 + drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder); 282 291 283 292 return 0; 284 293 }