drm/msm/dp: add displayPort driver support
Add the needed displayPort files to enable DP driver on msm target. "dp_display" module is the main module that calls into other sub-modules. "dp_drm" file represents the interface between DRM framework and DP driver. Changes in v12: -- Add support of pm ops in display port driver -- Clear bpp depth bits before writing to MISC register -- Fix edid read Previous Change log: https://lkml.kernel.org/lkml/20200818051137.21478-3-tanmay@codeaurora.org/ Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org> Signed-off-by: Vara Reddy <varar@codeaurora.org> Signed-off-by: Tanmay Shah <tanmay@codeaurora.org> Co-developed-by: Abhinav Kumar <abhinavk@codeaurora.org> Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org> Co-developed-by: Kuogee Hsieh <khsieh@codeaurora.org> Signed-off-by: Kuogee Hsieh <khsieh@codeaurora.org> Co-developed-by: Guenter Roeck <groeck@chromium.org> Signed-off-by: Guenter Roeck <groeck@chromium.org> Co-developed-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Rob Clark <robdclark@chromium.org>
This commit is contained in:
		
							parent
							
								
									b22960b8f2
								
							
						
					
					
						commit
						c943b4948b
					
				| @ -58,6 +58,14 @@ config DRM_MSM_HDMI_HDCP | ||||
| 	help | ||||
| 	  Choose this option to enable HDCP state machine | ||||
| 
 | ||||
| config DRM_MSM_DP | ||||
| 	bool "Enable DisplayPort support in MSM DRM driver" | ||||
| 	depends on DRM_MSM | ||||
| 	help | ||||
| 	  Compile in support for DP driver in MSM DRM driver. DP external | ||||
| 	  display support is enabled through this config option. It can | ||||
| 	  be primary or secondary display on device. | ||||
| 
 | ||||
| config DRM_MSM_DSI | ||||
| 	bool "Enable DSI support in MSM DRM driver" | ||||
| 	depends on DRM_MSM | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| ccflags-y := -I $(srctree)/$(src) | ||||
| ccflags-y += -I $(srctree)/$(src)/disp/dpu1 | ||||
| ccflags-$(CONFIG_DRM_MSM_DSI) += -I $(srctree)/$(src)/dsi | ||||
| ccflags-$(CONFIG_DRM_MSM_DP) += -I $(srctree)/$(src)/dp | ||||
| 
 | ||||
| msm-y := \
 | ||||
| 	adreno/adreno_device.o \
 | ||||
| @ -99,6 +100,17 @@ msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o | ||||
| 
 | ||||
| msm-$(CONFIG_DRM_MSM_GPU_STATE)	+= adreno/a6xx_gpu_state.o | ||||
| 
 | ||||
| msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
 | ||||
| 	dp/dp_catalog.o \
 | ||||
| 	dp/dp_ctrl.o \
 | ||||
| 	dp/dp_display.o \
 | ||||
| 	dp/dp_drm.o \
 | ||||
| 	dp/dp_hpd.o \
 | ||||
| 	dp/dp_link.o \
 | ||||
| 	dp/dp_panel.o \
 | ||||
| 	dp/dp_parser.o \
 | ||||
| 	dp/dp_power.o | ||||
| 
 | ||||
| msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o | ||||
| msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o | ||||
| msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o | ||||
|  | ||||
| @ -1001,6 +1001,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, | ||||
| 
 | ||||
| 	trace_dpu_enc_mode_set(DRMID(drm_enc)); | ||||
| 
 | ||||
| 	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) | ||||
| 		msm_dp_display_mode_set(priv->dp, drm_enc, mode, adj_mode); | ||||
| 
 | ||||
| 	list_for_each_entry(conn_iter, connector_list, head) | ||||
| 		if (conn_iter->encoder == drm_enc) | ||||
| 			conn = conn_iter; | ||||
| @ -1146,6 +1149,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc) | ||||
| { | ||||
| 	struct dpu_encoder_virt *dpu_enc = NULL; | ||||
| 	int ret = 0; | ||||
| 	struct msm_drm_private *priv; | ||||
| 	struct drm_display_mode *cur_mode = NULL; | ||||
| 
 | ||||
| 	if (!drm_enc) { | ||||
| @ -1156,6 +1160,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc) | ||||
| 
 | ||||
| 	mutex_lock(&dpu_enc->enc_lock); | ||||
| 	cur_mode = &dpu_enc->base.crtc->state->adjusted_mode; | ||||
| 	priv = drm_enc->dev->dev_private; | ||||
| 
 | ||||
| 	trace_dpu_enc_enable(DRMID(drm_enc), cur_mode->hdisplay, | ||||
| 			     cur_mode->vdisplay); | ||||
| @ -1176,6 +1181,15 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc) | ||||
| 
 | ||||
| 	_dpu_encoder_virt_enable_helper(drm_enc); | ||||
| 
 | ||||
| 	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) { | ||||
| 		ret = msm_dp_display_enable(priv->dp, | ||||
| 						drm_enc); | ||||
| 		if (ret) { | ||||
| 			DPU_ERROR_ENC(dpu_enc, "dp display enable failed: %d\n", | ||||
| 				ret); | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 	dpu_enc->enabled = true; | ||||
| 
 | ||||
| out: | ||||
| @ -1234,6 +1248,11 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc) | ||||
| 
 | ||||
| 	DPU_DEBUG_ENC(dpu_enc, "encoder disabled\n"); | ||||
| 
 | ||||
| 	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) { | ||||
| 		if (msm_dp_display_disable(priv->dp, drm_enc)) | ||||
| 			DPU_ERROR_ENC(dpu_enc, "dp display disable failed\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&dpu_enc->enc_lock); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -100,6 +100,14 @@ static void drm_mode_to_intf_timing_params( | ||||
| 	 * display_v_end -= mode->hsync_start - mode->hdisplay; | ||||
| 	 * } | ||||
| 	 */ | ||||
| 	/* for DP/EDP, Shift timings to align it to bottom right */ | ||||
| 	if ((phys_enc->hw_intf->cap->type == INTF_DP) || | ||||
| 		(phys_enc->hw_intf->cap->type == INTF_EDP)) { | ||||
| 		timing->h_back_porch += timing->h_front_porch; | ||||
| 		timing->h_front_porch = 0; | ||||
| 		timing->v_back_porch += timing->v_front_porch; | ||||
| 		timing->v_front_porch = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static u32 get_horizontal_total(const struct intf_timing_params *timing) | ||||
|  | ||||
							
								
								
									
										535
									
								
								drivers/gpu/drm/msm/dp/dp_aux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										535
									
								
								drivers/gpu/drm/msm/dp/dp_aux.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,535 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/delay.h> | ||||
| 
 | ||||
| #include "dp_reg.h" | ||||
| #include "dp_aux.h" | ||||
| 
 | ||||
| #define DP_AUX_ENUM_STR(x)		#x | ||||
| 
 | ||||
| struct dp_aux_private { | ||||
| 	struct device *dev; | ||||
| 	struct dp_catalog *catalog; | ||||
| 
 | ||||
| 	struct mutex mutex; | ||||
| 	struct completion comp; | ||||
| 
 | ||||
| 	u32 aux_error_num; | ||||
| 	u32 retry_cnt; | ||||
| 	bool cmd_busy; | ||||
| 	bool native; | ||||
| 	bool read; | ||||
| 	bool no_send_addr; | ||||
| 	bool no_send_stop; | ||||
| 	u32 offset; | ||||
| 	u32 segment; | ||||
| 	u32 isr; | ||||
| 
 | ||||
| 	struct drm_dp_aux dp_aux; | ||||
| }; | ||||
| 
 | ||||
| static const char *dp_aux_get_error(u32 aux_error) | ||||
| { | ||||
| 	switch (aux_error) { | ||||
| 	case DP_AUX_ERR_NONE: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE); | ||||
| 	case DP_AUX_ERR_ADDR: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR); | ||||
| 	case DP_AUX_ERR_TOUT: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT); | ||||
| 	case DP_AUX_ERR_NACK: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK); | ||||
| 	case DP_AUX_ERR_DEFER: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER); | ||||
| 	case DP_AUX_ERR_NACK_DEFER: | ||||
| 		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER); | ||||
| 	default: | ||||
| 		return "unknown"; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static u32 dp_aux_write(struct dp_aux_private *aux, | ||||
| 			struct drm_dp_aux_msg *msg) | ||||
| { | ||||
| 	u32 data[4], reg, len; | ||||
| 	u8 *msgdata = msg->buffer; | ||||
| 	int const AUX_CMD_FIFO_LEN = 128; | ||||
| 	int i = 0; | ||||
| 
 | ||||
| 	if (aux->read) | ||||
| 		len = 4; | ||||
| 	else | ||||
| 		len = msg->size + 4; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * cmd fifo only has depth of 144 bytes | ||||
| 	 * limit buf length to 128 bytes here | ||||
| 	 */ | ||||
| 	if (len > AUX_CMD_FIFO_LEN) { | ||||
| 		DRM_ERROR("buf size greater than allowed size of 128 bytes\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Pack cmd and write to HW */ | ||||
| 	data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */ | ||||
| 	if (aux->read) | ||||
| 		data[0] |=  BIT(4); /* R/W */ | ||||
| 
 | ||||
| 	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */ | ||||
| 	data[2] = msg->address & 0xff;		/* addr[7:0] */ | ||||
| 	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */ | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		reg = (i < 4) ? data[i] : msgdata[i - 4]; | ||||
| 		/* index = 0, write */ | ||||
| 		reg = (((reg) << DP_AUX_DATA_OFFSET) | ||||
| 		       & DP_AUX_DATA_MASK) | DP_AUX_DATA_WRITE; | ||||
| 		if (i == 0) | ||||
| 			reg |= DP_AUX_DATA_INDEX_WRITE; | ||||
| 		aux->catalog->aux_data = reg; | ||||
| 		dp_catalog_aux_write_data(aux->catalog); | ||||
| 	} | ||||
| 
 | ||||
| 	dp_catalog_aux_clear_trans(aux->catalog, false); | ||||
| 	dp_catalog_aux_clear_hw_interrupts(aux->catalog); | ||||
| 
 | ||||
| 	reg = 0; /* Transaction number == 1 */ | ||||
| 	if (!aux->native) { /* i2c */ | ||||
| 		reg |= DP_AUX_TRANS_CTRL_I2C; | ||||
| 
 | ||||
| 		if (aux->no_send_addr) | ||||
| 			reg |= DP_AUX_TRANS_CTRL_NO_SEND_ADDR; | ||||
| 
 | ||||
| 		if (aux->no_send_stop) | ||||
| 			reg |= DP_AUX_TRANS_CTRL_NO_SEND_STOP; | ||||
| 	} | ||||
| 
 | ||||
| 	reg |= DP_AUX_TRANS_CTRL_GO; | ||||
| 	aux->catalog->aux_data = reg; | ||||
| 	dp_catalog_aux_write_trans(aux->catalog); | ||||
| 
 | ||||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux, | ||||
| 			      struct drm_dp_aux_msg *msg) | ||||
| { | ||||
| 	u32 ret, len, timeout; | ||||
| 	int aux_timeout_ms = HZ/4; | ||||
| 
 | ||||
| 	reinit_completion(&aux->comp); | ||||
| 
 | ||||
| 	len = dp_aux_write(aux, msg); | ||||
| 	if (len == 0) { | ||||
| 		DRM_ERROR("DP AUX write failed\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms); | ||||
| 	if (!timeout) { | ||||
| 		DRM_ERROR("aux %s timeout\n", (aux->read ? "read" : "write")); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	if (aux->aux_error_num == DP_AUX_ERR_NONE) { | ||||
| 		ret = len; | ||||
| 	} else { | ||||
| 		DRM_ERROR_RATELIMITED("aux err: %s\n", | ||||
| 			dp_aux_get_error(aux->aux_error_num)); | ||||
| 
 | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux, | ||||
| 		struct drm_dp_aux_msg *msg) | ||||
| { | ||||
| 	u32 data; | ||||
| 	u8 *dp; | ||||
| 	u32 i, actual_i; | ||||
| 	u32 len = msg->size; | ||||
| 
 | ||||
| 	dp_catalog_aux_clear_trans(aux->catalog, true); | ||||
| 
 | ||||
| 	data = DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */ | ||||
| 	data |= DP_AUX_DATA_READ;  /* read */ | ||||
| 
 | ||||
| 	aux->catalog->aux_data = data; | ||||
| 	dp_catalog_aux_write_data(aux->catalog); | ||||
| 
 | ||||
| 	dp = msg->buffer; | ||||
| 
 | ||||
| 	/* discard first byte */ | ||||
| 	data = dp_catalog_aux_read_data(aux->catalog); | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		data = dp_catalog_aux_read_data(aux->catalog); | ||||
| 		*dp++ = (u8)((data >> DP_AUX_DATA_OFFSET) & 0xff); | ||||
| 
 | ||||
| 		actual_i = (data >> DP_AUX_DATA_INDEX_OFFSET) & 0xFF; | ||||
| 		if (i != actual_i) | ||||
| 			DRM_ERROR("Index mismatch: expected %d, found %d\n", | ||||
| 				i, actual_i); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void dp_aux_native_handler(struct dp_aux_private *aux) | ||||
| { | ||||
| 	u32 isr = aux->isr; | ||||
| 
 | ||||
| 	if (isr & DP_INTR_AUX_I2C_DONE) | ||||
| 		aux->aux_error_num = DP_AUX_ERR_NONE; | ||||
| 	else if (isr & DP_INTR_WRONG_ADDR) | ||||
| 		aux->aux_error_num = DP_AUX_ERR_ADDR; | ||||
| 	else if (isr & DP_INTR_TIMEOUT) | ||||
| 		aux->aux_error_num = DP_AUX_ERR_TOUT; | ||||
| 	if (isr & DP_INTR_NACK_DEFER) | ||||
| 		aux->aux_error_num = DP_AUX_ERR_NACK; | ||||
| 	if (isr & DP_INTR_AUX_ERROR) { | ||||
| 		aux->aux_error_num = DP_AUX_ERR_PHY; | ||||
| 		dp_catalog_aux_clear_hw_interrupts(aux->catalog); | ||||
| 	} | ||||
| 
 | ||||
| 	complete(&aux->comp); | ||||
| } | ||||
| 
 | ||||
| static void dp_aux_i2c_handler(struct dp_aux_private *aux) | ||||
| { | ||||
| 	u32 isr = aux->isr; | ||||
| 
 | ||||
| 	if (isr & DP_INTR_AUX_I2C_DONE) { | ||||
| 		if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER)) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_NACK; | ||||
| 		else | ||||
| 			aux->aux_error_num = DP_AUX_ERR_NONE; | ||||
| 	} else { | ||||
| 		if (isr & DP_INTR_WRONG_ADDR) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_ADDR; | ||||
| 		else if (isr & DP_INTR_TIMEOUT) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_TOUT; | ||||
| 		if (isr & DP_INTR_NACK_DEFER) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_NACK_DEFER; | ||||
| 		if (isr & DP_INTR_I2C_NACK) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_NACK; | ||||
| 		if (isr & DP_INTR_I2C_DEFER) | ||||
| 			aux->aux_error_num = DP_AUX_ERR_DEFER; | ||||
| 		if (isr & DP_INTR_AUX_ERROR) { | ||||
| 			aux->aux_error_num = DP_AUX_ERR_PHY; | ||||
| 			dp_catalog_aux_clear_hw_interrupts(aux->catalog); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	complete(&aux->comp); | ||||
| } | ||||
| 
 | ||||
| static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux, | ||||
| 					     struct drm_dp_aux_msg *input_msg) | ||||
| { | ||||
| 	u32 edid_address = 0x50; | ||||
| 	u32 segment_address = 0x30; | ||||
| 	bool i2c_read = input_msg->request & | ||||
| 		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ); | ||||
| 	u8 *data; | ||||
| 
 | ||||
| 	if (aux->native || i2c_read || ((input_msg->address != edid_address) && | ||||
| 		(input_msg->address != segment_address))) | ||||
| 		return; | ||||
| 
 | ||||
| 
 | ||||
| 	data = input_msg->buffer; | ||||
| 	if (input_msg->address == segment_address) | ||||
| 		aux->segment = *data; | ||||
| 	else | ||||
| 		aux->offset = *data; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_aux_transfer_helper() - helper function for EDID read transactions | ||||
|  * | ||||
|  * @aux: DP AUX private structure | ||||
|  * @input_msg: input message from DRM upstream APIs | ||||
|  * @send_seg: send the segment to sink | ||||
|  * | ||||
|  * return: void | ||||
|  * | ||||
|  * This helper function is used to fix EDID reads for non-compliant | ||||
|  * sinks that do not handle the i2c middle-of-transaction flag correctly. | ||||
|  */ | ||||
| static void dp_aux_transfer_helper(struct dp_aux_private *aux, | ||||
| 				   struct drm_dp_aux_msg *input_msg, | ||||
| 				   bool send_seg) | ||||
| { | ||||
| 	struct drm_dp_aux_msg helper_msg; | ||||
| 	u32 message_size = 0x10; | ||||
| 	u32 segment_address = 0x30; | ||||
| 	u32 const edid_block_length = 0x80; | ||||
| 	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT; | ||||
| 	bool i2c_read = input_msg->request & | ||||
| 		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ); | ||||
| 
 | ||||
| 	if (!i2c_mot || !i2c_read || (input_msg->size == 0)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Sending the segment value and EDID offset will be performed | ||||
| 	 * from the DRM upstream EDID driver for each block. Avoid | ||||
| 	 * duplicate AUX transactions related to this while reading the | ||||
| 	 * first 16 bytes of each block. | ||||
| 	 */ | ||||
| 	if (!(aux->offset % edid_block_length) || !send_seg) | ||||
| 		goto end; | ||||
| 
 | ||||
| 	aux->read = false; | ||||
| 	aux->cmd_busy = true; | ||||
| 	aux->no_send_addr = true; | ||||
| 	aux->no_send_stop = true; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Send the segment address for every i2c read in which the | ||||
| 	 * middle-of-tranaction flag is set. This is required to support EDID | ||||
| 	 * reads of more than 2 blocks as the segment address is reset to 0 | ||||
| 	 * since we are overriding the middle-of-transaction flag for read | ||||
| 	 * transactions. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (aux->segment) { | ||||
| 		memset(&helper_msg, 0, sizeof(helper_msg)); | ||||
| 		helper_msg.address = segment_address; | ||||
| 		helper_msg.buffer = &aux->segment; | ||||
| 		helper_msg.size = 1; | ||||
| 		dp_aux_cmd_fifo_tx(aux, &helper_msg); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Send the offset address for every i2c read in which the | ||||
| 	 * middle-of-transaction flag is set. This will ensure that the sink | ||||
| 	 * will update its read pointer and return the correct portion of the | ||||
| 	 * EDID buffer in the subsequent i2c read trasntion triggered in the | ||||
| 	 * native AUX transfer function. | ||||
| 	 */ | ||||
| 	memset(&helper_msg, 0, sizeof(helper_msg)); | ||||
| 	helper_msg.address = input_msg->address; | ||||
| 	helper_msg.buffer = &aux->offset; | ||||
| 	helper_msg.size = 1; | ||||
| 	dp_aux_cmd_fifo_tx(aux, &helper_msg); | ||||
| 
 | ||||
| end: | ||||
| 	aux->offset += message_size; | ||||
| 	if (aux->offset == 0x80 || aux->offset == 0x100) | ||||
| 		aux->segment = 0x0; /* reset segment at end of block */ | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This function does the real job to process an AUX transaction. | ||||
|  * It will call aux_reset() function to reset the AUX channel, | ||||
|  * if the waiting is timeout. | ||||
|  */ | ||||
| static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux, | ||||
| 			       struct drm_dp_aux_msg *msg) | ||||
| { | ||||
| 	ssize_t ret; | ||||
| 	int const aux_cmd_native_max = 16; | ||||
| 	int const aux_cmd_i2c_max = 128; | ||||
| 	int const retry_count = 5; | ||||
| 	struct dp_aux_private *aux = container_of(dp_aux, | ||||
| 		struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	mutex_lock(&aux->mutex); | ||||
| 
 | ||||
| 	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ); | ||||
| 
 | ||||
| 	/* Ignore address only message */ | ||||
| 	if ((msg->size == 0) || (msg->buffer == NULL)) { | ||||
| 		msg->reply = aux->native ? | ||||
| 			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; | ||||
| 		ret = msg->size; | ||||
| 		goto unlock_exit; | ||||
| 	} | ||||
| 
 | ||||
| 	/* msg sanity check */ | ||||
| 	if ((aux->native && (msg->size > aux_cmd_native_max)) || | ||||
| 		(msg->size > aux_cmd_i2c_max)) { | ||||
| 		DRM_ERROR("%s: invalid msg: size(%zu), request(%x)\n", | ||||
| 			__func__, msg->size, msg->request); | ||||
| 		ret = -EINVAL; | ||||
| 		goto unlock_exit; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_aux_update_offset_and_segment(aux, msg); | ||||
| 	dp_aux_transfer_helper(aux, msg, true); | ||||
| 
 | ||||
| 	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ); | ||||
| 	aux->cmd_busy = true; | ||||
| 
 | ||||
| 	if (aux->read) { | ||||
| 		aux->no_send_addr = true; | ||||
| 		aux->no_send_stop = false; | ||||
| 	} else { | ||||
| 		aux->no_send_addr = true; | ||||
| 		aux->no_send_stop = true; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = dp_aux_cmd_fifo_tx(aux, msg); | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		if (aux->native) { | ||||
| 			aux->retry_cnt++; | ||||
| 			if (!(aux->retry_cnt % retry_count)) | ||||
| 				dp_catalog_aux_update_cfg(aux->catalog, | ||||
| 					PHY_AUX_CFG1); | ||||
| 			dp_catalog_aux_reset(aux->catalog); | ||||
| 		} | ||||
| 		goto unlock_exit; | ||||
| 	} | ||||
| 
 | ||||
| 	if (aux->aux_error_num == DP_AUX_ERR_NONE) { | ||||
| 		if (aux->read) | ||||
| 			dp_aux_cmd_fifo_rx(aux, msg); | ||||
| 
 | ||||
| 		msg->reply = aux->native ? | ||||
| 			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; | ||||
| 	} else { | ||||
| 		/* Reply defer to retry */ | ||||
| 		msg->reply = aux->native ? | ||||
| 			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Return requested size for success or retry */ | ||||
| 	ret = msg->size; | ||||
| 	aux->retry_cnt = 0; | ||||
| 
 | ||||
| unlock_exit: | ||||
| 	aux->cmd_busy = false; | ||||
| 	mutex_unlock(&aux->mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void dp_aux_isr(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	if (!dp_aux) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	aux->isr = dp_catalog_aux_get_irq(aux->catalog); | ||||
| 
 | ||||
| 	if (!aux->cmd_busy) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (aux->native) | ||||
| 		dp_aux_native_handler(aux); | ||||
| 	else | ||||
| 		dp_aux_i2c_handler(aux); | ||||
| } | ||||
| 
 | ||||
| void dp_aux_reconfig(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	dp_catalog_aux_update_cfg(aux->catalog, PHY_AUX_CFG1); | ||||
| 	dp_catalog_aux_reset(aux->catalog); | ||||
| } | ||||
| 
 | ||||
| void dp_aux_init(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	if (!dp_aux) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	dp_catalog_aux_setup(aux->catalog); | ||||
| 	dp_catalog_aux_enable(aux->catalog, true); | ||||
| 	aux->retry_cnt = 0; | ||||
| } | ||||
| 
 | ||||
| void dp_aux_deinit(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	dp_catalog_aux_enable(aux->catalog, false); | ||||
| } | ||||
| 
 | ||||
| int dp_aux_register(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!dp_aux) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	aux->dp_aux.name = "dpu_dp_aux"; | ||||
| 	aux->dp_aux.dev = aux->dev; | ||||
| 	aux->dp_aux.transfer = dp_aux_transfer; | ||||
| 	ret = drm_dp_aux_register(&aux->dp_aux); | ||||
| 	if (ret) { | ||||
| 		DRM_ERROR("%s: failed to register drm aux: %d\n", __func__, | ||||
| 				ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dp_aux_unregister(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	drm_dp_aux_unregister(dp_aux); | ||||
| } | ||||
| 
 | ||||
| struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	if (!catalog) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 	} | ||||
| 
 | ||||
| 	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL); | ||||
| 	if (!aux) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	init_completion(&aux->comp); | ||||
| 	aux->cmd_busy = false; | ||||
| 	mutex_init(&aux->mutex); | ||||
| 
 | ||||
| 	aux->dev = dev; | ||||
| 	aux->catalog = catalog; | ||||
| 	aux->retry_cnt = 0; | ||||
| 
 | ||||
| 	return &aux->dp_aux; | ||||
| } | ||||
| 
 | ||||
| void dp_aux_put(struct drm_dp_aux *dp_aux) | ||||
| { | ||||
| 	struct dp_aux_private *aux; | ||||
| 
 | ||||
| 	if (!dp_aux) | ||||
| 		return; | ||||
| 
 | ||||
| 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux); | ||||
| 
 | ||||
| 	mutex_destroy(&aux->mutex); | ||||
| 
 | ||||
| 	devm_kfree(aux->dev, aux); | ||||
| } | ||||
							
								
								
									
										30
									
								
								drivers/gpu/drm/msm/dp/dp_aux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								drivers/gpu/drm/msm/dp/dp_aux.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_AUX_H_ | ||||
| #define _DP_AUX_H_ | ||||
| 
 | ||||
| #include "dp_catalog.h" | ||||
| #include <drm/drm_dp_helper.h> | ||||
| 
 | ||||
| #define DP_AUX_ERR_NONE		0 | ||||
| #define DP_AUX_ERR_ADDR		-1 | ||||
| #define DP_AUX_ERR_TOUT		-2 | ||||
| #define DP_AUX_ERR_NACK		-3 | ||||
| #define DP_AUX_ERR_DEFER	-4 | ||||
| #define DP_AUX_ERR_NACK_DEFER	-5 | ||||
| #define DP_AUX_ERR_PHY		-6 | ||||
| 
 | ||||
| int dp_aux_register(struct drm_dp_aux *dp_aux); | ||||
| void dp_aux_unregister(struct drm_dp_aux *dp_aux); | ||||
| void dp_aux_isr(struct drm_dp_aux *dp_aux); | ||||
| void dp_aux_init(struct drm_dp_aux *dp_aux); | ||||
| void dp_aux_deinit(struct drm_dp_aux *dp_aux); | ||||
| void dp_aux_reconfig(struct drm_dp_aux *dp_aux); | ||||
| 
 | ||||
| struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog); | ||||
| void dp_aux_put(struct drm_dp_aux *aux); | ||||
| 
 | ||||
| #endif /*__DP_AUX_H_*/ | ||||
							
								
								
									
										1019
									
								
								drivers/gpu/drm/msm/dp/dp_catalog.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1019
									
								
								drivers/gpu/drm/msm/dp/dp_catalog.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										102
									
								
								drivers/gpu/drm/msm/dp/dp_catalog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								drivers/gpu/drm/msm/dp/dp_catalog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_CATALOG_H_ | ||||
| #define _DP_CATALOG_H_ | ||||
| 
 | ||||
| #include "dp_parser.h" | ||||
| 
 | ||||
| /* interrupts */ | ||||
| #define DP_INTR_HPD		BIT(0) | ||||
| #define DP_INTR_AUX_I2C_DONE	BIT(3) | ||||
| #define DP_INTR_WRONG_ADDR	BIT(6) | ||||
| #define DP_INTR_TIMEOUT		BIT(9) | ||||
| #define DP_INTR_NACK_DEFER	BIT(12) | ||||
| #define DP_INTR_WRONG_DATA_CNT	BIT(15) | ||||
| #define DP_INTR_I2C_NACK	BIT(18) | ||||
| #define DP_INTR_I2C_DEFER	BIT(21) | ||||
| #define DP_INTR_PLL_UNLOCKED	BIT(24) | ||||
| #define DP_INTR_AUX_ERROR	BIT(27) | ||||
| 
 | ||||
| #define DP_INTR_READY_FOR_VIDEO		BIT(0) | ||||
| #define DP_INTR_IDLE_PATTERN_SENT	BIT(3) | ||||
| #define DP_INTR_FRAME_END		BIT(6) | ||||
| #define DP_INTR_CRC_UPDATED		BIT(9) | ||||
| 
 | ||||
| #define DP_AUX_CFG_MAX_VALUE_CNT 3 | ||||
| 
 | ||||
| /* PHY AUX config registers */ | ||||
| enum dp_phy_aux_config_type { | ||||
| 	PHY_AUX_CFG0, | ||||
| 	PHY_AUX_CFG1, | ||||
| 	PHY_AUX_CFG2, | ||||
| 	PHY_AUX_CFG3, | ||||
| 	PHY_AUX_CFG4, | ||||
| 	PHY_AUX_CFG5, | ||||
| 	PHY_AUX_CFG6, | ||||
| 	PHY_AUX_CFG7, | ||||
| 	PHY_AUX_CFG8, | ||||
| 	PHY_AUX_CFG9, | ||||
| 	PHY_AUX_CFG_MAX, | ||||
| }; | ||||
| 
 | ||||
| struct dp_catalog { | ||||
| 	u32 aux_data; | ||||
| 	u32 total; | ||||
| 	u32 sync_start; | ||||
| 	u32 width_blanking; | ||||
| 	u32 dp_active; | ||||
| }; | ||||
| 
 | ||||
| /* AUX APIs */ | ||||
| u32 dp_catalog_aux_read_data(struct dp_catalog *dp_catalog); | ||||
| int dp_catalog_aux_write_data(struct dp_catalog *dp_catalog); | ||||
| int dp_catalog_aux_write_trans(struct dp_catalog *dp_catalog); | ||||
| int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read); | ||||
| int dp_catalog_aux_clear_hw_interrupts(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_aux_reset(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable); | ||||
| void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog, | ||||
| 			enum dp_phy_aux_config_type type); | ||||
| void dp_catalog_aux_setup(struct dp_catalog *dp_catalog); | ||||
| int dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog); | ||||
| 
 | ||||
| /* DP Controller APIs */ | ||||
| void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state); | ||||
| void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 config); | ||||
| void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable); | ||||
| void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb); | ||||
| void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate, | ||||
| 				u32 stream_rate_khz, bool fixed_nvid); | ||||
| int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog, u32 pattern); | ||||
| void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip); | ||||
| bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable); | ||||
| void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool enable); | ||||
| void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped, | ||||
| 				u8 lane_cnt); | ||||
| int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level, | ||||
| 				u8 p_level); | ||||
| int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog, | ||||
| 				u32 dp_tu, u32 valid_boundary, | ||||
| 				u32 valid_boundary2); | ||||
| void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog, | ||||
| 				u32 pattern); | ||||
| u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog); | ||||
| 
 | ||||
