diff options
Diffstat (limited to 'drivers/gpu/drm')
127 files changed, 5860 insertions, 719 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b62f40cc9691..6b34949416b1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -350,6 +350,8 @@ source "drivers/gpu/drm/panfrost/Kconfig"  source "drivers/gpu/drm/aspeed/Kconfig" +source "drivers/gpu/drm/mcde/Kconfig" +  # Keep legacy drivers last  menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index af37253a8ec4..d36feb4a6233 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,7 @@ drm-y       :=	drm_auth.o drm_cache.o \  		drm_plane.o drm_color_mgmt.o drm_print.o \  		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \  		drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ -		drm_atomic_uapi.o drm_hdcp.o +		drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o  drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o  drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o @@ -118,3 +118,4 @@ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/  obj-$(CONFIG_DRM_LIMA)  += lima/  obj-$(CONFIG_DRM_PANFROST) += panfrost/  obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ +obj-$(CONFIG_DRM_MCDE) += mcde/ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b5a5cfae7ba1..86308d7b5b3f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3886,6 +3886,128 @@ fail:  	return result;  } +static int fill_hdr_info_packet(const struct drm_connector_state *state, +				struct dc_info_packet *out) +{ +	struct hdmi_drm_infoframe frame; +	unsigned char buf[30]; /* 26 + 4 */ +	ssize_t len; +	int ret, i; + +	memset(out, 0, sizeof(*out)); + +	if (!state->hdr_output_metadata) +		return 0; + +	ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state); +	if (ret) +		return ret; + +	len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf)); +	if (len < 0) +		return (int)len; + +	/* Static metadata is a fixed 26 bytes + 4 byte header. */ +	if (len != 30) +		return -EINVAL; + +	/* Prepare the infopacket for DC. */ +	switch (state->connector->connector_type) { +	case DRM_MODE_CONNECTOR_HDMIA: +		out->hb0 = 0x87; /* type */ +		out->hb1 = 0x01; /* version */ +		out->hb2 = 0x1A; /* length */ +		out->sb[0] = buf[3]; /* checksum */ +		i = 1; +		break; + +	case DRM_MODE_CONNECTOR_DisplayPort: +	case DRM_MODE_CONNECTOR_eDP: +		out->hb0 = 0x00; /* sdp id, zero */ +		out->hb1 = 0x87; /* type */ +		out->hb2 = 0x1D; /* payload len - 1 */ +		out->hb3 = (0x13 << 2); /* sdp version */ +		out->sb[0] = 0x01; /* version */ +		out->sb[1] = 0x1A; /* length */ +		i = 2; +		break; + +	default: +		return -EINVAL; +	} + +	memcpy(&out->sb[i], &buf[4], 26); +	out->valid = true; + +	print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb, +		       sizeof(out->sb), false); + +	return 0; +} + +static bool +is_hdr_metadata_different(const struct drm_connector_state *old_state, +			  const struct drm_connector_state *new_state) +{ +	struct drm_property_blob *old_blob = old_state->hdr_output_metadata; +	struct drm_property_blob *new_blob = new_state->hdr_output_metadata; + +	if (old_blob != new_blob) { +		if (old_blob && new_blob && +		    old_blob->length == new_blob->length) +			return memcmp(old_blob->data, new_blob->data, +				      old_blob->length); + +		return true; +	} + +	return false; +} + +static int +amdgpu_dm_connector_atomic_check(struct drm_connector *conn, +				 struct drm_connector_state *new_con_state) +{ +	struct drm_atomic_state *state = new_con_state->state; +	struct drm_connector_state *old_con_state = +		drm_atomic_get_old_connector_state(state, conn); +	struct drm_crtc *crtc = new_con_state->crtc; +	struct drm_crtc_state *new_crtc_state; +	int ret; + +	if (!crtc) +		return 0; + +	if (is_hdr_metadata_different(old_con_state, new_con_state)) { +		struct dc_info_packet hdr_infopacket; + +		ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket); +		if (ret) +			return ret; + +		new_crtc_state = drm_atomic_get_crtc_state(state, crtc); +		if (IS_ERR(new_crtc_state)) +			return PTR_ERR(new_crtc_state); + +		/* +		 * DC considers the stream backends changed if the +		 * static metadata changes. Forcing the modeset also +		 * gives a simple way for userspace to switch from +		 * 8bpc to 10bpc when setting the metadata to enter +		 * or exit HDR. +		 * +		 * Changing the static metadata after it's been +		 * set is permissible, however. So only force a +		 * modeset if we're entering or exiting HDR. +		 */ +		new_crtc_state->mode_changed = +			!old_con_state->hdr_output_metadata || +			!new_con_state->hdr_output_metadata; +	} + +	return 0; +} +  static const struct drm_connector_helper_funcs  amdgpu_dm_connector_helper_funcs = {  	/* @@ -3896,6 +4018,7 @@ amdgpu_dm_connector_helper_funcs = {  	 */  	.get_modes = get_modes,  	.mode_valid = amdgpu_dm_connector_mode_valid, +	.atomic_check = amdgpu_dm_connector_atomic_check,  };  static void dm_crtc_helper_disable(struct drm_crtc *crtc) @@ -4715,6 +4838,10 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,  	if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||  	    connector_type == DRM_MODE_CONNECTOR_DisplayPort ||  	    connector_type == DRM_MODE_CONNECTOR_eDP) { +		drm_object_attach_property( +			&aconnector->base.base, +			dm->ddev->mode_config.hdr_output_metadata_property, 0); +  		drm_connector_attach_vrr_capable_property(  			&aconnector->base);  	} @@ -5817,7 +5944,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)  		struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);  		struct dc_surface_update dummy_updates[MAX_SURFACES];  		struct dc_stream_update stream_update; +		struct dc_info_packet hdr_packet;  		struct dc_stream_status *status = NULL; +		bool abm_changed, hdr_changed, scaling_changed;  		memset(&dummy_updates, 0, sizeof(dummy_updates));  		memset(&stream_update, 0, sizeof(stream_update)); @@ -5834,11 +5963,19 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)  		dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);  		dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); -		if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state) && -				(dm_new_crtc_state->abm_level == dm_old_crtc_state->abm_level)) +		scaling_changed = is_scaling_state_different(dm_new_con_state, +							     dm_old_con_state); + +		abm_changed = dm_new_crtc_state->abm_level != +			      dm_old_crtc_state->abm_level; + +		hdr_changed = +			is_hdr_metadata_different(old_con_state, new_con_state); + +		if (!scaling_changed && !abm_changed && !hdr_changed)  			continue; -		if (is_scaling_state_different(dm_new_con_state, dm_old_con_state)) { +		if (scaling_changed) {  			update_stream_scaling_settings(&dm_new_con_state->base.crtc->mode,  					dm_new_con_state, (struct dc_stream_state *)dm_new_crtc_state->stream); @@ -5846,12 +5983,17 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)  			stream_update.dst = dm_new_crtc_state->stream->dst;  		} -		if (dm_new_crtc_state->abm_level != dm_old_crtc_state->abm_level) { +		if (abm_changed) {  			dm_new_crtc_state->stream->abm_level = dm_new_crtc_state->abm_level;  			stream_update.abm_level = &dm_new_crtc_state->abm_level;  		} +		if (hdr_changed) { +			fill_hdr_info_packet(new_con_state, &hdr_packet); +			stream_update.hdr_static_metadata = &hdr_packet; +		} +  		status = dc_stream_get_status(dm_new_crtc_state->stream);  		WARN_ON(!status);  		WARN_ON(!status->plane_count); @@ -6197,6 +6339,11 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,  		dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level; +		ret = fill_hdr_info_packet(drm_new_conn_state, +					   &new_stream->hdr_static_metadata); +		if (ret) +			goto fail; +  		if (dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&  		    dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) {  			new_crtc_state->mode_changed = false; diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c index 2362f07fe1fc..2a413e291a60 100644 --- a/drivers/gpu/drm/ati_pcigart.c +++ b/drivers/gpu/drm/ati_pcigart.c @@ -32,9 +32,12 @@   */  #include <linux/export.h> -#include <drm/drmP.h>  #include <drm/ati_pcigart.h> +#include <drm/drm_device.h> +#include <drm/drm_os_linux.h> +#include <drm/drm_pci.h> +#include <drm/drm_print.h>  # define ATI_PCIGART_PAGE_SIZE		4096	/**< PCI GART page size */ diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 8070a558d7b1..81c50772df05 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -78,7 +78,8 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)  	unsigned long mode_rate;  	struct videomode vm;  	unsigned long prate; -	unsigned int cfg; +	unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; +	unsigned int cfg = 0;  	int div;  	vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; @@ -101,7 +102,10 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)  		     (adj->crtc_hdisplay - 1) |  		     ((adj->crtc_vdisplay - 1) << 16)); -	cfg = ATMEL_HLCDC_CLKSEL; +	if (!crtc->dc->desc->fixed_clksrc) { +		cfg |= ATMEL_HLCDC_CLKSEL; +		mask |= ATMEL_HLCDC_CLKSEL; +	}  	prate = 2 * clk_get_rate(crtc->dc->hlcdc->sys_clk);  	mode_rate = adj->crtc_clock * 1000; @@ -132,11 +136,10 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)  	cfg |= ATMEL_HLCDC_CLKDIV(div); -	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), -			   ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | -			   ATMEL_HLCDC_CLKPOL, cfg); +	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); -	cfg = 0; +	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); +	cfg = state->output_mode << 8;  	if (adj->flags & DRM_MODE_FLAG_NVSYNC)  		cfg |= ATMEL_HLCDC_VSPOL; @@ -144,9 +147,6 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)  	if (adj->flags & DRM_MODE_FLAG_NHSYNC)  		cfg |= ATMEL_HLCDC_HSPOL; -	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); -	cfg |= state->output_mode << 8; -  	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),  			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |  			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 0be13eceedba..fb2e7646daeb 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -364,6 +364,103 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = {  	.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers),  	.layers = atmel_hlcdc_sama5d4_layers,  }; + +static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sam9x60_layers[] = { +	{ +		.name = "base", +		.formats = &atmel_hlcdc_plane_rgb_formats, +		.regs_offset = 0x60, +		.id = 0, +		.type = ATMEL_HLCDC_BASE_LAYER, +		.cfgs_offset = 0x2c, +		.layout = { +			.xstride = { 2 }, +			.default_color = 3, +			.general_config = 4, +			.disc_pos = 5, +			.disc_size = 6, +		}, +		.clut_offset = 0x600, +	}, +	{ +		.name = "overlay1", +		.formats = &atmel_hlcdc_plane_rgb_formats, +		.regs_offset = 0x160, +		.id = 1, +		.type = ATMEL_HLCDC_OVERLAY_LAYER, +		.cfgs_offset = 0x2c, +		.layout = { +			.pos = 2, +			.size = 3, +			.xstride = { 4 }, +			.pstride = { 5 }, +			.default_color = 6, +			.chroma_key = 7, +			.chroma_key_mask = 8, +			.general_config = 9, +		}, +		.clut_offset = 0xa00, +	}, +	{ +		.name = "overlay2", +		.formats = &atmel_hlcdc_plane_rgb_formats, +		.regs_offset = 0x260, +		.id = 2, +		.type = ATMEL_HLCDC_OVERLAY_LAYER, +		.cfgs_offset = 0x2c, +		.layout = { +			.pos = 2, +			.size = 3, +			.xstride = { 4 }, +			.pstride = { 5 }, +			.default_color = 6, +			.chroma_key = 7, +			.chroma_key_mask = 8, +			.general_config = 9, +		}, +		.clut_offset = 0xe00, +	}, +	{ +		.name = "high-end-overlay", +		.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, +		.regs_offset = 0x360, +		.id = 3, +		.type = ATMEL_HLCDC_OVERLAY_LAYER, +		.cfgs_offset = 0x4c, +		.layout = { +			.pos = 2, +			.size = 3, +			.memsize = 4, +			.xstride = { 5, 7 }, +			.pstride = { 6, 8 }, +			.default_color = 9, +			.chroma_key = 10, +			.chroma_key_mask = 11, +			.general_config = 12, +			.scaler_config = 13, +			.phicoeffs = { +				.x = 17, +				.y = 33, +			}, +			.csc = 14, +		}, +		.clut_offset = 0x1200, +	}, +}; + +static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sam9x60 = { +	.min_width = 0, +	.min_height = 0, +	.max_width = 2048, +	.max_height = 2048, +	.max_spw = 0xff, +	.max_vpw = 0xff, +	.max_hpw = 0x3ff, +	.fixed_clksrc = true, +	.nlayers = ARRAY_SIZE(atmel_hlcdc_sam9x60_layers), +	.layers = atmel_hlcdc_sam9x60_layers, +}; +  static const struct of_device_id atmel_hlcdc_of_match[] = {  	{  		.compatible = "atmel,at91sam9n12-hlcdc", @@ -385,6 +482,10 @@ static const struct of_device_id atmel_hlcdc_of_match[] = {  		.compatible = "atmel,sama5d4-hlcdc",  		.data = &atmel_hlcdc_dc_sama5d4,  	}, +	{ +		.compatible = "microchip,sam9x60-hlcdc", +		.data = &atmel_hlcdc_dc_sam9x60, +	},  	{ /* sentinel */ },  };  MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); @@ -625,10 +726,18 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)  	dc->hlcdc = dev_get_drvdata(dev->dev->parent);  	dev->dev_private = dc; +	if (dc->desc->fixed_clksrc) { +		ret = clk_prepare_enable(dc->hlcdc->sys_clk); +		if (ret) { +			dev_err(dev->dev, "failed to enable sys_clk\n"); +			goto err_destroy_wq; +		} +	} +  	ret = clk_prepare_enable(dc->hlcdc->periph_clk);  	if (ret) {  		dev_err(dev->dev, "failed to enable periph_clk\n"); -		goto err_destroy_wq; +		goto err_sys_clk_disable;  	}  	pm_runtime_enable(dev->dev); @@ -664,6 +773,9 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)  err_periph_clk_disable:  	pm_runtime_disable(dev->dev);  	clk_disable_unprepare(dc->hlcdc->periph_clk); +err_sys_clk_disable: +	if (dc->desc->fixed_clksrc) +		clk_disable_unprepare(dc->hlcdc->sys_clk);  err_destroy_wq:  	destroy_workqueue(dc->wq); @@ -688,6 +800,8 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)  	pm_runtime_disable(dev->dev);  	clk_disable_unprepare(dc->hlcdc->periph_clk); +	if (dc->desc->fixed_clksrc) +		clk_disable_unprepare(dc->hlcdc->sys_clk);  	destroy_workqueue(dc->wq);  } @@ -805,6 +919,8 @@ static int atmel_hlcdc_dc_drm_suspend(struct device *dev)  	regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr);  	regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr);  	clk_disable_unprepare(dc->hlcdc->periph_clk); +	if (dc->desc->fixed_clksrc) +		clk_disable_unprepare(dc->hlcdc->sys_clk);  	return 0;  } @@ -814,6 +930,8 @@ static int atmel_hlcdc_dc_drm_resume(struct device *dev)  	struct drm_device *drm_dev = dev_get_drvdata(dev);  	struct atmel_hlcdc_dc *dc = drm_dev->dev_private; +	if (dc->desc->fixed_clksrc) +		clk_prepare_enable(dc->hlcdc->sys_clk);  	clk_prepare_enable(dc->hlcdc->periph_clk);  	regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 70bd540d644e..0155efb9c443 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -328,6 +328,7 @@ atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *layer)   * @max_hpw: maximum horizontal back/front porch width   * @conflicting_output_formats: true if RGBXXX output formats conflict with   *				each other. + * @fixed_clksrc: true if clock source is fixed   * @layers: a layer description table describing available layers   * @nlayers: layer description table size   */ @@ -340,6 +341,7 @@ struct atmel_hlcdc_dc_desc {  	int max_vpw;  	int max_hpw;  	bool conflicting_output_formats; +	bool fixed_clksrc;  	const struct atmel_hlcdc_layer_desc *layers;  	int nlayers;  }; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index fdd607ad27fe..0ee5b7a3a4b0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -382,7 +382,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,  			cfg |= ATMEL_HLCDC_LAYER_LAEN;  		else  			cfg |= ATMEL_HLCDC_LAYER_GAEN | -			       ATMEL_HLCDC_LAYER_GA(state->base.alpha >> 8); +			       ATMEL_HLCDC_LAYER_GA(state->base.alpha);  	}  	if (state->disc_h && state->disc_w) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index c532e9c9e491..562420d9d8d5 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -6,21 +6,21 @@   * Licensed under the GPL-2.   */ +#include <linux/clk.h>  #include <linux/device.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of_device.h>  #include <linux/slab.h> -#include <linux/clk.h> -#include <drm/drmP.h> +#include <media/cec.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_edid.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <media/cec.h> -  #include "adv7511.h"  /* ADI recommended values for proper operation. */ diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index c09aaf93ae1b..479ce3642a79 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -16,23 +16,23 @@   */  #include <linux/delay.h>  #include <linux/err.h> -#include <linux/interrupt.h> +#include <linux/gpio/consumer.h>  #include <linux/i2c.h> +#include <linux/interrupt.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/of_gpio.h>  #include <linux/of_irq.h>  #include <linux/of_platform.h>  #include <linux/regmap.h> -#include <linux/types.h> -#include <linux/gpio/consumer.h>  #include <linux/regulator/consumer.h> +#include <linux/types.h> -#include <drm/drmP.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h>  #include <drm/drm_dp_helper.h>  #include <drm/drm_edid.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h>  #include "analogix-anx78xx.h" diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 257d69b21d99..d2de98d44184 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -10,27 +10,27 @@  * option) any later version.  */ -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/err.h>  #include <linux/clk.h> +#include <linux/component.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/iopoll.h> -#include <linux/interrupt.h> +#include <linux/module.h>  #include <linux/of.h>  #include <linux/of_gpio.h> -#include <linux/gpio.h> -#include <linux/component.h>  #include <linux/phy/phy.h> +#include <linux/platform_device.h> -#include <drm/drmP.h> +#include <drm/bridge/analogix_dp.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/bridge/analogix_dp.h> -  #include "analogix_dp_core.h"  #include "analogix_dp_reg.h" diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index e64736c39a9f..1a3800a58aaf 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -15,9 +15,9 @@  #include <linux/of_graph.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h>  struct dumb_vga { diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index ae8fc597eb38..6eaaa76970b1 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -7,12 +7,14 @@   * the License, or (at your option) any later version.   */ -#include <drm/drmP.h> -#include <drm/drm_bridge.h> -#include <drm/drm_panel.h> -  #include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h>  #include <linux/of_graph.h> +#include <linux/platform_device.h> + +#include <drm/drm_bridge.h> +#include <drm/drm_panel.h>  struct lvds_encoder {  	struct drm_bridge bridge; 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 a01028ec4de6..1644d281558a 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -34,11 +34,12 @@  #include <linux/i2c.h>  #include <linux/module.h>  #include <linux/of.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_edid.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/drmP.h>  #define EDID_EXT_BLOCK_CNT 0x7E diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index fb335afea4cf..3f4d4aef19e1 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -20,13 +20,14 @@  #include <linux/module.h>  #include <linux/of.h>  #include <linux/of_gpio.h> +  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h>  #include <drm/drm_edid.h>  #include <drm/drm_of.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/drmP.h>  #define PTN3460_EDID_ADDR			0x0  #define PTN3460_EDID_EMULATION_ADDR		0x84 diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 000ba7ce1ba8..08517740e6a0 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -8,12 +8,12 @@   * the License, or (at your option) any later version.   */ -#include <drm/drmP.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_connector.h>  #include <drm/drm_encoder.h>  #include <drm/drm_modeset_helper_vtables.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h>  struct panel_bridge { diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index fda1395b7481..05b9da6284f0 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -24,12 +24,13 @@  #include <linux/of_device.h>  #include <linux/pm.h>  #include <linux/regulator/consumer.h> +  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h>  #include <drm/drm_of.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/drmP.h>  /* Brightness scale on the Parade chip */  #define PS8622_MAX_BRIGHTNESS 0xff diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 08e12fef1349..961abc94d7a7 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -27,12 +27,16 @@  #include <linux/i2c.h>  #include <linux/module.h>  #include <linux/regmap.h> +#include <linux/clk.h> -#include <drm/drmP.h>  #include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h>  #include <drm/drm_edid.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> +#include <sound/hdmi-codec.h> +  #define SII902X_TPI_VIDEO_DATA			0x0  #define SII902X_TPI_PIXEL_REPETITION		0x8 @@ -74,6 +78,77 @@  #define SII902X_AVI_POWER_STATE_MSK		GENMASK(1, 0)  #define SII902X_AVI_POWER_STATE_D(l)		((l) & SII902X_AVI_POWER_STATE_MSK) +/* Audio  */ +#define SII902X_TPI_I2S_ENABLE_MAPPING_REG	0x1f +#define SII902X_TPI_I2S_CONFIG_FIFO0			(0 << 0) +#define SII902X_TPI_I2S_CONFIG_FIFO1			(1 << 0) +#define SII902X_TPI_I2S_CONFIG_FIFO2			(2 << 0) +#define SII902X_TPI_I2S_CONFIG_FIFO3			(3 << 0) +#define SII902X_TPI_I2S_LEFT_RIGHT_SWAP			(1 << 2) +#define SII902X_TPI_I2S_AUTO_DOWNSAMPLE			(1 << 3) +#define SII902X_TPI_I2S_SELECT_SD0			(0 << 4) +#define SII902X_TPI_I2S_SELECT_SD1			(1 << 4) +#define SII902X_TPI_I2S_SELECT_SD2			(2 << 4) +#define SII902X_TPI_I2S_SELECT_SD3			(3 << 4) +#define SII902X_TPI_I2S_FIFO_ENABLE			(1 << 7) + +#define SII902X_TPI_I2S_INPUT_CONFIG_REG	0x20 +#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES		(0 << 0) +#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_NO		(1 << 0) +#define SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST		(0 << 1) +#define SII902X_TPI_I2S_SD_DIRECTION_LSB_FIRST		(1 << 1) +#define SII902X_TPI_I2S_SD_JUSTIFY_LEFT			(0 << 2) +#define SII902X_TPI_I2S_SD_JUSTIFY_RIGHT		(1 << 2) +#define SII902X_TPI_I2S_WS_POLARITY_LOW			(0 << 3) +#define SII902X_TPI_I2S_WS_POLARITY_HIGH		(1 << 3) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_128		(0 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_256		(1 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_384		(2 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_512		(3 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_768		(4 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1024		(5 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1152		(6 << 4) +#define SII902X_TPI_I2S_MCLK_MULTIPLIER_192		(7 << 4) +#define SII902X_TPI_I2S_SCK_EDGE_FALLING		(0 << 7) +#define SII902X_TPI_I2S_SCK_EDGE_RISING			(1 << 7) + +#define SII902X_TPI_I2S_STRM_HDR_BASE	0x21 +#define SII902X_TPI_I2S_STRM_HDR_SIZE	5 + +#define SII902X_TPI_AUDIO_CONFIG_BYTE2_REG	0x26 +#define SII902X_TPI_AUDIO_CODING_STREAM_HEADER		(0 << 0) +#define SII902X_TPI_AUDIO_CODING_PCM			(1 << 0) +#define SII902X_TPI_AUDIO_CODING_AC3			(2 << 0) +#define SII902X_TPI_AUDIO_CODING_MPEG1			(3 << 0) +#define SII902X_TPI_AUDIO_CODING_MP3			(4 << 0) +#define SII902X_TPI_AUDIO_CODING_MPEG2			(5 << 0) +#define SII902X_TPI_AUDIO_CODING_AAC			(6 << 0) +#define SII902X_TPI_AUDIO_CODING_DTS			(7 << 0) +#define SII902X_TPI_AUDIO_CODING_ATRAC			(8 << 0) +#define SII902X_TPI_AUDIO_MUTE_DISABLE			(0 << 4) +#define SII902X_TPI_AUDIO_MUTE_ENABLE			(1 << 4) +#define SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS		(0 << 5) +#define SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS		(1 << 5) +#define SII902X_TPI_AUDIO_INTERFACE_DISABLE		(0 << 6) +#define SII902X_TPI_AUDIO_INTERFACE_SPDIF		(1 << 6) +#define SII902X_TPI_AUDIO_INTERFACE_I2S			(2 << 6) + +#define SII902X_TPI_AUDIO_CONFIG_BYTE3_REG	0x27 +#define SII902X_TPI_AUDIO_FREQ_STREAM			(0 << 3) +#define SII902X_TPI_AUDIO_FREQ_32KHZ			(1 << 3) +#define SII902X_TPI_AUDIO_FREQ_44KHZ			(2 << 3) +#define SII902X_TPI_AUDIO_FREQ_48KHZ			(3 << 3) +#define SII902X_TPI_AUDIO_FREQ_88KHZ			(4 << 3) +#define SII902X_TPI_AUDIO_FREQ_96KHZ			(5 << 3) +#define SII902X_TPI_AUDIO_FREQ_176KHZ			(6 << 3) +#define SII902X_TPI_AUDIO_FREQ_192KHZ			(7 << 3) +#define SII902X_TPI_AUDIO_SAMPLE_SIZE_STREAM		(0 << 6) +#define SII902X_TPI_AUDIO_SAMPLE_SIZE_16		(1 << 6) +#define SII902X_TPI_AUDIO_SAMPLE_SIZE_20		(2 << 6) +#define SII902X_TPI_AUDIO_SAMPLE_SIZE_24		(3 << 6) + +#define SII902X_TPI_AUDIO_CONFIG_BYTE4_REG	0x28 +  #define SII902X_INT_ENABLE			0x3c  #define SII902X_INT_STATUS			0x3d  #define SII902X_HOTPLUG_EVENT			BIT(0) @@ -81,6 +156,16 @@  #define SII902X_REG_TPI_RQB			0xc7 +/* Indirect internal register access */ +#define SII902X_IND_SET_PAGE			0xbc +#define SII902X_IND_OFFSET			0xbd +#define SII902X_IND_VALUE			0xbe + +#define SII902X_TPI_MISC_INFOFRAME_BASE		0xbf +#define SII902X_TPI_MISC_INFOFRAME_END		0xde +#define SII902X_TPI_MISC_INFOFRAME_SIZE	\ +	(SII902X_TPI_MISC_INFOFRAME_END - SII902X_TPI_MISC_INFOFRAME_BASE) +  #define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS	500  struct sii902x { @@ -90,6 +175,16 @@ struct sii902x {  	struct drm_connector connector;  	struct gpio_desc *reset_gpio;  	struct i2c_mux_core *i2cmux; +	/* +	 * Mutex protects audio and video functions from interfering +	 * each other, by keeping their i2c command sequences atomic. +	 */ +	struct mutex mutex; +	struct sii902x_audio { +		struct platform_device *pdev; +		struct clk *mclk; +		u32 i2s_fifo_sequence[4]; +	} audio;  };  static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val) @@ -161,8 +256,12 @@ sii902x_connector_detect(struct drm_connector *connector, bool force)  	struct sii902x *sii902x = connector_to_sii902x(connector);  	unsigned int status; +	mutex_lock(&sii902x->mutex); +  	regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); +	mutex_unlock(&sii902x->mutex); +  	return (status & SII902X_PLUGGED_STATUS) ?  	       connector_status_connected : connector_status_disconnected;  } @@ -180,12 +279,18 @@ static int sii902x_get_modes(struct drm_connector *connector)  {  	struct sii902x *sii902x = connector_to_sii902x(connector);  	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; +	u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;  	struct edid *edid;  	int num = 0, ret; +	mutex_lock(&sii902x->mutex); +  	edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);  	drm_connector_update_edid_property(connector, edid);  	if (edid) { +		if (drm_detect_hdmi_monitor(edid)) +			output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI; +  		num = drm_add_edid_modes(connector, edid);  		kfree(edid);  	} @@ -193,9 +298,19 @@ static int sii902x_get_modes(struct drm_connector *connector)  	ret = drm_display_info_set_bus_formats(&connector->display_info,  					       &bus_format, 1);  	if (ret) -		return ret; +		goto error_out; + +	ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, +				 SII902X_SYS_CTRL_OUTPUT_MODE, output_mode); +	if (ret) +		goto error_out; -	return num; +	ret = num; + +error_out: +	mutex_unlock(&sii902x->mutex); + +	return ret;  }  static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector, @@ -215,20 +330,28 @@ static void sii902x_bridge_disable(struct drm_bridge *bridge)  {  	struct sii902x *sii902x = bridge_to_sii902x(bridge); +	mutex_lock(&sii902x->mutex); +  	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,  			   SII902X_SYS_CTRL_PWR_DWN,  			   SII902X_SYS_CTRL_PWR_DWN); + +	mutex_unlock(&sii902x->mutex);  }  static void sii902x_bridge_enable(struct drm_bridge *bridge)  {  	struct sii902x *sii902x = bridge_to_sii902x(bridge); +	mutex_lock(&sii902x->mutex); +  	regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,  			   SII902X_AVI_POWER_STATE_MSK,  			   SII902X_AVI_POWER_STATE_D(0));  	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,  			   SII902X_SYS_CTRL_PWR_DWN, 0); + +	mutex_unlock(&sii902x->mutex);  }  static void sii902x_bridge_mode_set(struct drm_bridge *bridge, @@ -239,10 +362,11 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,  	struct regmap *regmap = sii902x->regmap;  	u8 buf[HDMI_INFOFRAME_SIZE(AVI)];  	struct hdmi_avi_infoframe frame; +	u16 pixel_clock_10kHz = adj->clock / 10;  	int ret; -	buf[0] = adj->clock; -	buf[1] = adj->clock >> 8; +	buf[0] = pixel_clock_10kHz & 0xff; +	buf[1] = pixel_clock_10kHz >> 8;  	buf[2] = adj->vrefresh;  	buf[3] = 0x00;  	buf[4] = adj->hdisplay; @@ -254,27 +378,32 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,  	buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO |  		 SII902X_TPI_AVI_INPUT_COLORSPACE_RGB; +	mutex_lock(&sii902x->mutex); +  	ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);  	if (ret) -		return; +		goto out;  	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame,  						       &sii902x->connector, adj);  	if (ret < 0) {  		DRM_ERROR("couldn't fill AVI infoframe\n"); -		return; +		goto out;  	}  	ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));  	if (ret < 0) {  		DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); -		return; +		goto out;  	}  	/* Do not send the infoframe header, but keep the CRC field. */  	regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME,  			  buf + HDMI_INFOFRAME_HEADER_SIZE - 1,  			  HDMI_AVI_INFOFRAME_SIZE + 1); + +out: +	mutex_unlock(&sii902x->mutex);  }  static int sii902x_bridge_attach(struct drm_bridge *bridge) @@ -315,6 +444,335 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = {  	.enable = sii902x_bridge_enable,  }; +static int sii902x_mute(struct sii902x *sii902x, bool mute) +{ +	struct device *dev = &sii902x->i2c->dev; +	unsigned int val = mute ? SII902X_TPI_AUDIO_MUTE_ENABLE : +		SII902X_TPI_AUDIO_MUTE_DISABLE; + +	dev_dbg(dev, "%s: %s\n", __func__, mute ? "Muted" : "Unmuted"); + +	return regmap_update_bits(sii902x->regmap, +				  SII902X_TPI_AUDIO_CONFIG_BYTE2_REG, +				  SII902X_TPI_AUDIO_MUTE_ENABLE, val); +} + +static const int sii902x_mclk_div_table[] = { +	128, 256, 384, 512, 768, 1024, 1152, 192 }; + +static int sii902x_select_mclk_div(u8 *i2s_config_reg, unsigned int rate, +				   unsigned int mclk) +{ +	int div = mclk / rate; +	int distance = 100000; +	u8 i, nearest = 0; + +	for (i = 0; i < ARRAY_SIZE(sii902x_mclk_div_table); i++) { +		unsigned int d = abs(div - sii902x_mclk_div_table[i]); + +		if (d >= distance) +			continue; + +		nearest = i; +		distance = d; +		if (d == 0) +			break; +	} + +	*i2s_config_reg |= nearest << 4; + +	return sii902x_mclk_div_table[nearest]; +} + +static const struct sii902x_sample_freq { +	u32 freq; +	u8 val; +} sii902x_sample_freq[] = { +	{ .freq = 32000,	.val = SII902X_TPI_AUDIO_FREQ_32KHZ }, +	{ .freq = 44000,	.val = SII902X_TPI_AUDIO_FREQ_44KHZ }, +	{ .freq = 48000,	.val = SII902X_TPI_AUDIO_FREQ_48KHZ }, +	{ .freq = 88000,	.val = SII902X_TPI_AUDIO_FREQ_88KHZ }, +	{ .freq = 96000,	.val = SII902X_TPI_AUDIO_FREQ_96KHZ }, +	{ .freq = 176000,	.val = SII902X_TPI_AUDIO_FREQ_176KHZ }, +	{ .freq = 192000,	.val = SII902X_TPI_AUDIO_FREQ_192KHZ }, +}; + +static int sii902x_audio_hw_params(struct device *dev, void *data, +				   struct hdmi_codec_daifmt *daifmt, +				   struct hdmi_codec_params *params) +{ +	struct sii902x *sii902x = dev_get_drvdata(dev); +	u8 i2s_config_reg = SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST; +	u8 config_byte2_reg = (SII902X_TPI_AUDIO_INTERFACE_I2S | +			       SII902X_TPI_AUDIO_MUTE_ENABLE | +			       SII902X_TPI_AUDIO_CODING_PCM); +	u8 config_byte3_reg = 0; +	u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; +	unsigned long mclk_rate; +	int i, ret; + +	if (daifmt->bit_clk_master || daifmt->frame_clk_master) { +		dev_dbg(dev, "%s: I2S master mode not supported\n", __func__); +		return -EINVAL; +	} + +	switch (daifmt->fmt) { +	case HDMI_I2S: +		i2s_config_reg |= SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES | +			SII902X_TPI_I2S_SD_JUSTIFY_LEFT; +		break; +	case HDMI_RIGHT_J: +		i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_RIGHT; +		break; +	case HDMI_LEFT_J: +		i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_LEFT; +		break; +	default: +		dev_dbg(dev, "%s: Unsupported i2s format %u\n", __func__, +			daifmt->fmt); +		return -EINVAL; +	} + +	if (daifmt->bit_clk_inv) +		i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_FALLING; +	else +		i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_RISING; + +	if (daifmt->frame_clk_inv) +		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_LOW; +	else +		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_HIGH; + +	if (params->channels > 2) +		config_byte2_reg |= SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS; +	else +		config_byte2_reg |= SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS; + +	switch (params->sample_width) { +	case 16: +		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_16; +		break; +	case 20: +		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_20; +		break; +	case 24: +	case 32: +		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_24; +		break; +	default: +		dev_err(dev, "%s: Unsupported sample width %u\n", __func__, +			params->sample_width); +		return -EINVAL; +	} + +	for (i = 0; i < ARRAY_SIZE(sii902x_sample_freq); i++) { +		if (params->sample_rate == sii902x_sample_freq[i].freq) { +			config_byte3_reg |= sii902x_sample_freq[i].val; +			break; +		} +	} + +	ret = clk_prepare_enable(sii902x->audio.mclk); +	if (ret) { +		dev_err(dev, "Enabling mclk failed: %d\n", ret); +		return ret; +	} + +	mclk_rate = clk_get_rate(sii902x->audio.mclk); + +	ret = sii902x_select_mclk_div(&i2s_config_reg, params->sample_rate, +				      mclk_rate); +	if (mclk_rate != ret * params->sample_rate) +		dev_dbg(dev, "Inaccurate reference clock (%ld/%d != %u)\n", +			mclk_rate, ret, params->sample_rate); + +	mutex_lock(&sii902x->mutex); + +	ret = regmap_write(sii902x->regmap, +			   SII902X_TPI_AUDIO_CONFIG_BYTE2_REG, +			   config_byte2_reg); +	if (ret < 0) +		goto out; + +	ret = regmap_write(sii902x->regmap, SII902X_TPI_I2S_INPUT_CONFIG_REG, +			   i2s_config_reg); +	if (ret) +		goto out; + +	for (i = 0; sii902x->audio.i2s_fifo_sequence[i] && +		     i < ARRAY_SIZE(sii902x->audio.i2s_fifo_sequence); i++) +		regmap_write(sii902x->regmap, +			     SII902X_TPI_I2S_ENABLE_MAPPING_REG, +			     sii902x->audio.i2s_fifo_sequence[i]); + +	ret = regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE3_REG, +			   config_byte3_reg); +	if (ret) +		goto out; + +	ret = regmap_bulk_write(sii902x->regmap, SII902X_TPI_I2S_STRM_HDR_BASE, +				params->iec.status, +				min((size_t) SII902X_TPI_I2S_STRM_HDR_SIZE, +				    sizeof(params->iec.status))); +	if (ret) +		goto out; + +	ret = hdmi_audio_infoframe_pack(¶ms->cea, infoframe_buf, +					sizeof(infoframe_buf)); +	if (ret < 0) { +		dev_err(dev, "%s: Failed to pack audio infoframe: %d\n", +			__func__, ret); +		goto out; +	} + +	ret = regmap_bulk_write(sii902x->regmap, +				SII902X_TPI_MISC_INFOFRAME_BASE, +				infoframe_buf, +				min(ret, SII902X_TPI_MISC_INFOFRAME_SIZE)); +	if (ret) +		goto out; + +	/* Decode Level 0 Packets */ +	ret = regmap_write(sii902x->regmap, SII902X_IND_SET_PAGE, 0x02); +	if (ret) +		goto out; + +	ret = regmap_write(sii902x->regmap, SII902X_IND_OFFSET, 0x24); +	if (ret) +		goto out; + +	ret = regmap_write(sii902x->regmap, SII902X_IND_VALUE, 0x02); +	if (ret) +		goto out; + +	dev_dbg(dev, "%s: hdmi audio enabled\n", __func__); +out: +	mutex_unlock(&sii902x->mutex); + +	if (ret) { +		clk_disable_unprepare(sii902x->audio.mclk); +		dev_err(dev, "%s: hdmi audio enable failed: %d\n", __func__, +			ret); +	} + +	return ret; +} + +static void sii902x_audio_shutdown(struct device *dev, void *data) +{ +	struct sii902x *sii902x = dev_get_drvdata(dev); + +	mutex_lock(&sii902x->mutex); + +	regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE2_REG, +		     SII902X_TPI_AUDIO_INTERFACE_DISABLE); + +	mutex_unlock(&sii902x->mutex); + +	clk_disable_unprepare(sii902x->audio.mclk); +} + +int sii902x_audio_digital_mute(struct device *dev, void *data, bool enable) +{ +	struct sii902x *sii902x = dev_get_drvdata(dev); + +	mutex_lock(&sii902x->mutex); + +	sii902x_mute(sii902x, enable); + +	mutex_unlock(&sii902x->mutex); + +	return 0; +} + +static int sii902x_audio_get_eld(struct device *dev, void *data, +				 uint8_t *buf, size_t len) +{ +	struct sii902x *sii902x = dev_get_drvdata(dev); + +	mutex_lock(&sii902x->mutex); + +	memcpy(buf, sii902x->connector.eld, +	       min(sizeof(sii902x->connector.eld), len)); + +	mutex_unlock(&sii902x->mutex); + +	return 0; +} + +static const struct hdmi_codec_ops sii902x_audio_codec_ops = { +	.hw_params = sii902x_audio_hw_params, +	.audio_shutdown = sii902x_audio_shutdown, +	.digital_mute = sii902x_audio_digital_mute, +	.get_eld = sii902x_audio_get_eld, +}; + +static int sii902x_audio_codec_init(struct sii902x *sii902x, +				    struct device *dev) +{ +	static const u8 audio_fifo_id[] = { +		SII902X_TPI_I2S_CONFIG_FIFO0, +		SII902X_TPI_I2S_CONFIG_FIFO1, +		SII902X_TPI_I2S_CONFIG_FIFO2, +		SII902X_TPI_I2S_CONFIG_FIFO3, +	}; +	static const u8 i2s_lane_id[] = { +		SII902X_TPI_I2S_SELECT_SD0, +		SII902X_TPI_I2S_SELECT_SD1, +		SII902X_TPI_I2S_SELECT_SD2, +		SII902X_TPI_I2S_SELECT_SD3, +	}; +	struct hdmi_codec_pdata codec_data = { +		.ops = &sii902x_audio_codec_ops, +		.i2s = 1, /* Only i2s support for now. */ +		.spdif = 0, +		.max_i2s_channels = 0, +	}; +	u8 lanes[4]; +	u32 num_lanes, i; + +	if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) { +		dev_dbg(dev, "%s: No \"#sound-dai-cells\", no audio\n", +			__func__); +		return 0; +	} + +	num_lanes = of_property_read_variable_u8_array(dev->of_node, +						       "sil,i2s-data-lanes", +						       lanes, 1, +						       ARRAY_SIZE(lanes)); + +	if (num_lanes == -EINVAL) { +		dev_dbg(dev, +			"%s: No \"sil,i2s-data-lanes\", use default <0>\n", +			__func__); +		num_lanes = 1; +		lanes[0] = 0; +	} else if (num_lanes < 0) { +		dev_err(dev, +			"%s: Error gettin \"sil,i2s-data-lanes\": %d\n", +			__func__, num_lanes); +		return num_lanes; +	} +	codec_data.max_i2s_channels = 2 * num_lanes; + +	for (i = 0; i < num_lanes; i++) +		sii902x->audio.i2s_fifo_sequence[i] |= audio_fifo_id[i] | +			i2s_lane_id[lanes[i]] |	SII902X_TPI_I2S_FIFO_ENABLE; + +	if (IS_ERR(sii902x->audio.mclk)) { +		dev_err(dev, "%s: No clock (audio mclk) found: %ld\n", +			__func__, PTR_ERR(sii902x->audio.mclk)); +		return 0; +	} + +	sii902x->audio.pdev = platform_device_register_data( +		dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, +		&codec_data, sizeof(codec_data)); + +	return PTR_ERR_OR_ZERO(sii902x->audio.pdev); +} +  static const struct regmap_range sii902x_volatile_ranges[] = {  	{ .range_min = 0, .range_max = 0xff },  }; @@ -327,6 +785,8 @@ static const struct regmap_access_table sii902x_volatile_table = {  static const struct regmap_config sii902x_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, +	.disable_locking = true, /* struct sii902x mutex should be enough */ +	.max_register = SII902X_TPI_MISC_INFOFRAME_END,  	.volatile_table = &sii902x_volatile_table,  	.cache_type = REGCACHE_NONE,  }; @@ -336,9 +796,13 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)  	struct sii902x *sii902x = data;  	unsigned int status = 0; +	mutex_lock(&sii902x->mutex); +  	regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);  	regmap_write(sii902x->regmap, SII902X_INT_STATUS, status); +	mutex_unlock(&sii902x->mutex); +  	if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev)  		drm_helper_hpd_irq_event(sii902x->bridge.dev); @@ -460,6 +924,12 @@ static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)  	return 0;  } +static const struct drm_bridge_timings default_sii902x_timings = { +	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE +		 | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE +		 | DRM_BUS_FLAG_DE_HIGH, +}; +  static int sii902x_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  { @@ -493,6 +963,8 @@ static int sii902x_probe(struct i2c_client *client,  		return PTR_ERR(sii902x->reset_gpio);  	} +	mutex_init(&sii902x->mutex); +  	sii902x_reset(sii902x);  	ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0); @@ -530,8 +1002,11 @@ static int sii902x_probe(struct i2c_client *client,  	sii902x->bridge.funcs = &sii902x_bridge_funcs;  	sii902x->bridge.of_node = dev->of_node; +	sii902x->bridge.timings = &default_sii902x_timings;  	drm_bridge_add(&sii902x->bridge); +	sii902x_audio_codec_init(sii902x, dev); +  	i2c_set_clientdata(client, sii902x);  	sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index ab7968c8f6a2..66bd66bad44c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -11,34 +11,35 @@   * (at your option) any later version.   *   */ -#include <linux/module.h> -#include <linux/irq.h> +#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/err.h> -#include <linux/clk.h>  #include <linux/hdmi.h> +#include <linux/irq.h> +#include <linux/module.h>  #include <linux/mutex.h>  #include <linux/of_device.h>  #include <linux/regmap.h> +#include <linux/dma-mapping.h>  #include <linux/spinlock.h> -#include <drm/drm_of.h> -#include <drm/drmP.h> +#include <media/cec-notifier.h> + +#include <uapi/linux/media-bus-format.h> +#include <uapi/linux/videodev2.h> + +#include <drm/bridge/dw_hdmi.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_edid.h>  #include <drm/drm_encoder_slave.h> -#include <drm/drm_scdc_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/bridge/dw_hdmi.h> - -#include <uapi/linux/media-bus-format.h> -#include <uapi/linux/videodev2.h> +#include <drm/drm_scdc_helper.h> -#include "dw-hdmi.h"  #include "dw-hdmi-audio.h"  #include "dw-hdmi-cec.h" - -#include <media/cec-notifier.h> +#include "dw-hdmi.h"  #define DDC_SEGMENT_ADDR	0x30 diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index e915ae8c9a92..a79c87bd0147 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -15,15 +15,18 @@  #include <linux/of_device.h>  #include <linux/pm_runtime.h>  #include <linux/reset.h> -#include <drm/drmP.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_bridge.h>  #include <drm/drm_crtc.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h>  #include <drm/drm_of.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/bridge/dw_mipi_dsi.h> -#include <video/mipi_display.h>  #define HWVER_131			0x31333100	/* IP version 1.31 */ diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index a20e454ddd64..170f162ffa55 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -7,18 +7,22 @@   *	Maciej Purski <m.purski@samsung.com>   */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h>  #include <drm/drm_fb_helper.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_of.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> -#include <drm/drmP.h> -#include <linux/gpio/consumer.h> -#include <linux/of_graph.h> -#include <linux/regulator/consumer.h> -#include <video/mipi_display.h>  #define FLD_MASK(start, end)    (((1 << ((start) - (end) + 1)) - 1) << (end))  #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index e570c9dee180..72666e07007a 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -71,6 +71,7 @@  /* System */  #define TC_IDREG		0x0500 +#define SYSSTAT			0x0508  #define SYSCTRL			0x0510  #define DP0_AUDSRC_NO_INPUT		(0 << 3)  #define DP0_AUDSRC_I2S_RX		(1 << 3) @@ -78,6 +79,19 @@  #define DP0_VIDSRC_DSI_RX		(1 << 0)  #define DP0_VIDSRC_DPI_RX		(2 << 0)  #define DP0_VIDSRC_COLOR_BAR		(3 << 0) +#define GPIOM			0x0540 +#define GPIOC			0x0544 +#define GPIOO			0x0548 +#define GPIOI			0x054c +#define INTCTL_G		0x0560 +#define INTSTS_G		0x0564 + +#define INT_SYSERR		BIT(16) +#define INT_GPIO_H(x)		(1 << (x == 0 ? 2 : 10)) +#define INT_GPIO_LC(x)		(1 << (x == 0 ? 3 : 11)) + +#define INT_GP0_LCNT		0x0584 +#define INT_GP1_LCNT		0x0588  /* Control */  #define DP0CTL			0x0600 @@ -186,11 +200,8 @@ module_param_named(test, tc_test_pattern, bool, 0644);  struct tc_edp_link {  	struct drm_dp_link	base;  	u8			assr; -	int			scrambler_dis; -	int			spread; -	int			coding8b10b; -	u8			swing; -	u8			preemp; +	bool			scrambler_dis; +	bool			spread;  };  struct tc_data { @@ -208,7 +219,7 @@ struct tc_data {  	/* display edid */  	struct edid		*edid;  	/* current mode */ -	const struct drm_display_mode	*mode; +	struct drm_display_mode	mode;  	u32			rev;  	u8			assr; @@ -216,6 +227,12 @@ struct tc_data {  	struct gpio_desc	*sd_gpio;  	struct gpio_desc	*reset_gpio;  	struct clk		*refclk; + +	/* do we have IRQ */ +	bool			have_irq; + +	/* HPD pin number (0 or 1) or -ENODEV */ +	int			hpd_pin;  };  static inline struct tc_data *aux_to_tc(struct drm_dp_aux *a) @@ -286,14 +303,17 @@ static int tc_aux_get_status(struct tc_data *tc, u8 *reply)  	ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &value);  	if (ret < 0)  		return ret; +  	if (value & AUX_BUSY) { -		if (value & AUX_TIMEOUT) { -			dev_err(tc->dev, "i2c access timeout!\n"); -			return -ETIMEDOUT; -		} +		dev_err(tc->dev, "aux busy!\n");  		return -EBUSY;  	} +	if (value & AUX_TIMEOUT) { +		dev_err(tc->dev, "aux access timeout!\n"); +		return -ETIMEDOUT; +	} +  	*reply = (value & AUX_STATUS_MASK) >> AUX_STATUS_SHIFT;  	return 0;  } @@ -387,13 +407,10 @@ static u32 tc_srcctrl(struct tc_data *tc)  	 * No training pattern, skew lane 1 data by two LSCLK cycles with  	 * respect to lane 0 data, AutoCorrect Mode = 0  	 */ -	u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW; +	u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_EN810B;  	if (tc->link.scrambler_dis)  		reg |= DP0_SRCCTRL_SCRMBLDIS;	/* Scrambler Disabled */ -	if (tc->link.coding8b10b) -		/* Enable 8/10B Encoder (TxData[19:16] not used) */ -		reg |= DP0_SRCCTRL_EN810B;  	if (tc->link.spread)  		reg |= DP0_SRCCTRL_SSCG;	/* Spread Spectrum Enable */  	if (tc->link.base.num_lanes == 2) @@ -545,7 +562,6 @@ static int tc_aux_link_setup(struct tc_data *tc)  	unsigned long rate;  	u32 value;  	int ret; -	u32 dp_phy_ctrl;  	rate = clk_get_rate(tc->refclk);  	switch (rate) { @@ -570,10 +586,7 @@ static int tc_aux_link_setup(struct tc_data *tc)  	value |= SYSCLK_SEL_LSCLK | LSCLK_DIV_2;  	tc_write(SYS_PLLPARAM, value); -	dp_phy_ctrl = BGREN | PWR_SW_EN | PHY_A0_EN; -	if (tc->link.base.num_lanes == 2) -		dp_phy_ctrl |= PHY_2LANE; -	tc_write(DP_PHY_CTRL, dp_phy_ctrl); +	tc_write(DP_PHY_CTRL, BGREN | PWR_SW_EN | PHY_A0_EN);  	/*  	 * Initially PLLs are in bypass. Force PLL parameter update, @@ -590,8 +603,9 @@ static int tc_aux_link_setup(struct tc_data *tc)  	if (ret == -ETIMEDOUT) {  		dev_err(tc->dev, "Timeout waiting for PHY to become ready");  		return ret; -	} else if (ret) +	} else if (ret) {  		goto err; +	}  	/* Setup AUX link */  	tc_write(DP0_AUXCFG1, AUX_RX_FILTER_EN | @@ -627,13 +641,13 @@ static int tc_get_display_props(struct tc_data *tc)  	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, tmp);  	if (ret < 0)  		goto err_dpcd_read; -	tc->link.spread = tmp[0] & BIT(0); /* 0.5% down spread */ +	tc->link.spread = tmp[0] & DP_MAX_DOWNSPREAD_0_5;  	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAIN_LINK_CHANNEL_CODING, tmp);  	if (ret < 0)  		goto err_dpcd_read; -	tc->link.coding8b10b = tmp[0] & BIT(0); -	tc->link.scrambler_dis = 0; + +	tc->link.scrambler_dis = false;  	/* read assr */  	ret = drm_dp_dpcd_readb(&tc->aux, DP_EDP_CONFIGURATION_SET, tmp);  	if (ret < 0) @@ -646,7 +660,9 @@ static int tc_get_display_props(struct tc_data *tc)  		tc->link.base.num_lanes,  		(tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ?  		"enhanced" : "non-enhanced"); -	dev_dbg(tc->dev, "ANSI 8B/10B: %d\n", tc->link.coding8b10b); +	dev_dbg(tc->dev, "Downspread: %s, scrambler: %s\n", +		tc->link.spread ? "0.5%" : "0.0%", +		tc->link.scrambler_dis ? "disabled" : "enabled");  	dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n",  		tc->link.assr, tc->assr); @@ -744,89 +760,29 @@ err:  	return ret;  } -static int tc_link_training(struct tc_data *tc, int pattern) +static int tc_wait_link_training(struct tc_data *tc)  { -	const char * const *errors; -	u32 srcctrl = tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | -		      DP0_SRCCTRL_AUTOCORRECT; -	int timeout; -	int retry; +	u32 timeout = 1000;  	u32 value;  	int ret; -	if (pattern == DP_TRAINING_PATTERN_1) { -		srcctrl |= DP0_SRCCTRL_TP1; -		errors = training_pattern1_errors; -	} else { -		srcctrl |= DP0_SRCCTRL_TP2; -		errors = training_pattern2_errors; -	} - -	/* Set DPCD 0x102 for Training Part 1 or 2 */ -	tc_write(DP0_SNKLTCTRL, DP_LINK_SCRAMBLING_DISABLE | pattern); - -	tc_write(DP0_LTLOOPCTRL, -		 (0x0f << 28) |	/* Defer Iteration Count */ -		 (0x0f << 24) |	/* Loop Iteration Count */ -		 (0x0d << 0));	/* Loop Timer Delay */ - -	retry = 5;  	do { -		/* Set DP0 Training Pattern */ -		tc_write(DP0_SRCCTRL, srcctrl); - -		/* Enable DP0 to start Link Training */ -		tc_write(DP0CTL, DP_EN); - -		/* wait */ -		timeout = 1000; -		do { -			tc_read(DP0_LTSTAT, &value); -			udelay(1); -		} while ((!(value & LT_LOOPDONE)) && (--timeout)); -		if (timeout == 0) { -			dev_err(tc->dev, "Link training timeout!\n"); -		} else { -			int pattern = (value >> 11) & 0x3; -			int error = (value >> 8) & 0x7; - -			dev_dbg(tc->dev, -				"Link training phase %d done after %d uS: %s\n", -				pattern, 1000 - timeout, errors[error]); -			if (pattern == DP_TRAINING_PATTERN_1 && error == 0) -				break; -			if (pattern == DP_TRAINING_PATTERN_2) { -				value &= LT_CHANNEL1_EQ_BITS | -					 LT_INTERLANE_ALIGN_DONE | -					 LT_CHANNEL0_EQ_BITS; -				/* in case of two lanes */ -				if ((tc->link.base.num_lanes == 2) && -				    (value == (LT_CHANNEL1_EQ_BITS | -					       LT_INTERLANE_ALIGN_DONE | -					       LT_CHANNEL0_EQ_BITS))) -					break; -				/* in case of one line */ -				if ((tc->link.base.num_lanes == 1) && -				    (value == (LT_INTERLANE_ALIGN_DONE | -					       LT_CHANNEL0_EQ_BITS))) -					break; -			} -		} -		/* restart */ -		tc_write(DP0CTL, 0); -		usleep_range(10, 20); -	} while (--retry); -	if (retry == 0) { -		dev_err(tc->dev, "Failed to finish training phase %d\n", -			pattern); +		udelay(1); +		tc_read(DP0_LTSTAT, &value); +	} while ((!(value & LT_LOOPDONE)) && (--timeout)); + +	if (timeout == 0) { +		dev_err(tc->dev, "Link training timeout waiting for LT_LOOPDONE!\n"); +		return -ETIMEDOUT;  	} -	return 0; +	return (value >> 8) & 0x7; +  err:  	return ret;  } -static int tc_main_link_setup(struct tc_data *tc) +static int tc_main_link_enable(struct tc_data *tc)  {  	struct drm_dp_aux *aux = &tc->aux;  	struct device *dev = tc->dev; @@ -837,9 +793,11 @@ static int tc_main_link_setup(struct tc_data *tc)  	int ret;  	u8 tmp[8]; -	/* display mode should be set at this point */ -	if (!tc->mode) -		return -EINVAL; +	dev_dbg(tc->dev, "link enable\n"); + +	tc_read(DP0CTL, &value); +	if (WARN_ON(value & DP_EN)) +		tc_write(DP0CTL, 0);  	tc_write(DP0_SRCCTRL, tc_srcctrl(tc));  	/* SSCG and BW27 on DP1 must be set to the same as on DP0 */ @@ -872,7 +830,6 @@ static int tc_main_link_setup(struct tc_data *tc)  	if (tc->link.base.num_lanes == 2)  		dp_phy_ctrl |= PHY_2LANE;  	tc_write(DP_PHY_CTRL, dp_phy_ctrl); -	msleep(100);  	/* PLL setup */  	tc_write(DP0_PLLCTRL, PLLUPDATE | PLLEN); @@ -881,14 +838,6 @@ static int tc_main_link_setup(struct tc_data *tc)  	tc_write(DP1_PLLCTRL, PLLUPDATE | PLLEN);  	tc_wait_pll_lock(tc); -	/* PXL PLL setup */ -	if (tc_test_pattern) { -		ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), -				    1000 * tc->mode->clock); -		if (ret) -			goto err; -	} -  	/* Reset/Enable Main Links */  	dp_phy_ctrl |= DP_PHY_RST | PHY_M1_RST | PHY_M0_RST;  	tc_write(DP_PHY_CTRL, dp_phy_ctrl); @@ -934,9 +883,9 @@ static int tc_main_link_setup(struct tc_data *tc)  		if (tmp[0] != tc->assr) {  			dev_dbg(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n", -				 tc->assr); +				tc->assr);  			/* trying with disabled scrambler */ -			tc->link.scrambler_dis = 1; +			tc->link.scrambler_dis = true;  		}  	} @@ -948,19 +897,82 @@ static int tc_main_link_setup(struct tc_data *tc)  	/* DOWNSPREAD_CTRL */  	tmp[0] = tc->link.spread ? DP_SPREAD_AMP_0_5 : 0x00;  	/* MAIN_LINK_CHANNEL_CODING_SET */ -	tmp[1] =  tc->link.coding8b10b ? DP_SET_ANSI_8B10B : 0x00; +	tmp[1] =  DP_SET_ANSI_8B10B;  	ret = drm_dp_dpcd_write(aux, DP_DOWNSPREAD_CTRL, tmp, 2);  	if (ret < 0)  		goto err_dpcd_write; -	ret = tc_link_training(tc, DP_TRAINING_PATTERN_1); -	if (ret) +	/* Reset voltage-swing & pre-emphasis */ +	tmp[0] = tmp[1] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | +			  DP_TRAIN_PRE_EMPH_LEVEL_0; +	ret = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, tmp, 2); +	if (ret < 0) +		goto err_dpcd_write; + +	/* Clock-Recovery */ + +	/* Set DPCD 0x102 for Training Pattern 1 */ +	tc_write(DP0_SNKLTCTRL, DP_LINK_SCRAMBLING_DISABLE | +		 DP_TRAINING_PATTERN_1); + +	tc_write(DP0_LTLOOPCTRL, +		 (15 << 28) |	/* Defer Iteration Count */ +		 (15 << 24) |	/* Loop Iteration Count */ +		 (0xd << 0));	/* Loop Timer Delay */ + +	tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | +		 DP0_SRCCTRL_AUTOCORRECT | DP0_SRCCTRL_TP1); + +	/* Enable DP0 to start Link Training */ +	tc_write(DP0CTL, +		 ((tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ? EF_EN : 0) | +		 DP_EN); + +	/* wait */ +	ret = tc_wait_link_training(tc); +	if (ret < 0)  		goto err; -	ret = tc_link_training(tc, DP_TRAINING_PATTERN_2); -	if (ret) +	if (ret) { +		dev_err(tc->dev, "Link training phase 1 failed: %s\n", +			training_pattern1_errors[ret]); +		ret = -ENODEV; +		goto err; +	} + +	/* Channel Equalization */ + +	/* Set DPCD 0x102 for Training Pattern 2 */ +	tc_write(DP0_SNKLTCTRL, DP_LINK_SCRAMBLING_DISABLE | +		 DP_TRAINING_PATTERN_2); + +	tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | +		 DP0_SRCCTRL_AUTOCORRECT | DP0_SRCCTRL_TP2); + +	/* wait */ +	ret = tc_wait_link_training(tc); +	if (ret < 0)  		goto err; +	if (ret) { +		dev_err(tc->dev, "Link training phase 2 failed: %s\n", +			training_pattern2_errors[ret]); +		ret = -ENODEV; +		goto err; +	} + +	/* +	 * Toshiba's documentation suggests to first clear DPCD 0x102, then +	 * clear the training pattern bit in DP0_SRCCTRL. Testing shows +	 * that the link sometimes drops if those steps are done in that order, +	 * but if the steps are done in reverse order, the link stays up. +	 * +	 * So we do the steps differently than documented here. +	 */ + +	/* Clear Training Pattern, set AutoCorrect Mode = 1 */ +	tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_AUTOCORRECT); +  	/* Clear DPCD 0x102 */  	/* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */  	tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00; @@ -968,47 +980,43 @@ static int tc_main_link_setup(struct tc_data *tc)  	if (ret < 0)  		goto err_dpcd_write; -	/* Clear Training Pattern, set AutoCorrect Mode = 1 */ -	tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_AUTOCORRECT); +	/* Check link status */ +	ret = drm_dp_dpcd_read_link_status(aux, tmp); +	if (ret < 0) +		goto err_dpcd_read; -	/* Wait */ -	timeout = 100; -	do { -		udelay(1); -		/* Read DPCD 0x202-0x207 */ -		ret = drm_dp_dpcd_read_link_status(aux, tmp + 2); -		if (ret < 0) -			goto err_dpcd_read; -	} while ((--timeout) && -		 !(drm_dp_channel_eq_ok(tmp + 2,  tc->link.base.num_lanes))); +	ret = 0; -	if (timeout == 0) { -		/* Read DPCD 0x200-0x201 */ -		ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT, tmp, 2); -		if (ret < 0) -			goto err_dpcd_read; -		dev_err(dev, "channel(s) EQ not ok\n"); -		dev_info(dev, "0x0200 SINK_COUNT: 0x%02x\n", tmp[0]); -		dev_info(dev, "0x0201 DEVICE_SERVICE_IRQ_VECTOR: 0x%02x\n", -			 tmp[1]); -		dev_info(dev, "0x0202 LANE0_1_STATUS: 0x%02x\n", tmp[2]); -		dev_info(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n", -			 tmp[4]); -		dev_info(dev, "0x0205 SINK_STATUS: 0x%02x\n", tmp[5]); -		dev_info(dev, "0x0206 ADJUST_REQUEST_LANE0_1: 0x%02x\n", -			 tmp[6]); - -		return -EAGAIN; +	value = tmp[0] & DP_CHANNEL_EQ_BITS; + +	if (value != DP_CHANNEL_EQ_BITS) { +		dev_err(tc->dev, "Lane 0 failed: %x\n", value); +		ret = -ENODEV;  	} -	ret = tc_set_video_mode(tc, tc->mode); -	if (ret) -		goto err; +	if (tc->link.base.num_lanes == 2) { +		value = (tmp[0] >> 4) & DP_CHANNEL_EQ_BITS; -	/* Set M/N */ -	ret = tc_stream_clock_calc(tc); -	if (ret) +		if (value != DP_CHANNEL_EQ_BITS) { +			dev_err(tc->dev, "Lane 1 failed: %x\n", value); +			ret = -ENODEV; +		} + +		if (!(tmp[2] & DP_INTERLANE_ALIGN_DONE)) { +			dev_err(tc->dev, "Interlane align failed\n"); +			ret = -ENODEV; +		} +	} + +	if (ret) { +		dev_err(dev, "0x0202 LANE0_1_STATUS:            0x%02x\n", tmp[0]); +		dev_err(dev, "0x0203 LANE2_3_STATUS             0x%02x\n", tmp[1]); +		dev_err(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n", tmp[2]); +		dev_err(dev, "0x0205 SINK_STATUS:               0x%02x\n", tmp[3]); +		dev_err(dev, "0x0206 ADJUST_REQUEST_LANE0_1:    0x%02x\n", tmp[4]); +		dev_err(dev, "0x0207 ADJUST_REQUEST_LANE2_3:    0x%02x\n", tmp[5]);  		goto err; +	}  	return 0;  err_dpcd_read: @@ -1020,39 +1028,84 @@ err:  	return ret;  } -static int tc_main_link_stream(struct tc_data *tc, int state) +static int tc_main_link_disable(struct tc_data *tc) +{ +	int ret; + +	dev_dbg(tc->dev, "link disable\n"); + +	tc_write(DP0_SRCCTRL, 0); +	tc_write(DP0CTL, 0); + +	return 0; +err: +	return ret; +} + +static int tc_stream_enable(struct tc_data *tc)  {  	int ret;  	u32 value; -	dev_dbg(tc->dev, "stream: %d\n", state); +	dev_dbg(tc->dev, "enable video stream\n"); -	if (state) { -		value = VID_MN_GEN | DP_EN; -		if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) -			value |= EF_EN; -		tc_write(DP0CTL, value); -		/* -		 * VID_EN assertion should be delayed by at least N * LSCLK -		 * cycles from the time VID_MN_GEN is enabled in order to -		 * generate stable values for VID_M. LSCLK is 270 MHz or -		 * 162 MHz, VID_N is set to 32768 in  tc_stream_clock_calc(), -		 * so a delay of at least 203 us should suffice. -		 */ -		usleep_range(500, 1000); -		value |= VID_EN; -		tc_write(DP0CTL, value); -		/* Set input interface */ -		value = DP0_AUDSRC_NO_INPUT; -		if (tc_test_pattern) -			value |= DP0_VIDSRC_COLOR_BAR; -		else -			value |= DP0_VIDSRC_DPI_RX; -		tc_write(SYSCTRL, value); -	} else { -		tc_write(DP0CTL, 0); +	/* PXL PLL setup */ +	if (tc_test_pattern) { +		ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), +				    1000 * tc->mode.clock); +		if (ret) +			goto err;  	} +	ret = tc_set_video_mode(tc, &tc->mode); +	if (ret) +		return ret; + +	/* Set M/N */ +	ret = tc_stream_clock_calc(tc); +	if (ret) +		return ret; + +	value = VID_MN_GEN | DP_EN; +	if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) +		value |= EF_EN; +	tc_write(DP0CTL, value); +	/* +	 * VID_EN assertion should be delayed by at least N * LSCLK +	 * cycles from the time VID_MN_GEN is enabled in order to +	 * generate stable values for VID_M. LSCLK is 270 MHz or +	 * 162 MHz, VID_N is set to 32768 in  tc_stream_clock_calc(), +	 * so a delay of at least 203 us should suffice. +	 */ +	usleep_range(500, 1000); +	value |= VID_EN; +	tc_write(DP0CTL, value); +	/* Set input interface */ +	value = DP0_AUDSRC_NO_INPUT; +	if (tc_test_pattern) +		value |= DP0_VIDSRC_COLOR_BAR; +	else +		value |= DP0_VIDSRC_DPI_RX; +	tc_write(SYSCTRL, value); + +	return 0; +err: +	return ret; +} + +static int tc_stream_disable(struct tc_data *tc) +{ +	int ret; +	u32 val; + +	dev_dbg(tc->dev, "disable video stream\n"); + +	tc_read(DP0CTL, &val); +	val &= ~VID_EN; +	tc_write(DP0CTL, val); + +	tc_pxl_pll_dis(tc); +  	return 0;  err:  	return ret; @@ -1070,15 +1123,22 @@ static void tc_bridge_enable(struct drm_bridge *bridge)  	struct tc_data *tc = bridge_to_tc(bridge);  	int ret; -	ret = tc_main_link_setup(tc); +	ret = tc_get_display_props(tc);  	if (ret < 0) { -		dev_err(tc->dev, "main link setup error: %d\n", ret); +		dev_err(tc->dev, "failed to read display props: %d\n", ret);  		return;  	} -	ret = tc_main_link_stream(tc, 1); +	ret = tc_main_link_enable(tc); +	if (ret < 0) { +		dev_err(tc->dev, "main link enable error: %d\n", ret); +		return; +	} + +	ret = tc_stream_enable(tc);  	if (ret < 0) {  		dev_err(tc->dev, "main link stream start error: %d\n", ret); +		tc_main_link_disable(tc);  		return;  	} @@ -1092,9 +1152,13 @@ static void tc_bridge_disable(struct drm_bridge *bridge)  	drm_panel_disable(tc->panel); -	ret = tc_main_link_stream(tc, 0); +	ret = tc_stream_disable(tc);  	if (ret < 0)  		dev_err(tc->dev, "main link stream stop error: %d\n", ret); + +	ret = tc_main_link_disable(tc); +	if (ret < 0) +		dev_err(tc->dev, "main link disable error: %d\n", ret);  }  static void tc_bridge_post_disable(struct drm_bridge *bridge) @@ -1116,10 +1180,10 @@ static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,  	return true;  } -static enum drm_mode_status tc_connector_mode_valid(struct drm_connector *connector, -				   struct drm_display_mode *mode) +static enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge, +					  const struct drm_display_mode *mode)  { -	struct tc_data *tc = connector_to_tc(connector); +	struct tc_data *tc = bridge_to_tc(bridge);  	u32 req, avail;  	u32 bits_per_pixel = 24; @@ -1142,7 +1206,7 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge,  {  	struct tc_data *tc = bridge_to_tc(bridge); -	tc->mode = mode; +	tc->mode = *mode;  }  static int tc_connector_get_modes(struct drm_connector *connector) @@ -1150,6 +1214,13 @@ static int tc_connector_get_modes(struct drm_connector *connector)  	struct tc_data *tc = connector_to_tc(connector);  	struct edid *edid;  	unsigned int count; +	int ret; + +	ret = tc_get_display_props(tc); +	if (ret < 0) { +		dev_err(tc->dev, "failed to read display props: %d\n", ret); +		return 0; +	}  	if (tc->panel && tc->panel->funcs && tc->panel->funcs->get_modes) {  		count = tc->panel->funcs->get_modes(tc->panel); @@ -1170,29 +1241,40 @@ static int tc_connector_get_modes(struct drm_connector *connector)  	return count;  } -static void tc_connector_set_polling(struct tc_data *tc, -				     struct drm_connector *connector) -{ -	/* TODO: add support for HPD */ -	connector->polled = DRM_CONNECTOR_POLL_CONNECT | -			    DRM_CONNECTOR_POLL_DISCONNECT; -} +static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { +	.get_modes = tc_connector_get_modes, +}; -static struct drm_encoder * -tc_connector_best_encoder(struct drm_connector *connector) +static enum drm_connector_status tc_connector_detect(struct drm_connector *connector, +						     bool force)  {  	struct tc_data *tc = connector_to_tc(connector); +	bool conn; +	u32 val; +	int ret; -	return tc->bridge.encoder; -} +	if (tc->hpd_pin < 0) { +		if (tc->panel) +			return connector_status_connected; +		else +			return connector_status_unknown; +	} -static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { -	.get_modes = tc_connector_get_modes, -	.mode_valid = tc_connector_mode_valid, -	.best_encoder = tc_connector_best_encoder, -}; +	tc_read(GPIOI, &val); + +	conn = val & BIT(tc->hpd_pin); + +	if (conn) +		return connector_status_connected; +	else +		return connector_status_disconnected; + +err: +	return connector_status_unknown; +}  static const struct drm_connector_funcs tc_connector_funcs = { +	.detect = tc_connector_detect,  	.fill_modes = drm_helper_probe_single_connector_modes,  	.destroy = drm_connector_cleanup,  	.reset = drm_atomic_helper_connector_reset, @@ -1207,7 +1289,7 @@ static int tc_bridge_attach(struct drm_bridge *bridge)  	struct drm_device *drm = bridge->dev;  	int ret; -	/* Create eDP connector */ +	/* Create DP/eDP connector */  	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);  	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,  				 tc->panel ? DRM_MODE_CONNECTOR_eDP : @@ -1215,6 +1297,15 @@ static int tc_bridge_attach(struct drm_bridge *bridge)  	if (ret)  		return ret; +	/* Don't poll if don't have HPD connected */ +	if (tc->hpd_pin >= 0) { +		if (tc->have_irq) +			tc->connector.polled = DRM_CONNECTOR_POLL_HPD; +		else +			tc->connector.polled = DRM_CONNECTOR_POLL_CONNECT | +					       DRM_CONNECTOR_POLL_DISCONNECT; +	} +  	if (tc->panel)  		drm_panel_attach(tc->panel, &tc->connector); @@ -1231,6 +1322,7 @@ static int tc_bridge_attach(struct drm_bridge *bridge)  static const struct drm_bridge_funcs tc_bridge_funcs = {  	.attach = tc_bridge_attach, +	.mode_valid = tc_mode_valid,  	.mode_set = tc_bridge_mode_set,  	.pre_enable = tc_bridge_pre_enable,  	.enable = tc_bridge_enable, @@ -1250,6 +1342,8 @@ static const struct regmap_range tc_volatile_ranges[] = {  	regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL),  	regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL),  	regmap_reg_range(VFUEN0, VFUEN0), +	regmap_reg_range(INTSTS_G, INTSTS_G), +	regmap_reg_range(GPIOI, GPIOI),  };  static const struct regmap_access_table tc_volatile_table = { @@ -1278,6 +1372,49 @@ static const struct regmap_config tc_regmap_config = {  	.val_format_endian = REGMAP_ENDIAN_LITTLE,  }; +static irqreturn_t tc_irq_handler(int irq, void *arg) +{ +	struct tc_data *tc = arg; +	u32 val; +	int r; + +	r = regmap_read(tc->regmap, INTSTS_G, &val); +	if (r) +		return IRQ_NONE; + +	if (!val) +		return IRQ_NONE; + +	if (val & INT_SYSERR) { +		u32 stat = 0; + +		regmap_read(tc->regmap, SYSSTAT, &stat); + +		dev_err(tc->dev, "syserr %x\n", stat); +	} + +	if (tc->hpd_pin >= 0 && tc->bridge.dev) { +		/* +		 * H is triggered when the GPIO goes high. +		 * +		 * LC is triggered when the GPIO goes low and stays low for +		 * the duration of LCNT +		 */ +		bool h = val & INT_GPIO_H(tc->hpd_pin); +		bool lc = val & INT_GPIO_LC(tc->hpd_pin); + +		dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin, +			h ? "H" : "", lc ? "LC" : ""); + +		if (h || lc) +			drm_kms_helper_hotplug_event(tc->bridge.dev); +	} + +	regmap_write(tc->regmap, INTSTS_G, val); + +	return IRQ_HANDLED; +} +  static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  {  	struct device *dev = &client->dev; @@ -1329,6 +1466,33 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  		return ret;  	} +	ret = of_property_read_u32(dev->of_node, "toshiba,hpd-pin", +				   &tc->hpd_pin); +	if (ret) { +		tc->hpd_pin = -ENODEV; +	} else { +		if (tc->hpd_pin < 0 || tc->hpd_pin > 1) { +			dev_err(dev, "failed to parse HPD number\n"); +			return ret; +		} +	} + +	if (client->irq > 0) { +		/* enable SysErr */ +		regmap_write(tc->regmap, INTCTL_G, INT_SYSERR); + +		ret = devm_request_threaded_irq(dev, client->irq, +						NULL, tc_irq_handler, +						IRQF_ONESHOT, +						"tc358767-irq", tc); +		if (ret) { +			dev_err(dev, "failed to register dp interrupt\n"); +			return ret; +		} + +		tc->have_irq = true; +	} +  	ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev);  	if (ret) {  		dev_err(tc->dev, "can not read device ID: %d\n", ret); @@ -1342,6 +1506,22 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  	tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */ +	if (tc->hpd_pin >= 0) { +		u32 lcnt_reg = tc->hpd_pin == 0 ? INT_GP0_LCNT : INT_GP1_LCNT; +		u32 h_lc = INT_GPIO_H(tc->hpd_pin) | INT_GPIO_LC(tc->hpd_pin); + +		/* Set LCNT to 2ms */ +		regmap_write(tc->regmap, lcnt_reg, +			     clk_get_rate(tc->refclk) * 2 / 1000); +		/* We need the "alternate" mode for HPD */ +		regmap_write(tc->regmap, GPIOM, BIT(tc->hpd_pin)); + +		if (tc->have_irq) { +			/* enable H & LC */ +			regmap_update_bits(tc->regmap, INTCTL_G, h_lc, h_lc); +		} +	} +  	ret = tc_aux_link_setup(tc);  	if (ret)  		return ret; @@ -1354,12 +1534,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  	if (ret)  		return ret; -	ret = tc_get_display_props(tc); -	if (ret) -		goto err_unregister_aux; - -	tc_connector_set_polling(tc, &tc->connector); -  	tc->bridge.funcs = &tc_bridge_funcs;  	tc->bridge.of_node = dev->of_node;  	drm_bridge_add(&tc->bridge); @@ -1367,9 +1541,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)  	i2c_set_clientdata(client, tc);  	return 0; -err_unregister_aux: -	drm_dp_aux_unregister(&tc->aux); -	return ret;  }  static int tc_remove(struct i2c_client *client) @@ -1379,8 +1550,6 @@ static int tc_remove(struct i2c_client *client)  	drm_bridge_remove(&tc->bridge);  	drm_dp_aux_unregister(&tc->aux); -	tc_pxl_pll_dis(tc); -  	return 0;  } diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index b083a740565c..fd597f58f4e6 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c @@ -5,15 +5,17 @@   * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>   */ -#include <drm/drmP.h> -#include <drm/drm_bridge.h> -#include <drm/drm_panel.h> -  #include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h>  #include <linux/of_graph.h> +#include <linux/platform_device.h>  #include <linux/regulator/consumer.h>  #include <linux/slab.h> +#include <drm/drm_bridge.h> +#include <drm/drm_panel.h> +  enum thc63_ports {  	THC63_LVDS_IN0,  	THC63_LVDS_IN1, diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index f72ee137e5f1..b77a52d05061 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -3,23 +3,25 @@   * Copyright (c) 2018, The Linux Foundation. All rights reserved.   */ -#include <drm/drmP.h> -#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_dp_helper.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_probe_helper.h>  #include <linux/clk.h>  #include <linux/gpio/consumer.h>  #include <linux/i2c.h>  #include <linux/iopoll.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_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +  #define SN_DEVICE_REV_REG			0x08  #define SN_DPPLL_SRC_REG			0x0A  #define  DPPLL_CLK_SRC_DSICLK			BIT(0) diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 8b0e71bd3ca7..bfb21b5eefe1 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -11,15 +11,15 @@  #include <linux/delay.h>  #include <linux/fwnode.h>  #include <linux/gpio/consumer.h> +#include <linux/i2c.h>  #include <linux/irq.h>  #include <linux/module.h>  #include <linux/of_graph.h>  #include <linux/platform_device.h> -#include <linux/i2c.h> -#include <drm/drmP.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h>  #define HOTPLUG_DEBOUNCE_MS		1100 diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index 40fba1c04dfc..117b8ee98243 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -31,13 +31,20 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h>  #include <linux/module.h> +#include <linux/pci.h>  #include <linux/slab.h> -#include "drm_legacy.h"  #include <asm/agp.h> +#include <drm/drm_agpsupport.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> + +#include "drm_legacy.h" +  /**   * Get AGP information.   * diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f4924cb7f495..b640e77184e5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -26,13 +26,18 @@   */ -#include <drm/drmP.h> +#include <linux/sync_file.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_uapi.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h>  #include <drm/drm_mode.h>  #include <drm/drm_print.h>  #include <drm/drm_writeback.h> -#include <linux/sync_file.h>  #include "drm_crtc_internal.h"  #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 79dbeafb9a52..acf993cb8e52 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -25,14 +25,17 @@   * Daniel Vetter <daniel.vetter@ffwll.ch>   */ -#include <drm/drmP.h> +#include <linux/dma-fence.h> +  #include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>  #include <drm/drm_atomic_uapi.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_device.h>  #include <drm/drm_plane_helper.h> -#include <drm/drm_atomic_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h>  #include <drm/drm_writeback.h> -#include <drm/drm_damage_helper.h> -#include <linux/dma-fence.h>  #include "drm_crtc_helper_internal.h"  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index ec13823153a9..97ab26679b96 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -24,12 +24,13 @@   * Daniel Vetter <daniel.vetter@ffwll.ch>   */ +#include <drm/drm_atomic.h>  #include <drm/drm_atomic_state_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_plane.h>  #include <drm/drm_connector.h> -#include <drm/drm_atomic.h> +#include <drm/drm_crtc.h>  #include <drm/drm_device.h> +#include <drm/drm_plane.h> +#include <drm/drm_print.h>  #include <drm/drm_writeback.h>  #include <linux/slab.h> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index bf98402f3210..cc9acd986c68 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -28,10 +28,16 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h> +#include <linux/slab.h> + +#include <drm/drm_auth.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_lease.h> +#include <drm/drm_print.h> +  #include "drm_internal.h"  #include "drm_legacy.h" -#include <drm/drm_lease.h>  /**   * DOC: master and authentication diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index 0c78ca386cbe..37ac168fcb60 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -23,13 +23,16 @@   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE   * OF THIS SOFTWARE.   */ -#include <drm/drmP.h> -#include <drm/drm_atomic.h> -#include <drm/drm_blend.h> +  #include <linux/export.h>  #include <linux/slab.h>  #include <linux/sort.h> +#include <drm/drm_atomic.h> +#include <drm/drm_blend.h> +#include <drm/drm_device.h> +#include <drm/drm_print.h> +  #include "drm_crtc_internal.h"  /** diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index bfc419ed9d6c..68dacf8422c6 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -28,15 +28,26 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/log2.h>  #include <linux/export.h> +#include <linux/log2.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/nospec.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +  #include <asm/shmparam.h> -#include <drm/drmP.h> + +#include <drm/drm_agpsupport.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_pci.h> +#include <drm/drm_print.h> +  #include "drm_legacy.h" -#include <linux/nospec.h>  static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,  						  struct drm_local_map *map) diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 5abcd83da6a6..410572f14257 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -15,10 +15,10 @@  #include <drm/drm_drv.h>  #include <drm/drm_file.h>  #include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h>  #include <drm/drm_gem.h>  #include <drm/drm_mode.h>  #include <drm/drm_print.h> -#include <drm/drmP.h>  #include "drm_crtc_internal.h"  #include "drm_internal.h" @@ -27,7 +27,6 @@   * DOC: overview   *   * This library provides support for clients running in the kernel like fbdev and bootsplash. - * Currently it's only partially implemented, just enough to support fbdev.   *   * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.   */ @@ -92,14 +91,20 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client,  	client->name = name;  	client->funcs = funcs; -	ret = drm_client_open(client); +	ret = drm_client_modeset_create(client);  	if (ret)  		goto err_put_module; +	ret = drm_client_open(client); +	if (ret) +		goto err_free; +  	drm_dev_get(dev);  	return 0; +err_free: +	drm_client_modeset_free(client);  err_put_module:  	if (funcs)  		module_put(funcs->owner); @@ -148,6 +153,7 @@ void drm_client_release(struct drm_client_dev *client)  	DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name); +	drm_client_modeset_free(client);  	drm_client_close(client);  	drm_dev_put(dev);  	if (client->funcs) diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c new file mode 100644 index 000000000000..66770ed3299e --- /dev/null +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2018 Noralf Trønnes + * Copyright (c) 2006-2009 Red Hat Inc. + * Copyright (c) 2006-2008 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + */ + +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +#include <drm/drm_client.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> + +int drm_client_modeset_create(struct drm_client_dev *client) +{ +	struct drm_device *dev = client->dev; +	unsigned int num_crtc = dev->mode_config.num_crtc; +	unsigned int max_connector_count = 1; +	struct drm_mode_set *modeset; +	struct drm_crtc *crtc; +	unsigned int i = 0; + +	/* Add terminating zero entry to enable index less iteration */ +	client->modesets = kcalloc(num_crtc + 1, sizeof(*client->modesets), GFP_KERNEL); +	if (!client->modesets) +		return -ENOMEM; + +	mutex_init(&client->modeset_mutex); + +	drm_for_each_crtc(crtc, dev) +		client->modesets[i++].crtc = crtc; + +	/* Cloning is only supported in the single crtc case. */ +	if (num_crtc == 1) +		max_connector_count = DRM_CLIENT_MAX_CLONED_CONNECTORS; + +	for (modeset = client->modesets; modeset->crtc; modeset++) { +		modeset->connectors = kcalloc(max_connector_count, +					      sizeof(*modeset->connectors), GFP_KERNEL); +		if (!modeset->connectors) +			goto err_free; +	} + +	return 0; + +err_free: +	drm_client_modeset_free(client); + +	return -ENOMEM; +} + +void drm_client_modeset_release(struct drm_client_dev *client) +{ +	struct drm_mode_set *modeset; +	unsigned int i; + +	drm_client_for_each_modeset(modeset, client) { +		drm_mode_destroy(client->dev, modeset->mode); +		modeset->mode = NULL; +		modeset->fb = NULL; + +		for (i = 0; i < modeset->num_connectors; i++) { +			drm_connector_put(modeset->connectors[i]); +			modeset->connectors[i] = NULL; +		} +		modeset->num_connectors = 0; +	} +} +/* TODO: Remove export when modeset code has been moved over */ +EXPORT_SYMBOL(drm_client_modeset_release); + +void drm_client_modeset_free(struct drm_client_dev *client) +{ +	struct drm_mode_set *modeset; + +	mutex_lock(&client->modeset_mutex); + +	drm_client_modeset_release(client); + +	drm_client_for_each_modeset(modeset, client) +		kfree(modeset->connectors); + +	mutex_unlock(&client->modeset_mutex); + +	mutex_destroy(&client->modeset_mutex); +	kfree(client->modesets); +} + +struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) +{ +	struct drm_mode_set *modeset; + +	drm_client_for_each_modeset(modeset, client) +		if (modeset->crtc == crtc) +			return modeset; + +	return NULL; +} +/* TODO: Remove export when modeset code has been moved over */ +EXPORT_SYMBOL(drm_client_find_modeset); diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index d5d34d0c79c7..4ce5c6d8de99 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -20,9 +20,13 @@   * OF THIS SOFTWARE.   */ -#include <drm/drmP.h> -#include <drm/drm_crtc.h> +#include <linux/uaccess.h> +  #include <drm/drm_color_mgmt.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_print.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index c9ac8b9e83ea..e17586aaa80f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -956,6 +956,46 @@ static const struct drm_prop_enum_list hdmi_colorspaces[] = {   *	  is no longer protected and userspace should take appropriate action   *	  (whatever that might be).   * + * HDR_OUTPUT_METADATA: + *	Connector property to enable userspace to send HDR Metadata to + *	driver. This metadata is based on the composition and blending + *	policies decided by user, taking into account the hardware and + *	sink capabilities. The driver gets this metadata and creates a + *	Dynamic Range and Mastering Infoframe (DRM) in case of HDMI, + *	SDP packet (Non-audio INFOFRAME SDP v1.3) for DP. This is then + *	sent to sink. This notifies the sink of the upcoming frame's Color + *	Encoding and Luminance parameters. + * + *	Userspace first need to detect the HDR capabilities of sink by + *	reading and parsing the EDID. Details of HDR metadata for HDMI + *	are added in CTA 861.G spec. For DP , its defined in VESA DP + *	Standard v1.4. It needs to then get the metadata information + *	of the video/game/app content which are encoded in HDR (basically + *	using HDR transfer functions). With this information it needs to + *	decide on a blending policy and compose the relevant + *	layers/overlays into a common format. Once this blending is done, + *	userspace will be aware of the metadata of the composed frame to + *	be send to sink. It then uses this property to communicate this + *	metadata to driver which then make a Infoframe packet and sends + *	to sink based on the type of encoder connected. + * + *	Userspace will be responsible to do Tone mapping operation in case: + *		- Some layers are HDR and others are SDR + *		- HDR layers luminance is not same as sink + *	It will even need to do colorspace conversion and get all layers + *	to one common colorspace for blending. It can use either GL, Media + *	or display engine to get this done based on the capabilties of the + *	associated hardware. + * + *	Driver expects metadata to be put in &struct hdr_output_metadata + *	structure from userspace. This is received as blob and stored in + *	&drm_connector_state.hdr_output_metadata. It parses EDID and saves the + *	sink metadata in &struct hdr_sink_metadata, as + *	&drm_connector.hdr_sink_metadata.  Driver uses + *	drm_hdmi_infoframe_set_hdr_metadata() helper to set the HDR metadata, + *	hdmi_drm_infoframe_pack() to pack the infoframe as per spec, in case of + *	HDMI encoder. + *   * max bpc:   *	This range property is used by userspace to limit the bit depth. When   *	used the driver would limit the bpc in accordance with the valid range diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 6e8e1a9fcae3..1f802d8e5681 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -28,7 +28,13 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> +  #include "drm_legacy.h"  struct drm_ctx_list { diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 747661f63fbb..6dd49a60deac 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -29,21 +29,23 @@   *      Jesse Barnes <jesse.barnes@intel.com>   */ -#include <linux/kernel.h>  #include <linux/export.h> +#include <linux/kernel.h>  #include <linux/moduleparam.h> -#include <drm/drmP.h>  #include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>  #include <drm/drm_atomic_uapi.h>  #include <drm/drm_crtc.h> -#include <drm/drm_encoder.h> -#include <drm/drm_fourcc.h>  #include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder.h>  #include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h>  #include <drm/drm_plane_helper.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_edid.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h>  /**   * DOC: overview diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 0719a235d6cc..c78a44fad13d 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -31,14 +31,30 @@   * and are not exported to drivers.   */ -enum drm_mode_status; +#include <linux/types.h> + +enum drm_color_encoding; +enum drm_color_range;  enum drm_connector_force; +enum drm_mode_status; -struct drm_display_mode; -struct work_struct; -struct drm_connector; +struct drm_atomic_state;  struct drm_bridge; +struct drm_connector; +struct drm_crtc; +struct drm_device; +struct drm_display_mode; +struct drm_file; +struct drm_framebuffer; +struct drm_mode_create_dumb; +struct drm_mode_fb_cmd2; +struct drm_mode_fb_cmd; +struct drm_mode_object; +struct drm_plane; +struct drm_property;  struct edid; +struct kref; +struct work_struct;  /* drm_crtc.c */  int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, diff --git a/drivers/gpu/drm/drm_damage_helper.c b/drivers/gpu/drm/drm_damage_helper.c index ee67c96841fa..8230dac01a89 100644 --- a/drivers/gpu/drm/drm_damage_helper.c +++ b/drivers/gpu/drm/drm_damage_helper.c @@ -286,7 +286,7 @@ drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,  	iter->plane_src.y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF);  	if (!iter->clips || !drm_rect_equals(&state->src, &old_state->src)) { -		iter->clips = 0; +		iter->clips = NULL;  		iter->num_clips = 0;  		iter->full_update = true;  	} diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index f8468eae0503..4b8e817d7420 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -24,20 +24,23 @@   */  #include <linux/debugfs.h> +#include <linux/export.h>  #include <linux/seq_file.h>  #include <linux/slab.h> -#include <linux/export.h> +#include <linux/uaccess.h> +#include <drm/drm_atomic.h> +#include <drm/drm_auth.h>  #include <drm/drm_client.h>  #include <drm/drm_debugfs.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h>  #include <drm/drm_edid.h> -#include <drm/drm_atomic.h> -#include <drm/drm_auth.h> +#include <drm/drm_file.h>  #include <drm/drm_gem.h> -#include <drm/drmP.h> -#include "drm_internal.h"  #include "drm_crtc_internal.h" +#include "drm_internal.h"  #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index 00e743153e94..585169f0dcc5 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -29,7 +29,14 @@  #include <linux/circ_buf.h>  #include <linux/ctype.h>  #include <linux/debugfs.h> -#include <drm/drmP.h> +#include <linux/poll.h> +#include <linux/uaccess.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_debugfs_crc.h> +#include <drm/drm_drv.h> +#include <drm/drm_print.h> +  #include "drm_internal.h"  /** diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c index 3f83e2ca80ad..5ef0227eaa0e 100644 --- a/drivers/gpu/drm/drm_dma.c +++ b/drivers/gpu/drm/drm_dma.c @@ -34,7 +34,11 @@   */  #include <linux/export.h> -#include <drm/drmP.h> + +#include <drm/drm_drv.h> +#include <drm/drm_pci.h> +#include <drm/drm_print.h> +  #include "drm_legacy.h"  /** diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index da1abca1b9e9..0984b9a34d55 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1996,7 +1996,11 @@ static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,  	if (ret != 1)  		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); -	txmsg->dst->tx_slots[txmsg->seqno] = NULL; +	if (txmsg->seqno != -1) { +		WARN_ON((unsigned int)txmsg->seqno > +			ARRAY_SIZE(txmsg->dst->tx_slots)); +		txmsg->dst->tx_slots[txmsg->seqno] = NULL; +	}  }  static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 862621494a93..95a55d98e152 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -35,16 +35,19 @@  #include <linux/srcu.h>  #include <drm/drm_client.h> +#include <drm/drm_color_mgmt.h>  #include <drm/drm_drv.h> -#include <drm/drmP.h> +#include <drm/drm_file.h> +#include <drm/drm_mode_object.h> +#include <drm/drm_print.h>  #include "drm_crtc_internal.h" -#include "drm_legacy.h"  #include "drm_internal.h" +#include "drm_legacy.h"  /*   * drm_debug: Enable debug output. - * Bitmask of DRM_UT_x. See include/drm/drmP.h for details. + * Bitmask of DRM_UT_x. See include/drm/drm_print.h for details.   */  unsigned int drm_debug = 0;  EXPORT_SYMBOL(drm_debug); diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index 81dfdd33753a..d18a740fe0f1 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -23,8 +23,10 @@   * OF THIS SOFTWARE.   */ -#include <drm/drmP.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h>  #include <drm/drm_gem.h> +#include <drm/drm_mode.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index feb1df935a9e..d38b3b255926 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -281,6 +281,8 @@ struct edid *drm_load_edid_firmware(struct drm_connector *connector)  	 * the last one found one as a fallback.  	 */  	fwstr = kstrdup(edid_firmware, GFP_KERNEL); +	if (!fwstr) +		return ERR_PTR(-ENOMEM);  	edidstr = fwstr;  	while ((edidname = strsep(&edidstr, ","))) { diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index b694fb57eaa4..7fb47b7b8b44 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -21,7 +21,9 @@   */  #include <linux/export.h> -#include <drm/drmP.h> + +#include <drm/drm_device.h> +#include <drm/drm_drv.h>  #include <drm/drm_encoder.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 302cf5f8bcce..b9b7c06cbc4f 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -32,18 +32,23 @@  #include <linux/console.h>  #include <linux/dma-buf.h>  #include <linux/kernel.h> -#include <linux/sysrq.h> -#include <linux/slab.h>  #include <linux/module.h> -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_crtc_helper.h> +#include <linux/slab.h> +#include <linux/sysrq.h> +#include <linux/vmalloc.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> -#include "drm_crtc_internal.h"  #include "drm_crtc_helper_internal.h" +#include "drm_crtc_internal.h"  #include "drm_internal.h"  static bool drm_fbdev_emulation = true; @@ -317,13 +322,11 @@ int drm_fb_helper_debug_enter(struct fb_info *info)  {  	struct drm_fb_helper *helper = info->par;  	const struct drm_crtc_helper_funcs *funcs; -	int i; +	struct drm_mode_set *mode_set;  	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { -		for (i = 0; i < helper->crtc_count; i++) { -			struct drm_mode_set *mode_set = -				&helper->crtc_info[i].mode_set; - +		mutex_lock(&helper->client.modeset_mutex); +		drm_client_for_each_modeset(mode_set, &helper->client) {  			if (!mode_set->crtc->enabled)  				continue; @@ -340,6 +343,7 @@ int drm_fb_helper_debug_enter(struct fb_info *info)  						    mode_set->y,  						    ENTER_ATOMIC_MODE_SET);  		} +		mutex_unlock(&helper->client.modeset_mutex);  	}  	return 0; @@ -353,14 +357,14 @@ EXPORT_SYMBOL(drm_fb_helper_debug_enter);  int drm_fb_helper_debug_leave(struct fb_info *info)  {  	struct drm_fb_helper *helper = info->par; +	struct drm_client_dev *client = &helper->client;  	struct drm_crtc *crtc;  	const struct drm_crtc_helper_funcs *funcs; +	struct drm_mode_set *mode_set;  	struct drm_framebuffer *fb; -	int i; - -	for (i = 0; i < helper->crtc_count; i++) { -		struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; +	mutex_lock(&client->modeset_mutex); +	drm_client_for_each_modeset(mode_set, client) {  		crtc = mode_set->crtc;  		if (drm_drv_uses_atomic_modeset(crtc->dev))  			continue; @@ -383,6 +387,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info)  		funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,  					    crtc->y, LEAVE_ATOMIC_MODE_SET);  	} +	mutex_unlock(&client->modeset_mutex);  	return 0;  } @@ -433,12 +438,14 @@ static bool drm_fb_helper_panel_rotation(struct drm_mode_set *modeset,  static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active)  { +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_device *dev = fb_helper->dev;  	struct drm_plane_state *plane_state;  	struct drm_plane *plane;  	struct drm_atomic_state *state; -	int i, ret;  	struct drm_modeset_acquire_ctx ctx; +	struct drm_mode_set *mode_set; +	int ret;  	drm_modeset_acquire_init(&ctx, 0); @@ -468,8 +475,7 @@ retry:  			goto out_state;  	} -	for (i = 0; i < fb_helper->crtc_count; i++) { -		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; +	drm_client_for_each_modeset(mode_set, client) {  		struct drm_plane *primary = mode_set->crtc->primary;  		unsigned int rotation; @@ -517,9 +523,11 @@ backoff:  static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)  { +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_device *dev = fb_helper->dev; +	struct drm_mode_set *mode_set;  	struct drm_plane *plane; -	int i, ret = 0; +	int ret = 0;  	drm_modeset_lock_all(fb_helper->dev);  	drm_for_each_plane(plane, dev) { @@ -532,8 +540,7 @@ static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)  						    DRM_MODE_ROTATE_0);  	} -	for (i = 0; i < fb_helper->crtc_count; i++) { -		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; +	drm_client_for_each_modeset(mode_set, client) {  		struct drm_crtc *crtc = mode_set->crtc;  		if (crtc->funcs->cursor_set2) { @@ -559,11 +566,16 @@ out:  static int restore_fbdev_mode_force(struct drm_fb_helper *fb_helper)  {  	struct drm_device *dev = fb_helper->dev; +	int ret; +	mutex_lock(&fb_helper->client.modeset_mutex);  	if (drm_drv_uses_atomic_modeset(dev)) -		return restore_fbdev_mode_atomic(fb_helper, true); +		ret = restore_fbdev_mode_atomic(fb_helper, true);  	else -		return restore_fbdev_mode_legacy(fb_helper); +		ret = restore_fbdev_mode_legacy(fb_helper); +	mutex_unlock(&fb_helper->client.modeset_mutex); + +	return ret;  }  static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) @@ -682,15 +694,14 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };  static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)  { +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_device *dev = fb_helper->dev;  	struct drm_connector *connector;  	struct drm_mode_set *modeset; -	int i, j; +	int j;  	drm_modeset_lock_all(dev); -	for (i = 0; i < fb_helper->crtc_count; i++) { -		modeset = &fb_helper->crtc_info[i].mode_set; - +	drm_client_for_each_modeset(modeset, client) {  		if (!modeset->crtc->enabled)  			continue; @@ -707,6 +718,7 @@ static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)  static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)  {  	struct drm_fb_helper *fb_helper = info->par; +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_device *dev = fb_helper->dev;  	/* @@ -716,10 +728,12 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)  	if (!drm_master_internal_acquire(dev))  		goto unlock; +	mutex_lock(&client->modeset_mutex);  	if (drm_drv_uses_atomic_modeset(dev))  		restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON);  	else  		dpms_legacy(fb_helper, dpms_mode); +	mutex_unlock(&client->modeset_mutex);  	drm_master_internal_release(dev);  unlock: @@ -762,43 +776,6 @@ int drm_fb_helper_blank(int blank, struct fb_info *info)  }  EXPORT_SYMBOL(drm_fb_helper_blank); -static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper, -					  struct drm_mode_set *modeset) -{ -	int i; - -	for (i = 0; i < modeset->num_connectors; i++) { -		drm_connector_put(modeset->connectors[i]); -		modeset->connectors[i] = NULL; -	} -	modeset->num_connectors = 0; - -	drm_mode_destroy(helper->dev, modeset->mode); -	modeset->mode = NULL; - -	/* FIXME should hold a ref? */ -	modeset->fb = NULL; -} - -static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) -{ -	int i; - -	for (i = 0; i < helper->connector_count; i++) { -		drm_connector_put(helper->connector_info[i]->connector); -		kfree(helper->connector_info[i]); -	} -	kfree(helper->connector_info); - -	for (i = 0; i < helper->crtc_count; i++) { -		struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set; - -		drm_fb_helper_modeset_release(helper, modeset); -		kfree(modeset->connectors); -	} -	kfree(helper->crtc_info); -} -  static void drm_fb_helper_resume_worker(struct work_struct *work)  {  	struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, @@ -877,7 +854,7 @@ EXPORT_SYMBOL(drm_fb_helper_prepare);   * drm_fb_helper_init - initialize a &struct drm_fb_helper   * @dev: drm device   * @fb_helper: driver-allocated fbdev helper structure to initialize - * @max_conn_count: max connector count + * @max_conn_count: max connector count (not used)   *   * This allocates the structures for the fbdev helper with the given limits.   * Note that this won't yet touch the hardware (through the driver interfaces) @@ -893,53 +870,36 @@ int drm_fb_helper_init(struct drm_device *dev,  		       struct drm_fb_helper *fb_helper,  		       int max_conn_count)  { -	struct drm_crtc *crtc; -	struct drm_mode_config *config = &dev->mode_config; -	int i; +	int ret;  	if (!drm_fbdev_emulation) {  		dev->fb_helper = fb_helper;  		return 0;  	} -	if (!max_conn_count) -		return -EINVAL; - -	fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); -	if (!fb_helper->crtc_info) -		return -ENOMEM; +	/* +	 * If this is not the generic fbdev client, initialize a drm_client +	 * without callbacks so we can use the modesets. +	 */ +	if (!fb_helper->client.funcs) { +		ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); +		if (ret) +			return ret; +	} -	fb_helper->crtc_count = config->num_crtc;  	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); -	if (!fb_helper->connector_info) { -		kfree(fb_helper->crtc_info); -		return -ENOMEM; -	} +	if (!fb_helper->connector_info) +		goto out_free; +  	fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;  	fb_helper->connector_count = 0; -	for (i = 0; i < fb_helper->crtc_count; i++) { -		fb_helper->crtc_info[i].mode_set.connectors = -			kcalloc(max_conn_count, -				sizeof(struct drm_connector *), -				GFP_KERNEL); - -		if (!fb_helper->crtc_info[i].mode_set.connectors) -			goto out_free; -		fb_helper->crtc_info[i].mode_set.num_connectors = 0; -	} - -	i = 0; -	drm_for_each_crtc(crtc, dev) { -		fb_helper->crtc_info[i].mode_set.crtc = crtc; -		i++; -	} -  	dev->fb_helper = fb_helper;  	return 0;  out_free: -	drm_fb_helper_crtc_free(fb_helper); +	drm_client_release(&fb_helper->client); +  	return -ENOMEM;  }  EXPORT_SYMBOL(drm_fb_helper_init); @@ -1015,6 +975,7 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);  void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)  {  	struct fb_info *info; +	int i;  	if (!fb_helper)  		return; @@ -1044,8 +1005,15 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)  	mutex_unlock(&kernel_fb_helper_lock);  	mutex_destroy(&fb_helper->lock); -	drm_fb_helper_crtc_free(fb_helper); +	if (!fb_helper->client.funcs) +		drm_client_release(&fb_helper->client); + +	for (i = 0; i < fb_helper->connector_count; i++) { +		drm_connector_put(fb_helper->connector_info[i]->connector); +		kfree(fb_helper->connector_info[i]); +	} +	kfree(fb_helper->connector_info);  }  EXPORT_SYMBOL(drm_fb_helper_fini); @@ -1390,13 +1358,14 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)  static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)  {  	struct drm_fb_helper *fb_helper = info->par; +	struct drm_mode_set *modeset;  	struct drm_crtc *crtc;  	u16 *r, *g, *b; -	int i, ret = 0; +	int ret = 0;  	drm_modeset_lock_all(fb_helper->dev); -	for (i = 0; i < fb_helper->crtc_count; i++) { -		crtc = fb_helper->crtc_info[i].mode_set.crtc; +	drm_client_for_each_modeset(modeset, &fb_helper->client) { +		crtc = modeset->crtc;  		if (!crtc->funcs->gamma_set || !crtc->gamma_size)  			return -EINVAL; @@ -1472,10 +1441,11 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)  	struct drm_modeset_acquire_ctx ctx;  	struct drm_crtc_state *crtc_state;  	struct drm_atomic_state *state; +	struct drm_mode_set *modeset;  	struct drm_crtc *crtc;  	u16 *r, *g, *b; -	int i, ret = 0;  	bool replaced; +	int ret = 0;  	drm_modeset_acquire_init(&ctx, 0); @@ -1487,8 +1457,8 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)  	state->acquire_ctx = &ctx;  retry: -	for (i = 0; i < fb_helper->crtc_count; i++) { -		crtc = fb_helper->crtc_info[i].mode_set.crtc; +	drm_client_for_each_modeset(modeset, &fb_helper->client) { +		crtc = modeset->crtc;  		if (!gamma_lut)  			gamma_lut = setcmap_new_gamma_lut(crtc, cmap); @@ -1516,8 +1486,8 @@ retry:  	if (ret)  		goto out_state; -	for (i = 0; i < fb_helper->crtc_count; i++) { -		crtc = fb_helper->crtc_info[i].mode_set.crtc; +	drm_client_for_each_modeset(modeset, &fb_helper->client) { +		crtc = modeset->crtc;  		r = crtc->gamma_store;  		g = r + crtc->gamma_size; @@ -1567,12 +1537,14 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)  		goto unlock;  	} +	mutex_lock(&fb_helper->client.modeset_mutex);  	if (info->fix.visual == FB_VISUAL_TRUECOLOR)  		ret = setcmap_pseudo_palette(cmap, info);  	else if (drm_drv_uses_atomic_modeset(fb_helper->dev))  		ret = setcmap_atomic(cmap, info);  	else  		ret = setcmap_legacy(cmap, info); +	mutex_unlock(&fb_helper->client.modeset_mutex);  	drm_master_internal_release(dev);  unlock: @@ -1596,7 +1568,6 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,  {  	struct drm_fb_helper *fb_helper = info->par;  	struct drm_device *dev = fb_helper->dev; -	struct drm_mode_set *mode_set;  	struct drm_crtc *crtc;  	int ret = 0; @@ -1624,8 +1595,7 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,  		 * make. If we're not smart enough here, one should  		 * just consider switch the userspace to KMS.  		 */ -		mode_set = &fb_helper->crtc_info[0].mode_set; -		crtc = mode_set->crtc; +		crtc = fb_helper->client.modesets[0].crtc;  		/*  		 * Only wait for a vblank event if the CRTC is @@ -1822,16 +1792,14 @@ EXPORT_SYMBOL(drm_fb_helper_set_par);  static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)  { -	int i; - -	for (i = 0; i < fb_helper->crtc_count; i++) { -		struct drm_mode_set *mode_set; - -		mode_set = &fb_helper->crtc_info[i].mode_set; +	struct drm_mode_set *mode_set; +	mutex_lock(&fb_helper->client.modeset_mutex); +	drm_client_for_each_modeset(mode_set, &fb_helper->client) {  		mode_set->x = x;  		mode_set->y = y;  	} +	mutex_unlock(&fb_helper->client.modeset_mutex);  }  static int pan_display_atomic(struct fb_var_screeninfo *var, @@ -1842,7 +1810,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var,  	pan_set(fb_helper, var->xoffset, var->yoffset); -	ret = restore_fbdev_mode_atomic(fb_helper, true); +	ret = restore_fbdev_mode_force(fb_helper);  	if (!ret) {  		info->var.xoffset = var->xoffset;  		info->var.yoffset = var->yoffset; @@ -1856,14 +1824,13 @@ static int pan_display_legacy(struct fb_var_screeninfo *var,  			      struct fb_info *info)  {  	struct drm_fb_helper *fb_helper = info->par; +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_mode_set *modeset;  	int ret = 0; -	int i;  	drm_modeset_lock_all(fb_helper->dev); -	for (i = 0; i < fb_helper->crtc_count; i++) { -		modeset = &fb_helper->crtc_info[i].mode_set; - +	mutex_lock(&client->modeset_mutex); +	drm_client_for_each_modeset(modeset, client) {  		modeset->x = var->xoffset;  		modeset->y = var->yoffset; @@ -1875,6 +1842,7 @@ static int pan_display_legacy(struct fb_var_screeninfo *var,  			}  		}  	} +	mutex_unlock(&client->modeset_mutex);  	drm_modeset_unlock_all(fb_helper->dev);  	return ret; @@ -1921,10 +1889,12 @@ EXPORT_SYMBOL(drm_fb_helper_pan_display);  static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,  					 int preferred_bpp)  { +	struct drm_client_dev *client = &fb_helper->client;  	int ret = 0;  	int crtc_count = 0;  	int i;  	struct drm_fb_helper_surface_size sizes; +	struct drm_mode_set *mode_set;  	int best_depth = 0;  	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); @@ -1975,13 +1945,13 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,  	 * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth  	 * 16) we need to scale down the depth of the sizes we request.  	 */ -	for (i = 0; i < fb_helper->crtc_count; i++) { -		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; +	mutex_lock(&client->modeset_mutex); +	drm_client_for_each_modeset(mode_set, client) {  		struct drm_crtc *crtc = mode_set->crtc;  		struct drm_plane *plane = crtc->primary;  		int j; -		DRM_DEBUG("test CRTC %d primary plane\n", i); +		DRM_DEBUG("test CRTC %u primary plane\n", drm_crtc_index(crtc));  		for (j = 0; j < plane->format_count; j++) {  			const struct drm_format_info *fmt; @@ -2021,9 +1991,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,  	/* first up get a count of crtcs now in use and new min/maxes width/heights */  	crtc_count = 0; -	for (i = 0; i < fb_helper->crtc_count; i++) { +	drm_client_for_each_modeset(mode_set, client) {  		struct drm_display_mode *desired_mode; -		struct drm_mode_set *mode_set;  		int x, y, j;  		/* in case of tile group, are we the last tile vert or horiz?  		 * If no tile group you are always the last one both vertically @@ -2031,7 +2000,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,  		 */  		bool lastv = true, lasth = true; -		mode_set = &fb_helper->crtc_info[i].mode_set;  		desired_mode = mode_set->mode;  		if (!desired_mode) @@ -2061,6 +2029,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,  		if (lastv)  			sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);  	} +	mutex_unlock(&client->modeset_mutex);  	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {  		DRM_INFO("Cannot find any crtc or sizes\n"); @@ -2292,7 +2261,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,  	struct drm_display_mode *dmt_mode, *mode;  	/* only contemplate cloning in the single crtc case */ -	if (fb_helper->crtc_count > 1) +	if (fb_helper->dev->mode_config.num_crtc > 1)  		return false;  	count = 0; @@ -2481,15 +2450,17 @@ static bool connector_has_possible_crtc(struct drm_connector *connector,  }  static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, -			  struct drm_fb_helper_crtc **best_crtcs, +			  struct drm_crtc **best_crtcs,  			  struct drm_display_mode **modes,  			  int n, int width, int height)  { -	int c, o; +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_connector *connector;  	int my_score, best_score, score; -	struct drm_fb_helper_crtc **crtcs, *crtc; +	struct drm_crtc **crtcs, *crtc; +	struct drm_mode_set *modeset;  	struct drm_fb_helper_connector *fb_helper_conn; +	int o;  	if (n == fb_helper->connector_count)  		return 0; @@ -2502,8 +2473,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,  	if (modes[n] == NULL)  		return best_score; -	crtcs = kcalloc(fb_helper->connector_count, -			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); +	crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);  	if (!crtcs)  		return best_score; @@ -2519,11 +2489,10 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,  	 * select a crtc for this connector and then attempt to configure  	 * remaining connectors  	 */ -	for (c = 0; c < fb_helper->crtc_count; c++) { -		crtc = &fb_helper->crtc_info[c]; +	drm_client_for_each_modeset(modeset, client) { +		crtc = modeset->crtc; -		if (!connector_has_possible_crtc(connector, -						 crtc->mode_set.crtc)) +		if (!connector_has_possible_crtc(connector, crtc))  			continue;  		for (o = 0; o < n; o++) @@ -2532,7 +2501,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,  		if (o < n) {  			/* ignore cloning unless only a single crtc */ -			if (fb_helper->crtc_count > 1) +			if (fb_helper->dev->mode_config.num_crtc > 1)  				continue;  			if (!drm_mode_equal(modes[o], modes[n])) @@ -2540,14 +2509,13 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,  		}  		crtcs[n] = crtc; -		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); +		memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));  		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,  						  width, height);  		if (score > best_score) {  			best_score = score;  			memcpy(best_crtcs, crtcs, -			       fb_helper->connector_count * -			       sizeof(struct drm_fb_helper_crtc *)); +			       fb_helper->connector_count * sizeof(*crtcs));  		}  	} @@ -2555,21 +2523,9 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,  	return best_score;  } -static struct drm_fb_helper_crtc * -drm_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) -{ -	int i; - -	for (i = 0; i < fb_helper->crtc_count; i++) -		if (fb_helper->crtc_info[i].mode_set.crtc == crtc) -			return &fb_helper->crtc_info[i]; - -	return NULL; -} -  /* Try to read the BIOS display configuration and use it for the initial config */  static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, -					  struct drm_fb_helper_crtc **crtcs, +					  struct drm_crtc **crtcs,  					  struct drm_display_mode **modes,  					  struct drm_fb_offset *offsets,  					  bool *enabled, int width, int height) @@ -2605,7 +2561,7 @@ retry:  		struct drm_fb_helper_connector *fb_conn;  		struct drm_connector *connector;  		struct drm_encoder *encoder; -		struct drm_fb_helper_crtc *new_crtc; +		struct drm_crtc *new_crtc;  		fb_conn = fb_helper->connector_info[i];  		connector = fb_conn->connector; @@ -2647,7 +2603,7 @@ retry:  		num_connectors_enabled++; -		new_crtc = drm_fb_helper_crtc(fb_helper, connector->state->crtc); +		new_crtc = connector->state->crtc;  		/*  		 * Make sure we're not trying to drive multiple connectors @@ -2747,10 +2703,11 @@ bail:  static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,  			    u32 width, u32 height)  { +	struct drm_client_dev *client = &fb_helper->client;  	struct drm_device *dev = fb_helper->dev; -	struct drm_fb_helper_crtc **crtcs;  	struct drm_display_mode **modes;  	struct drm_fb_offset *offsets; +	struct drm_crtc **crtcs;  	bool *enabled;  	int i; @@ -2758,8 +2715,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,  	/* prevent concurrent modification of connector_count by hotplug */  	lockdep_assert_held(&fb_helper->lock); -	crtcs = kcalloc(fb_helper->connector_count, -			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); +	crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);  	modes = kcalloc(fb_helper->connector_count,  			sizeof(struct drm_display_mode *), GFP_KERNEL);  	offsets = kcalloc(fb_helper->connector_count, @@ -2771,6 +2727,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,  		goto out;  	} +	mutex_lock(&client->modeset_mutex); +  	mutex_lock(&fb_helper->dev->mode_config.mutex);  	if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)  		DRM_DEBUG_KMS("No connectors reported connected with modes\n"); @@ -2795,24 +2753,24 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,  	}  	mutex_unlock(&fb_helper->dev->mode_config.mutex); -	/* need to set the modesets up here for use later */ -	/* fill out the connector<->crtc mappings into the modesets */ -	for (i = 0; i < fb_helper->crtc_count; i++) -		drm_fb_helper_modeset_release(fb_helper, -					      &fb_helper->crtc_info[i].mode_set); +	drm_client_modeset_release(client);  	drm_fb_helper_for_each_connector(fb_helper, i) {  		struct drm_display_mode *mode = modes[i]; -		struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; +		struct drm_crtc *crtc = crtcs[i];  		struct drm_fb_offset *offset = &offsets[i]; -		if (mode && fb_crtc) { -			struct drm_mode_set *modeset = &fb_crtc->mode_set; +		if (mode && crtc) { +			struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc);  			struct drm_connector *connector =  				fb_helper->connector_info[i]->connector;  			DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", -				      mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); +				      mode->name, crtc->base.id, offset->x, offset->y); + +			if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || +					 (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) +				break;  			modeset->mode = drm_mode_duplicate(dev, mode);  			drm_connector_get(connector); @@ -2821,6 +2779,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,  			modeset->y = offset->y;  		}  	} + +	mutex_unlock(&client->modeset_mutex);  out:  	kfree(crtcs);  	kfree(modes); @@ -2837,13 +2797,14 @@ out:   */  static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)  { +	struct drm_client_dev *client = &fb_helper->client;  	struct fb_info *info = fb_helper->fbdev;  	unsigned int rotation, sw_rotations = 0; +	struct drm_mode_set *modeset;  	int i; -	for (i = 0; i < fb_helper->crtc_count; i++) { -		struct drm_mode_set *modeset = &fb_helper->crtc_info[i].mode_set; - +	mutex_lock(&client->modeset_mutex); +	drm_client_for_each_modeset(modeset, client) {  		if (!modeset->num_connectors)  			continue; @@ -2855,6 +2816,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)  		else  			sw_rotations |= rotation;  	} +	mutex_unlock(&client->modeset_mutex);  	mutex_lock(&fb_helper->dev->mode_config.mutex);  	drm_fb_helper_for_each_connector(fb_helper, i) { @@ -3070,8 +3032,7 @@ EXPORT_SYMBOL(drm_fb_helper_hotplug_event);   * @funcs: fbdev helper functions   * @preferred_bpp: Preferred bits per pixel for the device.   *                 @dev->mode_config.preferred_depth is used if this is zero. - * @max_conn_count: Maximum number of connectors. - *                  @dev->mode_config.num_connector is used if this is zero. + * @max_conn_count: Maximum number of connectors (not used)   *   * This function sets up fbdev emulation and registers fbdev for access by   * userspace. If all connectors are disconnected, setup is deferred to the next @@ -3099,16 +3060,9 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev,  	if (!preferred_bpp)  		preferred_bpp = 32; -	if (!max_conn_count) -		max_conn_count = dev->mode_config.num_connector; -	if (!max_conn_count) { -		DRM_DEV_ERROR(dev->dev, "fbdev: No connectors\n"); -		return -EINVAL; -	} -  	drm_fb_helper_prepare(dev, fb_helper, funcs); -	ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); +	ret = drm_fb_helper_init(dev, fb_helper, 0);  	if (ret < 0) {  		DRM_DEV_ERROR(dev->dev, "fbdev: Failed to initialize (ret=%d)\n", ret);  		return ret; @@ -3421,7 +3375,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client)  	drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); -	ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); +	ret = drm_fb_helper_init(dev, fb_helper, 0);  	if (ret)  		goto err; diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 075a7766bb79..754af25fe255 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -31,17 +31,20 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ +#include <linux/dma-fence.h> +#include <linux/module.h> +#include <linux/pci.h>  #include <linux/poll.h>  #include <linux/slab.h> -#include <linux/module.h>  #include <drm/drm_client.h> +#include <drm/drm_drv.h>  #include <drm/drm_file.h> -#include <drm/drmP.h> +#include <drm/drm_print.h> -#include "drm_legacy.h" -#include "drm_internal.h"  #include "drm_crtc_internal.h" +#include "drm_internal.h" +#include "drm_legacy.h"  /* from BKL pushdown */  DEFINE_MUTEX(drm_global_mutex); diff --git a/drivers/gpu/drm/drm_flip_work.c b/drivers/gpu/drm/drm_flip_work.c index 3da3bf5af405..060b753881a2 100644 --- a/drivers/gpu/drm/drm_flip_work.c +++ b/drivers/gpu/drm/drm_flip_work.c @@ -21,9 +21,11 @@   * SOFTWARE.   */ -#include <drm/drmP.h> -#include <drm/drm_util.h> +#include <linux/slab.h> +  #include <drm/drm_flip_work.h> +#include <drm/drm_print.h> +#include <drm/drm_util.h>  /**   * drm_flip_work_allocate_task - allocate a flip-work task diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 35b459d186c5..c630064ccf41 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -27,7 +27,7 @@  #include <linux/export.h>  #include <linux/kernel.h> -#include <drm/drmP.h> +#include <drm/drm_device.h>  #include <drm/drm_fourcc.h>  static char printable_char(int c) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index d8d75e25f6fb..0b72468e8131 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -21,16 +21,21 @@   */  #include <linux/export.h> -#include <drm/drmP.h> -#include <drm/drm_auth.h> -#include <drm/drm_framebuffer.h> +#include <linux/uaccess.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_uapi.h> +#include <drm/drm_auth.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h>  #include <drm/drm_print.h>  #include <drm/drm_util.h> -#include "drm_internal.h"  #include "drm_crtc_internal.h" +#include "drm_internal.h"  /**   * DOC: overview diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 50de138c89e0..8a55f71325b1 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -38,10 +38,14 @@  #include <linux/dma-buf.h>  #include <linux/mem_encrypt.h>  #include <linux/pagevec.h> -#include <drm/drmP.h> -#include <drm/drm_vma_manager.h> + +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h>  #include <drm/drm_gem.h>  #include <drm/drm_print.h> +#include <drm/drm_vma_manager.h> +  #include "drm_internal.h"  /** @file drm_gem.c diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index e01ceed09e67..0cb1bbed1f68 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -17,15 +17,16 @@   * GNU General Public License for more details.   */ -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/export.h>  #include <linux/dma-buf.h>  #include <linux/dma-mapping.h> +#include <linux/export.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <linux/slab.h> -#include <drm/drmP.h>  #include <drm/drm.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h>  #include <drm/drm_gem_cma_helper.h>  #include <drm/drm_vma_manager.h> diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 65edb1ccb185..6791245963c3 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -14,7 +14,6 @@  #include <linux/reservation.h>  #include <linux/slab.h> -#include <drm/drmP.h>  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_uapi.h>  #include <drm/drm_damage_helper.h> @@ -285,6 +284,9 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);   * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple   * gem based framebuffer drivers which have their buffers always pinned in   * memory. + * + * See drm_atomic_set_fence_for_plane() for a discussion of implicit and + * explicit fencing in atomic modeset updates.   */  int drm_gem_fb_prepare_fb(struct drm_plane *plane,  			  struct drm_plane_state *state) @@ -315,6 +317,9 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);   * &dma_buf attached, extracts the exclusive fence and attaches it to plane   * state for the atomic helper to wait on. Drivers can use this as their   * &drm_simple_display_pipe_funcs.prepare_fb callback. + * + * See drm_atomic_set_fence_for_plane() for a discussion of implicit and + * explicit fencing in atomic modeset updates.   */  int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,  					      struct drm_plane_state *plane_state) diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index c92b00d42ece..c50fa6f0709f 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -32,11 +32,15 @@   * Thomas Hellström <thomas-at-tungstengraphics-dot-com>   */ -#include <drm/drmP.h> -#include <drm/drm_hashtab.h> +#include <linux/export.h>  #include <linux/hash.h> +#include <linux/mm.h> +#include <linux/rculist.h>  #include <linux/slab.h> -#include <linux/export.h> +#include <linux/vmalloc.h> + +#include <drm/drm_hashtab.h> +#include <drm/drm_print.h>  int drm_ht_create(struct drm_open_hash *ht, unsigned int order)  { diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index e6a900c7c1fc..596e9f4ca7fc 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -28,8 +28,16 @@  #define DRM_IF_VERSION(maj, min) (maj << 16 | min) -struct drm_prime_file_private; +struct dentry;  struct dma_buf; +struct drm_connector; +struct drm_crtc; +struct drm_framebuffer; +struct drm_gem_object; +struct drm_master; +struct drm_minor; +struct drm_prime_file_private; +struct drm_printer;  /* drm_file.c */  extern struct mutex drm_global_mutex; diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index 374b372da58a..586aa28024c5 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -31,10 +31,13 @@  #include <linux/ratelimit.h>  #include <linux/export.h> -#include <drm/drmP.h> -#include "drm_legacy.h" -#include "drm_internal.h" +#include <drm/drm_agpsupport.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> +  #include "drm_crtc_internal.h" +#include "drm_internal.h" +#include "drm_legacy.h"  #define DRM_IOCTL_VERSION32		DRM_IOWR(0x00, drm_version32_t)  #define DRM_IOCTL_GET_UNIQUE32		DRM_IOWR(0x01, drm_unique32_t) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 2263e3ddd822..9441a36a2469 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -28,16 +28,22 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drm_ioctl.h> -#include <drm/drmP.h> -#include <drm/drm_auth.h> -#include "drm_legacy.h" -#include "drm_internal.h" -#include "drm_crtc_internal.h" - -#include <linux/pci.h>  #include <linux/export.h>  #include <linux/nospec.h> +#include <linux/pci.h> +#include <linux/uaccess.h> + +#include <drm/drm_agpsupport.h> +#include <drm/drm_auth.h> +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_print.h> + +#include "drm_crtc_internal.h" +#include "drm_internal.h" +#include "drm_legacy.h"  /**   * DOC: getunique and setversion story diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 02f38cc9f468..03bce566a8c3 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -51,13 +51,18 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drm_irq.h> -#include <drm/drmP.h> +#include <linux/export.h>  #include <linux/interrupt.h>	/* For task queue support */ - +#include <linux/pci.h>  #include <linux/vgaarb.h> -#include <linux/export.h> + +#include <drm/drm.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_irq.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h>  #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 9c5ae825c507..d9a5ac81949e 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -26,7 +26,8 @@   */  #include <linux/module.h> -#include <drm/drmP.h> + +#include <drm/drm_print.h>  #include "drm_crtc_helper_internal.h" diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index 694ff363a90b..849da9414450 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -11,14 +11,19 @@   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * General Public License for more details.   */ +#include <linux/file.h> +#include <linux/uaccess.h> -#include <drm/drmP.h> -#include "drm_internal.h" -#include "drm_legacy.h" -#include "drm_crtc_internal.h" -#include <drm/drm_lease.h>  #include <drm/drm_auth.h>  #include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_lease.h> +#include <drm/drm_print.h> + +#include "drm_crtc_internal.h" +#include "drm_internal.h" +#include "drm_legacy.h"  #define drm_for_each_lessee(lessee, lessor) \  	list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h index 013ccdfd90be..1be3ea320474 100644 --- a/drivers/gpu/drm/drm_legacy.h +++ b/drivers/gpu/drm/drm_legacy.h @@ -29,11 +29,15 @@   * drivers use them, and removing them are API breaks.   */  #include <linux/list.h> + +#include <drm/drm.h> +#include <drm/drm_device.h>  #include <drm/drm_legacy.h>  struct agp_memory;  struct drm_device;  struct drm_file; +struct drm_buf_desc;  /*   * Generic DRM Contexts diff --git a/drivers/gpu/drm/drm_legacy_misc.c b/drivers/gpu/drm/drm_legacy_misc.c index 18d05a6c12b3..4d3a11cfd979 100644 --- a/drivers/gpu/drm/drm_legacy_misc.c +++ b/drivers/gpu/drm/drm_legacy_misc.c @@ -33,7 +33,12 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h> +#include <drm/drm_agpsupport.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_irq.h> +#include <drm/drm_print.h> +  #include "drm_internal.h"  #include "drm_legacy.h" diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index b70058e77a28..68b18b0e290c 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -36,9 +36,13 @@  #include <linux/export.h>  #include <linux/sched/signal.h> -#include <drm/drmP.h> -#include "drm_legacy.h" +#include <drm/drm.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> +  #include "drm_internal.h" +#include "drm_legacy.h"  static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c index 132fef8ff1b6..b634e1670190 100644 --- a/drivers/gpu/drm/drm_memory.c +++ b/drivers/gpu/drm/drm_memory.c @@ -33,10 +33,15 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <linux/highmem.h>  #include <linux/export.h> +#include <linux/highmem.h> +#include <linux/pci.h> +#include <linux/vmalloc.h>  #include <xen/xen.h> -#include <drm/drmP.h> + +#include <drm/drm_agpsupport.h> +#include <drm/drm_device.h> +  #include "drm_legacy.h"  #if IS_ENABLED(CONFIG_AGP) diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 8b4cd31ce7bd..9a59865ce574 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -42,12 +42,13 @@   * Thomas Hellström <thomas-at-tungstengraphics-dot-com>   */ -#include <drm/drmP.h> -#include <drm/drm_mm.h> -#include <linux/slab.h> -#include <linux/seq_file.h>  #include <linux/export.h>  #include <linux/interval_tree_generic.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/stacktrace.h> + +#include <drm/drm_mm.h>  /**   * DOC: Overview diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 1a346ae1599d..7bc03c3c154f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -20,9 +20,13 @@   * OF THIS SOFTWARE.   */ +#include <linux/uaccess.h> + +#include <drm/drm_drv.h>  #include <drm/drm_encoder.h> +#include <drm/drm_file.h>  #include <drm/drm_mode_config.h> -#include <drm/drmP.h> +#include <drm/drm_print.h>  #include "drm_crtc_internal.h"  #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index f32507e65b79..1c6e51135962 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -21,9 +21,14 @@   */  #include <linux/export.h> -#include <drm/drmP.h> -#include <drm/drm_mode_object.h> +#include <linux/uaccess.h> +  #include <drm/drm_atomic.h> +#include <drm/drm_drv.h> +#include <drm/drm_device.h> +#include <drm/drm_file.h> +#include <drm/drm_mode_object.h> +#include <drm/drm_print.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 56f92a0bba62..5a07a28fec6d 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -33,11 +33,14 @@  #include <linux/list.h>  #include <linux/list_sort.h>  #include <linux/export.h> -#include <drm/drmP.h> -#include <drm/drm_crtc.h> +  #include <video/of_videomode.h>  #include <video/videomode.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_modes.h> +#include <drm/drm_print.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 81dd11901ffd..53187821df01 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -21,9 +21,9 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h>  #include <drm/drm_atomic.h>  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_modeset_lock.h>  /** diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 6becf63f9166..43d89dd59c6b 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -3,12 +3,13 @@  #include <linux/export.h>  #include <linux/list.h>  #include <linux/of_graph.h> -#include <drm/drmP.h> +  #include <drm/drm_bridge.h>  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_encoder.h> -#include <drm/drm_panel.h>  #include <drm/drm_of.h> +#include <drm/drm_panel.h>  /**   * DOC: overview diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 693748ad8b88..a86a3ab2771c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -22,12 +22,17 @@   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.   */ -#include <linux/pci.h> -#include <linux/slab.h>  #include <linux/dma-mapping.h>  #include <linux/export.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include <drm/drm.h> +#include <drm/drm_agpsupport.h> +#include <drm/drm_drv.h>  #include <drm/drm_pci.h> -#include <drm/drmP.h> +#include <drm/drm_print.h> +  #include "drm_internal.h"  #include "drm_legacy.h" diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 0fff72dcd06d..3aae7ea522f2 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -24,14 +24,15 @@   */  #include <linux/list.h> -#include <drm/drmP.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_rect.h> +  #include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>  #include <drm/drm_atomic_uapi.h>  #include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h>  #include <drm/drm_encoder.h> -#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_rect.h>  #define SUBPIXEL_MASK 0xffff diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 706034fd403f..d0c01318076b 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -29,9 +29,12 @@  #include <linux/export.h>  #include <linux/dma-buf.h>  #include <linux/rbtree.h> -#include <drm/drm_prime.h> + +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_framebuffer.h>  #include <drm/drm_gem.h> -#include <drm/drmP.h> +#include <drm/drm_prime.h>  #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index f5cb0aabfe35..a17c8a14dba4 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -26,8 +26,13 @@  #define DEBUG /* for pr_debug() */  #include <stdarg.h> + +#include <linux/io.h>  #include <linux/seq_file.h> -#include <drm/drmP.h> +#include <linux/slab.h> + +#include <drm/drm.h> +#include <drm/drm_drv.h>  #include <drm/drm_print.h>  void __drm_puts_coredump(struct drm_printer *p, const char *str) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6fd08e04b323..01e243f1ea94 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -32,14 +32,15 @@  #include <linux/export.h>  #include <linux/moduleparam.h> -#include <drm/drmP.h>  #include <drm/drm_client.h>  #include <drm/drm_crtc.h> -#include <drm/drm_fourcc.h> -#include <drm/drm_fb_helper.h>  #include <drm/drm_edid.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h>  #include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h>  #include <drm/drm_probe_helper.h> +#include <drm/drm_sysfs.h>  #include "drm_crtc_helper_internal.h" @@ -574,6 +575,9 @@ static void output_poll_execute(struct work_struct *work)  	enum drm_connector_status old_status;  	bool repoll = false, changed; +	if (!dev->mode_config.poll_enabled) +		return; +  	/* Pick up any changes detected by the probe functions. */  	changed = dev->mode_config.delayed_event;  	dev->mode_config.delayed_event = false; @@ -728,7 +732,11 @@ EXPORT_SYMBOL(drm_kms_helper_poll_init);   */  void drm_kms_helper_poll_fini(struct drm_device *dev)  { -	drm_kms_helper_poll_disable(dev); +	if (!dev->mode_config.poll_enabled) +		return; + +	dev->mode_config.poll_enabled = false; +	cancel_delayed_work_sync(&dev->mode_config.output_poll_work);  }  EXPORT_SYMBOL(drm_kms_helper_poll_fini); diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index f8ec8f9c3e7a..892ce636ef72 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -21,7 +21,12 @@   */  #include <linux/export.h> -#include <drm/drmP.h> +#include <linux/uaccess.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_framebuffer.h>  #include <drm/drm_property.h>  #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 66c41b12719c..b8363aaa9032 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -24,7 +24,9 @@  #include <linux/errno.h>  #include <linux/export.h>  #include <linux/kernel.h> -#include <drm/drmP.h> + +#include <drm/drm_mode.h> +#include <drm/drm_print.h>  #include <drm/drm_rect.h>  /** diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c index bb829a115fc6..2d7790f14b0c 100644 --- a/drivers/gpu/drm/drm_scatter.c +++ b/drivers/gpu/drm/drm_scatter.c @@ -31,9 +31,14 @@   * DEALINGS IN THE SOFTWARE.   */ -#include <linux/vmalloc.h> +#include <linux/mm.h>  #include <linux/slab.h> -#include <drm/drmP.h> +#include <linux/vmalloc.h> + +#include <drm/drm.h> +#include <drm/drm_drv.h> +#include <drm/drm_print.h> +  #include "drm_legacy.h"  #define DEBUG_SCATTER 0 diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c index 870e25f1f788..311e71bbba5b 100644 --- a/drivers/gpu/drm/drm_scdc_helper.c +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -24,8 +24,8 @@  #include <linux/slab.h>  #include <linux/delay.h> +#include <drm/drm_print.h>  #include <drm/drm_scdc_helper.h> -#include <drm/drmP.h>  /**   * DOC: scdc helpers diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index a32f14cd7398..77c9645e17ed 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -7,13 +7,14 @@   * (at your option) any later version.   */ -#include <drm/drmP.h> +#include <linux/module.h> +#include <linux/slab.h> +  #include <drm/drm_atomic.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_plane_helper.h>  #include <drm/drm_probe_helper.h>  #include <drm/drm_simple_kms_helper.h> -#include <linux/slab.h>  /**   * DOC: overview diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 3d400905100b..a199c8d56b95 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -46,16 +46,21 @@   * The file takes a reference on the kref.   */ -#include <drm/drmP.h> +#include <linux/anon_inodes.h>  #include <linux/file.h>  #include <linux/fs.h> -#include <linux/anon_inodes.h> -#include <linux/sync_file.h>  #include <linux/sched/signal.h> +#include <linux/sync_file.h> +#include <linux/uaccess.h> -#include "drm_internal.h" +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_print.h>  #include <drm/drm_syncobj.h> +#include "drm_internal.h" +  struct syncobj_wait_entry {  	struct list_head node;  	struct task_struct *task; diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 18b1ac442997..acc44a55133d 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -13,13 +13,20 @@   */  #include <linux/device.h> -#include <linux/kdev_t.h> -#include <linux/gfp.h>  #include <linux/err.h>  #include <linux/export.h> - +#include <linux/gfp.h> +#include <linux/kdev_t.h> +#include <linux/slab.h> + +#include <drm/drm_connector.h> +#include <drm/drm_device.h> +#include <drm/drm_file.h> +#include <drm/drm_modes.h> +#include <drm/drm_print.h> +#include <drm/drm_property.h>  #include <drm/drm_sysfs.h> -#include <drm/drmP.h> +  #include "drm_internal.h"  #define to_drm_minor(d) dev_get_drvdata(d) diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h index baccc63db106..471eb927474b 100644 --- a/drivers/gpu/drm/drm_trace.h +++ b/drivers/gpu/drm/drm_trace.h @@ -6,6 +6,8 @@  #include <linux/types.h>  #include <linux/tracepoint.h> +struct drm_file; +  #undef TRACE_SYSTEM  #define TRACE_SYSTEM drm  #define TRACE_INCLUDE_FILE drm_trace diff --git a/drivers/gpu/drm/drm_trace_points.c b/drivers/gpu/drm/drm_trace_points.c index 3bbc4deb4dbc..1e2065b403c9 100644 --- a/drivers/gpu/drm/drm_trace_points.c +++ b/drivers/gpu/drm/drm_trace_points.c @@ -1,4 +1,5 @@ -#include <drm/drmP.h> + +#include <drm/drm_file.h>  #define CREATE_TRACE_POINTS  #include "drm_trace.h" diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index a1b65d26d761..0d704bddb1a6 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -24,12 +24,18 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drm_vblank.h> -#include <drm/drmP.h>  #include <linux/export.h> +#include <linux/moduleparam.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_print.h> +#include <drm/drm_os_linux.h> +#include <drm/drm_vblank.h> -#include "drm_trace.h"  #include "drm_internal.h" +#include "drm_trace.h"  /**   * DOC: vblank handling diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 10cf83d569e1..2f24ee6c7a92 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -33,15 +33,27 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h>  #include <linux/export.h> +#include <linux/pci.h>  #include <linux/seq_file.h> +#include <linux/vmalloc.h> +  #if defined(__ia64__)  #include <linux/efi.h>  #include <linux/slab.h>  #endif  #include <linux/mem_encrypt.h> +  #include <asm/pgtable.h> + +#include <drm/drm_agpsupport.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_print.h> +  #include "drm_internal.h"  #include "drm_legacy.h" diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index c5d0d2358301..4565319fa6b3 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -23,9 +23,6 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include <drm/drmP.h> -#include <drm/drm_mm.h> -#include <drm/drm_vma_manager.h>  #include <linux/mm.h>  #include <linux/module.h>  #include <linux/rbtree.h> @@ -33,6 +30,9 @@  #include <linux/spinlock.h>  #include <linux/types.h> +#include <drm/drm_mm.h> +#include <drm/drm_vma_manager.h> +  /**   * DOC: vma offset manager   * diff --git a/drivers/gpu/drm/drm_vram_mm_helper.c b/drivers/gpu/drm/drm_vram_mm_helper.c index c94a6dc5ade7..c911781d6728 100644 --- a/drivers/gpu/drm/drm_vram_mm_helper.c +++ b/drivers/gpu/drm/drm_vram_mm_helper.c @@ -1,7 +1,9 @@  // SPDX-License-Identifier: GPL-2.0-or-later +#include <drm/drm_device.h> +#include <drm/drm_file.h>  #include <drm/drm_vram_mm_helper.h> -#include <drm/drmP.h> +  #include <drm/ttm/ttm_page_alloc.h>  /** diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index 79ac014701c8..ff138b6ec48b 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -9,12 +9,14 @@   * of such GNU licence.   */ +#include <linux/dma-fence.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h>  #include <drm/drm_modeset_helper_vtables.h>  #include <drm/drm_property.h>  #include <drm/drm_writeback.h> -#include <drm/drmP.h> -#include <linux/dma-fence.h>  /**   * DOC: overview diff --git a/drivers/gpu/drm/mcde/Kconfig b/drivers/gpu/drm/mcde/Kconfig new file mode 100644 index 000000000000..b3990126562c --- /dev/null +++ b/drivers/gpu/drm/mcde/Kconfig @@ -0,0 +1,18 @@ +config DRM_MCDE +	tristate "DRM Support for ST-Ericsson MCDE (Multichannel Display Engine)" +	depends on DRM +	depends on CMA +	depends on ARM || COMPILE_TEST +	depends on OF +	select MFD_SYSCON +	select DRM_MIPI_DSI +	select DRM_BRIDGE +	select DRM_PANEL_BRIDGE +	select DRM_KMS_HELPER +	select DRM_KMS_CMA_HELPER +	select DRM_GEM_CMA_HELPER +	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE +	help +	  Choose this option for DRM support for the ST-Ericsson MCDE +	  Multi-Channel Display Engine. +	  If M is selected the module will be called mcde_drm. diff --git a/drivers/gpu/drm/mcde/Makefile b/drivers/gpu/drm/mcde/Makefile new file mode 100644 index 000000000000..fe28f4e0fe46 --- /dev/null +++ b/drivers/gpu/drm/mcde/Makefile @@ -0,0 +1,3 @@ +mcde_drm-y +=	mcde_drv.o mcde_dsi.o mcde_display.o + +obj-$(CONFIG_DRM_MCDE) += mcde_drm.o diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c new file mode 100644 index 000000000000..f3ef108a41d9 --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org> + * Parts of this file were based on the MCDE driver by Marcus Lorentzon + * (C) ST-Ericsson SA 2013 + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-buf.h> + +#include <drm/drm_device.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_vblank.h> +#include <video/mipi_display.h> + +#include "mcde_drm.h" +#include "mcde_display_regs.h" + +enum mcde_fifo { +	MCDE_FIFO_A, +	MCDE_FIFO_B, +	/* TODO: implement FIFO C0 and FIFO C1 */ +}; + +enum mcde_channel { +	MCDE_CHANNEL_0 = 0, +	MCDE_CHANNEL_1, +	MCDE_CHANNEL_2, +	MCDE_CHANNEL_3, +}; + +enum mcde_extsrc { +	MCDE_EXTSRC_0 = 0, +	MCDE_EXTSRC_1, +	MCDE_EXTSRC_2, +	MCDE_EXTSRC_3, +	MCDE_EXTSRC_4, +	MCDE_EXTSRC_5, +	MCDE_EXTSRC_6, +	MCDE_EXTSRC_7, +	MCDE_EXTSRC_8, +	MCDE_EXTSRC_9, +}; + +enum mcde_overlay { +	MCDE_OVERLAY_0 = 0, +	MCDE_OVERLAY_1, +	MCDE_OVERLAY_2, +	MCDE_OVERLAY_3, +	MCDE_OVERLAY_4, +	MCDE_OVERLAY_5, +}; + +enum mcde_dsi_formatter { +	MCDE_DSI_FORMATTER_0 = 0, +	MCDE_DSI_FORMATTER_1, +	MCDE_DSI_FORMATTER_2, +}; + +void mcde_display_irq(struct mcde *mcde) +{ +	u32 mispp, misovl, mischnl; +	bool vblank; + +	/* Handle display IRQs */ +	mispp = readl(mcde->regs + MCDE_MISPP); +	misovl = readl(mcde->regs + MCDE_MISOVL); +	mischnl = readl(mcde->regs + MCDE_MISCHNL); + +	/* +	 * Handle IRQs from the DSI link. All IRQs from the DSI links +	 * are just latched onto the MCDE IRQ line, so we need to traverse +	 * any active DSI masters and check if an IRQ is originating from +	 * them. +	 * +	 * TODO: Currently only one DSI link is supported. +	 */ +	if (mcde_dsi_irq(mcde->mdsi)) { +		u32 val; + +		/* +		 * In oneshot mode we do not send continuous updates +		 * to the display, instead we only push out updates when +		 * the update function is called, then we disable the +		 * flow on the channel once we get the TE IRQ. +		 */ +		if (mcde->oneshot_mode) { +			spin_lock(&mcde->flow_lock); +			if (--mcde->flow_active == 0) { +				dev_dbg(mcde->dev, "TE0 IRQ\n"); +				/* Disable FIFO A flow */ +				val = readl(mcde->regs + MCDE_CRA0); +				val &= ~MCDE_CRX0_FLOEN; +				writel(val, mcde->regs + MCDE_CRA0); +			} +			spin_unlock(&mcde->flow_lock); +		} +	} + +	/* Vblank from one of the channels */ +	if (mispp & MCDE_PP_VCMPA) { +		dev_dbg(mcde->dev, "chnl A vblank IRQ\n"); +		vblank = true; +	} +	if (mispp & MCDE_PP_VCMPB) { +		dev_dbg(mcde->dev, "chnl B vblank IRQ\n"); +		vblank = true; +	} +	if (mispp & MCDE_PP_VCMPC0) +		dev_dbg(mcde->dev, "chnl C0 vblank IRQ\n"); +	if (mispp & MCDE_PP_VCMPC1) +		dev_dbg(mcde->dev, "chnl C1 vblank IRQ\n"); +	if (mispp & MCDE_PP_VSCC0) +		dev_dbg(mcde->dev, "chnl C0 TE IRQ\n"); +	if (mispp & MCDE_PP_VSCC1) +		dev_dbg(mcde->dev, "chnl C1 TE IRQ\n"); +	writel(mispp, mcde->regs + MCDE_RISPP); + +	if (vblank) +		drm_crtc_handle_vblank(&mcde->pipe.crtc); + +	if (misovl) +		dev_info(mcde->dev, "some stray overlay IRQ %08x\n", misovl); +	writel(misovl, mcde->regs + MCDE_RISOVL); + +	if (mischnl) +		dev_info(mcde->dev, "some stray channel error IRQ %08x\n", +			 mischnl); +	writel(mischnl, mcde->regs + MCDE_RISCHNL); +} + +void mcde_display_disable_irqs(struct mcde *mcde) +{ +	/* Disable all IRQs */ +	writel(0, mcde->regs + MCDE_IMSCPP); +	writel(0, mcde->regs + MCDE_IMSCOVL); +	writel(0, mcde->regs + MCDE_IMSCCHNL); + +	/* Clear any pending IRQs */ +	writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP); +	writel(0xFFFFFFFF, mcde->regs + MCDE_RISOVL); +	writel(0xFFFFFFFF, mcde->regs + MCDE_RISCHNL); +} + +static int mcde_display_check(struct drm_simple_display_pipe *pipe, +			      struct drm_plane_state *pstate, +			      struct drm_crtc_state *cstate) +{ +	const struct drm_display_mode *mode = &cstate->mode; +	struct drm_framebuffer *old_fb = pipe->plane.state->fb; +	struct drm_framebuffer *fb = pstate->fb; + +	if (fb) { +		u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0); + +		/* FB base address must be dword aligned. */ +		if (offset & 3) { +			DRM_DEBUG_KMS("FB not 32-bit aligned\n"); +			return -EINVAL; +		} + +		/* +		 * There's no pitch register, the mode's hdisplay +		 * controls this. +		 */ +		if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) { +			DRM_DEBUG_KMS("can't handle pitches\n"); +			return -EINVAL; +		} + +		/* +		 * We can't change the FB format in a flicker-free +		 * manner (and only update it during CRTC enable). +		 */ +		if (old_fb && old_fb->format != fb->format) +			cstate->mode_changed = true; +	} + +	return 0; +} + +static int mcde_configure_extsrc(struct mcde *mcde, enum mcde_extsrc src, +				 u32 format) +{ +	u32 val; +	u32 conf; +	u32 cr; + +	switch (src) { +	case MCDE_EXTSRC_0: +		conf = MCDE_EXTSRC0CONF; +		cr = MCDE_EXTSRC0CR; +		break; +	case MCDE_EXTSRC_1: +		conf = MCDE_EXTSRC1CONF; +		cr = MCDE_EXTSRC1CR; +		break; +	case MCDE_EXTSRC_2: +		conf = MCDE_EXTSRC2CONF; +		cr = MCDE_EXTSRC2CR; +		break; +	case MCDE_EXTSRC_3: +		conf = MCDE_EXTSRC3CONF; +		cr = MCDE_EXTSRC3CR; +		break; +	case MCDE_EXTSRC_4: +		conf = MCDE_EXTSRC4CONF; +		cr = MCDE_EXTSRC4CR; +		break; +	case MCDE_EXTSRC_5: +		conf = MCDE_EXTSRC5CONF; +		cr = MCDE_EXTSRC5CR; +		break; +	case MCDE_EXTSRC_6: +		conf = MCDE_EXTSRC6CONF; +		cr = MCDE_EXTSRC6CR; +		break; +	case MCDE_EXTSRC_7: +		conf = MCDE_EXTSRC7CONF; +		cr = MCDE_EXTSRC7CR; +		break; +	case MCDE_EXTSRC_8: +		conf = MCDE_EXTSRC8CONF; +		cr = MCDE_EXTSRC8CR; +		break; +	case MCDE_EXTSRC_9: +		conf = MCDE_EXTSRC9CONF; +		cr = MCDE_EXTSRC9CR; +		break; +	} + +	/* +	 * Configure external source 0 one buffer (buffer 0) +	 * primary overlay ID 0. +	 * From mcde_hw.c ovly_update_registers() in the vendor tree +	 */ +	val = 0 << MCDE_EXTSRCXCONF_BUF_ID_SHIFT; +	val |= 1 << MCDE_EXTSRCXCONF_BUF_NB_SHIFT; +	val |= 0 << MCDE_EXTSRCXCONF_PRI_OVLID_SHIFT; +	/* +	 * MCDE has inverse semantics from DRM on RBG/BGR which is why +	 * all the modes are inversed here. +	 */ +	switch (format) { +	case DRM_FORMAT_ARGB8888: +		val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_ABGR8888: +		val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_XRGB8888: +		val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_XBGR8888: +		val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_RGB888: +		val |= MCDE_EXTSRCXCONF_BPP_RGB888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_BGR888: +		val |= MCDE_EXTSRCXCONF_BPP_RGB888 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_ARGB4444: +		val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_ABGR4444: +		val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_XRGB4444: +		val |= MCDE_EXTSRCXCONF_BPP_RGB444 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_XBGR4444: +		val |= MCDE_EXTSRCXCONF_BPP_RGB444 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_XRGB1555: +		val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_XBGR1555: +		val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_RGB565: +		val |= MCDE_EXTSRCXCONF_BPP_RGB565 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		val |= MCDE_EXTSRCXCONF_BGR; +		break; +	case DRM_FORMAT_BGR565: +		val |= MCDE_EXTSRCXCONF_BPP_RGB565 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	case DRM_FORMAT_YUV422: +		val |= MCDE_EXTSRCXCONF_BPP_YCBCR422 << +			MCDE_EXTSRCXCONF_BPP_SHIFT; +		break; +	default: +		dev_err(mcde->dev, "Unknown pixel format 0x%08x\n", +			format); +		return -EINVAL; +	} +	writel(val, mcde->regs + conf); + +	/* Software select, primary */ +	val = MCDE_EXTSRCXCR_SEL_MOD_SOFTWARE_SEL; +	val |= MCDE_EXTSRCXCR_MULTIOVL_CTRL_PRIMARY; +	writel(val, mcde->regs + cr); + +	return 0; +} + +static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl, +				   enum mcde_extsrc src, +				   enum mcde_channel ch, +				   const struct drm_display_mode *mode, +				   u32 format) +{ +	u32 val; +	u32 conf1; +	u32 conf2; +	u32 crop; +	u32 ljinc; +	u32 cr; +	u32 comp; + +	switch (ovl) { +	case MCDE_OVERLAY_0: +		conf1 = MCDE_OVL0CONF; +		conf2 = MCDE_OVL0CONF2; +		crop = MCDE_OVL0CROP; +		ljinc = MCDE_OVL0LJINC; +		cr = MCDE_OVL0CR; +		comp = MCDE_OVL0COMP; +		break; +	case MCDE_OVERLAY_1: +		conf1 = MCDE_OVL1CONF; +		conf2 = MCDE_OVL1CONF2; +		crop = MCDE_OVL1CROP; +		ljinc = MCDE_OVL1LJINC; +		cr = MCDE_OVL1CR; +		comp = MCDE_OVL1COMP; +		break; +	case MCDE_OVERLAY_2: +		conf1 = MCDE_OVL2CONF; +		conf2 = MCDE_OVL2CONF2; +		crop = MCDE_OVL2CROP; +		ljinc = MCDE_OVL2LJINC; +		cr = MCDE_OVL2CR; +		comp = MCDE_OVL2COMP; +		break; +	case MCDE_OVERLAY_3: +		conf1 = MCDE_OVL3CONF; +		conf2 = MCDE_OVL3CONF2; +		crop = MCDE_OVL3CROP; +		ljinc = MCDE_OVL3LJINC; +		cr = MCDE_OVL3CR; +		comp = MCDE_OVL3COMP; +		break; +	case MCDE_OVERLAY_4: +		conf1 = MCDE_OVL4CONF; +		conf2 = MCDE_OVL4CONF2; +		crop = MCDE_OVL4CROP; +		ljinc = MCDE_OVL4LJINC; +		cr = MCDE_OVL4CR; +		comp = MCDE_OVL4COMP; +		break; +	case MCDE_OVERLAY_5: +		conf1 = MCDE_OVL5CONF; +		conf2 = MCDE_OVL5CONF2; +		crop = MCDE_OVL5CROP; +		ljinc = MCDE_OVL5LJINC; +		cr = MCDE_OVL5CR; +		comp = MCDE_OVL5COMP; +		break; +	} + +	val = mode->hdisplay << MCDE_OVLXCONF_PPL_SHIFT; +	val |= mode->vdisplay << MCDE_OVLXCONF_LPF_SHIFT; +	/* Use external source 0 that we just configured */ +	val |= src << MCDE_OVLXCONF_EXTSRC_ID_SHIFT; +	writel(val, mcde->regs + conf1); + +	val = MCDE_OVLXCONF2_BP_PER_PIXEL_ALPHA; +	val |= 0xff << MCDE_OVLXCONF2_ALPHAVALUE_SHIFT; +	/* OPQ: overlay is opaque */ +	switch (format) { +	case DRM_FORMAT_ARGB8888: +	case DRM_FORMAT_ABGR8888: +	case DRM_FORMAT_ARGB4444: +	case DRM_FORMAT_ABGR4444: +	case DRM_FORMAT_XRGB1555: +	case DRM_FORMAT_XBGR1555: +		/* No OPQ */ +		break; +	case DRM_FORMAT_XRGB8888: +	case DRM_FORMAT_XBGR8888: +	case DRM_FORMAT_RGB888: +	case DRM_FORMAT_BGR888: +	case DRM_FORMAT_RGB565: +	case DRM_FORMAT_BGR565: +	case DRM_FORMAT_YUV422: +		val |= MCDE_OVLXCONF2_OPQ; +		break; +	default: +		dev_err(mcde->dev, "Unknown pixel format 0x%08x\n", +			format); +		break; +	} +	/* The default watermark level for overlay 0 is 48 */ +	val |= 48 << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT; +	writel(val, mcde->regs + conf2); + +	/* Number of bytes to fetch per line */ +	writel(mcde->stride, mcde->regs + ljinc); +	/* No cropping */ +	writel(0, mcde->regs + crop); + +	/* Set up overlay control register */ +	val = MCDE_OVLXCR_OVLEN; +	val |= MCDE_OVLXCR_COLCCTRL_DISABLED; +	val |= MCDE_OVLXCR_BURSTSIZE_8W << +		MCDE_OVLXCR_BURSTSIZE_SHIFT; +	val |= MCDE_OVLXCR_MAXOUTSTANDING_8_REQ << +		MCDE_OVLXCR_MAXOUTSTANDING_SHIFT; +	/* Not using rotation but set it up anyways */ +	val |= MCDE_OVLXCR_ROTBURSTSIZE_8W << +		MCDE_OVLXCR_ROTBURSTSIZE_SHIFT; +	writel(val, mcde->regs + cr); + +	/* +	 * Set up the overlay compositor to route the overlay out to +	 * the desired channel +	 */ +	val = ch << MCDE_OVLXCOMP_CH_ID_SHIFT; +	writel(val, mcde->regs + comp); +} + +static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch, +				   enum mcde_fifo fifo, +				   const struct drm_display_mode *mode) +{ +	u32 val; +	u32 conf; +	u32 sync; +	u32 stat; +	u32 bgcol; +	u32 mux; + +	switch (ch) { +	case MCDE_CHANNEL_0: +		conf = MCDE_CHNL0CONF; +		sync = MCDE_CHNL0SYNCHMOD; +		stat = MCDE_CHNL0STAT; +		bgcol = MCDE_CHNL0BCKGNDCOL; +		mux = MCDE_CHNL0MUXING; +		break; +	case MCDE_CHANNEL_1: +		conf = MCDE_CHNL1CONF; +		sync = MCDE_CHNL1SYNCHMOD; +		stat = MCDE_CHNL1STAT; +		bgcol = MCDE_CHNL1BCKGNDCOL; +		mux = MCDE_CHNL1MUXING; +		break; +	case MCDE_CHANNEL_2: +		conf = MCDE_CHNL2CONF; +		sync = MCDE_CHNL2SYNCHMOD; +		stat = MCDE_CHNL2STAT; +		bgcol = MCDE_CHNL2BCKGNDCOL; +		mux = MCDE_CHNL2MUXING; +		break; +	case MCDE_CHANNEL_3: +		conf = MCDE_CHNL3CONF; +		sync = MCDE_CHNL3SYNCHMOD; +		stat = MCDE_CHNL3STAT; +		bgcol = MCDE_CHNL3BCKGNDCOL; +		mux = MCDE_CHNL3MUXING; +		return; +	} + +	/* Set up channel 0 sync (based on chnl_update_registers()) */ +	if (mcde->te_sync) { +		/* +		 * Turn on hardware TE0 synchronization +		 */ +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE +			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; +		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 +			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; +	} else { +		/* +		 * Set up sync source to software, out sync formatter +		 * Code mostly from mcde_hw.c chnl_update_registers() +		 */ +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE +			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; +		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER +			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; +	} +	writel(val, mcde->regs + sync); + +	/* Set up pixels per line and lines per frame */ +	val = (mode->hdisplay - 1) << MCDE_CHNLXCONF_PPL_SHIFT; +	val |= (mode->vdisplay - 1) << MCDE_CHNLXCONF_LPF_SHIFT; +	writel(val, mcde->regs + conf); + +	/* +	 * Normalize color conversion: +	 * black background, OLED conversion disable on channel +	 */ +	val = MCDE_CHNLXSTAT_CHNLBLBCKGND_EN | +		MCDE_CHNLXSTAT_CHNLRD; +	writel(val, mcde->regs + stat); +	writel(0, mcde->regs + bgcol); + +	/* Set up muxing: connect the channel to the desired FIFO */ +	switch (fifo) { +	case MCDE_FIFO_A: +		writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_A, +		       mcde->regs + mux); +		break; +	case MCDE_FIFO_B: +		writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_B, +		       mcde->regs + mux); +		break; +	} +} + +static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo, +				enum mcde_dsi_formatter fmt, +				int fifo_wtrmrk) +{ +	u32 val; +	u32 ctrl; +	u32 cr0, cr1; + +	switch (fifo) { +	case MCDE_FIFO_A: +		ctrl = MCDE_CTRLA; +		cr0 = MCDE_CRA0; +		cr1 = MCDE_CRA1; +		break; +	case MCDE_FIFO_B: +		ctrl = MCDE_CTRLB; +		cr0 = MCDE_CRB0; +		cr1 = MCDE_CRB1; +		break; +	} + +	val = fifo_wtrmrk << MCDE_CTRLX_FIFOWTRMRK_SHIFT; +	/* We only support DSI formatting for now */ +	val |= MCDE_CTRLX_FORMTYPE_DSI << +		MCDE_CTRLX_FORMTYPE_SHIFT; + +	/* Select the formatter to use for this FIFO */ +	val |= fmt << MCDE_CTRLX_FORMID_SHIFT; +	writel(val, mcde->regs + ctrl); + +	/* Blend source with Alpha 0xff on FIFO */ +	val = MCDE_CRX0_BLENDEN | +		0xff << MCDE_CRX0_ALPHABLEND_SHIFT; +	writel(val, mcde->regs + cr0); + +	/* Set-up from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */ + +	/* Use the MCDE clock for this FIFO */ +	val = MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT; + +	/* TODO: when adding DPI support add OUTBPP etc here */ +	writel(val, mcde->regs + cr1); +}; + +static void mcde_configure_dsi_formatter(struct mcde *mcde, +					 enum mcde_dsi_formatter fmt, +					 u32 formatter_frame, +					 int pkt_size) +{ +	u32 val; +	u32 conf0; +	u32 frame; +	u32 pkt; +	u32 sync; +	u32 cmdw; +	u32 delay0, delay1; + +	switch (fmt) { +	case MCDE_DSI_FORMATTER_0: +		conf0 = MCDE_DSIVID0CONF0; +		frame = MCDE_DSIVID0FRAME; +		pkt = MCDE_DSIVID0PKT; +		sync = MCDE_DSIVID0SYNC; +		cmdw = MCDE_DSIVID0CMDW; +		delay0 = MCDE_DSIVID0DELAY0; +		delay1 = MCDE_DSIVID0DELAY1; +		break; +	case MCDE_DSI_FORMATTER_1: +		conf0 = MCDE_DSIVID1CONF0; +		frame = MCDE_DSIVID1FRAME; +		pkt = MCDE_DSIVID1PKT; +		sync = MCDE_DSIVID1SYNC; +		cmdw = MCDE_DSIVID1CMDW; +		delay0 = MCDE_DSIVID1DELAY0; +		delay1 = MCDE_DSIVID1DELAY1; +		break; +	case MCDE_DSI_FORMATTER_2: +		conf0 = MCDE_DSIVID2CONF0; +		frame = MCDE_DSIVID2FRAME; +		pkt = MCDE_DSIVID2PKT; +		sync = MCDE_DSIVID2SYNC; +		cmdw = MCDE_DSIVID2CMDW; +		delay0 = MCDE_DSIVID2DELAY0; +		delay1 = MCDE_DSIVID2DELAY1; +		break; +	} + +	/* +	 * Enable formatter +	 * 8 bit commands and DCS commands (notgen = not generic) +	 */ +	val = MCDE_DSICONF0_CMD8 | MCDE_DSICONF0_DCSVID_NOTGEN; +	if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) +		val |= MCDE_DSICONF0_VID_MODE_VID; +	switch (mcde->mdsi->format) { +	case MIPI_DSI_FMT_RGB888: +		val |= MCDE_DSICONF0_PACKING_RGB888 << +			MCDE_DSICONF0_PACKING_SHIFT; +		break; +	case MIPI_DSI_FMT_RGB666: +		val |= MCDE_DSICONF0_PACKING_RGB666 << +			MCDE_DSICONF0_PACKING_SHIFT; +		break; +	case MIPI_DSI_FMT_RGB666_PACKED: +		val |= MCDE_DSICONF0_PACKING_RGB666_PACKED << +			MCDE_DSICONF0_PACKING_SHIFT; +		break; +	case MIPI_DSI_FMT_RGB565: +		val |= MCDE_DSICONF0_PACKING_RGB565 << +			MCDE_DSICONF0_PACKING_SHIFT; +		break; +	default: +		dev_err(mcde->dev, "unknown DSI format\n"); +		return; +	} +	writel(val, mcde->regs + conf0); + +	writel(formatter_frame, mcde->regs + frame); +	writel(pkt_size, mcde->regs + pkt); +	writel(0, mcde->regs + sync); +	/* Define the MIPI command: we want to write into display memory */ +	val = MIPI_DCS_WRITE_MEMORY_CONTINUE << +		MCDE_DSIVIDXCMDW_CMDW_CONTINUE_SHIFT; +	val |= MIPI_DCS_WRITE_MEMORY_START << +		MCDE_DSIVIDXCMDW_CMDW_START_SHIFT; +	writel(val, mcde->regs + cmdw); + +	/* +	 * FIXME: the vendor driver has some hack around this value in +	 * CMD mode with autotrig. +	 */ +	writel(0, mcde->regs + delay0); +	writel(0, mcde->regs + delay1); +} + +static void mcde_enable_fifo(struct mcde *mcde, enum mcde_fifo fifo) +{ +	u32 val; +	u32 cr; + +	switch (fifo) { +	case MCDE_FIFO_A: +		cr = MCDE_CRA0; +		break; +	case MCDE_FIFO_B: +		cr = MCDE_CRB0; +		break; +	default: +		dev_err(mcde->dev, "cannot enable FIFO %c\n", +			'A' + fifo); +		return; +	} + +	spin_lock(&mcde->flow_lock); +	val = readl(mcde->regs + cr); +	val |= MCDE_CRX0_FLOEN; +	writel(val, mcde->regs + cr); +	mcde->flow_active++; +	spin_unlock(&mcde->flow_lock); +} + +static void mcde_disable_fifo(struct mcde *mcde, enum mcde_fifo fifo, +			      bool wait_for_drain) +{ +	int timeout = 100; +	u32 val; +	u32 cr; + +	switch (fifo) { +	case MCDE_FIFO_A: +		cr = MCDE_CRA0; +		break; +	case MCDE_FIFO_B: +		cr = MCDE_CRB0; +		break; +	default: +		dev_err(mcde->dev, "cannot disable FIFO %c\n", +			'A' + fifo); +		return; +	} + +	spin_lock(&mcde->flow_lock); +	val = readl(mcde->regs + cr); +	val &= ~MCDE_CRX0_FLOEN; +	writel(val, mcde->regs + cr); +	mcde->flow_active = 0; +	spin_unlock(&mcde->flow_lock); + +	if (!wait_for_drain) +		return; + +	/* Check that we really drained and stopped the flow */ +	while (readl(mcde->regs + cr) & MCDE_CRX0_FLOEN) { +		usleep_range(1000, 1500); +		if (!--timeout) { +			dev_err(mcde->dev, +				"FIFO timeout while clearing FIFO %c\n", +				'A' + fifo); +			return; +		} +	} +} + +/* + * This drains a pipe i.e. a FIFO connected to a certain channel + */ +static void mcde_drain_pipe(struct mcde *mcde, enum mcde_fifo fifo, +			    enum mcde_channel ch) +{ +	u32 val; +	u32 ctrl; +	u32 synsw; + +	switch (fifo) { +	case MCDE_FIFO_A: +		ctrl = MCDE_CTRLA; +		break; +	case MCDE_FIFO_B: +		ctrl = MCDE_CTRLB; +		break; +	} + +	switch (ch) { +	case MCDE_CHANNEL_0: +		synsw = MCDE_CHNL0SYNCHSW; +		break; +	case MCDE_CHANNEL_1: +		synsw = MCDE_CHNL1SYNCHSW; +		break; +	case MCDE_CHANNEL_2: +		synsw = MCDE_CHNL2SYNCHSW; +		break; +	case MCDE_CHANNEL_3: +		synsw = MCDE_CHNL3SYNCHSW; +		return; +	} + +	val = readl(mcde->regs + ctrl); +	if (!(val & MCDE_CTRLX_FIFOEMPTY)) { +		dev_err(mcde->dev, "Channel A FIFO not empty (handover)\n"); +		/* Attempt to clear the FIFO */ +		mcde_enable_fifo(mcde, fifo); +		/* Trigger a software sync out on respective channel (0-3) */ +		writel(MCDE_CHNLXSYNCHSW_SW_TRIG, mcde->regs + synsw); +		/* Disable FIFO A flow again */ +		mcde_disable_fifo(mcde, fifo, true); +	} +} + +static int mcde_dsi_get_pkt_div(int ppl, int fifo_size) +{ +	/* +	 * DSI command mode line packets should be split into an even number of +	 * packets smaller than or equal to the fifo size. +	 */ +	int div; +	const int max_div = DIV_ROUND_UP(MCDE_MAX_WIDTH, fifo_size); + +	for (div = 1; div < max_div; div++) +		if (ppl % div == 0 && ppl / div <= fifo_size) +			return div; +	return 1; +} + +static void mcde_display_enable(struct drm_simple_display_pipe *pipe, +				struct drm_crtc_state *cstate, +				struct drm_plane_state *plane_state) +{ +	struct drm_crtc *crtc = &pipe->crtc; +	struct drm_plane *plane = &pipe->plane; +	struct drm_device *drm = crtc->dev; +	struct mcde *mcde = drm->dev_private; +	const struct drm_display_mode *mode = &cstate->mode; +	struct drm_framebuffer *fb = plane->state->fb; +	u32 format = fb->format->format; +	u32 formatter_ppl = mode->hdisplay; /* pixels per line */ +	u32 formatter_lpf = mode->vdisplay; /* lines per frame */ +	int pkt_size, fifo_wtrmrk; +	int cpp = fb->format->cpp[0]; +	int formatter_cpp; +	struct drm_format_name_buf tmp; +	u32 formatter_frame; +	u32 pkt_div; +	u32 val; + +	dev_info(drm->dev, "enable MCDE, %d x %d format %s\n", +		 mode->hdisplay, mode->vdisplay, +		 drm_get_format_name(format, &tmp)); +	if (!mcde->mdsi) { +		/* TODO: deal with this for non-DSI output */ +		dev_err(drm->dev, "no DSI master attached!\n"); +		return; +	} + +	dev_info(drm->dev, "output in %s mode, format %dbpp\n", +		 (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? +		 "VIDEO" : "CMD", +		 mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format)); +	formatter_cpp = +		mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format) / 8; +	dev_info(drm->dev, "overlay CPP %d bytes, DSI CPP %d bytes\n", +		 cpp, +		 formatter_cpp); + +	/* Calculations from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */ + +	/* +	 * Set up FIFO A watermark level: +	 * 128 for LCD 32bpp video mode +	 * 48  for LCD 32bpp command mode +	 * 128 for LCD 16bpp video mode +	 * 64  for LCD 16bpp command mode +	 * 128 for HDMI 32bpp +	 * 192 for HDMI 16bpp +	 */ +	fifo_wtrmrk = mode->hdisplay; +	if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { +		fifo_wtrmrk = min(fifo_wtrmrk, 128); +		pkt_div = 1; +	} else { +		fifo_wtrmrk = min(fifo_wtrmrk, 48); +		/* The FIFO is 640 entries deep on this v3 hardware */ +		pkt_div = mcde_dsi_get_pkt_div(mode->hdisplay, 640); +	} +	dev_dbg(drm->dev, "FIFO watermark after flooring: %d bytes\n", +		fifo_wtrmrk); +	dev_dbg(drm->dev, "Packet divisor: %d bytes\n", pkt_div); + +	/* NOTE: pkt_div is 1 for video mode */ +	pkt_size = (formatter_ppl * formatter_cpp) / pkt_div; +	/* Commands CMD8 need one extra byte */ +	if (!(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)) +		pkt_size++; + +	dev_dbg(drm->dev, "DSI packet size: %d * %d bytes per line\n", +		pkt_size, pkt_div); +	dev_dbg(drm->dev, "Overlay frame size: %u bytes\n", +		mode->hdisplay * mode->vdisplay * cpp); +	mcde->stride = mode->hdisplay * cpp; +	dev_dbg(drm->dev, "Overlay line stride: %u bytes\n", +		mcde->stride); +	/* NOTE: pkt_div is 1 for video mode */ +	formatter_frame = pkt_size * pkt_div * formatter_lpf; +	dev_dbg(drm->dev, "Formatter frame size: %u bytes\n", formatter_frame); + +	/* Drain the FIFO A + channel 0 pipe so we have a clean slate */ +	mcde_drain_pipe(mcde, MCDE_FIFO_A, MCDE_CHANNEL_0); + +	/* +	 * We set up our display pipeline: +	 * EXTSRC 0 -> OVERLAY 0 -> CHANNEL 0 -> FIFO A -> DSI FORMATTER 0 +	 * +	 * First configure the external source (memory) on external source 0 +	 * using the desired bitstream/bitmap format +	 */ +	mcde_configure_extsrc(mcde, MCDE_EXTSRC_0, format); + +	/* +	 * Configure overlay 0 according to format and mode and take input +	 * from external source 0 and route the output of this overlay to +	 * channel 0 +	 */ +	mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0, +			       MCDE_CHANNEL_0, mode, format); + +	/* +	 * Configure pixel-per-line and line-per-frame for channel 0 and then +	 * route channel 0 to FIFO A +	 */ +	mcde_configure_channel(mcde, MCDE_CHANNEL_0, MCDE_FIFO_A, mode); + +	/* Configure FIFO A to use DSI formatter 0 */ +	mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0, +			    fifo_wtrmrk); + +	/* Configure the DSI formatter 0 for the DSI panel output */ +	mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0, +				     formatter_frame, pkt_size); + +	if (mcde->te_sync) { +		if (mode->flags & DRM_MODE_FLAG_NVSYNC) +			val = MCDE_VSCRC_VSPOL; +		else +			val = 0; +		writel(val, mcde->regs + MCDE_VSCRC0); +		/* Enable VSYNC capture on TE0 */ +		val = readl(mcde->regs + MCDE_CRC); +		val |= MCDE_CRC_SYCEN0; +		writel(val, mcde->regs + MCDE_CRC); + +		drm_crtc_vblank_on(crtc); +	} + +	dev_info(drm->dev, "MCDE display is enabled\n"); +} + +static void mcde_display_disable(struct drm_simple_display_pipe *pipe) +{ +	struct drm_crtc *crtc = &pipe->crtc; +	struct drm_device *drm = crtc->dev; +	struct mcde *mcde = drm->dev_private; + +	if (mcde->te_sync) +		drm_crtc_vblank_off(crtc); + +	/* Disable FIFO A flow */ +	mcde_disable_fifo(mcde, MCDE_FIFO_A, true); + +	dev_info(drm->dev, "MCDE display is disabled\n"); +} + +static void mcde_display_send_one_frame(struct mcde *mcde) +{ +	/* Request a TE ACK */ +	if (mcde->te_sync) +		mcde_dsi_te_request(mcde->mdsi); + +	/* Enable FIFO A flow */ +	mcde_enable_fifo(mcde, MCDE_FIFO_A); + +	if (mcde->te_sync) { +		/* +		 * If oneshot mode is enabled, the flow will be disabled +		 * when the TE0 IRQ arrives in the interrupt handler. Otherwise +		 * updates are continuously streamed to the display after this +		 * point. +		 */ +		dev_dbg(mcde->dev, "sent TE0 framebuffer update\n"); +		return; +	} + +	/* Trigger a software sync out on channel 0 */ +	writel(MCDE_CHNLXSYNCHSW_SW_TRIG, +	       mcde->regs + MCDE_CHNL0SYNCHSW); + +	/* +	 * Disable FIFO A flow again: since we are using TE sync we +	 * need to wait for the FIFO to drain before we continue +	 * so repeated calls to this function will not cause a mess +	 * in the hardware by pushing updates will updates are going +	 * on already. +	 */ +	mcde_disable_fifo(mcde, MCDE_FIFO_A, true); + +	dev_dbg(mcde->dev, "sent SW framebuffer update\n"); +} + +static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address) +{ +	/* Write bitmap base address to register */ +	writel(buffer_address, mcde->regs + MCDE_EXTSRCXA0); +	/* +	 * Base address for next line this is probably only used +	 * in interlace modes. +	 */ +	writel(buffer_address + mcde->stride, mcde->regs + MCDE_EXTSRCXA1); +} + +static void mcde_display_update(struct drm_simple_display_pipe *pipe, +				struct drm_plane_state *old_pstate) +{ +	struct drm_crtc *crtc = &pipe->crtc; +	struct drm_device *drm = crtc->dev; +	struct mcde *mcde = drm->dev_private; +	struct drm_pending_vblank_event *event = crtc->state->event; +	struct drm_plane *plane = &pipe->plane; +	struct drm_plane_state *pstate = plane->state; +	struct drm_framebuffer *fb = pstate->fb; + +	/* +	 * Handle any pending event first, we need to arm the vblank +	 * interrupt before sending any update to the display so we don't +	 * miss the interrupt. +	 */ +	if (event) { +		crtc->state->event = NULL; + +		spin_lock_irq(&crtc->dev->event_lock); +		/* +		 * Hardware must be on before we can arm any vblank event, +		 * this is not a scanout controller where there is always +		 * some periodic update going on, it is completely frozen +		 * until we get an update. If MCDE output isn't yet enabled, +		 * we just send a vblank dummy event back. +		 */ +		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) { +			dev_dbg(mcde->dev, "arm vblank event\n"); +			drm_crtc_arm_vblank_event(crtc, event); +		} else { +			dev_dbg(mcde->dev, "insert fake vblank event\n"); +			drm_crtc_send_vblank_event(crtc, event); +		} + +		spin_unlock_irq(&crtc->dev->event_lock); +	} + +	/* +	 * We do not start sending framebuffer updates before the +	 * display is enabled. Update events will however be dispatched +	 * from the DRM core before the display is enabled. +	 */ +	if (fb) { +		mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0)); +		/* Send a single frame using software sync */ +		mcde_display_send_one_frame(mcde); +		dev_info_once(mcde->dev, "sent first display update\n"); +	} else { +		/* +		 * If an update is receieved before the MCDE is enabled +		 * (before mcde_display_enable() is called) we can't really +		 * do much with that buffer. +		 */ +		dev_info(mcde->dev, "ignored a display update\n"); +	} +} + +static int mcde_display_enable_vblank(struct drm_simple_display_pipe *pipe) +{ +	struct drm_crtc *crtc = &pipe->crtc; +	struct drm_device *drm = crtc->dev; +	struct mcde *mcde = drm->dev_private; +	u32 val; + +	/* Enable all VBLANK IRQs */ +	val = MCDE_PP_VCMPA | +		MCDE_PP_VCMPB | +		MCDE_PP_VSCC0 | +		MCDE_PP_VSCC1 | +		MCDE_PP_VCMPC0 | +		MCDE_PP_VCMPC1; +	writel(val, mcde->regs + MCDE_IMSCPP); + +	return 0; +} + +static void mcde_display_disable_vblank(struct drm_simple_display_pipe *pipe) +{ +	struct drm_crtc *crtc = &pipe->crtc; +	struct drm_device *drm = crtc->dev; +	struct mcde *mcde = drm->dev_private; + +	/* Disable all VBLANK IRQs */ +	writel(0, mcde->regs + MCDE_IMSCPP); +	/* Clear any pending IRQs */ +	writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP); +} + +static struct drm_simple_display_pipe_funcs mcde_display_funcs = { +	.check = mcde_display_check, +	.enable = mcde_display_enable, +	.disable = mcde_display_disable, +	.update = mcde_display_update, +	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, +}; + +int mcde_display_init(struct drm_device *drm) +{ +	struct mcde *mcde = drm->dev_private; +	int ret; +	static const u32 formats[] = { +		DRM_FORMAT_ARGB8888, +		DRM_FORMAT_ABGR8888, +		DRM_FORMAT_XRGB8888, +		DRM_FORMAT_XBGR8888, +		DRM_FORMAT_RGB888, +		DRM_FORMAT_BGR888, +		DRM_FORMAT_ARGB4444, +		DRM_FORMAT_ABGR4444, +		DRM_FORMAT_XRGB4444, +		DRM_FORMAT_XBGR4444, +		/* These are actually IRGB1555 so intensity bit is lost */ +		DRM_FORMAT_XRGB1555, +		DRM_FORMAT_XBGR1555, +		DRM_FORMAT_RGB565, +		DRM_FORMAT_BGR565, +		DRM_FORMAT_YUV422, +	}; + +	/* Provide vblank only when we have TE enabled */ +	if (mcde->te_sync) { +		mcde_display_funcs.enable_vblank = mcde_display_enable_vblank; +		mcde_display_funcs.disable_vblank = mcde_display_disable_vblank; +	} + +	ret = drm_simple_display_pipe_init(drm, &mcde->pipe, +					   &mcde_display_funcs, +					   formats, ARRAY_SIZE(formats), +					   NULL, +					   mcde->connector); +	if (ret) +		return ret; + +	return 0; +} +EXPORT_SYMBOL_GPL(mcde_display_init); diff --git a/drivers/gpu/drm/mcde/mcde_display_regs.h b/drivers/gpu/drm/mcde/mcde_display_regs.h new file mode 100644 index 000000000000..d3ac7ef5ff9a --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_display_regs.h @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DRM_MCDE_DISPLAY_REGS +#define __DRM_MCDE_DISPLAY_REGS + +/* PP (pixel processor) interrupts */ +#define MCDE_IMSCPP 0x00000104 +#define MCDE_RISPP 0x00000114 +#define MCDE_MISPP 0x00000124 +#define MCDE_SISPP 0x00000134 + +#define MCDE_PP_VCMPA BIT(0) +#define MCDE_PP_VCMPB BIT(1) +#define MCDE_PP_VSCC0 BIT(2) +#define MCDE_PP_VSCC1 BIT(3) +#define MCDE_PP_VCMPC0 BIT(4) +#define MCDE_PP_VCMPC1 BIT(5) +#define MCDE_PP_ROTFD_A BIT(6) +#define MCDE_PP_ROTFD_B BIT(7) + +/* Overlay interrupts */ +#define MCDE_IMSCOVL 0x00000108 +#define MCDE_RISOVL 0x00000118 +#define MCDE_MISOVL 0x00000128 +#define MCDE_SISOVL 0x00000138 + +/* Channel interrupts */ +#define MCDE_IMSCCHNL 0x0000010C +#define MCDE_RISCHNL 0x0000011C +#define MCDE_MISCHNL 0x0000012C +#define MCDE_SISCHNL 0x0000013C + +/* X = 0..9 */ +#define MCDE_EXTSRCXA0 0x00000200 +#define MCDE_EXTSRCXA0_GROUPOFFSET 0x20 +#define MCDE_EXTSRCXA0_BASEADDRESS0_SHIFT 3 +#define MCDE_EXTSRCXA0_BASEADDRESS0_MASK 0xFFFFFFF8 + +#define MCDE_EXTSRCXA1 0x00000204 +#define MCDE_EXTSRCXA1_GROUPOFFSET 0x20 +#define MCDE_EXTSRCXA1_BASEADDRESS1_SHIFT 3 +#define MCDE_EXTSRCXA1_BASEADDRESS1_MASK 0xFFFFFFF8 + +/* External sources 0..9 */ +#define MCDE_EXTSRC0CONF 0x0000020C +#define MCDE_EXTSRC1CONF 0x0000022C +#define MCDE_EXTSRC2CONF 0x0000024C +#define MCDE_EXTSRC3CONF 0x0000026C +#define MCDE_EXTSRC4CONF 0x0000028C +#define MCDE_EXTSRC5CONF 0x000002AC +#define MCDE_EXTSRC6CONF 0x000002CC +#define MCDE_EXTSRC7CONF 0x000002EC +#define MCDE_EXTSRC8CONF 0x0000030C +#define MCDE_EXTSRC9CONF 0x0000032C +#define MCDE_EXTSRCXCONF_GROUPOFFSET 0x20 +#define MCDE_EXTSRCXCONF_BUF_ID_SHIFT 0 +#define MCDE_EXTSRCXCONF_BUF_ID_MASK 0x00000003 +#define MCDE_EXTSRCXCONF_BUF_NB_SHIFT 2 +#define MCDE_EXTSRCXCONF_BUF_NB_MASK 0x0000000C +#define MCDE_EXTSRCXCONF_PRI_OVLID_SHIFT 4 +#define MCDE_EXTSRCXCONF_PRI_OVLID_MASK 0x000000F0 +#define MCDE_EXTSRCXCONF_BPP_SHIFT 8 +#define MCDE_EXTSRCXCONF_BPP_MASK 0x00000F00 +#define MCDE_EXTSRCXCONF_BPP_1BPP_PAL 0 +#define MCDE_EXTSRCXCONF_BPP_2BPP_PAL 1 +#define MCDE_EXTSRCXCONF_BPP_4BPP_PAL 2 +#define MCDE_EXTSRCXCONF_BPP_8BPP_PAL 3 +#define MCDE_EXTSRCXCONF_BPP_RGB444 4 +#define MCDE_EXTSRCXCONF_BPP_ARGB4444 5 +#define MCDE_EXTSRCXCONF_BPP_IRGB1555 6 +#define MCDE_EXTSRCXCONF_BPP_RGB565 7 +#define MCDE_EXTSRCXCONF_BPP_RGB888 8 +#define MCDE_EXTSRCXCONF_BPP_XRGB8888 9 +#define MCDE_EXTSRCXCONF_BPP_ARGB8888 10 +#define MCDE_EXTSRCXCONF_BPP_YCBCR422 11 +#define MCDE_EXTSRCXCONF_BGR BIT(12) +#define MCDE_EXTSRCXCONF_BEBO BIT(13) +#define MCDE_EXTSRCXCONF_BEPO BIT(14) +#define MCDE_EXTSRCXCONF_TUNNELING_BUFFER_HEIGHT_SHIFT 16 +#define MCDE_EXTSRCXCONF_TUNNELING_BUFFER_HEIGHT_MASK 0x0FFF0000 + +/* External sources 0..9 */ +#define MCDE_EXTSRC0CR 0x00000210 +#define MCDE_EXTSRC1CR 0x00000230 +#define MCDE_EXTSRC2CR 0x00000250 +#define MCDE_EXTSRC3CR 0x00000270 +#define MCDE_EXTSRC4CR 0x00000290 +#define MCDE_EXTSRC5CR 0x000002B0 +#define MCDE_EXTSRC6CR 0x000002D0 +#define MCDE_EXTSRC7CR 0x000002F0 +#define MCDE_EXTSRC8CR 0x00000310 +#define MCDE_EXTSRC9CR 0x00000330 +#define MCDE_EXTSRCXCR_SEL_MOD_SHIFT 0 +#define MCDE_EXTSRCXCR_SEL_MOD_MASK 0x00000003 +#define MCDE_EXTSRCXCR_SEL_MOD_EXTERNAL_SEL 0 +#define MCDE_EXTSRCXCR_SEL_MOD_AUTO_TOGGLE 1 +#define MCDE_EXTSRCXCR_SEL_MOD_SOFTWARE_SEL 2 +#define MCDE_EXTSRCXCR_MULTIOVL_CTRL_PRIMARY BIT(2) /* 0 = all */ +#define MCDE_EXTSRCXCR_FS_DIV_DISABLE BIT(3) +#define MCDE_EXTSRCXCR_FORCE_FS_DIV BIT(4) + +/* Only external source 6 has a second address register */ +#define MCDE_EXTSRC6A2 0x000002C8 + +/* 6 overlays */ +#define MCDE_OVL0CR 0x00000400 +#define MCDE_OVL1CR 0x00000420 +#define MCDE_OVL2CR 0x00000440 +#define MCDE_OVL3CR 0x00000460 +#define MCDE_OVL4CR 0x00000480 +#define MCDE_OVL5CR 0x000004A0 +#define MCDE_OVLXCR_OVLEN BIT(0) +#define MCDE_OVLXCR_COLCCTRL_DISABLED 0 +#define MCDE_OVLXCR_COLCCTRL_ENABLED_NO_SAT (1 << 1) +#define MCDE_OVLXCR_COLCCTRL_ENABLED_SAT (2 << 1) +#define MCDE_OVLXCR_CKEYGEN BIT(3) +#define MCDE_OVLXCR_ALPHAPMEN BIT(4) +#define MCDE_OVLXCR_OVLF BIT(5) +#define MCDE_OVLXCR_OVLR BIT(6) +#define MCDE_OVLXCR_OVLB BIT(7) +#define MCDE_OVLXCR_FETCH_ROPC_SHIFT 8 +#define MCDE_OVLXCR_FETCH_ROPC_MASK 0x0000FF00 +#define MCDE_OVLXCR_STBPRIO_SHIFT 16 +#define MCDE_OVLXCR_STBPRIO_MASK 0x000F0000 +#define MCDE_OVLXCR_BURSTSIZE_SHIFT 20 +#define MCDE_OVLXCR_BURSTSIZE_MASK 0x00F00000 +#define MCDE_OVLXCR_BURSTSIZE_1W 0 +#define MCDE_OVLXCR_BURSTSIZE_2W 1 +#define MCDE_OVLXCR_BURSTSIZE_4W 2 +#define MCDE_OVLXCR_BURSTSIZE_8W 3 +#define MCDE_OVLXCR_BURSTSIZE_16W 4 +#define MCDE_OVLXCR_BURSTSIZE_HW_1W 8 +#define MCDE_OVLXCR_BURSTSIZE_HW_2W 9 +#define MCDE_OVLXCR_BURSTSIZE_HW_4W 10 +#define MCDE_OVLXCR_BURSTSIZE_HW_8W 11 +#define MCDE_OVLXCR_BURSTSIZE_HW_16W 12 +#define MCDE_OVLXCR_MAXOUTSTANDING_SHIFT 24 +#define MCDE_OVLXCR_MAXOUTSTANDING_MASK 0x0F000000 +#define MCDE_OVLXCR_MAXOUTSTANDING_1_REQ 0 +#define MCDE_OVLXCR_MAXOUTSTANDING_2_REQ 1 +#define MCDE_OVLXCR_MAXOUTSTANDING_4_REQ 2 +#define MCDE_OVLXCR_MAXOUTSTANDING_8_REQ 3 +#define MCDE_OVLXCR_MAXOUTSTANDING_16_REQ 4 +#define MCDE_OVLXCR_ROTBURSTSIZE_SHIFT 28 +#define MCDE_OVLXCR_ROTBURSTSIZE_MASK 0xF0000000 +#define MCDE_OVLXCR_ROTBURSTSIZE_1W 0 +#define MCDE_OVLXCR_ROTBURSTSIZE_2W 1 +#define MCDE_OVLXCR_ROTBURSTSIZE_4W 2 +#define MCDE_OVLXCR_ROTBURSTSIZE_8W 3 +#define MCDE_OVLXCR_ROTBURSTSIZE_16W 4 +#define MCDE_OVLXCR_ROTBURSTSIZE_HW_1W 8 +#define MCDE_OVLXCR_ROTBURSTSIZE_HW_2W 9 +#define MCDE_OVLXCR_ROTBURSTSIZE_HW_4W 10 +#define MCDE_OVLXCR_ROTBURSTSIZE_HW_8W 11 +#define MCDE_OVLXCR_ROTBURSTSIZE_HW_16W 12 + +#define MCDE_OVL0CONF 0x00000404 +#define MCDE_OVL1CONF 0x00000424 +#define MCDE_OVL2CONF 0x00000444 +#define MCDE_OVL3CONF 0x00000464 +#define MCDE_OVL4CONF 0x00000484 +#define MCDE_OVL5CONF 0x000004A4 +#define MCDE_OVLXCONF_PPL_SHIFT 0 +#define MCDE_OVLXCONF_PPL_MASK 0x000007FF +#define MCDE_OVLXCONF_EXTSRC_ID_SHIFT 11 +#define MCDE_OVLXCONF_EXTSRC_ID_MASK 0x00007800 +#define MCDE_OVLXCONF_LPF_SHIFT 16 +#define MCDE_OVLXCONF_LPF_MASK 0x07FF0000 + +#define MCDE_OVL0CONF2 0x00000408 +#define MCDE_OVL1CONF2 0x00000428 +#define MCDE_OVL2CONF2 0x00000448 +#define MCDE_OVL3CONF2 0x00000468 +#define MCDE_OVL4CONF2 0x00000488 +#define MCDE_OVL5CONF2 0x000004A8 +#define MCDE_OVLXCONF2_BP_PER_PIXEL_ALPHA 0 +#define MCDE_OVLXCONF2_BP_CONSTANT_ALPHA BIT(0) +#define MCDE_OVLXCONF2_ALPHAVALUE_SHIFT 1 +#define MCDE_OVLXCONF2_ALPHAVALUE_MASK 0x000001FE +#define MCDE_OVLXCONF2_OPQ BIT(9) +#define MCDE_OVLXCONF2_PIXOFF_SHIFT 10 +#define MCDE_OVLXCONF2_PIXOFF_MASK 0x0000FC00 +#define MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16 +#define MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000 + +#define MCDE_OVL0LJINC 0x0000040C +#define MCDE_OVL1LJINC 0x0000042C +#define MCDE_OVL2LJINC 0x0000044C +#define MCDE_OVL3LJINC 0x0000046C +#define MCDE_OVL4LJINC 0x0000048C +#define MCDE_OVL5LJINC 0x000004AC + +#define MCDE_OVL0CROP 0x00000410 +#define MCDE_OVL1CROP 0x00000430 +#define MCDE_OVL2CROP 0x00000450 +#define MCDE_OVL3CROP 0x00000470 +#define MCDE_OVL4CROP 0x00000490 +#define MCDE_OVL5CROP 0x000004B0 +#define MCDE_OVLXCROP_TMRGN_SHIFT 0 +#define MCDE_OVLXCROP_TMRGN_MASK 0x003FFFFF +#define MCDE_OVLXCROP_LMRGN_SHIFT 22 +#define MCDE_OVLXCROP_LMRGN_MASK 0xFFC00000 + +#define MCDE_OVL0COMP 0x00000414 +#define MCDE_OVL1COMP 0x00000434 +#define MCDE_OVL2COMP 0x00000454 +#define MCDE_OVL3COMP 0x00000474 +#define MCDE_OVL4COMP 0x00000494 +#define MCDE_OVL5COMP 0x000004B4 +#define MCDE_OVLXCOMP_XPOS_SHIFT 0 +#define MCDE_OVLXCOMP_XPOS_MASK 0x000007FF +#define MCDE_OVLXCOMP_CH_ID_SHIFT 11 +#define MCDE_OVLXCOMP_CH_ID_MASK 0x00007800 +#define MCDE_OVLXCOMP_YPOS_SHIFT 16 +#define MCDE_OVLXCOMP_YPOS_MASK 0x07FF0000 +#define MCDE_OVLXCOMP_Z_SHIFT 27 +#define MCDE_OVLXCOMP_Z_MASK 0x78000000 + +#define MCDE_CRC 0x00000C00 +#define MCDE_CRC_C1EN BIT(2) +#define MCDE_CRC_C2EN BIT(3) +#define MCDE_CRC_SYCEN0 BIT(7) +#define MCDE_CRC_SYCEN1 BIT(8) +#define MCDE_CRC_SIZE1 BIT(9) +#define MCDE_CRC_SIZE2 BIT(10) +#define MCDE_CRC_YUVCONVC1EN BIT(15) +#define MCDE_CRC_CS1EN BIT(16) +#define MCDE_CRC_CS2EN BIT(17) +#define MCDE_CRC_CS1POL BIT(19) +#define MCDE_CRC_CS2POL BIT(20) +#define MCDE_CRC_CD1POL BIT(21) +#define MCDE_CRC_CD2POL BIT(22) +#define MCDE_CRC_WR1POL BIT(23) +#define MCDE_CRC_WR2POL BIT(24) +#define MCDE_CRC_RD1POL BIT(25) +#define MCDE_CRC_RD2POL BIT(26) +#define MCDE_CRC_SYNCCTRL_SHIFT 29 +#define MCDE_CRC_SYNCCTRL_MASK 0x60000000 +#define MCDE_CRC_SYNCCTRL_NO_SYNC 0 +#define MCDE_CRC_SYNCCTRL_DBI0 1 +#define MCDE_CRC_SYNCCTRL_DBI1 2 +#define MCDE_CRC_SYNCCTRL_PING_PONG 3 +#define MCDE_CRC_CLAMPC1EN BIT(31) + +#define MCDE_VSCRC0 0x00000C5C +#define MCDE_VSCRC1 0x00000C60 +#define MCDE_VSCRC_VSPMIN_MASK 0x00000FFF +#define MCDE_VSCRC_VSPMAX_SHIFT 12 +#define MCDE_VSCRC_VSPMAX_MASK 0x00FFF000 +#define MCDE_VSCRC_VSPDIV_SHIFT 24 +#define MCDE_VSCRC_VSPDIV_MASK 0x07000000 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_1 0 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_2 1 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_4 2 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_8 3 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_16 4 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_32 5 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_64 6 +#define MCDE_VSCRC_VSPDIV_MCDECLK_DIV_128 7 +#define MCDE_VSCRC_VSPOL BIT(27) /* 0 active high, 1 active low */ +#define MCDE_VSCRC_VSSEL BIT(28) /* 0 VSYNC0, 1 VSYNC1 */ +#define MCDE_VSCRC_VSDBL BIT(29) + +/* Channel config 0..3 */ +#define MCDE_CHNL0CONF 0x00000600 +#define MCDE_CHNL1CONF 0x00000620 +#define MCDE_CHNL2CONF 0x00000640 +#define MCDE_CHNL3CONF 0x00000660 +#define MCDE_CHNLXCONF_PPL_SHIFT 0 +#define MCDE_CHNLXCONF_PPL_MASK 0x000007FF +#define MCDE_CHNLXCONF_LPF_SHIFT 16 +#define MCDE_CHNLXCONF_LPF_MASK 0x07FF0000 +#define MCDE_MAX_WIDTH 2048 + +/* Channel status 0..3 */ +#define MCDE_CHNL0STAT 0x00000604 +#define MCDE_CHNL1STAT 0x00000624 +#define MCDE_CHNL2STAT 0x00000644 +#define MCDE_CHNL3STAT 0x00000664 +#define MCDE_CHNLXSTAT_CHNLRD BIT(0) +#define MCDE_CHNLXSTAT_CHNLA BIT(1) +#define MCDE_CHNLXSTAT_CHNLBLBCKGND_EN BIT(16) +#define MCDE_CHNLXSTAT_PPLX2_V422 BIT(17) +#define MCDE_CHNLXSTAT_LPFX2_V422 BIT(18) + +/* Sync settings for channel 0..3 */ +#define MCDE_CHNL0SYNCHMOD 0x00000608 +#define MCDE_CHNL1SYNCHMOD 0x00000628 +#define MCDE_CHNL2SYNCHMOD 0x00000648 +#define MCDE_CHNL3SYNCHMOD 0x00000668 + +#define MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT 0 +#define MCDE_CHNLXSYNCHMOD_SRC_SYNCH_MASK 0x00000003 +#define MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE 0 +#define MCDE_CHNLXSYNCHMOD_SRC_SYNCH_NO_SYNCH 1 +#define MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE 2 +#define MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT 2 +#define MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_MASK 0x0000001C +#define MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER 0 +#define MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 1 +#define MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE1 2 + +/* Software sync triggers for channel 0..3 */ +#define MCDE_CHNL0SYNCHSW 0x0000060C +#define MCDE_CHNL1SYNCHSW 0x0000062C +#define MCDE_CHNL2SYNCHSW 0x0000064C +#define MCDE_CHNL3SYNCHSW 0x0000066C +#define MCDE_CHNLXSYNCHSW_SW_TRIG BIT(0) + +#define MCDE_CHNL0BCKGNDCOL 0x00000610 +#define MCDE_CHNL1BCKGNDCOL 0x00000630 +#define MCDE_CHNL2BCKGNDCOL 0x00000650 +#define MCDE_CHNL3BCKGNDCOL 0x00000670 +#define MCDE_CHNLXBCKGNDCOL_B_SHIFT 0 +#define MCDE_CHNLXBCKGNDCOL_B_MASK 0x000000FF +#define MCDE_CHNLXBCKGNDCOL_G_SHIFT 8 +#define MCDE_CHNLXBCKGNDCOL_G_MASK 0x0000FF00 +#define MCDE_CHNLXBCKGNDCOL_R_SHIFT 16 +#define MCDE_CHNLXBCKGNDCOL_R_MASK 0x00FF0000 + +#define MCDE_CHNL0MUXING 0x00000614 +#define MCDE_CHNL1MUXING 0x00000634 +#define MCDE_CHNL2MUXING 0x00000654 +#define MCDE_CHNL3MUXING 0x00000674 +#define MCDE_CHNLXMUXING_FIFO_ID_FIFO_A 0 +#define MCDE_CHNLXMUXING_FIFO_ID_FIFO_B 1 +#define MCDE_CHNLXMUXING_FIFO_ID_FIFO_C0 2 +#define MCDE_CHNLXMUXING_FIFO_ID_FIFO_C1 3 + +/* Pixel processing control registers for channel A B,  */ +#define MCDE_CRA0 0x00000800 +#define MCDE_CRB0 0x00000A00 +#define MCDE_CRX0_FLOEN BIT(0) +#define MCDE_CRX0_POWEREN BIT(1) +#define MCDE_CRX0_BLENDEN BIT(2) +#define MCDE_CRX0_AFLICKEN BIT(3) +#define MCDE_CRX0_PALEN BIT(4) +#define MCDE_CRX0_DITHEN BIT(5) +#define MCDE_CRX0_GAMEN BIT(6) +#define MCDE_CRX0_KEYCTRL_SHIFT 7 +#define MCDE_CRX0_KEYCTRL_MASK 0x00000380 +#define MCDE_CRX0_KEYCTRL_OFF 0 +#define MCDE_CRX0_KEYCTRL_ALPHA_RGB 1 +#define MCDE_CRX0_KEYCTRL_RGB 2 +#define MCDE_CRX0_KEYCTRL_FALPHA_FRGB 4 +#define MCDE_CRX0_KEYCTRL_FRGB 5 +#define MCDE_CRX0_BLENDCTRL BIT(10) +#define MCDE_CRX0_FLICKMODE_SHIFT 11 +#define MCDE_CRX0_FLICKMODE_MASK 0x00001800 +#define MCDE_CRX0_FLICKMODE_FORCE_FILTER_0 0 +#define MCDE_CRX0_FLICKMODE_ADAPTIVE 1 +#define MCDE_CRX0_FLICKMODE_TEST_MODE 2 +#define MCDE_CRX0_FLOCKFORMAT_RGB BIT(13) /* 0 = YCVCR */ +#define MCDE_CRX0_PALMODE_GAMMA BIT(14) /* 0 = palette */ +#define MCDE_CRX0_OLEDEN BIT(15) +#define MCDE_CRX0_ALPHABLEND_SHIFT 16 +#define MCDE_CRX0_ALPHABLEND_MASK 0x00FF0000 +#define MCDE_CRX0_ROTEN BIT(24) + +#define MCDE_CRA1 0x00000804 +#define MCDE_CRB1 0x00000A04 +#define MCDE_CRX1_PCD_SHIFT 0 +#define MCDE_CRX1_PCD_MASK 0x000003FF +#define MCDE_CRX1_CLKSEL_SHIFT 10 +#define MCDE_CRX1_CLKSEL_MASK 0x00001C00 +#define MCDE_CRX1_CLKSEL_CLKPLL72 0 +#define MCDE_CRX1_CLKSEL_CLKPLL27 2 +#define MCDE_CRX1_CLKSEL_TV1CLK 3 +#define MCDE_CRX1_CLKSEL_TV2CLK 4 +#define MCDE_CRX1_CLKSEL_MCDECLK 5 +#define MCDE_CRX1_CDWIN_SHIFT 13 +#define MCDE_CRX1_CDWIN_MASK 0x0001E000 +#define MCDE_CRX1_CDWIN_8BPP_C1 0 +#define MCDE_CRX1_CDWIN_12BPP_C1 1 +#define MCDE_CRX1_CDWIN_12BPP_C2 2 +#define MCDE_CRX1_CDWIN_16BPP_C1 3 +#define MCDE_CRX1_CDWIN_16BPP_C2 4 +#define MCDE_CRX1_CDWIN_16BPP_C3 5 +#define MCDE_CRX1_CDWIN_18BPP_C1 6 +#define MCDE_CRX1_CDWIN_18BPP_C2 7 +#define MCDE_CRX1_CDWIN_24BPP 8 +#define MCDE_CRX1_OUTBPP_SHIFT 25 +#define MCDE_CRX1_OUTBPP_MASK 0x1E000000 +#define MCDE_CRX1_OUTBPP_MONO1 0 +#define MCDE_CRX1_OUTBPP_MONO2 1 +#define MCDE_CRX1_OUTBPP_MONO4 2 +#define MCDE_CRX1_OUTBPP_MONO8 3 +#define MCDE_CRX1_OUTBPP_8BPP 4 +#define MCDE_CRX1_OUTBPP_12BPP 5 +#define MCDE_CRX1_OUTBPP_15BPP 6 +#define MCDE_CRX1_OUTBPP_16BPP 7 +#define MCDE_CRX1_OUTBPP_18BPP 8 +#define MCDE_CRX1_OUTBPP_24BPP 9 +#define MCDE_CRX1_BCD BIT(29) +#define MCDE_CRA1_CLKTYPE_TVXCLKSEL1 BIT(30) /* 0 = TVXCLKSEL1 */ + +#define MCDE_COLKEYA 0x00000808 +#define MCDE_COLKEYB 0x00000A08 + +#define MCDE_FCOLKEYA 0x0000080C +#define MCDE_FCOLKEYB 0x00000A0C + +#define MCDE_RGBCONV1A 0x00000810 +#define MCDE_RGBCONV1B 0x00000A10 + +#define MCDE_RGBCONV2A 0x00000814 +#define MCDE_RGBCONV2B 0x00000A14 + +#define MCDE_RGBCONV3A 0x00000818 +#define MCDE_RGBCONV3B 0x00000A18 + +#define MCDE_RGBCONV4A 0x0000081C +#define MCDE_RGBCONV4B 0x00000A1C + +#define MCDE_RGBCONV5A 0x00000820 +#define MCDE_RGBCONV5B 0x00000A20 + +#define MCDE_RGBCONV6A 0x00000824 +#define MCDE_RGBCONV6B 0x00000A24 + +/* Rotation */ +#define MCDE_ROTACONF 0x0000087C +#define MCDE_ROTBCONF 0x00000A7C + +#define MCDE_SYNCHCONFA 0x00000880 +#define MCDE_SYNCHCONFB 0x00000A80 + +/* Channel A+B control registers */ +#define MCDE_CTRLA 0x00000884 +#define MCDE_CTRLB 0x00000A84 +#define MCDE_CTRLX_FIFOWTRMRK_SHIFT 0 +#define MCDE_CTRLX_FIFOWTRMRK_MASK 0x000003FF +#define MCDE_CTRLX_FIFOEMPTY BIT(12) +#define MCDE_CTRLX_FIFOFULL BIT(13) +#define MCDE_CTRLX_FORMID_SHIFT 16 +#define MCDE_CTRLX_FORMID_MASK 0x00070000 +#define MCDE_CTRLX_FORMID_DSI0VID 0 +#define MCDE_CTRLX_FORMID_DSI0CMD 1 +#define MCDE_CTRLX_FORMID_DSI1VID 2 +#define MCDE_CTRLX_FORMID_DSI1CMD 3 +#define MCDE_CTRLX_FORMID_DSI2VID 4 +#define MCDE_CTRLX_FORMID_DSI2CMD 5 +#define MCDE_CTRLX_FORMID_DPIA 0 +#define MCDE_CTRLX_FORMID_DPIB 1 +#define MCDE_CTRLX_FORMTYPE_SHIFT 20 +#define MCDE_CTRLX_FORMTYPE_MASK 0x00700000 +#define MCDE_CTRLX_FORMTYPE_DPITV 0 +#define MCDE_CTRLX_FORMTYPE_DBI 1 +#define MCDE_CTRLX_FORMTYPE_DSI 2 + +#define MCDE_DSIVID0CONF0 0x00000E00 +#define MCDE_DSICMD0CONF0 0x00000E20 +#define MCDE_DSIVID1CONF0 0x00000E40 +#define MCDE_DSICMD1CONF0 0x00000E60 +#define MCDE_DSIVID2CONF0 0x00000E80 +#define MCDE_DSICMD2CONF0 0x00000EA0 +#define MCDE_DSICONF0_BLANKING_SHIFT 0 +#define MCDE_DSICONF0_BLANKING_MASK 0x000000FF +#define MCDE_DSICONF0_VID_MODE_CMD 0 +#define MCDE_DSICONF0_VID_MODE_VID BIT(12) +#define MCDE_DSICONF0_CMD8 BIT(13) +#define MCDE_DSICONF0_BIT_SWAP BIT(16) +#define MCDE_DSICONF0_BYTE_SWAP BIT(17) +#define MCDE_DSICONF0_DCSVID_NOTGEN BIT(18) +#define MCDE_DSICONF0_PACKING_SHIFT 20 +#define MCDE_DSICONF0_PACKING_MASK 0x00700000 +#define MCDE_DSICONF0_PACKING_RGB565 0 +#define MCDE_DSICONF0_PACKING_RGB666 1 +#define MCDE_DSICONF0_PACKING_RGB666_PACKED 2 +#define MCDE_DSICONF0_PACKING_RGB888 3 +#define MCDE_DSICONF0_PACKING_HDTV 4 + +#define MCDE_DSIVID0FRAME 0x00000E04 +#define MCDE_DSICMD0FRAME 0x00000E24 +#define MCDE_DSIVID1FRAME 0x00000E44 +#define MCDE_DSICMD1FRAME 0x00000E64 +#define MCDE_DSIVID2FRAME 0x00000E84 +#define MCDE_DSICMD2FRAME 0x00000EA4 + +#define MCDE_DSIVID0PKT 0x00000E08 +#define MCDE_DSICMD0PKT 0x00000E28 +#define MCDE_DSIVID1PKT 0x00000E48 +#define MCDE_DSICMD1PKT 0x00000E68 +#define MCDE_DSIVID2PKT 0x00000E88 +#define MCDE_DSICMD2PKT 0x00000EA8 + +#define MCDE_DSIVID0SYNC 0x00000E0C +#define MCDE_DSICMD0SYNC 0x00000E2C +#define MCDE_DSIVID1SYNC 0x00000E4C +#define MCDE_DSICMD1SYNC 0x00000E6C +#define MCDE_DSIVID2SYNC 0x00000E8C +#define MCDE_DSICMD2SYNC 0x00000EAC + +#define MCDE_DSIVID0CMDW 0x00000E10 +#define MCDE_DSICMD0CMDW 0x00000E30 +#define MCDE_DSIVID1CMDW 0x00000E50 +#define MCDE_DSICMD1CMDW 0x00000E70 +#define MCDE_DSIVID2CMDW 0x00000E90 +#define MCDE_DSICMD2CMDW 0x00000EB0 +#define MCDE_DSIVIDXCMDW_CMDW_CONTINUE_SHIFT 0 +#define MCDE_DSIVIDXCMDW_CMDW_CONTINUE_MASK 0x0000FFFF +#define MCDE_DSIVIDXCMDW_CMDW_START_SHIFT 16 +#define MCDE_DSIVIDXCMDW_CMDW_START_MASK 0xFFFF0000 + +#define MCDE_DSIVID0DELAY0 0x00000E14 +#define MCDE_DSICMD0DELAY0 0x00000E34 +#define MCDE_DSIVID1DELAY0 0x00000E54 +#define MCDE_DSICMD1DELAY0 0x00000E74 +#define MCDE_DSIVID2DELAY0 0x00000E94 +#define MCDE_DSICMD2DELAY0 0x00000EB4 + +#define MCDE_DSIVID0DELAY1 0x00000E18 +#define MCDE_DSICMD0DELAY1 0x00000E38 +#define MCDE_DSIVID1DELAY1 0x00000E58 +#define MCDE_DSICMD1DELAY1 0x00000E78 +#define MCDE_DSIVID2DELAY1 0x00000E98 +#define MCDE_DSICMD2DELAY1 0x00000EB8 + +#endif /* __DRM_MCDE_DISPLAY_REGS */ diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h new file mode 100644 index 000000000000..dab4db021231 --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_drm.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org> + * Parts of this file were based on the MCDE driver by Marcus Lorentzon + * (C) ST-Ericsson SA 2013 + */ +#include <drm/drm_simple_kms_helper.h> + +#ifndef _MCDE_DRM_H_ +#define _MCDE_DRM_H_ + +struct mcde { +	struct drm_device drm; +	struct device *dev; +	struct drm_panel *panel; +	struct drm_bridge *bridge; +	struct drm_connector *connector; +	struct drm_simple_display_pipe pipe; +	struct mipi_dsi_device *mdsi; +	s16 stride; +	bool te_sync; +	bool oneshot_mode; +	unsigned int flow_active; +	spinlock_t flow_lock; /* Locks the channel flow control */ + +	void __iomem *regs; + +	struct clk *mcde_clk; +	struct clk *lcd_clk; +	struct clk *hdmi_clk; + +	struct regulator *epod; +	struct regulator *vana; +}; + +bool mcde_dsi_irq(struct mipi_dsi_device *mdsi); +void mcde_dsi_te_request(struct mipi_dsi_device *mdsi); +extern struct platform_driver mcde_dsi_driver; + +void mcde_display_irq(struct mcde *mcde); +void mcde_display_disable_irqs(struct mcde *mcde); +int mcde_display_init(struct drm_device *drm); + +#endif /* _MCDE_DRM_H_ */ diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c new file mode 100644 index 000000000000..baf63fb6850a --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org> + * Parts of this file were based on the MCDE driver by Marcus Lorentzon + * (C) ST-Ericsson SA 2013 + */ + +/** + * DOC: ST-Ericsson MCDE Driver + * + * The MCDE (short for multi-channel display engine) is a graphics + * controller found in the Ux500 chipsets, such as NovaThor U8500. + * It was initially conceptualized by ST Microelectronics for the + * successor of the Nomadik line, STn8500 but productified in the + * ST-Ericsson U8500 where is was used for mass-market deployments + * in Android phones from Samsung and Sony Ericsson. + * + * It can do 1080p30 on SDTV CCIR656, DPI-2, DBI-2 or DSI for + * panels with or without frame buffering and can convert most + * input formats including most variants of RGB and YUV. + * + * The hardware has four display pipes, and the layout is a little + * bit like this: + * + * Memory     -> Overlay -> Channel -> FIFO -> 5 formatters -> DSI/DPI + * External      0..5       0..3       A,B,    3 x DSI         bridge + * source 0..9                         C0,C1   2 x DPI + * + * FIFOs A and B are for LCD and HDMI while FIFO CO/C1 are for + * panels with embedded buffer. + * 3 of the formatters are for DSI. + * 2 of the formatters are for DPI. + * + * Behind the formatters are the DSI or DPI ports that route to + * the external pins of the chip. As there are 3 DSI ports and one + * DPI port, it is possible to configure up to 4 display pipelines + * (effectively using channels 0..3) for concurrent use. + * + * In the current DRM/KMS setup, we use one external source, one overlay, + * one FIFO and one formatter which we connect to the simple CMA framebuffer + * helpers. We then provide a bridge to the DSI port, and on the DSI port + * bridge we connect hang a panel bridge or other bridge. This may be subject + * to change as we exploit more of the hardware capabilities. + * + * TODO: + * - Enabled damaged rectangles using drm_plane_enable_fb_damage_clips() + *   so we can selectively just transmit the damaged area to a + *   command-only display. + * - Enable mixing of more planes, possibly at the cost of moving away + *   from using the simple framebuffer pipeline. + * - Enable output to bridges such as the AV8100 HDMI encoder from + *   the DSI bridge. + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/dma-buf.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_panel.h> +#include <drm/drm_vblank.h> + +#include "mcde_drm.h" + +#define DRIVER_DESC	"DRM module for MCDE" + +#define MCDE_CR 0x00000000 +#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0 +#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F +#define MCDE_CR_IFIFOCTRLEN BIT(15) +#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16) +#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17) +#define MCDE_CR_AUTOCLKG_EN BIT(30) +#define MCDE_CR_MCDEEN BIT(31) + +#define MCDE_CONF0 0x00000004 +#define MCDE_CONF0_SYNCMUX0 BIT(0) +#define MCDE_CONF0_SYNCMUX1 BIT(1) +#define MCDE_CONF0_SYNCMUX2 BIT(2) +#define MCDE_CONF0_SYNCMUX3 BIT(3) +#define MCDE_CONF0_SYNCMUX4 BIT(4) +#define MCDE_CONF0_SYNCMUX5 BIT(5) +#define MCDE_CONF0_SYNCMUX6 BIT(6) +#define MCDE_CONF0_SYNCMUX7 BIT(7) +#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12 +#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000 +#define MCDE_CONF0_OUTMUX0_SHIFT 16 +#define MCDE_CONF0_OUTMUX0_MASK 0x00070000 +#define MCDE_CONF0_OUTMUX1_SHIFT 19 +#define MCDE_CONF0_OUTMUX1_MASK 0x00380000 +#define MCDE_CONF0_OUTMUX2_SHIFT 22 +#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000 +#define MCDE_CONF0_OUTMUX3_SHIFT 25 +#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000 +#define MCDE_CONF0_OUTMUX4_SHIFT 28 +#define MCDE_CONF0_OUTMUX4_MASK 0x70000000 + +#define MCDE_SSP 0x00000008 +#define MCDE_AIS 0x00000100 +#define MCDE_IMSCERR 0x00000110 +#define MCDE_RISERR 0x00000120 +#define MCDE_MISERR 0x00000130 +#define MCDE_SISERR 0x00000140 + +#define MCDE_PID 0x000001FC +#define MCDE_PID_METALFIX_VERSION_SHIFT 0 +#define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF +#define MCDE_PID_DEVELOPMENT_VERSION_SHIFT 8 +#define MCDE_PID_DEVELOPMENT_VERSION_MASK 0x0000FF00 +#define MCDE_PID_MINOR_VERSION_SHIFT 16 +#define MCDE_PID_MINOR_VERSION_MASK 0x00FF0000 +#define MCDE_PID_MAJOR_VERSION_SHIFT 24 +#define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000 + +static const struct drm_mode_config_funcs mcde_mode_config_funcs = { +	.fb_create = drm_gem_fb_create_with_dirty, +	.atomic_check = drm_atomic_helper_check, +	.atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_mode_config_helper_funcs mcde_mode_config_helpers = { +	/* +	 * Using this function is necessary to commit atomic updates +	 * that need the CRTC to be enabled before a commit, as is +	 * the case with e.g. DSI displays. +	 */ +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static irqreturn_t mcde_irq(int irq, void *data) +{ +	struct mcde *mcde = data; +	u32 val; + +	val = readl(mcde->regs + MCDE_MISERR); + +	mcde_display_irq(mcde); + +	if (val) +		dev_info(mcde->dev, "some error IRQ\n"); +	writel(val, mcde->regs + MCDE_RISERR); + +	return IRQ_HANDLED; +} + +static int mcde_modeset_init(struct drm_device *drm) +{ +	struct drm_mode_config *mode_config; +	struct mcde *mcde = drm->dev_private; +	int ret; + +	if (!mcde->bridge) { +		dev_err(drm->dev, "no display output bridge yet\n"); +		return -EPROBE_DEFER; +	} + +	mode_config = &drm->mode_config; +	mode_config->funcs = &mcde_mode_config_funcs; +	mode_config->helper_private = &mcde_mode_config_helpers; +	/* This hardware can do 1080p */ +	mode_config->min_width = 1; +	mode_config->max_width = 1920; +	mode_config->min_height = 1; +	mode_config->max_height = 1080; + +	/* +	 * Currently we only support vblank handling on the DSI bridge, using +	 * TE synchronization. If TE sync is not set up, it is still possible +	 * to push out a single update on demand, but this is hard for DRM to +	 * exploit. +	 */ +	if (mcde->te_sync) { +		ret = drm_vblank_init(drm, 1); +		if (ret) { +			dev_err(drm->dev, "failed to init vblank\n"); +			goto out_config; +		} +	} + +	ret = mcde_display_init(drm); +	if (ret) { +		dev_err(drm->dev, "failed to init display\n"); +		goto out_config; +	} + +	/* +	 * Attach the DSI bridge +	 * +	 * TODO: when adding support for the DPI bridge or several DSI bridges, +	 * we selectively connect the bridge(s) here instead of this simple +	 * attachment. +	 */ +	ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe, +						    mcde->bridge); +	if (ret) { +		dev_err(drm->dev, "failed to attach display output bridge\n"); +		goto out_config; +	} + +	drm_mode_config_reset(drm); +	drm_kms_helper_poll_init(drm); +	drm_fbdev_generic_setup(drm, 32); + +	return 0; + +out_config: +	drm_mode_config_cleanup(drm); +	return ret; +} + +static void mcde_release(struct drm_device *drm) +{ +	struct mcde *mcde = drm->dev_private; + +	drm_mode_config_cleanup(drm); +	drm_dev_fini(drm); +	kfree(mcde); +} + +DEFINE_DRM_GEM_CMA_FOPS(drm_fops); + +static struct drm_driver mcde_drm_driver = { +	.driver_features = +		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, +	.release = mcde_release, +	.lastclose = drm_fb_helper_lastclose, +	.ioctls = NULL, +	.fops = &drm_fops, +	.name = "mcde", +	.desc = DRIVER_DESC, +	.date = "20180529", +	.major = 1, +	.minor = 0, +	.patchlevel = 0, +	.dumb_create = drm_gem_cma_dumb_create, +	.gem_free_object_unlocked = drm_gem_cma_free_object, +	.gem_vm_ops = &drm_gem_cma_vm_ops, + +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd, +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle, +	.gem_prime_import = drm_gem_prime_import, +	.gem_prime_export = drm_gem_prime_export, +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table, +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, +	.gem_prime_vmap = drm_gem_cma_prime_vmap, +	.gem_prime_vunmap = drm_gem_cma_prime_vunmap, +	.gem_prime_mmap = drm_gem_cma_prime_mmap, +}; + +static int mcde_drm_bind(struct device *dev) +{ +	struct drm_device *drm = dev_get_drvdata(dev); +	int ret; + +	drm_mode_config_init(drm); + +	ret = component_bind_all(drm->dev, drm); +	if (ret) { +		dev_err(dev, "can't bind component devices\n"); +		return ret; +	} + +	ret = mcde_modeset_init(drm); +	if (ret) +		goto unbind; + +	ret = drm_dev_register(drm, 0); +	if (ret < 0) +		goto unbind; + +	return 0; + +unbind: +	component_unbind_all(drm->dev, drm); +	return ret; +} + +static void mcde_drm_unbind(struct device *dev) +{ +	struct drm_device *drm = dev_get_drvdata(dev); + +	drm_dev_unregister(drm); +	drm_atomic_helper_shutdown(drm); +	component_unbind_all(drm->dev, drm); +} + +static const struct component_master_ops mcde_drm_comp_ops = { +	.bind = mcde_drm_bind, +	.unbind = mcde_drm_unbind, +}; + +static struct platform_driver *const mcde_component_drivers[] = { +	&mcde_dsi_driver, +}; + +static int mcde_compare_dev(struct device *dev, void *data) +{ +	return dev == data; +} + +static int mcde_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct drm_device *drm; +	struct mcde *mcde; +	struct component_match *match; +	struct resource *res; +	u32 pid; +	u32 val; +	int irq; +	int ret; +	int i; + +	mcde = kzalloc(sizeof(*mcde), GFP_KERNEL); +	if (!mcde) +		return -ENOMEM; +	mcde->dev = dev; + +	ret = drm_dev_init(&mcde->drm, &mcde_drm_driver, dev); +	if (ret) { +		kfree(mcde); +		return ret; +	} +	drm = &mcde->drm; +	drm->dev_private = mcde; +	platform_set_drvdata(pdev, drm); + +	/* Enable use of the TE signal and interrupt */ +	mcde->te_sync = true; +	/* Enable continuous updates: this is what Linux' framebuffer expects */ +	mcde->oneshot_mode = false; +	drm->dev_private = mcde; + +	/* First obtain and turn on the main power */ +	mcde->epod = devm_regulator_get(dev, "epod"); +	if (IS_ERR(mcde->epod)) { +		ret = PTR_ERR(mcde->epod); +		dev_err(dev, "can't get EPOD regulator\n"); +		goto dev_unref; +	} +	ret = regulator_enable(mcde->epod); +	if (ret) { +		dev_err(dev, "can't enable EPOD regulator\n"); +		goto dev_unref; +	} +	mcde->vana = devm_regulator_get(dev, "vana"); +	if (IS_ERR(mcde->vana)) { +		ret = PTR_ERR(mcde->vana); +		dev_err(dev, "can't get VANA regulator\n"); +		goto regulator_epod_off; +	} +	ret = regulator_enable(mcde->vana); +	if (ret) { +		dev_err(dev, "can't enable VANA regulator\n"); +		goto regulator_epod_off; +	} +	/* +	 * The vendor code uses ESRAM (onchip RAM) and need to activate +	 * the v-esram34 regulator, but we don't use that yet +	 */ + +	/* Clock the silicon so we can access the registers */ +	mcde->mcde_clk = devm_clk_get(dev, "mcde"); +	if (IS_ERR(mcde->mcde_clk)) { +		dev_err(dev, "unable to get MCDE main clock\n"); +		ret = PTR_ERR(mcde->mcde_clk); +		goto regulator_off; +	} +	ret = clk_prepare_enable(mcde->mcde_clk); +	if (ret) { +		dev_err(dev, "failed to enable MCDE main clock\n"); +		goto regulator_off; +	} +	dev_info(dev, "MCDE clk rate %lu Hz\n", clk_get_rate(mcde->mcde_clk)); + +	mcde->lcd_clk = devm_clk_get(dev, "lcd"); +	if (IS_ERR(mcde->lcd_clk)) { +		dev_err(dev, "unable to get LCD clock\n"); +		ret = PTR_ERR(mcde->lcd_clk); +		goto clk_disable; +	} +	mcde->hdmi_clk = devm_clk_get(dev, "hdmi"); +	if (IS_ERR(mcde->hdmi_clk)) { +		dev_err(dev, "unable to get HDMI clock\n"); +		ret = PTR_ERR(mcde->hdmi_clk); +		goto clk_disable; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	mcde->regs = devm_ioremap_resource(dev, res); +	if (IS_ERR(mcde->regs)) { +		dev_err(dev, "no MCDE regs\n"); +		ret = -EINVAL; +		goto clk_disable; +	} + +	irq = platform_get_irq(pdev, 0); +	if (!irq) { +		ret = -EINVAL; +		goto clk_disable; +	} + +	ret = devm_request_irq(dev, irq, mcde_irq, 0, "mcde", mcde); +	if (ret) { +		dev_err(dev, "failed to request irq %d\n", ret); +		goto clk_disable; +	} + +	/* +	 * Check hardware revision, we only support U8500v2 version +	 * as this was the only version used for mass market deployment, +	 * but surely you can add more versions if you have them and +	 * need them. +	 */ +	pid = readl(mcde->regs + MCDE_PID); +	dev_info(dev, "found MCDE HW revision %d.%d (dev %d, metal fix %d)\n", +		 (pid & MCDE_PID_MAJOR_VERSION_MASK) +		 >> MCDE_PID_MAJOR_VERSION_SHIFT, +		 (pid & MCDE_PID_MINOR_VERSION_MASK) +		 >> MCDE_PID_MINOR_VERSION_SHIFT, +		 (pid & MCDE_PID_DEVELOPMENT_VERSION_MASK) +		 >> MCDE_PID_DEVELOPMENT_VERSION_SHIFT, +		 (pid & MCDE_PID_METALFIX_VERSION_MASK) +		 >> MCDE_PID_METALFIX_VERSION_SHIFT); +	if (pid != 0x03000800) { +		dev_err(dev, "unsupported hardware revision\n"); +		ret = -ENODEV; +		goto clk_disable; +	} + +	/* Set up the main control, watermark level at 7 */ +	val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; +	/* 24 bits DPI: connect LSB Ch B to D[0:7] */ +	val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; +	/* TV out: connect LSB Ch B to D[8:15] */ +	val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; +	/* Don't care about this muxing */ +	val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; +	/* 24 bits DPI: connect MID Ch B to D[24:31] */ +	val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; +	/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */ +	val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; +	/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */ +	writel(val, mcde->regs + MCDE_CONF0); + +	/* Enable automatic clock gating */ +	val = readl(mcde->regs + MCDE_CR); +	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; +	writel(val, mcde->regs + MCDE_CR); + +	/* Clear any pending interrupts */ +	mcde_display_disable_irqs(mcde); +	writel(0, mcde->regs + MCDE_IMSCERR); +	writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); + +	/* Spawn child devices for the DSI ports */ +	devm_of_platform_populate(dev); + +	/* Create something that will match the subdrivers when we bind */ +	for (i = 0; i < ARRAY_SIZE(mcde_component_drivers); i++) { +		struct device_driver *drv = &mcde_component_drivers[i]->driver; +		struct device *p = NULL, *d; + +		while ((d = bus_find_device(&platform_bus_type, p, drv, +					    (void *)platform_bus_type.match))) { +			put_device(p); +			component_match_add(dev, &match, mcde_compare_dev, d); +			p = d; +		} +		put_device(p); +	} +	if (IS_ERR(match)) { +		dev_err(dev, "could not create component match\n"); +		ret = PTR_ERR(match); +		goto clk_disable; +	} +	ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops, +					      match); +	if (ret) { +		dev_err(dev, "failed to add component master\n"); +		goto clk_disable; +	} +	return 0; + +clk_disable: +	clk_disable_unprepare(mcde->mcde_clk); +regulator_off: +	regulator_disable(mcde->vana); +regulator_epod_off: +	regulator_disable(mcde->epod); +dev_unref: +	drm_dev_put(drm); +	return ret; + +} + +static int mcde_remove(struct platform_device *pdev) +{ +	struct drm_device *drm = platform_get_drvdata(pdev); +	struct mcde *mcde = drm->dev_private; + +	component_master_del(&pdev->dev, &mcde_drm_comp_ops); +	clk_disable_unprepare(mcde->mcde_clk); +	regulator_disable(mcde->vana); +	regulator_disable(mcde->epod); +	drm_dev_put(drm); + +	return 0; +} + +static const struct of_device_id mcde_of_match[] = { +	{ +		.compatible = "ste,mcde", +	}, +	{}, +}; + +static struct platform_driver mcde_driver = { +	.driver = { +		.name           = "mcde", +		.of_match_table = of_match_ptr(mcde_of_match), +	}, +	.probe = mcde_probe, +	.remove = mcde_remove, +}; + +static struct platform_driver *const component_drivers[] = { +	&mcde_dsi_driver, +}; + +static int __init mcde_drm_register(void) +{ +	int ret; + +	ret = platform_register_drivers(component_drivers, +					ARRAY_SIZE(component_drivers)); +	if (ret) +		return ret; + +	return platform_driver_register(&mcde_driver); +} + +static void __exit mcde_drm_unregister(void) +{ +	platform_unregister_drivers(component_drivers, +				    ARRAY_SIZE(component_drivers)); +	platform_driver_unregister(&mcde_driver); +} + +module_init(mcde_drm_register); +module_exit(mcde_drm_unregister); + +MODULE_ALIAS("platform:mcde-drm"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c new file mode 100644 index 000000000000..07f7090d08b3 --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -0,0 +1,1044 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <video/mipi_display.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_encoder.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "mcde_drm.h" +#include "mcde_dsi_regs.h" + +#define DSI_DEFAULT_LP_FREQ_HZ	19200000 +#define DSI_DEFAULT_HS_FREQ_HZ	420160000 + +/* PRCMU DSI reset registers */ +#define PRCM_DSI_SW_RESET 0x324 +#define PRCM_DSI_SW_RESET_DSI0_SW_RESETN BIT(0) +#define PRCM_DSI_SW_RESET_DSI1_SW_RESETN BIT(1) +#define PRCM_DSI_SW_RESET_DSI2_SW_RESETN BIT(2) + +struct mcde_dsi { +	struct device *dev; +	struct mcde *mcde; +	struct drm_bridge bridge; +	struct drm_connector connector; +	struct drm_panel *panel; +	struct drm_bridge *bridge_out; +	struct mipi_dsi_host dsi_host; +	struct mipi_dsi_device *mdsi; +	struct clk *hs_clk; +	struct clk *lp_clk; +	unsigned long hs_freq; +	unsigned long lp_freq; +	bool unused; + +	void __iomem *regs; +	struct regmap *prcmu; +}; + +static inline struct mcde_dsi *bridge_to_mcde_dsi(struct drm_bridge *bridge) +{ +	return container_of(bridge, struct mcde_dsi, bridge); +} + +static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h) +{ +	return container_of(h, struct mcde_dsi, dsi_host); +} + +static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c) +{ +	return container_of(c, struct mcde_dsi, connector); +} + +bool mcde_dsi_irq(struct mipi_dsi_device *mdsi) +{ +	struct mcde_dsi *d; +	u32 val; +	bool te_received = false; + +	d = host_to_mcde_dsi(mdsi->host); + +	dev_dbg(d->dev, "%s called\n", __func__); + +	val = readl(d->regs + DSI_DIRECT_CMD_STS_FLAG); +	if (val) +		dev_dbg(d->dev, "DSI_DIRECT_CMD_STS_FLAG = %08x\n", val); +	if (val & DSI_DIRECT_CMD_STS_WRITE_COMPLETED) +		dev_dbg(d->dev, "direct command write completed\n"); +	if (val & DSI_DIRECT_CMD_STS_TE_RECEIVED) { +		te_received = true; +		dev_dbg(d->dev, "direct command TE received\n"); +	} +	if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) +		dev_err(d->dev, "direct command ACK ERR received\n"); +	if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) +		dev_err(d->dev, "direct command read ERR received\n"); +	/* Mask off the ACK value and clear status */ +	writel(val, d->regs + DSI_DIRECT_CMD_STS_CLR); + +	val = readl(d->regs + DSI_CMD_MODE_STS_FLAG); +	if (val) +		dev_dbg(d->dev, "DSI_CMD_MODE_STS_FLAG = %08x\n", val); +	if (val & DSI_CMD_MODE_STS_ERR_NO_TE) +		/* This happens all the time (safe to ignore) */ +		dev_dbg(d->dev, "CMD mode no TE\n"); +	if (val & DSI_CMD_MODE_STS_ERR_TE_MISS) +		/* This happens all the time (safe to ignore) */ +		dev_dbg(d->dev, "CMD mode TE miss\n"); +	if (val & DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN) +		dev_err(d->dev, "CMD mode SD1 underrun\n"); +	if (val & DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN) +		dev_err(d->dev, "CMD mode SD2 underrun\n"); +	if (val & DSI_CMD_MODE_STS_ERR_UNWANTED_RD) +		dev_err(d->dev, "CMD mode unwanted RD\n"); +	writel(val, d->regs + DSI_CMD_MODE_STS_CLR); + +	val = readl(d->regs + DSI_DIRECT_CMD_RD_STS_FLAG); +	if (val) +		dev_dbg(d->dev, "DSI_DIRECT_CMD_RD_STS_FLAG = %08x\n", val); +	writel(val, d->regs + DSI_DIRECT_CMD_RD_STS_CLR); + +	val = readl(d->regs + DSI_TG_STS_FLAG); +	if (val) +		dev_dbg(d->dev, "DSI_TG_STS_FLAG = %08x\n", val); +	writel(val, d->regs + DSI_TG_STS_CLR); + +	val = readl(d->regs + DSI_VID_MODE_STS_FLAG); +	if (val) +		dev_err(d->dev, "some video mode error status\n"); +	writel(val, d->regs + DSI_VID_MODE_STS_CLR); + +	return te_received; +} + +static int mcde_dsi_host_attach(struct mipi_dsi_host *host, +				struct mipi_dsi_device *mdsi) +{ +	struct mcde_dsi *d = host_to_mcde_dsi(host); + +	if (mdsi->lanes < 1 || mdsi->lanes > 2) { +		DRM_ERROR("dsi device params invalid, 1 or 2 lanes supported\n"); +		return -EINVAL; +	} + +	dev_info(d->dev, "attached DSI device with %d lanes\n", mdsi->lanes); +	/* MIPI_DSI_FMT_RGB88 etc */ +	dev_info(d->dev, "format %08x, %dbpp\n", mdsi->format, +		 mipi_dsi_pixel_format_to_bpp(mdsi->format)); +	dev_info(d->dev, "mode flags: %08lx\n", mdsi->mode_flags); + +	d->mdsi = mdsi; +	if (d->mcde) +		d->mcde->mdsi = mdsi; + +	return 0; +} + +static int mcde_dsi_host_detach(struct mipi_dsi_host *host, +				struct mipi_dsi_device *mdsi) +{ +	struct mcde_dsi *d = host_to_mcde_dsi(host); + +	d->mdsi = NULL; +	if (d->mcde) +		d->mcde->mdsi = NULL; + +	return 0; +} + +#define MCDE_DSI_HOST_IS_READ(type)			    \ +	((type == MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM) || \ +	 (type == MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM) || \ +	 (type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \ +	 (type == MIPI_DSI_DCS_READ)) + +static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, +				      const struct mipi_dsi_msg *msg) +{ +	struct mcde_dsi *d = host_to_mcde_dsi(host); +	const u32 loop_delay_us = 10; /* us */ +	const u8 *tx = msg->tx_buf; +	u32 loop_counter; +	size_t txlen; +	u32 val; +	int ret; +	int i; + +	txlen = msg->tx_len; +	if (txlen > 12) { +		dev_err(d->dev, +			"dunno how to write more than 12 bytes yet\n"); +		return -EIO; +	} + +	dev_dbg(d->dev, +		"message to channel %d, %zd bytes", +		msg->channel, +		txlen); + +	/* Command "nature" */ +	if (MCDE_DSI_HOST_IS_READ(msg->type)) +		/* MCTL_MAIN_DATA_CTL already set up */ +		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ; +	else +		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE; +	/* +	 * More than 2 bytes will not fit in a single packet, so it's +	 * time to set the "long not short" bit. One byte is used by +	 * the MIPI DCS command leaving just one byte for the payload +	 * in a short package. +	 */ +	if (mipi_dsi_packet_format_is_long(msg->type)) +		val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT; +	val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; +	/* Add one to the length for the MIPI DCS command */ +	val |= txlen +		<< DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; +	val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; +	val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; +	writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); + +	/* MIPI DCS command is part of the data */ +	if (txlen > 0) { +		val = 0; +		for (i = 0; i < 4 && i < txlen; i++) +			val |= tx[i] << (i & 3) * 8; +	} +	writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0); +	if (txlen > 4) { +		val = 0; +		for (i = 0; i < 4 && (i + 4) < txlen; i++) +			val |= tx[i + 4] << (i & 3) * 8; +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1); +	} +	if (txlen > 8) { +		val = 0; +		for (i = 0; i < 4 && (i + 8) < txlen; i++) +			val |= tx[i + 8] << (i & 3) * 8; +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2); +	} +	if (txlen > 12) { +		val = 0; +		for (i = 0; i < 4 && (i + 12) < txlen; i++) +			val |= tx[i + 12] << (i & 3) * 8; +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3); +	} + +	writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); +	writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); +	/* Send command */ +	writel(1, d->regs + DSI_DIRECT_CMD_SEND); + +	loop_counter = 1000 * 1000 / loop_delay_us; +	while (!(readl(d->regs + DSI_DIRECT_CMD_STS) & +		 DSI_DIRECT_CMD_STS_WRITE_COMPLETED) +	       && --loop_counter) +		usleep_range(loop_delay_us, (loop_delay_us * 3) / 2); + +	if (!loop_counter) { +		dev_err(d->dev, "DSI write timeout!\n"); +		return -ETIME; +	} + +	val = readl(d->regs + DSI_DIRECT_CMD_STS); +	if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) { +		val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT; +		dev_err(d->dev, "error during transmission: %04x\n", +			val); +		return -EIO; +	} + +	if (!MCDE_DSI_HOST_IS_READ(msg->type)) { +		/* Return number of bytes written */ +		if (mipi_dsi_packet_format_is_long(msg->type)) +			ret = 4 + txlen; +		else +			ret = 4; +	} else { +		/* OK this is a read command, get the response */ +		u32 rdsz; +		u32 rddat; +		u8 *rx = msg->rx_buf; + +		rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY); +		rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK; +		rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT); +		for (i = 0; i < 4 && i < rdsz; i++) +			rx[i] = (rddat >> (i * 8)) & 0xff; +		ret = rdsz; +	} + +	writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); +	writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); + +	return ret; +} + +static const struct mipi_dsi_host_ops mcde_dsi_host_ops = { +	.attach = mcde_dsi_host_attach, +	.detach = mcde_dsi_host_detach, +	.transfer = mcde_dsi_host_transfer, +}; + +/* This sends a direct (short) command to request TE */ +void mcde_dsi_te_request(struct mipi_dsi_device *mdsi) +{ +	struct mcde_dsi *d; +	u32 val; + +	d = host_to_mcde_dsi(mdsi->host); + +	/* Command "nature" TE request */ +	val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TE_REQ; +	val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; +	val |= 2 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; +	val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; +	val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 << +		DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; +	writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); + +	/* Clear TE reveived and error status bits and enables them */ +	writel(DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR | +	       DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR, +	       d->regs + DSI_DIRECT_CMD_STS_CLR); +	val = readl(d->regs + DSI_DIRECT_CMD_STS_CTL); +	val |= DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN; +	val |= DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN; +	writel(val, d->regs + DSI_DIRECT_CMD_STS_CTL); + +	/* Clear and enable no TE or TE missing status */ +	writel(DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR | +	       DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR, +	       d->regs + DSI_CMD_MODE_STS_CLR); +	val = readl(d->regs + DSI_CMD_MODE_STS_CTL); +	val |= DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN; +	val |= DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN; +	writel(val, d->regs + DSI_CMD_MODE_STS_CTL); + +	/* Send this TE request command */ +	writel(1, d->regs + DSI_DIRECT_CMD_SEND); +} + +static void mcde_dsi_setup_video_mode(struct mcde_dsi *d, +				      const struct drm_display_mode *mode) +{ +	u8 bpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format); +	u64 bpl; +	u32 hfp; +	u32 hbp; +	u32 hsa; +	u32 blkline_pck, line_duration; +	u32 blkeol_pck, blkeol_duration; +	u32 val; + +	val = 0; +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) +		val |= DSI_VID_MAIN_CTL_BURST_MODE; +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { +		val |= DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE; +		val |= DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL; +	} +	/* RGB header and pixel mode */ +	switch (d->mdsi->format) { +	case MIPI_DSI_FMT_RGB565: +		val |= MIPI_DSI_PACKED_PIXEL_STREAM_16 << +			DSI_VID_MAIN_CTL_HEADER_SHIFT; +		val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_16BITS; +		break; +	case MIPI_DSI_FMT_RGB666_PACKED: +		val |= MIPI_DSI_PACKED_PIXEL_STREAM_18 << +			DSI_VID_MAIN_CTL_HEADER_SHIFT; +		val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS; +		break; +	case MIPI_DSI_FMT_RGB666: +		val |= MIPI_DSI_PIXEL_STREAM_3BYTE_18 +			<< DSI_VID_MAIN_CTL_HEADER_SHIFT; +		val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS_LOOSE; +		break; +	case MIPI_DSI_FMT_RGB888: +		val |= MIPI_DSI_PACKED_PIXEL_STREAM_24 << +			DSI_VID_MAIN_CTL_HEADER_SHIFT; +		val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_24BITS; +		break; +	default: +		dev_err(d->dev, "unknown pixel mode\n"); +		return; +	} + +	/* TODO: TVG could be enabled here */ + +	/* Send blanking packet */ +	val |= DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0; +	/* Send EOL packet */ +	val |= DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0; +	/* Recovery mode 1 */ +	val |= 1 << DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT; +	/* All other fields zero */ +	writel(val, d->regs + DSI_VID_MAIN_CTL); + +	/* Vertical frame parameters are pretty straight-forward */ +	val = mode->vdisplay << DSI_VID_VSIZE_VSA_LENGTH_SHIFT; +	/* vertical front porch */ +	val |= (mode->vsync_start - mode->vdisplay) +		<< DSI_VID_VSIZE_VFP_LENGTH_SHIFT; +	/* vertical sync active */ +	val |= (mode->vsync_end - mode->vsync_start) +		<< DSI_VID_VSIZE_VACT_LENGTH_SHIFT; +	/* vertical back porch */ +	val |= (mode->vtotal - mode->vsync_end) +		<< DSI_VID_VSIZE_VBP_LENGTH_SHIFT; +	writel(val, d->regs + DSI_VID_VSIZE); + +	/* +	 * Horizontal frame parameters: +	 * horizontal resolution is given in pixels and must be re-calculated +	 * into bytes since this is what the hardware expects. +	 * +	 * 6 + 2 is HFP header + checksum +	 */ +	hfp = (mode->hsync_start - mode->hdisplay) * bpp - 6 - 2; +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { +		/* +		 * 6 is HBP header + checksum +		 * 4 is RGB header + checksum +		 */ +		hbp = (mode->htotal - mode->hsync_end) * bpp - 4 - 6; +		/* +		 * 6 is HBP header + checksum +		 * 4 is HSW packet bytes +		 * 4 is RGB header + checksum +		 */ +		hsa = (mode->hsync_end - mode->hsync_start) * bpp - 4 - 4 - 6; +	} else { +		/* +		 * HBP includes both back porch and sync +		 * 6 is HBP header + checksum +		 * 4 is HSW packet bytes +		 * 4 is RGB header + checksum +		 */ +		hbp = (mode->htotal - mode->hsync_start) * bpp - 4 - 4 - 6; +		/* HSA is not considered in this mode and set to 0 */ +		hsa = 0; +	} +	dev_dbg(d->dev, "hfp: %u, hbp: %u, hsa: %u\n", +		hfp, hbp, hsa); + +	/* Frame parameters: horizontal sync active */ +	val = hsa << DSI_VID_HSIZE1_HSA_LENGTH_SHIFT; +	/* horizontal back porch */ +	val |= hbp << DSI_VID_HSIZE1_HBP_LENGTH_SHIFT; +	/* horizontal front porch */ +	val |= hfp << DSI_VID_HSIZE1_HFP_LENGTH_SHIFT; +	writel(val, d->regs + DSI_VID_HSIZE1); + +	/* RGB data length (bytes on one scanline) */ +	val = mode->hdisplay * (bpp / 8); +	writel(val, d->regs + DSI_VID_HSIZE2); + +	/* TODO: further adjustments for TVG mode here */ + +	/* +	 * EOL packet length from bits per line calculations: pixel clock +	 * is given in kHz, calculate the time between two pixels in +	 * picoseconds. +	 */ +	bpl = mode->clock * mode->htotal; +	bpl *= (d->hs_freq / 8); +	do_div(bpl, 1000000); /* microseconds */ +	do_div(bpl, 1000000); /* seconds */ +	bpl *= d->mdsi->lanes; +	dev_dbg(d->dev, "calculated bytes per line: %llu\n", bpl); +	/* +	 * 6 is header + checksum, header = 4 bytes, checksum = 2 bytes +	 * 4 is short packet for vsync/hsync +	 */ +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { +		/* Fixme: isn't the hsync width in pixels? */ +		blkline_pck = bpl - (mode->hsync_end - mode->hsync_start) - 6; +		val = blkline_pck << DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT; +		writel(val, d->regs + DSI_VID_BLKSIZE2); +	} else { +		blkline_pck = bpl - 4 - 6; +		val = blkline_pck << DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT; +		writel(val, d->regs + DSI_VID_BLKSIZE1); +	} + +	line_duration = (blkline_pck + 6) / d->mdsi->lanes; +	dev_dbg(d->dev, "line duration %u\n", line_duration); +	val = line_duration << DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT; +	/* +	 * This is the time to perform LP->HS on D-PHY +	 * FIXME: nowhere to get this from: DT property on the DSI? +	 */ +	val |= 0 << DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT; +	writel(val, d->regs + DSI_VID_DPHY_TIME); + +	/* Calculate block end of line */ +	blkeol_pck = bpl - mode->hdisplay * bpp - 6; +	blkeol_duration = (blkeol_pck + 6) / d->mdsi->lanes; +	dev_dbg(d->dev, "blkeol pck: %u, duration: %u\n", +		 blkeol_pck, blkeol_duration); + +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { +		/* Set up EOL clock for burst mode */ +		val = readl(d->regs + DSI_VID_BLKSIZE1); +		val |= blkeol_pck << DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT; +		writel(val, d->regs + DSI_VID_BLKSIZE1); +		writel(blkeol_pck, d->regs + DSI_VID_VCA_SETTING2); + +		writel(blkeol_duration, d->regs + DSI_VID_PCK_TIME); +		writel(blkeol_duration - 6, d->regs + DSI_VID_VCA_SETTING1); +	} + +	/* Maximum line limit */ +	val = readl(d->regs + DSI_VID_VCA_SETTING2); +	val |= blkline_pck << +		DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT; +	writel(val, d->regs + DSI_VID_VCA_SETTING2); + +	/* Put IF1 into video mode */ +	val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); +	val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE; +	writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); + +	/* Disable command mode on IF1 */ +	val = readl(d->regs + DSI_CMD_MODE_CTL); +	val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN; +	writel(val, d->regs + DSI_CMD_MODE_CTL); + +	/* Enable some error interrupts */ +	val = readl(d->regs + DSI_VID_MODE_STS_CTL); +	val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC; +	val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA; +	writel(val, d->regs + DSI_VID_MODE_STS_CTL); + +	/* Enable video mode */ +	val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); +	val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN; +	writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); +} + +static void mcde_dsi_start(struct mcde_dsi *d) +{ +	unsigned long hs_freq; +	u32 val; +	int i; + +	/* No integration mode */ +	writel(0, d->regs + DSI_MCTL_INTEGRATION_MODE); + +	/* Enable the DSI port, from drivers/video/mcde/dsilink_v2.c */ +	val = DSI_MCTL_MAIN_DATA_CTL_LINK_EN | +		DSI_MCTL_MAIN_DATA_CTL_BTA_EN | +		DSI_MCTL_MAIN_DATA_CTL_READ_EN | +		DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN; +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET) +		val |= DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN; +	writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); + +	/* Set a high command timeout, clear other fields */ +	val = 0x3ff << DSI_CMD_MODE_CTL_TE_TIMEOUT_SHIFT; +	writel(val, d->regs + DSI_CMD_MODE_CTL); + +	/* +	 * UI_X4 is described as "unit interval times four" +	 * I guess since DSI packets are 4 bytes wide, one unit +	 * is one byte. +	 */ +	hs_freq = clk_get_rate(d->hs_clk); +	hs_freq /= 1000000; /* MHz */ +	val = 4000 / hs_freq; +	dev_dbg(d->dev, "UI value: %d\n", val); +	val <<= DSI_MCTL_DPHY_STATIC_UI_X4_SHIFT; +	val &= DSI_MCTL_DPHY_STATIC_UI_X4_MASK; +	writel(val, d->regs + DSI_MCTL_DPHY_STATIC); + +	/* +	 * Enable clocking: 0x0f (something?) between each burst, +	 * enable the second lane if needed, enable continuous clock if +	 * needed, enable switch into ULPM (ultra-low power mode) on +	 * all the lines. +	 */ +	val = 0x0f << DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_SHIFT; +	if (d->mdsi->lanes == 2) +		val |= DSI_MCTL_MAIN_PHY_CTL_LANE2_EN; +	if (!(d->mdsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) +		val |= DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS; +	val |= DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN | +		DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN | +		DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN; +	writel(val, d->regs + DSI_MCTL_MAIN_PHY_CTL); + +	val = (1 << DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_SHIFT) | +		(1 << DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_SHIFT); +	writel(val, d->regs + DSI_MCTL_ULPOUT_TIME); + +	writel(DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_90, +	       d->regs + DSI_DPHY_LANES_TRIM); + +	/* High PHY timeout */ +	val = (0x0f << DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_SHIFT) | +		(0x3fff << DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_SHIFT) | +		(0x3fff << DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_SHIFT); +	writel(val, d->regs + DSI_MCTL_DPHY_TIMEOUT); + +	val = DSI_MCTL_MAIN_EN_PLL_START | +		DSI_MCTL_MAIN_EN_CKLANE_EN | +		DSI_MCTL_MAIN_EN_DAT1_EN | +		DSI_MCTL_MAIN_EN_IF1_EN; +	if (d->mdsi->lanes == 2) +		val |= DSI_MCTL_MAIN_EN_DAT2_EN; +	writel(val, d->regs + DSI_MCTL_MAIN_EN); + +	/* Wait for the PLL to lock and the clock and data lines to come up */ +	i = 0; +	val = DSI_MCTL_MAIN_STS_PLL_LOCK | +		DSI_MCTL_MAIN_STS_CLKLANE_READY | +		DSI_MCTL_MAIN_STS_DAT1_READY; +	if (d->mdsi->lanes == 2) +		val |= DSI_MCTL_MAIN_STS_DAT2_READY; +	while ((readl(d->regs + DSI_MCTL_MAIN_STS) & val) != val) { +		/* Sleep for a millisecond */ +		usleep_range(1000, 1500); +		if (i++ == 100) { +			dev_warn(d->dev, "DSI lanes did not start up\n"); +			return; +		} +	} + +	/* TODO needed? */ + +	/* Command mode, clear IF1 ID */ +	val = readl(d->regs + DSI_CMD_MODE_CTL); +	/* +	 * If we enable low-power mode here, with +	 * val |= DSI_CMD_MODE_CTL_IF1_LP_EN +	 * then display updates become really slow. +	 */ +	val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK; +	writel(val, d->regs + DSI_CMD_MODE_CTL); + +	/* Wait for DSI PHY to initialize */ +	usleep_range(100, 200); +	dev_info(d->dev, "DSI link enabled\n"); +} + + +static void mcde_dsi_bridge_enable(struct drm_bridge *bridge) +{ +	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); + +	dev_info(d->dev, "enable DSI master\n"); +}; + +static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, +				     const struct drm_display_mode *mode, +				     const struct drm_display_mode *adj) +{ +	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); +	unsigned long pixel_clock_hz = mode->clock * 1000; +	unsigned long hs_freq, lp_freq; +	u32 val; +	int ret; + +	if (!d->mdsi) { +		dev_err(d->dev, "no DSI device attached to encoder!\n"); +		return; +	} + +	dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n", +		 mode->hdisplay, mode->vdisplay, pixel_clock_hz, +		 (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD" +		); + +	/* Copy maximum clock frequencies */ +	if (d->mdsi->lp_rate) +		lp_freq = d->mdsi->lp_rate; +	else +		lp_freq = DSI_DEFAULT_LP_FREQ_HZ; +	if (d->mdsi->hs_rate) +		hs_freq = d->mdsi->hs_rate; +	else +		hs_freq = DSI_DEFAULT_HS_FREQ_HZ; + +	/* Enable LP (Low Power, Energy Save, ES) and HS (High Speed) clocks */ +	d->lp_freq = clk_round_rate(d->lp_clk, lp_freq); +	ret = clk_set_rate(d->lp_clk, d->lp_freq); +	if (ret) +		dev_err(d->dev, "failed to set LP clock rate %lu Hz\n", +			d->lp_freq); + +	d->hs_freq = clk_round_rate(d->hs_clk, hs_freq); +	ret = clk_set_rate(d->hs_clk, d->hs_freq); +	if (ret) +		dev_err(d->dev, "failed to set HS clock rate %lu Hz\n", +			d->hs_freq); + +	/* Start clocks */ +	ret = clk_prepare_enable(d->lp_clk); +	if (ret) +		dev_err(d->dev, "failed to enable LP clock\n"); +	else +		dev_info(d->dev, "DSI LP clock rate %lu Hz\n", +			 d->lp_freq); +	ret = clk_prepare_enable(d->hs_clk); +	if (ret) +		dev_err(d->dev, "failed to enable HS clock\n"); +	else +		dev_info(d->dev, "DSI HS clock rate %lu Hz\n", +			 d->hs_freq); + +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { +		mcde_dsi_setup_video_mode(d, mode); +	} else { +		/* Command mode, clear IF1 ID */ +		val = readl(d->regs + DSI_CMD_MODE_CTL); +		/* +		 * If we enable low-power mode here with +		 * val |= DSI_CMD_MODE_CTL_IF1_LP_EN +		 * the display updates become really slow. +		 */ +		val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK; +		writel(val, d->regs + DSI_CMD_MODE_CTL); +	} +} + +static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d) +{ +	u32 val; +	int i; + +	/* +	 * Wait until we get out of command mode +	 * CSM = Command State Machine +	 */ +	i = 0; +	val = DSI_CMD_MODE_STS_CSM_RUNNING; +	while ((readl(d->regs + DSI_CMD_MODE_STS) & val) == val) { +		/* Sleep for a millisecond */ +		usleep_range(1000, 2000); +		if (i++ == 100) { +			dev_warn(d->dev, +				 "could not get out of command mode\n"); +			return; +		} +	} +} + +static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d) +{ +	u32 val; +	int i; + +	/* Wait until we get out og video mode */ +	i = 0; +	val = DSI_VID_MODE_STS_VSG_RUNNING; +	while ((readl(d->regs + DSI_VID_MODE_STS) & val) == val) { +		/* Sleep for a millisecond */ +		usleep_range(1000, 2000); +		if (i++ == 100) { +			dev_warn(d->dev, +				 "could not get out of video mode\n"); +			return; +		} +	} +} + +static void mcde_dsi_bridge_disable(struct drm_bridge *bridge) +{ +	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); +	u32 val; + +	/* Disable all error interrupts */ +	writel(0, d->regs + DSI_VID_MODE_STS_CTL); + +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { +		/* Stop video mode */ +		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); +		val &= ~DSI_MCTL_MAIN_DATA_CTL_VID_EN; +		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); +		mcde_dsi_wait_for_video_mode_stop(d); +	} else { +		/* Stop command mode */ +		mcde_dsi_wait_for_command_mode_stop(d); +	} + +	/* Stop clocks */ +	clk_disable_unprepare(d->hs_clk); +	clk_disable_unprepare(d->lp_clk); +} + +/* + * This connector needs no special handling, just use the default + * helpers for everything. It's pretty dummy. + */ +static const struct drm_connector_funcs mcde_dsi_connector_funcs = { +	.reset = drm_atomic_helper_connector_reset, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = drm_connector_cleanup, +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int mcde_dsi_get_modes(struct drm_connector *connector) +{ +	struct mcde_dsi *d = connector_to_mcde_dsi(connector); + +	/* Just pass the question to the panel */ +	if (d->panel) +		return drm_panel_get_modes(d->panel); + +	/* TODO: deal with bridges */ + +	return 0; +} + +static const struct drm_connector_helper_funcs +mcde_dsi_connector_helper_funcs = { +	.get_modes = mcde_dsi_get_modes, +}; + +static int mcde_dsi_bridge_attach(struct drm_bridge *bridge) +{ +	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); +	struct drm_device *drm = bridge->dev; +	int ret; + +	drm_connector_helper_add(&d->connector, +				 &mcde_dsi_connector_helper_funcs); + +	if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) { +		dev_err(d->dev, "we need atomic updates\n"); +		return -ENOTSUPP; +	} + +	ret = drm_connector_init(drm, &d->connector, +				 &mcde_dsi_connector_funcs, +				 DRM_MODE_CONNECTOR_DSI); +	if (ret) { +		dev_err(d->dev, "failed to initialize DSI bridge connector\n"); +		return ret; +	} +	d->connector.polled = DRM_CONNECTOR_POLL_CONNECT; +	/* The encoder in the bridge attached to the DSI bridge */ +	drm_connector_attach_encoder(&d->connector, bridge->encoder); +	/* Then we attach the DSI bridge to the output (panel etc) bridge */ +	ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge); +	if (ret) { +		dev_err(d->dev, "failed to attach the DSI bridge\n"); +		return ret; +	} +	d->connector.status = connector_status_connected; + +	return 0; +} + +static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = { +	.attach = mcde_dsi_bridge_attach, +	.mode_set = mcde_dsi_bridge_mode_set, +	.disable = mcde_dsi_bridge_disable, +	.enable = mcde_dsi_bridge_enable, +}; + +static int mcde_dsi_bind(struct device *dev, struct device *master, +			 void *data) +{ +	struct drm_device *drm = data; +	struct mcde *mcde = drm->dev_private; +	struct mcde_dsi *d = dev_get_drvdata(dev); +	struct device_node *child; +	struct drm_panel *panel = NULL; +	struct drm_bridge *bridge = NULL; + +	if (!of_get_available_child_count(dev->of_node)) { +		dev_info(dev, "unused DSI interface\n"); +		d->unused = true; +		return 0; +	} +	d->mcde = mcde; +	/* If the display attached before binding, set this up */ +	if (d->mdsi) +		d->mcde->mdsi = d->mdsi; + +	/* Obtain the clocks */ +	d->hs_clk = devm_clk_get(dev, "hs"); +	if (IS_ERR(d->hs_clk)) { +		dev_err(dev, "unable to get HS clock\n"); +		return PTR_ERR(d->hs_clk); +	} + +	d->lp_clk = devm_clk_get(dev, "lp"); +	if (IS_ERR(d->lp_clk)) { +		dev_err(dev, "unable to get LP clock\n"); +		return PTR_ERR(d->lp_clk); +	} + +	/* Assert RESET through the PRCMU, active low */ +	/* FIXME: which DSI block? */ +	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, +			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0); + +	usleep_range(100, 200); + +	/* De-assert RESET again */ +	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, +			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN, +			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN); + +	/* Start up the hardware */ +	mcde_dsi_start(d); + +	/* Look for a panel as a child to this node */ +	for_each_available_child_of_node(dev->of_node, child) { +		panel = of_drm_find_panel(child); +		if (IS_ERR(panel)) { +			dev_err(dev, "failed to find panel try bridge (%lu)\n", +				PTR_ERR(panel)); +			bridge = of_drm_find_bridge(child); +			if (IS_ERR(bridge)) { +				dev_err(dev, "failed to find bridge (%lu)\n", +					PTR_ERR(bridge)); +				return PTR_ERR(bridge); +			} +		} +	} +	if (panel) { +		bridge = drm_panel_bridge_add(panel, +					      DRM_MODE_CONNECTOR_DSI); +		if (IS_ERR(bridge)) { +			dev_err(dev, "error adding panel bridge\n"); +			return PTR_ERR(bridge); +		} +		dev_info(dev, "connected to panel\n"); +		d->panel = panel; +	} else if (bridge) { +		/* TODO: AV8100 HDMI encoder goes here for example */ +		dev_info(dev, "connected to non-panel bridge (unsupported)\n"); +		return -ENODEV; +	} else { +		dev_err(dev, "no panel or bridge\n"); +		return -ENODEV; +	} + +	d->bridge_out = bridge; + +	/* Create a bridge for this DSI channel */ +	d->bridge.funcs = &mcde_dsi_bridge_funcs; +	d->bridge.of_node = dev->of_node; +	drm_bridge_add(&d->bridge); + +	/* TODO: first come first serve, use a list */ +	mcde->bridge = &d->bridge; + +	dev_info(dev, "initialized MCDE DSI bridge\n"); + +	return 0; +} + +static void mcde_dsi_unbind(struct device *dev, struct device *master, +			    void *data) +{ +	struct mcde_dsi *d = dev_get_drvdata(dev); + +	if (d->panel) +		drm_panel_bridge_remove(d->bridge_out); +	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, +			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0); +} + +static const struct component_ops mcde_dsi_component_ops = { +	.bind   = mcde_dsi_bind, +	.unbind = mcde_dsi_unbind, +}; + +static int mcde_dsi_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct mcde_dsi *d; +	struct mipi_dsi_host *host; +	struct resource *res; +	u32 dsi_id; +	int ret; + +	d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); +	if (!d) +		return -ENOMEM; +	d->dev = dev; +	platform_set_drvdata(pdev, d); + +	/* Get a handle on the PRCMU so we can do reset */ +	d->prcmu = +		syscon_regmap_lookup_by_compatible("stericsson,db8500-prcmu"); +	if (IS_ERR(d->prcmu)) { +		dev_err(dev, "no PRCMU regmap\n"); +		return PTR_ERR(d->prcmu); +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	d->regs = devm_ioremap_resource(dev, res); +	if (IS_ERR(d->regs)) { +		dev_err(dev, "no DSI regs\n"); +		return PTR_ERR(d->regs); +	} + +	dsi_id = readl(d->regs + DSI_ID_REG); +	dev_info(dev, "HW revision 0x%08x\n", dsi_id); + +	host = &d->dsi_host; +	host->dev = dev; +	host->ops = &mcde_dsi_host_ops; +	ret = mipi_dsi_host_register(host); +	if (ret < 0) { +		dev_err(dev, "failed to register DSI host: %d\n", ret); +		return ret; +	} +	dev_info(dev, "registered DSI host\n"); + +	platform_set_drvdata(pdev, d); +	return component_add(dev, &mcde_dsi_component_ops); +} + +static int mcde_dsi_remove(struct platform_device *pdev) +{ +	struct mcde_dsi *d = platform_get_drvdata(pdev); + +	component_del(&pdev->dev, &mcde_dsi_component_ops); +	mipi_dsi_host_unregister(&d->dsi_host); + +	return 0; +} + +static const struct of_device_id mcde_dsi_of_match[] = { +	{ +		.compatible = "ste,mcde-dsi", +	}, +	{}, +}; + +struct platform_driver mcde_dsi_driver = { +	.driver = { +		.name           = "mcde-dsi", +		.of_match_table = of_match_ptr(mcde_dsi_of_match), +	}, +	.probe = mcde_dsi_probe, +	.remove = mcde_dsi_remove, +}; diff --git a/drivers/gpu/drm/mcde/mcde_dsi_regs.h b/drivers/gpu/drm/mcde/mcde_dsi_regs.h new file mode 100644 index 000000000000..c9253321a3be --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_dsi_regs.h @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DRM_MCDE_DSI_REGS +#define __DRM_MCDE_DSI_REGS + +#define DSI_MCTL_INTEGRATION_MODE 0x00000000 + +#define DSI_MCTL_MAIN_DATA_CTL 0x00000004 +#define DSI_MCTL_MAIN_DATA_CTL_LINK_EN BIT(0) +#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE BIT(1) +#define DSI_MCTL_MAIN_DATA_CTL_VID_EN BIT(2) +#define DSI_MCTL_MAIN_DATA_CTL_TVG_SEL BIT(3) +#define DSI_MCTL_MAIN_DATA_CTL_TBG_SEL BIT(4) +#define DSI_MCTL_MAIN_DATA_CTL_IF1_TE_EN BIT(5) +#define DSI_MCTL_MAIN_DATA_CTL_IF2_TE_EN BIT(6) +#define DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN BIT(7) +#define DSI_MCTL_MAIN_DATA_CTL_READ_EN BIT(8) +#define DSI_MCTL_MAIN_DATA_CTL_BTA_EN BIT(9) +#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_ECC BIT(10) +#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_CHECKSUM BIT(11) +#define DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN BIT(12) +#define DSI_MCTL_MAIN_DATA_CTL_DISP_EOT_GEN BIT(13) +#define DSI_MCTL_MAIN_DATA_CTL_DLX_REMAP_EN BIT(14) +#define DSI_MCTL_MAIN_DATA_CTL_TE_POLLING_EN BIT(15) + +#define DSI_MCTL_MAIN_PHY_CTL 0x00000008 +#define DSI_MCTL_MAIN_PHY_CTL_LANE2_EN BIT(0) +#define DSI_MCTL_MAIN_PHY_CTL_FORCE_STOP_MODE BIT(1) +#define DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS BIT(2) +#define DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN BIT(3) +#define DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN BIT(4) +#define DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN BIT(5) +#define DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_SHIFT 6 +#define DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_MASK 0x000003C0 +#define DSI_MCTL_MAIN_PHY_CTL_CLOCK_FORCE_STOP_MODE BIT(10) + +#define DSI_MCTL_PLL_CTL 0x0000000C +#define DSI_MCTL_LANE_STS 0x00000010 + +#define DSI_MCTL_DPHY_TIMEOUT 0x00000014 +#define DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_SHIFT 0 +#define DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_MASK 0x0000000F +#define DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_SHIFT 4 +#define DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_MASK 0x0003FFF0 +#define DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_SHIFT 18 +#define DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_MASK 0xFFFC0000 + +#define DSI_MCTL_ULPOUT_TIME 0x00000018 +#define DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_SHIFT 0 +#define DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_MASK 0x000001FF +#define DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_SHIFT 9 +#define DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_MASK 0x0003FE00 + +#define DSI_MCTL_DPHY_STATIC 0x0000001C +#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_CLK BIT(0) +#define DSI_MCTL_DPHY_STATIC_HS_INVERT_CLK BIT(1) +#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT1 BIT(2) +#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT1 BIT(3) +#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT2 BIT(4) +#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT2 BIT(5) +#define DSI_MCTL_DPHY_STATIC_UI_X4_SHIFT 6 +#define DSI_MCTL_DPHY_STATIC_UI_X4_MASK 0x00000FC0 + +#define DSI_MCTL_MAIN_EN 0x00000020 +#define DSI_MCTL_MAIN_EN_PLL_START BIT(0) +#define DSI_MCTL_MAIN_EN_CKLANE_EN BIT(3) +#define DSI_MCTL_MAIN_EN_DAT1_EN BIT(4) +#define DSI_MCTL_MAIN_EN_DAT2_EN BIT(5) +#define DSI_MCTL_MAIN_EN_CLKLANE_ULPM_REQ BIT(6) +#define DSI_MCTL_MAIN_EN_DAT1_ULPM_REQ BIT(7) +#define DSI_MCTL_MAIN_EN_DAT2_ULPM_REQ BIT(8) +#define DSI_MCTL_MAIN_EN_IF1_EN BIT(9) +#define DSI_MCTL_MAIN_EN_IF2_EN BIT(10) + +#define DSI_MCTL_MAIN_STS 0x00000024 +#define DSI_MCTL_MAIN_STS_PLL_LOCK BIT(0) +#define DSI_MCTL_MAIN_STS_CLKLANE_READY BIT(1) +#define DSI_MCTL_MAIN_STS_DAT1_READY BIT(2) +#define DSI_MCTL_MAIN_STS_DAT2_READY BIT(3) +#define DSI_MCTL_MAIN_STS_HSTX_TO_ERR BIT(4) +#define DSI_MCTL_MAIN_STS_LPRX_TO_ERR BIT(5) +#define DSI_MCTL_MAIN_STS_CRS_UNTERM_PCK BIT(6) +#define DSI_MCTL_MAIN_STS_VRS_UNTERM_PCK BIT(7) + +#define DSI_MCTL_DPHY_ERR 0x00000028 +#define DSI_INT_VID_RDDATA 0x00000030 +#define DSI_INT_VID_GNT 0x00000034 +#define DSI_INT_CMD_RDDATA 0x00000038 +#define DSI_INT_CMD_GNT 0x0000003C +#define DSI_INT_INTERRUPT_CTL 0x00000040 + +#define DSI_CMD_MODE_CTL 0x00000050 +#define DSI_CMD_MODE_CTL_IF1_ID_SHIFT 0 +#define DSI_CMD_MODE_CTL_IF1_ID_MASK 0x00000003 +#define DSI_CMD_MODE_CTL_IF2_ID_SHIFT 2 +#define DSI_CMD_MODE_CTL_IF2_ID_MASK 0x0000000C +#define DSI_CMD_MODE_CTL_IF1_LP_EN BIT(4) +#define DSI_CMD_MODE_CTL_IF2_LP_EN BIT(5) +#define DSI_CMD_MODE_CTL_ARB_MODE BIT(6) +#define DSI_CMD_MODE_CTL_ARB_PRI BIT(7) +#define DSI_CMD_MODE_CTL_FIL_VALUE_SHIFT 8 +#define DSI_CMD_MODE_CTL_FIL_VALUE_MASK 0x0000FF00 +#define DSI_CMD_MODE_CTL_TE_TIMEOUT_SHIFT 16 +#define DSI_CMD_MODE_CTL_TE_TIMEOUT_MASK 0x03FF0000 + +#define DSI_CMD_MODE_STS 0x00000054 +#define DSI_CMD_MODE_STS_ERR_NO_TE BIT(0) +#define DSI_CMD_MODE_STS_ERR_TE_MISS BIT(1) +#define DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN BIT(2) +#define DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN BIT(3) +#define DSI_CMD_MODE_STS_ERR_UNWANTED_RD BIT(4) +#define DSI_CMD_MODE_STS_CSM_RUNNING BIT(5) + +#define DSI_DIRECT_CMD_SEND 0x00000060 + +#define DSI_DIRECT_CMD_MAIN_SETTINGS 0x00000064 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_SHIFT 0 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_MASK 0x00000007 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE 0 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ 1 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TE_REQ 4 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TRIG_REQ 5 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_BTA_REQ 6 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT BIT(3) +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT 8 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_MASK 0x00003F00 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_TURN_ON_PERIPHERAL 50 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHUT_DOWN_PERIPHERAL 34 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_0 3 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_1 19 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_2 35 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_LONG_WRITE 41 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_0 5 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 21 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_LONG_WRITE 57 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_READ 6 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SET_MAX_PKT_SIZE 55 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT 14 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT 16 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN BIT(21) +#define DSI_DIRECT_CMD_MAIN_SETTINGS_TRIGGER_VAL_SHIFT 24 +#define DSI_DIRECT_CMD_MAIN_SETTINGS_TRIGGER_VAL_MASK 0x0F000000 + +#define DSI_DIRECT_CMD_STS 0x00000068 +#define DSI_DIRECT_CMD_STS_CMD_TRANSMISSION BIT(0) +#define DSI_DIRECT_CMD_STS_WRITE_COMPLETED BIT(1) +#define DSI_DIRECT_CMD_STS_TRIGGER_COMPLETED BIT(2) +#define DSI_DIRECT_CMD_STS_READ_COMPLETED BIT(3) +#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_RECEIVED_SHIFT BIT(4) +#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED BIT(5) +#define DSI_DIRECT_CMD_STS_TRIGGER_RECEIVED BIT(6) +#define DSI_DIRECT_CMD_STS_TE_RECEIVED BIT(7) +#define DSI_DIRECT_CMD_STS_BTA_COMPLETED BIT(8) +#define DSI_DIRECT_CMD_STS_BTA_FINISHED BIT(9) +#define DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR BIT(10) +#define DSI_DIRECT_CMD_STS_TRIGGER_VAL_MASK 0x00007800 +#define DSI_DIRECT_CMD_STS_TRIGGER_VAL_SHIFT 11 +#define DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT 16 +#define DSI_DIRECT_CMD_STS_ACK_VAL_MASK 0xFFFF0000 + +#define DSI_DIRECT_CMD_RD_INIT 0x0000006C +#define DSI_DIRECT_CMD_RD_INIT_RESET_SHIFT 0 +#define DSI_DIRECT_CMD_RD_INIT_RESET_MASK 0xFFFFFFFF + +#define DSI_DIRECT_CMD_WRDAT0 0x00000070 +#define DSI_DIRECT_CMD_WRDAT1 0x00000074 +#define DSI_DIRECT_CMD_WRDAT2 0x00000078 +#define DSI_DIRECT_CMD_WRDAT3 0x0000007C + +#define DSI_DIRECT_CMD_RDDAT 0x00000080 + +#define DSI_DIRECT_CMD_RD_PROPERTY 0x00000084 +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_SHIFT 0 +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK 0x0000FFFF +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_ID_SHIFT 16 +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_ID_MASK 0x00030000 +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_DCSNOTGENERIC_SHIFT 18 +#define DSI_DIRECT_CMD_RD_PROPERTY_RD_DCSNOTGENERIC_MASK 0x00040000 + +#define DSI_DIRECT_CMD_RD_STS 0x00000088 + +#define DSI_VID_MAIN_CTL 0x00000090 +#define DSI_VID_MAIN_CTL_START_MODE_SHIFT 0 +#define DSI_VID_MAIN_CTL_START_MODE_MASK 0x00000003 +#define DSI_VID_MAIN_CTL_STOP_MODE_SHIFT 2 +#define DSI_VID_MAIN_CTL_STOP_MODE_MASK 0x0000000C +#define DSI_VID_MAIN_CTL_VID_ID_SHIFT 4 +#define DSI_VID_MAIN_CTL_VID_ID_MASK 0x00000030 +#define DSI_VID_MAIN_CTL_HEADER_SHIFT 6 +#define DSI_VID_MAIN_CTL_HEADER_MASK 0x00000FC0 +#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_16BITS 0 +#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS BIT(12) +#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS_LOOSE BIT(13) +#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_24BITS (BIT(12) | BIT(13)) +#define DSI_VID_MAIN_CTL_BURST_MODE BIT(14) +#define DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE BIT(15) +#define DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL BIT(16) +#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_NULL 0 +#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_BLANKING BIT(17) +#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0 BIT(18) +#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_1 (BIT(17) | BIT(18)) +#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_NULL 0 +#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_BLANKING BIT(19) +#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0 BIT(20) +#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_1 (BIT(19) | BIT(20)) +#define DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT 21 +#define DSI_VID_MAIN_CTL_RECOVERY_MODE_MASK 0x00600000 + +#define DSI_VID_VSIZE 0x00000094 +#define DSI_VID_VSIZE_VSA_LENGTH_SHIFT 0 +#define DSI_VID_VSIZE_VSA_LENGTH_MASK 0x0000003F +#define DSI_VID_VSIZE_VBP_LENGTH_SHIFT 6 +#define DSI_VID_VSIZE_VBP_LENGTH_MASK 0x00000FC0 +#define DSI_VID_VSIZE_VFP_LENGTH_SHIFT 12 +#define DSI_VID_VSIZE_VFP_LENGTH_MASK 0x000FF000 +#define DSI_VID_VSIZE_VACT_LENGTH_SHIFT 20 +#define DSI_VID_VSIZE_VACT_LENGTH_MASK 0x7FF00000 + +#define DSI_VID_HSIZE1 0x00000098 +#define DSI_VID_HSIZE1_HSA_LENGTH_SHIFT 0 +#define DSI_VID_HSIZE1_HSA_LENGTH_MASK 0x000003FF +#define DSI_VID_HSIZE1_HBP_LENGTH_SHIFT 10 +#define DSI_VID_HSIZE1_HBP_LENGTH_MASK 0x000FFC00 +#define DSI_VID_HSIZE1_HFP_LENGTH_SHIFT 20 +#define DSI_VID_HSIZE1_HFP_LENGTH_MASK 0x7FF00000 + +#define DSI_VID_HSIZE2 0x0000009C +#define DSI_VID_HSIZE2_RGB_SIZE_SHIFT 0 +#define DSI_VID_HSIZE2_RGB_SIZE_MASK 0x00001FFF + +#define DSI_VID_BLKSIZE1 0x000000A0 +#define DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT 0 +#define DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_MASK 0x00001FFF +#define DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT 13 +#define DSI_VID_BLKSIZE1_BLKEOL_PCK_MASK 0x03FFE000 + +#define DSI_VID_BLKSIZE2 0x000000A4 +#define DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT 0 +#define DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_MASK 0x00001FFF + +#define DSI_VID_PCK_TIME 0x000000A8 +#define DSI_VID_PCK_TIME_BLKEOL_DURATION_SHIFT 0 + +#define DSI_VID_DPHY_TIME 0x000000AC +#define DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT 0 +#define DSI_VID_DPHY_TIME_REG_LINE_DURATION_MASK 0x00001FFF +#define DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT 13 +#define DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_MASK 0x00FFE000 + +#define DSI_VID_MODE_STS 0x000000BC +#define DSI_VID_MODE_STS_VSG_RUNNING BIT(0) + +#define DSI_VID_VCA_SETTING1 0x000000C0 +#define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT 0 +#define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_MASK 0x0000FFFF +#define DSI_VID_VCA_SETTING1_BURST_LP BIT(16) + +#define DSI_VID_VCA_SETTING2 0x000000C4 +#define DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT 0 +#define DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_MASK 0x0000FFFF +#define DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_SHIFT 16 +#define DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_MASK 0xFFFF0000 + +#define DSI_CMD_MODE_STS_CTL 0x000000F4 +#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN BIT(0) +#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN BIT(1) +#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EN BIT(2) +#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EN BIT(3) +#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EN BIT(4) +#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EN BIT(5) +#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EDGE BIT(16) +#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EDGE BIT(17) +#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EDGE BIT(18) +#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EDGE BIT(19) +#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EDGE BIT(20) +#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EDGE BIT(21) + +#define DSI_DIRECT_CMD_STS_CTL 0x000000F8 +#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EN BIT(0) +#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EN BIT(1) +#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EN BIT(2) +#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EN BIT(3) +#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EN BIT(4) +#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN BIT(5) +#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EN BIT(6) +#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN BIT(7) +#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EN BIT(8) +#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EN BIT(9) +#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EN BIT(10) +#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EDGE BIT(16) +#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EDGE BIT(17) +#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EDGE BIT(18) +#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EDGE BIT(19) +#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EDGE BIT(20) +#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EDGE BIT(21) +#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EDGE BIT(22) +#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EDGE BIT(23) +#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EDGE BIT(24) +#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EDGE BIT(25) +#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EDGE BIT(26) + +#define DSI_VID_MODE_STS_CTL 0x00000100 +#define DSI_VID_MODE_STS_CTL_VSG_RUNNING BIT(0) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA BIT(1) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC BIT(2) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC BIT(3) +#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH BIT(4) +#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT BIT(5) +#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE BIT(6) +#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE BIT(7) +#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD BIT(8) +#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH BIT(9) +#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EDGE BIT(16) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EDGE BIT(17) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EDGE BIT(18) +#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EDGE BIT(19) +#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EDGE BIT(20) +#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EDGE BIT(21) +#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EDGE BIT(22) +#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EDGE BIT(23) +#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EDGE BIT(24) +#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EDGE BIT(25) +#define DSI_VID_MODE_STS_CTL_VSG_RECOVERY_EDGE BIT(26) + +#define DSI_TG_STS_CTL 0x00000104 +#define DSI_MCTL_DHPY_ERR_CTL 0x00000108 +#define DSI_MCTL_MAIN_STS_CLR 0x00000110 + +#define DSI_CMD_MODE_STS_CLR 0x00000114 +#define DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR BIT(0) +#define DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR BIT(1) +#define DSI_CMD_MODE_STS_CLR_ERR_SDI1_UNDERRUN_CLR BIT(2) +#define DSI_CMD_MODE_STS_CLR_ERR_SDI2_UNDERRUN_CLR BIT(3) +#define DSI_CMD_MODE_STS_CLR_ERR_UNWANTED_RD_CLR BIT(4) +#define DSI_CMD_MODE_STS_CLR_CSM_RUNNING_CLR BIT(5) + +#define DSI_DIRECT_CMD_STS_CLR 0x00000118 +#define DSI_DIRECT_CMD_STS_CLR_CMD_TRANSMISSION_CLR BIT(0) +#define DSI_DIRECT_CMD_STS_CLR_WRITE_COMPLETED_CLR BIT(1) +#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_COMPLETED_CLR BIT(2) +#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_CLR BIT(3) +#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_RECEIVED_CLR BIT(4) +#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR BIT(5) +#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_RECEIVED_CLR BIT(6) +#define DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR BIT(7) +#define DSI_DIRECT_CMD_STS_CLR_BTA_COMPLETED_CLR BIT(8) +#define DSI_DIRECT_CMD_STS_CLR_BTA_FINISHED_CLR BIT(9) +#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_WITH_ERR_CLR BIT(10) + +#define DSI_DIRECT_CMD_RD_STS_CLR 0x0000011C +#define DSI_VID_MODE_STS_CLR 0x00000120 +#define DSI_TG_STS_CLR 0x00000124 +#define DSI_MCTL_DPHY_ERR_CLR 0x00000128 +#define DSI_MCTL_MAIN_STS_FLAG 0x00000130 +#define DSI_CMD_MODE_STS_FLAG 0x00000134 +#define DSI_DIRECT_CMD_STS_FLAG 0x00000138 +#define DSI_DIRECT_CMD_RD_STS_FLAG 0x0000013C +#define DSI_VID_MODE_STS_FLAG 0x00000140 +#define DSI_TG_STS_FLAG 0x00000144 + +#define DSI_DPHY_LANES_TRIM 0x00000150 +#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT1_SHIFT 0 +#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT1_MASK 0x00000003 +#define DSI_DPHY_LANES_TRIM_DPHY_CD_OFF_DAT1 BIT(2) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT1 BIT(3) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT1 BIT(4) +#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT1 BIT(5) +#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_CLK_SHIFT 6 +#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_CLK_MASK 0x000000C0 +#define DSI_DPHY_LANES_TRIM_DPHY_LP_RX_VIL_CLK_SHIFT 8 +#define DSI_DPHY_LANES_TRIM_DPHY_LP_RX_VIL_CLK_MASK 0x00000300 +#define DSI_DPHY_LANES_TRIM_DPHY_LP_TX_SLEWRATE_CLK_SHIFT 10 +#define DSI_DPHY_LANES_TRIM_DPHY_LP_TX_SLEWRATE_CLK_MASK 0x00000C00 +#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_81 0 +#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_90 BIT(12) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_CLK BIT(13) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_CLK BIT(14) +#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_CLK BIT(15) +#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT2 BIT(16) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT2 BIT(18) +#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT2 BIT(19) +#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT2 BIT(20) + +#define DSI_ID_REG	0x00000FF0 + +#endif /* __DRM_MCDE_DSI_REGS */ diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index e450387d0eab..9f9281dd49f8 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -15,3 +15,4 @@ config DRM_MESON_DW_HDMI  	depends on DRM_MESON  	default y if DRM_MESON  	select DRM_DW_HDMI +	imply DRM_DW_HDMI_I2S_AUDIO diff --git a/drivers/gpu/drm/panel/panel-arm-versatile.c b/drivers/gpu/drm/panel/panel-arm-versatile.c index a79908dfa3c8..5f72c922a04b 100644 --- a/drivers/gpu/drm/panel/panel-arm-versatile.c +++ b/drivers/gpu/drm/panel/panel-arm-versatile.c @@ -25,13 +25,12 @@   * Epson QCIF display.   *   */ -#include <drm/drmP.h> -#include <drm/drm_panel.h>  #include <linux/bitops.h>  #include <linux/init.h>  #include <linux/kernel.h>  #include <linux/mfd/syscon.h> +#include <linux/mod_devicetable.h>  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/regmap.h> @@ -39,6 +38,9 @@  #include <video/of_videomode.h>  #include <video/videomode.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +  /*   * This configuration register in the Versatile and RealView   * family is uniformly present but appears more and more diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c index a1c4cd2940fb..35a4bd05edf5 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c @@ -22,13 +22,10 @@   * published by the Free Software Foundation.   */ -#include <drm/drmP.h> -#include <drm/drm_panel.h> - -#include <linux/of_device.h>  #include <linux/bitops.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h> +#include <linux/of_device.h>  #include <linux/regmap.h>  #include <linux/regulator/consumer.h>  #include <linux/spi/spi.h> @@ -37,6 +34,10 @@  #include <video/of_videomode.h>  #include <video/videomode.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  #define ILI9322_CHIP_ID			0x00  #define ILI9322_CHIP_ID_MAGIC		0x96 diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 8e5724b63f1f..ab5f2dc1f3ce 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -8,18 +8,21 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/of_device.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h>  #include <drm/drm_panel.h> - -#include <video/mipi_display.h> +#include <drm/drm_print.h>  struct panel_init_cmd {  	size_t len; @@ -55,7 +58,6 @@ struct innolux_panel {  	struct backlight_device *backlight;  	struct regulator_bulk_data *supplies; -	unsigned int num_supplies;  	struct gpio_desc *enable_gpio;  	bool prepared; diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c index 99caa7835e7b..ee5ddf771e10 100644 --- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -21,19 +21,21 @@   * You should have received a copy of the GNU General Public License along with   * this program.  If not, see <http://www.gnu.org/licenses/>.   */ +  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h>  #include <drm/drm_panel.h> -#include <video/mipi_display.h> -  static const char * const regulator_names[] = {  	"vddp",  	"iovcc" diff --git a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c index 2a25a914d09e..3ac04eb8d0fe 100644 --- a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c +++ b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c @@ -4,17 +4,20 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h>  #include <drm/drm_panel.h> - -#include <video/mipi_display.h> +#include <drm/drm_print.h>  struct kingdisplay_panel {  	struct drm_panel base; diff --git a/drivers/gpu/drm/panel/panel-lg-lg4573.c b/drivers/gpu/drm/panel/panel-lg-lg4573.c index 6989238b276a..0dd4bdda7c4e 100644 --- a/drivers/gpu/drm/panel/panel-lg-lg4573.c +++ b/drivers/gpu/drm/panel/panel-lg-lg4573.c @@ -15,10 +15,9 @@   * published by the Free Software Foundation.  */ -#include <drm/drmP.h> -#include <drm/drm_panel.h> - +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/regulator/consumer.h>  #include <linux/spi/spi.h> @@ -26,6 +25,10 @@  #include <video/of_videomode.h>  #include <video/videomode.h> +#include <drm/drm_device.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +  struct lg4573 {  	struct drm_panel panel;  	struct spi_device *spi; diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 3f6550e6b6a4..1ec57d0806a8 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -16,14 +16,13 @@  #include <linux/regulator/consumer.h>  #include <linux/slab.h> -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_panel.h> -  #include <video/display_timing.h>  #include <video/of_display_timing.h>  #include <video/videomode.h> +#include <drm/drm_crtc.h> +#include <drm/drm_panel.h> +  struct panel_lvds {  	struct drm_panel panel;  	struct device *dev; diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c index a1d8d92fac2b..2bae1db3ff34 100644 --- a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c +++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c @@ -15,13 +15,13 @@  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drm_modes.h> -#include <drm/drm_panel.h> -#include <drm/drmP.h> -  #include <video/videomode.h>  #include <video/display_timing.h> +#include <drm/drm_device.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +  #define LCD_OLINUXINO_HEADER_MAGIC	0x4F4CB727  #define LCD_OLINUXINO_DATA_LEN		256 diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c index f27a7e426574..c7b48df8869a 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c @@ -6,14 +6,19 @@   *          Yannick Fertre <yannick.fertre@st.com>   */ -#include <drm/drmP.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_panel.h>  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/regulator/consumer.h> +  #include <video/mipi_display.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  #define OTM8009A_BACKLIGHT_DEFAULT	240  #define OTM8009A_BACKLIGHT_MAX		255 diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c index cb4dfb98be0f..045df41dbde2 100644 --- a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c +++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c @@ -19,17 +19,18 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_panel.h> -#include <video/mipi_display.h> -  /*   * When power is turned off to this panel a minimum off time of 500ms has to be   * observed before powering back on as there's no external reset pin. Keep diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c index 1b708c85fd27..28c0620dfe0f 100644 --- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c +++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c @@ -53,10 +53,10 @@  #include <linux/of_graph.h>  #include <linux/pm.h> -#include <drm/drm_panel.h> -#include <drm/drmP.h>  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h>  #define RPI_DSI_DRIVER_NAME "rpi-ts-dsi" diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c index 14186827e591..ba889625ad43 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c @@ -7,14 +7,17 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/regulator/consumer.h>  #include <video/mipi_display.h> -#include <drm/drmP.h>  #include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h>  #include <drm/drm_panel.h> +#include <drm/drm_print.h>  /*** Manufacturer Command Set ***/  #define MCS_CMD_MODE_SW		0xFE /* CMD Mode Switch */ diff --git a/drivers/gpu/drm/panel/panel-samsung-ld9040.c b/drivers/gpu/drm/panel/panel-samsung-ld9040.c index 3cf4cf6a6942..5c2a1cae603b 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ld9040.c +++ b/drivers/gpu/drm/panel/panel-samsung-ld9040.c @@ -11,10 +11,10 @@   * published by the Free Software Foundation.  */ -#include <drm/drmP.h> -#include <drm/drm_panel.h> - +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h>  #include <linux/regulator/consumer.h>  #include <linux/spi/spi.h> @@ -22,6 +22,10 @@  #include <video/of_videomode.h>  #include <video/videomode.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  /* Manufacturer Command Set */  #define MCS_MANPWR		0xb0  #define MCS_ELVSS_ON		0xb1 diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c index 797bbc7a264e..351eee951648 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c @@ -11,14 +11,18 @@   * published by the Free Software Foundation.   */ -#include <drm/drmP.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_panel.h>  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/of_device.h>  #include <linux/regulator/consumer.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  #define S6E3HA2_MIN_BRIGHTNESS		0  #define S6E3HA2_MAX_BRIGHTNESS		100  #define S6E3HA2_DEFAULT_BRIGHTNESS	80 diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index aeb32aa58899..19ea325a0e9b 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -11,14 +11,19 @@   * published by the Free Software Foundation.   */ -#include <drm/drmP.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_panel.h>  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/regulator/consumer.h> +  #include <video/mipi_display.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  #define MCS_LEVEL2_KEY		0xf0  #define MCS_MTP_KEY		0xf1  #define MCS_MTP_SET3		0xd4 diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c index 6ad827b93ae1..0dcbda8310e3 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c @@ -15,17 +15,21 @@   * published by the Free Software Foundation.  */ -#include <drm/drmP.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_panel.h> - +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h>  #include <linux/regulator/consumer.h>  #include <video/mipi_display.h>  #include <video/of_videomode.h>  #include <video/videomode.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  #define LDI_MTP_LENGTH			24  #define GAMMA_LEVEL_NUM			25  #define GAMMA_TABLE_LEN			26 diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c index bdcc5d80823d..18b22b1294fb 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -7,17 +7,19 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/platform_device.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_panel.h> -  #include <video/display_timing.h>  #include <video/videomode.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_panel.h> +  struct seiko_panel_desc {  	const struct drm_display_mode *modes;  	unsigned int num_modes; diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c index 02fc0f5423d4..1ac75a30e431 100644 --- a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c @@ -7,18 +7,19 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_panel.h> -#include <video/mipi_display.h> -  struct sharp_panel {  	struct drm_panel base;  	/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */ diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c index e5cae0050f52..89d7fc842972 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c @@ -19,18 +19,19 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/mipi_display.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_panel.h> -#include <video/mipi_display.h> -  struct sharp_nt_panel {  	struct drm_panel base;  	struct mipi_dsi_device *dsi; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index c22c4719cd2c..5a93c4edf1e4 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -22,20 +22,21 @@   */  #include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of_platform.h>  #include <linux/platform_device.h>  #include <linux/regulator/consumer.h> -#include <drm/drmP.h> +#include <video/display_timing.h> +#include <video/videomode.h> +  #include <drm/drm_crtc.h> +#include <drm/drm_device.h>  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_panel.h> -#include <video/display_timing.h> -#include <video/videomode.h> -  struct panel_desc {  	const struct drm_display_mode *modes;  	unsigned int num_modes; @@ -446,6 +447,32 @@ static const struct panel_desc ampire_am800480r3tmqwa1h = {  	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,  }; +static const struct display_timing santek_st0700i5y_rbslw_f_timing = { +	.pixelclock = { 26400000, 33300000, 46800000 }, +	.hactive = { 800, 800, 800 }, +	.hfront_porch = { 16, 210, 354 }, +	.hback_porch = { 45, 36, 6 }, +	.hsync_len = { 1, 10, 40 }, +	.vactive = { 480, 480, 480 }, +	.vfront_porch = { 7, 22, 147 }, +	.vback_porch = { 22, 13, 3 }, +	.vsync_len = { 1, 10, 20 }, +	.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | +		DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE +}; + +static const struct panel_desc armadeus_st0700_adapt = { +	.timings = &santek_st0700i5y_rbslw_f_timing, +	.num_timings = 1, +	.bpc = 6, +	.size = { +		.width = 154, +		.height = 86, +	}, +	.bus_format = MEDIA_BUS_FMT_RGB666_1X18, +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; +  static const struct drm_display_mode auo_b101aw03_mode = {  	.clock = 51450,  	.hdisplay = 1024, @@ -1649,6 +1676,29 @@ static const struct panel_desc innolux_zj070na_01p = {  	},  }; +static const struct display_timing koe_tx14d24vm1bpa_timing = { +	.pixelclock = { 5580000, 5850000, 6200000 }, +	.hactive = { 320, 320, 320 }, +	.hfront_porch = { 30, 30, 30 }, +	.hback_porch = { 30, 30, 30 }, +	.hsync_len = { 1, 5, 17 }, +	.vactive = { 240, 240, 240 }, +	.vfront_porch = { 6, 6, 6 }, +	.vback_porch = { 5, 5, 5 }, +	.vsync_len = { 1, 2, 11 }, +	.flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc koe_tx14d24vm1bpa = { +	.timings = &koe_tx14d24vm1bpa_timing, +	.num_timings = 1, +	.bpc = 6, +	.size = { +		.width = 115, +		.height = 86, +	}, +}; +  static const struct display_timing koe_tx31d200vm0baa_timing = {  	.pixelclock = { 39600000, 43200000, 48000000 },  	.hactive = { 1280, 1280, 1280 }, @@ -2722,6 +2772,9 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "arm,rtsm-display",  		.data = &arm_rtsm,  	}, { +		.compatible = "armadeus,st0700-adapt", +		.data = &armadeus_st0700_adapt, +	}, {  		.compatible = "auo,b101aw03",  		.data = &auo_b101aw03,  	}, { @@ -2869,6 +2922,9 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "innolux,zj070na-01p",  		.data = &innolux_zj070na_01p,  	}, { +		.compatible = "koe,tx14d24vm1bpa", +		.data = &koe_tx14d24vm1bpa, +	}, {  		.compatible = "koe,tx31d200vm0baa",  		.data = &koe_tx31d200vm0baa,  	}, { diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 63f9a1c7fb1b..09c5d9a6f9fa 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -305,9 +305,9 @@ static const struct drm_display_mode ts8550b_mode = {  	.htotal		= 480 + 38 + 12 + 12,  	.vdisplay	= 854, -	.vsync_start	= 854 + 4, -	.vsync_end	= 854 + 4 + 8, -	.vtotal		= 854 + 4 + 8 + 18, +	.vsync_start	= 854 + 18, +	.vsync_end	= 854 + 18 + 8, +	.vtotal		= 854 + 18 + 8 + 4,  	.width_mm	= 69,  	.height_mm	= 139, diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index 74284e5afc5d..bf85a8fa9ad0 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -6,15 +6,19 @@   * 2 as published by the Free Software Foundation.   */ +#include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/regulator/consumer.h>  #include <linux/spi/spi.h> -#include <drm/drmP.h> -#include <drm/drm_panel.h> -  #include <video/mipi_display.h> +#include <drm/drm_device.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +  #define ST7789V_COLMOD_RGB_FMT_18BITS		(6 << 4)  #define ST7789V_COLMOD_CTRL_FMT_18BITS		(6 << 0) diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c index fc2a66c53db4..b68b1426e8cc 100644 --- a/drivers/gpu/drm/panel/panel-truly-nt35597.c +++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c @@ -3,11 +3,10 @@   * Copyright (c) 2018, The Linux Foundation. All rights reserved.   */ -#include <drm/drmP.h> -#include <drm/drm_panel.h> -#include <drm/drm_mipi_dsi.h> - +#include <linux/backlight.h> +#include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/module.h>  #include <linux/of_device.h>  #include <linux/of_graph.h>  #include <linux/pinctrl/consumer.h> @@ -15,6 +14,11 @@  #include <video/mipi_display.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +  static const char * const regulator_names[] = {  	"vdda",  	"vdispp", diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index cf596fc0355b..c1058eece16b 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -286,16 +286,17 @@ static void drm_sched_job_timedout(struct work_struct *work)  	job = list_first_entry_or_null(&sched->ring_mirror_list,  				       struct drm_sched_job, node); -	if (job) +	if (job) {  		job->sched->ops->timedout_job(job); -	/* -	 * Guilty job did complete and hence needs to be manually removed -	 * See drm_sched_stop doc. -	 */ -	if (sched->free_guilty) { -		job->sched->ops->free_job(job); -		sched->free_guilty = false; +		/* +		 * Guilty job did complete and hence needs to be manually removed +		 * See drm_sched_stop doc. +		 */ +		if (sched->free_guilty) { +			job->sched->ops->free_job(job); +			sched->free_guilty = false; +		}  	}  	spin_lock_irqsave(&sched->job_list_lock, flags); @@ -352,6 +353,7 @@ EXPORT_SYMBOL(drm_sched_increase_karma);   * drm_sched_stop - stop the scheduler   *   * @sched: scheduler instance + * @bad: job which caused the time out   *   * Stop the scheduler and also removes and frees all completed jobs.   * Note: bad job will not be freed as it might be used later and so it's @@ -421,6 +423,7 @@ EXPORT_SYMBOL(drm_sched_stop);   * drm_sched_job_recovery - recover jobs after a reset   *   * @sched: scheduler instance + * @full_recovery: proceed with complete sched restart   *   */  void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index d8e4a146b320..01db020c8953 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -227,7 +227,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,  	u32 val;  	/* Update lane capabilities according to hw version */ -	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;  	dsi->lane_min_kbps = LANE_MIN_KBPS;  	dsi->lane_max_kbps = LANE_MAX_KBPS;  	if (dsi->hw_version == HWVER_131) { @@ -306,6 +305,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev;  	struct dw_mipi_dsi_stm *dsi; +	struct clk *pclk;  	struct resource *res;  	int ret; @@ -348,6 +348,28 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)  		goto err_clk_get;  	} +	pclk = devm_clk_get(dev, "pclk"); +	if (IS_ERR(pclk)) { +		ret = PTR_ERR(pclk); +		DRM_ERROR("Unable to get peripheral clock: %d\n", ret); +		goto err_dsi_probe; +	} + +	ret = clk_prepare_enable(pclk); +	if (ret) { +		DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); +		goto err_dsi_probe; +	} + +	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; +	clk_disable_unprepare(pclk); + +	if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { +		ret = -ENODEV; +		DRM_ERROR("bad dsi hardware version\n"); +		goto err_dsi_probe; +	} +  	dw_mipi_dsi_stm_plat_data.base = dsi->base;  	dw_mipi_dsi_stm_plat_data.priv_data = dsi; diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 14eb8c40953b..ac29890edeb6 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -507,11 +507,6 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,  	struct ltdc_device *ldev = crtc_to_ltdc(crtc);  	int rate = mode->clock * 1000; -	/* -	 * TODO clk_round_rate() does not work yet. When ready, it can -	 * be used instead of clk_set_rate() then clk_get_rate(). -	 */ -  	clk_disable(ldev->pixel_clk);  	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {  		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); @@ -521,6 +516,9 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,  	adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000; +	DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n", +			 mode->clock, adjusted_mode->clock); +  	return true;  }  | 

