Thomas Zimmermann ebf85e
From a85066840d29fc68d95ce7dbd6bcf15ef2775d66 Mon Sep 17 00:00:00 2001
Thomas Zimmermann ebf85e
From: Daniel Vetter <daniel.vetter@ffwll.ch>
Thomas Zimmermann ebf85e
Date: Wed, 26 Jul 2017 15:26:47 +0200
Thomas Zimmermann ebf85e
Subject: [PATCH] drm/i915: Rework sdvo proxy i2c locking
Thomas Zimmermann ebf85e
Git-commit: a85066840d29fc68d95ce7dbd6bcf15ef2775d66
Thomas Zimmermann ebf85e
Patch-mainline: v4.14-rc1
Thomas Zimmermann ebf85e
References: FATE#322643 bsc#1055900
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
lockdep complaints about a locking recursion for the i2c bus lock
Thomas Zimmermann ebf85e
because both the sdvo ddc proxy bus and the gmbus nested within use
Thomas Zimmermann ebf85e
the same locking class. It's not really a deadlock since we never nest
Thomas Zimmermann ebf85e
the other way round, but it's annoying.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Fix it by pulling the gmbus locking into the i2c lock_ops for both
Thomas Zimmermann ebf85e
i2c_adapater and making sure that the ddc_proxy_xfer function is
Thomas Zimmermann ebf85e
entirely lockless.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Re-layouting the extracted function resulted in some whitespace
Thomas Zimmermann ebf85e
cleanups, I figured we might as well keep them.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
V2: Review from Chris:
Thomas Zimmermann ebf85e
- s/locked/unlocked/ since I got the naming backwards
Thomas Zimmermann ebf85e
- Use the vfuncs of the proxied adatper instead of re-rolling copies.
Thomas Zimmermann ebf85e
  That's more consistent with the other proxying we're doing.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Thomas Zimmermann ebf85e
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Thomas Zimmermann ebf85e
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Thomas Zimmermann ebf85e
Link: https://patchwork.freedesktop.org/patch/msgid/20170726132647.31833-1-daniel.vetter@ffwll.ch
Thomas Zimmermann ebf85e
Acked-by: Takashi Iwai <tiwai@suse.de>
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
---
Thomas Zimmermann ebf85e
 drivers/gpu/drm/i915/intel_i2c.c  |   36 ++++++++++++++++++++--
Thomas Zimmermann ebf85e
 drivers/gpu/drm/i915/intel_sdvo.c |   62 ++++++++++++++++++++++++++++++--------
