diff options
Diffstat (limited to 'drivers/gpu/drm/bridge')
22 files changed, 1480 insertions, 545 deletions
| diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 431b6e12a81f..61db5a66b493 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -182,6 +182,7 @@ config DRM_PARADE_PS8622  config DRM_PARADE_PS8640  	tristate "Parade PS8640 MIPI DSI to eDP Converter"  	depends on OF +	select DRM_DP_AUX_BUS  	select DRM_KMS_HELPER  	select DRM_MIPI_DSI  	select DRM_PANEL diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 05e3abb5a0c9..592ecfcf00ca 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -401,7 +401,6 @@ void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode);  int adv7533_patch_registers(struct adv7511 *adv);  int adv7533_patch_cec_registers(struct adv7511 *adv);  int adv7533_attach_dsi(struct adv7511 *adv); -void adv7533_detach_dsi(struct adv7511 *adv);  int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);  #ifdef CONFIG_DRM_I2C_ADV7511_AUDIO diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 76555ae64e9c..f8e5da148599 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -910,9 +910,6 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge,  			return ret;  	} -	if (adv->type == ADV7533 || adv->type == ADV7535) -		ret = adv7533_attach_dsi(adv); -  	if (adv->i2c_main->irq)  		regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0),  			     ADV7511_INT0_HPD); @@ -1288,8 +1285,18 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  	drm_bridge_add(&adv7511->bridge);  	adv7511_audio_init(dev, adv7511); + +	if (adv7511->type == ADV7533 || adv7511->type == ADV7535) { +		ret = adv7533_attach_dsi(adv7511); +		if (ret) +			goto err_unregister_audio; +	} +  	return 0; +err_unregister_audio: +	adv7511_audio_exit(adv7511); +	drm_bridge_remove(&adv7511->bridge);  err_unregister_cec:  	i2c_unregister_device(adv7511->i2c_cec);  	clk_disable_unprepare(adv7511->cec_clk); @@ -1307,8 +1314,6 @@ static int adv7511_remove(struct i2c_client *i2c)  {  	struct adv7511 *adv7511 = i2c_get_clientdata(i2c); -	if (adv7511->type == ADV7533 || adv7511->type == ADV7535) -		adv7533_detach_dsi(adv7511);  	i2c_unregister_device(adv7511->i2c_cec);  	clk_disable_unprepare(adv7511->cec_clk); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index 59d718bde8c4..eb7579dec40a 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -153,11 +153,10 @@ int adv7533_attach_dsi(struct adv7511 *adv)  		return -EPROBE_DEFER;  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) {  		dev_err(dev, "failed to create dsi device\n"); -		ret = PTR_ERR(dsi); -		goto err_dsi_device; +		return PTR_ERR(dsi);  	}  	adv->dsi = dsi; @@ -167,24 +166,13 @@ int adv7533_attach_dsi(struct adv7511 *adv)  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |  			  MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; -	ret = mipi_dsi_attach(dsi); +	ret = devm_mipi_dsi_attach(dev, dsi);  	if (ret < 0) {  		dev_err(dev, "failed to attach dsi to host\n"); -		goto err_dsi_attach; +		return ret;  	}  	return 0; - -err_dsi_attach: -	mipi_dsi_device_unregister(dsi); -err_dsi_device: -	return ret; -} - -void adv7533_detach_dsi(struct adv7511 *adv) -{ -	mipi_dsi_detach(adv->dsi); -	mipi_dsi_device_unregister(adv->dsi);  }  int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index cab6c8b92efd..6a4f20fccf84 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -998,11 +998,21 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp,  	if (!blocking)  		return 0; +	/* +	 * db[1]!=0: entering PSR, wait for fully active remote frame buffer. +	 * db[1]==0: exiting PSR, wait for either +	 *  (a) ACTIVE_RESYNC - the sink "must display the +	 *      incoming active frames from the Source device with no visible +	 *      glitches and/or artifacts", even though timings may still be +	 *      re-synchronizing; or +	 *  (b) INACTIVE - the transition is fully complete. +	 */  	ret = readx_poll_timeout(analogix_dp_get_psr_status, dp, psr_status,  		psr_status >= 0 &&  		((vsc->db[1] && psr_status == DP_PSR_SINK_ACTIVE_RFB) || -		(!vsc->db[1] && psr_status == DP_PSR_SINK_INACTIVE)), 1500, -		DP_TIMEOUT_PSR_LOOP_MS * 1000); +		(!vsc->db[1] && (psr_status == DP_PSR_SINK_ACTIVE_RESYNC || +				 psr_status == DP_PSR_SINK_INACTIVE))), +		1500, DP_TIMEOUT_PSR_LOOP_MS * 1000);  	if (ret) {  		dev_warn(dp->dev, "Failed to apply PSR %d\n", ret);  		return ret; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 1a871f6b6822..2346dbcc505f 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -32,6 +32,8 @@  #include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> +#include <media/v4l2-fwnode.h> +#include <sound/hdmi-codec.h>  #include <video/display_timing.h>  #include "anx7625.h" @@ -166,6 +168,20 @@ static int anx7625_write_and_or(struct anx7625_data *ctx,  				 offset, (val & and_mask) | (or_mask));  } +static int anx7625_config_bit_matrix(struct anx7625_data *ctx) +{ +	int i, ret; + +	ret = anx7625_write_or(ctx, ctx->i2c.tx_p2_client, +			       AUDIO_CONTROL_REGISTER, 0x80); +	for (i = 0; i < 13; i++) +		ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, +					 VIDEO_BIT_MATRIX_12 + i, +					 0x18 + i); + +	return ret; +} +  static int anx7625_read_ctrl_status_p0(struct anx7625_data *ctx)  {  	return anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, AP_AUX_CTRL_STATUS); @@ -191,10 +207,10 @@ static int wait_aux_op_finish(struct anx7625_data *ctx)  			       AP_AUX_CTRL_STATUS);  	if (val < 0 || (val & 0x0F)) {  		DRM_DEV_ERROR(dev, "aux status %02x\n", val); -		val = -EIO; +		return -EIO;  	} -	return val; +	return 0;  }  static int anx7625_video_mute_control(struct anx7625_data *ctx, @@ -221,38 +237,6 @@ static int anx7625_video_mute_control(struct anx7625_data *ctx,  	return ret;  } -static int anx7625_config_audio_input(struct anx7625_data *ctx) -{ -	struct device *dev = &ctx->client->dev; -	int ret; - -	/* Channel num */ -	ret = anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, -				AUDIO_CHANNEL_STATUS_6, I2S_CH_2 << 5); - -	/* FS */ -	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, -				    AUDIO_CHANNEL_STATUS_4, -				    0xf0, AUDIO_FS_48K); -	/* Word length */ -	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, -				    AUDIO_CHANNEL_STATUS_5, -				    0xf0, AUDIO_W_LEN_24_24MAX); -	/* I2S */ -	ret |= anx7625_write_or(ctx, ctx->i2c.tx_p2_client, -				AUDIO_CHANNEL_STATUS_6, I2S_SLAVE_MODE); -	ret |= anx7625_write_and(ctx, ctx->i2c.tx_p2_client, -				 AUDIO_CONTROL_REGISTER, ~TDM_TIMING_MODE); -	/* Audio change flag */ -	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client, -				AP_AV_STATUS, AP_AUDIO_CHG); - -	if (ret < 0) -		DRM_DEV_ERROR(dev, "fail to config audio.\n"); - -	return ret; -} -  /* Reduction of fraction a/b */  static void anx7625_reduction_of_a_fraction(unsigned long *a, unsigned long *b)  { @@ -431,7 +415,7 @@ static int anx7625_dsi_video_timing_config(struct anx7625_data *ctx)  	ret |= anx7625_write_and(ctx, ctx->i2c.rx_p1_client,  			MIPI_LANE_CTRL_0, 0xfc);  	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, -				MIPI_LANE_CTRL_0, 3); +				MIPI_LANE_CTRL_0, ctx->pdata.mipi_lanes - 1);  	/* Htotal */  	htotal = ctx->dt.hactive.min + ctx->dt.hfront_porch.min + @@ -615,6 +599,76 @@ static int anx7625_dsi_config(struct anx7625_data *ctx)  	return ret;  } +static int anx7625_api_dpi_config(struct anx7625_data *ctx) +{ +	struct device *dev = &ctx->client->dev; +	u16 freq = ctx->dt.pixelclock.min / 1000; +	int ret; + +	/* configure pixel clock */ +	ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, +				PIXEL_CLOCK_L, freq & 0xFF); +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, +				 PIXEL_CLOCK_H, (freq >> 8)); + +	/* set DPI mode */ +	/* set to DPI PLL module sel */ +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client, +				 MIPI_DIGITAL_PLL_9, 0x20); +	/* power down MIPI */ +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client, +				 MIPI_LANE_CTRL_10, 0x08); +	/* enable DPI mode */ +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client, +				 MIPI_DIGITAL_PLL_18, 0x1C); +	/* set first edge */ +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, +				 VIDEO_CONTROL_0, 0x06); +	if (ret < 0) +		DRM_DEV_ERROR(dev, "IO error : dpi phy set failed.\n"); + +	return ret; +} + +static int anx7625_dpi_config(struct anx7625_data *ctx) +{ +	struct device *dev = &ctx->client->dev; +	int ret; + +	DRM_DEV_DEBUG_DRIVER(dev, "config dpi\n"); + +	/* DSC disable */ +	ret = anx7625_write_and(ctx, ctx->i2c.rx_p0_client, +				R_DSC_CTRL_0, ~DSC_EN); +	if (ret < 0) { +		DRM_DEV_ERROR(dev, "IO error : disable dsc failed.\n"); +		return ret; +	} + +	ret = anx7625_config_bit_matrix(ctx); +	if (ret < 0) { +		DRM_DEV_ERROR(dev, "config bit matrix failed.\n"); +		return ret; +	} + +	ret = anx7625_api_dpi_config(ctx); +	if (ret < 0) { +		DRM_DEV_ERROR(dev, "mipi phy(dpi) setup failed.\n"); +		return ret; +	} + +	/* set MIPI RX EN */ +	ret = anx7625_write_or(ctx, ctx->i2c.rx_p0_client, +			       AP_AV_STATUS, AP_MIPI_RX_EN); +	/* clear mute flag */ +	ret |= anx7625_write_and(ctx, ctx->i2c.rx_p0_client, +				 AP_AV_STATUS, (u8)~AP_MIPI_MUTE); +	if (ret < 0) +		DRM_DEV_ERROR(dev, "IO error : enable mipi rx failed.\n"); + +	return ret; +} +  static void anx7625_dp_start(struct anx7625_data *ctx)  {  	int ret; @@ -625,9 +679,10 @@ static void anx7625_dp_start(struct anx7625_data *ctx)  		return;  	} -	anx7625_config_audio_input(ctx); - -	ret = anx7625_dsi_config(ctx); +	if (ctx->pdata.is_dpi) +		ret = anx7625_dpi_config(ctx); +	else +		ret = anx7625_dsi_config(ctx);  	if (ret < 0)  		DRM_DEV_ERROR(dev, "MIPI phy setup error.\n"); @@ -795,7 +850,7 @@ static int sp_tx_edid_read(struct anx7625_data *ctx,  	int count, blocks_num;  	u8 pblock_buf[MAX_DPCD_BUFFER_SIZE];  	u8 i, j; -	u8 g_edid_break = 0; +	int g_edid_break = 0;  	int ret;  	struct device *dev = &ctx->client->dev; @@ -826,7 +881,7 @@ static int sp_tx_edid_read(struct anx7625_data *ctx,  				g_edid_break = edid_read(ctx, offset,  							 pblock_buf); -				if (g_edid_break) +				if (g_edid_break < 0)  					break;  				memcpy(&pedid_blocks_buf[offset], @@ -1075,6 +1130,7 @@ static void anx7625_start_dp_work(struct anx7625_data *ctx)  		return;  	} +	ctx->hpd_status = 1;  	ctx->hpd_high_cnt++;  	/* Not support HDCP */ @@ -1084,8 +1140,10 @@ static void anx7625_start_dp_work(struct anx7625_data *ctx)  	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xec, 0x10);  	/* Interrupt for DRM */  	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xff, 0x01); -	if (ret < 0) +	if (ret < 0) { +		DRM_DEV_ERROR(dev, "fail to setting HDCP/auth\n");  		return; +	}  	ret = anx7625_reg_read(ctx, ctx->i2c.rx_p1_client, 0x86);  	if (ret < 0) @@ -1104,6 +1162,10 @@ static void anx7625_hpd_polling(struct anx7625_data *ctx)  	int ret, val;  	struct device *dev = &ctx->client->dev; +	/* Interrupt mode, no need poll HPD status, just return */ +	if (ctx->pdata.intp_irq) +		return; +  	ret = readx_poll_timeout(anx7625_read_hpd_status_p0,  				 ctx, val,  				 ((val & HPD_STATUS) || (val < 0)), @@ -1131,6 +1193,21 @@ static void anx7625_remove_edid(struct anx7625_data *ctx)  	ctx->slimport_edid_p.edid_block_num = -1;  } +static void anx7625_dp_adjust_swing(struct anx7625_data *ctx) +{ +	int i; + +	for (i = 0; i < ctx->pdata.dp_lane0_swing_reg_cnt; i++) +		anx7625_reg_write(ctx, ctx->i2c.tx_p1_client, +				  DP_TX_LANE0_SWING_REG0 + i, +				  ctx->pdata.lane0_reg_data[i] & 0xFF); + +	for (i = 0; i < ctx->pdata.dp_lane1_swing_reg_cnt; i++) +		anx7625_reg_write(ctx, ctx->i2c.tx_p1_client, +				  DP_TX_LANE1_SWING_REG0 + i, +				  ctx->pdata.lane1_reg_data[i] & 0xFF); +} +  static void dp_hpd_change_handler(struct anx7625_data *ctx, bool on)  {  	struct device *dev = &ctx->client->dev; @@ -1146,9 +1223,8 @@ static void dp_hpd_change_handler(struct anx7625_data *ctx, bool on)  	} else {  		DRM_DEV_DEBUG_DRIVER(dev, " HPD high\n");  		anx7625_start_dp_work(ctx); +		anx7625_dp_adjust_swing(ctx);  	} - -	ctx->hpd_status = 1;  }  static int anx7625_hpd_change_detect(struct anx7625_data *ctx) @@ -1225,20 +1301,75 @@ static irqreturn_t anx7625_intr_hpd_isr(int irq, void *data)  	return IRQ_HANDLED;  } +static int anx7625_get_swing_setting(struct device *dev, +				     struct anx7625_platform_data *pdata) +{ +	int num_regs; + +	if (of_get_property(dev->of_node, +			    "analogix,lane0-swing", &num_regs)) { +		if (num_regs > DP_TX_SWING_REG_CNT) +			num_regs = DP_TX_SWING_REG_CNT; + +		pdata->dp_lane0_swing_reg_cnt = num_regs; +		of_property_read_u32_array(dev->of_node, "analogix,lane0-swing", +					   pdata->lane0_reg_data, num_regs); +	} + +	if (of_get_property(dev->of_node, +			    "analogix,lane1-swing", &num_regs)) { +		if (num_regs > DP_TX_SWING_REG_CNT) +			num_regs = DP_TX_SWING_REG_CNT; + +		pdata->dp_lane1_swing_reg_cnt = num_regs; +		of_property_read_u32_array(dev->of_node, "analogix,lane1-swing", +					   pdata->lane1_reg_data, num_regs); +	} + +	return 0; +} +  static int anx7625_parse_dt(struct device *dev,  			    struct anx7625_platform_data *pdata)  { -	struct device_node *np = dev->of_node; +	struct device_node *np = dev->of_node, *ep0;  	struct drm_panel *panel;  	int ret; +	int bus_type, mipi_lanes; + +	anx7625_get_swing_setting(dev, pdata); +	pdata->is_dpi = 1; /* default dpi mode */  	pdata->mipi_host_node = of_graph_get_remote_node(np, 0, 0);  	if (!pdata->mipi_host_node) {  		DRM_DEV_ERROR(dev, "fail to get internal panel.\n");  		return -ENODEV;  	} -	DRM_DEV_DEBUG_DRIVER(dev, "found dsi host node.\n"); +	bus_type = V4L2_FWNODE_BUS_TYPE_PARALLEL; +	mipi_lanes = MAX_LANES_SUPPORT; +	ep0 = of_graph_get_endpoint_by_regs(np, 0, 0); +	if (ep0) { +		if (of_property_read_u32(ep0, "bus-type", &bus_type)) +			bus_type = 0; + +		mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes"); +	} + +	if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */ +		pdata->is_dpi = 0; + +	pdata->mipi_lanes = mipi_lanes; +	if (pdata->mipi_lanes > MAX_LANES_SUPPORT || pdata->mipi_lanes <= 0) +		pdata->mipi_lanes = MAX_LANES_SUPPORT; + +	if (pdata->is_dpi) +		DRM_DEV_DEBUG_DRIVER(dev, "found MIPI DPI host node.\n"); +	else +		DRM_DEV_DEBUG_DRIVER(dev, "found MIPI DSI host node.\n"); + +	if (of_property_read_bool(np, "analogix,audio-enable")) +		pdata->audio_en = 1;  	ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL);  	if (ret < 0) { @@ -1301,9 +1432,215 @@ static enum drm_connector_status anx7625_sink_detect(struct anx7625_data *ctx)  {  	struct device *dev = &ctx->client->dev; -	DRM_DEV_DEBUG_DRIVER(dev, "sink detect, return connected\n"); +	DRM_DEV_DEBUG_DRIVER(dev, "sink detect\n"); -	return connector_status_connected; +	if (ctx->pdata.panel_bridge) +		return connector_status_connected; + +	return ctx->hpd_status ? connector_status_connected : +				     connector_status_disconnected; +} + +static int anx7625_audio_hw_params(struct device *dev, void *data, +				   struct hdmi_codec_daifmt *fmt, +				   struct hdmi_codec_params *params) +{ +	struct anx7625_data *ctx = dev_get_drvdata(dev); +	int wl, ch, rate; +	int ret = 0; + +	if (fmt->fmt != HDMI_DSP_A) { +		DRM_DEV_ERROR(dev, "only supports DSP_A\n"); +		return -EINVAL; +	} + +	DRM_DEV_DEBUG_DRIVER(dev, "setting %d Hz, %d bit, %d channels\n", +			     params->sample_rate, params->sample_width, +			     params->cea.channels); + +	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, +				    AUDIO_CHANNEL_STATUS_6, +				    ~I2S_SLAVE_MODE, +				    TDM_SLAVE_MODE); + +	/* Word length */ +	switch (params->sample_width) { +	case 16: +		wl = AUDIO_W_LEN_16_20MAX; +		break; +	case 18: +		wl = AUDIO_W_LEN_18_20MAX; +		break; +	case 20: +		wl = AUDIO_W_LEN_20_20MAX; +		break; +	case 24: +		wl = AUDIO_W_LEN_24_24MAX; +		break; +	default: +		DRM_DEV_DEBUG_DRIVER(dev, "wordlength: %d bit not support", +				     params->sample_width); +		return -EINVAL; +	} +	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, +				    AUDIO_CHANNEL_STATUS_5, +				    0xf0, wl); + +	/* Channel num */ +	switch (params->cea.channels) { +	case 2: +		ch = I2S_CH_2; +		break; +	case 4: +		ch = TDM_CH_4; +		break; +	case 6: +		ch = TDM_CH_6; +		break; +	case 8: +		ch = TDM_CH_8; +		break; +	default: +		DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support", +				     params->cea.channels); +		return -EINVAL; +	} +	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, +			       AUDIO_CHANNEL_STATUS_6, 0x1f, ch << 5); +	if (ch > I2S_CH_2) +		ret |= anx7625_write_or(ctx, ctx->i2c.tx_p2_client, +				AUDIO_CHANNEL_STATUS_6, AUDIO_LAYOUT); +	else +		ret |= anx7625_write_and(ctx, ctx->i2c.tx_p2_client, +				AUDIO_CHANNEL_STATUS_6, ~AUDIO_LAYOUT); + +	/* FS */ +	switch (params->sample_rate) { +	case 32000: +		rate = AUDIO_FS_32K; +		break; +	case 44100: +		rate = AUDIO_FS_441K; +		break; +	case 48000: +		rate = AUDIO_FS_48K; +		break; +	case 88200: +		rate = AUDIO_FS_882K; +		break; +	case 96000: +		rate = AUDIO_FS_96K; +		break; +	case 176400: +		rate = AUDIO_FS_1764K; +		break; +	case 192000: +		rate = AUDIO_FS_192K; +		break; +	default: +		DRM_DEV_DEBUG_DRIVER(dev, "sample rate: %d not support", +				     params->sample_rate); +		return -EINVAL; +	} +	ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client, +				    AUDIO_CHANNEL_STATUS_4, +				    0xf0, rate); +	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client, +				AP_AV_STATUS, AP_AUDIO_CHG); +	if (ret < 0) { +		DRM_DEV_ERROR(dev, "IO error : config audio.\n"); +		return -EIO; +	} + +	return 0; +} + +static void anx7625_audio_shutdown(struct device *dev, void *data) +{ +	DRM_DEV_DEBUG_DRIVER(dev, "stop audio\n"); +} + +static int anx7625_hdmi_i2s_get_dai_id(struct snd_soc_component *component, +				       struct device_node *endpoint) +{ +	struct of_endpoint of_ep; +	int ret; + +	ret = of_graph_parse_endpoint(endpoint, &of_ep); +	if (ret < 0) +		return ret; + +	/* +	 * HDMI sound should be located at external DPI port +	 * Didn't have good way to check where is internal(DSI) +	 * or external(DPI) bridge +	 */ +	return 0; +} + +static void +anx7625_audio_update_connector_status(struct anx7625_data *ctx, +				      enum drm_connector_status status) +{ +	if (ctx->plugged_cb && ctx->codec_dev) { +		ctx->plugged_cb(ctx->codec_dev, +				status == connector_status_connected); +	} +} + +static int anx7625_audio_hook_plugged_cb(struct device *dev, void *data, +					 hdmi_codec_plugged_cb fn, +					 struct device *codec_dev) +{ +	struct anx7625_data *ctx = data; + +	ctx->plugged_cb = fn; +	ctx->codec_dev = codec_dev; +	anx7625_audio_update_connector_status(ctx, anx7625_sink_detect(ctx)); + +	return 0; +} + +static const struct hdmi_codec_ops anx7625_codec_ops = { +	.hw_params	= anx7625_audio_hw_params, +	.audio_shutdown = anx7625_audio_shutdown, +	.get_dai_id	= anx7625_hdmi_i2s_get_dai_id, +	.hook_plugged_cb = anx7625_audio_hook_plugged_cb, +}; + +static void anx7625_unregister_audio(struct anx7625_data *ctx) +{ +	struct device *dev = &ctx->client->dev; + +	if (ctx->audio_pdev) { +		platform_device_unregister(ctx->audio_pdev); +		ctx->audio_pdev = NULL; +	} + +	DRM_DEV_DEBUG_DRIVER(dev, "unbound to %s", HDMI_CODEC_DRV_NAME); +} + +static int anx7625_register_audio(struct device *dev, struct anx7625_data *ctx) +{ +	struct hdmi_codec_pdata codec_data = { +		.ops = &anx7625_codec_ops, +		.max_i2s_channels = 8, +		.i2s = 1, +		.data = ctx, +	}; + +	ctx->audio_pdev = platform_device_register_data(dev, +							HDMI_CODEC_DRV_NAME, +							PLATFORM_DEVID_AUTO, +							&codec_data, +							sizeof(codec_data)); + +	if (IS_ERR(ctx->audio_pdev)) +		return PTR_ERR(ctx->audio_pdev); + +	DRM_DEV_DEBUG_DRIVER(dev, "bound to %s", HDMI_CODEC_DRV_NAME); + +	return 0;  }  static int anx7625_attach_dsi(struct anx7625_data *ctx) @@ -1316,6 +1653,7 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx)  		.channel = 0,  		.node = NULL,  	}; +	int ret;  	DRM_DEV_DEBUG_DRIVER(dev, "attach dsi\n"); @@ -1325,22 +1663,22 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx)  		return -EINVAL;  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) {  		DRM_DEV_ERROR(dev, "fail to create dsi device.\n");  		return -EINVAL;  	} -	dsi->lanes = 4; +	dsi->lanes = ctx->pdata.mipi_lanes;  	dsi->format = MIPI_DSI_FMT_RGB888;  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO	|  		MIPI_DSI_MODE_VIDEO_SYNC_PULSE	|  		MIPI_DSI_MODE_VIDEO_HSE; -	if (mipi_dsi_attach(dsi) < 0) { +	ret = devm_mipi_dsi_attach(dev, dsi); +	if (ret) {  		DRM_DEV_ERROR(dev, "fail to attach dsi to host.\n"); -		mipi_dsi_device_unregister(dsi); -		return -EINVAL; +		return ret;  	}  	ctx->dsi = dsi; @@ -1350,16 +1688,6 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx)  	return 0;  } -static void anx7625_bridge_detach(struct drm_bridge *bridge) -{ -	struct anx7625_data *ctx = bridge_to_anx7625(bridge); - -	if (ctx->dsi) { -		mipi_dsi_detach(ctx->dsi); -		mipi_dsi_device_unregister(ctx->dsi); -	} -} -  static int anx7625_bridge_attach(struct drm_bridge *bridge,  				 enum drm_bridge_attach_flags flags)  { @@ -1376,12 +1704,6 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge,  		return -ENODEV;  	} -	err = anx7625_attach_dsi(ctx); -	if (err) { -		DRM_DEV_ERROR(dev, "Fail to attach to dsi : %d\n", err); -		return err; -	} -  	if (ctx->pdata.panel_bridge) {  		err = drm_bridge_attach(bridge->encoder,  					ctx->pdata.panel_bridge, @@ -1475,6 +1797,10 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,  	DRM_DEV_DEBUG_DRIVER(dev, "drm mode fixup set\n"); +	/* No need fixup for external monitor */ +	if (!ctx->pdata.panel_bridge) +		return true; +  	hsync = mode->hsync_end - mode->hsync_start;  	hfp = mode->hsync_start - mode->hdisplay;  	hbp = mode->htotal - mode->hsync_end; @@ -1624,7 +1950,6 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,  static const struct drm_bridge_funcs anx7625_bridge_funcs = {  	.attach = anx7625_bridge_attach, -	.detach = anx7625_bridge_detach,  	.disable = anx7625_bridge_disable,  	.mode_valid = anx7625_bridge_mode_valid,  	.mode_set = anx7625_bridge_mode_set, @@ -1851,14 +2176,39 @@ static int anx7625_i2c_probe(struct i2c_client *client,  	platform->bridge.funcs = &anx7625_bridge_funcs;  	platform->bridge.of_node = client->dev.of_node; -	platform->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; -	platform->bridge.type = DRM_MODE_CONNECTOR_eDP; +	platform->bridge.ops = DRM_BRIDGE_OP_EDID; +	if (!platform->pdata.panel_bridge) +		platform->bridge.ops |= DRM_BRIDGE_OP_HPD | +					DRM_BRIDGE_OP_DETECT; +	platform->bridge.type = platform->pdata.panel_bridge ? +				    DRM_MODE_CONNECTOR_eDP : +				    DRM_MODE_CONNECTOR_DisplayPort; +  	drm_bridge_add(&platform->bridge); +	if (!platform->pdata.is_dpi) { +		ret = anx7625_attach_dsi(platform); +		if (ret) { +			DRM_DEV_ERROR(dev, "Fail to attach to dsi : %d\n", ret); +			goto unregister_bridge; +		} +	} + +	if (platform->pdata.audio_en) +		anx7625_register_audio(dev, platform); +  	DRM_DEV_DEBUG_DRIVER(dev, "probe done\n");  	return 0; +unregister_bridge: +	drm_bridge_remove(&platform->bridge); + +	if (!platform->pdata.low_power_mode) +		pm_runtime_put_sync_suspend(&client->dev); + +	anx7625_unregister_i2c_dummy_clients(platform); +  free_wq:  	if (platform->workqueue)  		destroy_workqueue(platform->workqueue); @@ -1883,6 +2233,9 @@ static int anx7625_i2c_remove(struct i2c_client *client)  	anx7625_unregister_i2c_dummy_clients(platform); +	if (platform->pdata.audio_en) +		anx7625_unregister_audio(platform); +  	kfree(platform);  	return 0;  } diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 6dcf64c703f9..3d79b6fb13c8 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -111,6 +111,7 @@  #define AUDIO_CHANNEL_STATUS_6 0xd5  #define TDM_SLAVE_MODE 0x10  #define I2S_SLAVE_MODE 0x08 +#define AUDIO_LAYOUT   0x01  #define AUDIO_CONTROL_REGISTER 0xe6  #define TDM_TIMING_MODE 0x08 @@ -141,12 +142,20 @@  #define  HORIZONTAL_BACK_PORCH_H      0x22  /* Bit[7:4] are reserved */  /******** END of I2C Address 0x72 *********/ + +/***************************************************************/ +/* Register definition of device address 0x7a */ +#define DP_TX_SWING_REG_CNT		0x14 +#define DP_TX_LANE0_SWING_REG0		0x00 +#define DP_TX_LANE1_SWING_REG0		0x14 +/******** END of I2C Address 0x7a *********/ +  /***************************************************************/  /* Register definition of device address 0x7e */  #define  I2C_ADDR_7E_FLASH_CONTROLLER  0x7E -#define FLASH_LOAD_STA 0x05 +#define FLASH_LOAD_STA          0x05  #define FLASH_LOAD_STA_CHK	BIT(7)  #define  XTAL_FRQ_SEL    0x3F @@ -349,12 +358,21 @@ struct s_edid_data {  /***************** Display End *****************/ +#define MAX_LANES_SUPPORT	4 +  struct anx7625_platform_data {  	struct gpio_desc *gpio_p_on;  	struct gpio_desc *gpio_reset;  	struct regulator_bulk_data supplies[3];  	struct drm_bridge *panel_bridge;  	int intp_irq; +	int is_dpi; +	int mipi_lanes; +	int audio_en; +	int dp_lane0_swing_reg_cnt; +	int lane0_reg_data[DP_TX_SWING_REG_CNT]; +	int dp_lane1_swing_reg_cnt; +	int lane1_reg_data[DP_TX_SWING_REG_CNT];  	u32 low_power_mode;  	struct device_node *mipi_host_node;  }; @@ -371,6 +389,7 @@ struct anx7625_i2c_client {  struct anx7625_data {  	struct anx7625_platform_data pdata; +	struct platform_device *audio_pdev;  	int hpd_status;  	int hpd_high_cnt;  	/* Lock for work queue */ @@ -379,6 +398,8 @@ struct anx7625_data {  	struct anx7625_i2c_client i2c;  	struct i2c_client *last_client;  	struct s_edid_data slimport_edid_p; +	struct device *codec_dev; +	hdmi_codec_plugged_cb plugged_cb;  	struct work_struct work;  	struct workqueue_struct *workqueue;  	char edid_block; diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 05eb759da6fc..d24f5b90feab 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -13,6 +13,7 @@  #include <linux/platform_device.h>  #include <linux/regulator/consumer.h> +#include <drm/drm_atomic_helper.h>  #include <drm/drm_bridge.h>  #include <drm/drm_edid.h> @@ -87,10 +88,95 @@ static struct edid *display_connector_get_edid(struct drm_bridge *bridge,  	return drm_get_edid(connector, conn->bridge.ddc);  } +/* + * Since this bridge is tied to the connector, it acts like a passthrough, + * so concerning the output bus formats, either pass the bus formats from the + * previous bridge or return fallback data like done in the bridge function: + * drm_atomic_bridge_chain_select_bus_fmts(). + * This supports negotiation if the bridge chain has all bits in place. + */ +static u32 *display_connector_get_output_bus_fmts(struct drm_bridge *bridge, +					struct drm_bridge_state *bridge_state, +					struct drm_crtc_state *crtc_state, +					struct drm_connector_state *conn_state, +					unsigned int *num_output_fmts) +{ +	struct drm_bridge *prev_bridge = drm_bridge_get_prev_bridge(bridge); +	struct drm_bridge_state *prev_bridge_state; + +	if (!prev_bridge || !prev_bridge->funcs->atomic_get_output_bus_fmts) { +		struct drm_connector *conn = conn_state->connector; +		u32 *out_bus_fmts; + +		*num_output_fmts = 1; +		out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL); +		if (!out_bus_fmts) +			return NULL; + +		if (conn->display_info.num_bus_formats && +		    conn->display_info.bus_formats) +			out_bus_fmts[0] = conn->display_info.bus_formats[0]; +		else +			out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; + +		return out_bus_fmts; +	} + +	prev_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, +							    prev_bridge); + +	return prev_bridge->funcs->atomic_get_output_bus_fmts(prev_bridge, prev_bridge_state, +							      crtc_state, conn_state, +							      num_output_fmts); +} + +/* + * Since this bridge is tied to the connector, it acts like a passthrough, + * so concerning the input bus formats, either pass the bus formats from the + * previous bridge or MEDIA_BUS_FMT_FIXED (like select_bus_fmt_recursive()) + * when atomic_get_input_bus_fmts is not supported. + * This supports negotiation if the bridge chain has all bits in place. + */ +static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, +					struct drm_bridge_state *bridge_state, +					struct drm_crtc_state *crtc_state, +					struct drm_connector_state *conn_state, +					u32 output_fmt, +					unsigned int *num_input_fmts) +{ +	struct drm_bridge *prev_bridge = drm_bridge_get_prev_bridge(bridge); +	struct drm_bridge_state *prev_bridge_state; + +	if (!prev_bridge || !prev_bridge->funcs->atomic_get_input_bus_fmts) { +		u32 *in_bus_fmts; + +		*num_input_fmts = 1; +		in_bus_fmts = kmalloc(sizeof(*in_bus_fmts), GFP_KERNEL); +		if (!in_bus_fmts) +			return NULL; + +		in_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; + +		return in_bus_fmts; +	} + +	prev_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, +							    prev_bridge); + +	return prev_bridge->funcs->atomic_get_input_bus_fmts(prev_bridge, prev_bridge_state, +							     crtc_state, conn_state, output_fmt, +							     num_input_fmts); +} +  static const struct drm_bridge_funcs display_connector_bridge_funcs = {  	.attach = display_connector_attach,  	.detect = display_connector_detect,  	.get_edid = display_connector_get_edid, +	.atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, +	.atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts, +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, +	.atomic_reset = drm_atomic_helper_bridge_reset,  };  static irqreturn_t display_connector_hpd_irq(int irq, void *arg) @@ -107,7 +193,7 @@ static int display_connector_probe(struct platform_device *pdev)  {  	struct display_connector *conn;  	unsigned int type; -	const char *label; +	const char *label = NULL;  	int ret;  	conn = devm_kzalloc(&pdev->dev, sizeof(*conn), GFP_KERNEL); diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 1b0c7eaf6c84..c642d1e02b2f 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -472,11 +472,11 @@ static int lt8912_attach_dsi(struct lt8912 *lt)  		return -EPROBE_DEFER;  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) {  		ret = PTR_ERR(dsi);  		dev_err(dev, "failed to create dsi device (%d)\n", ret); -		goto err_dsi_device; +		return ret;  	}  	lt->dsi = dsi; @@ -489,24 +489,13 @@ static int lt8912_attach_dsi(struct lt8912 *lt)  			  MIPI_DSI_MODE_LPM |  			  MIPI_DSI_MODE_NO_EOT_PACKET; -	ret = mipi_dsi_attach(dsi); +	ret = devm_mipi_dsi_attach(dev, dsi);  	if (ret < 0) {  		dev_err(dev, "failed to attach dsi to host\n"); -		goto err_dsi_attach; +		return ret;  	}  	return 0; - -err_dsi_attach: -	mipi_dsi_device_unregister(dsi); -err_dsi_device: -	return ret; -} - -static void lt8912_detach_dsi(struct lt8912 *lt) -{ -	mipi_dsi_detach(lt->dsi); -	mipi_dsi_device_unregister(lt->dsi);  }  static int lt8912_bridge_connector_init(struct drm_bridge *bridge) @@ -555,10 +544,6 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge,  	if (ret)  		goto error; -	ret = lt8912_attach_dsi(lt); -	if (ret) -		goto error; -  	lt->is_attached = true;  	return 0; @@ -573,7 +558,6 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge)  	struct lt8912 *lt = bridge_to_lt8912(bridge);  	if (lt->is_attached) { -		lt8912_detach_dsi(lt);  		lt8912_hard_power_off(lt);  		drm_connector_unregister(<->connector);  		drm_connector_cleanup(<->connector); @@ -718,8 +702,15 @@ static int lt8912_probe(struct i2c_client *client,  	drm_bridge_add(<->bridge); +	ret = lt8912_attach_dsi(lt); +	if (ret) +		goto err_attach; +  	return 0; +err_attach: +	drm_bridge_remove(<->bridge); +	lt8912_free_i2c(lt);  err_i2c:  	lt8912_put_dt(lt);  err_dt_parse: diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 29b1ce2140ab..dafb1b47c15f 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -586,7 +586,7 @@ lt9611_connector_detect(struct drm_connector *connector, bool force)  	int connected = 0;  	regmap_read(lt9611->regmap, 0x825e, ®_val); -	connected  = (reg_val & BIT(2)); +	connected  = (reg_val & BIT(0));  	lt9611->status = connected ?  connector_status_connected :  				connector_status_disconnected; @@ -760,6 +760,7 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611,  	const struct mipi_dsi_device_info info = { "lt9611", 0, NULL };  	struct mipi_dsi_device *dsi;  	struct mipi_dsi_host *host; +	struct device *dev = lt9611->dev;  	int ret;  	host = of_find_mipi_dsi_host_by_node(dsi_node); @@ -768,7 +769,7 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611,  		return ERR_PTR(-EPROBE_DEFER);  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) {  		dev_err(lt9611->dev, "failed to create dsi device\n");  		return dsi; @@ -779,29 +780,15 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611,  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |  			  MIPI_DSI_MODE_VIDEO_HSE; -	ret = mipi_dsi_attach(dsi); +	ret = devm_mipi_dsi_attach(dev, dsi);  	if (ret < 0) { -		dev_err(lt9611->dev, "failed to attach dsi to host\n"); -		mipi_dsi_device_unregister(dsi); +		dev_err(dev, "failed to attach dsi to host\n");  		return ERR_PTR(ret);  	}  	return dsi;  } -static void lt9611_bridge_detach(struct drm_bridge *bridge) -{ -	struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - -	if (lt9611->dsi1) { -		mipi_dsi_detach(lt9611->dsi1); -		mipi_dsi_device_unregister(lt9611->dsi1); -	} - -	mipi_dsi_detach(lt9611->dsi0); -	mipi_dsi_device_unregister(lt9611->dsi0); -} -  static int lt9611_connector_init(struct drm_bridge *bridge, struct lt9611 *lt9611)  {  	int ret; @@ -838,28 +825,7 @@ static int lt9611_bridge_attach(struct drm_bridge *bridge,  			return ret;  	} -	/* Attach primary DSI */ -	lt9611->dsi0 = lt9611_attach_dsi(lt9611, lt9611->dsi0_node); -	if (IS_ERR(lt9611->dsi0)) -		return PTR_ERR(lt9611->dsi0); - -	/* Attach secondary DSI, if specified */ -	if (lt9611->dsi1_node) { -		lt9611->dsi1 = lt9611_attach_dsi(lt9611, lt9611->dsi1_node); -		if (IS_ERR(lt9611->dsi1)) { -			ret = PTR_ERR(lt9611->dsi1); -			goto err_unregister_dsi0; -		} -	} -  	return 0; - -err_unregister_dsi0: -	lt9611_bridge_detach(bridge); -	drm_connector_cleanup(<9611->connector); -	mipi_dsi_device_unregister(lt9611->dsi0); - -	return ret;  }  static enum drm_mode_status lt9611_bridge_mode_valid(struct drm_bridge *bridge, @@ -926,7 +892,7 @@ static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge)  	int connected;  	regmap_read(lt9611->regmap, 0x825e, ®_val); -	connected  = reg_val & BIT(2); +	connected  = reg_val & BIT(0);  	lt9611->status = connected ?  connector_status_connected :  				connector_status_disconnected; @@ -952,7 +918,6 @@ static void lt9611_bridge_hpd_enable(struct drm_bridge *bridge)  static const struct drm_bridge_funcs lt9611_bridge_funcs = {  	.attach = lt9611_bridge_attach, -	.detach = lt9611_bridge_detach,  	.mode_valid = lt9611_bridge_mode_valid,  	.enable = lt9611_bridge_enable,  	.disable = lt9611_bridge_disable, @@ -1181,10 +1146,29 @@ static int lt9611_probe(struct i2c_client *client,  	drm_bridge_add(<9611->bridge); +	/* Attach primary DSI */ +	lt9611->dsi0 = lt9611_attach_dsi(lt9611, lt9611->dsi0_node); +	if (IS_ERR(lt9611->dsi0)) { +		ret = PTR_ERR(lt9611->dsi0); +		goto err_remove_bridge; +	} + +	/* Attach secondary DSI, if specified */ +	if (lt9611->dsi1_node) { +		lt9611->dsi1 = lt9611_attach_dsi(lt9611, lt9611->dsi1_node); +		if (IS_ERR(lt9611->dsi1)) { +			ret = PTR_ERR(lt9611->dsi1); +			goto err_remove_bridge; +		} +	} +  	lt9611_enable_hpd_interrupts(lt9611);  	return lt9611_audio_init(dev, lt9611); +err_remove_bridge: +	drm_bridge_remove(<9611->bridge); +  err_disable_regulators:  	regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 010657ea7af7..33f9716da0ee 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -258,17 +258,18 @@ static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc,  	const struct mipi_dsi_device_info info = { "lt9611uxc", 0, NULL };  	struct mipi_dsi_device *dsi;  	struct mipi_dsi_host *host; +	struct device *dev = lt9611uxc->dev;  	int ret;  	host = of_find_mipi_dsi_host_by_node(dsi_node);  	if (!host) { -		dev_err(lt9611uxc->dev, "failed to find dsi host\n"); +		dev_err(dev, "failed to find dsi host\n");  		return ERR_PTR(-EPROBE_DEFER);  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) { -		dev_err(lt9611uxc->dev, "failed to create dsi device\n"); +		dev_err(dev, "failed to create dsi device\n");  		return dsi;  	} @@ -277,10 +278,9 @@ static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc,  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |  			  MIPI_DSI_MODE_VIDEO_HSE; -	ret = mipi_dsi_attach(dsi); +	ret = devm_mipi_dsi_attach(dev, dsi);  	if (ret < 0) { -		dev_err(lt9611uxc->dev, "failed to attach dsi to host\n"); -		mipi_dsi_device_unregister(dsi); +		dev_err(dev, "failed to attach dsi to host\n");  		return ERR_PTR(ret);  	} @@ -355,19 +355,6 @@ static int lt9611uxc_connector_init(struct drm_bridge *bridge, struct lt9611uxc  	return drm_connector_attach_encoder(<9611uxc->connector, bridge->encoder);  } -static void lt9611uxc_bridge_detach(struct drm_bridge *bridge) -{ -	struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); - -	if (lt9611uxc->dsi1) { -		mipi_dsi_detach(lt9611uxc->dsi1); -		mipi_dsi_device_unregister(lt9611uxc->dsi1); -	} - -	mipi_dsi_detach(lt9611uxc->dsi0); -	mipi_dsi_device_unregister(lt9611uxc->dsi0); -} -  static int lt9611uxc_bridge_attach(struct drm_bridge *bridge,  				   enum drm_bridge_attach_flags flags)  { @@ -380,27 +367,7 @@ static int lt9611uxc_bridge_attach(struct drm_bridge *bridge,  			return ret;  	} -	/* Attach primary DSI */ -	lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node); -	if (IS_ERR(lt9611uxc->dsi0)) -		return PTR_ERR(lt9611uxc->dsi0); - -	/* Attach secondary DSI, if specified */ -	if (lt9611uxc->dsi1_node) { -		lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node); -		if (IS_ERR(lt9611uxc->dsi1)) { -			ret = PTR_ERR(lt9611uxc->dsi1); -			goto err_unregister_dsi0; -		} -	} -  	return 0; - -err_unregister_dsi0: -	mipi_dsi_detach(lt9611uxc->dsi0); -	mipi_dsi_device_unregister(lt9611uxc->dsi0); - -	return ret;  }  static enum drm_mode_status @@ -544,7 +511,6 @@ static struct edid *lt9611uxc_bridge_get_edid(struct drm_bridge *bridge,  static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {  	.attach = lt9611uxc_bridge_attach, -	.detach = lt9611uxc_bridge_detach,  	.mode_valid = lt9611uxc_bridge_mode_valid,  	.mode_set = lt9611uxc_bridge_mode_set,  	.detect = lt9611uxc_bridge_detect, @@ -980,8 +946,27 @@ retry:  	drm_bridge_add(<9611uxc->bridge); +	/* Attach primary DSI */ +	lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node); +	if (IS_ERR(lt9611uxc->dsi0)) { +		ret = PTR_ERR(lt9611uxc->dsi0); +		goto err_remove_bridge; +	} + +	/* Attach secondary DSI, if specified */ +	if (lt9611uxc->dsi1_node) { +		lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node); +		if (IS_ERR(lt9611uxc->dsi1)) { +			ret = PTR_ERR(lt9611uxc->dsi1); +			goto err_remove_bridge; +		} +	} +  	return lt9611uxc_audio_init(dev, lt9611uxc); +err_remove_bridge: +	drm_bridge_remove(<9611uxc->bridge); +  err_disable_regulators:  	regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies); diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index ad460b96c0a3..702ea803a743 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -14,12 +14,14 @@  #include <drm/drm_atomic_helper.h>  #include <drm/drm_bridge.h> +#include <drm/drm_of.h>  #include <drm/drm_panel.h>  struct lvds_codec {  	struct device *dev;  	struct drm_bridge bridge;  	struct drm_bridge *panel_bridge; +	struct drm_bridge_timings timings;  	struct regulator *vcc;  	struct gpio_desc *powerdown_gpio;  	u32 connector_type; @@ -118,7 +120,7 @@ static int lvds_codec_probe(struct platform_device *pdev)  	struct device_node *bus_node;  	struct drm_panel *panel;  	struct lvds_codec *lvds_codec; -	const char *mapping; +	u32 val;  	int ret;  	lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); @@ -174,32 +176,38 @@ static int lvds_codec_probe(struct platform_device *pdev)  			return -ENXIO;  		} -		ret = of_property_read_string(bus_node, "data-mapping", -					      &mapping); +		ret = drm_of_lvds_get_data_mapping(bus_node);  		of_node_put(bus_node); -		if (ret < 0) { +		if (ret == -ENODEV) {  			dev_warn(dev, "missing 'data-mapping' DT property\n"); +		} else if (ret) { +			dev_err(dev, "invalid 'data-mapping' DT property\n"); +			return ret;  		} else { -			if (!strcmp(mapping, "jeida-18")) { -				lvds_codec->bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG; -			} else if (!strcmp(mapping, "jeida-24")) { -				lvds_codec->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; -			} else if (!strcmp(mapping, "vesa-24")) { -				lvds_codec->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG; -			} else { -				dev_err(dev, "invalid 'data-mapping' DT property\n"); -				return -EINVAL; -			} +			lvds_codec->bus_format = ret;  			lvds_codec->bridge.funcs = &funcs_decoder;  		}  	}  	/* +	 * Encoder might sample data on different clock edge than the display, +	 * for example OnSemi FIN3385 has a dedicated strapping pin to select +	 * the sampling edge. +	 */ +	if (lvds_codec->connector_type == DRM_MODE_CONNECTOR_LVDS && +	    !of_property_read_u32(dev->of_node, "pclk-sample", &val)) { +		lvds_codec->timings.input_bus_flags = val ? +			DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE : +			DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; +	} + +	/*  	 * The panel_bridge bridge is attached to the panel's of_node,  	 * but we need a bridge attached to our of_node for our user  	 * to look up.  	 */  	lvds_codec->bridge.of_node = dev->of_node; +	lvds_codec->bridge.timings = &lvds_codec->timings;  	drm_bridge_add(&lvds_codec->bridge);  	platform_set_drvdata(pdev, lvds_codec); diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index d2808c4a6fb1..cce98bf2a4e7 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -306,19 +306,10 @@ out:  	mutex_unlock(&ge_b850v3_lvds_dev_mutex);  } -static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, -				       const struct i2c_device_id *id) +static int ge_b850v3_register(void)  { +	struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c;  	struct device *dev = &stdp4028_i2c->dev; -	int ret; - -	ret = ge_b850v3_lvds_init(dev); - -	if (ret) -		return ret; - -	ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; -	i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr);  	/* drm bridge initialization */  	ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; @@ -343,6 +334,27 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,  			"ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr);  } +static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, +				       const struct i2c_device_id *id) +{ +	struct device *dev = &stdp4028_i2c->dev; +	int ret; + +	ret = ge_b850v3_lvds_init(dev); + +	if (ret) +		return ret; + +	ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; +	i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr); + +	/* Only register after both bridges are probed */ +	if (!ge_b850v3_lvds_ptr->stdp2690_i2c) +		return 0; + +	return ge_b850v3_register(); +} +  static int stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c)  {  	ge_b850v3_lvds_remove(); @@ -386,7 +398,11 @@ static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c,  	ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c;  	i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr); -	return 0; +	/* Only register after both bridges are probed */ +	if (!ge_b850v3_lvds_ptr->stdp4028_i2c) +		return 0; + +	return ge_b850v3_register();  }  static int stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c) diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 3aaa90913bf8..818704bf5e86 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -9,10 +9,12 @@  #include <linux/i2c.h>  #include <linux/module.h>  #include <linux/of_graph.h> +#include <linux/pm_runtime.h>  #include <linux/regmap.h>  #include <linux/regulator/consumer.h>  #include <drm/drm_bridge.h> +#include <drm/drm_dp_aux_bus.h>  #include <drm/drm_dp_helper.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_of.h> @@ -100,7 +102,7 @@ struct ps8640 {  	struct regulator_bulk_data supplies[2];  	struct gpio_desc *gpio_reset;  	struct gpio_desc *gpio_powerdown; -	bool powered; +	bool pre_enabled;  };  static const struct regmap_config ps8640_regmap_config[] = { @@ -148,8 +150,46 @@ static inline struct ps8640 *aux_to_ps8640(struct drm_dp_aux *aux)  	return container_of(aux, struct ps8640, aux);  } -static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux, -				   struct drm_dp_aux_msg *msg) +static bool ps8640_of_panel_on_aux_bus(struct device *dev) +{ +	struct device_node *bus, *panel; + +	bus = of_get_child_by_name(dev->of_node, "aux-bus"); +	if (!bus) +		return false; + +	panel = of_get_child_by_name(bus, "panel"); +	of_node_put(bus); +	if (!panel) +		return false; +	of_node_put(panel); + +	return true; +} + +static int ps8640_ensure_hpd(struct ps8640 *ps_bridge) +{ +	struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; +	struct device *dev = &ps_bridge->page[PAGE2_TOP_CNTL]->dev; +	int status; +	int ret; + +	/* +	 * Apparently something about the firmware in the chip signals that +	 * HPD goes high by reporting GPIO9 as high (even though HPD isn't +	 * actually connected to GPIO9). +	 */ +	ret = regmap_read_poll_timeout(map, PAGE2_GPIO_H, status, +				       status & PS_GPIO9, 20 * 1000, 200 * 1000); + +	if (ret < 0) +		dev_warn(dev, "HPD didn't go high: %d\n", ret); + +	return ret; +} + +static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, +				       struct drm_dp_aux_msg *msg)  {  	struct ps8640 *ps_bridge = aux_to_ps8640(aux);  	struct regmap *map = ps_bridge->regmap[PAGE0_DP_CNTL]; @@ -274,38 +314,49 @@ static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux,  	return len;  } -static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge, -				     const enum ps8640_vdo_control ctrl) +static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux, +				   struct drm_dp_aux_msg *msg) +{ +	struct ps8640 *ps_bridge = aux_to_ps8640(aux); +	struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev; +	int ret; + +	pm_runtime_get_sync(dev); +	ret = ps8640_ensure_hpd(ps_bridge); +	if (!ret) +		ret = ps8640_aux_transfer_msg(aux, msg); +	pm_runtime_mark_last_busy(dev); +	pm_runtime_put_autosuspend(dev); + +	return ret; +} + +static void ps8640_bridge_vdo_control(struct ps8640 *ps_bridge, +				      const enum ps8640_vdo_control ctrl)  {  	struct regmap *map = ps_bridge->regmap[PAGE3_DSI_CNTL1]; +	struct device *dev = &ps_bridge->page[PAGE3_DSI_CNTL1]->dev;  	u8 vdo_ctrl_buf[] = { VDO_CTL_ADD, ctrl };  	int ret;  	ret = regmap_bulk_write(map, PAGE3_SET_ADD,  				vdo_ctrl_buf, sizeof(vdo_ctrl_buf)); -	if (ret < 0) { -		DRM_ERROR("failed to %sable VDO: %d\n", -			  ctrl == ENABLE ? "en" : "dis", ret); -		return ret; -	} - -	return 0; +	if (ret < 0) +		dev_err(dev, "failed to %sable VDO: %d\n", +			ctrl == ENABLE ? "en" : "dis", ret);  } -static void ps8640_bridge_poweron(struct ps8640 *ps_bridge) +static int __maybe_unused ps8640_resume(struct device *dev)  { -	struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; -	int ret, status; - -	if (ps_bridge->powered) -		return; +	struct ps8640 *ps_bridge = dev_get_drvdata(dev); +	int ret;  	ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),  				    ps_bridge->supplies);  	if (ret < 0) { -		DRM_ERROR("cannot enable regulators %d\n", ret); -		return; +		dev_err(dev, "cannot enable regulators %d\n", ret); +		return ret;  	}  	gpiod_set_value(ps_bridge->gpio_powerdown, 0); @@ -314,86 +365,78 @@ static void ps8640_bridge_poweron(struct ps8640 *ps_bridge)  	gpiod_set_value(ps_bridge->gpio_reset, 0);  	/* -	 * Wait for the ps8640 embedded MCU to be ready -	 * First wait 200ms and then check the MCU ready flag every 20ms +	 * Mystery 200 ms delay for the "MCU to be ready". It's unclear if +	 * this is truly necessary since the MCU will already signal that +	 * things are "good to go" by signaling HPD on "gpio 9". See +	 * ps8640_ensure_hpd(). For now we'll keep this mystery delay just in +	 * case.  	 */  	msleep(200); -	ret = regmap_read_poll_timeout(map, PAGE2_GPIO_H, status, -				       status & PS_GPIO9, 20 * 1000, 200 * 1000); - -	if (ret < 0) { -		DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", ret); -		goto err_regulators_disable; -	} - -	msleep(50); - -	/* -	 * The Manufacturer Command Set (MCS) is a device dependent interface -	 * intended for factory programming of the display module default -	 * parameters. Once the display module is configured, the MCS shall be -	 * disabled by the manufacturer. Once disabled, all MCS commands are -	 * ignored by the display interface. -	 */ - -	ret = regmap_update_bits(map, PAGE2_MCS_EN, MCS_EN, 0); -	if (ret < 0) { -		DRM_ERROR("failed write PAGE2_MCS_EN: %d\n", ret); -		goto err_regulators_disable; -	} - -	/* Switch access edp panel's edid through i2c */ -	ret = regmap_write(map, PAGE2_I2C_BYPASS, I2C_BYPASS_EN); -	if (ret < 0) { -		DRM_ERROR("failed write PAGE2_I2C_BYPASS: %d\n", ret); -		goto err_regulators_disable; -	} - -	ps_bridge->powered = true; - -	return; - -err_regulators_disable: -	regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies), -			       ps_bridge->supplies); +	return 0;  } -static void ps8640_bridge_poweroff(struct ps8640 *ps_bridge) +static int __maybe_unused ps8640_suspend(struct device *dev)  { +	struct ps8640 *ps_bridge = dev_get_drvdata(dev);  	int ret; -	if (!ps_bridge->powered) -		return; -  	gpiod_set_value(ps_bridge->gpio_reset, 1);  	gpiod_set_value(ps_bridge->gpio_powerdown, 1);  	ret = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),  				     ps_bridge->supplies);  	if (ret < 0) -		DRM_ERROR("cannot disable regulators %d\n", ret); +		dev_err(dev, "cannot disable regulators %d\n", ret); -	ps_bridge->powered = false; +	return ret;  } +static const struct dev_pm_ops ps8640_pm_ops = { +	SET_RUNTIME_PM_OPS(ps8640_suspend, ps8640_resume, NULL) +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, +				pm_runtime_force_resume) +}; +  static void ps8640_pre_enable(struct drm_bridge *bridge)  {  	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); +	struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; +	struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev;  	int ret; -	ps8640_bridge_poweron(ps_bridge); +	pm_runtime_get_sync(dev); +	ps8640_ensure_hpd(ps_bridge); -	ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE); +	/* +	 * The Manufacturer Command Set (MCS) is a device dependent interface +	 * intended for factory programming of the display module default +	 * parameters. Once the display module is configured, the MCS shall be +	 * disabled by the manufacturer. Once disabled, all MCS commands are +	 * ignored by the display interface. +	 */ + +	ret = regmap_update_bits(map, PAGE2_MCS_EN, MCS_EN, 0);  	if (ret < 0) -		ps8640_bridge_poweroff(ps_bridge); +		dev_warn(dev, "failed write PAGE2_MCS_EN: %d\n", ret); + +	/* Switch access edp panel's edid through i2c */ +	ret = regmap_write(map, PAGE2_I2C_BYPASS, I2C_BYPASS_EN); +	if (ret < 0) +		dev_warn(dev, "failed write PAGE2_MCS_EN: %d\n", ret); + +	ps8640_bridge_vdo_control(ps_bridge, ENABLE); + +	ps_bridge->pre_enabled = true;  }  static void ps8640_post_disable(struct drm_bridge *bridge)  {  	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); +	ps_bridge->pre_enabled = false; +  	ps8640_bridge_vdo_control(ps_bridge, DISABLE); -	ps8640_bridge_poweroff(ps_bridge); +	pm_runtime_put_sync_suspend(&ps_bridge->page[PAGE0_DP_CNTL]->dev);  }  static int ps8640_bridge_attach(struct drm_bridge *bridge, @@ -401,68 +444,21 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge,  {  	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);  	struct device *dev = &ps_bridge->page[0]->dev; -	struct device_node *in_ep, *dsi_node; -	struct mipi_dsi_device *dsi; -	struct mipi_dsi_host *host;  	int ret; -	const struct mipi_dsi_device_info info = { .type = "ps8640", -						   .channel = 0, -						   .node = NULL, -						 };  	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))  		return -EINVAL; -	/* port@0 is ps8640 dsi input port */ -	in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); -	if (!in_ep) -		return -ENODEV; - -	dsi_node = of_graph_get_remote_port_parent(in_ep); -	of_node_put(in_ep); -	if (!dsi_node) -		return -ENODEV; - -	host = of_find_mipi_dsi_host_by_node(dsi_node); -	of_node_put(dsi_node); -	if (!host) -		return -ENODEV; - -	dsi = mipi_dsi_device_register_full(host, &info); -	if (IS_ERR(dsi)) { -		dev_err(dev, "failed to create dsi device\n"); -		ret = PTR_ERR(dsi); -		return ret; -	} - -	ps_bridge->dsi = dsi; - -	dsi->host = host; -	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | -			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE; -	dsi->format = MIPI_DSI_FMT_RGB888; -	dsi->lanes = NUM_MIPI_LANES; -	ret = mipi_dsi_attach(dsi); -	if (ret) { -		dev_err(dev, "failed to attach dsi device: %d\n", ret); -		goto err_dsi_attach; -	} - +	ps_bridge->aux.drm_dev = bridge->dev;  	ret = drm_dp_aux_register(&ps_bridge->aux);  	if (ret) {  		dev_err(dev, "failed to register DP AUX channel: %d\n", ret); -		goto err_aux_register; +		return ret;  	}  	/* Attach the panel-bridge to the dsi bridge */  	return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge,  				 &ps_bridge->bridge, flags); - -err_aux_register: -	mipi_dsi_detach(dsi); -err_dsi_attach: -	mipi_dsi_device_unregister(dsi); -	return ret;  }  static void ps8640_bridge_detach(struct drm_bridge *bridge) @@ -474,7 +470,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,  					   struct drm_connector *connector)  {  	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); -	bool poweroff = !ps_bridge->powered; +	bool poweroff = !ps_bridge->pre_enabled;  	struct edid *edid;  	/* @@ -504,6 +500,12 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,  	return edid;  } +static void ps8640_runtime_disable(void *data) +{ +	pm_runtime_dont_use_autosuspend(data); +	pm_runtime_disable(data); +} +  static const struct drm_bridge_funcs ps8640_bridge_funcs = {  	.attach = ps8640_bridge_attach,  	.detach = ps8640_bridge_detach, @@ -512,6 +514,53 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = {  	.pre_enable = ps8640_pre_enable,  }; +static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridge) +{ +	struct device_node *in_ep, *dsi_node; +	struct mipi_dsi_device *dsi; +	struct mipi_dsi_host *host; +	int ret; +	const struct mipi_dsi_device_info info = { .type = "ps8640", +						   .channel = 0, +						   .node = NULL, +						 }; + +	/* port@0 is ps8640 dsi input port */ +	in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); +	if (!in_ep) +		return -ENODEV; + +	dsi_node = of_graph_get_remote_port_parent(in_ep); +	of_node_put(in_ep); +	if (!dsi_node) +		return -ENODEV; + +	host = of_find_mipi_dsi_host_by_node(dsi_node); +	of_node_put(dsi_node); +	if (!host) +		return -EPROBE_DEFER; + +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info); +	if (IS_ERR(dsi)) { +		dev_err(dev, "failed to create dsi device\n"); +		return PTR_ERR(dsi); +	} + +	ps_bridge->dsi = dsi; + +	dsi->host = host; +	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | +			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE; +	dsi->format = MIPI_DSI_FMT_RGB888; +	dsi->lanes = NUM_MIPI_LANES; + +	ret = devm_mipi_dsi_attach(dev, dsi); +	if (ret) +		return ret; + +	return 0; +} +  static int ps8640_probe(struct i2c_client *client)  {  	struct device *dev = &client->dev; @@ -525,17 +574,6 @@ static int ps8640_probe(struct i2c_client *client)  	if (!ps_bridge)  		return -ENOMEM; -	/* port@1 is ps8640 output port */ -	ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); -	if (ret < 0) -		return ret; -	if (!panel) -		return -ENODEV; - -	ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel); -	if (IS_ERR(ps_bridge->panel_bridge)) -		return PTR_ERR(ps_bridge->panel_bridge); -  	ps_bridge->supplies[0].supply = "vdd33";  	ps_bridge->supplies[1].supply = "vdd12";  	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies), @@ -558,9 +596,16 @@ static int ps8640_probe(struct i2c_client *client)  	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;  	ps_bridge->bridge.of_node = dev->of_node; -	ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;  	ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP; +	/* +	 * In the device tree, if panel is listed under aux-bus of the bridge +	 * node, panel driver should be able to retrieve EDID by itself using +	 * aux-bus. So let's not set DRM_BRIDGE_OP_EDID here. +	 */ +	if (!ps8640_of_panel_on_aux_bus(&client->dev)) +		ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; +  	ps_bridge->page[PAGE0_DP_CNTL] = client;  	ps_bridge->regmap[PAGE0_DP_CNTL] = devm_regmap_init_i2c(client, ps8640_regmap_config); @@ -587,9 +632,46 @@ static int ps8640_probe(struct i2c_client *client)  	ps_bridge->aux.transfer = ps8640_aux_transfer;  	drm_dp_aux_init(&ps_bridge->aux); +	pm_runtime_enable(dev); +	/* +	 * Powering on ps8640 takes ~300ms. To avoid wasting time on power +	 * cycling ps8640 too often, set autosuspend_delay to 1000ms to ensure +	 * the bridge wouldn't suspend in between each _aux_transfer_msg() call +	 * during EDID read (~20ms in my experiment) and in between the last +	 * _aux_transfer_msg() call during EDID read and the _pre_enable() call +	 * (~100ms in my experiment). +	 */ +	pm_runtime_set_autosuspend_delay(dev, 1000); +	pm_runtime_use_autosuspend(dev); +	pm_suspend_ignore_children(dev, true); +	ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev); +	if (ret) +		return ret; + +	devm_of_dp_aux_populate_ep_devices(&ps_bridge->aux); + +	/* port@1 is ps8640 output port */ +	ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); +	if (ret < 0) +		return ret; +	if (!panel) +		return -ENODEV; + +	ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel); +	if (IS_ERR(ps_bridge->panel_bridge)) +		return PTR_ERR(ps_bridge->panel_bridge); +  	drm_bridge_add(&ps_bridge->bridge); +	ret = ps8640_bridge_host_attach(dev, ps_bridge); +	if (ret) +		goto err_bridge_remove; +  	return 0; + +err_bridge_remove: +	drm_bridge_remove(&ps_bridge->bridge); +	return ret;  }  static int ps8640_remove(struct i2c_client *client) @@ -613,6 +695,7 @@ static struct i2c_driver ps8640_driver = {  	.driver = {  		.name = "ps8640",  		.of_match_table = ps8640_match, +		.pm = &ps8640_pm_ops,  	},  };  module_i2c_driver(ps8640_driver); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c index d0db1acf11d7..7d2ed0ed2fe2 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c @@ -320,13 +320,17 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)  	struct snd_pcm_runtime *runtime = substream->runtime;  	struct snd_dw_hdmi *dw = substream->private_data;  	void __iomem *base = dw->data.base; +	u8 *eld;  	int ret;  	runtime->hw = dw_hdmi_hw; -	ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld); -	if (ret < 0) -		return ret; +	eld = dw->data.get_eld(dw->data.hdmi); +	if (eld) { +		ret = snd_pcm_hw_constraint_eld(runtime, eld); +		if (ret < 0) +			return ret; +	}  	ret = snd_pcm_limit_hw_rates(runtime);  	if (ret < 0) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index cb07dc0da5a7..f72d27208ebe 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -9,15 +9,15 @@ struct dw_hdmi_audio_data {  	void __iomem *base;  	int irq;  	struct dw_hdmi *hdmi; -	u8 *eld; +	u8 *(*get_eld)(struct dw_hdmi *hdmi);  };  struct dw_hdmi_i2s_audio_data {  	struct dw_hdmi *hdmi; -	u8 *eld;  	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);  	u8 (*read)(struct dw_hdmi *hdmi, int offset); +	u8 *(*get_eld)(struct dw_hdmi *hdmi);  };  #endif diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index feb04f127b55..f50b47ac11a8 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -135,8 +135,15 @@ static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,  			       size_t len)  {  	struct dw_hdmi_i2s_audio_data *audio = data; +	u8 *eld; + +	eld = audio->get_eld(audio->hdmi); +	if (eld) +		memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len)); +	else +		/* Pass en empty ELD if connector not available */ +		memset(buf, 0, len); -	memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len));  	return 0;  } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index f08d0fded61f..54d8fdad395f 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -757,6 +757,14 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)  	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);  } +static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi) +{ +	if (!hdmi->curr_conn) +		return NULL; + +	return hdmi->curr_conn->eld; +} +  static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)  {  	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); @@ -3413,6 +3421,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,  	hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;  	hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID  			 | DRM_BRIDGE_OP_HPD; +	hdmi->bridge.interlace_allowed = true;  #ifdef CONFIG_OF  	hdmi->bridge.of_node = pdev->dev.of_node;  #endif @@ -3431,7 +3440,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,  		audio.base = hdmi->regs;  		audio.irq = irq;  		audio.hdmi = hdmi; -		audio.eld = hdmi->connector.eld; +		audio.get_eld = hdmi_audio_get_eld;  		hdmi->enable_audio = dw_hdmi_ahb_audio_enable;  		hdmi->disable_audio = dw_hdmi_ahb_audio_disable; @@ -3444,7 +3453,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,  		struct dw_hdmi_i2s_audio_data audio;  		audio.hdmi	= hdmi; -		audio.eld	= hdmi->connector.eld; +		audio.get_eld	= hdmi_audio_get_eld;  		audio.write	= hdmi_writeb;  		audio.read	= hdmi_readb;  		hdmi->enable_audio = dw_hdmi_i2s_audio_enable; diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index a3db532bbdd1..fd585bf925fe 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -237,6 +237,10 @@ static void tc358768_hw_enable(struct tc358768_priv *priv)  	if (priv->enabled)  		return; +	ret = clk_prepare_enable(priv->refclk); +	if (ret < 0) +		dev_err(priv->dev, "error enabling refclk (%d)\n", ret); +  	ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);  	if (ret < 0)  		dev_err(priv->dev, "error enabling regulators (%d)\n", ret); @@ -274,6 +278,8 @@ static void tc358768_hw_disable(struct tc358768_priv *priv)  	if (ret < 0)  		dev_err(priv->dev, "error disabling regulators (%d)\n", ret); +	clk_disable_unprepare(priv->refclk); +  	priv->enabled = false;  } @@ -625,12 +631,19 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  {  	struct tc358768_priv *priv = bridge_to_tc358768(bridge);  	struct mipi_dsi_device *dsi_dev = priv->output.dev; +	unsigned long mode_flags = dsi_dev->mode_flags;  	u32 val, val2, lptxcnt, hact, data_type;  	const struct drm_display_mode *mode;  	u32 dsibclk_nsk, dsiclk_nsk, ui_nsk, phy_delay_nsk; -	u32 dsiclk, dsibclk; +	u32 dsiclk, dsibclk, video_start; +	const u32 internal_delay = 40;  	int ret, i; +	if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { +		dev_warn_once(priv->dev, "Non-continuous mode unimplemented, falling back to continuous\n"); +		mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; +	} +  	tc358768_hw_enable(priv);  	ret = tc358768_sw_reset(priv); @@ -657,23 +670,27 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  	case MIPI_DSI_FMT_RGB888:  		val |= (0x3 << 4);  		hact = mode->hdisplay * 3; +		video_start = (mode->htotal - mode->hsync_start) * 3;  		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;  		break;  	case MIPI_DSI_FMT_RGB666:  		val |= (0x4 << 4);  		hact = mode->hdisplay * 3; +		video_start = (mode->htotal - mode->hsync_start) * 3;  		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;  		break;  	case MIPI_DSI_FMT_RGB666_PACKED:  		val |= (0x4 << 4) | BIT(3);  		hact = mode->hdisplay * 18 / 8; +		video_start = (mode->htotal - mode->hsync_start) * 18 / 8;  		data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;  		break;  	case MIPI_DSI_FMT_RGB565:  		val |= (0x5 << 4);  		hact = mode->hdisplay * 2; +		video_start = (mode->htotal - mode->hsync_start) * 2;  		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;  		break;  	default: @@ -684,7 +701,8 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  	}  	/* VSDly[9:0] */ -	tc358768_write(priv, TC358768_VSDLY, 1); +	video_start = max(video_start, internal_delay + 1) - internal_delay; +	tc358768_write(priv, TC358768_VSDLY, video_start);  	tc358768_write(priv, TC358768_DATAFMT, val);  	tc358768_write(priv, TC358768_DSITX_DT, data_type); @@ -764,7 +782,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  		val |= BIT(i + 1);  	tc358768_write(priv, TC358768_HSTXVREGEN, val); -	if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) +	if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))  		tc358768_write(priv, TC358768_TXOPTIONCNTRL, 0x1);  	/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */ @@ -772,31 +790,61 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  	val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;  	val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk),  				  dsibclk_nsk) - 2; -	val |= val2 << 16; +	val = val << 16 | val2;  	dev_dbg(priv->dev, "BTACNTRL1: 0x%x\n", val);  	tc358768_write(priv, TC358768_BTACNTRL1, val);  	/* START[0] */  	tc358768_write(priv, TC358768_STARTCNTRL, 1); -	/* Set event mode */ -	tc358768_write(priv, TC358768_DSI_EVENT, 1); - -	/* vsw (+ vbp) */ -	tc358768_write(priv, TC358768_DSI_VSW, -		       mode->vtotal - mode->vsync_start); -	/* vbp (not used in event mode) */ -	tc358768_write(priv, TC358768_DSI_VBPR, 0); -	/* vact */ -	tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); - -	/* (hsw + hbp) * byteclk * ndl / pclk */ -	val = (u32)div_u64((mode->htotal - mode->hsync_start) * -			   ((u64)priv->dsiclk / 4) * priv->dsi_lanes, -			   mode->clock * 1000); -	tc358768_write(priv, TC358768_DSI_HSW, val); -	/* hbp (not used in event mode) */ -	tc358768_write(priv, TC358768_DSI_HBPR, 0); +	if (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { +		/* Set pulse mode */ +		tc358768_write(priv, TC358768_DSI_EVENT, 0); + +		/* vact */ +		tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + +		/* vsw */ +		tc358768_write(priv, TC358768_DSI_VSW, +			       mode->vsync_end - mode->vsync_start); +		/* vbp */ +		tc358768_write(priv, TC358768_DSI_VBPR, +			       mode->vtotal - mode->vsync_end); + +		/* hsw * byteclk * ndl / pclk */ +		val = (u32)div_u64((mode->hsync_end - mode->hsync_start) * +				   ((u64)priv->dsiclk / 4) * priv->dsi_lanes, +				   mode->clock * 1000); +		tc358768_write(priv, TC358768_DSI_HSW, val); + +		/* hbp * byteclk * ndl / pclk */ +		val = (u32)div_u64((mode->htotal - mode->hsync_end) * +				   ((u64)priv->dsiclk / 4) * priv->dsi_lanes, +				   mode->clock * 1000); +		tc358768_write(priv, TC358768_DSI_HBPR, val); +	} else { +		/* Set event mode */ +		tc358768_write(priv, TC358768_DSI_EVENT, 1); + +		/* vact */ +		tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + +		/* vsw (+ vbp) */ +		tc358768_write(priv, TC358768_DSI_VSW, +			       mode->vtotal - mode->vsync_start); +		/* vbp (not used in event mode) */ +		tc358768_write(priv, TC358768_DSI_VBPR, 0); + +		/* (hsw + hbp) * byteclk * ndl / pclk */ +		val = (u32)div_u64((mode->htotal - mode->hsync_start) * +				   ((u64)priv->dsiclk / 4) * priv->dsi_lanes, +				   mode->clock * 1000); +		tc358768_write(priv, TC358768_DSI_HSW, val); + +		/* hbp (not used in event mode) */ +		tc358768_write(priv, TC358768_DSI_HBPR, 0); +	} +  	/* hact (bytes) */  	tc358768_write(priv, TC358768_DSI_HACT, hact); @@ -822,7 +870,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)  	if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM))  		val |= TC358768_DSI_CONTROL_TXMD; -	if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) +	if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))  		val |= TC358768_DSI_CONTROL_HSCKMD;  	if (dsi_dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 2272adcc5b4a..2c76331b251d 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -594,11 +594,26 @@ static int tc_bridge_attach(struct drm_bridge *bridge,  			    enum drm_bridge_attach_flags flags)  {  	struct tc_data *tc = bridge_to_tc(bridge); + +	/* Attach the panel-bridge to the dsi bridge */ +	return drm_bridge_attach(bridge->encoder, tc->panel_bridge, +				 &tc->bridge, flags); +} + +static const struct drm_bridge_funcs tc_bridge_funcs = { +	.attach = tc_bridge_attach, +	.pre_enable = tc_bridge_pre_enable, +	.enable = tc_bridge_enable, +	.mode_valid = tc_mode_valid, +	.post_disable = tc_bridge_post_disable, +}; + +static int tc_attach_host(struct tc_data *tc) +{  	struct device *dev = &tc->i2c->dev;  	struct mipi_dsi_host *host;  	struct mipi_dsi_device *dsi;  	int ret; -  	const struct mipi_dsi_device_info info = { .type = "tc358775",  							.channel = 0,  							.node = NULL, @@ -610,11 +625,10 @@ static int tc_bridge_attach(struct drm_bridge *bridge,  		return -EPROBE_DEFER;  	} -	dsi = mipi_dsi_device_register_full(host, &info); +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);  	if (IS_ERR(dsi)) {  		dev_err(dev, "failed to create dsi device\n"); -		ret = PTR_ERR(dsi); -		goto err_dsi_device; +		return PTR_ERR(dsi);  	}  	tc->dsi = dsi; @@ -623,29 +637,15 @@ static int tc_bridge_attach(struct drm_bridge *bridge,  	dsi->format = MIPI_DSI_FMT_RGB888;  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO; -	ret = mipi_dsi_attach(dsi); +	ret = devm_mipi_dsi_attach(dev, dsi);  	if (ret < 0) {  		dev_err(dev, "failed to attach dsi to host\n"); -		goto err_dsi_attach; +		return ret;  	} -	/* Attach the panel-bridge to the dsi bridge */ -	return drm_bridge_attach(bridge->encoder, tc->panel_bridge, -				 &tc->bridge, flags); -err_dsi_attach: -	mipi_dsi_device_unregister(dsi); -err_dsi_device: -	return ret; +	return 0;  } -static const struct drm_bridge_funcs tc_bridge_funcs = { -	.attach = tc_bridge_attach, -	.pre_enable = tc_bridge_pre_enable, -	.enable = tc_bridge_enable, -	.mode_valid = tc_mode_valid, -	.post_disable = tc_bridge_post_disable, -}; -  static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  {  	struct device *dev = &client->dev; @@ -709,7 +709,15 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  	i2c_set_clientdata(client, tc); +	ret = tc_attach_host(tc); +	if (ret) +		goto err_bridge_remove; +  	return 0; + +err_bridge_remove: +	drm_bridge_remove(&tc->bridge); +	return ret;  }  static int tc_remove(struct i2c_client *client) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index ba1160ec6d6e..945f08de45f1 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -245,47 +245,9 @@ static int sn65dsi83_attach(struct drm_bridge *bridge,  			    enum drm_bridge_attach_flags flags)  {  	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); -	struct device *dev = ctx->dev; -	struct mipi_dsi_device *dsi; -	struct mipi_dsi_host *host; -	int ret = 0; - -	const struct mipi_dsi_device_info info = { -		.type = "sn65dsi83", -		.channel = 0, -		.node = NULL, -	}; - -	host = of_find_mipi_dsi_host_by_node(ctx->host_node); -	if (!host) { -		dev_err(dev, "failed to find dsi host\n"); -		return -EPROBE_DEFER; -	} - -	dsi = mipi_dsi_device_register_full(host, &info); -	if (IS_ERR(dsi)) { -		return dev_err_probe(dev, PTR_ERR(dsi), -				     "failed to create dsi device\n"); -	} - -	ctx->dsi = dsi; - -	dsi->lanes = ctx->dsi_lanes; -	dsi->format = MIPI_DSI_FMT_RGB888; -	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; - -	ret = mipi_dsi_attach(dsi); -	if (ret < 0) { -		dev_err(dev, "failed to attach dsi to host\n"); -		goto err_dsi_attach; -	}  	return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,  				 &ctx->bridge, flags); - -err_dsi_attach: -	mipi_dsi_device_unregister(dsi); -	return ret;  }  static void sn65dsi83_detach(struct drm_bridge *bridge) @@ -295,28 +257,9 @@ static void sn65dsi83_detach(struct drm_bridge *bridge)  	if (!ctx->dsi)  		return; -	mipi_dsi_detach(ctx->dsi); -	mipi_dsi_device_unregister(ctx->dsi); -	drm_bridge_remove(&ctx->bridge);  	ctx->dsi = NULL;  } -static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, -					struct drm_bridge_state *old_bridge_state) -{ -	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); - -	/* -	 * Reset the chip, pull EN line low for t_reset=10ms, -	 * then high for t_en=1ms. -	 */ -	regcache_mark_dirty(ctx->regmap); -	gpiod_set_value(ctx->enable_gpio, 0); -	usleep_range(10000, 11000); -	gpiod_set_value(ctx->enable_gpio, 1); -	usleep_range(1000, 1100); -} -  static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx,  				   const struct drm_display_mode *mode)  { @@ -394,6 +337,10 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,  	u16 val;  	int ret; +	/* Deassert reset */ +	gpiod_set_value(ctx->enable_gpio, 1); +	usleep_range(1000, 1100); +  	/* Get the LVDS format from the bridge state. */  	bridge_state = drm_atomic_get_new_bridge_state(state, bridge); @@ -540,18 +487,11 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,  {  	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); -	/* Clear reset, disable PLL */ -	regmap_write(ctx->regmap, REG_RC_RESET, 0x00); -	regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00); -} - -static void sn65dsi83_atomic_post_disable(struct drm_bridge *bridge, -					  struct drm_bridge_state *old_bridge_state) -{ -	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); - -	/* Put the chip in reset, pull EN line low. */ +	/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */  	gpiod_set_value(ctx->enable_gpio, 0); +	usleep_range(10000, 11000); + +	regcache_mark_dirty(ctx->regmap);  }  static enum drm_mode_status @@ -597,10 +537,8 @@ sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,  static const struct drm_bridge_funcs sn65dsi83_funcs = {  	.attach			= sn65dsi83_attach,  	.detach			= sn65dsi83_detach, -	.atomic_pre_enable	= sn65dsi83_atomic_pre_enable,  	.atomic_enable		= sn65dsi83_atomic_enable,  	.atomic_disable		= sn65dsi83_atomic_disable, -	.atomic_post_disable	= sn65dsi83_atomic_post_disable,  	.mode_valid		= sn65dsi83_mode_valid,  	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, @@ -664,6 +602,44 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)  	return 0;  } +static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) +{ +	struct device *dev = ctx->dev; +	struct mipi_dsi_device *dsi; +	struct mipi_dsi_host *host; +	const struct mipi_dsi_device_info info = { +		.type = "sn65dsi83", +		.channel = 0, +		.node = NULL, +	}; +	int ret; + +	host = of_find_mipi_dsi_host_by_node(ctx->host_node); +	if (!host) { +		dev_err(dev, "failed to find dsi host\n"); +		return -EPROBE_DEFER; +	} + +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info); +	if (IS_ERR(dsi)) +		return dev_err_probe(dev, PTR_ERR(dsi), +				     "failed to create dsi device\n"); + +	ctx->dsi = dsi; + +	dsi->lanes = ctx->dsi_lanes; +	dsi->format = MIPI_DSI_FMT_RGB888; +	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; + +	ret = devm_mipi_dsi_attach(dev, dsi); +	if (ret < 0) { +		dev_err(dev, "failed to attach dsi to host: %d\n", ret); +		return ret; +	} + +	return 0; +} +  static int sn65dsi83_probe(struct i2c_client *client,  			   const struct i2c_device_id *id)  { @@ -685,10 +661,13 @@ static int sn65dsi83_probe(struct i2c_client *client,  		model = id->driver_data;  	} +	/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */  	ctx->enable_gpio = devm_gpiod_get(ctx->dev, "enable", GPIOD_OUT_LOW);  	if (IS_ERR(ctx->enable_gpio))  		return PTR_ERR(ctx->enable_gpio); +	usleep_range(10000, 11000); +  	ret = sn65dsi83_parse_dt(ctx, model);  	if (ret)  		return ret; @@ -704,13 +683,22 @@ static int sn65dsi83_probe(struct i2c_client *client,  	ctx->bridge.of_node = dev->of_node;  	drm_bridge_add(&ctx->bridge); +	ret = sn65dsi83_host_attach(ctx); +	if (ret) +		goto err_remove_bridge; +  	return 0; + +err_remove_bridge: +	drm_bridge_remove(&ctx->bridge); +	return ret;  }  static int sn65dsi83_remove(struct i2c_client *client)  {  	struct sn65dsi83 *ctx = i2c_get_clientdata(client); +	drm_bridge_remove(&ctx->bridge);  	of_node_put(ctx->host_node);  	return 0; diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 6154bed0af5b..dab8f76618f3 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -4,7 +4,9 @@   * datasheet: https://www.ti.com/lit/ds/symlink/sn65dsi86.pdf   */ +#include <linux/atomic.h>  #include <linux/auxiliary_bus.h> +#include <linux/bitfield.h>  #include <linux/bits.h>  #include <linux/clk.h>  #include <linux/debugfs.h> @@ -15,6 +17,7 @@  #include <linux/module.h>  #include <linux/of_graph.h>  #include <linux/pm_runtime.h> +#include <linux/pwm.h>  #include <linux/regmap.h>  #include <linux/regulator/consumer.h> @@ -91,6 +94,13 @@  #define SN_ML_TX_MODE_REG			0x96  #define  ML_TX_MAIN_LINK_OFF			0  #define  ML_TX_NORMAL_MODE			BIT(0) +#define SN_PWM_PRE_DIV_REG			0xA0 +#define SN_BACKLIGHT_SCALE_REG			0xA1 +#define  BACKLIGHT_SCALE_MAX			0xFFFF +#define SN_BACKLIGHT_REG			0xA3 +#define SN_PWM_EN_INV_REG			0xA5 +#define  SN_PWM_INV_MASK			BIT(0) +#define  SN_PWM_EN_MASK				BIT(1)  #define SN_AUX_CMD_STATUS_REG			0xF4  #define  AUX_IRQ_STATUS_AUX_RPLY_TOUT		BIT(3)  #define  AUX_IRQ_STATUS_AUX_SHORT		BIT(5) @@ -113,11 +123,14 @@  #define SN_LINK_TRAINING_TRIES		10 +#define SN_PWM_GPIO_IDX			3 /* 4th GPIO */ +  /**   * struct ti_sn65dsi86 - Platform data for ti-sn65dsi86 driver.   * @bridge_aux:   AUX-bus sub device for MIPI-to-eDP bridge functionality.   * @gpio_aux:     AUX-bus sub device for GPIO controller functionality.   * @aux_aux:      AUX-bus sub device for eDP AUX channel functionality. + * @pwm_aux:      AUX-bus sub device for PWM controller functionality.   *   * @dev:          Pointer to the top level (i2c) device.   * @regmap:       Regmap for accessing i2c. @@ -145,11 +158,17 @@   *                bitmap so we can do atomic ops on it without an extra   *                lock so concurrent users of our 4 GPIOs don't stomp on   *                each other's read-modify-write. + * + * @pchip:        pwm_chip if the PWM is exposed. + * @pwm_enabled:  Used to track if the PWM signal is currently enabled. + * @pwm_pin_busy: Track if GPIO4 is currently requested for GPIO or PWM. + * @pwm_refclk_freq: Cache for the reference clock input to the PWM.   */  struct ti_sn65dsi86 {  	struct auxiliary_device		bridge_aux;  	struct auxiliary_device		gpio_aux;  	struct auxiliary_device		aux_aux; +	struct auxiliary_device		pwm_aux;  	struct device			*dev;  	struct regmap			*regmap; @@ -172,6 +191,12 @@ struct ti_sn65dsi86 {  	struct gpio_chip		gchip;  	DECLARE_BITMAP(gchip_output, SN_NUM_GPIOS);  #endif +#if defined(CONFIG_PWM) +	struct pwm_chip			pchip; +	bool				pwm_enabled; +	atomic_t			pwm_pin_busy; +#endif +	unsigned int			pwm_refclk_freq;  };  static const struct regmap_range ti_sn65dsi86_volatile_ranges[] = { @@ -188,13 +213,30 @@ static const struct regmap_config ti_sn65dsi86_regmap_config = {  	.val_bits = 8,  	.volatile_table = &ti_sn_bridge_volatile_table,  	.cache_type = REGCACHE_NONE, +	.max_register = 0xFF,  }; +static int __maybe_unused ti_sn65dsi86_read_u16(struct ti_sn65dsi86 *pdata, +						unsigned int reg, u16 *val) +{ +	u8 buf[2]; +	int ret; + +	ret = regmap_bulk_read(pdata->regmap, reg, buf, ARRAY_SIZE(buf)); +	if (ret) +		return ret; + +	*val = buf[0] | (buf[1] << 8); + +	return 0; +} +  static void ti_sn65dsi86_write_u16(struct ti_sn65dsi86 *pdata,  				   unsigned int reg, u16 val)  { -	regmap_write(pdata->regmap, reg, val & 0xFF); -	regmap_write(pdata->regmap, reg + 1, val >> 8); +	u8 buf[2] = { val & 0xff, val >> 8 }; + +	regmap_bulk_write(pdata->regmap, reg, buf, ARRAY_SIZE(buf));  }  static u32 ti_sn_bridge_get_dsi_freq(struct ti_sn65dsi86 *pdata) @@ -253,6 +295,12 @@ static void ti_sn_bridge_set_refclk_freq(struct ti_sn65dsi86 *pdata)  	regmap_update_bits(pdata->regmap, SN_DPPLL_SRC_REG, REFCLK_FREQ_MASK,  			   REFCLK_FREQ(i)); + +	/* +	 * The PWM refclk is based on the value written to SN_DPPLL_SRC_REG, +	 * regardless of its actual sourcing. +	 */ +	pdata->pwm_refclk_freq = ti_sn_bridge_refclk_lut[i];  }  static void ti_sn65dsi86_enable_comms(struct ti_sn65dsi86 *pdata) @@ -655,58 +703,24 @@ static struct ti_sn65dsi86 *bridge_to_ti_sn65dsi86(struct drm_bridge *bridge)  	return container_of(bridge, struct ti_sn65dsi86, bridge);  } -static int ti_sn_bridge_attach(struct drm_bridge *bridge, -			       enum drm_bridge_attach_flags flags) +static int ti_sn_attach_host(struct ti_sn65dsi86 *pdata)  { -	int ret, val; -	struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); +	int val;  	struct mipi_dsi_host *host;  	struct mipi_dsi_device *dsi; +	struct device *dev = pdata->dev;  	const struct mipi_dsi_device_info info = { .type = "ti_sn_bridge",  						   .channel = 0,  						   .node = NULL, -						 }; - -	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { -		DRM_ERROR("Fix bridge driver to make connector optional!"); -		return -EINVAL; -	} - -	pdata->aux.drm_dev = bridge->dev; -	ret = drm_dp_aux_register(&pdata->aux); -	if (ret < 0) { -		drm_err(bridge->dev, "Failed to register DP AUX channel: %d\n", ret); -		return ret; -	} - -	ret = ti_sn_bridge_connector_init(pdata); -	if (ret < 0) -		goto err_conn_init; +	}; -	/* -	 * TODO: ideally finding host resource and dsi dev registration needs -	 * to be done in bridge probe. But some existing DSI host drivers will -	 * wait for any of the drm_bridge/drm_panel to get added to the global -	 * bridge/panel list, before completing their probe. So if we do the -	 * dsi dev registration part in bridge probe, before populating in -	 * the global bridge list, then it will cause deadlock as dsi host probe -	 * will never complete, neither our bridge probe. So keeping it here -	 * will satisfy most of the existing host drivers. Once the host driver -	 * is fixed we can move the below code to bridge probe safely. -	 */  	host = of_find_mipi_dsi_host_by_node(pdata->host_node); -	if (!host) { -		DRM_ERROR("failed to find dsi host\n"); -		ret = -ENODEV; -		goto err_dsi_host; -	} +	if (!host) +		return -EPROBE_DEFER; -	dsi = mipi_dsi_device_register_full(host, &info); -	if (IS_ERR(dsi)) { -		DRM_ERROR("failed to create dsi device\n"); -		ret = PTR_ERR(dsi); -		goto err_dsi_host; -	} +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info); +	if (IS_ERR(dsi)) +		return PTR_ERR(dsi);  	/* TODO: setting to 4 MIPI lanes always for now */  	dsi->lanes = 4; @@ -714,18 +728,38 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO;  	/* check if continuous dsi clock is required or not */ -	pm_runtime_get_sync(pdata->dev); +	pm_runtime_get_sync(dev);  	regmap_read(pdata->regmap, SN_DPPLL_SRC_REG, &val); -	pm_runtime_put_autosuspend(pdata->dev); +	pm_runtime_put_autosuspend(dev);  	if (!(val & DPPLL_CLK_SRC_DSICLK))  		dsi->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS; -	ret = mipi_dsi_attach(dsi); +	pdata->dsi = dsi; + +	return devm_mipi_dsi_attach(dev, dsi); +} + +static int ti_sn_bridge_attach(struct drm_bridge *bridge, +			       enum drm_bridge_attach_flags flags) +{ +	struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); +	int ret; + +	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { +		DRM_ERROR("Fix bridge driver to make connector optional!"); +		return -EINVAL; +	} + +	pdata->aux.drm_dev = bridge->dev; +	ret = drm_dp_aux_register(&pdata->aux);  	if (ret < 0) { -		DRM_ERROR("failed to attach dsi to host\n"); -		goto err_dsi_attach; +		drm_err(bridge->dev, "Failed to register DP AUX channel: %d\n", ret); +		return ret;  	} -	pdata->dsi = dsi; + +	ret = ti_sn_bridge_connector_init(pdata); +	if (ret < 0) +		goto err_conn_init;  	/* We never want the next bridge to *also* create a connector: */  	flags |= DRM_BRIDGE_ATTACH_NO_CONNECTOR; @@ -734,14 +768,10 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,  	ret = drm_bridge_attach(bridge->encoder, pdata->next_bridge,  				&pdata->bridge, flags);  	if (ret < 0) -		goto err_dsi_detach; +		goto err_dsi_host;  	return 0; -err_dsi_detach: -	mipi_dsi_detach(dsi); -err_dsi_attach: -	mipi_dsi_device_unregister(dsi);  err_dsi_host:  	drm_connector_cleanup(&pdata->connector);  err_conn_init: @@ -1227,7 +1257,17 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev,  	drm_bridge_add(&pdata->bridge); +	ret = ti_sn_attach_host(pdata); +	if (ret) { +		dev_err_probe(pdata->dev, ret, "failed to attach dsi host\n"); +		goto err_remove_bridge; +	} +  	return 0; + +err_remove_bridge: +	drm_bridge_remove(&pdata->bridge); +	return ret;  }  static void ti_sn_bridge_remove(struct auxiliary_device *adev) @@ -1237,11 +1277,6 @@ static void ti_sn_bridge_remove(struct auxiliary_device *adev)  	if (!pdata)  		return; -	if (pdata->dsi) { -		mipi_dsi_detach(pdata->dsi); -		mipi_dsi_device_unregister(pdata->dsi); -	} -  	drm_bridge_remove(&pdata->bridge);  	of_node_put(pdata->host_node); @@ -1260,9 +1295,287 @@ static struct auxiliary_driver ti_sn_bridge_driver = {  };  /* ----------------------------------------------------------------------------- - * GPIO Controller + * PWM Controller   */ +#if defined(CONFIG_PWM) +static int ti_sn_pwm_pin_request(struct ti_sn65dsi86 *pdata) +{ +	return atomic_xchg(&pdata->pwm_pin_busy, 1) ? -EBUSY : 0; +} + +static void ti_sn_pwm_pin_release(struct ti_sn65dsi86 *pdata) +{ +	atomic_set(&pdata->pwm_pin_busy, 0); +} + +static struct ti_sn65dsi86 *pwm_chip_to_ti_sn_bridge(struct pwm_chip *chip) +{ +	return container_of(chip, struct ti_sn65dsi86, pchip); +} + +static int ti_sn_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ +	struct ti_sn65dsi86 *pdata = pwm_chip_to_ti_sn_bridge(chip); + +	return ti_sn_pwm_pin_request(pdata); +} + +static void ti_sn_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ +	struct ti_sn65dsi86 *pdata = pwm_chip_to_ti_sn_bridge(chip); + +	ti_sn_pwm_pin_release(pdata); +} + +/* + * Limitations: + * - The PWM signal is not driven when the chip is powered down, or in its + *   reset state and the driver does not implement the "suspend state" + *   described in the documentation. In order to save power, state->enabled is + *   interpreted as denoting if the signal is expected to be valid, and is used + *   to determine if the chip needs to be kept powered. + * - Changing both period and duty_cycle is not done atomically, neither is the + *   multi-byte register updates, so the output might briefly be undefined + *   during update. + */ +static int ti_sn_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +			   const struct pwm_state *state) +{ +	struct ti_sn65dsi86 *pdata = pwm_chip_to_ti_sn_bridge(chip); +	unsigned int pwm_en_inv; +	unsigned int backlight; +	unsigned int pre_div; +	unsigned int scale; +	u64 period_max; +	u64 period; +	int ret; + +	if (!pdata->pwm_enabled) { +		ret = pm_runtime_get_sync(pdata->dev); +		if (ret < 0) { +			pm_runtime_put_sync(pdata->dev); +			return ret; +		} +	} + +	if (state->enabled) { +		if (!pdata->pwm_enabled) { +			/* +			 * The chip might have been powered down while we +			 * didn't hold a PM runtime reference, so mux in the +			 * PWM function on the GPIO pin again. +			 */ +			ret = regmap_update_bits(pdata->regmap, SN_GPIO_CTRL_REG, +						 SN_GPIO_MUX_MASK << (2 * SN_PWM_GPIO_IDX), +						 SN_GPIO_MUX_SPECIAL << (2 * SN_PWM_GPIO_IDX)); +			if (ret) { +				dev_err(pdata->dev, "failed to mux in PWM function\n"); +				goto out; +			} +		} + +		/* +		 * Per the datasheet the PWM frequency is given by: +		 * +		 *                          REFCLK_FREQ +		 *   PWM_FREQ = ----------------------------------- +		 *               PWM_PRE_DIV * BACKLIGHT_SCALE + 1 +		 * +		 * However, after careful review the author is convinced that +		 * the documentation has lost some parenthesis around +		 * "BACKLIGHT_SCALE + 1". +		 * +		 * With the period T_pwm = 1/PWM_FREQ this can be written: +		 * +		 *   T_pwm * REFCLK_FREQ = PWM_PRE_DIV * (BACKLIGHT_SCALE + 1) +		 * +		 * In order to keep BACKLIGHT_SCALE within its 16 bits, +		 * PWM_PRE_DIV must be: +		 * +		 *                     T_pwm * REFCLK_FREQ +		 *   PWM_PRE_DIV >= ------------------------- +		 *                   BACKLIGHT_SCALE_MAX + 1 +		 * +		 * To simplify the search and to favour higher resolution of +		 * the duty cycle over accuracy of the period, the lowest +		 * possible PWM_PRE_DIV is used. Finally the scale is +		 * calculated as: +		 * +		 *                      T_pwm * REFCLK_FREQ +		 *   BACKLIGHT_SCALE = ---------------------- - 1 +		 *                          PWM_PRE_DIV +		 * +		 * Here T_pwm is represented in seconds, so appropriate scaling +		 * to nanoseconds is necessary. +		 */ + +		/* Minimum T_pwm is 1 / REFCLK_FREQ */ +		if (state->period <= NSEC_PER_SEC / pdata->pwm_refclk_freq) { +			ret = -EINVAL; +			goto out; +		} + +		/* +		 * Maximum T_pwm is 255 * (65535 + 1) / REFCLK_FREQ +		 * Limit period to this to avoid overflows +		 */ +		period_max = div_u64((u64)NSEC_PER_SEC * 255 * (65535 + 1), +				     pdata->pwm_refclk_freq); +		period = min(state->period, period_max); + +		pre_div = DIV64_U64_ROUND_UP(period * pdata->pwm_refclk_freq, +					     (u64)NSEC_PER_SEC * (BACKLIGHT_SCALE_MAX + 1)); +		scale = div64_u64(period * pdata->pwm_refclk_freq, (u64)NSEC_PER_SEC * pre_div) - 1; + +		/* +		 * The documentation has the duty ratio given as: +		 * +		 *     duty          BACKLIGHT +		 *   ------- = --------------------- +		 *    period    BACKLIGHT_SCALE + 1 +		 * +		 * Solve for BACKLIGHT, substituting BACKLIGHT_SCALE according +		 * to definition above and adjusting for nanosecond +		 * representation of duty cycle gives us: +		 */ +		backlight = div64_u64(state->duty_cycle * pdata->pwm_refclk_freq, +				      (u64)NSEC_PER_SEC * pre_div); +		if (backlight > scale) +			backlight = scale; + +		ret = regmap_write(pdata->regmap, SN_PWM_PRE_DIV_REG, pre_div); +		if (ret) { +			dev_err(pdata->dev, "failed to update PWM_PRE_DIV\n"); +			goto out; +		} + +		ti_sn65dsi86_write_u16(pdata, SN_BACKLIGHT_SCALE_REG, scale); +		ti_sn65dsi86_write_u16(pdata, SN_BACKLIGHT_REG, backlight); +	} + +	pwm_en_inv = FIELD_PREP(SN_PWM_EN_MASK, state->enabled) | +		     FIELD_PREP(SN_PWM_INV_MASK, state->polarity == PWM_POLARITY_INVERSED); +	ret = regmap_write(pdata->regmap, SN_PWM_EN_INV_REG, pwm_en_inv); +	if (ret) { +		dev_err(pdata->dev, "failed to update PWM_EN/PWM_INV\n"); +		goto out; +	} + +	pdata->pwm_enabled = state->enabled; +out: + +	if (!pdata->pwm_enabled) +		pm_runtime_put_sync(pdata->dev); + +	return ret; +} + +static void ti_sn_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, +				struct pwm_state *state) +{ +	struct ti_sn65dsi86 *pdata = pwm_chip_to_ti_sn_bridge(chip); +	unsigned int pwm_en_inv; +	unsigned int pre_div; +	u16 backlight; +	u16 scale; +	int ret; + +	ret = regmap_read(pdata->regmap, SN_PWM_EN_INV_REG, &pwm_en_inv); +	if (ret) +		return; + +	ret = ti_sn65dsi86_read_u16(pdata, SN_BACKLIGHT_SCALE_REG, &scale); +	if (ret) +		return; + +	ret = ti_sn65dsi86_read_u16(pdata, SN_BACKLIGHT_REG, &backlight); +	if (ret) +		return; + +	ret = regmap_read(pdata->regmap, SN_PWM_PRE_DIV_REG, &pre_div); +	if (ret) +		return; + +	state->enabled = FIELD_GET(SN_PWM_EN_MASK, pwm_en_inv); +	if (FIELD_GET(SN_PWM_INV_MASK, pwm_en_inv)) +		state->polarity = PWM_POLARITY_INVERSED; +	else +		state->polarity = PWM_POLARITY_NORMAL; + +	state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pre_div * (scale + 1), +					 pdata->pwm_refclk_freq); +	state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pre_div * backlight, +					     pdata->pwm_refclk_freq); + +	if (state->duty_cycle > state->period) +		state->duty_cycle = state->period; +} +static const struct pwm_ops ti_sn_pwm_ops = { +	.request = ti_sn_pwm_request, +	.free = ti_sn_pwm_free, +	.apply = ti_sn_pwm_apply, +	.get_state = ti_sn_pwm_get_state, +	.owner = THIS_MODULE, +}; + +static int ti_sn_pwm_probe(struct auxiliary_device *adev, +			   const struct auxiliary_device_id *id) +{ +	struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent); + +	pdata->pchip.dev = pdata->dev; +	pdata->pchip.ops = &ti_sn_pwm_ops; +	pdata->pchip.npwm = 1; +	pdata->pchip.of_xlate = of_pwm_single_xlate; +	pdata->pchip.of_pwm_n_cells = 1; + +	return pwmchip_add(&pdata->pchip); +} + +static void ti_sn_pwm_remove(struct auxiliary_device *adev) +{ +	struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent); + +	pwmchip_remove(&pdata->pchip); + +	if (pdata->pwm_enabled) +		pm_runtime_put_sync(pdata->dev); +} + +static const struct auxiliary_device_id ti_sn_pwm_id_table[] = { +	{ .name = "ti_sn65dsi86.pwm", }, +	{}, +}; + +static struct auxiliary_driver ti_sn_pwm_driver = { +	.name = "pwm", +	.probe = ti_sn_pwm_probe, +	.remove = ti_sn_pwm_remove, +	.id_table = ti_sn_pwm_id_table, +}; + +static int __init ti_sn_pwm_register(void) +{ +	return auxiliary_driver_register(&ti_sn_pwm_driver); +} + +static void ti_sn_pwm_unregister(void) +{ +	auxiliary_driver_unregister(&ti_sn_pwm_driver); +} + +#else +static inline int ti_sn_pwm_pin_request(struct ti_sn65dsi86 *pdata) { return 0; } +static inline void ti_sn_pwm_pin_release(struct ti_sn65dsi86 *pdata) {} + +static inline int ti_sn_pwm_register(void) { return 0; } +static inline void ti_sn_pwm_unregister(void) {} +#endif + +/* ----------------------------------------------------------------------------- + * GPIO Controller + */  #if defined(CONFIG_OF_GPIO)  static int tn_sn_bridge_of_xlate(struct gpio_chip *chip, @@ -1395,10 +1708,25 @@ static int ti_sn_bridge_gpio_direction_output(struct gpio_chip *chip,  	return ret;  } +static int ti_sn_bridge_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ +	struct ti_sn65dsi86 *pdata = gpiochip_get_data(chip); + +	if (offset == SN_PWM_GPIO_IDX) +		return ti_sn_pwm_pin_request(pdata); + +	return 0; +} +  static void ti_sn_bridge_gpio_free(struct gpio_chip *chip, unsigned int offset)  { +	struct ti_sn65dsi86 *pdata = gpiochip_get_data(chip); +  	/* We won't keep pm_runtime if we're input, so switch there on free */  	ti_sn_bridge_gpio_direction_input(chip, offset); + +	if (offset == SN_PWM_GPIO_IDX) +		ti_sn_pwm_pin_release(pdata);  }  static const char * const ti_sn_bridge_gpio_names[SN_NUM_GPIOS] = { @@ -1420,6 +1748,7 @@ static int ti_sn_gpio_probe(struct auxiliary_device *adev,  	pdata->gchip.owner = THIS_MODULE;  	pdata->gchip.of_xlate = tn_sn_bridge_of_xlate;  	pdata->gchip.of_gpio_n_cells = 2; +	pdata->gchip.request = ti_sn_bridge_gpio_request;  	pdata->gchip.free = ti_sn_bridge_gpio_free;  	pdata->gchip.get_direction = ti_sn_bridge_gpio_get_direction;  	pdata->gchip.direction_input = ti_sn_bridge_gpio_direction_input; @@ -1546,10 +1875,9 @@ static int ti_sn65dsi86_probe(struct i2c_client *client,  	 * ordering. The bridge wants the panel to be there when it probes.  	 * The panel wants its HPD GPIO (provided by sn65dsi86 on some boards)  	 * when it probes. The panel and maybe backlight might want the DDC -	 * bus. Soon the PWM provided by the bridge chip will have the same -	 * problem. Having sub-devices allows the some sub devices to finish -	 * probing even if others return -EPROBE_DEFER and gets us around the -	 * problems. +	 * bus or the pwm_chip. Having sub-devices allows the some sub devices +	 * to finish probing even if others return -EPROBE_DEFER and gets us +	 * around the problems.  	 */  	if (IS_ENABLED(CONFIG_OF_GPIO)) { @@ -1558,6 +1886,12 @@ static int ti_sn65dsi86_probe(struct i2c_client *client,  			return ret;  	} +	if (IS_ENABLED(CONFIG_PWM)) { +		ret = ti_sn65dsi86_add_aux_device(pdata, &pdata->pwm_aux, "pwm"); +		if (ret) +			return ret; +	} +  	/*  	 * NOTE: At the end of the AUX channel probe we'll add the aux device  	 * for the bridge. This is because the bridge can't be used until the @@ -1601,10 +1935,14 @@ static int __init ti_sn65dsi86_init(void)  	if (ret)  		goto err_main_was_registered; -	ret = auxiliary_driver_register(&ti_sn_aux_driver); +	ret = ti_sn_pwm_register();  	if (ret)  		goto err_gpio_was_registered; +	ret = auxiliary_driver_register(&ti_sn_aux_driver); +	if (ret) +		goto err_pwm_was_registered; +  	ret = auxiliary_driver_register(&ti_sn_bridge_driver);  	if (ret)  		goto err_aux_was_registered; @@ -1613,6 +1951,8 @@ static int __init ti_sn65dsi86_init(void)  err_aux_was_registered:  	auxiliary_driver_unregister(&ti_sn_aux_driver); +err_pwm_was_registered: +	ti_sn_pwm_unregister();  err_gpio_was_registered:  	ti_sn_gpio_unregister();  err_main_was_registered: @@ -1626,6 +1966,7 @@ static void __exit ti_sn65dsi86_exit(void)  {  	auxiliary_driver_unregister(&ti_sn_bridge_driver);  	auxiliary_driver_unregister(&ti_sn_aux_driver); +	ti_sn_pwm_unregister();  	ti_sn_gpio_unregister();  	i2c_del_driver(&ti_sn65dsi86_driver);  } |