From: Ville Syrjala <ville.syrjala@linux.intel.com>
To: intel-gfx@lists.freedesktop.org
Subject: [Intel-gfx] [PATCH 12/13] drm/i915/sdvo: Rework DDC bus handling
Date: Wed, 5 Jul 2023 23:21:21 +0300 [thread overview]
Message-ID: <20230705202122.17915-13-ville.syrjala@linux.intel.com> (raw)
In-Reply-To: <20230705202122.17915-1-ville.syrjala@linux.intel.com>
From: Ville Syrjälä <ville.syrjala@linux.intel.com>
Each SDVO device can have up to three sets of DDC pins.
Currently we just register a single i2c_adapter for the
entire SDVO device and semi-randomly pick the "correct"
set of DDC pins during intel_sdvo_tmds_sink_detect().
This doesn't make any real sense especially if we have
multiple outputs each with their own dedicated DDC bus.
Let's clean up this mess and register a dedicated
i2c_adapter for each of the possible pin pairs. Each
output (ie. connector) can then pick the correct i2c_adapter
to use for its DDC bus. And we can just switch over to
drm_connector_init_with_ddc() to take care of the
connector->ddc association, which also populates the
"ddc" sysfs symlink as a bonus.
And now that things are based on the actual connector we can
also nuke the sketchy sdvo->controller_output thing.
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
drivers/gpu/drm/i915/display/intel_sdvo.c | 205 ++++++++++++----------
1 file changed, 114 insertions(+), 91 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index 5c25417d256a..d7edb5714c4c 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -65,6 +65,8 @@
#define IS_TV_OR_LVDS(c) ((c)->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK))
#define IS_DIGITAL(c) ((c)->output_flag & (SDVO_TMDS_MASK | SDVO_LVDS_MASK))
+#define HAS_DDC(c) ((c)->output_flag & (SDVO_RGB_MASK | SDVO_TMDS_MASK | \
+ SDVO_LVDS_MASK))
static const char * const tv_format_names[] = {
"NTSC_M" , "NTSC_J" , "NTSC_443",
@@ -78,20 +80,25 @@ static const char * const tv_format_names[] = {
#define TV_FORMAT_NUM ARRAY_SIZE(tv_format_names)
+struct intel_sdvo;
+
+struct intel_sdvo_ddc {
+ struct i2c_adapter ddc;
+ struct intel_sdvo *sdvo;
+ u8 ddc_bus;
+};
+
struct intel_sdvo {
struct intel_encoder base;
struct i2c_adapter *i2c;
u8 slave_addr;
- struct i2c_adapter ddc;
+ struct intel_sdvo_ddc ddc[3];
/* Register for the SDVO device: SDVOB or SDVOC */
i915_reg_t sdvo_reg;
- /* Active outputs controlled by this SDVO output */
- u16 controlled_output;
-
/*
* Capabilities of the SDVO device returned by
* intel_sdvo_get_capabilities()
@@ -108,9 +115,6 @@ struct intel_sdvo {
*/
u16 hotplug_active;
- /* DDC bus used by this SDVO encoder */
- u8 ddc_bus;
-
/*
* the sdvo flag gets lost in round trip: dtd->adjusted_mode->dtd
*/
@@ -2035,18 +2039,15 @@ intel_sdvo_hotplug(struct intel_encoder *encoder,
return intel_encoder_hotplug(encoder, connector);
}
-static bool
-intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
-{
- /* Is there more than one type of output? */
- return hweight16(intel_sdvo->caps.output_flags) > 1;
-}
-
static const struct drm_edid *
intel_sdvo_get_edid(struct drm_connector *connector)
{
- struct intel_sdvo *sdvo = intel_attached_sdvo(to_intel_connector(connector));
- return drm_edid_read_ddc(connector, &sdvo->ddc);
+ struct i2c_adapter *ddc = connector->ddc;
+
+ if (!ddc)
+ return NULL;
+
+ return drm_edid_read_ddc(connector, ddc);
}
/* Mac mini hack -- use the same DDC as the analog connector */
@@ -2054,43 +2055,23 @@ static const struct drm_edid *
intel_sdvo_get_analog_edid(struct drm_connector *connector)
{
struct drm_i915_private *i915 = to_i915(connector->dev);
- struct i2c_adapter *i2c;
+ struct i2c_adapter *ddc;
- i2c = intel_gmbus_get_adapter(i915, i915->display.vbt.crt_ddc_pin);
+ ddc = intel_gmbus_get_adapter(i915, i915->display.vbt.crt_ddc_pin);
+ if (!ddc)
+ return NULL;
- return drm_edid_read_ddc(connector, i2c);
+ return drm_edid_read_ddc(connector, ddc);
}
static enum drm_connector_status
intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
{
- struct intel_sdvo *intel_sdvo = intel_attached_sdvo(to_intel_connector(connector));
enum drm_connector_status status;
const struct drm_edid *drm_edid;
drm_edid = intel_sdvo_get_edid(connector);
- if (!drm_edid && intel_sdvo_multifunc_encoder(intel_sdvo)) {
- u8 ddc, saved_ddc = intel_sdvo->ddc_bus;
-
- /*
- * Don't use the 1 as the argument of DDC bus switch to get
- * the EDID. It is used for SDVO SPD ROM.
- */
- for (ddc = intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) {
- intel_sdvo->ddc_bus = ddc;
- drm_edid = intel_sdvo_get_edid(connector);
- if (drm_edid)
- break;
- }
- /*
- * If we found the EDID on the other bus,
- * assume that is the correct DDC bus.
- */
- if (!drm_edid)
- intel_sdvo->ddc_bus = saved_ddc;
- }
-
/*
* When there is no edid and no monitor is connected with VGA
* port, try to use the CRT ddc to read the EDID for DVI-connector.
@@ -2524,29 +2505,37 @@ static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs
.atomic_check = intel_sdvo_atomic_check,
};
-static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+static void intel_sdvo_encoder_destroy(struct drm_encoder *_encoder)
{
- struct intel_sdvo *intel_sdvo = to_sdvo(to_intel_encoder(encoder));
+ struct intel_encoder *encoder = to_intel_encoder(_encoder);
+ struct intel_sdvo *sdvo = to_sdvo(encoder);
+ int i;
- i2c_del_adapter(&intel_sdvo->ddc);
- intel_encoder_destroy(encoder);
-}
+ for (i = 0; i < ARRAY_SIZE(sdvo->ddc); i++) {
+ if (sdvo->ddc[i].ddc_bus)
+ i2c_del_adapter(&sdvo->ddc[i].ddc);
+ }
+
+ drm_encoder_cleanup(&encoder->base);
+ kfree(sdvo);
+};
static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
- .destroy = intel_sdvo_enc_destroy,
+ .destroy = intel_sdvo_encoder_destroy,
};
-static void
-intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo)
+static int
+intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo,
+ struct intel_sdvo_connector *connector)
{
u16 mask = 0;
- unsigned int num_bits;
+ int num_bits;
/*
* Make a mask of outputs less than or equal to our own priority in the
* list.
*/
- switch (sdvo->controlled_output) {
+ switch (connector->output_flag) {
case SDVO_OUTPUT_LVDS1:
mask |= SDVO_OUTPUT_LVDS1;
fallthrough;
@@ -2575,7 +2564,7 @@ intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo)
num_bits = 3;
/* Corresponds to SDVO_CONTROL_BUS_DDCx */
- sdvo->ddc_bus = 1 << num_bits;
+ return num_bits;
}
/*
@@ -2585,11 +2574,13 @@ intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo)
* DDC bus number assignment is in a priority order of RGB outputs, then TMDS
* outputs, then LVDS outputs.
*/
-static void
-intel_sdvo_select_ddc_bus(struct intel_sdvo *sdvo)
+static struct intel_sdvo_ddc *
+intel_sdvo_select_ddc_bus(struct intel_sdvo *sdvo,
+ struct intel_sdvo_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(sdvo->base.base.dev);
struct sdvo_device_mapping *mapping;
+ int ddc_bus;
if (sdvo->base.port == PORT_B)
mapping = &dev_priv->display.vbt.sdvo_mappings[0];
@@ -2597,9 +2588,14 @@ intel_sdvo_select_ddc_bus(struct intel_sdvo *sdvo)
mapping = &dev_priv->display.vbt.sdvo_mappings[1];
if (mapping->initialized)
- sdvo->ddc_bus = 1 << ((mapping->ddc_pin & 0xf0) >> 4);
+ ddc_bus = (mapping->ddc_pin & 0xf0) >> 4;
else
- intel_sdvo_guess_ddc_bus(sdvo);
+ ddc_bus = intel_sdvo_guess_ddc_bus(sdvo, connector);
+
+ if (ddc_bus < 1 || ddc_bus > 3)
+ return NULL;
+
+ return &sdvo->ddc[ddc_bus - 1];
}
static void
@@ -2682,22 +2678,30 @@ intel_sdvo_get_slave_addr(struct intel_sdvo *sdvo)
return 0x72;
}
+static int
+intel_sdvo_init_ddc_proxy(struct intel_sdvo_ddc *ddc,
+ struct intel_sdvo *sdvo, int bit);
+
static int
intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
struct intel_sdvo *encoder)
{
- struct drm_connector *drm_connector;
+ struct drm_i915_private *i915 = to_i915(encoder->base.base.dev);
+ struct intel_sdvo_ddc *ddc = NULL;
int ret;
- drm_connector = &connector->base.base;
- ret = drm_connector_init(encoder->base.base.dev,
- drm_connector,
- &intel_sdvo_connector_funcs,
- connector->base.base.connector_type);
+ if (HAS_DDC(connector))
+ ddc = intel_sdvo_select_ddc_bus(encoder, connector);
+
+ ret = drm_connector_init_with_ddc(encoder->base.base.dev,
+ &connector->base.base,
+ &intel_sdvo_connector_funcs,
+ connector->base.base.connector_type,
+ ddc ? &ddc->ddc : NULL);
if (ret < 0)
return ret;
- drm_connector_helper_add(drm_connector,
+ drm_connector_helper_add(&connector->base.base,
&intel_sdvo_connector_helper_funcs);
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
@@ -2706,6 +2710,11 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
intel_connector_attach_encoder(&connector->base, &encoder->base);
+ if (ddc)
+ drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] using %s\n",
+ connector->base.base.base.id, connector->base.base.name,
+ ddc->ddc.name);
+
return 0;
}
@@ -2903,7 +2912,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type)
if (!intel_panel_preferred_fixed_mode(intel_connector)) {
mutex_lock(&i915->drm.mode_config.mutex);
- intel_ddc_get_modes(connector, &intel_sdvo->ddc);
+ intel_ddc_get_modes(connector, connector->ddc);
intel_panel_add_edid_fixed_modes(intel_connector, false);
mutex_unlock(&i915->drm.mode_config.mutex);
@@ -2978,10 +2987,6 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo)
return false;
}
- intel_sdvo->controlled_output = flags;
-
- intel_sdvo_select_ddc_bus(intel_sdvo);
-
for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
u16 type = flags & probe_order[i];
@@ -3234,9 +3239,10 @@ static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs,
int num)
{
- struct intel_sdvo *sdvo = adapter->algo_data;
+ struct intel_sdvo_ddc *ddc = adapter->algo_data;
+ struct intel_sdvo *sdvo = ddc->sdvo;
- if (!__intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
+ if (!__intel_sdvo_set_control_bus_switch(sdvo, 1 << ddc->ddc_bus))
return -EIO;
return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
@@ -3244,7 +3250,9 @@ static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
static u32 intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter)
{
- struct intel_sdvo *sdvo = adapter->algo_data;
+ struct intel_sdvo_ddc *ddc = adapter->algo_data;
+ struct intel_sdvo *sdvo = ddc->sdvo;
+
return sdvo->i2c->algo->functionality(sdvo->i2c);
}
@@ -3256,21 +3264,27 @@ static const struct i2c_algorithm intel_sdvo_ddc_proxy = {
static void proxy_lock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
- struct intel_sdvo *sdvo = adapter->algo_data;
+ struct intel_sdvo_ddc *ddc = adapter->algo_data;
+ struct intel_sdvo *sdvo = ddc->sdvo;
+
sdvo->i2c->lock_ops->lock_bus(sdvo->i2c, flags);
}
static int proxy_trylock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
- struct intel_sdvo *sdvo = adapter->algo_data;
+ struct intel_sdvo_ddc *ddc = adapter->algo_data;
+ struct intel_sdvo *sdvo = ddc->sdvo;
+
return sdvo->i2c->lock_ops->trylock_bus(sdvo->i2c, flags);
}
static void proxy_unlock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
- struct intel_sdvo *sdvo = adapter->algo_data;
+ struct intel_sdvo_ddc *ddc = adapter->algo_data;
+ struct intel_sdvo *sdvo = ddc->sdvo;
+
sdvo->i2c->lock_ops->unlock_bus(sdvo->i2c, flags);
}
@@ -3280,21 +3294,26 @@ static const struct i2c_lock_operations proxy_lock_ops = {
.unlock_bus = proxy_unlock_bus,
};
-static bool
-intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo)
+static int
+intel_sdvo_init_ddc_proxy(struct intel_sdvo_ddc *ddc,
+ struct intel_sdvo *sdvo, int ddc_bus)
{
struct drm_i915_private *dev_priv = to_i915(sdvo->base.base.dev);
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
- sdvo->ddc.owner = THIS_MODULE;
- sdvo->ddc.class = I2C_CLASS_DDC;
- snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy");
- sdvo->ddc.dev.parent = &pdev->dev;
- sdvo->ddc.algo_data = sdvo;
- sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
- sdvo->ddc.lock_ops = &proxy_lock_ops;
+ ddc->sdvo = sdvo;
+ ddc->ddc_bus = ddc_bus;
- return i2c_add_adapter(&sdvo->ddc) == 0;
+ ddc->ddc.owner = THIS_MODULE;
+ ddc->ddc.class = I2C_CLASS_DDC;
+ snprintf(ddc->ddc.name, I2C_NAME_SIZE, "SDVO %c DDC%d",
+ port_name(sdvo->base.port), ddc_bus);
+ ddc->ddc.dev.parent = &pdev->dev;
+ ddc->ddc.algo_data = ddc;
+ ddc->ddc.algo = &intel_sdvo_ddc_proxy;
+ ddc->ddc.lock_ops = &proxy_lock_ops;
+
+ return i2c_add_adapter(&ddc->ddc);
}
static bool is_sdvo_port_valid(struct drm_i915_private *dev_priv, enum port port)
@@ -3341,9 +3360,8 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
intel_sdvo->sdvo_reg = sdvo_reg;
intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(intel_sdvo) >> 1;
+
intel_sdvo_select_i2c_bus(intel_sdvo);
- if (!intel_sdvo_init_ddc_proxy(intel_sdvo))
- goto err_i2c_bus;
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
@@ -3376,6 +3394,15 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
intel_sdvo->colorimetry_cap =
intel_sdvo_get_colorimetry_cap(intel_sdvo);
+ for (i = 0; i < ARRAY_SIZE(intel_sdvo->ddc); i++) {
+ int ret;
+
+ ret = intel_sdvo_init_ddc_proxy(&intel_sdvo->ddc[i],
+ intel_sdvo, i + 1);
+ if (ret)
+ goto err;
+ }
+
if (!intel_sdvo_output_setup(intel_sdvo)) {
drm_dbg_kms(&dev_priv->drm,
"SDVO output failed to setup on %s\n",
@@ -3436,13 +3463,9 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
err_output:
intel_sdvo_output_cleanup(intel_sdvo);
-
err:
- drm_encoder_cleanup(&intel_encoder->base);
- i2c_del_adapter(&intel_sdvo->ddc);
-err_i2c_bus:
intel_sdvo_unselect_i2c_bus(intel_sdvo);
- kfree(intel_sdvo);
+ intel_sdvo_encoder_destroy(&intel_encoder->base);
return false;
}
--
2.39.3
next prev parent reply other threads:[~2023-07-05 20:22 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-07-05 20:21 [Intel-gfx] [PATCH 00/13] drm/i915/sdvo: DDC rework and fixes Ville Syrjala
2023-07-05 20:21 ` [Intel-gfx] [PATCH 01/13] drm/i915/sdvo: Issue SetTargetOutput prior ot GetAttachedDisplays Ville Syrjala
2023-07-06 7:20 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 02/13] drm/i915/sdvo: Protect macro args Ville Syrjala
2023-07-06 7:20 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 03/13] drm/i915/sdvo: s/sdvo_inputs_mask/sdvo_num_inputs/ Ville Syrjala
2023-07-06 7:24 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 04/13] drm/i915: Don't warn about zero N/P in *_calc_dpll_params() Ville Syrjala
2023-07-06 8:17 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 05/13] drm/i915: Fully populate crtc_state->dpll Ville Syrjala
2023-07-06 8:41 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 06/13] drm/i915/sdvo: Pick the TV dotclock from adjusted_mode Ville Syrjala
2023-07-06 8:22 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 07/13] drm/i915/sdvo: Fail gracefully if the TV dotclock is out of range Ville Syrjala
2023-07-06 8:22 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 08/13] drm/i915/sdvo: Nuke attached_output tracking Ville Syrjala
2023-07-06 8:24 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 09/13] drm/i915/sdvo: Initialize the encoder ealier Ville Syrjala
2023-07-06 8:28 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 10/13] drm/i915/sdvo: Nuke the duplicate sdvo->port Ville Syrjala
2023-07-06 8:29 ` Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 11/13] drm/i915/sdvo: Get rid of the per-connector i2c symlink Ville Syrjala
2023-07-06 8:30 ` Jani Nikula
2023-07-05 20:21 ` Ville Syrjala [this message]
2023-07-06 8:38 ` [Intel-gfx] [PATCH 12/13] drm/i915/sdvo: Rework DDC bus handling Jani Nikula
2023-07-05 20:21 ` [Intel-gfx] [PATCH 13/13] drm/i915/sdvo: Print out the i2c pin and slave address Ville Syrjala
2023-07-06 8:38 ` Jani Nikula
2023-07-05 21:24 ` [Intel-gfx] ✗ Fi.CI.SPARSE: warning for drm/i915/sdvo: DDC rework and fixes Patchwork
2023-07-05 21:38 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
2023-07-06 1:17 ` [Intel-gfx] ✗ Fi.CI.IGT: failure " Patchwork
2023-07-06 8:45 ` [Intel-gfx] [PATCH 00/13] " Jani Nikula
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230705202122.17915-13-ville.syrjala@linux.intel.com \
--to=ville.syrjala@linux.intel.com \
--cc=intel-gfx@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox