brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · 00dfafb Raw
578 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.4 */5 6#include <linux/delay.h>7#include <linux/phy/phy.h>8#include <drm/drm_print.h>9 10#include "dp_reg.h"11#include "dp_aux.h"12 13enum msm_dp_aux_err {14	DP_AUX_ERR_NONE,15	DP_AUX_ERR_ADDR,16	DP_AUX_ERR_TOUT,17	DP_AUX_ERR_NACK,18	DP_AUX_ERR_DEFER,19	DP_AUX_ERR_NACK_DEFER,20	DP_AUX_ERR_PHY,21};22 23struct dp_aux_private {24	struct device *dev;25	struct dp_catalog *catalog;26 27	struct phy *phy;28 29	struct mutex mutex;30	struct completion comp;31 32	enum msm_dp_aux_err aux_error_num;33	u32 retry_cnt;34	bool cmd_busy;35	bool native;36	bool read;37	bool no_send_addr;38	bool no_send_stop;39	bool initted;40	bool is_edp;41	bool enable_xfers;42	u32 offset;43	u32 segment;44 45	struct drm_dp_aux dp_aux;46};47 48#define MAX_AUX_RETRIES			549 50static ssize_t dp_aux_write(struct dp_aux_private *aux,51			struct drm_dp_aux_msg *msg)52{53	u8 data[4];54	u32 reg;55	ssize_t len;56	u8 *msgdata = msg->buffer;57	int const AUX_CMD_FIFO_LEN = 128;58	int i = 0;59 60	if (aux->read)61		len = 0;62	else63		len = msg->size;64 65	/*66	 * cmd fifo only has depth of 144 bytes67	 * limit buf length to 128 bytes here68	 */69	if (len > AUX_CMD_FIFO_LEN - 4) {70		DRM_ERROR("buf size greater than allowed size of 128 bytes\n");71		return -EINVAL;72	}73 74	/* Pack cmd and write to HW */75	data[0] = (msg->address >> 16) & 0xf;	/* addr[19:16] */76	if (aux->read)77		data[0] |=  BIT(4);		/* R/W */78 79	data[1] = msg->address >> 8;		/* addr[15:8] */80	data[2] = msg->address;			/* addr[7:0] */81	data[3] = msg->size - 1;		/* len[7:0] */82 83	for (i = 0; i < len + 4; i++) {84		reg = (i < 4) ? data[i] : msgdata[i - 4];85		reg <<= DP_AUX_DATA_OFFSET;86		reg &= DP_AUX_DATA_MASK;87		reg |= DP_AUX_DATA_WRITE;88		/* index = 0, write */89		if (i == 0)90			reg |= DP_AUX_DATA_INDEX_WRITE;91		dp_catalog_aux_write_data(aux->catalog, reg);92	}93 94	dp_catalog_aux_clear_trans(aux->catalog, false);95	dp_catalog_aux_clear_hw_interrupts(aux->catalog);96 97	reg = 0; /* Transaction number == 1 */98	if (!aux->native) { /* i2c */99		reg |= DP_AUX_TRANS_CTRL_I2C;100 101		if (aux->no_send_addr)102			reg |= DP_AUX_TRANS_CTRL_NO_SEND_ADDR;103 104		if (aux->no_send_stop)105			reg |= DP_AUX_TRANS_CTRL_NO_SEND_STOP;106	}107 108	reg |= DP_AUX_TRANS_CTRL_GO;109	dp_catalog_aux_write_trans(aux->catalog, reg);110 111	return len;112}113 114static ssize_t dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,115			      struct drm_dp_aux_msg *msg)116{117	ssize_t ret;118	unsigned long time_left;119 120	reinit_completion(&aux->comp);121 122	ret = dp_aux_write(aux, msg);123	if (ret < 0)124		return ret;125 126	time_left = wait_for_completion_timeout(&aux->comp,127						msecs_to_jiffies(250));128	if (!time_left)129		return -ETIMEDOUT;130 131	return ret;132}133 134static ssize_t dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,135		struct drm_dp_aux_msg *msg)136{137	u32 data;138	u8 *dp;139	u32 i, actual_i;140	u32 len = msg->size;141 142	dp_catalog_aux_clear_trans(aux->catalog, true);143 144	data = DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */145	data |= DP_AUX_DATA_READ;  /* read */146 147	dp_catalog_aux_write_data(aux->catalog, data);148 149	dp = msg->buffer;150 151	/* discard first byte */152	data = dp_catalog_aux_read_data(aux->catalog);153 154	for (i = 0; i < len; i++) {155		data = dp_catalog_aux_read_data(aux->catalog);156		*dp++ = (u8)((data >> DP_AUX_DATA_OFFSET) & 0xff);157 158		actual_i = (data >> DP_AUX_DATA_INDEX_OFFSET) & 0xFF;159		if (i != actual_i)160			break;161	}162 163	return i;164}165 166static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,167					     struct drm_dp_aux_msg *input_msg)168{169	u32 edid_address = 0x50;170	u32 segment_address = 0x30;171	bool i2c_read = input_msg->request &172		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);173	u8 *data;174 175	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&176		(input_msg->address != segment_address)))177		return;178 179 180	data = input_msg->buffer;181	if (input_msg->address == segment_address)182		aux->segment = *data;183	else184		aux->offset = *data;185}186 187/**188 * dp_aux_transfer_helper() - helper function for EDID read transactions189 *190 * @aux: DP AUX private structure191 * @input_msg: input message from DRM upstream APIs192 * @send_seg: send the segment to sink193 *194 * return: void195 *196 * This helper function is used to fix EDID reads for non-compliant197 * sinks that do not handle the i2c middle-of-transaction flag correctly.198 */199static void dp_aux_transfer_helper(struct dp_aux_private *aux,200				   struct drm_dp_aux_msg *input_msg,201				   bool send_seg)202{203	struct drm_dp_aux_msg helper_msg;204	u32 message_size = 0x10;205	u32 segment_address = 0x30;206	u32 const edid_block_length = 0x80;207	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;208	bool i2c_read = input_msg->request &209		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);210 211	if (!i2c_mot || !i2c_read || (input_msg->size == 0))212		return;213 214	/*215	 * Sending the segment value and EDID offset will be performed216	 * from the DRM upstream EDID driver for each block. Avoid217	 * duplicate AUX transactions related to this while reading the218	 * first 16 bytes of each block.219	 */220	if (!(aux->offset % edid_block_length) || !send_seg)221		goto end;222 223	aux->read = false;224	aux->cmd_busy = true;225	aux->no_send_addr = true;226	aux->no_send_stop = true;227 228	/*229	 * Send the segment address for every i2c read in which the230	 * middle-of-tranaction flag is set. This is required to support EDID231	 * reads of more than 2 blocks as the segment address is reset to 0232	 * since we are overriding the middle-of-transaction flag for read233	 * transactions.234	 */235 236	if (aux->segment) {237		memset(&helper_msg, 0, sizeof(helper_msg));238		helper_msg.address = segment_address;239		helper_msg.buffer = &aux->segment;240		helper_msg.size = 1;241		dp_aux_cmd_fifo_tx(aux, &helper_msg);242	}243 244	/*245	 * Send the offset address for every i2c read in which the246	 * middle-of-transaction flag is set. This will ensure that the sink247	 * will update its read pointer and return the correct portion of the248	 * EDID buffer in the subsequent i2c read trasntion triggered in the249	 * native AUX transfer function.250	 */251	memset(&helper_msg, 0, sizeof(helper_msg));252	helper_msg.address = input_msg->address;253	helper_msg.buffer = &aux->offset;254	helper_msg.size = 1;255	dp_aux_cmd_fifo_tx(aux, &helper_msg);256 257end:258	aux->offset += message_size;259	if (aux->offset == 0x80 || aux->offset == 0x100)260		aux->segment = 0x0; /* reset segment at end of block */261}262 263/*264 * This function does the real job to process an AUX transaction.265 * It will call aux_reset() function to reset the AUX channel,266 * if the waiting is timeout.267 */268static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux,269			       struct drm_dp_aux_msg *msg)270{271	ssize_t ret;272	int const aux_cmd_native_max = 16;273	int const aux_cmd_i2c_max = 128;274	struct dp_aux_private *aux;275 276	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);277 278	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);279 280	/* Ignore address only message */281	if (msg->size == 0 || !msg->buffer) {282		msg->reply = aux->native ?283			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;284		return msg->size;285	}286 287	/* msg sanity check */288	if ((aux->native && msg->size > aux_cmd_native_max) ||289	    msg->size > aux_cmd_i2c_max) {290		DRM_ERROR("%s: invalid msg: size(%zu), request(%x)\n",291			__func__, msg->size, msg->request);292		return -EINVAL;293	}294 295	ret = pm_runtime_resume_and_get(dp_aux->dev);296	if (ret)297		return  ret;298 299	mutex_lock(&aux->mutex);300	if (!aux->initted) {301		ret = -EIO;302		goto exit;303	}304 305	/*306	 * If we're using DP and an external display isn't connected then the307	 * transfer won't succeed. Return right away. If we don't do this we308	 * can end up with long timeouts if someone tries to access the DP AUX309	 * character device when no DP device is connected.310	 */311	if (!aux->is_edp && !aux->enable_xfers) {312		ret = -ENXIO;313		goto exit;314	}315 316	dp_aux_update_offset_and_segment(aux, msg);317	dp_aux_transfer_helper(aux, msg, true);318 319	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);320	aux->cmd_busy = true;321 322	if (aux->read) {323		aux->no_send_addr = true;324		aux->no_send_stop = false;325	} else {326		aux->no_send_addr = true;327		aux->no_send_stop = true;328	}329 330	ret = dp_aux_cmd_fifo_tx(aux, msg);331	if (ret < 0) {332		if (aux->native) {333			aux->retry_cnt++;334			if (!(aux->retry_cnt % MAX_AUX_RETRIES))335				phy_calibrate(aux->phy);336		}337		/* reset aux if link is in connected state */338		if (dp_catalog_link_is_connected(aux->catalog))339			dp_catalog_aux_reset(aux->catalog);340	} else {341		aux->retry_cnt = 0;342		switch (aux->aux_error_num) {343		case DP_AUX_ERR_NONE:344			if (aux->read)345				ret = dp_aux_cmd_fifo_rx(aux, msg);346			msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;347			break;348		case DP_AUX_ERR_DEFER:349			msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;350			break;351		case DP_AUX_ERR_PHY:352		case DP_AUX_ERR_ADDR:353		case DP_AUX_ERR_NACK:354		case DP_AUX_ERR_NACK_DEFER:355			msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_NACK : DP_AUX_I2C_REPLY_NACK;356			break;357		case DP_AUX_ERR_TOUT:358			ret = -ETIMEDOUT;359			break;360		}361	}362 363	aux->cmd_busy = false;364 365exit:366	mutex_unlock(&aux->mutex);367	pm_runtime_put_sync(dp_aux->dev);368 369	return ret;370}371 372irqreturn_t dp_aux_isr(struct drm_dp_aux *dp_aux)373{374	u32 isr;375	struct dp_aux_private *aux;376 377	if (!dp_aux) {378		DRM_ERROR("invalid input\n");379		return IRQ_NONE;380	}381 382	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);383 384	isr = dp_catalog_aux_get_irq(aux->catalog);385 386	/* no interrupts pending, return immediately */387	if (!isr)388		return IRQ_NONE;389 390	if (!aux->cmd_busy) {391		DRM_ERROR("Unexpected DP AUX IRQ %#010x when not busy\n", isr);392		return IRQ_NONE;393	}394 395	/*396	 * The logic below assumes only one error bit is set (other than "done"397	 * which can apparently be set at the same time as some of the other398	 * bits). Warn if more than one get set so we know we need to improve399	 * the logic.400	 */401	if (hweight32(isr & ~DP_INTR_AUX_XFER_DONE) > 1)402		DRM_WARN("Some DP AUX interrupts unhandled: %#010x\n", isr);403 404	if (isr & DP_INTR_AUX_ERROR) {405		aux->aux_error_num = DP_AUX_ERR_PHY;406		dp_catalog_aux_clear_hw_interrupts(aux->catalog);407	} else if (isr & DP_INTR_NACK_DEFER) {408		aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;409	} else if (isr & DP_INTR_WRONG_ADDR) {410		aux->aux_error_num = DP_AUX_ERR_ADDR;411	} else if (isr & DP_INTR_TIMEOUT) {412		aux->aux_error_num = DP_AUX_ERR_TOUT;413	} else if (!aux->native && (isr & DP_INTR_I2C_NACK)) {414		aux->aux_error_num = DP_AUX_ERR_NACK;415	} else if (!aux->native && (isr & DP_INTR_I2C_DEFER)) {416		if (isr & DP_INTR_AUX_XFER_DONE)417			aux->aux_error_num = DP_AUX_ERR_NACK;418		else419			aux->aux_error_num = DP_AUX_ERR_DEFER;420	} else if (isr & DP_INTR_AUX_XFER_DONE) {421		aux->aux_error_num = DP_AUX_ERR_NONE;422	} else {423		DRM_WARN("Unexpected interrupt: %#010x\n", isr);424		return IRQ_NONE;425	}426 427	complete(&aux->comp);428 429	return IRQ_HANDLED;430}431 432void dp_aux_enable_xfers(struct drm_dp_aux *dp_aux, bool enabled)433{434	struct dp_aux_private *aux;435 436	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);437	aux->enable_xfers = enabled;438}439 440void dp_aux_reconfig(struct drm_dp_aux *dp_aux)441{442	struct dp_aux_private *aux;443 444	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);445 446	phy_calibrate(aux->phy);447	dp_catalog_aux_reset(aux->catalog);448}449 450void dp_aux_init(struct drm_dp_aux *dp_aux)451{452	struct dp_aux_private *aux;453 454	if (!dp_aux) {455		DRM_ERROR("invalid input\n");456		return;457	}458 459	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);460 461	mutex_lock(&aux->mutex);462 463	dp_catalog_aux_enable(aux->catalog, true);464	aux->retry_cnt = 0;465	aux->initted = true;466 467	mutex_unlock(&aux->mutex);468}469 470void dp_aux_deinit(struct drm_dp_aux *dp_aux)471{472	struct dp_aux_private *aux;473 474	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);475 476	mutex_lock(&aux->mutex);477 478	aux->initted = false;479	dp_catalog_aux_enable(aux->catalog, false);480 481	mutex_unlock(&aux->mutex);482}483 484int dp_aux_register(struct drm_dp_aux *dp_aux)485{486	int ret;487 488	if (!dp_aux) {489		DRM_ERROR("invalid input\n");490		return -EINVAL;491	}492 493	ret = drm_dp_aux_register(dp_aux);494	if (ret) {495		DRM_ERROR("%s: failed to register drm aux: %d\n", __func__,496				ret);497		return ret;498	}499 500	return 0;501}502 503void dp_aux_unregister(struct drm_dp_aux *dp_aux)504{505	drm_dp_aux_unregister(dp_aux);506}507 508static int dp_wait_hpd_asserted(struct drm_dp_aux *dp_aux,509				 unsigned long wait_us)510{511	int ret;512	struct dp_aux_private *aux;513 514	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);515 516	ret = pm_runtime_resume_and_get(aux->dev);517	if (ret)518		return ret;519 520	ret = dp_catalog_aux_wait_for_hpd_connect_state(aux->catalog, wait_us);521	pm_runtime_put_sync(aux->dev);522 523	return ret;524}525 526struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,527			      struct phy *phy,528			      bool is_edp)529{530	struct dp_aux_private *aux;531 532	if (!catalog) {533		DRM_ERROR("invalid input\n");534		return ERR_PTR(-ENODEV);535	}536 537	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);538	if (!aux)539		return ERR_PTR(-ENOMEM);540 541	init_completion(&aux->comp);542	aux->cmd_busy = false;543	aux->is_edp = is_edp;544	mutex_init(&aux->mutex);545 546	aux->dev = dev;547	aux->catalog = catalog;548	aux->phy = phy;549	aux->retry_cnt = 0;550 551	/*552	 * Use the drm_dp_aux_init() to use the aux adapter553	 * before registering AUX with the DRM device so that554	 * msm eDP panel can be detected by generic_dep_panel_probe().555	 */556	aux->dp_aux.name = "dpu_dp_aux";557	aux->dp_aux.dev = dev;558	aux->dp_aux.transfer = dp_aux_transfer;559	aux->dp_aux.wait_hpd_asserted = dp_wait_hpd_asserted;560	drm_dp_aux_init(&aux->dp_aux);561 562	return &aux->dp_aux;563}564 565void dp_aux_put(struct drm_dp_aux *dp_aux)566{567	struct dp_aux_private *aux;568 569	if (!dp_aux)570		return;571 572	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);573 574	mutex_destroy(&aux->mutex);575 576	devm_kfree(aux->dev, aux);577}578