[PATCH v3 36/40] drm/i915: Implement gmbus burst read

Ramalingam C ramalingam.c at intel.com
Thu Apr 5 13:44:22 UTC 2018



On Thursday 05 April 2018 02:42 PM, Jani Nikula wrote:
> On Tue, 03 Apr 2018, Daniel Vetter <daniel at ffwll.ch> wrote:
>> On Tue, Apr 03, 2018 at 07:27:49PM +0530, Ramalingam C wrote:
>>> Implements a interface for single burst read of data that is larger
>>> than 512 Bytes through gmbus.
> Where does 512 come from? Current code chunks at GMBUS_BYTE_COUNT_MAX
> i.e. 256 bytes. Should GMBUS_BYTE_COUNT_MAX be platform specific?
Actually I am not sure why 256 is the max limit for gmbus read/write 
when HW allows upto 511Bytes. 24:16 9bits of GMBUS is Total byte count. 
So max value could be 511Bytes.
256 is choosen because of some practical reason or any legacy platform 
capability?
>
>>> HDCP2.2 spec expects HDCP2.2 transmitter to read 522Bytes of HDCP
>>> receiver certificates in single burst read. On gmbus, to read more
>>> than 511Bytes, HW provides a workaround for burst read.
>>>
>>> This patch passes the burst read request through gmbus read functions.
>>> And implements the sequence of enabling and disabling the burst read.
>>>
>>> v2:
>>>    No Changes.
>>> v3:
>>>    No Changes.
>>>
>>> Signed-off-by: Ramalingam C <ramalingam.c at intel.com>
>> Why only enable this burst_read mode for hdcp, and not for all i2c
>> transactions? Seems to unecessarily complicate the code, since it requires
>> that you pass burst_read through the entire call chain. For other changes
>> we've done for hdcp (like enabling the read/write mode and other stuff)
>> we've enabled it for all i2c transactions. That also means more testing,
>> since it will be used even when HDCP is not in use.
> Agreed.
>
> Shouldn't the decision to use burst read be based on the length to be
> read? More than 256 bytes, use burst, otherwise not. Or are there
> functional differences or benefits to using burst mode for shorter
> reads?
Busrt read is enabled on few latest platforms (from KBL+ for HDCP2.2 
compliance requirement,
where split I2C read of a msg(534Bytes) is not accepted)

If there is no objection, extend the single normal rd/wr to 511Bytes 
from 256bytes.
And any read for more than 511 Bytes use the Burst read on capable 
platforms.

Though personally I dont believe it will give much of optimization, as 
burst read also brings in some
extra programming requirements. But might avoid extra driver entry point 
for GMBUS like intel_gmbus_burst_read().

>
> I guess I'd still prepare to add some burst field to struct intel_gmbus,
> not unlike force_bit.
>
> Side note, I would prefer significant changes to basic plumbing like
> gmbus be highlighted better. Please at least prefix the hdcp patches
> with "drm/i915/hdcp" and gmbus with "drm/i915/gmbus". The patch at hand
> is kind of hidden in the middle of a large topical series, and would
> deserve to be looked at by people who aren't necessarily all that into
> hdcp.
Sure.

