| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Cadence MHDP DP MST bridge driver.
- *
- * Copyright: 2018 Cadence Design Systems, Inc.
- *
- * Author: Piotr Sroka <piotrs@cadence.com>
- */
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_connector.h>
- #include <drm/drm_crtc_helper.h>
- #include <drm/drm_fixed.h>
- #include <drm/drm_print.h>
- #include <drm/drm_probe_helper.h>
- #include <linux/iopoll.h>
- #include <drm/bridge/cdns-mhdp-common.h>
- #include "cdns-mhdp.h"
- static void cdns_mhdp_mst_stream_enable(struct cdns_mhdp_bridge *mhdp_bridge,
- const bool enable)
- {
- u32 reg;
- struct cdns_mhdp_device *mhdp = mhdp_bridge->mhdp;
- const u8 stream_id = mhdp_bridge->stream_id;
- cdns_mhdp_reg_read(mhdp, CDNS_DP_MST_STREAM_CONFIG(stream_id), ®);
- if (enable) {
- reg |= CDNS_DP_MST_STRM_CFG_STREAM_EN;
- reg &= ~CDNS_DP_MST_STRM_CFG_NO_VIDEO;
- } else {
- reg &= ~CDNS_DP_MST_STRM_CFG_STREAM_EN;
- reg |= CDNS_DP_MST_STRM_CFG_NO_VIDEO;
- }
- cdns_mhdp_reg_write(mhdp, CDNS_DP_MST_STREAM_CONFIG(stream_id), reg);
- }
- static inline s64 calc_fixed_avg_slots(const u32 pbn, const u32 pbn_div)
- {
- s64 fixed_pbn, fixed_pbn_div, fixed_targ_avg_slots;
- fixed_pbn = drm_int2fixp(pbn);
- fixed_pbn_div = drm_int2fixp(pbn_div);
- fixed_targ_avg_slots = drm_fixp_div(fixed_pbn, fixed_pbn_div);
- return fixed_targ_avg_slots;
- }
- static void cdns_mhdp_set_rate_governing(struct cdns_mhdp_bridge *bridge,
- const bool enable)
- {
- struct cdns_mhdp_device *mhdp = bridge->mhdp;
- const u8 stream_id = bridge->stream_id;
- s64 fixed_targ_avg_slots, fixed_y;
- u32 x, y;
- if (!enable) {
- cdns_mhdp_reg_write(mhdp, CDNS_DP_RATE_GOVERNING(stream_id), 0);
- return;
- }
- fixed_targ_avg_slots = calc_fixed_avg_slots(bridge->pbn,
- mhdp->mst_mgr.pbn_div);
- x = bridge->pbn / mhdp->mst_mgr.pbn_div;
- fixed_y = fixed_targ_avg_slots - drm_int2fixp(x);
- fixed_y *= 16;
- y = drm_fixp2int_ceil(fixed_y);
- cdns_mhdp_reg_write(mhdp, CDNS_DP_RATE_GOVERNING(stream_id),
- CDNS_DP_RG_TARG_AV_SLOTS_Y(y) |
- CDNS_DP_RG_TARG_AV_SLOTS_X(x) |
- CDNS_DP_RG_ENABLE);
- }
- static struct drm_dp_payload *
- cdns_mhdp_get_payload(struct cdns_mhdp_bridge *bridge)
- {
- const int vcpi = bridge->connector->port->vcpi.vcpi;
- struct cdns_mhdp_device *mhdp = bridge->mhdp;
- int i;
- for (i = 0; i < mhdp->mst_mgr.max_payloads; i++) {
- struct drm_dp_payload *payload = &mhdp->mst_mgr.payloads[i];
- if (payload->vcpi == vcpi)
- return payload;
- }
- return NULL;
- }
- static int
- cdns_mhdp_set_act_enable(struct cdns_mhdp_device *mhdp)
- {
- u32 reg;
- int ret;
- cdns_mhdp_reg_read(mhdp, CDNS_DP_MTPH_CONTROL, ®);
- cdns_mhdp_reg_write(mhdp, CDNS_DP_MTPH_CONTROL,
- reg | CDNS_DP_MTPH_ACT_EN);
- ret = readl_poll_timeout(mhdp->regs + CDNS_DP_MTPH_STATUS, reg,
- ((reg & CDNS_DP_MTPH_ACT_STATUS) == 0), 0,
- 30);
- if (ret) {
- dev_err(mhdp->dev,
- "ACT sequence cannot complete in 30us\n");
- return -EIO;
- }
- return drm_dp_check_act_status(&mhdp->mst_mgr);
- }
- static int
- cdns_mhdp_apply_slot_allocation(struct cdns_mhdp_bridge *mhdp_bridge)
- {
- struct drm_dp_payload *payload;
- struct cdns_mhdp_device *mhdp = mhdp_bridge->mhdp;
- u8 stream_id = mhdp_bridge->stream_id;
- payload = cdns_mhdp_get_payload(mhdp_bridge);
- if (!payload) {
- DRM_ERROR("payload is not found\n");
- return -EIO;
- }
- cdns_mhdp_reg_write(mhdp, CDNS_DP_MST_SLOT_ALLOCATE(stream_id),
- CDNS_DP_S_ALLOC_START_SLOT(payload->start_slot) |
- CDNS_DP_S_ALLOC_END_SLOT(payload->start_slot +
- payload->num_slots - 1));
- return 0;
- }
- static void
- cdns_mhdp_update_slot_allocation(struct cdns_mhdp_bridge *mhdp_bridge)
- {
- struct drm_device *dev = mhdp_bridge->base.dev;
- struct drm_connector *connector;
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- struct cdns_mhdp_connector *mhdp_connector;
- if (!connector->encoder)
- continue;
- mhdp_connector = to_mhdp_connector(connector);
- if (!mhdp_connector->is_mst_connector)
- continue;
- if (mhdp_connector->bridge->stream_id != -1)
- cdns_mhdp_apply_slot_allocation(mhdp_connector->bridge);
- }
- }
- static enum drm_connector_status
- cdns_dp_mst_detect(struct drm_connector *connector, bool force)
- {
- enum drm_connector_status stat;
- struct cdns_mhdp_connector *mhdp_connector;
- struct cdns_mhdp_device *mhdp;
- mhdp_connector = to_mhdp_connector(connector);
- mhdp = mhdp_connector->bridge->mhdp;
- stat = drm_dp_mst_detect_port(connector, &mhdp->mst_mgr,
- mhdp_connector->port);
- return stat;
- }
- static void cdns_dp_mst_connector_destroy(struct drm_connector *connector)
- {
- struct cdns_mhdp_connector *mhdp_connector;
- struct cdns_mhdp_bridge *mhdp_bridge;
- mhdp_connector = to_mhdp_connector(connector);
- mhdp_bridge = mhdp_connector->bridge;
- drm_connector_cleanup(&mhdp_connector->base);
- drm_bridge_remove(&mhdp_bridge->base);
- kfree(mhdp_connector);
- kfree(mhdp_bridge);
- }
- static const struct drm_connector_funcs cdns_mhdp_mst_connector_funcs = {
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .reset = drm_atomic_helper_connector_reset,
- .dpms = drm_helper_connector_dpms,
- .detect = cdns_dp_mst_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = cdns_dp_mst_connector_destroy,
- };
- static int cdns_mhdp_mst_get_ddc_modes(struct drm_connector *connector)
- {
- struct cdns_mhdp_connector *mhdp_connector;
- struct cdns_mhdp_device *mhdp;
- struct edid *edid;
- int num_modes = 0;
- mhdp_connector = to_mhdp_connector(connector);
- mhdp = mhdp_connector->bridge->mhdp;
- edid = drm_dp_mst_get_edid(connector, &mhdp->mst_mgr,
- mhdp_connector->port);
- if (edid) {
- DRM_DEBUG_KMS("edid retrieved %p\n", edid);
- drm_connector_update_edid_property(connector, edid);
- num_modes = drm_add_edid_modes(connector, edid);
- kfree(edid);
- } else
- drm_connector_update_edid_property(connector, NULL);
- return num_modes;
- }
- static int cdns_mhdp_mst_get_modes(struct drm_connector *connector)
- {
- return cdns_mhdp_mst_get_ddc_modes(connector);
- }
- static struct
- drm_encoder *cdns_mhdp_mst_best_encoder(struct drm_connector *connector)
- {
- struct cdns_mhdp_connector *mhdp_connector;
- mhdp_connector = to_mhdp_connector(connector);
- return mhdp_connector->bridge->base.encoder;
- }
- static const struct drm_connector_helper_funcs cdns_mhdp_mst_conn_helper_fun = {
- .get_modes = cdns_mhdp_mst_get_modes,
- .best_encoder = cdns_mhdp_mst_best_encoder,
- };
- void cdns_mhdp_mst_enable(struct drm_bridge *bridge)
- {
- struct cdns_mhdp_bridge *mhdp_bridge = to_mhdp_bridge(bridge);
- struct cdns_mhdp_device *mhdp = mhdp_bridge->mhdp;
- struct drm_display_info *disp_info;
- struct drm_display_mode *mode;
- struct cdns_mhdp_connector *mhdp_connector;
- u32 bpp;
- enum pixel_format pxlfmt;
- int ret, slots, stream_id;
- disp_info = &mhdp_bridge->connector->base.display_info;
- pxlfmt = cdns_mhdp_get_pxlfmt(disp_info->color_formats);
- bpp = cdns_mhdp_get_bpp(disp_info->bpc, pxlfmt);
- mhdp_connector = mhdp_bridge->connector;
- if (mhdp_bridge->stream_id > -1) {
- DRM_ERROR("stream id is attached before bridge is enabled\n");
- return;
- }
- stream_id = bridge->encoder->crtc->index;
- mode = &bridge->encoder->crtc->state->adjusted_mode;
- mhdp_bridge->pbn = drm_dp_calc_pbn_mode(mode->clock, bpp);
- slots = drm_dp_find_vcpi_slots(&mhdp->mst_mgr, mhdp_bridge->pbn);
- ret = drm_dp_mst_allocate_vcpi(&mhdp->mst_mgr,
- mhdp_connector->port,
- mhdp_bridge->pbn, slots);
- if (ret == false) {
- DRM_ERROR("failed to allocate vcpi\n");
- return;
- }
- ret = drm_dp_update_payload_part1(&mhdp->mst_mgr);
- if (ret < 0)
- DRM_ERROR("failed update_payload_part1\n");
- mhdp_bridge->stream_id = stream_id;
- mhdp_bridge->is_active = true;
- cdns_mhdp_mst_stream_enable(mhdp_bridge, true);
- cdns_mhdp_configure_video(bridge);
- ret = cdns_mhdp_apply_slot_allocation(mhdp_bridge);
- if (ret < 0) {
- cdns_mhdp_mst_stream_enable(mhdp_bridge, false);
- mhdp_bridge->stream_id = -1;
- mhdp_bridge->is_active = false;
- return;
- }
- ret = cdns_mhdp_set_act_enable(mhdp);
- if (ret)
- DRM_ERROR("failed ACT sequence\n");
- cdns_mhdp_set_rate_governing(mhdp_bridge, true);
- drm_dp_update_payload_part2(&mhdp->mst_mgr);
- }
- void cdns_mhdp_mst_disable(struct drm_bridge *bridge)
- {
- struct cdns_mhdp_bridge *mhdp_bridge = to_mhdp_bridge(bridge);
- struct cdns_mhdp_device *mhdp = mhdp_bridge->mhdp;
- struct cdns_mhdp_connector *connector = mhdp_bridge->connector;
- drm_dp_mst_reset_vcpi_slots(&mhdp->mst_mgr, connector->port);
- drm_dp_update_payload_part1(&mhdp->mst_mgr);
- cdns_mhdp_update_slot_allocation(mhdp_bridge);
- drm_dp_check_act_status(&mhdp->mst_mgr);
- drm_dp_update_payload_part2(&mhdp->mst_mgr);
- drm_dp_mst_deallocate_vcpi(&mhdp->mst_mgr, connector->port);
- cdns_mhdp_set_rate_governing(mhdp_bridge, false);
- cdns_mhdp_mst_stream_enable(mhdp_bridge, false);
- mhdp_bridge->stream_id = -1;
- mhdp_bridge->is_active = false;
- }
- static const struct drm_bridge_funcs cdns_mhdp_mst_bridge_funcs = {
- .enable = cdns_mhdp_enable,
- .disable = cdns_mhdp_mst_disable,
- };
- static struct cdns_mhdp_bridge*
- cdns_mhpd_create_fake_mst_bridge(struct cdns_mhdp_device *mhdp,
- struct cdns_mhdp_connector *mhdp_connector)
- {
- struct cdns_mhdp_bridge *mhdp_bridge;
- struct drm_encoder *encoder = NULL;
- struct cdns_mhdp_mst_cbs *cbs = &mhdp->cbs;
- mhdp_bridge = kzalloc(sizeof(*mhdp_bridge), GFP_KERNEL);
- if (!mhdp_bridge)
- return NULL;
- mhdp_bridge->mhdp = mhdp;
- mhdp_bridge->stream_id = -1;
- mhdp_bridge->connector = mhdp_connector;
- mhdp_bridge->is_active = false;
- mhdp_bridge->base.funcs = &cdns_mhdp_mst_bridge_funcs;
- drm_bridge_add(&mhdp_bridge->base);
- if (cbs->funcs.create_mst_encoder)
- encoder = cbs->funcs.create_mst_encoder(cbs->priv_data,
- &mhdp_bridge->base);
- if (encoder) {
- int ret;
- /* use the same drm device as is in the first encoder */
- encoder->dev = mhdp->bridge.base.encoder->dev;
- encoder->possible_crtcs &= ((1 << CDNS_MHDP_MAX_STREAMS) - 1);
- ret = drm_bridge_attach(encoder, &mhdp_bridge->base, NULL);
- if (ret) {
- dev_err(mhdp->dev, "bridge attaching error %d\n", ret);
- return NULL;
- }
- ret = drm_connector_attach_encoder(&mhdp_connector->base,
- encoder);
- if (ret) {
- dev_err(mhdp->dev, "failed to attach connector to encoder\n");
- return NULL;
- }
- }
- return mhdp_bridge;
- }
- static struct drm_connector *
- cdns_mhdp_mst_cbs_add_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- const char *pathprop)
- {
- struct cdns_mhdp_device *mhdp = mgr_to_mhdp(mgr);
- struct drm_device *dev = mhdp->bridge.base.dev;
- struct cdns_mhdp_connector *mhdp_connector;
- struct drm_connector *connector;
- struct drm_connector_state *conn_state;
- int ret;
- mhdp_connector = kzalloc(sizeof(struct cdns_mhdp_connector),
- GFP_KERNEL);
- if (!mhdp_connector)
- return NULL;
- mhdp_connector->is_mst_connector = true;
- connector = &mhdp_connector->base;
- mhdp_connector->port = port;
- DRM_DEBUG_KMS("\n");
- conn_state = kzalloc(sizeof(*conn_state), GFP_KERNEL);
- if (!conn_state)
- return NULL;
- __drm_atomic_helper_connector_reset(connector,
- conn_state);
- drm_connector_init(dev, connector, &cdns_mhdp_mst_connector_funcs,
- DRM_MODE_CONNECTOR_DisplayPort);
- drm_connector_helper_add(connector, &cdns_mhdp_mst_conn_helper_fun);
- mhdp_connector->bridge =
- cdns_mhpd_create_fake_mst_bridge(mhdp, mhdp_connector);
- drm_object_attach_property(&connector->base,
- dev->mode_config.path_property, 0);
- drm_object_attach_property(&connector->base,
- dev->mode_config.tile_property, 0);
- ret = drm_connector_set_path_property(connector, pathprop);
- if (ret)
- DRM_ERROR("set path propertty failed\n");
- return connector;
- }
- static void
- cdns_mhdp_mst_cbs_destroy_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_connector *connector)
- {
- struct cdns_mhdp_connector *mhdp_connector;
- struct cdns_mhdp_device *mhdp;
- struct cdns_mhdp_bridge *mhdp_bridge;
- mhdp_connector = to_mhdp_connector(connector);
- mhdp_bridge = mhdp_connector->bridge;
- mhdp = mhdp_bridge->mhdp;
- drm_connector_unregister(&mhdp_connector->base);
- if (mhdp->cbs.funcs.create_mst_encoder)
- mhdp->cbs.funcs.destroy_mst_encoder(mhdp->cbs.priv_data,
- &mhdp_bridge->base);
- drm_connector_put(&mhdp_connector->base);
- }
- static void
- cdns_mhdp_mst_cbs_register_connector(struct drm_connector *connector)
- {
- int ret;
- ret = drm_connector_register(connector);
- if (ret)
- DRM_ERROR("Register connector failed\n");
- }
- static const struct drm_dp_mst_topology_cbs mst_cbs = {
- .add_connector = cdns_mhdp_mst_cbs_add_connector,
- .register_connector = cdns_mhdp_mst_cbs_register_connector,
- .destroy_connector = cdns_mhdp_mst_cbs_destroy_connector,
- };
- static void cdns_mhdp_set_mst_enable(struct cdns_mhdp_device *mhdp, bool enable)
- {
- u32 reg_val;
- cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, ®_val);
- if (enable)
- reg_val |= CDNS_DP_MST_EN;
- else
- reg_val &= ~CDNS_DP_MST_EN;
- cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, reg_val);
- }
- bool cdns_mhdp_mst_probe(struct cdns_mhdp_device *mhdp)
- {
- u8 mstm_cap;
- u8 dpcd_cap[DP_RECEIVER_CAP_SIZE];
- bool is_mst;
- if (!mhdp->can_mst)
- return false;
- drm_dp_dpcd_read(&mhdp->aux, DP_DPCD_REV, dpcd_cap,
- DP_RECEIVER_CAP_SIZE);
- if (dpcd_cap[DP_DPCD_REV] < 0x12)
- return false;
- if (drm_dp_dpcd_readb(&mhdp->aux, DP_MSTM_CAP, &mstm_cap) != 1)
- return false;
- if (mstm_cap & DP_MST_CAP) {
- DRM_DEBUG_KMS("Sink is MST capable\n");
- is_mst = true;
- } else {
- DRM_DEBUG_KMS("Sink is not MST capable\n");
- is_mst = false;
- }
- if (is_mst != mhdp->is_mst) {
- mhdp->is_mst = is_mst;
- cdns_mhdp_set_mst_enable(mhdp, mhdp->is_mst);
- drm_dp_mst_topology_mgr_set_mst(&mhdp->mst_mgr,
- mhdp->is_mst);
- }
- return mhdp->is_mst;
- }
- int cdns_mhdp_mst_init(struct cdns_mhdp_device *mhdp)
- {
- struct cdns_mhdp_bridge *bridge = &mhdp->bridge;
- struct drm_device *dev = bridge->base.dev;
- struct cdns_mhdp_connector *connector = bridge->connector;
- int ret;
- mhdp->mst_mgr.cbs = &mst_cbs;
- ret = drm_dp_mst_topology_mgr_init(&mhdp->mst_mgr, dev,
- &mhdp->aux, 16,
- CDNS_MHDP_MAX_STREAMS,
- connector->base.base.id);
- mhdp->can_mst = ret ? false : true;
- mhdp->is_mst = false;
- bridge->stream_id = -1;
- return ret;
- }
- void cdns_mhdp_mst_deinit(struct cdns_mhdp_device *mhdp)
- {
- if (mhdp->is_mst) {
- mhdp->is_mst = false;
- drm_dp_mst_topology_mgr_set_mst(&mhdp->mst_mgr,
- mhdp->is_mst);
- }
- if (mhdp->can_mst)
- drm_dp_mst_topology_mgr_destroy(&mhdp->mst_mgr);
- }
|