#include <sound/hdmi-codec.h>
 
 #include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_hdmi_audio_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge_connector.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include "cdn-dp-core.h"
 #include "cdn-dp-reg.h"
 
-static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
+static inline struct cdn_dp_device *bridge_to_dp(struct drm_bridge *bridge)
 {
-       return container_of(connector, struct cdn_dp_device, connector);
+       return container_of(bridge, struct cdn_dp_device, bridge);
 }
 
 static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
 }
 
 static enum drm_connector_status
-cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+cdn_dp_bridge_detect(struct drm_bridge *bridge)
 {
-       struct cdn_dp_device *dp = connector_to_dp(connector);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        enum drm_connector_status status = connector_status_disconnected;
 
        mutex_lock(&dp->lock);
        return status;
 }
 
-static void cdn_dp_connector_destroy(struct drm_connector *connector)
+static const struct drm_edid *
+cdn_dp_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector)
 {
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
-static const struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
-       .detect = cdn_dp_connector_detect,
-       .destroy = cdn_dp_connector_destroy,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .reset = drm_atomic_helper_connector_reset,
-       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int cdn_dp_connector_get_modes(struct drm_connector *connector)
-{
-       struct cdn_dp_device *dp = connector_to_dp(connector);
-       int ret = 0;
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
+       const struct drm_edid *drm_edid;
 
        mutex_lock(&dp->lock);
-
-       ret = drm_edid_connector_add_modes(connector);
-
+       drm_edid = drm_edid_read_custom(connector, cdn_dp_get_edid_block, dp);
        mutex_unlock(&dp->lock);
 
-       return ret;
+       return drm_edid;
 }
 
 static enum drm_mode_status
-cdn_dp_connector_mode_valid(struct drm_connector *connector,
-                           const struct drm_display_mode *mode)
+cdn_dp_bridge_mode_valid(struct drm_bridge *bridge,
+                        const struct drm_display_info *display_info,
+                        const struct drm_display_mode *mode)
 {
-       struct cdn_dp_device *dp = connector_to_dp(connector);
-       struct drm_display_info *display_info = &dp->connector.display_info;
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        u32 requested, actual, rate, sink_max, source_max = 0;
        u8 lanes, bpc;
 
        return MODE_OK;
 }
 
-static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
-       .get_modes = cdn_dp_connector_get_modes,
-       .mode_valid = cdn_dp_connector_mode_valid,
-};
-
 static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
 {
        int ret;
 
 static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
 {
-       const struct drm_display_info *info = &dp->connector.display_info;
        int ret;
 
        if (!cdn_dp_check_sink_connection(dp))
                return ret;
        }
 
-       drm_edid_free(dp->drm_edid);
-       dp->drm_edid = drm_edid_read_custom(&dp->connector,
-                                           cdn_dp_get_edid_block, dp);
-       drm_edid_connector_update(&dp->connector, dp->drm_edid);
-
-       dp->sink_has_audio = info->has_audio;
-
-       if (dp->drm_edid)
-               DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n",
-                                 info->width_mm / 10, info->height_mm / 10);
-
        return 0;
 }
 
        dp->active = false;
        dp->max_lanes = 0;
        dp->max_rate = 0;
-       if (!dp->connected) {
-               drm_edid_free(dp->drm_edid);
-               dp->drm_edid = NULL;
-       }
 
        return 0;
 }
        return ret;
 }
 
-static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
-                                   struct drm_display_mode *mode,
-                                   struct drm_display_mode *adjusted)
+static void cdn_dp_bridge_mode_set(struct drm_bridge *bridge,
+                                  const struct drm_display_mode *mode,
+                                  const struct drm_display_mode *adjusted)
 {
-       struct cdn_dp_device *dp = encoder_to_dp(encoder);
-       struct drm_display_info *display_info = &dp->connector.display_info;
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        struct video_info *video = &dp->video_info;
 
-       switch (display_info->bpc) {
-       case 10:
-               video->color_depth = 10;
-               break;
-       case 6:
-               video->color_depth = 6;
-               break;
-       default:
-               video->color_depth = 8;
-               break;
-       }
-
        video->color_fmt = PXL_RGB;
        video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
        video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
        return drm_dp_channel_eq_ok(link_status, min(port->lanes, sink_lanes));
 }
 
-static void cdn_dp_audio_handle_plugged_change(struct cdn_dp_device *dp,
-                                              bool plugged)
+static void cdn_dp_display_info_update(struct cdn_dp_device *dp,
+                                      struct drm_display_info *display_info)
 {
-       if (dp->codec_dev)
-               dp->plugged_cb(dp->codec_dev, plugged);
+       struct video_info *video = &dp->video_info;
+
+       switch (display_info->bpc) {
+       case 10:
+               video->color_depth = 10;
+               break;
+       case 6:
+               video->color_depth = 6;
+               break;
+       default:
+               video->color_depth = 8;
+               break;
+       }
 }
 