Thomas Zimmermann ebf85e
 2 files changed, 84 insertions(+), 14 deletions(-)
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/i915/intel_i2c.c
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/i915/intel_i2c.c
Thomas Zimmermann ebf85e
@@ -592,7 +592,6 @@ gmbus_xfer(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
 	int ret;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
Thomas Zimmermann ebf85e
-	mutex_lock(&dev_priv->gmbus_mutex);
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	if (bus->force_bit) {
Thomas Zimmermann ebf85e
 		ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
Thomas Zimmermann ebf85e
@@ -604,7 +603,6 @@ gmbus_xfer(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
 			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
Thomas Zimmermann ebf85e
 	}
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-	mutex_unlock(&dev_priv->gmbus_mutex);
Thomas Zimmermann ebf85e
 	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	return ret;
Thomas Zimmermann ebf85e
@@ -624,6 +622,39 @@ static const struct i2c_algorithm gmbus_
Thomas Zimmermann ebf85e
 	.functionality	= gmbus_func
Thomas Zimmermann ebf85e
 };
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+static void gmbus_lock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			   unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_gmbus *bus = to_intel_gmbus(adapter);
Thomas Zimmermann ebf85e
+	struct drm_i915_private *dev_priv = bus->dev_priv;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	mutex_lock(&dev_priv->gmbus_mutex);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+static int gmbus_trylock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			     unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_gmbus *bus = to_intel_gmbus(adapter);
Thomas Zimmermann ebf85e
+	struct drm_i915_private *dev_priv = bus->dev_priv;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	return mutex_trylock(&dev_priv->gmbus_mutex);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+static void gmbus_unlock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			     unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_gmbus *bus = to_intel_gmbus(adapter);
Thomas Zimmermann ebf85e
+	struct drm_i915_private *dev_priv = bus->dev_priv;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	mutex_unlock(&dev_priv->gmbus_mutex);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+const struct i2c_lock_operations gmbus_lock_ops = {
Thomas Zimmermann ebf85e
+	.lock_bus =    gmbus_lock_bus,
Thomas Zimmermann ebf85e
+	.trylock_bus = gmbus_trylock_bus,
Thomas Zimmermann ebf85e
+	.unlock_bus =  gmbus_unlock_bus,
Thomas Zimmermann ebf85e
+};
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 /**
Thomas Zimmermann ebf85e
  * intel_gmbus_setup - instantiate all Intel i2c GMBuses
Thomas Zimmermann ebf85e
  * @dev_priv: i915 device private
Thomas Zimmermann ebf85e
@@ -665,6 +696,7 @@ int intel_setup_gmbus(struct drm_i915_pr
Thomas Zimmermann ebf85e
 		bus->dev_priv = dev_priv;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 		bus->adapter.algo = &gmbus_algorithm;
Thomas Zimmermann ebf85e
+		bus->adapter.lock_ops = &gmbus_lock_ops;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 		/*
Thomas Zimmermann ebf85e
 		 * We wish to retry with bit banging
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/i915/intel_sdvo.c
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
Thomas Zimmermann ebf85e
@@ -451,23 +451,24 @@ static const char * const cmd_status_nam
Thomas Zimmermann ebf85e
 	"Scaling not supported"
Thomas Zimmermann ebf85e
 };
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
Thomas Zimmermann ebf85e
-				 const void *args, int args_len)
Thomas Zimmermann ebf85e
+static bool __intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
Thomas Zimmermann ebf85e
+				   const void *args, int args_len,
Thomas Zimmermann ebf85e
+				   bool unlocked)
Thomas Zimmermann ebf85e
 {
Thomas Zimmermann ebf85e
 	u8 *buf, status;
Thomas Zimmermann ebf85e
 	struct i2c_msg *msgs;
Thomas Zimmermann ebf85e
 	int i, ret = true;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-        /* Would be simpler to allocate both in one go ? */        
Thomas Zimmermann ebf85e
+	/* Would be simpler to allocate both in one go ? */
Thomas Zimmermann ebf85e
 	buf = kzalloc(args_len * 2 + 2, GFP_KERNEL);
Thomas Zimmermann ebf85e
 	if (!buf)
Thomas Zimmermann ebf85e
 		return false;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	msgs = kcalloc(args_len + 3, sizeof(*msgs), GFP_KERNEL);
Thomas Zimmermann ebf85e
 	if (!msgs) {
Thomas Zimmermann ebf85e
-	        kfree(buf);
Thomas Zimmermann ebf85e
+		kfree(buf);
Thomas Zimmermann ebf85e
 		return false;
Thomas Zimmermann ebf85e
-        }
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
@@ -498,7 +499,10 @@ static bool intel_sdvo_write_cmd(struct
Thomas Zimmermann ebf85e
 	msgs[i+2].len = 1;
Thomas Zimmermann ebf85e
 	msgs[i+2].buf = &status;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-	ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
Thomas Zimmermann ebf85e
+	if (unlocked)
Thomas Zimmermann ebf85e
+		ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
Thomas Zimmermann ebf85e
+	else
Thomas Zimmermann ebf85e
+		ret = __i2c_transfer(intel_sdvo->i2c, msgs, i+3);
Thomas Zimmermann ebf85e
 	if (ret < 0) {
Thomas Zimmermann ebf85e
 		DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
Thomas Zimmermann ebf85e
 		ret = false;
Thomas Zimmermann ebf85e
@@ -516,6 +520,12 @@ out:
Thomas Zimmermann ebf85e
 	return ret;
Thomas Zimmermann ebf85e
 }
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
Thomas Zimmermann ebf85e
+				 const void *args, int args_len)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	return __intel_sdvo_write_cmd(intel_sdvo, cmd, args, args_len, true);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
Thomas Zimmermann ebf85e
 				     void *response, int response_len)