| /* DP Panel APIs */ | ||||
| int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_dump_regs(struct dp_catalog *dp_catalog); | ||||
| void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog, | ||||
| 				struct drm_display_mode *drm_mode); | ||||
| void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog); | ||||
| 
 | ||||
| struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io); | ||||
| 
 | ||||
| #endif /* _DP_CATALOG_H_ */ | ||||
							
								
								
									
										1694
									
								
								drivers/gpu/drm/msm/dp/dp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1694
									
								
								drivers/gpu/drm/msm/dp/dp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										35
									
								
								drivers/gpu/drm/msm/dp/dp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								drivers/gpu/drm/msm/dp/dp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_CTRL_H_ | ||||
| #define _DP_CTRL_H_ | ||||
| 
 | ||||
| #include "dp_aux.h" | ||||
| #include "dp_panel.h" | ||||
| #include "dp_link.h" | ||||
| #include "dp_parser.h" | ||||
| #include "dp_power.h" | ||||
| #include "dp_catalog.h" | ||||
| 
 | ||||
| struct dp_ctrl { | ||||
| 	bool orientation; | ||||
| 	atomic_t aborted; | ||||
| 	u32 pixel_rate; | ||||
| }; | ||||
| 
 | ||||
| int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip); | ||||
| void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl); | ||||
| int dp_ctrl_on(struct dp_ctrl *dp_ctrl); | ||||
| int dp_ctrl_off(struct dp_ctrl *dp_ctrl); | ||||
| void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl); | ||||
| void dp_ctrl_isr(struct dp_ctrl *dp_ctrl); | ||||
| void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl); | ||||
| struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link, | ||||
| 			struct dp_panel *panel,	struct drm_dp_aux *aux, | ||||
| 			struct dp_power *power, struct dp_catalog *catalog, | ||||
| 			struct dp_parser *parser); | ||||
| void dp_ctrl_put(struct dp_ctrl *dp_ctrl); | ||||
| 
 | ||||
| #endif /* _DP_CTRL_H_ */ | ||||
							
								
								
									
										936
									
								
								drivers/gpu/drm/msm/dp/dp_display.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										936
									
								
								drivers/gpu/drm/msm/dp/dp_display.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,936 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/component.h> | ||||
| #include <linux/of_irq.h> | ||||
| 
 | ||||
| #include "msm_drv.h" | ||||
| #include "msm_kms.h" | ||||
| #include "dp_hpd.h" | ||||
| #include "dp_parser.h" | ||||
| #include "dp_power.h" | ||||
| #include "dp_catalog.h" | ||||
| #include "dp_aux.h" | ||||
| #include "dp_link.h" | ||||
| #include "dp_panel.h" | ||||
| #include "dp_ctrl.h" | ||||
| #include "dp_display.h" | ||||
| #include "dp_drm.h" | ||||
| 
 | ||||
| static struct msm_dp *g_dp_display; | ||||
| #define HPD_STRING_SIZE 30 | ||||
| 
 | ||||