-static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+static void cdn_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state)
 {
-       struct cdn_dp_device *dp = encoder_to_dp(encoder);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
+       struct drm_connector *connector;
        int ret, val;
 
-       ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+       connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+       if (!connector)
+               return;
+
+       cdn_dp_display_info_update(dp, &connector->display_info);
+
+       ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, &dp->encoder.encoder);
        if (ret < 0) {
                DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret);
                return;
 
        ret = cdn_dp_enable(dp);
        if (ret) {
-               DRM_DEV_ERROR(dp->dev, "Failed to enable encoder %d\n",
+               DRM_DEV_ERROR(dp->dev, "Failed to enable bridge %d\n",
                              ret);
                goto out;
        }
                goto out;
        }
 
-       cdn_dp_audio_handle_plugged_change(dp, true);
-
 out:
        mutex_unlock(&dp->lock);
 }
 
-static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+static void cdn_dp_bridge_atomic_disable(struct drm_bridge *bridge, struct drm_atomic_state *state)
 {
-       struct cdn_dp_device *dp = encoder_to_dp(encoder);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        int ret;
 
        mutex_lock(&dp->lock);
-       cdn_dp_audio_handle_plugged_change(dp, false);
 
        if (dp->active) {
                ret = cdn_dp_disable(dp);
                if (ret) {
-                       DRM_DEV_ERROR(dp->dev, "Failed to disable encoder %d\n",
+                       DRM_DEV_ERROR(dp->dev, "Failed to disable bridge %d\n",
                                      ret);
                }
        }
 }
 
 static const struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
-       .mode_set = cdn_dp_encoder_mode_set,
-       .enable = cdn_dp_encoder_enable,
-       .disable = cdn_dp_encoder_disable,
        .atomic_check = cdn_dp_encoder_atomic_check,
 };
 
        return 0;
 }
 
-static int cdn_dp_audio_hw_params(struct device *dev,  void *data,
-                                 struct hdmi_codec_daifmt *daifmt,
-                                 struct hdmi_codec_params *params)
+static int cdn_dp_audio_prepare(struct drm_connector *connector,
+                               struct drm_bridge *bridge,
+                               struct hdmi_codec_daifmt *daifmt,
+                               struct hdmi_codec_params *params)
 {
-       struct cdn_dp_device *dp = dev_get_drvdata(dev);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        struct audio_info audio = {
                .sample_width = params->sample_width,
                .sample_rate = params->sample_rate,
                audio.format = AFMT_SPDIF;
                break;
        default:
-               DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt);
+               drm_err(bridge->dev, "Invalid format %d\n", daifmt->fmt);
                ret = -EINVAL;
                goto out;
        }
        return ret;
 }
 
-static void cdn_dp_audio_shutdown(struct device *dev, void *data)
+static void cdn_dp_audio_shutdown(struct drm_connector *connector,
+                                 struct drm_bridge *bridge)
 {
-       struct cdn_dp_device *dp = dev_get_drvdata(dev);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        int ret;
 
        mutex_lock(&dp->lock);
        mutex_unlock(&dp->lock);
 }
 
-static int cdn_dp_audio_mute_stream(struct device *dev, void *data,
+static int cdn_dp_audio_mute_stream(struct drm_connector *connector,
+                                   struct drm_bridge *bridge,
                                    bool enable, int direction)
 {
-       struct cdn_dp_device *dp = dev_get_drvdata(dev);
+       struct cdn_dp_device *dp = bridge_to_dp(bridge);
        int ret;
 
        mutex_lock(&dp->lock);
        return ret;
 }
 
-static int cdn_dp_audio_get_eld(struct device *dev, void *data,
-                               u8 *buf, size_t len)
-{
-       struct cdn_dp_device *dp = dev_get_drvdata(dev);
-
-       memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len));
-
-       return 0;
-}
-
-static int cdn_dp_audio_hook_plugged_cb(struct device *dev, void *data,
-                                       hdmi_codec_plugged_cb fn,
-                                       struct device *codec_dev)
-{
-       struct cdn_dp_device *dp = dev_get_drvdata(dev);
-
-       mutex_lock(&dp->lock);
-       dp->plugged_cb = fn;
-       dp->codec_dev = codec_dev;
-       cdn_dp_audio_handle_plugged_change(dp, dp->connected);
-       mutex_unlock(&dp->lock);
-
-       return 0;
-}
-
-static const struct hdmi_codec_ops audio_codec_ops = {
-       .hw_params = cdn_dp_audio_hw_params,
-       .audio_shutdown = cdn_dp_audio_shutdown,
-       .mute_stream = cdn_dp_audio_mute_stream,
-       .get_eld = cdn_dp_audio_get_eld,
-       .hook_plugged_cb = cdn_dp_audio_hook_plugged_cb,
+static const struct drm_bridge_funcs cdn_dp_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset = drm_atomic_helper_bridge_reset,
+       .detect = cdn_dp_bridge_detect,
+       .edid_read = cdn_dp_bridge_edid_read,
+       .atomic_enable = cdn_dp_bridge_atomic_enable,
+       .atomic_disable = cdn_dp_bridge_atomic_disable,
+       .mode_valid = cdn_dp_bridge_mode_valid,
+       .mode_set = cdn_dp_bridge_mode_set,
+
+       .dp_audio_prepare = cdn_dp_audio_prepare,
+       .dp_audio_mute_stream = cdn_dp_audio_mute_stream,
+       .dp_audio_shutdown = cdn_dp_audio_shutdown,
 };
 
