aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/bridge/ti-sn65dsi86.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c119
1 files changed, 96 insertions, 23 deletions
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index d6dd4d99a229..3c3561942eb6 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -29,6 +29,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_edid.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
@@ -68,6 +69,7 @@
#define BPP_18_RGB BIT(0)
#define SN_HPD_DISABLE_REG 0x5C
#define HPD_DISABLE BIT(0)
+#define HPD_DEBOUNCED_STATE BIT(4)
#define SN_GPIO_IO_REG 0x5E
#define SN_GPIO_INPUT_SHIFT 4
#define SN_GPIO_OUTPUT_SHIFT 0
@@ -92,6 +94,8 @@
#define SN_DATARATE_CONFIG_REG 0x94
#define DP_DATARATE_MASK GENMASK(7, 5)
#define DP_DATARATE(x) ((x) << 5)
+#define SN_TRAINING_SETTING_REG 0x95
+#define SCRAMBLE_DISABLE BIT(4)
#define SN_ML_TX_MODE_REG 0x96
#define ML_TX_MAIN_LINK_OFF 0
#define ML_TX_NORMAL_MODE BIT(0)
@@ -698,11 +702,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,
struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
int ret;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
- return -EINVAL;
- }
-
pdata->aux.drm_dev = bridge->dev;
ret = drm_dp_aux_register(&pdata->aux);
if (ret < 0) {
@@ -710,15 +709,18 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,
return ret;
}
- /* We never want the next bridge to *also* create a connector: */
- flags |= DRM_BRIDGE_ATTACH_NO_CONNECTOR;
-
- /* Attach the next bridge */
+ /*
+ * Attach the next bridge.
+ * We never want the next bridge to *also* create a connector.
+ */
ret = drm_bridge_attach(bridge->encoder, pdata->next_bridge,
- &pdata->bridge, flags);
+ &pdata->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
goto err_initted_aux;
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return 0;
+
pdata->connector = drm_bridge_connector_init(pdata->bridge.dev,
pdata->bridge.encoder);
if (IS_ERR(pdata->connector)) {
@@ -749,6 +751,29 @@ ti_sn_bridge_mode_valid(struct drm_bridge *bridge,
if (mode->clock > 594000)
return MODE_CLOCK_HIGH;
+ /*
+ * The front and back porch registers are 8 bits, and pulse width
+ * registers are 15 bits, so reject any modes with larger periods.
+ */
+
+ if ((mode->hsync_start - mode->hdisplay) > 0xff)
+ return MODE_HBLANK_WIDE;
+
+ if ((mode->vsync_start - mode->vdisplay) > 0xff)
+ return MODE_VBLANK_WIDE;
+
+ if ((mode->hsync_end - mode->hsync_start) > 0x7fff)
+ return MODE_HSYNC_WIDE;
+
+ if ((mode->vsync_end - mode->vsync_start) > 0x7fff)
+ return MODE_VSYNC_WIDE;
+
+ if ((mode->htotal - mode->hsync_end) > 0xff)
+ return MODE_HBLANK_WIDE;
+
+ if ((mode->vtotal - mode->vsync_end) > 0xff)
+ return MODE_VBLANK_WIDE;
+
return MODE_OK;
}
@@ -779,9 +804,9 @@ static void ti_sn_bridge_set_dsi_rate(struct ti_sn65dsi86 *pdata)
regmap_write(pdata->regmap, SN_DSIA_CLK_FREQ_REG, val);
}
-static unsigned int ti_sn_bridge_get_bpp(struct ti_sn65dsi86 *pdata)
+static unsigned int ti_sn_bridge_get_bpp(struct drm_connector *connector)
{
- if (pdata->connector->display_info.bpc <= 6)
+ if (connector->display_info.bpc <= 6)
return 18;
else
return 24;
@@ -796,7 +821,7 @@ static const unsigned int ti_sn_bridge_dp_rate_lut[] = {
0, 1620, 2160, 2430, 2700, 3240, 4320, 5400
};
-static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata)
+static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata, unsigned int bpp)
{
unsigned int bit_rate_khz, dp_rate_mhz;
unsigned int i;
@@ -804,7 +829,7 @@ static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata)
&pdata->bridge.encoder->crtc->state->adjusted_mode;
/* Calculate minimum bit rate based on our pixel clock. */
- bit_rate_khz = mode->clock * ti_sn_bridge_get_bpp(pdata);
+ bit_rate_khz = mode->clock * bpp;
/* Calculate minimum DP data rate, taking 80% as per DP spec */
dp_rate_mhz = DIV_ROUND_UP(bit_rate_khz * DP_CLK_FUDGE_NUM,
@@ -1016,12 +1041,21 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ struct drm_connector *connector;
const char *last_err_str = "No supported DP rate";
unsigned int valid_rates;
int dp_rate_idx;
unsigned int val;
int ret = -EINVAL;
int max_dp_lanes;
+ unsigned int bpp;
+
+ connector = drm_atomic_get_new_connector_for_encoder(old_bridge_state->base.state,
+ bridge->encoder);
+ if (!connector) {
+ dev_err_ratelimited(pdata->dev, "Could not get the connector\n");
+ return;
+ }
max_dp_lanes = ti_sn_get_max_lanes(pdata);
pdata->dp_lanes = min(pdata->dp_lanes, max_dp_lanes);
@@ -1040,15 +1074,27 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge,
/*
* The SN65DSI86 only supports ASSR Display Authentication method and
- * this method is enabled by default. An eDP panel must support this
+ * this method is enabled for eDP panels. An eDP panel must support this
* authentication method. We need to enable this method in the eDP panel
* at DisplayPort address 0x0010A prior to link training.
+ *
+ * As only ASSR is supported by SN65DSI86, for full DisplayPort displays
+ * we need to disable the scrambler.
*/
- drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
- DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
+ if (pdata->bridge.type == DRM_MODE_CONNECTOR_eDP) {
+ drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
+ DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
+ regmap_update_bits(pdata->regmap, SN_TRAINING_SETTING_REG,
+ SCRAMBLE_DISABLE, 0);
+ } else {
+ regmap_update_bits(pdata->regmap, SN_TRAINING_SETTING_REG,
+ SCRAMBLE_DISABLE, SCRAMBLE_DISABLE);
+ }
+
+ bpp = ti_sn_bridge_get_bpp(connector);
/* Set the DP output format (18 bpp or 24 bpp) */
- val = (ti_sn_bridge_get_bpp(pdata) == 18) ? BPP_18_RGB : 0;
+ val = bpp == 18 ? BPP_18_RGB : 0;
regmap_update_bits(pdata->regmap, SN_DATA_FORMAT_REG, BPP_18_RGB, val);
/* DP lane config */
@@ -1059,7 +1105,7 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge,
valid_rates = ti_sn_bridge_read_valid_rates(pdata);
/* Train until we run out of rates */
- for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata);
+ for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata, bpp);
dp_rate_idx < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut);
dp_rate_idx++) {
if (!(valid_rates & BIT(dp_rate_idx)))
@@ -1114,10 +1160,33 @@ static void ti_sn_bridge_atomic_post_disable(struct drm_bridge *bridge,
pm_runtime_put_sync(pdata->dev);
}
+static enum drm_connector_status ti_sn_bridge_detect(struct drm_bridge *bridge)
+{
+ struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ int val = 0;
+
+ pm_runtime_get_sync(pdata->dev);
+ regmap_read(pdata->regmap, SN_HPD_DISABLE_REG, &val);
+ pm_runtime_put_autosuspend(pdata->dev);
+
+ return val & HPD_DEBOUNCED_STATE ? connector_status_connected
+ : connector_status_disconnected;
+}
+
+static struct edid *ti_sn_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+
+ return drm_get_edid(connector, &pdata->aux.ddc);
+}
+
static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
.attach = ti_sn_bridge_attach,
.detach = ti_sn_bridge_detach,
.mode_valid = ti_sn_bridge_mode_valid,
+ .get_edid = ti_sn_bridge_get_edid,
+ .detect = ti_sn_bridge_detect,
.atomic_pre_enable = ti_sn_bridge_atomic_pre_enable,
.atomic_enable = ti_sn_bridge_atomic_enable,
.atomic_disable = ti_sn_bridge_atomic_disable,
@@ -1198,10 +1267,9 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev,
int ret;
pdata->next_bridge = devm_drm_of_get_bridge(pdata->dev, np, 1, 0);
- if (IS_ERR(pdata->next_bridge)) {
- DRM_ERROR("failed to create panel bridge\n");
- return PTR_ERR(pdata->next_bridge);
- }
+ if (IS_ERR(pdata->next_bridge))
+ return dev_err_probe(pdata->dev, PTR_ERR(pdata->next_bridge),
+ "failed to create panel bridge\n");
ti_sn_bridge_parse_lanes(pdata, np);
@@ -1211,6 +1279,11 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev,
pdata->bridge.funcs = &ti_sn_bridge_funcs;
pdata->bridge.of_node = np;
+ pdata->bridge.type = pdata->next_bridge->type == DRM_MODE_CONNECTOR_DisplayPort
+ ? DRM_MODE_CONNECTOR_DisplayPort : DRM_MODE_CONNECTOR_eDP;
+
+ if (pdata->bridge.type == DRM_MODE_CONNECTOR_DisplayPort)
+ pdata->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
drm_bridge_add(&pdata->bridge);