| struct dp_display_private { | ||||
| 	char *name; | ||||
| 	int irq; | ||||
| 
 | ||||
| 	/* state variables */ | ||||
| 	bool core_initialized; | ||||
| 	bool power_on; | ||||
| 	bool hpd_irq_on; | ||||
| 	bool audio_supported; | ||||
| 
 | ||||
| 	struct platform_device *pdev; | ||||
| 	struct dentry *root; | ||||
| 	struct completion notification_comp; | ||||
| 
 | ||||
| 	struct dp_usbpd   *usbpd; | ||||
| 	struct dp_parser  *parser; | ||||
| 	struct dp_power   *power; | ||||
| 	struct dp_catalog *catalog; | ||||
| 	struct drm_dp_aux *aux; | ||||
| 	struct dp_link    *link; | ||||
| 	struct dp_panel   *panel; | ||||
| 	struct dp_ctrl    *ctrl; | ||||
| 
 | ||||
| 	struct dp_usbpd_cb usbpd_cb; | ||||
| 	struct dp_display_mode dp_mode; | ||||
| 	struct msm_dp dp_display; | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id dp_dt_match[] = { | ||||
| 	{.compatible = "qcom,sc7180-dp"}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static irqreturn_t dp_display_irq(int irq, void *dev_id) | ||||
| { | ||||
| 	struct dp_display_private *dp = dev_id; | ||||
| 
 | ||||
| 	/* DP controller isr */ | ||||
| 	dp_ctrl_isr(dp->ctrl); | ||||
| 
 | ||||
| 	/* DP aux isr */ | ||||
| 	dp_aux_isr(dp->aux); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_bind(struct device *dev, struct device *master, | ||||
| 			   void *data) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 	struct drm_device *drm; | ||||
| 	struct msm_drm_private *priv; | ||||
| 	struct platform_device *pdev = to_platform_device(dev); | ||||
| 
 | ||||
| 	drm = dev_get_drvdata(master); | ||||
| 
 | ||||
| 	dp = platform_get_drvdata(pdev); | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("DP driver bind failed. Invalid driver data\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->dp_display.drm_dev = drm; | ||||
| 	priv = drm->dev_private; | ||||
| 	priv->dp = &(dp->dp_display); | ||||
| 
 | ||||
| 	rc = dp->parser->parse(dp->parser); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("device tree parsing failed\n"); | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_aux_register(dp->aux); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DRM DP AUX register failed\n"); | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_power_client_init(dp->power); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("Power client create failed\n"); | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| end: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_unbind(struct device *dev, struct device *master, | ||||
| 			      void *data) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 	struct platform_device *pdev = to_platform_device(dev); | ||||
| 	struct drm_device *drm = dev_get_drvdata(master); | ||||
| 	struct msm_drm_private *priv = drm->dev_private; | ||||
| 
 | ||||
| 	dp = platform_get_drvdata(pdev); | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("Invalid DP driver data\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_power_client_deinit(dp->power); | ||||
| 	dp_aux_unregister(dp->aux); | ||||
| 	priv->dp = NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct component_ops dp_display_comp_ops = { | ||||
| 	.bind = dp_display_bind, | ||||
| 	.unbind = dp_display_unbind, | ||||
| }; | ||||
| 
 | ||||
| static bool dp_display_is_ds_bridge(struct dp_panel *panel) | ||||
| { | ||||
| 	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] & | ||||
| 		DP_DWN_STRM_PORT_PRESENT); | ||||
| } | ||||
| 
 | ||||
| static bool dp_display_is_sink_count_zero(struct dp_display_private *dp) | ||||
| { | ||||
| 	return dp_display_is_ds_bridge(dp->panel) && | ||||
| 		(dp->link->sink_count == 0); | ||||
| } | ||||
| 
 | ||||
| static void dp_display_send_hpd_event(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 	struct drm_connector *connector; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	connector = dp->dp_display.connector; | ||||
| 	drm_helper_hpd_irq_event(connector->dev); | ||||
| } | ||||
| 
 | ||||
| static int dp_display_send_hpd_notification(struct dp_display_private *dp, | ||||
| 					    bool hpd) | ||||
| { | ||||
| 	static bool encoder_mode_set; | ||||
| 	struct msm_drm_private *priv = dp->dp_display.drm_dev->dev_private; | ||||
| 	struct msm_kms *kms = priv->kms; | ||||
| 
 | ||||
| 	mutex_lock(&dp->dp_display.connect_mutex); | ||||
| 	if ((hpd && dp->dp_display.is_connected) || | ||||
| 			(!hpd && !dp->dp_display.is_connected)) { | ||||
| 		DRM_DEBUG_DP("HPD already %s\n", (hpd ? "on" : "off")); | ||||
| 		mutex_unlock(&dp->dp_display.connect_mutex); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* reset video pattern flag on disconnect */ | ||||
| 	if (!hpd) | ||||
| 		dp->panel->video_test = false; | ||||
| 
 | ||||
| 	dp->dp_display.is_connected = hpd; | ||||
| 	reinit_completion(&dp->notification_comp); | ||||
| 
 | ||||
| 	if (dp->dp_display.is_connected && dp->dp_display.encoder | ||||
| 				&& !encoder_mode_set | ||||
| 				&& kms->funcs->set_encoder_mode) { | ||||
| 		kms->funcs->set_encoder_mode(kms, | ||||
| 				dp->dp_display.encoder, false); | ||||
| 		DRM_DEBUG_DP("set_encoder_mode() Completed\n"); | ||||
| 		encoder_mode_set = true; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display_send_hpd_event(&dp->dp_display); | ||||
| 
 | ||||
| 	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) { | ||||
| 		pr_warn("%s timeout\n", hpd ? "connect" : "disconnect"); | ||||
| 		mutex_unlock(&dp->dp_display.connect_mutex); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&dp->dp_display.connect_mutex); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_process_hpd_high(struct dp_display_private *dp) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct edid *edid; | ||||
| 
 | ||||
| 	dp_aux_init(dp->aux); | ||||
| 
 | ||||
| 	if (dp->link->psm_enabled) | ||||
| 		goto notify; | ||||
| 
 | ||||
| 	dp->panel->max_dp_lanes = dp->parser->max_dp_lanes; | ||||
| 
 | ||||
| 	rc = dp_panel_read_sink_caps(dp->panel, dp->dp_display.connector); | ||||
| 	if (rc) | ||||
| 		goto notify; | ||||
| 
 | ||||
| 	dp_link_process_request(dp->link); | ||||
| 
 | ||||
| 	if (dp_display_is_sink_count_zero(dp)) { | ||||
| 		DRM_DEBUG_DP("no downstream devices connected\n"); | ||||
| 		rc = -EINVAL; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	edid = dp->panel->edid; | ||||
| 
 | ||||
| 	dp->audio_supported = drm_detect_monitor_audio(edid); | ||||
| 
 | ||||
| 	dp_panel_handle_sink_request(dp->panel); | ||||
| 
 | ||||
| 	dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; | ||||
| 	dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes; | ||||
| notify: | ||||
| 	dp_display_send_hpd_notification(dp, true); | ||||
| 
 | ||||
| end: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_host_init(struct dp_display_private *dp) | ||||
| { | ||||
| 	bool flip = false; | ||||
| 
 | ||||
| 	if (dp->core_initialized) { | ||||
| 		DRM_DEBUG_DP("DP core already initialized\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dp->usbpd->orientation == ORIENTATION_CC2) | ||||
| 		flip = true; | ||||
| 
 | ||||
| 	dp_power_init(dp->power, flip); | ||||
| 	dp_ctrl_host_init(dp->ctrl, flip); | ||||
| 	dp_aux_init(dp->aux); | ||||
| 	dp->core_initialized = true; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_host_deinit(struct dp_display_private *dp) | ||||
| { | ||||
| 	if (!dp->core_initialized) { | ||||
| 		DRM_DEBUG_DP("DP core already off\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_ctrl_host_deinit(dp->ctrl); | ||||
| 	dp_aux_deinit(dp->aux); | ||||
| 	dp_power_deinit(dp->power); | ||||
| 	disable_irq(dp->irq); | ||||
| 	dp->core_initialized = false; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_process_hpd_low(struct dp_display_private *dp) | ||||
| { | ||||
| 	dp_display_send_hpd_notification(dp, false); | ||||
| 
 | ||||
| 	dp_aux_deinit(dp->aux); | ||||
| } | ||||
| 
 | ||||
| static int dp_display_usbpd_configure_cb(struct device *dev) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	if (!dev) { | ||||
| 		DRM_ERROR("invalid dev\n"); | ||||
| 		rc = -EINVAL; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	dp = dev_get_drvdata(dev); | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("no driver data found\n"); | ||||
| 		rc = -ENODEV; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display_host_init(dp); | ||||
| 
 | ||||
| 	if (dp->usbpd->hpd_high) | ||||
| 		dp_display_process_hpd_high(dp); | ||||
| end: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_clean(struct dp_display_private *dp) | ||||
| { | ||||
| 	dp_ctrl_push_idle(dp->ctrl); | ||||
| 	dp_ctrl_off(dp->ctrl); | ||||
| } | ||||
| 
 | ||||
| static int dp_display_usbpd_disconnect_cb(struct device *dev) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	rc = dp_display_send_hpd_notification(dp, false); | ||||
| 
 | ||||
| 	/* if cable is disconnected, reset psm_enabled flag */ | ||||
| 	if (!dp->usbpd->alt_mode_cfg_done) | ||||
| 		dp->link->psm_enabled = false; | ||||
| 
 | ||||
| 	if ((rc < 0) && dp->power_on) | ||||
| 		dp_display_clean(dp); | ||||
| 
 | ||||
| 	dp_display_host_deinit(dp); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_handle_video_request(struct dp_display_private *dp) | ||||
| { | ||||
| 	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { | ||||
| 		/* force disconnect followed by connect */ | ||||
| 		dp->usbpd->connect(dp->usbpd, false); | ||||
| 		dp->panel->video_test = true; | ||||
| 		dp->usbpd->connect(dp->usbpd, true); | ||||
| 		dp_link_send_test_response(dp->link); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int dp_display_handle_hpd_irq(struct dp_display_private *dp) | ||||
| { | ||||
| 	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { | ||||
| 		dp_display_send_hpd_notification(dp, false); | ||||
| 
 | ||||
| 		if (dp_display_is_sink_count_zero(dp)) { | ||||
| 			DRM_DEBUG_DP("sink count is zero, nothing to do\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return dp_display_process_hpd_high(dp); | ||||
| 	} | ||||
| 
 | ||||
| 	dp_ctrl_handle_sink_request(dp->ctrl); | ||||
| 
 | ||||
| 	dp_display_handle_video_request(dp); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_usbpd_attention_cb(struct device *dev) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	if (!dev) { | ||||
| 		DRM_ERROR("invalid dev\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	dp = dev_get_drvdata(dev); | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("no driver data found\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dp->usbpd->hpd_irq) { | ||||
| 		dp->hpd_irq_on = true; | ||||
| 
 | ||||
| 		rc = dp_link_process_request(dp->link); | ||||
| 		/* check for any test request issued by sink */ | ||||
| 		if (!rc) | ||||
| 			dp_display_handle_hpd_irq(dp); | ||||
| 
 | ||||
| 		dp->hpd_irq_on = false; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!dp->usbpd->hpd_high) { | ||||
| 		dp_display_process_hpd_low(dp); | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dp->usbpd->alt_mode_cfg_done) | ||||
| 		dp_display_process_hpd_high(dp); | ||||
| end: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_deinit_sub_modules(struct dp_display_private *dp) | ||||
| { | ||||
| 	dp_ctrl_put(dp->ctrl); | ||||
| 	dp_panel_put(dp->panel); | ||||
| 	dp_aux_put(dp->aux); | ||||
| } | ||||
| 
 | ||||
| static int dp_init_sub_modules(struct dp_display_private *dp) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct device *dev = &dp->pdev->dev; | ||||
| 	struct dp_usbpd_cb *cb = &dp->usbpd_cb; | ||||
| 	struct dp_panel_in panel_in = { | ||||
| 		.dev = dev, | ||||
| 	}; | ||||
| 
 | ||||
| 	/* Callback APIs used for cable status change event */ | ||||
| 	cb->configure  = dp_display_usbpd_configure_cb; | ||||
| 	cb->disconnect = dp_display_usbpd_disconnect_cb; | ||||
| 	cb->attention  = dp_display_usbpd_attention_cb; | ||||
| 
 | ||||
| 	dp->usbpd = dp_hpd_get(dev, cb); | ||||
| 	if (IS_ERR(dp->usbpd)) { | ||||
| 		rc = PTR_ERR(dp->usbpd); | ||||
| 		DRM_ERROR("failed to initialize hpd, rc = %d\n", rc); | ||||
| 		dp->usbpd = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->parser = dp_parser_get(dp->pdev); | ||||
| 	if (IS_ERR(dp->parser)) { | ||||
| 		rc = PTR_ERR(dp->parser); | ||||
| 		DRM_ERROR("failed to initialize parser, rc = %d\n", rc); | ||||
| 		dp->parser = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->catalog = dp_catalog_get(dev, &dp->parser->io); | ||||
| 	if (IS_ERR(dp->catalog)) { | ||||
| 		rc = PTR_ERR(dp->catalog); | ||||
| 		DRM_ERROR("failed to initialize catalog, rc = %d\n", rc); | ||||
| 		dp->catalog = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->power = dp_power_get(dp->parser); | ||||
| 	if (IS_ERR(dp->power)) { | ||||
| 		rc = PTR_ERR(dp->power); | ||||
| 		DRM_ERROR("failed to initialize power, rc = %d\n", rc); | ||||
| 		dp->power = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->aux = dp_aux_get(dev, dp->catalog); | ||||
| 	if (IS_ERR(dp->aux)) { | ||||
| 		rc = PTR_ERR(dp->aux); | ||||
| 		DRM_ERROR("failed to initialize aux, rc = %d\n", rc); | ||||
| 		dp->aux = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->link = dp_link_get(dev, dp->aux); | ||||
| 	if (IS_ERR(dp->link)) { | ||||
| 		rc = PTR_ERR(dp->link); | ||||
| 		DRM_ERROR("failed to initialize link, rc = %d\n", rc); | ||||
| 		dp->link = NULL; | ||||
| 		goto error_link; | ||||
| 	} | ||||
| 
 | ||||
| 	panel_in.aux = dp->aux; | ||||
| 	panel_in.catalog = dp->catalog; | ||||
| 	panel_in.link = dp->link; | ||||
| 
 | ||||
| 	dp->panel = dp_panel_get(&panel_in); | ||||
| 	if (IS_ERR(dp->panel)) { | ||||
| 		rc = PTR_ERR(dp->panel); | ||||
| 		DRM_ERROR("failed to initialize panel, rc = %d\n", rc); | ||||
| 		dp->panel = NULL; | ||||
| 		goto error_link; | ||||
| 	} | ||||
| 
 | ||||
| 	dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, | ||||
| 			       dp->power, dp->catalog, dp->parser); | ||||
| 	if (IS_ERR(dp->ctrl)) { | ||||
| 		rc = PTR_ERR(dp->ctrl); | ||||
| 		DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc); | ||||
| 		dp->ctrl = NULL; | ||||
| 		goto error_ctrl; | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| error_ctrl: | ||||
| 	dp_panel_put(dp->panel); | ||||
| error_link: | ||||
| 	dp_aux_put(dp->aux); | ||||
| error: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_set_mode(struct msm_dp *dp_display, | ||||
| 			       struct dp_display_mode *mode) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	dp->panel->dp_mode.drm_mode = mode->drm_mode; | ||||
| 	dp->panel->dp_mode.bpp = mode->bpp; | ||||
| 	dp->panel->dp_mode.capabilities = mode->capabilities; | ||||
| 	dp_panel_init_panel_info(dp->panel); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_prepare(struct msm_dp *dp) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dp_display_dump(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	dp_panel_dump_regs(dp->panel); | ||||
| } | ||||
| 
 | ||||
| static int dp_display_enable(struct msm_dp *dp_display) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 	bool dump_dp = false; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	if (dp->power_on) { | ||||
| 		DRM_DEBUG_DP("Link already setup, return\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_ctrl_on(dp->ctrl); | ||||
| 	if (!rc) | ||||
| 		dp->power_on = true; | ||||
| 
 | ||||
| 	if (dump_dp != false) | ||||
| 		dp_display_dump(dp_display); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_post_enable(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	complete_all(&dp->notification_comp); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_pre_disable(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	if (dp->usbpd->alt_mode_cfg_done) | ||||
| 		dp_link_psm_config(dp->link, &dp->panel->link_info, true); | ||||
| 
 | ||||
| 	dp_ctrl_push_idle(dp->ctrl); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_disable(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	if (!dp->power_on || !dp->core_initialized) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	dp_ctrl_off(dp->ctrl); | ||||
| 
 | ||||
| 	dp->power_on = false; | ||||
| 
 | ||||
| 	complete_all(&dp->notification_comp); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dp_display_request_irq(struct msm_dp *dp_display) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	if (!dp_display) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	dp = container_of(dp_display, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0); | ||||
| 	if (dp->irq < 0) { | ||||
| 		rc = dp->irq; | ||||
| 		DRM_ERROR("failed to get irq: %d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq, | ||||
| 		IRQF_TRIGGER_HIGH, "dp_display_isr", dp); | ||||
| 	if (rc < 0) { | ||||
| 		DRM_ERROR("failed to request IRQ%u: %d\n", | ||||
| 				dp->irq, rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	disable_irq(dp->irq); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_unprepare(struct msm_dp *dp) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz) | ||||
| { | ||||
| 	const u32 num_components = 3, default_bpp = 24; | ||||
| 	struct dp_display_private *dp_display; | ||||
| 	struct dp_link_info *link_info; | ||||
| 	u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; | ||||
| 
 | ||||
| 	if (!dp || !mode_pclk_khz || !dp->connector) { | ||||
| 		DRM_ERROR("invalid params\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 	link_info = &dp_display->panel->link_info; | ||||
| 
 | ||||
| 	mode_bpp = dp->connector->display_info.bpc * num_components; | ||||
| 	if (!mode_bpp) | ||||
| 		mode_bpp = default_bpp; | ||||
| 
 | ||||
| 	mode_bpp = dp_panel_get_mode_bpp(dp_display->panel, | ||||
| 			mode_bpp, mode_pclk_khz); | ||||
| 
 | ||||
| 	mode_rate_khz = mode_pclk_khz * mode_bpp; | ||||
| 	supported_rate_khz = link_info->num_lanes * link_info->rate * 8; | ||||
| 
 | ||||
| 	if (mode_rate_khz > supported_rate_khz) | ||||
| 		return MODE_BAD; | ||||
| 
 | ||||
| 	return MODE_OK; | ||||
| } | ||||
| 
 | ||||
| int dp_display_get_modes(struct msm_dp *dp, | ||||
| 				struct dp_display_mode *dp_mode) | ||||
| { | ||||
| 	struct dp_display_private *dp_display; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("invalid params\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	ret = dp_panel_get_modes(dp_display->panel, | ||||
| 		dp->connector, dp_mode); | ||||
| 	if (dp_mode->drm_mode.clock) | ||||
| 		dp->max_pclk_khz = dp_mode->drm_mode.clock; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| bool dp_display_check_video_test(struct msm_dp *dp) | ||||
| { | ||||
| 	struct dp_display_private *dp_display; | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	return dp_display->panel->video_test; | ||||
| } | ||||
| 
 | ||||
| int dp_display_get_test_bpp(struct msm_dp *dp) | ||||
| { | ||||
| 	struct dp_display_private *dp_display; | ||||
| 
 | ||||
| 	if (!dp) { | ||||
| 		DRM_ERROR("invalid params\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	return dp_link_bit_depth_to_bpp( | ||||
| 		dp_display->link->test_video.test_bit_depth); | ||||
| } | ||||
| 
 | ||||
| static int dp_display_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	if (!pdev || !pdev->dev.of_node) { | ||||
| 		DRM_ERROR("pdev not found\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL); | ||||
| 	if (!dp) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	init_completion(&dp->notification_comp); | ||||
| 
 | ||||
| 	dp->pdev = pdev; | ||||
| 	dp->name = "drm_dp"; | ||||
| 
 | ||||
| 	rc = dp_init_sub_modules(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("init sub module failed\n"); | ||||
| 		return -EPROBE_DEFER; | ||||
| 	} | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, dp); | ||||
| 
 | ||||
| 	mutex_init(&dp->dp_display.connect_mutex); | ||||
| 	g_dp_display = &dp->dp_display; | ||||
| 
 | ||||
| 	rc = component_add(&pdev->dev, &dp_display_comp_ops); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("component add failed, rc=%d\n", rc); | ||||
| 		dp_display_deinit_sub_modules(dp); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int dp_display_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct dp_display_private *dp; | ||||
| 
 | ||||
| 	dp = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	dp_display_deinit_sub_modules(dp); | ||||
| 
 | ||||
| 	component_del(&pdev->dev, &dp_display_comp_ops); | ||||
| 	platform_set_drvdata(pdev, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_pm_resume(struct device *dev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_pm_suspend(struct device *dev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_pm_prepare(struct device *dev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dp_pm_complete(struct device *dev) | ||||
| { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops dp_pm_ops = { | ||||
| 	.suspend = dp_pm_suspend, | ||||
| 	.resume =  dp_pm_resume, | ||||
| 	.prepare = dp_pm_prepare, | ||||
| 	.complete = dp_pm_complete, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver dp_display_driver = { | ||||
| 	.probe  = dp_display_probe, | ||||
| 	.remove = dp_display_remove, | ||||
| 	.driver = { | ||||
| 		.name = "msm-dp-display", | ||||
| 		.of_match_table = dp_dt_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 		.pm = &dp_pm_ops, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init msm_dp_register(void) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = platform_driver_register(&dp_display_driver); | ||||
| 	if (ret) | ||||
| 		DRM_ERROR("Dp display driver register failed"); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void __exit msm_dp_unregister(void) | ||||
| { | ||||
| 	platform_driver_unregister(&dp_display_driver); | ||||
| } | ||||
| 
 | ||||
| int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, | ||||
| 			struct drm_encoder *encoder) | ||||
| { | ||||
| 	struct msm_drm_private *priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (WARN_ON(!encoder) || WARN_ON(!dp_display) || WARN_ON(!dev)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	priv = dev->dev_private; | ||||
| 	dp_display->drm_dev = dev; | ||||
| 
 | ||||
| 	ret = dp_display_request_irq(dp_display); | ||||
| 	if (ret) { | ||||
| 		DRM_ERROR("request_irq failed, ret=%d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	dp_display->encoder = encoder; | ||||
| 
 | ||||
| 	dp_display->connector = dp_drm_connector_init(dp_display); | ||||
| 	if (IS_ERR(dp_display->connector)) { | ||||
| 		ret = PTR_ERR(dp_display->connector); | ||||
| 		DRM_DEV_ERROR(dev->dev, | ||||
| 			"failed to create dp connector: %d\n", ret); | ||||
| 		dp_display->connector = NULL; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	priv->connectors[priv->num_connectors++] = dp_display->connector; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_display_private *dp_display; | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 	if (!dp_display->dp_mode.drm_mode.clock) { | ||||
| 		DRM_ERROR("invalid params\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_set_mode(dp, &dp_display->dp_mode); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_prepare(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DP display prepare failed, rc=%d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_enable(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DP display enable failed, rc=%d\n", rc); | ||||
| 		dp_display_unprepare(dp); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_post_enable(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DP display post enable failed, rc=%d\n", rc); | ||||
| 		dp_display_disable(dp); | ||||
| 		dp_display_unprepare(dp); | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 
 | ||||
| 	rc = dp_display_pre_disable(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DP display pre disable failed, rc=%d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_disable(dp); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("DP display disable failed, rc=%d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_display_unprepare(dp); | ||||
| 	if (rc) | ||||
| 		DRM_ERROR("DP display unprepare failed, rc=%d\n", rc); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder, | ||||
| 				struct drm_display_mode *mode, | ||||
| 				struct drm_display_mode *adjusted_mode) | ||||
| { | ||||
| 	struct dp_display_private *dp_display; | ||||
| 
 | ||||
| 	dp_display = container_of(dp, struct dp_display_private, dp_display); | ||||
| 
 | ||||
| 	memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode)); | ||||
| 
 | ||||
| 	if (dp_display_check_video_test(dp)) | ||||
| 		dp_display->dp_mode.bpp = dp_display_get_test_bpp(dp); | ||||
| 	else /* Default num_components per pixel = 3 */ | ||||
| 		dp_display->dp_mode.bpp = dp->connector->display_info.bpc * 3; | ||||
| 
 | ||||
| 	if (!dp_display->dp_mode.bpp) | ||||
| 		dp_display->dp_mode.bpp = 24; /* Default bpp */ | ||||
| 
 | ||||
| 	drm_mode_copy(&dp_display->dp_mode.drm_mode, adjusted_mode); | ||||
| 
 | ||||
| 	dp_display->dp_mode.v_active_low = | ||||
| 		!!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC); | ||||
| 
 | ||||
| 	dp_display->dp_mode.h_active_low = | ||||
| 		!!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC); | ||||
| } | ||||
							
								
								
									
										28
									
								
								drivers/gpu/drm/msm/dp/dp_display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								drivers/gpu/drm/msm/dp/dp_display.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_DISPLAY_H_ | ||||
| #define _DP_DISPLAY_H_ | ||||
| 
 | ||||
| #include "dp_panel.h" | ||||
| 
 | ||||
| struct msm_dp { | ||||
| 	struct drm_device *drm_dev; | ||||
| 	struct drm_connector *connector; | ||||
| 	struct drm_encoder *encoder; | ||||
| 	bool is_connected; | ||||
| 	struct mutex connect_mutex; | ||||
| 	u32 max_pclk_khz; | ||||
| 	u32 max_dp_lanes; | ||||
| }; | ||||
| 
 | ||||
| int dp_display_validate_mode(struct msm_dp *dp_display, u32 mode_pclk_khz); | ||||
| int dp_display_get_modes(struct msm_dp *dp_display, | ||||
| 		struct dp_display_mode *dp_mode); | ||||
| int dp_display_request_irq(struct msm_dp *dp_display); | ||||
| bool dp_display_check_video_test(struct msm_dp *dp_display); | ||||
| int dp_display_get_test_bpp(struct msm_dp *dp_display); | ||||
| 
 | ||||
| #endif /* _DP_DISPLAY_H_ */ | ||||
							
								
								
									
										168
									
								
								drivers/gpu/drm/msm/dp/dp_drm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								drivers/gpu/drm/msm/dp/dp_drm.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,168 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <drm/drm_atomic_helper.h> | ||||
| #include <drm/drm_atomic.h> | ||||
| #include <drm/drm_crtc.h> | ||||
| 
 | ||||
| #include "msm_drv.h" | ||||
| #include "msm_kms.h" | ||||
| #include "dp_drm.h" | ||||
| 
 | ||||
| struct dp_connector { | ||||
| 	struct drm_connector base; | ||||
| 	struct msm_dp *dp_display; | ||||
| }; | ||||
| #define to_dp_connector(x) container_of(x, struct dp_connector, base) | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_connector_detect - callback to determine if connector is connected | ||||
|  * @conn: Pointer to drm connector structure | ||||
|  * @force: Force detect setting from drm framework | ||||
|  * Returns: Connector 'is connected' status | ||||
|  */ | ||||
| static enum drm_connector_status dp_connector_detect(struct drm_connector *conn, | ||||
| 		bool force) | ||||
| { | ||||
| 	struct msm_dp *dp; | ||||
| 
 | ||||
| 	dp = to_dp_connector(conn)->dp_display; | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("is_connected = %s\n", | ||||
| 		(dp->is_connected) ? "true" : "false"); | ||||
| 
 | ||||
| 	return (dp->is_connected) ? connector_status_connected : | ||||
| 					connector_status_disconnected; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add() | ||||
|  * @connector: Pointer to drm connector structure | ||||
|  * Returns: Number of modes added | ||||
|  */ | ||||
| static int dp_connector_get_modes(struct drm_connector *connector) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct msm_dp *dp; | ||||
| 	struct dp_display_mode *dp_mode = NULL; | ||||
| 	struct drm_display_mode *m, drm_mode; | ||||
| 
 | ||||
| 	if (!connector) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	dp = to_dp_connector(connector)->dp_display; | ||||
| 
 | ||||
| 	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL); | ||||
| 	if (!dp_mode) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	mutex_lock(&dp->connect_mutex); | ||||
| 	/* pluggable case assumes EDID is read when HPD */ | ||||
| 	if (dp->is_connected) { | ||||
| 		/*
 | ||||
| 		 *The get_modes() function might return one mode that is stored | ||||
| 		 * in dp_mode when compliance test is in progress. If not, the | ||||
| 		 * return value is equal to the total number of modes supported | ||||
| 		 * by the sink | ||||
| 		 */ | ||||
| 		rc = dp_display_get_modes(dp, dp_mode); | ||||
| 		if (rc <= 0) { | ||||
| 			DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc); | ||||
| 			kfree(dp_mode); | ||||
| 			mutex_unlock(&dp->connect_mutex); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		if (dp_mode->drm_mode.clock) { /* valid DP mode */ | ||||
| 			memset(&drm_mode, 0x0, sizeof(drm_mode)); | ||||
| 			drm_mode_copy(&drm_mode, &dp_mode->drm_mode); | ||||
| 			m = drm_mode_duplicate(connector->dev, &drm_mode); | ||||
| 			if (!m) { | ||||
| 				DRM_ERROR("failed to add mode %ux%u\n", | ||||
| 				       drm_mode.hdisplay, | ||||
| 				       drm_mode.vdisplay); | ||||
| 				kfree(dp_mode); | ||||
| 				mutex_unlock(&dp->connect_mutex); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			drm_mode_probed_add(connector, m); | ||||
| 		} | ||||
| 	} else { | ||||
| 		DRM_DEBUG_DP("No sink connected\n"); | ||||
| 	} | ||||
| 	mutex_unlock(&dp->connect_mutex); | ||||
| 	kfree(dp_mode); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_connector_mode_valid - callback to determine if specified mode is valid | ||||
|  * @connector: Pointer to drm connector structure | ||||
|  * @mode: Pointer to drm mode structure | ||||
|  * Returns: Validity status for specified mode | ||||
|  */ | ||||
| static enum drm_mode_status dp_connector_mode_valid( | ||||
| 		struct drm_connector *connector, | ||||
| 		struct drm_display_mode *mode) | ||||
| { | ||||
| 	struct msm_dp *dp_disp; | ||||
| 
 | ||||
| 	dp_disp = to_dp_connector(connector)->dp_display; | ||||
| 
 | ||||
| 	if ((dp_disp->max_pclk_khz <= 0) || | ||||
| 			(dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) || | ||||
| 			(mode->clock > dp_disp->max_pclk_khz)) | ||||
| 		return MODE_BAD; | ||||
| 
 | ||||
| 	return dp_display_validate_mode(dp_disp, mode->clock); | ||||
| } | ||||
| 
 | ||||
| static const struct drm_connector_funcs dp_connector_funcs = { | ||||
| 	.detect = dp_connector_detect, | ||||
| 	.fill_modes = drm_helper_probe_single_connector_modes, | ||||
| 	.destroy = drm_connector_cleanup, | ||||
| 	.reset = drm_atomic_helper_connector_reset, | ||||
| 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||||
| 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||||
| }; | ||||
| 
 | ||||
| static const struct drm_connector_helper_funcs dp_connector_helper_funcs = { | ||||
| 	.get_modes = dp_connector_get_modes, | ||||
| 	.mode_valid = dp_connector_mode_valid, | ||||
| }; | ||||
| 
 | ||||
| /* connector initialization */ | ||||
| struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display) | ||||
| { | ||||
| 	struct drm_connector *connector = NULL; | ||||
| 	struct dp_connector *dp_connector; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	dp_connector = devm_kzalloc(dp_display->drm_dev->dev, | ||||
| 					sizeof(*dp_connector), | ||||
| 					GFP_KERNEL); | ||||
| 	if (!dp_connector) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	dp_connector->dp_display = dp_display; | ||||
| 
 | ||||
| 	connector = &dp_connector->base; | ||||
| 
 | ||||
| 	ret = drm_connector_init(dp_display->drm_dev, connector, | ||||
| 			&dp_connector_funcs, | ||||
| 			DRM_MODE_CONNECTOR_DisplayPort); | ||||
| 	if (ret) | ||||
| 		return ERR_PTR(ret); | ||||
| 
 | ||||
| 	drm_connector_helper_add(connector, &dp_connector_helper_funcs); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable HPD to let hpd event is handled when cable is connected. | ||||
| 	 */ | ||||
| 	connector->polled = DRM_CONNECTOR_POLL_HPD; | ||||
| 
 | ||||
| 	drm_connector_attach_encoder(connector, dp_display->encoder); | ||||
| 
 | ||||
| 	return connector; | ||||
| } | ||||
							
								
								
									
										18
									
								
								drivers/gpu/drm/msm/dp/dp_drm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								drivers/gpu/drm/msm/dp/dp_drm.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_DRM_H_ | ||||
| #define _DP_DRM_H_ | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <drm/drm_crtc.h> | ||||
| #include <drm/drm_crtc_helper.h> | ||||
| 
 | ||||
| #include "msm_drv.h" | ||||
| #include "dp_display.h" | ||||
| 
 | ||||
| struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display); | ||||
| 
 | ||||
| #endif /* _DP_DRM_H_ */ | ||||
							
								
								
									
										69
									
								
								drivers/gpu/drm/msm/dp/dp_hpd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								drivers/gpu/drm/msm/dp/dp_hpd.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__ | ||||
| 
 | ||||
| #include <linux/slab.h> | ||||
| #include <linux/device.h> | ||||
| 
 | ||||
| #include "dp_hpd.h" | ||||
| 
 | ||||
| /* DP specific VDM commands */ | ||||
| #define DP_USBPD_VDM_STATUS	0x10 | ||||
| #define DP_USBPD_VDM_CONFIGURE	0x11 | ||||
| 
 | ||||
| /* USBPD-TypeC specific Macros */ | ||||
| #define VDM_VERSION		0x0 | ||||
| #define USB_C_DP_SID		0xFF01 | ||||
| 
 | ||||
| struct dp_hpd_private { | ||||
| 	struct device *dev; | ||||
| 	struct dp_usbpd_cb *dp_cb; | ||||
| 	struct dp_usbpd dp_usbpd; | ||||
| }; | ||||
| 
 | ||||
| static int dp_hpd_connect(struct dp_usbpd *dp_usbpd, bool hpd) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_hpd_private *hpd_priv; | ||||
| 
 | ||||
| 	hpd_priv = container_of(dp_usbpd, struct dp_hpd_private, | ||||
| 					dp_usbpd); | ||||
| 
 | ||||
| 	dp_usbpd->hpd_high = hpd; | ||||
| 
 | ||||
| 	if (!hpd_priv->dp_cb && !hpd_priv->dp_cb->configure | ||||
| 				&& !hpd_priv->dp_cb->disconnect) { | ||||
| 		pr_err("hpd dp_cb not initialized\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (hpd) | ||||
| 		hpd_priv->dp_cb->configure(hpd_priv->dev); | ||||
| 	else | ||||
| 		hpd_priv->dp_cb->disconnect(hpd_priv->dev); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb) | ||||
| { | ||||
| 	struct dp_hpd_private *dp_hpd; | ||||
| 
 | ||||
| 	if (!cb) { | ||||
| 		pr_err("invalid cb data\n"); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	dp_hpd = devm_kzalloc(dev, sizeof(*dp_hpd), GFP_KERNEL); | ||||
| 	if (!dp_hpd) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	dp_hpd->dev = dev; | ||||
| 	dp_hpd->dp_cb = cb; | ||||
| 
 | ||||
| 	dp_hpd->dp_usbpd.connect = dp_hpd_connect; | ||||
| 
 | ||||
| 	return &dp_hpd->dp_usbpd; | ||||
| } | ||||
							
								
								
									
										79
									
								
								drivers/gpu/drm/msm/dp/dp_hpd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								drivers/gpu/drm/msm/dp/dp_hpd.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_HPD_H_ | ||||
| #define _DP_HPD_H_ | ||||
| 
 | ||||
| //#include <linux/usb/usbpd.h>
 | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/device.h> | ||||
| 
 | ||||
| enum plug_orientation { | ||||
| 	ORIENTATION_NONE, | ||||
| 	ORIENTATION_CC1, | ||||
| 	ORIENTATION_CC2, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_usbpd - DisplayPort status | ||||
|  * | ||||
|  * @orientation: plug orientation configuration | ||||
|  * @low_pow_st: low power state | ||||
|  * @adaptor_dp_en: adaptor functionality enabled | ||||
|  * @multi_func: multi-function preferred | ||||
|  * @usb_config_req: request to switch to usb | ||||
|  * @exit_dp_mode: request exit from displayport mode | ||||
|  * @hpd_high: Hot Plug Detect signal is high. | ||||
|  * @hpd_irq: Change in the status since last message | ||||
|  * @alt_mode_cfg_done: bool to specify alt mode status | ||||
|  * @debug_en: bool to specify debug mode | ||||
|  * @connect: simulate disconnect or connect for debug mode | ||||
|  */ | ||||
| struct dp_usbpd { | ||||
| 	enum plug_orientation orientation; | ||||
| 	bool low_pow_st; | ||||
| 	bool adaptor_dp_en; | ||||
| 	bool multi_func; | ||||
| 	bool usb_config_req; | ||||
| 	bool exit_dp_mode; | ||||
| 	bool hpd_high; | ||||
| 	bool hpd_irq; | ||||
| 	bool alt_mode_cfg_done; | ||||
| 	bool debug_en; | ||||
| 
 | ||||
| 	int (*connect)(struct dp_usbpd *dp_usbpd, bool hpd); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_usbpd_cb - callback functions provided by the client | ||||
|  * | ||||
|  * @configure: called by usbpd module when PD communication has | ||||
|  * been completed and the usb peripheral has been configured on | ||||
|  * dp mode. | ||||
|  * @disconnect: notify the cable disconnect issued by usb. | ||||
|  * @attention: notify any attention message issued by usb. | ||||
|  */ | ||||
| struct dp_usbpd_cb { | ||||
| 	int (*configure)(struct device *dev); | ||||
| 	int (*disconnect)(struct device *dev); | ||||
| 	int (*attention)(struct device *dev); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_hpd_get() - setup hpd module | ||||
|  * | ||||
|  * @dev: device instance of the caller | ||||
|  * @cb: struct containing callback function pointers. | ||||
|  * | ||||
|  * This function allows the client to initialize the usbpd | ||||
|  * module. The module will communicate with HPD module. | ||||
|  */ | ||||
| struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb); | ||||
| 
 | ||||
| int dp_hpd_register(struct dp_usbpd *dp_usbpd); | ||||
| void dp_hpd_unregister(struct dp_usbpd *dp_usbpd); | ||||
| 
 | ||||
| #endif /* _DP_HPD_H_ */ | ||||
							
								
								
									
										1214
									
								
								drivers/gpu/drm/msm/dp/dp_link.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1214
									
								
								drivers/gpu/drm/msm/dp/dp_link.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										132
									
								
								drivers/gpu/drm/msm/dp/dp_link.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								drivers/gpu/drm/msm/dp/dp_link.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_LINK_H_ | ||||
| #define _DP_LINK_H_ | ||||
| 
 | ||||
| #include "dp_aux.h" | ||||
| 
 | ||||
| #define DS_PORT_STATUS_CHANGED 0x200 | ||||
| #define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF | ||||
| #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) | ||||
| 
 | ||||
| struct dp_link_info { | ||||
| 	unsigned char revision; | ||||
| 	unsigned int rate; | ||||
| 	unsigned int num_lanes; | ||||
| 	unsigned long capabilities; | ||||
| }; | ||||
| 
 | ||||
| enum dp_link_voltage_level { | ||||
| 	DP_TRAIN_VOLTAGE_SWING_LVL_0	= 0, | ||||
| 	DP_TRAIN_VOLTAGE_SWING_LVL_1	= 1, | ||||
| 	DP_TRAIN_VOLTAGE_SWING_LVL_2	= 2, | ||||
| 	DP_TRAIN_VOLTAGE_SWING_MAX	= DP_TRAIN_VOLTAGE_SWING_LVL_2, | ||||
| }; | ||||
| 
 | ||||
| enum dp_link_preemaphasis_level { | ||||
| 	DP_TRAIN_PRE_EMPHASIS_LVL_0	= 0, | ||||
| 	DP_TRAIN_PRE_EMPHASIS_LVL_1	= 1, | ||||
| 	DP_TRAIN_PRE_EMPHASIS_LVL_2	= 2, | ||||
| 	DP_TRAIN_PRE_EMPHASIS_MAX	= DP_TRAIN_PRE_EMPHASIS_LVL_2, | ||||
| }; | ||||
| 
 | ||||
| struct dp_link_test_video { | ||||
| 	u32 test_video_pattern; | ||||
| 	u32 test_bit_depth; | ||||
| 	u32 test_dyn_range; | ||||
| 	u32 test_h_total; | ||||
| 	u32 test_v_total; | ||||
| 	u32 test_h_start; | ||||
| 	u32 test_v_start; | ||||
| 	u32 test_hsync_pol; | ||||
| 	u32 test_hsync_width; | ||||
| 	u32 test_vsync_pol; | ||||
| 	u32 test_vsync_width; | ||||
| 	u32 test_h_width; | ||||
| 	u32 test_v_height; | ||||
| 	u32 test_rr_d; | ||||
| 	u32 test_rr_n; | ||||
| }; | ||||
| 
 | ||||
| struct dp_link_test_audio { | ||||
| 	u32 test_audio_sampling_rate; | ||||
| 	u32 test_audio_channel_count; | ||||
| 	u32 test_audio_pattern_type; | ||||
| 	u32 test_audio_period_ch_1; | ||||
| 	u32 test_audio_period_ch_2; | ||||
| 	u32 test_audio_period_ch_3; | ||||
| 	u32 test_audio_period_ch_4; | ||||
| 	u32 test_audio_period_ch_5; | ||||
| 	u32 test_audio_period_ch_6; | ||||
| 	u32 test_audio_period_ch_7; | ||||
| 	u32 test_audio_period_ch_8; | ||||
| }; | ||||
| 
 | ||||
| struct dp_link_phy_params { | ||||
| 	u32 phy_test_pattern_sel; | ||||
| 	u8 v_level; | ||||
| 	u8 p_level; | ||||
| }; | ||||
| 
 | ||||
| struct dp_link { | ||||
| 	u32 sink_request; | ||||
| 	u32 test_response; | ||||
| 	bool psm_enabled; | ||||
| 
 | ||||
| 	u8 sink_count; | ||||
| 	struct dp_link_test_video test_video; | ||||
| 	struct dp_link_test_audio test_audio; | ||||
| 	struct dp_link_phy_params phy_params; | ||||
| 	struct dp_link_info link_params; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp | ||||
|  * @tbd: test bit depth | ||||
|  * | ||||
|  * Returns the bits per pixel (bpp) to be used corresponding to the | ||||
|  * git bit depth value. This function assumes that bit depth has | ||||
|  * already been validated. | ||||
|  */ | ||||
| static inline u32 dp_link_bit_depth_to_bpp(u32 tbd) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Few simplistic rules and assumptions made here: | ||||
| 	 *    1. Bit depth is per color component | ||||
| 	 *    2. If bit depth is unknown return 0 | ||||
| 	 *    3. Assume 3 color components | ||||
| 	 */ | ||||
| 	switch (tbd) { | ||||
| 	case DP_TEST_BIT_DEPTH_6: | ||||
| 		return 18; | ||||
| 	case DP_TEST_BIT_DEPTH_8: | ||||
| 		return 24; | ||||
| 	case DP_TEST_BIT_DEPTH_10: | ||||
| 		return 30; | ||||
| 	case DP_TEST_BIT_DEPTH_UNKNOWN: | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp); | ||||
| int dp_link_process_request(struct dp_link *dp_link); | ||||
| int dp_link_get_colorimetry_config(struct dp_link *dp_link); | ||||
| int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status); | ||||
| bool dp_link_send_test_response(struct dp_link *dp_link); | ||||
| int dp_link_psm_config(struct dp_link *dp_link, | ||||
| 		struct dp_link_info *link_info, bool enable); | ||||
| bool dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum); | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_link_get() - get the functionalities of dp test module | ||||
|  * | ||||
|  * | ||||
|  * return: a pointer to dp_link struct | ||||
|  */ | ||||
| struct dp_link *dp_link_get(struct device *dev, struct drm_dp_aux *aux); | ||||
| 
 | ||||
| #endif /* _DP_LINK_H_ */ | ||||
							
								
								
									
										486
									
								
								drivers/gpu/drm/msm/dp/dp_panel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								drivers/gpu/drm/msm/dp/dp_panel.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,486 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include "dp_panel.h" | ||||
| 
 | ||||
| #include <drm/drm_connector.h> | ||||
| #include <drm/drm_edid.h> | ||||
| 
 | ||||
| #define DP_MAX_DS_PORT_COUNT 1 | ||||
| 
 | ||||
| struct dp_panel_private { | ||||
| 	struct device *dev; | ||||
| 	struct dp_panel dp_panel; | ||||
| 	struct drm_dp_aux *aux; | ||||
| 	struct dp_link *link; | ||||
| 	struct dp_catalog *catalog; | ||||
| 	bool panel_on; | ||||
| 	bool aux_cfg_update_done; | ||||
| }; | ||||
| 
 | ||||
| static int dp_panel_read_dpcd(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	size_t rlen; | ||||
| 	struct dp_panel_private *panel; | ||||
| 	struct dp_link_info *link_info; | ||||
| 	u8 *dpcd, major = 0, minor = 0, temp; | ||||
| 	u32 dfp_count = 0, offset = DP_DPCD_REV; | ||||
| 
 | ||||
| 	dpcd = dp_panel->dpcd; | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 	link_info = &dp_panel->link_info; | ||||
| 
 | ||||
| 	rlen = drm_dp_dpcd_read(panel->aux, | ||||
| 				DP_TRAINING_AUX_RD_INTERVAL, &temp, 1); | ||||
| 	if (rlen < 0) { | ||||
| 		DRM_ERROR("err reading DP_TRAINING_AUX_RD_INTERVAL,rlen=%zd\n", | ||||
| 				rlen); | ||||
| 		rc = -EINVAL; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	/* check for EXTENDED_RECEIVER_CAPABILITY_FIELD_PRESENT */ | ||||
| 	if (temp & BIT(7)) { | ||||
| 		DRM_DEBUG_DP("using EXTENDED_RECEIVER_CAPABILITY_FIELD\n"); | ||||
| 		offset = DPRX_EXTENDED_DPCD_FIELD; | ||||
| 	} | ||||
| 
 | ||||
| 	rlen = drm_dp_dpcd_read(panel->aux, offset, | ||||
| 		dpcd, (DP_RECEIVER_CAP_SIZE + 1)); | ||||
| 	if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) { | ||||
| 		DRM_ERROR("dpcd read failed, rlen=%zd\n", rlen); | ||||
| 		if (rlen == -ETIMEDOUT) | ||||
| 			rc = rlen; | ||||
| 		else | ||||
| 			rc = -EINVAL; | ||||
| 
 | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ", | ||||
| 		DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false); | ||||
| 
 | ||||
| 	link_info->revision = dpcd[DP_DPCD_REV]; | ||||
| 	major = (link_info->revision >> 4) & 0x0f; | ||||
| 	minor = link_info->revision & 0x0f; | ||||
| 
 | ||||
| 	link_info->rate = drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); | ||||
| 	link_info->num_lanes = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; | ||||
| 
 | ||||
| 	if (link_info->num_lanes > dp_panel->max_dp_lanes) | ||||
| 		link_info->num_lanes = dp_panel->max_dp_lanes; | ||||
| 
 | ||||
| 	/* Limit support upto HBR2 until HBR3 support is added */ | ||||
| 	if (link_info->rate >= (drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4))) | ||||
| 		link_info->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4); | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("version: %d.%d\n", major, minor); | ||||
| 	DRM_DEBUG_DP("link_rate=%d\n", link_info->rate); | ||||
| 	DRM_DEBUG_DP("lane_count=%d\n", link_info->num_lanes); | ||||
| 
 | ||||
| 	if (drm_dp_enhanced_frame_cap(dpcd)) | ||||
| 		link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; | ||||
| 
 | ||||
| 	dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & | ||||
| 						DP_DOWN_STREAM_PORT_COUNT; | ||||
| 
 | ||||
| 	if (dfp_count > DP_MAX_DS_PORT_COUNT) { | ||||
| 		DRM_ERROR("DS port count %d greater that max (%d) supported\n", | ||||
| 			dfp_count, DP_MAX_DS_PORT_COUNT); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| end: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel, | ||||
| 		u32 mode_edid_bpp, u32 mode_pclk_khz) | ||||
| { | ||||
| 	struct dp_link_info *link_info; | ||||
| 	const u32 max_supported_bpp = 30, min_supported_bpp = 18; | ||||
| 	u32 bpp = 0, data_rate_khz = 0; | ||||
| 
 | ||||
| 	bpp = min_t(u32, mode_edid_bpp, max_supported_bpp); | ||||
| 
 | ||||
| 	link_info = &dp_panel->link_info; | ||||
| 	data_rate_khz = link_info->num_lanes * link_info->rate * 8; | ||||
| 
 | ||||
| 	while (bpp > min_supported_bpp) { | ||||
| 		if (mode_pclk_khz * bpp <= data_rate_khz) | ||||
| 			break; | ||||
| 		bpp -= 6; | ||||
| 	} | ||||
| 
 | ||||
| 	return bpp; | ||||
| } | ||||
| 
 | ||||
| static void dp_panel_set_test_mode(struct dp_panel_private *panel, | ||||
| 		struct dp_display_mode *mode) | ||||
| { | ||||
| 	struct drm_display_mode *drm_mode = NULL; | ||||
| 	struct dp_link_test_video *test_info = NULL; | ||||
| 
 | ||||
| 	drm_mode = &mode->drm_mode; | ||||
| 	test_info = &panel->link->test_video; | ||||
| 
 | ||||
| 	drm_mode->hdisplay = test_info->test_h_width; | ||||
| 	drm_mode->hsync_start = drm_mode->hdisplay + test_info->test_h_total - | ||||
| 			(test_info->test_h_start + test_info->test_h_width); | ||||
| 	drm_mode->hsync_end = drm_mode->hsync_start + | ||||
| 				test_info->test_hsync_width; | ||||
| 	drm_mode->htotal = drm_mode->hsync_end + test_info->test_h_start - | ||||
| 						test_info->test_hsync_width; | ||||
| 
 | ||||
| 	drm_mode->vdisplay = test_info->test_v_height; | ||||
| 	drm_mode->vsync_start = drm_mode->vdisplay + test_info->test_v_total - | ||||
| 			(test_info->test_v_start + test_info->test_v_height); | ||||
| 	drm_mode->vsync_end = drm_mode->vsync_start + | ||||
| 				test_info->test_vsync_width; | ||||
| 	drm_mode->vtotal = drm_mode->vsync_end + test_info->test_v_start - | ||||
| 						test_info->test_vsync_width; | ||||
| 
 | ||||
| 	drm_mode->clock = test_info->test_h_total * | ||||
| 		test_info->test_v_total * test_info->test_rr_n; | ||||
| 
 | ||||
| 	drm_mode->type = 0x48; | ||||
| 	drm_mode_set_name(drm_mode); | ||||
| 
 | ||||
| 	if (test_info->test_rr_d == 0) | ||||
| 		drm_mode->clock /= 1000; | ||||
| 	else | ||||
| 		drm_mode->clock /= 1001; | ||||
| 
 | ||||
| 	if (test_info->test_h_width == 640) | ||||
| 		drm_mode->clock = 25170; | ||||
| } | ||||
| 
 | ||||
| static int dp_panel_update_modes(struct drm_connector *connector, | ||||
| 	struct edid *edid) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 
 | ||||
| 	if (edid) { | ||||
| 		rc = drm_connector_update_edid_property(connector, edid); | ||||
| 		if (rc) { | ||||
| 			DRM_ERROR("failed to update edid property %d\n", rc); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		rc = drm_add_edid_modes(connector, edid); | ||||
| 		DRM_DEBUG_DP("%s -", __func__); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = drm_connector_update_edid_property(connector, NULL); | ||||
| 	if (rc) | ||||
| 		DRM_ERROR("failed to update edid property %d\n", rc); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int dp_panel_read_sink_caps(struct dp_panel *dp_panel, | ||||
| 	struct drm_connector *connector) | ||||
| { | ||||
| 	int rc = 0, bw_code; | ||||
| 	struct dp_panel_private *panel; | ||||
| 
 | ||||
| 	if (!dp_panel || !connector) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 
 | ||||
| 	rc = dp_panel_read_dpcd(dp_panel); | ||||
| 	bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate); | ||||
| 	if (rc || !is_link_rate_valid(bw_code) || | ||||
| 			!is_lane_count_valid(dp_panel->link_info.num_lanes) || | ||||
| 			(bw_code > dp_panel->max_bw_code)) { | ||||
| 		DRM_ERROR("read dpcd failed %d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	rc = drm_dp_read_desc(panel->aux, &dp_panel->desc, | ||||
| 			      drm_dp_is_branch(dp_panel->dpcd)); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("read sink/branch descriptor failed %d\n", rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(dp_panel->edid); | ||||
| 	dp_panel->edid = NULL; | ||||
| 
 | ||||
| 	dp_panel->edid = drm_get_edid(connector, | ||||
| 					      &panel->aux->ddc); | ||||
| 	if (!dp_panel->edid) { | ||||
| 		DRM_ERROR("panel edid read failed\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (panel->aux_cfg_update_done) { | ||||
| 		DRM_DEBUG_DP("read DPCD with updated AUX config\n"); | ||||
| 		rc = dp_panel_read_dpcd(dp_panel); | ||||
| 		bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate); | ||||
| 		if (rc || !is_link_rate_valid(bw_code) || | ||||
| 			!is_lane_count_valid(dp_panel->link_info.num_lanes) | ||||
| 			|| (bw_code > dp_panel->max_bw_code)) { | ||||
| 			DRM_ERROR("read dpcd failed %d\n", rc); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		panel->aux_cfg_update_done = false; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel, | ||||
| 		u32 mode_edid_bpp, u32 mode_pclk_khz) | ||||
| { | ||||
| 	struct dp_panel_private *panel; | ||||
| 	u32 bpp = mode_edid_bpp; | ||||
| 
 | ||||
| 	if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 
 | ||||
| 	if (dp_panel->video_test) | ||||
| 		bpp = dp_link_bit_depth_to_bpp( | ||||
| 				panel->link->test_video.test_bit_depth); | ||||
| 	else | ||||
| 		bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp, | ||||
| 				mode_pclk_khz); | ||||
| 
 | ||||
| 	return bpp; | ||||
| } | ||||
| 
 | ||||
| int dp_panel_get_modes(struct dp_panel *dp_panel, | ||||
| 	struct drm_connector *connector, struct dp_display_mode *mode) | ||||
| { | ||||
| 	struct dp_panel_private *panel; | ||||
| 
 | ||||
| 	if (!dp_panel) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 
 | ||||
| 	if (dp_panel->video_test) { | ||||
| 		dp_panel_set_test_mode(panel, mode); | ||||
| 		return 1; | ||||
| 	} else if (dp_panel->edid) { | ||||
| 		return dp_panel_update_modes(connector, dp_panel->edid); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static u8 dp_panel_get_edid_checksum(struct edid *edid) | ||||
| { | ||||
| 	struct edid *last_block; | ||||
| 	u8 *raw_edid; | ||||
| 	bool is_edid_corrupt; | ||||
| 
 | ||||
| 	if (!edid) { | ||||
| 		DRM_ERROR("invalid edid input\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	raw_edid = (u8 *)edid; | ||||
| 	raw_edid += (edid->extensions * EDID_LENGTH); | ||||
| 	last_block = (struct edid *)raw_edid; | ||||
| 
 | ||||
| 	/* block type extension */ | ||||
| 	drm_edid_block_valid(raw_edid, 1, false, &is_edid_corrupt); | ||||
| 	if (!is_edid_corrupt) | ||||
| 		return last_block->checksum; | ||||
| 
 | ||||
| 	DRM_ERROR("Invalid block, no checksum\n"); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dp_panel_handle_sink_request(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	struct dp_panel_private *panel; | ||||
| 
 | ||||
| 	if (!dp_panel) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 
 | ||||
| 	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) { | ||||
| 		u8 checksum = dp_panel_get_edid_checksum(dp_panel->edid); | ||||
| 
 | ||||
| 		dp_link_send_edid_checksum(panel->link, checksum); | ||||
| 		dp_link_send_test_response(panel->link); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable) | ||||
| { | ||||
| 	struct dp_catalog *catalog; | ||||
| 	struct dp_panel_private *panel; | ||||
| 
 | ||||
| 	if (!dp_panel) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 	catalog = panel->catalog; | ||||
| 
 | ||||
| 	if (!panel->panel_on) { | ||||
| 		DRM_DEBUG_DP("DP panel not enabled, handle TPG on next on\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!enable) { | ||||
| 		dp_catalog_panel_tpg_disable(catalog); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("%s: calling catalog tpg_enable\n", __func__); | ||||
| 	dp_catalog_panel_tpg_enable(catalog, &panel->dp_panel.dp_mode.drm_mode); | ||||
| } | ||||
| 
 | ||||
| void dp_panel_dump_regs(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	struct dp_catalog *catalog; | ||||
| 	struct dp_panel_private *panel; | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 	catalog = panel->catalog; | ||||
| 
 | ||||
| 	dp_catalog_dump_regs(catalog); | ||||
| } | ||||
| 
 | ||||
| int dp_panel_timing_cfg(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	u32 data, total_ver, total_hor; | ||||
| 	struct dp_catalog *catalog; | ||||
| 	struct dp_panel_private *panel; | ||||
| 	struct drm_display_mode *drm_mode; | ||||
| 
 | ||||
| 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel); | ||||
| 	catalog = panel->catalog; | ||||
| 	drm_mode = &panel->dp_panel.dp_mode.drm_mode; | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("width=%d hporch= %d %d %d\n", | ||||
| 		drm_mode->hdisplay, drm_mode->htotal - drm_mode->hsync_end, | ||||
| 		drm_mode->hsync_start - drm_mode->hdisplay, | ||||
| 		drm_mode->hsync_end - drm_mode->hsync_start); | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("height=%d vporch= %d %d %d\n", | ||||
| 		drm_mode->vdisplay, drm_mode->vtotal - drm_mode->vsync_end, | ||||
| 		drm_mode->vsync_start - drm_mode->vdisplay, | ||||
| 		drm_mode->vsync_end - drm_mode->vsync_start); | ||||
| 
 | ||||
| 	total_hor = drm_mode->htotal; | ||||
| 
 | ||||
| 	total_ver = drm_mode->vtotal; | ||||
| 
 | ||||
| 	data = total_ver; | ||||
| 	data <<= 16; | ||||
| 	data |= total_hor; | ||||
| 
 | ||||
| 	catalog->total = data; | ||||
| 
 | ||||
| 	data = (drm_mode->vtotal - drm_mode->vsync_start); | ||||
| 	data <<= 16; | ||||
| 	data |= (drm_mode->htotal - drm_mode->hsync_start); | ||||
| 
 | ||||
| 	catalog->sync_start = data; | ||||
| 
 | ||||
| 	data = drm_mode->vsync_end - drm_mode->vsync_start; | ||||
| 	data <<= 16; | ||||
| 	data |= (panel->dp_panel.dp_mode.v_active_low << 31); | ||||
| 	data |= drm_mode->hsync_end - drm_mode->hsync_start; | ||||
| 	data |= (panel->dp_panel.dp_mode.h_active_low << 15); | ||||
| 
 | ||||
| 	catalog->width_blanking = data; | ||||
| 
 | ||||
| 	data = drm_mode->vdisplay; | ||||
| 	data <<= 16; | ||||
| 	data |= drm_mode->hdisplay; | ||||
| 
 | ||||
| 	catalog->dp_active = data; | ||||
| 
 | ||||
| 	dp_catalog_panel_timing_cfg(catalog); | ||||
| 	panel->panel_on = true; | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int dp_panel_init_panel_info(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct drm_display_mode *drm_mode; | ||||
| 
 | ||||
| 	drm_mode = &dp_panel->dp_mode.drm_mode; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * print resolution info as this is a result | ||||
| 	 * of user initiated action of cable connection | ||||
| 	 */ | ||||
| 	DRM_DEBUG_DP("SET NEW RESOLUTION:\n"); | ||||
| 	DRM_DEBUG_DP("%dx%d@%dfps\n", drm_mode->hdisplay, | ||||
| 		drm_mode->vdisplay, drm_mode_vrefresh(drm_mode)); | ||||
| 	DRM_DEBUG_DP("h_porches(back|front|width) = (%d|%d|%d)\n", | ||||
| 			drm_mode->htotal - drm_mode->hsync_end, | ||||
| 			drm_mode->hsync_start - drm_mode->hdisplay, | ||||
| 			drm_mode->hsync_end - drm_mode->hsync_start); | ||||
| 	DRM_DEBUG_DP("v_porches(back|front|width) = (%d|%d|%d)\n", | ||||
| 			drm_mode->vtotal - drm_mode->vsync_end, | ||||
| 			drm_mode->vsync_start - drm_mode->vdisplay, | ||||
| 			drm_mode->vsync_end - drm_mode->vsync_start); | ||||
| 	DRM_DEBUG_DP("pixel clock (KHz)=(%d)\n", drm_mode->clock); | ||||
| 	DRM_DEBUG_DP("bpp = %d\n", dp_panel->dp_mode.bpp); | ||||
| 
 | ||||
| 	dp_panel->dp_mode.bpp = max_t(u32, 18, | ||||
| 					min_t(u32, dp_panel->dp_mode.bpp, 30)); | ||||
| 	DRM_DEBUG_DP("updated bpp = %d\n", dp_panel->dp_mode.bpp); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| struct dp_panel *dp_panel_get(struct dp_panel_in *in) | ||||
| { | ||||
| 	struct dp_panel_private *panel; | ||||
| 	struct dp_panel *dp_panel; | ||||
| 
 | ||||
| 	if (!in->dev || !in->catalog || !in->aux || !in->link) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	panel = devm_kzalloc(in->dev, sizeof(*panel), GFP_KERNEL); | ||||
| 	if (!panel) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	panel->dev = in->dev; | ||||
| 	panel->aux = in->aux; | ||||
| 	panel->catalog = in->catalog; | ||||
| 	panel->link = in->link; | ||||
| 
 | ||||
| 	dp_panel = &panel->dp_panel; | ||||
| 	dp_panel->max_bw_code = DP_LINK_BW_8_1; | ||||
| 	panel->aux_cfg_update_done = false; | ||||
| 
 | ||||
| 	return dp_panel; | ||||
| } | ||||
| 
 | ||||
| void dp_panel_put(struct dp_panel *dp_panel) | ||||
| { | ||||
| 	if (!dp_panel) | ||||
| 		return; | ||||
| 
 | ||||
| 	kfree(dp_panel->edid); | ||||
| } | ||||
							
								
								
									
										95
									
								
								drivers/gpu/drm/msm/dp/dp_panel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								drivers/gpu/drm/msm/dp/dp_panel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_PANEL_H_ | ||||
| #define _DP_PANEL_H_ | ||||
| 
 | ||||
| #include <drm/msm_drm.h> | ||||
| 
 | ||||
| #include "dp_aux.h" | ||||
| #include "dp_link.h" | ||||
| #include "dp_hpd.h" | ||||
| 
 | ||||
| struct edid; | ||||
| 
 | ||||
| #define DP_MAX_DOWNSTREAM_PORTS 0x10 | ||||
| #define DPRX_EXTENDED_DPCD_FIELD 0x2200 | ||||
| 
 | ||||
| struct dp_display_mode { | ||||
| 	struct drm_display_mode drm_mode; | ||||
| 	u32 capabilities; | ||||
| 	u32 bpp; | ||||
| 	u32 h_active_low; | ||||
| 	u32 v_active_low; | ||||
| }; | ||||
| 
 | ||||
| struct dp_panel_in { | ||||
| 	struct device *dev; | ||||
| 	struct drm_dp_aux *aux; | ||||
| 	struct dp_link *link; | ||||
| 	struct dp_catalog *catalog; | ||||
| }; | ||||
| 
 | ||||
| struct dp_panel { | ||||
| 	/* dpcd raw data */ | ||||
| 	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1]; | ||||
| 
 | ||||
| 	struct dp_link_info link_info; | ||||
| 	struct drm_dp_desc desc; | ||||
| 	struct edid *edid; | ||||
| 	struct drm_connector *connector; | ||||
| 	struct dp_display_mode dp_mode; | ||||
| 	bool video_test; | ||||
| 
 | ||||
| 	u32 vic; | ||||
| 	u32 max_pclk_khz; | ||||
| 	u32 max_dp_lanes; | ||||
| 
 | ||||
| 	u32 max_bw_code; | ||||
| }; | ||||
| 
 | ||||
| int dp_panel_init_panel_info(struct dp_panel *dp_panel); | ||||
| int dp_panel_deinit(struct dp_panel *dp_panel); | ||||
| int dp_panel_timing_cfg(struct dp_panel *dp_panel); | ||||
| void dp_panel_dump_regs(struct dp_panel *dp_panel); | ||||
| int dp_panel_read_sink_caps(struct dp_panel *dp_panel, | ||||
| 		struct drm_connector *connector); | ||||
| u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel, u32 mode_max_bpp, | ||||
| 			u32 mode_pclk_khz); | ||||
| int dp_panel_get_modes(struct dp_panel *dp_panel, | ||||
| 		struct drm_connector *connector, struct dp_display_mode *mode); | ||||
| void dp_panel_handle_sink_request(struct dp_panel *dp_panel); | ||||
| void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable); | ||||
| 
 | ||||
| /**
 | ||||
|  * is_link_rate_valid() - validates the link rate | ||||
|  * @lane_rate: link rate requested by the sink | ||||
|  * | ||||
|  * Returns true if the requested link rate is supported. | ||||
|  */ | ||||
| static inline bool is_link_rate_valid(u32 bw_code) | ||||
| { | ||||
| 	return (bw_code == DP_LINK_BW_1_62 || | ||||
| 		bw_code == DP_LINK_BW_2_7 || | ||||
| 		bw_code == DP_LINK_BW_5_4 || | ||||
| 		bw_code == DP_LINK_BW_8_1); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_link_is_lane_count_valid() - validates the lane count | ||||
|  * @lane_count: lane count requested by the sink | ||||
|  * | ||||
|  * Returns true if the requested lane count is supported. | ||||
|  */ | ||||
| static inline bool is_lane_count_valid(u32 lane_count) | ||||
| { | ||||
| 	return (lane_count == 1 || | ||||
| 		lane_count == 2 || | ||||
| 		lane_count == 4); | ||||
| } | ||||
| 
 | ||||
| struct dp_panel *dp_panel_get(struct dp_panel_in *in); | ||||
| void dp_panel_put(struct dp_panel *dp_panel); | ||||
| #endif /* _DP_PANEL_H_ */ | ||||
							
								
								
									
										265
									
								
								drivers/gpu/drm/msm/dp/dp_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								drivers/gpu/drm/msm/dp/dp_parser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,265 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/of_gpio.h> | ||||
| 
 | ||||
| #include "dp_parser.h" | ||||
| 
 | ||||
| static const struct dp_regulator_cfg sdm845_dp_reg_cfg = { | ||||
| 	.num = 2, | ||||
| 	.regs = { | ||||
| 		{"vdda-1p2", 21800, 4 },	/* 1.2 V */ | ||||
| 		{"vdda-0p9", 36000, 32 },	/* 0.9 V */ | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int msm_dss_ioremap(struct platform_device *pdev, | ||||
| 				struct dss_io_data *io_data) | ||||
| { | ||||
| 	struct resource *res = NULL; | ||||
| 
 | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!res) { | ||||
| 		DRM_ERROR("%pS->%s: msm_dss_get_res failed\n", | ||||
| 			__builtin_return_address(0), __func__); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	io_data->len = (u32)resource_size(res); | ||||
| 	io_data->base = ioremap(res->start, io_data->len); | ||||
| 	if (!io_data->base) { | ||||
| 		DRM_ERROR("%pS->%s: ioremap failed\n", | ||||
| 			__builtin_return_address(0), __func__); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void msm_dss_iounmap(struct dss_io_data *io_data) | ||||
| { | ||||
| 	if (io_data->base) { | ||||
| 		iounmap(io_data->base); | ||||
| 		io_data->base = NULL; | ||||
| 	} | ||||
| 	io_data->len = 0; | ||||
| } | ||||
| 
 | ||||
| static void dp_parser_unmap_io_resources(struct dp_parser *parser) | ||||
| { | ||||
| 	struct dp_io *io = &parser->io; | ||||
| 
 | ||||
| 	msm_dss_iounmap(&io->dp_controller); | ||||
| 	msm_dss_iounmap(&io->usb3_dp_com); | ||||
| } | ||||
| 
 | ||||
| static int dp_parser_ctrl_res(struct dp_parser *parser) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct platform_device *pdev = parser->pdev; | ||||
| 	struct dp_io *io = &parser->io; | ||||
| 
 | ||||
| 	rc = msm_dss_ioremap(pdev, &io->dp_controller); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("unable to remap dp io resources, rc=%d\n", rc); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	dp_parser_unmap_io_resources(parser); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int dp_parser_misc(struct dp_parser *parser) | ||||
| { | ||||
| 	struct device_node *of_node = parser->pdev->dev.of_node; | ||||
| 	int len = 0; | ||||
| 	const char *data_lane_property = "data-lanes"; | ||||
| 
 | ||||
| 	len = of_property_count_elems_of_size(of_node, | ||||
| 			 data_lane_property, sizeof(u32)); | ||||
| 	if (len < 0) { | ||||
| 		DRM_WARN("Invalid property %s, default max DP lanes = %d\n", | ||||
| 				data_lane_property, DP_MAX_NUM_DP_LANES); | ||||
| 		len = DP_MAX_NUM_DP_LANES; | ||||
| 	} | ||||
| 
 | ||||
| 	parser->max_dp_lanes = len; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline bool dp_parser_check_prefix(const char *clk_prefix, | ||||
| 						const char *clk_name) | ||||
| { | ||||
| 	return !strncmp(clk_prefix, clk_name, strlen(clk_prefix)); | ||||
| } | ||||
| 
 | ||||
| static int dp_parser_init_clk_data(struct dp_parser *parser) | ||||
| { | ||||
| 	int num_clk, i, rc; | ||||
| 	int core_clk_count = 0, ctrl_clk_count = 0; | ||||
| 	const char *clk_name; | ||||
| 	struct device *dev = &parser->pdev->dev; | ||||
| 	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; | ||||
| 	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; | ||||
| 
 | ||||
| 	num_clk = of_property_count_strings(dev->of_node, "clock-names"); | ||||
| 	if (num_clk <= 0) { | ||||
| 		DRM_ERROR("no clocks are defined\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < num_clk; i++) { | ||||
| 		rc = of_property_read_string_index(dev->of_node, | ||||
| 				"clock-names", i, &clk_name); | ||||
| 		if (rc < 0) | ||||
| 			return rc; | ||||
| 
 | ||||
| 		if (dp_parser_check_prefix("core", clk_name)) | ||||
| 			core_clk_count++; | ||||
| 
 | ||||
| 		if (dp_parser_check_prefix("ctrl", clk_name)) | ||||
| 			ctrl_clk_count++; | ||||
| 
 | ||||
| 		if (dp_parser_check_prefix("stream", clk_name)) | ||||
| 			ctrl_clk_count++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Initialize the CORE power module */ | ||||
| 	if (core_clk_count == 0) { | ||||
| 		DRM_ERROR("no core clocks are defined\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	core_power->num_clk = core_clk_count; | ||||
| 	core_power->clk_config = devm_kzalloc(dev, | ||||
| 			sizeof(struct dss_clk) * core_power->num_clk, | ||||
| 			GFP_KERNEL); | ||||
| 	if (!core_power->clk_config) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* Initialize the CTRL power module */ | ||||
| 	if (ctrl_clk_count == 0) { | ||||
| 		DRM_ERROR("no ctrl clocks are defined\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	ctrl_power->num_clk = ctrl_clk_count; | ||||
| 	ctrl_power->clk_config = devm_kzalloc(dev, | ||||
| 			sizeof(struct dss_clk) * ctrl_power->num_clk, | ||||
| 			GFP_KERNEL); | ||||
| 	if (!ctrl_power->clk_config) { | ||||
| 		ctrl_power->num_clk = 0; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_parser_clock(struct dp_parser *parser) | ||||
| { | ||||
| 	int rc = 0, i = 0; | ||||
| 	int num_clk = 0; | ||||
| 	int core_clk_index = 0, ctrl_clk_index = 0; | ||||
| 	int core_clk_count = 0, ctrl_clk_count = 0; | ||||
| 	const char *clk_name; | ||||
| 	struct device *dev = &parser->pdev->dev; | ||||
| 	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; | ||||
| 	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; | ||||
| 
 | ||||
| 	core_power = &parser->mp[DP_CORE_PM]; | ||||
| 	ctrl_power = &parser->mp[DP_CTRL_PM]; | ||||
| 
 | ||||
| 	rc =  dp_parser_init_clk_data(parser); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to initialize power data %d\n", rc); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	core_clk_count = core_power->num_clk; | ||||
| 	ctrl_clk_count = ctrl_power->num_clk; | ||||
| 
 | ||||
| 	num_clk = core_clk_count + ctrl_clk_count; | ||||
| 
 | ||||
| 	for (i = 0; i < num_clk; i++) { | ||||
| 		rc = of_property_read_string_index(dev->of_node, "clock-names", | ||||
| 				i, &clk_name); | ||||
| 		if (rc) { | ||||
| 			DRM_ERROR("error reading clock-names %d\n", rc); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		if (dp_parser_check_prefix("core", clk_name) && | ||||
| 				core_clk_index < core_clk_count) { | ||||
| 			struct dss_clk *clk = | ||||
| 				&core_power->clk_config[core_clk_index]; | ||||
| 			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); | ||||
| 			clk->type = DSS_CLK_AHB; | ||||
| 			core_clk_index++; | ||||
| 		} else if ((dp_parser_check_prefix("ctrl", clk_name) || | ||||
| 			   dp_parser_check_prefix("stream", clk_name))  && | ||||
| 			   ctrl_clk_index < ctrl_clk_count) { | ||||
| 			struct dss_clk *clk = | ||||
| 				&ctrl_power->clk_config[ctrl_clk_index]; | ||||
| 			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); | ||||
| 			ctrl_clk_index++; | ||||
| 
 | ||||
| 			if (dp_parser_check_prefix("ctrl_link", clk_name) || | ||||
| 			    dp_parser_check_prefix("stream_pixel", clk_name)) | ||||
| 				clk->type = DSS_CLK_PCLK; | ||||
| 			else | ||||
| 				clk->type = DSS_CLK_AHB; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("clock parsing successful\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_parser_parse(struct dp_parser *parser) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 
 | ||||
| 	if (!parser) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_parser_ctrl_res(parser); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	rc = dp_parser_misc(parser); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	rc = dp_parser_clock(parser); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	/* Map the corresponding regulator information according to
 | ||||
| 	 * version. Currently, since we only have one supported platform, | ||||
| 	 * mapping the regulator directly. | ||||
| 	 */ | ||||
| 	parser->regulator_cfg = &sdm845_dp_reg_cfg; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct dp_parser *dp_parser_get(struct platform_device *pdev) | ||||
| { | ||||
| 	struct dp_parser *parser; | ||||
| 
 | ||||
| 	parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL); | ||||
| 	if (!parser) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	parser->parse = dp_parser_parse; | ||||
| 	parser->pdev = pdev; | ||||
| 
 | ||||
| 	return parser; | ||||
| } | ||||
							
								
								
									
										139
									
								
								drivers/gpu/drm/msm/dp/dp_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								drivers/gpu/drm/msm/dp/dp_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_PARSER_H_ | ||||
| #define _DP_PARSER_H_ | ||||
| 
 | ||||
| #include <linux/platform_device.h> | ||||
| 
 | ||||
| #include "dpu_io_util.h" | ||||
| #include "msm_drv.h" | ||||
| 
 | ||||
| #define DP_LABEL "MDSS DP DISPLAY" | ||||
| #define DP_MAX_PIXEL_CLK_KHZ	675000 | ||||
| #define DP_MAX_NUM_DP_LANES	4 | ||||
| 
 | ||||
| enum dp_pm_type { | ||||
| 	DP_CORE_PM, | ||||
| 	DP_CTRL_PM, | ||||
| 	DP_PHY_PM, | ||||
| 	DP_MAX_PM | ||||
| }; | ||||
| 
 | ||||
| struct dss_io_data { | ||||
| 	u32 len; | ||||
| 	void __iomem *base; | ||||
| }; | ||||
| 
 | ||||
| static inline const char *dp_parser_pm_name(enum dp_pm_type module) | ||||
| { | ||||
| 	switch (module) { | ||||
| 	case DP_CORE_PM:	return "DP_CORE_PM"; | ||||
| 	case DP_CTRL_PM:	return "DP_CTRL_PM"; | ||||
| 	case DP_PHY_PM:		return "DP_PHY_PM"; | ||||
| 	default:		return "???"; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_display_data  - display related device tree data. | ||||
|  * | ||||
|  * @ctrl_node: referece to controller device | ||||
|  * @phy_node:  reference to phy device | ||||
|  * @is_active: is the controller currently active | ||||
|  * @name: name of the display | ||||
|  * @display_type: type of the display | ||||
|  */ | ||||
| struct dp_display_data { | ||||
| 	struct device_node *ctrl_node; | ||||
| 	struct device_node *phy_node; | ||||
| 	bool is_active; | ||||
| 	const char *name; | ||||
| 	const char *display_type; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_ctrl_resource - controller's IO related data | ||||
|  * | ||||
|  * @dp_controller: Display Port controller mapped memory address | ||||
|  * @phy_io: phy's mapped memory address | ||||
|  * @ln_tx0_io: USB-DP lane TX0's mapped memory address | ||||
|  * @ln_tx1_io: USB-DP lane TX1's mapped memory address | ||||
|  * @dp_pll_io: DP PLL mapped memory address | ||||
|  * @usb3_dp_com: USB3 DP PHY combo mapped memory address | ||||
|  */ | ||||
| struct dp_io { | ||||
| 	struct dss_io_data dp_controller; | ||||
| 	struct dss_io_data phy_io; | ||||
| 	struct dss_io_data ln_tx0_io; | ||||
| 	struct dss_io_data ln_tx1_io; | ||||
| 	struct dss_io_data dp_pll_io; | ||||
| 	struct dss_io_data usb3_dp_com; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_pinctrl - DP's pin control | ||||
|  * | ||||
|  * @pin: pin-controller's instance | ||||
|  * @state_active: active state pin control | ||||
|  * @state_hpd_active: hpd active state pin control | ||||
|  * @state_suspend: suspend state pin control | ||||
|  */ | ||||
| struct dp_pinctrl { | ||||
| 	struct pinctrl *pin; | ||||
| 	struct pinctrl_state *state_active; | ||||
| 	struct pinctrl_state *state_hpd_active; | ||||
| 	struct pinctrl_state *state_suspend; | ||||
| }; | ||||
| 
 | ||||
| #define DP_DEV_REGULATOR_MAX	4 | ||||
| 
 | ||||
| /* Regulators for DP devices */ | ||||
| struct dp_reg_entry { | ||||
| 	char name[32]; | ||||
| 	int enable_load; | ||||
| 	int disable_load; | ||||
| }; | ||||
| 
 | ||||
| struct dp_regulator_cfg { | ||||
| 	int num; | ||||
| 	struct dp_reg_entry regs[DP_DEV_REGULATOR_MAX]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dp_parser - DP parser's data exposed to clients | ||||
|  * | ||||
|  * @pdev: platform data of the client | ||||
|  * @mp: gpio, regulator and clock related data | ||||
|  * @pinctrl: pin-control related data | ||||
|  * @disp_data: controller's display related data | ||||
|  * @parse: function to be called by client to parse device tree. | ||||
|  */ | ||||
| struct dp_parser { | ||||
| 	struct platform_device *pdev; | ||||
| 	struct dss_module_power mp[DP_MAX_PM]; | ||||
| 	struct dp_pinctrl pinctrl; | ||||
| 	struct dp_io io; | ||||
| 	struct dp_display_data disp_data; | ||||
| 	const struct dp_regulator_cfg *regulator_cfg; | ||||
| 	u32 max_dp_lanes; | ||||
| 
 | ||||
| 	int (*parse)(struct dp_parser *parser); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_parser_get() - get the DP's device tree parser module | ||||
|  * | ||||
|  * @pdev: platform data of the client | ||||
|  * return: pointer to dp_parser structure. | ||||
|  * | ||||
|  * This function provides client capability to parse the | ||||
|  * device tree and populate the data structures. The data | ||||
|  * related to clock, regulators, pin-control and other | ||||
|  * can be parsed using this module. | ||||
|  */ | ||||
| struct dp_parser *dp_parser_get(struct platform_device *pdev); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										363
									
								
								drivers/gpu/drm/msm/dp/dp_power.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								drivers/gpu/drm/msm/dp/dp_power.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,363 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| #include <linux/clk-provider.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include "dp_power.h" | ||||
| 
 | ||||
| struct dp_power_private { | ||||
| 	struct dp_parser *parser; | ||||
| 	struct platform_device *pdev; | ||||
| 	struct clk *link_clk_src; | ||||
| 	struct clk *pixel_provider; | ||||
| 	struct clk *link_provider; | ||||
| 	struct regulator_bulk_data supplies[DP_DEV_REGULATOR_MAX]; | ||||
| 
 | ||||
| 	struct dp_power dp_power; | ||||
| }; | ||||
| 
 | ||||
| static void dp_power_regulator_disable(struct dp_power_private *power) | ||||
| { | ||||
| 	struct regulator_bulk_data *s = power->supplies; | ||||
| 	const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs; | ||||
| 	int num = power->parser->regulator_cfg->num; | ||||
| 	int i; | ||||
| 
 | ||||
| 	DBG(""); | ||||
| 	for (i = num - 1; i >= 0; i--) | ||||
| 		if (regs[i].disable_load >= 0) | ||||
| 			regulator_set_load(s[i].consumer, | ||||
| 					   regs[i].disable_load); | ||||
| 
 | ||||
| 	regulator_bulk_disable(num, s); | ||||
| } | ||||
| 
 | ||||
| static int dp_power_regulator_enable(struct dp_power_private *power) | ||||
| { | ||||
| 	struct regulator_bulk_data *s = power->supplies; | ||||
| 	const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs; | ||||
| 	int num = power->parser->regulator_cfg->num; | ||||
| 	int ret, i; | ||||
| 
 | ||||
| 	DBG(""); | ||||
| 	for (i = 0; i < num; i++) { | ||||
| 		if (regs[i].enable_load >= 0) { | ||||
| 			ret = regulator_set_load(s[i].consumer, | ||||
| 						 regs[i].enable_load); | ||||
| 			if (ret < 0) { | ||||
| 				pr_err("regulator %d set op mode failed, %d\n", | ||||
| 					i, ret); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret = regulator_bulk_enable(num, s); | ||||
| 	if (ret < 0) { | ||||
| 		pr_err("regulator enable failed, %d\n", ret); | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| fail: | ||||
| 	for (i--; i >= 0; i--) | ||||
| 		regulator_set_load(s[i].consumer, regs[i].disable_load); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int dp_power_regulator_init(struct dp_power_private *power) | ||||
| { | ||||
| 	struct regulator_bulk_data *s = power->supplies; | ||||
| 	const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs; | ||||
| 	struct platform_device *pdev = power->pdev; | ||||
| 	int num = power->parser->regulator_cfg->num; | ||||
| 	int i, ret; | ||||
| 
 | ||||
| 	for (i = 0; i < num; i++) | ||||
| 		s[i].supply = regs[i].name; | ||||
| 
 | ||||
| 	ret = devm_regulator_bulk_get(&pdev->dev, num, s); | ||||
| 	if (ret < 0) { | ||||
| 		pr_err("%s: failed to init regulator, ret=%d\n", | ||||
| 						__func__, ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_power_clk_init(struct dp_power_private *power) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dss_module_power *core, *ctrl; | ||||
| 	struct device *dev = &power->pdev->dev; | ||||
| 
 | ||||
| 	core = &power->parser->mp[DP_CORE_PM]; | ||||
| 	ctrl = &power->parser->mp[DP_CTRL_PM]; | ||||
| 
 | ||||
| 	rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to get %s clk. err=%d\n", | ||||
| 			dp_parser_pm_name(DP_CORE_PM), rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to get %s clk. err=%d\n", | ||||
| 			dp_parser_pm_name(DP_CTRL_PM), rc); | ||||
| 		msm_dss_put_clk(core->clk_config, core->num_clk); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_power_clk_deinit(struct dp_power_private *power) | ||||
| { | ||||
| 	struct dss_module_power *core, *ctrl; | ||||
| 
 | ||||
| 	core = &power->parser->mp[DP_CORE_PM]; | ||||
| 	ctrl = &power->parser->mp[DP_CTRL_PM]; | ||||
| 
 | ||||
| 	if (!core || !ctrl) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk); | ||||
| 	msm_dss_put_clk(core->clk_config, core->num_clk); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dp_power_clk_set_rate(struct dp_power_private *power, | ||||
| 		enum dp_pm_type module, bool enable) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dss_module_power *mp = &power->parser->mp[module]; | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk); | ||||
| 		if (rc) { | ||||
| 			DRM_ERROR("failed to set clks rate.\n"); | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to %d clks, err: %d\n", enable, rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dp_power_clk_enable(struct dp_power *dp_power, | ||||
| 		enum dp_pm_type pm_type, bool enable) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_power_private *power; | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM) { | ||||
| 		DRM_ERROR("unsupported power module: %s\n", | ||||
| 				dp_parser_pm_name(pm_type)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		if (pm_type == DP_CORE_PM && dp_power->core_clks_on) { | ||||
| 			DRM_DEBUG_DP("core clks already enabled\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) { | ||||
| 			DRM_DEBUG_DP("links clks already enabled\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) { | ||||
| 			DRM_DEBUG_DP("Enable core clks before link clks\n"); | ||||
| 
 | ||||
| 			rc = dp_power_clk_set_rate(power, DP_CORE_PM, enable); | ||||
| 			if (rc) { | ||||
| 				DRM_ERROR("fail to enable clks: %s. err=%d\n", | ||||
| 					dp_parser_pm_name(DP_CORE_PM), rc); | ||||
| 				return rc; | ||||
| 			} | ||||
| 			dp_power->core_clks_on = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_power_clk_set_rate(power, pm_type, enable); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to '%s' clks for: %s. err=%d\n", | ||||
| 			enable ? "enable" : "disable", | ||||
| 			dp_parser_pm_name(pm_type), rc); | ||||
| 			return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pm_type == DP_CORE_PM) | ||||
| 		dp_power->core_clks_on = enable; | ||||
| 	else | ||||
| 		dp_power->link_clks_on = enable; | ||||
| 
 | ||||
| 	DRM_DEBUG_DP("%s clocks for %s\n", | ||||
| 			enable ? "enable" : "disable", | ||||
| 			dp_parser_pm_name(pm_type)); | ||||
| 	DRM_DEBUG_DP("link_clks:%s core_clks:%s\n", | ||||
| 		dp_power->link_clks_on ? "on" : "off", | ||||
| 		dp_power->core_clks_on ? "on" : "off"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dp_power_client_init(struct dp_power *dp_power) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_power_private *power; | ||||
| 
 | ||||
| 	if (!dp_power) { | ||||
| 		DRM_ERROR("invalid power data\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	pm_runtime_enable(&power->pdev->dev); | ||||
| 
 | ||||
| 	rc = dp_power_regulator_init(power); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to init regulators %d\n", rc); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_power_clk_init(power); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to init clocks %d\n", rc); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	return 0; | ||||
| 
 | ||||
| error: | ||||
| 	pm_runtime_disable(&power->pdev->dev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| void dp_power_client_deinit(struct dp_power *dp_power) | ||||
| { | ||||
| 	struct dp_power_private *power; | ||||
| 
 | ||||
| 	if (!dp_power) { | ||||
| 		DRM_ERROR("invalid power data\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	dp_power_clk_deinit(power); | ||||
| 	pm_runtime_disable(&power->pdev->dev); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int dp_power_set_link_clk_parent(struct dp_power *dp_power) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_power_private *power; | ||||
| 	u32 num; | ||||
| 	struct dss_clk *cfg; | ||||
| 	char *name = "ctrl_link"; | ||||
| 
 | ||||
| 	if (!dp_power) { | ||||
| 		DRM_ERROR("invalid power data\n"); | ||||
| 		rc = -EINVAL; | ||||
| 		goto exit; | ||||
| 	} | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	num = power->parser->mp[DP_CTRL_PM].num_clk; | ||||
| 	cfg = power->parser->mp[DP_CTRL_PM].clk_config; | ||||
| 
 | ||||
| 	while (num && strcmp(cfg->clk_name, name)) { | ||||
| 		num--; | ||||
| 		cfg++; | ||||
| 	} | ||||
| 
 | ||||
| exit: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int dp_power_init(struct dp_power *dp_power, bool flip) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	struct dp_power_private *power = NULL; | ||||
| 
 | ||||
| 	if (!dp_power) { | ||||
| 		DRM_ERROR("invalid power data\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	pm_runtime_get_sync(&power->pdev->dev); | ||||
| 	rc = dp_power_regulator_enable(power); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to enable regulators, %d\n", rc); | ||||
| 		goto exit; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true); | ||||
| 	if (rc) { | ||||
| 		DRM_ERROR("failed to enable DP core clocks, %d\n", rc); | ||||
| 		goto err_clk; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_clk: | ||||
| 	dp_power_regulator_disable(power); | ||||
| exit: | ||||
| 	pm_runtime_put_sync(&power->pdev->dev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int dp_power_deinit(struct dp_power *dp_power) | ||||
| { | ||||
| 	struct dp_power_private *power; | ||||
| 
 | ||||
| 	power = container_of(dp_power, struct dp_power_private, dp_power); | ||||
| 
 | ||||
| 	dp_power_clk_enable(dp_power, DP_CORE_PM, false); | ||||
| 	dp_power_regulator_disable(power); | ||||
| 	pm_runtime_put_sync(&power->pdev->dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct dp_power *dp_power_get(struct dp_parser *parser) | ||||
| { | ||||
| 	struct dp_power_private *power; | ||||
| 	struct dp_power *dp_power; | ||||
| 
 | ||||
| 	if (!parser) { | ||||
| 		DRM_ERROR("invalid input\n"); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL); | ||||
| 	if (!power) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	power->parser = parser; | ||||
| 	power->pdev = parser->pdev; | ||||
| 
 | ||||
| 	dp_power = &power->dp_power; | ||||
| 
 | ||||
| 	return dp_power; | ||||
| } | ||||
							
								
								
									
										65
									
								
								drivers/gpu/drm/msm/dp/dp_power.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								drivers/gpu/drm/msm/dp/dp_power.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_POWER_H_ | ||||
| #define _DP_POWER_H_ | ||||
| 
 | ||||
| #include "dp_parser.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * sruct dp_power - DisplayPort's power related data | ||||
|  * | ||||
|  * @init: initializes the regulators/core clocks/GPIOs/pinctrl | ||||
|  * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl | ||||
|  * @clk_enable: enable/disable the DP clocks | ||||
|  * @set_link_clk_parent: set the parent of DP link clock | ||||
|  * @set_pixel_clk_parent: set the parent of DP pixel clock | ||||
|  */ | ||||
| struct dp_power { | ||||
| 	bool core_clks_on; | ||||
| 	bool link_clks_on; | ||||
| }; | ||||
| 
 | ||||
| int dp_power_init(struct dp_power *power, bool flip); | ||||
| int dp_power_deinit(struct dp_power *power); | ||||
| int dp_power_clk_enable(struct dp_power *power, enum dp_pm_type pm_type, | ||||
| 				bool enable); | ||||
| int dp_power_set_link_clk_parent(struct dp_power *power); | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_power_client_init() - initialize clock and regulator modules | ||||
|  * | ||||
|  * @power: instance of power module | ||||
|  * return: 0 for success, error for failure. | ||||
|  * | ||||
|  * This API will configure the DisplayPort's clocks and regulator | ||||
|  * modules. | ||||
|  */ | ||||
| int dp_power_client_init(struct dp_power *power); | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_power_clinet_deinit() - de-initialize clock and regulator modules | ||||
|  * | ||||
|  * @power: instance of power module | ||||
|  * return: 0 for success, error for failure. | ||||
|  * | ||||
|  * This API will de-initialize the DisplayPort's clocks and regulator | ||||
|  * modueles. | ||||
|  */ | ||||
| void dp_power_client_deinit(struct dp_power *power); | ||||
| 
 | ||||
| /**
 | ||||
|  * dp_power_get() - configure and get the DisplayPort power module data | ||||
|  * | ||||
|  * @parser: instance of parser module | ||||
|  * return: pointer to allocated power module data | ||||
|  * | ||||
|  * This API will configure the DisplayPort's power module and provides | ||||
|  * methods to be called by the client to configure the power related | ||||
|  * modueles. | ||||
|  */ | ||||
| struct dp_power *dp_power_get(struct dp_parser *parser); | ||||
| 
 | ||||
| #endif /* _DP_POWER_H_ */ | ||||
							
								
								
									
										490
									
								
								drivers/gpu/drm/msm/dp/dp_reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										490
									
								
								drivers/gpu/drm/msm/dp/dp_reg.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,490 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DP_REG_H_ | ||||
| #define _DP_REG_H_ | ||||
| 
 | ||||
| /* DP_TX Registers */ | ||||
| #define REG_DP_HW_VERSION			(0x00000000) | ||||
| 
 | ||||
| #define REG_DP_SW_RESET				(0x00000010) | ||||
| #define DP_SW_RESET				(0x00000001) | ||||
| 
 | ||||
| #define REG_DP_PHY_CTRL				(0x00000014) | ||||
| #define DP_PHY_CTRL_SW_RESET_PLL		(0x00000001) | ||||
| #define DP_PHY_CTRL_SW_RESET			(0x00000004) | ||||
| 
 | ||||
| #define REG_DP_CLK_CTRL				(0x00000018) | ||||
| #define REG_DP_CLK_ACTIVE			(0x0000001C) | ||||
| #define REG_DP_INTR_STATUS			(0x00000020) | ||||
| #define REG_DP_INTR_STATUS2			(0x00000024) | ||||
| #define REG_DP_INTR_STATUS3			(0x00000028) | ||||
| 
 | ||||
| #define REG_DP_DP_HPD_CTRL			(0x00000000) | ||||
| #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001) | ||||
| 
 | ||||
| #define REG_DP_DP_HPD_INT_STATUS		(0x00000004) | ||||
| 
 | ||||
| #define REG_DP_DP_HPD_INT_ACK			(0x00000008) | ||||
| #define DP_DP_HPD_PLUG_INT_ACK			(0x00000001) | ||||
| #define DP_DP_IRQ_HPD_INT_ACK			(0x00000002) | ||||
| #define DP_DP_HPD_REPLUG_INT_ACK		(0x00000004) | ||||
| #define DP_DP_HPD_UNPLUG_INT_ACK		(0x00000008) | ||||
| 
 | ||||
| #define REG_DP_DP_HPD_INT_MASK			(0x0000000C) | ||||
| #define DP_DP_HPD_PLUG_INT_MASK			(0x00000001) | ||||
| #define DP_DP_IRQ_HPD_INT_MASK			(0x00000002) | ||||
| #define DP_DP_HPD_REPLUG_INT_MASK		(0x00000004) | ||||
| #define DP_DP_HPD_UNPLUG_INT_MASK		(0x00000008) | ||||
| 
 | ||||
| #define REG_DP_DP_HPD_REFTIMER			(0x00000018) | ||||
| #define REG_DP_DP_HPD_EVENT_TIME_0		(0x0000001C) | ||||
| #define REG_DP_DP_HPD_EVENT_TIME_1		(0x00000020) | ||||
| 
 | ||||
| #define REG_DP_AUX_CTRL				(0x00000030) | ||||
| #define DP_AUX_CTRL_ENABLE			(0x00000001) | ||||
| #define DP_AUX_CTRL_RESET			(0x00000002) | ||||
| 
 | ||||
| #define REG_DP_AUX_DATA				(0x00000034) | ||||
| #define DP_AUX_DATA_READ			(0x00000001) | ||||
| #define DP_AUX_DATA_WRITE			(0x00000000) | ||||
| #define DP_AUX_DATA_OFFSET			(0x00000008) | ||||
| #define DP_AUX_DATA_INDEX_OFFSET		(0x00000010) | ||||
| #define DP_AUX_DATA_MASK			(0x0000ff00) | ||||
| #define DP_AUX_DATA_INDEX_WRITE			(0x80000000) | ||||
| 
 | ||||
| #define REG_DP_AUX_TRANS_CTRL			(0x00000038) | ||||
| #define DP_AUX_TRANS_CTRL_I2C			(0x00000100) | ||||
| #define DP_AUX_TRANS_CTRL_GO			(0x00000200) | ||||
| #define DP_AUX_TRANS_CTRL_NO_SEND_ADDR		(0x00000400) | ||||
| #define DP_AUX_TRANS_CTRL_NO_SEND_STOP		(0x00000800) | ||||
| 
 | ||||
| #define REG_DP_TIMEOUT_COUNT			(0x0000003C) | ||||
| #define REG_DP_AUX_LIMITS			(0x00000040) | ||||
| #define REG_DP_AUX_STATUS			(0x00000044) | ||||
| 
 | ||||
| #define DP_DPCD_CP_IRQ				(0x201) | ||||
| #define DP_DPCD_RXSTATUS			(0x69493) | ||||
| 
 | ||||
| #define DP_INTERRUPT_TRANS_NUM			(0x000000A0) | ||||
| 
 | ||||
| #define REG_DP_MAINLINK_CTRL			(0x00000000) | ||||
| #define DP_MAINLINK_CTRL_ENABLE			(0x00000001) | ||||
| #define DP_MAINLINK_CTRL_RESET			(0x00000002) | ||||
| #define DP_MAINLINK_FB_BOUNDARY_SEL		(0x02000000) | ||||
| 
 | ||||
| #define REG_DP_STATE_CTRL			(0x00000004) | ||||
| #define DP_STATE_CTRL_LINK_TRAINING_PATTERN1	(0x00000001) | ||||
| #define DP_STATE_CTRL_LINK_TRAINING_PATTERN2	(0x00000002) | ||||
| #define DP_STATE_CTRL_LINK_TRAINING_PATTERN3	(0x00000004) | ||||
| #define DP_STATE_CTRL_LINK_TRAINING_PATTERN4	(0x00000008) | ||||
| #define DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE	(0x00000010) | ||||
| #define DP_STATE_CTRL_LINK_PRBS7		(0x00000020) | ||||
| #define DP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN	(0x00000040) | ||||
| #define DP_STATE_CTRL_SEND_VIDEO		(0x00000080) | ||||
| #define DP_STATE_CTRL_PUSH_IDLE			(0x00000100) | ||||
| 
 | ||||
| #define REG_DP_CONFIGURATION_CTRL		(0x00000008) | ||||
| #define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK	(0x00000001) | ||||
| #define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002) | ||||
| #define DP_CONFIGURATION_CTRL_P_INTERLACED	(0x00000004) | ||||
| #define DP_CONFIGURATION_CTRL_INTERLACED_BTF	(0x00000008) | ||||
| #define DP_CONFIGURATION_CTRL_NUM_OF_LANES	(0x00000010) | ||||
| #define DP_CONFIGURATION_CTRL_ENHANCED_FRAMING	(0x00000040) | ||||
| #define DP_CONFIGURATION_CTRL_SEND_VSC		(0x00000080) | ||||
| #define DP_CONFIGURATION_CTRL_BPC		(0x00000100) | ||||
| #define DP_CONFIGURATION_CTRL_ASSR		(0x00000400) | ||||
| #define DP_CONFIGURATION_CTRL_RGB_YUV		(0x00000800) | ||||
| #define DP_CONFIGURATION_CTRL_LSCLK_DIV		(0x00002000) | ||||
| #define DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT	(0x04) | ||||
| #define DP_CONFIGURATION_CTRL_BPC_SHIFT		(0x08) | ||||
| #define DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT	(0x0D) | ||||
| 
 | ||||
| #define REG_DP_SOFTWARE_MVID			(0x00000010) | ||||
| #define REG_DP_SOFTWARE_NVID			(0x00000018) | ||||
| #define REG_DP_TOTAL_HOR_VER			(0x0000001C) | ||||
| #define REG_DP_START_HOR_VER_FROM_SYNC		(0x00000020) | ||||
| #define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY	(0x00000024) | ||||
| #define REG_DP_ACTIVE_HOR_VER			(0x00000028) | ||||
| 
 | ||||
| #define REG_DP_MISC1_MISC0			(0x0000002C) | ||||
| #define DP_MISC0_SYNCHRONOUS_CLK		(0x00000001) | ||||
| #define DP_MISC0_COLORIMETRY_CFG_SHIFT		(0x00000001) | ||||
| #define DP_MISC0_TEST_BITS_DEPTH_SHIFT		(0x00000005) | ||||
| 
 | ||||
| #define REG_DP_VALID_BOUNDARY			(0x00000030) | ||||
| #define REG_DP_VALID_BOUNDARY_2			(0x00000034) | ||||
| 
 | ||||
| #define REG_DP_LOGICAL2PHYSICAL_LANE_MAPPING	(0x00000038) | ||||
| #define LANE0_MAPPING_SHIFT			(0x00000000) | ||||
| #define LANE1_MAPPING_SHIFT			(0x00000002) | ||||
| #define LANE2_MAPPING_SHIFT			(0x00000004) | ||||
| #define LANE3_MAPPING_SHIFT			(0x00000006) | ||||
| 
 | ||||
| #define REG_DP_MAINLINK_READY			(0x00000040) | ||||
| #define DP_MAINLINK_READY_FOR_VIDEO		(0x00000001) | ||||
| #define DP_MAINLINK_READY_LINK_TRAINING_SHIFT	(0x00000003) | ||||
| 
 | ||||
| #define REG_DP_MAINLINK_LEVELS			(0x00000044) | ||||
| #define DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2	(0x00000002) | ||||
| 
 | ||||
| 
 | ||||
| #define REG_DP_TU				(0x0000004C) | ||||
| 
 | ||||
| #define REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET	(0x00000054) | ||||
| #define DP_HBR2_ERM_PATTERN			(0x00010000) | ||||
| 
 | ||||
| #define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG0	(0x000000C0) | ||||
| #define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG1	(0x000000C4) | ||||
| #define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG2	(0x000000C8) | ||||
| 
 | ||||
| #define MMSS_DP_MISC1_MISC0			(0x0000002C) | ||||
| #define MMSS_DP_AUDIO_TIMING_GEN		(0x00000080) | ||||
| #define MMSS_DP_AUDIO_TIMING_RBR_32		(0x00000084) | ||||
| #define MMSS_DP_AUDIO_TIMING_HBR_32		(0x00000088) | ||||
| #define MMSS_DP_AUDIO_TIMING_RBR_44		(0x0000008C) | ||||
| #define MMSS_DP_AUDIO_TIMING_HBR_44		(0x00000090) | ||||
| #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094) | ||||
| #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098) | ||||
| 
 | ||||
| #define MMSS_DP_PSR_CRC_RG			(0x00000154) | ||||
| #define MMSS_DP_PSR_CRC_B			(0x00000158) | ||||
| 
 | ||||
| #define REG_DP_COMPRESSION_MODE_CTRL		(0x00000180) | ||||
| 
 | ||||
| #define MMSS_DP_AUDIO_CFG			(0x00000200) | ||||
| #define MMSS_DP_AUDIO_STATUS			(0x00000204) | ||||
| #define MMSS_DP_AUDIO_PKT_CTRL			(0x00000208) | ||||
| #define MMSS_DP_AUDIO_PKT_CTRL2			(0x0000020C) | ||||
| #define MMSS_DP_AUDIO_ACR_CTRL			(0x00000210) | ||||
| #define MMSS_DP_AUDIO_CTRL_RESET		(0x00000214) | ||||
| 
 | ||||
| #define MMSS_DP_SDP_CFG				(0x00000228) | ||||
| #define MMSS_DP_SDP_CFG2			(0x0000022C) | ||||
| #define MMSS_DP_AUDIO_TIMESTAMP_0		(0x00000230) | ||||
| #define MMSS_DP_AUDIO_TIMESTAMP_1		(0x00000234) | ||||
| 
 | ||||
| #define MMSS_DP_AUDIO_STREAM_0			(0x00000240) | ||||
| #define MMSS_DP_AUDIO_STREAM_1			(0x00000244) | ||||
| 
 | ||||
| #define MMSS_DP_EXTENSION_0			(0x00000250) | ||||
| #define MMSS_DP_EXTENSION_1			(0x00000254) | ||||
| #define MMSS_DP_EXTENSION_2			(0x00000258) | ||||
| #define MMSS_DP_EXTENSION_3			(0x0000025C) | ||||
| #define MMSS_DP_EXTENSION_4			(0x00000260) | ||||
| #define MMSS_DP_EXTENSION_5			(0x00000264) | ||||
| #define MMSS_DP_EXTENSION_6			(0x00000268) | ||||
| #define MMSS_DP_EXTENSION_7			(0x0000026C) | ||||
| #define MMSS_DP_EXTENSION_8			(0x00000270) | ||||
| #define MMSS_DP_EXTENSION_9			(0x00000274) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_0		(0x00000278) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_1		(0x0000027C) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_2		(0x00000280) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_3		(0x00000284) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_4		(0x00000288) | ||||
| #define MMSS_DP_AUDIO_COPYMANAGEMENT_5		(0x0000028C) | ||||
| #define MMSS_DP_AUDIO_ISRC_0			(0x00000290) | ||||
| #define MMSS_DP_AUDIO_ISRC_1			(0x00000294) | ||||
| #define MMSS_DP_AUDIO_ISRC_2			(0x00000298) | ||||
| #define MMSS_DP_AUDIO_ISRC_3			(0x0000029C) | ||||
| #define MMSS_DP_AUDIO_ISRC_4			(0x000002A0) | ||||
| #define MMSS_DP_AUDIO_ISRC_5			(0x000002A4) | ||||
| #define MMSS_DP_AUDIO_INFOFRAME_0		(0x000002A8) | ||||
| #define MMSS_DP_AUDIO_INFOFRAME_1		(0x000002AC) | ||||
| #define MMSS_DP_AUDIO_INFOFRAME_2		(0x000002B0) | ||||
| 
 | ||||
| #define MMSS_DP_GENERIC0_0			(0x00000300) | ||||
| #define MMSS_DP_GENERIC0_1			(0x00000304) | ||||
| #define MMSS_DP_GENERIC0_2			(0x00000308) | ||||
| #define MMSS_DP_GENERIC0_3			(0x0000030C) | ||||
| #define MMSS_DP_GENERIC0_4			(0x00000310) | ||||
| #define MMSS_DP_GENERIC0_5			(0x00000314) | ||||
| #define MMSS_DP_GENERIC0_6			(0x00000318) | ||||
| #define MMSS_DP_GENERIC0_7			(0x0000031C) | ||||
| #define MMSS_DP_GENERIC0_8			(0x00000320) | ||||
| #define MMSS_DP_GENERIC0_9			(0x00000324) | ||||
| #define MMSS_DP_GENERIC1_0			(0x00000328) | ||||
| #define MMSS_DP_GENERIC1_1			(0x0000032C) | ||||
| #define MMSS_DP_GENERIC1_2			(0x00000330) | ||||
| #define MMSS_DP_GENERIC1_3			(0x00000334) | ||||
| #define MMSS_DP_GENERIC1_4			(0x00000338) | ||||
| #define MMSS_DP_GENERIC1_5			(0x0000033C) | ||||
| #define MMSS_DP_GENERIC1_6			(0x00000340) | ||||
| #define MMSS_DP_GENERIC1_7			(0x00000344) | ||||
| #define MMSS_DP_GENERIC1_8			(0x00000348) | ||||
| #define MMSS_DP_GENERIC1_9			(0x0000034C) | ||||
| 
 | ||||
| #define MMSS_DP_VSCEXT_0			(0x000002D0) | ||||
| #define MMSS_DP_VSCEXT_1			(0x000002D4) | ||||
| #define MMSS_DP_VSCEXT_2			(0x000002D8) | ||||
| #define MMSS_DP_VSCEXT_3			(0x000002DC) | ||||
| #define MMSS_DP_VSCEXT_4			(0x000002E0) | ||||
| #define MMSS_DP_VSCEXT_5			(0x000002E4) | ||||
| #define MMSS_DP_VSCEXT_6			(0x000002E8) | ||||
| #define MMSS_DP_VSCEXT_7			(0x000002EC) | ||||
| #define MMSS_DP_VSCEXT_8			(0x000002F0) | ||||
| #define MMSS_DP_VSCEXT_9			(0x000002F4) | ||||
| 
 | ||||
| #define MMSS_DP_BIST_ENABLE			(0x00000000) | ||||
| #define DP_BIST_ENABLE_DPBIST_EN		(0x00000001) | ||||
| 
 | ||||
| #define MMSS_DP_TIMING_ENGINE_EN		(0x00000010) | ||||
| #define DP_TIMING_ENGINE_EN_EN			(0x00000001) | ||||
| 
 | ||||
| #define MMSS_DP_INTF_CONFIG			(0x00000014) | ||||
| #define MMSS_DP_INTF_HSYNC_CTL			(0x00000018) | ||||
| #define MMSS_DP_INTF_VSYNC_PERIOD_F0		(0x0000001C) | ||||
| #define MMSS_DP_INTF_VSYNC_PERIOD_F1		(0x00000020) | ||||
| #define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0	(0x00000024) | ||||
| #define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1	(0x00000028) | ||||
| #define MMSS_INTF_DISPLAY_V_START_F0		(0x0000002C) | ||||
| #define MMSS_INTF_DISPLAY_V_START_F1		(0x00000030) | ||||
| #define MMSS_DP_INTF_DISPLAY_V_END_F0		(0x00000034) | ||||
| #define MMSS_DP_INTF_DISPLAY_V_END_F1		(0x00000038) | ||||
| #define MMSS_DP_INTF_ACTIVE_V_START_F0		(0x0000003C) | ||||
| #define MMSS_DP_INTF_ACTIVE_V_START_F1		(0x00000040) | ||||
| #define MMSS_DP_INTF_ACTIVE_V_END_F0		(0x00000044) | ||||
| #define MMSS_DP_INTF_ACTIVE_V_END_F1		(0x00000048) | ||||
| #define MMSS_DP_INTF_DISPLAY_HCTL		(0x0000004C) | ||||
| #define MMSS_DP_INTF_ACTIVE_HCTL		(0x00000050) | ||||
| #define MMSS_DP_INTF_POLARITY_CTL		(0x00000058) | ||||
| 
 | ||||
| #define MMSS_DP_TPG_MAIN_CONTROL		(0x00000060) | ||||
| #define MMSS_DP_DSC_DTO				(0x0000007C) | ||||
| #define DP_TPG_CHECKERED_RECT_PATTERN		(0x00000100) | ||||
| 
 | ||||
| #define MMSS_DP_TPG_VIDEO_CONFIG		(0x00000064) | ||||
| #define DP_TPG_VIDEO_CONFIG_BPP_8BIT		(0x00000001) | ||||
| #define DP_TPG_VIDEO_CONFIG_RGB			(0x00000004) | ||||
| 
 | ||||
| #define MMSS_DP_ASYNC_FIFO_CONFIG		(0x00000088) | ||||
| 
 | ||||
| /*DP PHY Register offsets */ | ||||
| #define REG_DP_PHY_REVISION_ID0                 (0x00000000) | ||||
| #define REG_DP_PHY_REVISION_ID1                 (0x00000004) | ||||
| #define REG_DP_PHY_REVISION_ID2                 (0x00000008) | ||||
| #define REG_DP_PHY_REVISION_ID3                 (0x0000000C) | ||||
| 
 | ||||
| #define REG_DP_PHY_CFG                          (0x00000010) | ||||
| 
 | ||||
| #define REG_DP_PHY_PD_CTL                       (0x00000018) | ||||
| #define DP_PHY_PD_CTL_PWRDN                     (0x00000001) | ||||
| #define DP_PHY_PD_CTL_PSR_PWRDN			(0x00000002) | ||||
| #define DP_PHY_PD_CTL_AUX_PWRDN			(0x00000004) | ||||
| #define DP_PHY_PD_CTL_LANE_0_1_PWRDN		(0x00000008) | ||||
| #define DP_PHY_PD_CTL_LANE_2_3_PWRDN		(0x00000010) | ||||
| #define DP_PHY_PD_CTL_PLL_PWRDN			(0x00000020) | ||||
| #define DP_PHY_PD_CTL_DP_CLAMP_EN		(0x00000040) | ||||
| 
 | ||||
| #define REG_DP_PHY_MODE                         (0x0000001C) | ||||
| 
 | ||||
| #define REG_DP_PHY_AUX_CFG0                     (0x00000020) | ||||
| #define REG_DP_PHY_AUX_CFG1                     (0x00000024) | ||||
| #define REG_DP_PHY_AUX_CFG2                     (0x00000028) | ||||
| #define REG_DP_PHY_AUX_CFG3                     (0x0000002C) | ||||
| #define REG_DP_PHY_AUX_CFG4                     (0x00000030) | ||||
| #define REG_DP_PHY_AUX_CFG5                     (0x00000034) | ||||
| #define REG_DP_PHY_AUX_CFG6                     (0x00000038) | ||||
| #define REG_DP_PHY_AUX_CFG7                     (0x0000003C) | ||||
| #define REG_DP_PHY_AUX_CFG8                     (0x00000040) | ||||
| #define REG_DP_PHY_AUX_CFG9                     (0x00000044) | ||||
| 
 | ||||
| #define REG_DP_PHY_AUX_INTERRUPT_MASK           (0x00000048) | ||||
| #define PHY_AUX_STOP_ERR_MASK                   (0x00000001) | ||||
| #define PHY_AUX_DEC_ERR_MASK                    (0x00000002) | ||||
| #define PHY_AUX_SYNC_ERR_MASK                   (0x00000004) | ||||
| #define PHY_AUX_ALIGN_ERR_MASK                  (0x00000008) | ||||
| #define PHY_AUX_REQ_ERR_MASK                    (0x00000010) | ||||
| 
 | ||||
| 
 | ||||
| #define REG_DP_PHY_AUX_INTERRUPT_CLEAR          (0x0000004C) | ||||
| #define REG_DP_PHY_AUX_BIST_CFG			(0x00000050) | ||||
| #define REG_DP_PHY_AUX_INTERRUPT_STATUS         (0x000000BC) | ||||
| 
 | ||||
| #define REG_DP_PHY_VCO_DIV			0x0064 | ||||
| #define REG_DP_PHY_TX0_TX1_LANE_CTL		0x006C | ||||
| #define REG_DP_PHY_TX2_TX3_LANE_CTL		0x0088 | ||||
| 
 | ||||
| #define REG_DP_PHY_SPARE0			(0x00AC) | ||||
| #define DP_PHY_SPARE0_MASK			(0x000F) | ||||
| #define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT	(0x0004) | ||||
| 
 | ||||
| #define REG_DP_PHY_STATUS			(0x00C0) | ||||
| 
 | ||||
| /* Tx registers */ | ||||
| #define REG_DP_PHY_TXn_BIST_MODE_LANENO		0x0000 | ||||
| #define REG_DP_PHY_TXn_CLKBUF_ENABLE		0x0008 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_TX_EMP_POST1_LVL		0x000C | ||||
| #define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK	0x001F | ||||
| #define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN	0x0020 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_TX_DRV_LVL		0x001C | ||||
| #define DP_PHY_TXn_TX_DRV_LVL_MASK		0x001F | ||||
| #define DP_PHY_TXn_TX_DRV_LVL_MUX_EN		0x0020 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_RESET_TSYNC_EN		0x0024 | ||||
| #define REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN	0x0028 | ||||
| #define REG_DP_PHY_TXn_TX_BAND			0x002C | ||||
| #define REG_DP_PHY_TXn_SLEW_CNTL		0x0030 | ||||
| #define REG_DP_PHY_TXn_INTERFACE_SELECT		0x0034 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_RES_CODE_LANE_TX		0x003C | ||||
| #define REG_DP_PHY_TXn_RES_CODE_LANE_RX		0x0040 | ||||
| #define REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX	0x0044 | ||||
| #define REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX	0x0048 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_DEBUG_BUS_SEL		0x0058 | ||||
| #define REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN	0x005C | ||||
| #define REG_DP_PHY_TXn_HIGHZ_DRVR_EN		0x0060 | ||||
| #define REG_DP_PHY_TXn_TX_POL_INV		0x0064 | ||||
| #define REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN 0x0068 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_LANE_MODE_1		0x008C | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN		0x00C0 | ||||
| #define REG_DP_PHY_TXn_TX_INTERFACE_MODE	0x00C4 | ||||
| 
 | ||||
| #define REG_DP_PHY_TXn_VMODE_CTRL1		0x00F0 | ||||
| 
 | ||||
| /* PLL register offset */ | ||||
| #define QSERDES_COM_ATB_SEL1			0x0000 | ||||
| #define QSERDES_COM_ATB_SEL2			0x0004 | ||||
| #define QSERDES_COM_FREQ_UPDATE			0x0008 | ||||
| #define QSERDES_COM_BG_TIMER			0x000C | ||||
| #define QSERDES_COM_SSC_EN_CENTER		0x0010 | ||||
| #define QSERDES_COM_SSC_ADJ_PER1		0x0014 | ||||
| #define QSERDES_COM_SSC_ADJ_PER2		0x0018 | ||||
| #define QSERDES_COM_SSC_PER1			0x001C | ||||
| #define QSERDES_COM_SSC_PER2			0x0020 | ||||
| #define QSERDES_COM_SSC_STEP_SIZE1		0x0024 | ||||
| #define QSERDES_COM_SSC_STEP_SIZE2		0x0028 | ||||
| #define QSERDES_COM_POST_DIV			0x002C | ||||
| #define QSERDES_COM_POST_DIV_MUX		0x0030 | ||||
| 
 | ||||
| #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		0x0034 | ||||
| #define QSERDES_COM_BIAS_EN			0x0001 | ||||
| #define QSERDES_COM_BIAS_EN_MUX			0x0002 | ||||
| #define QSERDES_COM_CLKBUF_R_EN			0x0004 | ||||
| #define QSERDES_COM_CLKBUF_L_EN			0x0008 | ||||
| #define QSERDES_COM_EN_SYSCLK_TX_SEL		0x0010 | ||||
| #define QSERDES_COM_CLKBUF_RX_DRIVE_L		0x0020 | ||||
| #define QSERDES_COM_CLKBUF_RX_DRIVE_R		0x0040 | ||||
| 
 | ||||
| #define QSERDES_COM_CLK_ENABLE1			0x0038 | ||||
| #define QSERDES_COM_SYS_CLK_CTRL		0x003C | ||||
| #define QSERDES_COM_SYSCLK_BUF_ENABLE		0x0040 | ||||
| #define QSERDES_COM_PLL_EN			0x0044 | ||||
| #define QSERDES_COM_PLL_IVCO			0x0048 | ||||
| #define QSERDES_COM_CMN_IETRIM			0x004C | ||||
| #define QSERDES_COM_CMN_IPTRIM			0x0050 | ||||
| 
 | ||||
| #define QSERDES_COM_CP_CTRL_MODE0		0x0060 | ||||
| #define QSERDES_COM_CP_CTRL_MODE1		0x0064 | ||||
| #define QSERDES_COM_PLL_RCTRL_MODE0		0x0068 | ||||
| #define QSERDES_COM_PLL_RCTRL_MODE1		0x006C | ||||
| #define QSERDES_COM_PLL_CCTRL_MODE0		0x0070 | ||||
| #define QSERDES_COM_PLL_CCTRL_MODE1		0x0074 | ||||
| #define QSERDES_COM_PLL_CNTRL			0x0078 | ||||
| #define QSERDES_COM_BIAS_EN_CTRL_BY_PSM		0x007C | ||||
| #define QSERDES_COM_SYSCLK_EN_SEL		0x0080 | ||||
| #define QSERDES_COM_CML_SYSCLK_SEL		0x0084 | ||||
| #define QSERDES_COM_RESETSM_CNTRL		0x0088 | ||||
| #define QSERDES_COM_RESETSM_CNTRL2		0x008C | ||||
| #define QSERDES_COM_LOCK_CMP_EN			0x0090 | ||||
| #define QSERDES_COM_LOCK_CMP_CFG		0x0094 | ||||
| #define QSERDES_COM_LOCK_CMP1_MODE0		0x0098 | ||||
| #define QSERDES_COM_LOCK_CMP2_MODE0		0x009C | ||||
| #define QSERDES_COM_LOCK_CMP3_MODE0		0x00A0 | ||||
| 
 | ||||
| #define QSERDES_COM_DEC_START_MODE0		0x00B0 | ||||
| #define QSERDES_COM_DEC_START_MODE1		0x00B4 | ||||
| #define QSERDES_COM_DIV_FRAC_START1_MODE0	0x00B8 | ||||
| #define QSERDES_COM_DIV_FRAC_START2_MODE0	0x00BC | ||||
| #define QSERDES_COM_DIV_FRAC_START3_MODE0	0x00C0 | ||||
| #define QSERDES_COM_DIV_FRAC_START1_MODE1	0x00C4 | ||||
| #define QSERDES_COM_DIV_FRAC_START2_MODE1	0x00C8 | ||||
| #define QSERDES_COM_DIV_FRAC_START3_MODE1	0x00CC | ||||
| #define QSERDES_COM_INTEGLOOP_INITVAL		0x00D0 | ||||
| #define QSERDES_COM_INTEGLOOP_EN		0x00D4 | ||||
| #define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	0x00D8 | ||||
| #define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	0x00DC | ||||
| #define QSERDES_COM_INTEGLOOP_GAIN0_MODE1	0x00E0 | ||||
| #define QSERDES_COM_INTEGLOOP_GAIN1_MODE1	0x00E4 | ||||
| #define QSERDES_COM_VCOCAL_DEADMAN_CTRL		0x00E8 | ||||
| #define QSERDES_COM_VCO_TUNE_CTRL		0x00EC | ||||
| #define QSERDES_COM_VCO_TUNE_MAP		0x00F0 | ||||
| 
 | ||||
| #define QSERDES_COM_CMN_STATUS			0x0124 | ||||
| #define QSERDES_COM_RESET_SM_STATUS		0x0128 | ||||
| 
 | ||||
| #define QSERDES_COM_CLK_SEL			0x0138 | ||||
| #define QSERDES_COM_HSCLK_SEL			0x013C | ||||
| 
 | ||||
| #define QSERDES_COM_CORECLK_DIV_MODE0		0x0148 | ||||
| 
 | ||||
| #define QSERDES_COM_SW_RESET			0x0150 | ||||
| #define QSERDES_COM_CORE_CLK_EN			0x0154 | ||||
| #define QSERDES_COM_C_READY_STATUS		0x0158 | ||||
| #define QSERDES_COM_CMN_CONFIG			0x015C | ||||
| 
 | ||||
| #define QSERDES_COM_SVS_MODE_CLK_SEL		0x0164 | ||||
| 
 | ||||
| /* DP MMSS_CC registers */ | ||||
| #define MMSS_DP_LINK_CMD_RCGR			(0x0138) | ||||
| #define MMSS_DP_LINK_CFG_RCGR			(0x013C) | ||||
| #define MMSS_DP_PIXEL_M				(0x01B4) | ||||
| #define MMSS_DP_PIXEL_N				(0x01B8) | ||||
| 
 | ||||
| /* DP HDCP 1.3 registers */ | ||||
| #define DP_HDCP_CTRL                                   (0x0A0) | ||||
| #define DP_HDCP_STATUS                                 (0x0A4) | ||||
| #define DP_HDCP_SW_UPPER_AKSV                          (0x098) | ||||
| #define DP_HDCP_SW_LOWER_AKSV                          (0x09C) | ||||
| #define DP_HDCP_ENTROPY_CTRL0                          (0x350) | ||||
| #define DP_HDCP_ENTROPY_CTRL1                          (0x35C) | ||||
| #define DP_HDCP_SHA_STATUS                             (0x0C8) | ||||
| #define DP_HDCP_RCVPORT_DATA2_0                        (0x0B0) | ||||
| #define DP_HDCP_RCVPORT_DATA3                          (0x0A4) | ||||
| #define DP_HDCP_RCVPORT_DATA4                          (0x0A8) | ||||
| #define DP_HDCP_RCVPORT_DATA5                          (0x0C0) | ||||
| #define DP_HDCP_RCVPORT_DATA6                          (0x0C4) | ||||
| 
 | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL           (0x024) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_DATA           (0x028) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0      (0x004) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1      (0x008) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7      (0x00C) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8      (0x010) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9      (0x014) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10     (0x018) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11     (0x01C) | ||||
| #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12     (0x020) | ||||
| 
 | ||||
| /* USB3 DP COM registers */ | ||||
| #define REG_USB3_DP_COM_RESET_OVRD_CTRL			(0x1C) | ||||
| #define USB3_DP_COM_OVRD_CTRL_SW_DPPHY_RESET		(0x01) | ||||
| #define USB3_DP_COM_OVRD_CTRL_SW_DPPHY_RESET_MUX	(0x02) | ||||
| #define USB3_DP_COM_OVRD_CTRL_SW_USB3PHY_RESET		(0x04) | ||||
| #define USB3_DP_COM_OVRD_CTRL_SW_USB3PHY_RESET_MUX	(0x08) | ||||
| 
 | ||||
| #define REG_USB3_DP_COM_PHY_MODE_CTRL			(0x00) | ||||
| #define USB3_DP_COM_PHY_MODE_DP				(0x03) | ||||
| 
 | ||||
| #define REG_USB3_DP_COM_SW_RESET			(0x04) | ||||
| #define USB3_DP_COM_SW_RESET_SET			(0x01) | ||||
| 
 | ||||
| #define REG_USB3_DP_COM_TYPEC_CTRL			(0x10) | ||||
| #define USB3_DP_COM_TYPEC_CTRL_PORTSEL			(0x01) | ||||
| #define USB3_DP_COM_TYPEC_CTRL_PORTSEL_MUX		(0x02) | ||||
| 
 | ||||
| #define REG_USB3_DP_COM_SWI_CTRL			(0x0c) | ||||
| 
 | ||||
| #define REG_USB3_DP_COM_POWER_DOWN_CTRL			(0x08) | ||||
| #define USB3_DP_COM_POWER_DOWN_CTRL_SW_PWRDN		(0x01) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #endif /* _DP_REG_H_ */ | ||||
| @ -1352,6 +1352,7 @@ static int __init msm_drm_register(void) | ||||
| 	msm_dsi_register(); | ||||
| 	msm_edp_register(); | ||||
| 	msm_hdmi_register(); | ||||
| 	msm_dp_register(); | ||||
| 	adreno_register(); | ||||
| 	return platform_driver_register(&msm_platform_driver); | ||||
| } | ||||
| @ -1360,6 +1361,7 @@ static void __exit msm_drm_unregister(void) | ||||
| { | ||||
| 	DBG("fini"); | ||||
| 	platform_driver_unregister(&msm_platform_driver); | ||||
| 	msm_dp_unregister(); | ||||
| 	msm_hdmi_unregister(); | ||||
| 	adreno_unregister(); | ||||
| 	msm_edp_unregister(); | ||||
|  | ||||
| @ -160,6 +160,8 @@ struct msm_drm_private { | ||||
| 	/* DSI is shared by mdp4 and mdp5 */ | ||||
| 	struct msm_dsi *dsi[2]; | ||||
| 
 | ||||
| 	struct msm_dp *dp; | ||||
| 
 | ||||
| 	/* when we have more than one 'msm_gpu' these need to be an array: */ | ||||
| 	struct msm_gpu *gpu; | ||||
| 	struct msm_file_private *lastctx; | ||||
| @ -383,6 +385,49 @@ static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_DRM_MSM_DP | ||||
| int __init msm_dp_register(void); | ||||
| void __exit msm_dp_unregister(void); | ||||
| int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, | ||||
| 			 struct drm_encoder *encoder); | ||||
| int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder); | ||||
| int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder); | ||||
| void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder, | ||||
| 				struct drm_display_mode *mode, | ||||
| 				struct drm_display_mode *adjusted_mode); | ||||
| 
 | ||||
| #else | ||||
| static inline int __init msm_dp_register(void) | ||||
| { | ||||
| 	return -EINVAL; | ||||
| } | ||||
| static inline void __exit msm_dp_unregister(void) | ||||
| { | ||||
| } | ||||
| static inline int msm_dp_modeset_init(struct msm_dp *dp_display, | ||||
| 				       struct drm_device *dev, | ||||
| 				       struct drm_encoder *encoder) | ||||
| { | ||||
| 	return -EINVAL; | ||||
| } | ||||
| static inline int msm_dp_display_enable(struct msm_dp *dp, | ||||
| 					struct drm_encoder *encoder) | ||||
| { | ||||
| 	return -EINVAL; | ||||
| } | ||||
| static inline int msm_dp_display_disable(struct msm_dp *dp, | ||||
| 					struct drm_encoder *encoder) | ||||
| { | ||||
| 	return -EINVAL; | ||||
| } | ||||
| static inline void msm_dp_display_mode_set(struct msm_dp *dp, | ||||
| 				struct drm_encoder *encoder, | ||||
| 				struct drm_display_mode *mode, | ||||
| 				struct drm_display_mode *adjusted_mode) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void __init msm_mdp_register(void); | ||||
| void __exit msm_mdp_unregister(void); | ||||
| void __init msm_dpu_register(void); | ||||
| @ -403,8 +448,9 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); | ||||
| #else | ||||
| static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } | ||||
| __printf(3, 4) | ||||
| static inline void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit, | ||||
| 		const char *fmt, ...) {} | ||||
| static inline void msm_rd_dump_submit(struct msm_rd_state *rd, | ||||
| 			struct msm_gem_submit *submit, | ||||
| 			const char *fmt, ...) {} | ||||
| static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {} | ||||
| static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {} | ||||
| #endif | ||||
| @ -424,7 +470,8 @@ struct msm_gpu_submitqueue; | ||||
| int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx); | ||||
| struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, | ||||
| 		u32 id); | ||||
| int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, | ||||
| int msm_submitqueue_create(struct drm_device *drm, | ||||
| 		struct msm_file_private *ctx, | ||||
| 		u32 prio, u32 flags, u32 *id); | ||||
| int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx, | ||||
| 		struct drm_msm_submitqueue_query *args); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user