-static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
-                                  struct device *dev)
-{
-       struct hdmi_codec_pdata codec_data = {
-               .i2s = 1,
-               .spdif = 1,
-               .ops = &audio_codec_ops,
-               .max_i2s_channels = 8,
-               .no_capture_mute = 1,
-       };
-
-       dp->audio_pdev = platform_device_register_data(
-                        dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
-                        &codec_data, sizeof(codec_data));
-
-       return PTR_ERR_OR_ZERO(dp->audio_pdev);
-}
-
 static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
 {
        int ret;
 
 out:
        mutex_unlock(&dp->lock);
-       drm_connector_helper_hpd_irq_event(&dp->connector);
+       drm_bridge_hpd_notify(&dp->bridge,
+                             dp->connected ? connector_status_connected
+                                           : connector_status_disconnected);
 }
 
 static int cdn_dp_pd_event(struct notifier_block *nb,
 
        drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
 
-       connector = &dp->connector;
-       connector->polled = DRM_CONNECTOR_POLL_HPD;
-       connector->dpms = DRM_MODE_DPMS_OFF;
-
-       ret = drm_connector_init(drm_dev, connector,
-                                &cdn_dp_atomic_connector_funcs,
-                                DRM_MODE_CONNECTOR_DisplayPort);
-       if (ret) {
-               DRM_ERROR("failed to initialize connector with drm\n");
-               goto err_free_encoder;
-       }
+       dp->bridge.ops =
+                       DRM_BRIDGE_OP_DETECT |
+                       DRM_BRIDGE_OP_EDID |
+                       DRM_BRIDGE_OP_HPD |
+                       DRM_BRIDGE_OP_DP_AUDIO;
+       dp->bridge.of_node = dp->dev->of_node;
+       dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+       dp->bridge.hdmi_audio_dev = dp->dev;
+       dp->bridge.hdmi_audio_max_i2s_playback_channels = 8;
+       dp->bridge.hdmi_audio_spdif_playback = 1;
+       dp->bridge.hdmi_audio_dai_port = -1;
+
+       ret = devm_drm_bridge_add(dev, &dp->bridge);
+       if (ret)
+               return ret;
 
-       drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+       ret = drm_bridge_attach(encoder, &dp->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+       if (ret)
+               return ret;
 
-       ret = drm_connector_attach_encoder(connector, encoder);
-       if (ret) {
-               DRM_ERROR("failed to attach connector and encoder\n");
-               goto err_free_connector;
+       connector = drm_bridge_connector_init(drm_dev, encoder);
+       if (IS_ERR(connector)) {
+               ret = PTR_ERR(connector);
+               dev_err(dp->dev, "failed to init bridge connector: %d\n", ret);
+               return ret;
        }
 
+       drm_connector_attach_encoder(connector, encoder);
+
        for (i = 0; i < dp->ports; i++) {
                port = dp->port[i];
 
                if (ret) {
                        DRM_DEV_ERROR(dev,
                                      "register EXTCON_DISP_DP notifier err\n");
-                       goto err_free_connector;
+                       return ret;
                }
        }
 
        schedule_work(&dp->event_work);
 
        return 0;
-
-err_free_connector:
-       drm_connector_cleanup(connector);
-err_free_encoder:
-       drm_encoder_cleanup(encoder);
-       return ret;
 }
 
 static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 {
        struct cdn_dp_device *dp = dev_get_drvdata(dev);
        struct drm_encoder *encoder = &dp->encoder.encoder;
-       struct drm_connector *connector = &dp->connector;
 
        cancel_work_sync(&dp->event_work);
-       cdn_dp_encoder_disable(encoder);
        encoder->funcs->destroy(encoder);
-       connector->funcs->destroy(connector);
 
        pm_runtime_disable(dev);
        if (dp->fw_loaded)
                release_firmware(dp->fw);
-       drm_edid_free(dp->drm_edid);
-       dp->drm_edid = NULL;
 }
 
 static const struct component_ops cdn_dp_component_ops = {
        int ret;
        int i;
 
-       dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
-       if (!dp)
-               return -ENOMEM;
+       dp = devm_drm_bridge_alloc(dev, struct cdn_dp_device, bridge,
+                                  &cdn_dp_bridge_funcs);
+       if (IS_ERR(dp))
+               return PTR_ERR(dp);
        dp->dev = dev;
 
        match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
        mutex_init(&dp->lock);
        dev_set_drvdata(dev, dp);
 
-       ret = cdn_dp_audio_codec_init(dp, dev);
-       if (ret)
-               return ret;
-
        ret = component_add(dev, &cdn_dp_component_ops);
        if (ret)
-               goto err_audio_deinit;
+               return ret;
 
        return 0;
-
-err_audio_deinit:
-       platform_device_unregister(dp->audio_pdev);
-       return ret;
 }
 
 static void cdn_dp_remove(struct platform_device *pdev)