diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/ite-it66121.c')
| -rw-r--r-- | drivers/gpu/drm/bridge/ite-it66121.c | 321 | 
1 files changed, 156 insertions, 165 deletions
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 4f6f1deba28c..a2d723d6a4be 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -22,7 +22,6 @@  #include <drm/drm_atomic_helper.h>  #include <drm/drm_bridge.h> -#include <drm/drm_crtc_helper.h>  #include <drm/drm_edid.h>  #include <drm/drm_modes.h>  #include <drm/drm_print.h> @@ -35,10 +34,6 @@  #define IT66121_DEVICE_ID0_REG			0x02  #define IT66121_DEVICE_ID1_REG			0x03 -#define IT66121_VENDOR_ID0			0x54 -#define IT66121_VENDOR_ID1			0x49 -#define IT66121_DEVICE_ID0			0x12 -#define IT66121_DEVICE_ID1			0x06  #define IT66121_REVISION_MASK			GENMASK(7, 4)  #define IT66121_DEVICE_ID1_MASK			GENMASK(3, 0) @@ -72,6 +67,7 @@  #define IT66121_AFE_XP_ENO			BIT(4)  #define IT66121_AFE_XP_RESETB			BIT(3)  #define IT66121_AFE_XP_PWDI			BIT(2) +#define IT6610_AFE_XP_BYPASS			BIT(0)  #define IT66121_AFE_IP_REG			0x64  #define IT66121_AFE_IP_GAINBIT			BIT(7) @@ -286,13 +282,18 @@  #define IT66121_AUD_SWL_16BIT			0x2  #define IT66121_AUD_SWL_NOT_INDICATED		0x0 -#define IT66121_VENDOR_ID0			0x54 -#define IT66121_VENDOR_ID1			0x49 -#define IT66121_DEVICE_ID0			0x12 -#define IT66121_DEVICE_ID1			0x06 -#define IT66121_DEVICE_MASK			0x0F  #define IT66121_AFE_CLK_HIGH			80000 /* Khz */ +enum chip_id { +	ID_IT6610, +	ID_IT66121, +}; + +struct it66121_chip_info { +	enum chip_id id; +	u16 vid, pid; +}; +  struct it66121_ctx {  	struct regmap *regmap;  	struct drm_bridge bridge; @@ -301,7 +302,6 @@ struct it66121_ctx {  	struct device *dev;  	struct gpio_desc *gpio_reset;  	struct i2c_client *client; -	struct regulator_bulk_data supplies[3];  	u32 bus_width;  	struct mutex lock; /* Protects fields below and device registers */  	struct hdmi_avi_infoframe hdmi_avi_infoframe; @@ -312,6 +312,7 @@ struct it66121_ctx {  		u8 swl;  		bool auto_cts;  	} audio; +	const struct it66121_chip_info *info;  };  static const struct regmap_range_cfg it66121_regmap_banks[] = { @@ -342,16 +343,6 @@ static void it66121_hw_reset(struct it66121_ctx *ctx)  	gpiod_set_value(ctx->gpio_reset, 0);  } -static inline int ite66121_power_on(struct it66121_ctx *ctx) -{ -	return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - -static inline int ite66121_power_off(struct it66121_ctx *ctx) -{ -	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} -  static inline int it66121_preamble_ddc(struct it66121_ctx *ctx)  {  	return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); @@ -406,16 +397,22 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,  		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,  					IT66121_AFE_IP_GAINBIT | -					IT66121_AFE_IP_ER0 | -					IT66121_AFE_IP_EC1, +					IT66121_AFE_IP_ER0,  					IT66121_AFE_IP_GAINBIT);  		if (ret)  			return ret; -		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, -					IT66121_AFE_XP_EC1_LOWCLK, 0x80); -		if (ret) -			return ret; +		if (ctx->info->id == ID_IT66121) { +			ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, +						IT66121_AFE_IP_EC1, 0); +			if (ret) +				return ret; + +			ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, +						IT66121_AFE_XP_EC1_LOWCLK, 0x80); +			if (ret) +				return ret; +		}  	} else {  		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,  					IT66121_AFE_XP_GAINBIT | @@ -426,17 +423,24 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,  		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,  					IT66121_AFE_IP_GAINBIT | -					IT66121_AFE_IP_ER0 | -					IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | -					IT66121_AFE_IP_EC1); +					IT66121_AFE_IP_ER0, +					IT66121_AFE_IP_ER0);  		if (ret)  			return ret; -		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, -					IT66121_AFE_XP_EC1_LOWCLK, -					IT66121_AFE_XP_EC1_LOWCLK); -		if (ret) -			return ret; +		if (ctx->info->id == ID_IT66121) { +			ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, +						IT66121_AFE_IP_EC1, +						IT66121_AFE_IP_EC1); +			if (ret) +				return ret; + +			ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, +						IT66121_AFE_XP_EC1_LOWCLK, +						IT66121_AFE_XP_EC1_LOWCLK); +			if (ret) +				return ret; +		}  	}  	/* Clear reset flags */ @@ -445,38 +449,36 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,  	if (ret)  		return ret; +	if (ctx->info->id == ID_IT6610) { +		ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, +					IT6610_AFE_XP_BYPASS, +					IT6610_AFE_XP_BYPASS); +		if (ret) +			return ret; +	} +  	return it66121_fire_afe(ctx);  }  static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx)  {  	int ret, val; -	u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | -		   IT66121_DDC_STATUS_ARBI_LOSE; +	u32 error = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | +		    IT66121_DDC_STATUS_ARBI_LOSE; +	u32 done = IT66121_DDC_STATUS_TX_DONE; -	ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, -				       IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); +	ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, +				       val & (error | done), IT66121_EDID_SLEEP_US, +				       IT66121_EDID_TIMEOUT_US);  	if (ret)  		return ret; -	if (val & busy) +	if (val & error)  		return -EAGAIN;  	return 0;  } -static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx) -{ -	int ret; - -	ret = it66121_preamble_ddc(ctx); -	if (ret) -		return ret; - -	return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, -			    IT66121_DDC_COMMAND_FIFO_CLR); -} -  static int it66121_abort_ddc_ops(struct it66121_ctx *ctx)  {  	int ret; @@ -516,7 +518,6 @@ static int it66121_get_edid_block(void *context, u8 *buf,  				  unsigned int block, size_t len)  {  	struct it66121_ctx *ctx = context; -	unsigned int val;  	int remain = len;  	int offset = 0;  	int ret, cnt; @@ -524,26 +525,9 @@ static int it66121_get_edid_block(void *context, u8 *buf,  	offset = (block % 2) * len;  	block = block / 2; -	ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); -	if (ret) -		return ret; - -	if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { -		ret = it66121_abort_ddc_ops(ctx); -		if (ret) -			return ret; -	} - -	ret = it66121_clear_ddc_fifo(ctx); -	if (ret) -		return ret; -  	while (remain > 0) {  		cnt = (remain > IT66121_EDID_FIFO_SIZE) ?  				IT66121_EDID_FIFO_SIZE : remain; -		ret = it66121_preamble_ddc(ctx); -		if (ret) -			return ret;  		ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,  				   IT66121_DDC_COMMAND_FIFO_CLR); @@ -554,25 +538,6 @@ static int it66121_get_edid_block(void *context, u8 *buf,  		if (ret)  			return ret; -		ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); -		if (ret) -			return ret; - -		if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { -			ret = it66121_abort_ddc_ops(ctx); -			if (ret) -				return ret; -		} - -		ret = it66121_preamble_ddc(ctx); -		if (ret) -			return ret; - -		ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, -				   IT66121_DDC_HEADER_EDID); -		if (ret) -			return ret; -  		ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset);  		if (ret)  			return ret; @@ -593,20 +558,18 @@ static int it66121_get_edid_block(void *context, u8 *buf,  		offset += cnt;  		remain -= cnt; -		/* Per programming manual, sleep here before emptying the FIFO */ -		msleep(20); -  		ret = it66121_wait_ddc_ready(ctx); +		if (ret) { +			it66121_abort_ddc_ops(ctx); +			return ret; +		} + +		ret = regmap_noinc_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, +					buf, cnt);  		if (ret)  			return ret; -		do { -			ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val); -			if (ret) -				return ret; -			*(buf++) = val; -			cnt--; -		} while (cnt > 0); +		buf += cnt;  	}  	return 0; @@ -635,10 +598,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,  	if (ret)  		return ret; -	ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, -				IT66121_CLK_BANK_PWROFF_RCLK, 0); -	if (ret) -		return ret; +	if (ctx->info->id == ID_IT66121) { +		ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, +					IT66121_CLK_BANK_PWROFF_RCLK, 0); +		if (ret) +			return ret; +	}  	ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG,  				IT66121_INT_TX_CLK_OFF, 0); @@ -684,11 +649,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,  	/* Per programming manual, sleep here for bridge to settle */  	msleep(50); -	/* Start interrupts */ -	return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, -				 IT66121_INT_MASK1_DDC_NOACK | -				 IT66121_INT_MASK1_DDC_FIFOERR | -				 IT66121_INT_MASK1_DDC_BUSHANG, 0); +	return 0;  }  static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) @@ -780,29 +741,32 @@ static void it66121_bridge_disable(struct drm_bridge *bridge,  	ctx->connector = NULL;  } +static int it66121_bridge_check(struct drm_bridge *bridge, +				struct drm_bridge_state *bridge_state, +				struct drm_crtc_state *crtc_state, +				struct drm_connector_state *conn_state) +{ +	struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + +	if (ctx->info->id == ID_IT6610) { +		/* The IT6610 only supports these settings */ +		bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH | +			DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; +		bridge_state->input_bus_cfg.flags &= +			~DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; +	} + +	return 0; +} +  static  void it66121_bridge_mode_set(struct drm_bridge *bridge,  			     const struct drm_display_mode *mode,  			     const struct drm_display_mode *adjusted_mode)  { -	int ret, i;  	u8 buf[HDMI_INFOFRAME_SIZE(AVI)];  	struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); -	const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { -		IT66121_AVIINFO_DB1_REG, -		IT66121_AVIINFO_DB2_REG, -		IT66121_AVIINFO_DB3_REG, -		IT66121_AVIINFO_DB4_REG, -		IT66121_AVIINFO_DB5_REG, -		IT66121_AVIINFO_DB6_REG, -		IT66121_AVIINFO_DB7_REG, -		IT66121_AVIINFO_DB8_REG, -		IT66121_AVIINFO_DB9_REG, -		IT66121_AVIINFO_DB10_REG, -		IT66121_AVIINFO_DB11_REG, -		IT66121_AVIINFO_DB12_REG, -		IT66121_AVIINFO_DB13_REG -	}; +	int ret;  	mutex_lock(&ctx->lock); @@ -822,10 +786,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,  	}  	/* Write new AVI infoframe packet */ -	for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { -		if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) -			goto unlock; -	} +	ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, +				&buf[HDMI_INFOFRAME_HEADER_SIZE], +				HDMI_AVI_INFOFRAME_SIZE); +	if (ret) +		goto unlock; +  	if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3]))  		goto unlock; @@ -838,9 +804,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,  	if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))  		goto unlock; -	if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, -			      IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) +	if (ctx->info->id == ID_IT66121 && +	    regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, +			      IT66121_CLK_BANK_PWROFF_TXCLK, +			      IT66121_CLK_BANK_PWROFF_TXCLK)) {  		goto unlock; +	}  	if (it66121_configure_input(ctx))  		goto unlock; @@ -848,7 +817,11 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,  	if (it66121_configure_afe(ctx, adjusted_mode))  		goto unlock; -	regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); +	if (ctx->info->id == ID_IT66121 && +	    regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, +			      IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { +		goto unlock; +	}  unlock:  	mutex_unlock(&ctx->lock); @@ -906,9 +879,25 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge,  {  	struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);  	struct edid *edid; +	int ret;  	mutex_lock(&ctx->lock); +	ret = it66121_preamble_ddc(ctx); +	if (ret) { +		edid = ERR_PTR(ret); +		goto out_unlock; +	} + +	ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, +			   IT66121_DDC_HEADER_EDID); +	if (ret) { +		edid = ERR_PTR(ret); +		goto out_unlock; +	} +  	edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); + +out_unlock:  	mutex_unlock(&ctx->lock);  	return edid; @@ -923,6 +912,7 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = {  	.atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts,  	.atomic_enable = it66121_bridge_enable,  	.atomic_disable = it66121_bridge_disable, +	.atomic_check = it66121_bridge_check,  	.mode_set = it66121_bridge_mode_set,  	.mode_valid = it66121_bridge_mode_valid,  	.detect = it66121_bridge_detect, @@ -952,21 +942,14 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id)  	ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);  	if (ret) {  		dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); -	} else { -		if (val & IT66121_INT_STATUS1_DDC_FIFOERR) -			it66121_clear_ddc_fifo(ctx); -		if (val & (IT66121_INT_STATUS1_DDC_BUSHANG | -			   IT66121_INT_STATUS1_DDC_NOACK)) -			it66121_abort_ddc_ops(ctx); -		if (val & IT66121_INT_STATUS1_HPD_STATUS) { -			regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, -					  IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); +	} else if (val & IT66121_INT_STATUS1_HPD_STATUS) { +		regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, +				  IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); -			status = it66121_is_hpd_detect(ctx) ? connector_status_connected -							    : connector_status_disconnected; +		status = it66121_is_hpd_detect(ctx) ? connector_status_connected +			: connector_status_disconnected; -			event = true; -		} +		event = true;  	}  	regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, @@ -1512,9 +1495,13 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)  	return PTR_ERR_OR_ZERO(ctx->audio.pdev);  } -static int it66121_probe(struct i2c_client *client, -			 const struct i2c_device_id *id) +static const char * const it66121_supplies[] = { +	"vcn33", "vcn18", "vrf12" +}; + +static int it66121_probe(struct i2c_client *client)  { +	const struct i2c_device_id *id = i2c_client_get_device_id(client);  	u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 };  	struct device_node *ep;  	int ret; @@ -1536,6 +1523,7 @@ static int it66121_probe(struct i2c_client *client,  	ctx->dev = dev;  	ctx->client = client; +	ctx->info = (const struct it66121_chip_info *) id->driver_data;  	of_property_read_u32(ep, "bus-width", &ctx->bus_width);  	of_node_put(ep); @@ -1565,26 +1553,18 @@ static int it66121_probe(struct i2c_client *client,  	i2c_set_clientdata(client, ctx);  	mutex_init(&ctx->lock); -	ctx->supplies[0].supply = "vcn33"; -	ctx->supplies[1].supply = "vcn18"; -	ctx->supplies[2].supply = "vrf12"; -	ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies); +	ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it66121_supplies), +					     it66121_supplies);  	if (ret) { -		dev_err(ctx->dev, "regulator_bulk failed\n"); +		dev_err(dev, "Failed to enable power supplies\n");  		return ret;  	} -	ret = ite66121_power_on(ctx); -	if (ret) -		return ret; -  	it66121_hw_reset(ctx);  	ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); -	if (IS_ERR(ctx->regmap)) { -		ite66121_power_off(ctx); +	if (IS_ERR(ctx->regmap))  		return PTR_ERR(ctx->regmap); -	}  	regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]);  	regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); @@ -1595,9 +1575,8 @@ static int it66121_probe(struct i2c_client *client,  	revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]);  	device_ids[1] &= IT66121_DEVICE_ID1_MASK; -	if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || -	    device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { -		ite66121_power_off(ctx); +	if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid || +	    (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) {  		return -ENODEV;  	} @@ -1610,7 +1589,6 @@ static int it66121_probe(struct i2c_client *client,  					IRQF_ONESHOT, dev_name(dev), ctx);  	if (ret < 0) {  		dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); -		ite66121_power_off(ctx);  		return ret;  	} @@ -1627,19 +1605,32 @@ static void it66121_remove(struct i2c_client *client)  {  	struct it66121_ctx *ctx = i2c_get_clientdata(client); -	ite66121_power_off(ctx);  	drm_bridge_remove(&ctx->bridge);  	mutex_destroy(&ctx->lock);  }  static const struct of_device_id it66121_dt_match[] = {  	{ .compatible = "ite,it66121" }, +	{ .compatible = "ite,it6610" },  	{ }  };  MODULE_DEVICE_TABLE(of, it66121_dt_match); +static const struct it66121_chip_info it66121_chip_info = { +	.id = ID_IT66121, +	.vid = 0x4954, +	.pid = 0x0612, +}; + +static const struct it66121_chip_info it6610_chip_info = { +	.id = ID_IT6610, +	.vid = 0xca00, +	.pid = 0x0611, +}; +  static const struct i2c_device_id it66121_id[] = { -	{ "it66121", 0 }, +	{ "it66121", (kernel_ulong_t) &it66121_chip_info }, +	{ "it6610", (kernel_ulong_t) &it6610_chip_info },  	{ }  };  MODULE_DEVICE_TABLE(i2c, it66121_id); @@ -1649,7 +1640,7 @@ static struct i2c_driver it66121_driver = {  		.name	= "it66121",  		.of_match_table = it66121_dt_match,  	}, -	.probe = it66121_probe, +	.probe_new = it66121_probe,  	.remove = it66121_remove,  	.id_table = it66121_id,  };  |