diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/ti-sn65dsi86.c')
| -rw-r--r-- | drivers/gpu/drm/bridge/ti-sn65dsi86.c | 146 | 
1 files changed, 101 insertions, 45 deletions
| diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index ecdf9b01340f..f27306c51e4d 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -17,6 +17,8 @@  #include <linux/regmap.h>  #include <linux/regulator/consumer.h> +#include <asm/unaligned.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_bridge.h> @@ -72,6 +74,7 @@  #define SN_AUX_ADDR_19_16_REG			0x74  #define SN_AUX_ADDR_15_8_REG			0x75  #define SN_AUX_ADDR_7_0_REG			0x76 +#define SN_AUX_ADDR_MASK			GENMASK(19, 0)  #define SN_AUX_LENGTH_REG			0x77  #define SN_AUX_CMD_REG				0x78  #define  AUX_CMD_SEND				BIT(0) @@ -106,6 +109,8 @@  #define SN_NUM_GPIOS			4  #define SN_GPIO_PHYSICAL_OFFSET		1 +#define SN_LINK_TRAINING_TRIES		10 +  /**   * struct ti_sn_bridge - Platform data for ti-sn65dsi86 driver.   * @dev:          Pointer to our device. @@ -116,6 +121,7 @@   * @debugfs:      Used for managing our debugfs.   * @host_node:    Remote DSI node.   * @dsi:          Our MIPI DSI source. + * @edid:         Detected EDID of eDP panel.   * @refclk:       Our reference clock.   * @panel:        Our panel.   * @enable_gpio:  The GPIO we toggle to enable the bridge. @@ -141,6 +147,7 @@ struct ti_sn_bridge {  	struct drm_bridge		bridge;  	struct drm_connector		connector;  	struct dentry			*debugfs; +	struct edid			*edid;  	struct device_node		*host_node;  	struct mipi_dsi_device		*dsi;  	struct clk			*refclk; @@ -262,6 +269,23 @@ connector_to_ti_sn_bridge(struct drm_connector *connector)  static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector)  {  	struct ti_sn_bridge *pdata = connector_to_ti_sn_bridge(connector); +	struct edid *edid = pdata->edid; +	int num, ret; + +	if (!edid) { +		pm_runtime_get_sync(pdata->dev); +		edid = pdata->edid = drm_get_edid(connector, &pdata->aux.ddc); +		pm_runtime_put(pdata->dev); +	} + +	if (edid && drm_edid_is_valid(edid)) { +		ret = drm_connector_update_edid_property(connector, edid); +		if (!ret) { +			num = drm_add_edid_modes(connector, edid); +			if (num) +				return num; +		} +	}  	return drm_panel_get_modes(pdata->panel, connector);  } @@ -673,6 +697,7 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata, int dp_rate_idx,  {  	unsigned int val;  	int ret; +	int i;  	/* set dp clk frequency value */  	regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, @@ -689,19 +714,34 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata, int dp_rate_idx,  		goto exit;  	} -	/* Semi auto link training mode */ -	regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A); -	ret = regmap_read_poll_timeout(pdata->regmap, SN_ML_TX_MODE_REG, val, -				       val == ML_TX_MAIN_LINK_OFF || -				       val == ML_TX_NORMAL_MODE, 1000, -				       500 * 1000); -	if (ret) { -		*last_err_str = "Training complete polling failed"; -	} else if (val == ML_TX_MAIN_LINK_OFF) { -		*last_err_str = "Link training failed, link is off"; -		ret = -EIO; +	/* +	 * We'll try to link train several times.  As part of link training +	 * the bridge chip will write DP_SET_POWER_D0 to DP_SET_POWER.  If +	 * the panel isn't ready quite it might respond NAK here which means +	 * we need to try again. +	 */ +	for (i = 0; i < SN_LINK_TRAINING_TRIES; i++) { +		/* Semi auto link training mode */ +		regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A); +		ret = regmap_read_poll_timeout(pdata->regmap, SN_ML_TX_MODE_REG, val, +					       val == ML_TX_MAIN_LINK_OFF || +					       val == ML_TX_NORMAL_MODE, 1000, +					       500 * 1000); +		if (ret) { +			*last_err_str = "Training complete polling failed"; +		} else if (val == ML_TX_MAIN_LINK_OFF) { +			*last_err_str = "Link training failed, link is off"; +			ret = -EIO; +			continue; +		} + +		break;  	} +	/* If we saw quite a few retries, add a note about it */ +	if (!ret && i > SN_LINK_TRAINING_TRIES / 2) +		DRM_DEV_INFO(pdata->dev, "Link training needed %d retries\n", i); +  exit:  	/* Disable the PLL if we failed */  	if (ret) @@ -816,8 +856,7 @@ static void ti_sn_bridge_post_disable(struct drm_bridge *bridge)  {  	struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); -	if (pdata->refclk) -		clk_disable_unprepare(pdata->refclk); +	clk_disable_unprepare(pdata->refclk);  	pm_runtime_put_sync(pdata->dev);  } @@ -839,13 +878,15 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,  				  struct drm_dp_aux_msg *msg)  {  	struct ti_sn_bridge *pdata = aux_to_ti_sn_bridge(aux); -	u32 request = msg->request & ~DP_AUX_I2C_MOT; +	u32 request = msg->request & ~(DP_AUX_I2C_MOT | DP_AUX_I2C_WRITE_STATUS_UPDATE);  	u32 request_val = AUX_CMD_REQ(msg->request); -	u8 *buf = (u8 *)msg->buffer; +	u8 *buf = msg->buffer; +	unsigned int len = msg->size;  	unsigned int val; -	int ret, i; +	int ret; +	u8 addr_len[SN_AUX_LENGTH_REG + 1 - SN_AUX_ADDR_19_16_REG]; -	if (msg->size > SN_AUX_MAX_PAYLOAD_BYTES) +	if (len > SN_AUX_MAX_PAYLOAD_BYTES)  		return -EINVAL;  	switch (request) { @@ -854,24 +895,21 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,  	case DP_AUX_NATIVE_READ:  	case DP_AUX_I2C_READ:  		regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val); +		/* Assume it's good */ +		msg->reply = 0;  		break;  	default:  		return -EINVAL;  	} -	regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, -		     (msg->address >> 16) & 0xF); -	regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, -		     (msg->address >> 8) & 0xFF); -	regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, msg->address & 0xFF); - -	regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, msg->size); +	BUILD_BUG_ON(sizeof(addr_len) != sizeof(__be32)); +	put_unaligned_be32((msg->address & SN_AUX_ADDR_MASK) << 8 | len, +			   addr_len); +	regmap_bulk_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, addr_len, +			  ARRAY_SIZE(addr_len)); -	if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) { -		for (i = 0; i < msg->size; i++) -			regmap_write(pdata->regmap, SN_AUX_WDATA_REG(i), -				     buf[i]); -	} +	if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) +		regmap_bulk_write(pdata->regmap, SN_AUX_WDATA_REG(0), buf, len);  	/* Clear old status bits before start so we don't get confused */  	regmap_write(pdata->regmap, SN_AUX_CMD_STATUS_REG, @@ -881,35 +919,52 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,  	regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND); +	/* Zero delay loop because i2c transactions are slow already */  	ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val, -				       !(val & AUX_CMD_SEND), 200, -				       50 * 1000); +				       !(val & AUX_CMD_SEND), 0, 50 * 1000);  	if (ret)  		return ret;  	ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val);  	if (ret)  		return ret; -	else if ((val & AUX_IRQ_STATUS_NAT_I2C_FAIL) -		 || (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT) -		 || (val & AUX_IRQ_STATUS_AUX_SHORT)) -		return -ENXIO; -	if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) -		return msg->size; +	if (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT) { +		/* +		 * The hardware tried the message seven times per the DP spec +		 * but it hit a timeout. We ignore defers here because they're +		 * handled in hardware. +		 */ +		return -ETIMEDOUT; +	} -	for (i = 0; i < msg->size; i++) { -		unsigned int val; -		ret = regmap_read(pdata->regmap, SN_AUX_RDATA_REG(i), -				  &val); +	if (val & AUX_IRQ_STATUS_AUX_SHORT) { +		ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &len);  		if (ret)  			return ret; - -		WARN_ON(val & ~0xFF); -		buf[i] = (u8)(val & 0xFF); +	} else if (val & AUX_IRQ_STATUS_NAT_I2C_FAIL) { +		switch (request) { +		case DP_AUX_I2C_WRITE: +		case DP_AUX_I2C_READ: +			msg->reply |= DP_AUX_I2C_REPLY_NACK; +			break; +		case DP_AUX_NATIVE_READ: +		case DP_AUX_NATIVE_WRITE: +			msg->reply |= DP_AUX_NATIVE_REPLY_NACK; +			break; +		} +		return 0;  	} -	return msg->size; +	if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE || +	    len == 0) +		return len; + +	ret = regmap_bulk_read(pdata->regmap, SN_AUX_RDATA_REG(0), buf, len); +	if (ret) +		return ret; + +	return len;  }  static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata) @@ -1251,6 +1306,7 @@ static int ti_sn_bridge_remove(struct i2c_client *client)  	if (!pdata)  		return -EINVAL; +	kfree(pdata->edid);  	ti_sn_debugfs_remove(pdata);  	of_node_put(pdata->host_node); | 