Thomas Zimmermann ebf85e
 {
Thomas Zimmermann ebf85e
@@ -602,13 +612,13 @@ static int intel_sdvo_get_pixel_multipli
Thomas Zimmermann ebf85e
 		return 4;
Thomas Zimmermann ebf85e
 }
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
Thomas Zimmermann ebf85e
-					      u8 ddc_bus)
Thomas Zimmermann ebf85e
+static bool __intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
Thomas Zimmermann ebf85e
+						u8 ddc_bus)
Thomas Zimmermann ebf85e
 {
Thomas Zimmermann ebf85e
 	/* This must be the immediately preceding write before the i2c xfer */
Thomas Zimmermann ebf85e
-	return intel_sdvo_write_cmd(intel_sdvo,
Thomas Zimmermann ebf85e
-				    SDVO_CMD_SET_CONTROL_BUS_SWITCH,
Thomas Zimmermann ebf85e
-				    &ddc_bus, 1);
Thomas Zimmermann ebf85e
+	return __intel_sdvo_write_cmd(intel_sdvo,
Thomas Zimmermann ebf85e
+				      SDVO_CMD_SET_CONTROL_BUS_SWITCH,
Thomas Zimmermann ebf85e
+				      &ddc_bus, 1, false);
Thomas Zimmermann ebf85e
 }
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len)
Thomas Zimmermann ebf85e
@@ -2926,7 +2936,7 @@ static int intel_sdvo_ddc_proxy_xfer(str
Thomas Zimmermann ebf85e
 {
Thomas Zimmermann ebf85e
 	struct intel_sdvo *sdvo = adapter->algo_data;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-	if (!intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
Thomas Zimmermann ebf85e
+	if (!__intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
Thomas Zimmermann ebf85e
 		return -EIO;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
Thomas Zimmermann ebf85e
@@ -2943,6 +2953,33 @@ static const struct i2c_algorithm intel_
Thomas Zimmermann ebf85e
 	.functionality	= intel_sdvo_ddc_proxy_func
Thomas Zimmermann ebf85e
 };
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+static void proxy_lock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			   unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_sdvo *sdvo = adapter->algo_data;
Thomas Zimmermann ebf85e
+	sdvo->i2c->lock_ops->lock_bus(sdvo->i2c, flags);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+static int proxy_trylock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			     unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_sdvo *sdvo = adapter->algo_data;
Thomas Zimmermann ebf85e
+	return sdvo->i2c->lock_ops->trylock_bus(sdvo->i2c, flags);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+static void proxy_unlock_bus(struct i2c_adapter *adapter,
Thomas Zimmermann ebf85e
+			     unsigned int flags)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_sdvo *sdvo = adapter->algo_data;
Thomas Zimmermann ebf85e
+	sdvo->i2c->lock_ops->unlock_bus(sdvo->i2c, flags);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+const struct i2c_lock_operations proxy_lock_ops = {
Thomas Zimmermann ebf85e
+	.lock_bus =    proxy_lock_bus,
Thomas Zimmermann ebf85e
+	.trylock_bus = proxy_trylock_bus,
Thomas Zimmermann ebf85e
+	.unlock_bus =  proxy_unlock_bus,
Thomas Zimmermann ebf85e
+};
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 static bool
Thomas Zimmermann ebf85e
 intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo,
Thomas Zimmermann ebf85e
 			  struct drm_i915_private *dev_priv)
Thomas Zimmermann ebf85e
@@ -2955,6 +2992,7 @@ intel_sdvo_init_ddc_proxy(struct intel_s
Thomas Zimmermann ebf85e
 	sdvo->ddc.dev.parent = &pdev->dev;
Thomas Zimmermann ebf85e
 	sdvo->ddc.algo_data = sdvo;
Thomas Zimmermann ebf85e
 	sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
Thomas Zimmermann ebf85e
+	sdvo->ddc.lock_ops = &proxy_lock_ops;
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	return i2c_add_adapter(&sdvo->ddc) == 0;
Thomas Zimmermann ebf85e
 }