--Ram
>
>
> BR,
> Jani.
>
>
>> -Daniel
>>
>>> ---
>>>   drivers/gpu/drm/i915/i915_drv.h  |   2 +
>>>   drivers/gpu/drm/i915/i915_reg.h  |   3 +
>>>   drivers/gpu/drm/i915/intel_i2c.c | 124 +++++++++++++++++++++++++++++++++------
>>>   3 files changed, 112 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>>> index 6e740f6fe33f..72534a1e544b 100644
>>> --- a/drivers/gpu/drm/i915/i915_drv.h
>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>>> @@ -3688,6 +3688,8 @@ extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv);
>>>   extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv,
>>>   				     unsigned int pin);
>>>   extern int intel_gmbus_output_aksv(struct i2c_adapter *adapter);
>>> +extern int intel_gmbus_burst_read(struct i2c_adapter *adapter,
>>> +				  unsigned int offset, void *buf, size_t size);
>>>   
>>>   extern struct i2c_adapter *
>>>   intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin);
>>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>>> index f04ad3c15abd..56979bc4e9d8 100644
>>> --- a/drivers/gpu/drm/i915/i915_reg.h
>>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>>> @@ -3123,6 +3123,7 @@ enum i915_power_well_id {
>>>   #define   GMBUS_RATE_400KHZ	(2<<8) /* reserved on Pineview */
>>>   #define   GMBUS_RATE_1MHZ	(3<<8) /* reserved on Pineview */
>>>   #define   GMBUS_HOLD_EXT	(1<<7) /* 300ns hold time, rsvd on Pineview */
>>> +#define   GMBUS_BYTE_CNT_OVERRIDE (1<<6)
>>>   #define   GMBUS_PIN_DISABLED	0
>>>   #define   GMBUS_PIN_SSC		1
>>>   #define   GMBUS_PIN_VGADDC	2
>>> @@ -3150,8 +3151,10 @@ enum i915_power_well_id {
>>>   #define   GMBUS_CYCLE_WAIT	(1<<25)
>>>   #define   GMBUS_CYCLE_INDEX	(2<<25)
>>>   #define   GMBUS_CYCLE_STOP	(4<<25)
>>> +#define   GMBUS_CYCLE_MASK	(7<<25)
>>>   #define   GMBUS_BYTE_COUNT_SHIFT 16
>>>   #define   GMBUS_BYTE_COUNT_MAX   256U
>>> +#define   GMBUS_BYTE_COUNT_HW_MAX 511U
>>>   #define   GMBUS_SLAVE_INDEX_SHIFT 8
>>>   #define   GMBUS_SLAVE_ADDR_SHIFT 1
>>>   #define   GMBUS_SLAVE_READ	(1<<0)
>>> diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
>>> index e6875509bcd9..dcb2be0d54ee 100644
>>> --- a/drivers/gpu/drm/i915/intel_i2c.c
>>> +++ b/drivers/gpu/drm/i915/intel_i2c.c
>>> @@ -364,21 +364,30 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv)
>>>   static int
>>>   gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
>>>   		      unsigned short addr, u8 *buf, unsigned int len,
>>> -		      u32 gmbus1_index)
>>> +		      u32 gmbus1_index, bool burst_read)
>>>   {
>>> +	unsigned int size = len;
>>> +	int ret;
>>> +
>>> +	if (burst_read) {
>>> +		/* Seq to enable Burst Read */
>>> +		I915_WRITE_FW(GMBUS0, (I915_READ_FW(GMBUS0) |
>>> +			      GMBUS_BYTE_CNT_OVERRIDE));
>>> +		size = GMBUS_BYTE_COUNT_HW_MAX;
>>> +	}
>>> +
>>>   	I915_WRITE_FW(GMBUS1,
>>>   		      gmbus1_index |
>>>   		      GMBUS_CYCLE_WAIT |
>>> -		      (len << GMBUS_BYTE_COUNT_SHIFT) |
>>> +		      (size << GMBUS_BYTE_COUNT_SHIFT) |
>>>   		      (addr << GMBUS_SLAVE_ADDR_SHIFT) |
>>>   		      GMBUS_SLAVE_READ | GMBUS_SW_RDY);
>>>   	while (len) {
>>> -		int ret;
>>>   		u32 val, loop = 0;
>>>   
>>>   		ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN);
>>>   		if (ret)
>>> -			return ret;
>>> +			goto exit;
>>>   
>>>   		val = I915_READ_FW(GMBUS3);
>>>   		do {
>>> @@ -387,12 +396,29 @@ gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
>>>   		} while (--len && ++loop < 4);
>>>   	}
>>>   
>>> -	return 0;
>>> +exit:
>>> +	if (burst_read) {
>>> +
>>> +		/* Seq to disable the Burst Read */
>>> +		I915_WRITE_FW(GMBUS0, (I915_READ_FW(GMBUS0) &
>>> +			      ~GMBUS_BYTE_CNT_OVERRIDE));
>>> +		I915_WRITE_FW(GMBUS1, (I915_READ_FW(GMBUS1) &
>>> +			      ~GMBUS_CYCLE_MASK) | GMBUS_CYCLE_STOP);
>>> +
>>> +		/*
>>> +		 * On Burst read disable, GMBUS need more time to settle
>>> +		 * down to Idle State.
>>> +		 */
>>> +		ret = intel_wait_for_register_fw(dev_priv, GMBUS2,
>>> +						 GMBUS_ACTIVE, 0, 50);
>>> +	}
>>> +
>>> +	return ret;
>>>   }
>>>   
>>>   static int
>>>   gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
>>> -		u32 gmbus1_index)
>>> +		u32 gmbus1_index, bool burst_read)
>>>   {
>>>   	u8 *buf = msg->buf;
>>>   	unsigned int rx_size = msg->len;
>>> @@ -400,10 +426,13 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
>>>   	int ret;
>>>   
>>>   	do {
>>> -		len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
>>> +		if (burst_read)
>>> +			len = rx_size;
>>> +		else
>>> +			len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
>>>   
>>> -		ret = gmbus_xfer_read_chunk(dev_priv, msg->addr,
>>> -					    buf, len, gmbus1_index);
>>> +		ret = gmbus_xfer_read_chunk(dev_priv, msg->addr, buf, len,
>>> +					    gmbus1_index, burst_read);
>>>   		if (ret)
>>>   			return ret;
>>>   
>>> @@ -491,7 +520,8 @@ gmbus_is_index_xfer(struct i2c_msg *msgs, int i, int num)
>>>   }
>>>   
>>>   static int
>>> -gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>>> +gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs,
>>> +		 bool burst_read)
>>>   {
>>>   	u32 gmbus1_index = 0;
>>>   	u32 gmbus5 = 0;
>>> @@ -509,7 +539,8 @@ gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>>>   		I915_WRITE_FW(GMBUS5, gmbus5);
>>>   
>>>   	if (msgs[1].flags & I2C_M_RD)
>>> -		ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
>>> +		ret = gmbus_xfer_read(dev_priv, &msgs[1],
>>> +				      gmbus1_index, burst_read);
>>>   	else
>>>   		ret = gmbus_xfer_write(dev_priv, &msgs[1], gmbus1_index);
>>>   
>>> @@ -522,7 +553,7 @@ gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>>>   
>>>   static int
>>>   do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num,
>>> -	      u32 gmbus0_source)
>>> +	      u32 gmbus0_source, bool burst_read)
>>>   {
>>>   	struct intel_gmbus *bus = container_of(adapter,
>>>   					       struct intel_gmbus,
>>> @@ -544,15 +575,20 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num,
>>>   	for (; i < num; i += inc) {
>>>   		inc = 1;
>>>   		if (gmbus_is_index_xfer(msgs, i, num)) {
>>> -			ret = gmbus_index_xfer(dev_priv, &msgs[i]);
>>> +			ret = gmbus_index_xfer(dev_priv, &msgs[i], burst_read);
>>>   			inc = 2; /* an index transmission is two msgs */
>>>   		} else if (msgs[i].flags & I2C_M_RD) {
>>> -			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
>>> +			ret = gmbus_xfer_read(dev_priv, &msgs[i],
>>> +					      0, burst_read);
>>>   		} else {
>>>   			ret = gmbus_xfer_write(dev_priv, &msgs[i], 0);
>>>   		}
>>>   
>>> -		if (!ret)
>>> +		/*
>>> +		 * Burst read Sequence ends with STOP. So Dont expect
>>> +		 * HW wait phase.
>>> +		 */
>>> +		if (!ret && !burst_read)
>>>   			ret = gmbus_wait(dev_priv,
>>>   					 GMBUS_HW_WAIT_PHASE, GMBUS_HW_WAIT_EN);
>>>   		if (ret == -ETIMEDOUT)
>>> @@ -664,7 +700,7 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
>>>   		if (ret < 0)
>>>   			bus->force_bit &= ~GMBUS_FORCE_BIT_RETRY;
>>>   	} else {
>>> -		ret = do_gmbus_xfer(adapter, msgs, num, 0);
>>> +		ret = do_gmbus_xfer(adapter, msgs, num, 0, false);
>>>   		if (ret == -EAGAIN)
>>>   			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
>>>   	}
>>> @@ -705,7 +741,8 @@ int intel_gmbus_output_aksv(struct i2c_adapter *adapter)
>>>   	 * pass the i2c command, and tell GMBUS to use the HW-provided value
>>>   	 * instead of sourcing GMBUS3 for the data.
>>>   	 */
>>> -	ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT);
>>> +	ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs),
>>> +			    GMBUS_AKSV_SELECT, false);
>>>   
>>>   	mutex_unlock(&dev_priv->gmbus_mutex);
>>>   	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
>>> @@ -713,6 +750,59 @@ int intel_gmbus_output_aksv(struct i2c_adapter *adapter)
>>>   	return ret;
>>>   }
>>>   
>>> +static inline
>>> +bool intel_gmbus_burst_read_supported(struct drm_i915_private *dev_priv)
>>> +{
>>> +	if (INTEL_GEN(dev_priv) > 10 || IS_GEMINILAKE(dev_priv) ||
>>> +	    IS_KABYLAKE(dev_priv))
>>> +		return true;
>>> +	return false;
>>> +}
>>> +
>>> +int intel_gmbus_burst_read(struct i2c_adapter *adapter, unsigned int offset,
>>> +			   void *buf, size_t size)
>>> +{
>>> +	struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus,
>>> +					       adapter);
>>> +	struct drm_i915_private *dev_priv = bus->dev_priv;
>>> +	int ret;
>>> +	u8 start = offset & 0xff;
>>> +	struct i2c_msg msgs[] = {
>>> +		{
>>> +			.addr = DRM_HDCP_DDC_ADDR,
>>> +			.flags = 0,
>>> +			.len = 1,
>>> +			.buf = &start,
>>> +		},
>>> +		{
>>> +			.addr = DRM_HDCP_DDC_ADDR,
>>> +			.flags = I2C_M_RD,
>>> +			.len = size,
>>> +			.buf = buf,
>>> +		}
>>> +	};
>>> +
>>> +	if (!intel_gmbus_burst_read_supported(dev_priv))
>>> +		return -EINVAL;
>>> +
>>> +	intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
>>> +	mutex_lock(&dev_priv->gmbus_mutex);
>>> +
>>> +	/*
>>> +	 * In order to read the complete length(More than GMBus Limit) of data,
>>> +	 * in burst mode, implement the Workaround supported in HW.
>>> +	 */
>>> +	ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs), 0, true);
>>> +
>>> +	mutex_unlock(&dev_priv->gmbus_mutex);
>>> +	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
>>> +
>>> +	if (ret == ARRAY_SIZE(msgs))
>>> +		return 0;
>>> +
>>> +	return ret >= 0 ? -EIO : ret;
>>> +}
>>> +
>>>   static u32 gmbus_func(struct i2c_adapter *adapter)
>>>   {
>>>   	return i2c_bit_algo.functionality(adapter) &
>>> -- 
>>> 2.7.4
>>>



More information about the dri-devel mailing list