Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 110b6748 authored by Abhinav Kumar's avatar Abhinav Kumar
Browse files

drm/msm: clean up DDC handling for SDE HDMI driver



Separate out the DRM HDMI utility functions into a separate
module.

Make the DRM HDMI utility functions support self retry where
they shall try for an arbitrary number of times on failure
otherwise let the client call the API to retry the number of
times as warranted.

Add a SDE HDMI utility file which shall invoke the upstream
functions in a manner as required to maintain the functionality
of legacy drivers.

Change-Id: I64af3f997a16b2d9358ea867585aa12772d22599
Signed-off-by: default avatarAbhinav Kumar <abhinavk@codeaurora.org>
parent 7c1f3156
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ msm_drm-y := \
	hdmi/hdmi_connector.o \
	hdmi/hdmi_hdcp.o \
	hdmi/hdmi_i2c.o \
	hdmi/hdmi_util.o \
	hdmi/hdmi_phy_8960.o \
	hdmi/hdmi_phy_8x60.o \
	hdmi/hdmi_phy_8x74.o \
@@ -101,6 +102,7 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
				dsi-staging/dsi_display_test.o

msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
	hdmi-staging/sde_hdmi_util.o \
	hdmi-staging/sde_hdmi.o \
	hdmi-staging/sde_hdmi_bridge.o \
	hdmi-staging/sde_hdmi_audio.o \
+14 −80
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include "msm_drv.h"
#include "sde_hdmi.h"
#include "sde_hdmi_regs.h"
#include "hdmi.h"

static DEFINE_MUTEX(sde_hdmi_list_lock);
static LIST_HEAD(sde_hdmi_list);
@@ -917,6 +918,12 @@ static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display)
	else
		cec_notifier_set_phys_addr(display->notifier,
			CEC_PHYS_ADDR_INVALID);

}

static void _sde_hdmi_init_ddc(struct sde_hdmi *display, struct hdmi *hdmi)
{
	display->ddc_ctrl.io = &display->io[HDMI_TX_CORE_IO];
}

static void _sde_hdmi_map_regs(struct sde_hdmi *display, struct hdmi *hdmi)
@@ -1200,84 +1207,8 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
			power_on ? "Enable" : "Disable", ctrl);
}

int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
					  u8 *data, u16 data_len)
{
	int rc;
	int retry = 5;
	struct i2c_msg msgs[] = {
		{
			.addr   = addr >> 1,
			.flags  = 0,
			.len    = 1,
			.buf    = &offset,
		}, {
			.addr   = addr >> 1,
			.flags  = I2C_M_RD,
			.len    = data_len,
			.buf    = data,
		}
	};

	SDE_HDMI_DEBUG("Start DDC read");
 retry:
	rc = i2c_transfer(hdmi->i2c, msgs, 2);

	retry--;
	if (rc == 2)
		rc = 0;
	else if (retry > 0)
		goto retry;
	else
		rc = -EIO;

	SDE_HDMI_DEBUG("End DDC read %d", rc);

	return rc;
}

#define DDC_WRITE_MAX_BYTE_NUM 32

int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
					   u8 *data, u16 data_len)
{
	int rc;
	int retry = 10;
	u8 buf[DDC_WRITE_MAX_BYTE_NUM];
	struct i2c_msg msgs[] = {
		{
			.addr   = addr >> 1,
			.flags  = 0,
			.len    = 1,
		}
	};

	SDE_HDMI_DEBUG("Start DDC write");
	if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) {
		SDE_ERROR("%s: write size too big\n", __func__);
		return -ERANGE;
	}

	buf[0] = offset;
	memcpy(&buf[1], data, data_len);
	msgs[0].buf = buf;
	msgs[0].len = data_len + 1;
 retry:
	rc = i2c_transfer(hdmi->i2c, msgs, 1);

	retry--;
	if (rc == 1)
		rc = 0;
	else if (retry > 0)
		goto retry;
	else
		rc = -EIO;

	SDE_HDMI_DEBUG("End DDC write %d", rc);

	return rc;
}

int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
{
	int rc = 0;
@@ -1334,7 +1265,8 @@ int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
		break;
	}

	rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len);
	rc = hdmi_ddc_read(hdmi, dev_addr, offset, data_buf,
					   data_len, true);
	if (rc) {
		SDE_ERROR("DDC Read failed for %d\n", data_type);
		return rc;
@@ -1406,8 +1338,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
		dev_addr = 0xA8;
		data_len = 1;
		offset = HDMI_SCDC_TMDS_CONFIG;
		rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
							   data_len);
		rc = hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
						   data_len, true);
		if (rc) {
			SDE_ERROR("scdc read failed\n");
			return rc;
@@ -1431,7 +1363,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
		return -EINVAL;
	}

	rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len);
	rc = hdmi_ddc_write(hdmi, dev_addr, offset, data_buf,
						data_len, true);
	if (rc) {
		SDE_ERROR("DDC Read failed for %d\n", data_type);
		return rc;
@@ -1861,6 +1794,7 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
	display->drm_dev = drm;

	_sde_hdmi_map_regs(display, priv->hdmi);
	_sde_hdmi_init_ddc(display, priv->hdmi);

	mutex_unlock(&display->display_lock);
	return rc;
+2 −26
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "sde_connector.h"
#include "msm_drv.h"
#include "sde_edid_parser.h"
#include "sde_hdmi_util.h"

#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...)   SDE_ERROR(fmt, ##args)
@@ -130,6 +131,7 @@ struct sde_hdmi {
	u32 max_pclk_khz;
	bool hdcp1_use_sw_keys;
	u32 hdcp14_present;
	struct sde_hdmi_tx_ddc_ctrl ddc_ctrl;
	struct work_struct hpd_work;
	bool codec_ready;
	bool client_notify_pending;
@@ -337,32 +339,6 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi);
 */
void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on);

/**
 * sde_hdmi_ddc_read() - common hdmi ddc read API.
 * @hdmi:          Handle to the hdmi.
 * @addr:          Command address.
 * @offset:        Command offset.
 * @data:          Data buffer for read back.
 * @data_len:      Data buffer length.
 *
 * Return: error code.
 */
int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
					  u8 *data, u16 data_len);

/**
 * sde_hdmi_ddc_write() - common hdmi ddc write API.
 * @hdmi:          Handle to the hdmi.
 * @addr:          Command address.
 * @offset:        Command offset.
 * @data:          Data buffer for write.
 * @data_len:      Data buffer length.
 *
 * Return: error code.
 */
int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
					   u8 *data, u16 data_len);

/**
 * sde_hdmi_scdc_read() - hdmi 2.0 ddc read API.
 * @hdmi:          Handle to the hdmi.
+200 −0
Original line number Diff line number Diff line
/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/iopoll.h>
#include <linux/types.h>
#include <linux/switch.h>
#include <linux/gcd.h>

#include "drm_edid.h"
#include "sde_kms.h"
#include "sde_hdmi.h"
#include "sde_hdmi_regs.h"
#include "hdmi.h"

static int sde_hdmi_ddc_read_retry(struct sde_hdmi *display)
{
	int status;
	int busy_wait_us;
	struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct sde_hdmi_tx_ddc_data *ddc_data;
	struct hdmi *hdmi;

	if (!display) {
		SDE_ERROR("invalid input\n");
		return -EINVAL;
	}

	hdmi = display->ctrl.ctrl;
	ddc_ctrl = &display->ddc_ctrl;
	ddc_data = &ddc_ctrl->ddc_data;

	if (!ddc_data) {
		SDE_ERROR("invalid input\n");
		return -EINVAL;
	}

	if (!ddc_data->data_buf) {
		status = -EINVAL;
		SDE_ERROR("%s: invalid buf\n", ddc_data->what);
		goto error;
	}

	if (ddc_data->retry < 0) {
		SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
		status = -EINVAL;
		goto error;
	}

	do {
		if (ddc_data->hard_timeout) {
			HDMI_UTIL_DEBUG("using hard_timeout %dms\n",
					 ddc_data->hard_timeout);

			busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
			hdmi->use_hard_timeout = true;
			hdmi->busy_wait_us = busy_wait_us;
		}

		/* Calling upstream ddc read method */
		status = hdmi_ddc_read(hdmi, ddc_data->dev_addr,
			ddc_data->offset,
			ddc_data->data_buf, ddc_data->request_len,
			false);

		if (ddc_data->hard_timeout)
			ddc_data->timeout_left = hdmi->timeout_count;


		if (ddc_data->hard_timeout && !hdmi->timeout_count) {
			HDMI_UTIL_DEBUG("%s: timedout\n", ddc_data->what);
			status = -ETIMEDOUT;
		}

	} while (status && ddc_data->retry--);

	if (status) {
		HDMI_UTIL_ERROR("%s: failed status = %d\n",
						ddc_data->what, status);
		goto error;
	}

	HDMI_UTIL_DEBUG("%s: success\n",  ddc_data->what);

error:
	return status;
} /* sde_hdmi_ddc_read_retry */

int sde_hdmi_ddc_read(void *cb_data)
{
	int rc = 0;
	int retry;
	struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct sde_hdmi_tx_ddc_data *ddc_data;
	struct sde_hdmi *display = (struct sde_hdmi *)cb_data;

	if (!display) {
		SDE_ERROR("invalid ddc ctrl\n");
		return -EINVAL;
	}

	ddc_ctrl = &display->ddc_ctrl;
	ddc_data = &ddc_ctrl->ddc_data;
	retry = ddc_data->retry;

	rc = sde_hdmi_ddc_read_retry(display);
	if (!rc)
		return rc;

	if (ddc_data->retry_align) {
		ddc_data->retry = retry;

		ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32);
		rc = sde_hdmi_ddc_read_retry(display);
	}

	return rc;
} /* hdmi_ddc_read */

int sde_hdmi_ddc_write(void *cb_data)
{
	int status;
	struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct sde_hdmi_tx_ddc_data *ddc_data;
	int busy_wait_us;
	struct hdmi *hdmi;
	struct sde_hdmi *display = (struct sde_hdmi *)cb_data;

	if (!display) {
		SDE_ERROR("invalid input\n");
		return -EINVAL;
	}

	hdmi = display->ctrl.ctrl;
	ddc_ctrl = &display->ddc_ctrl;

	ddc_data = &ddc_ctrl->ddc_data;

	if (!ddc_data) {
		SDE_ERROR("invalid input\n");
		return -EINVAL;
	}

	if (!ddc_data->data_buf) {
		status = -EINVAL;
		SDE_ERROR("%s: invalid buf\n", ddc_data->what);
		goto error;
	}

	if (ddc_data->retry < 0) {
		SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
		status = -EINVAL;
		goto error;
	}

	do {
		if (ddc_data->hard_timeout) {
			busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
			hdmi->use_hard_timeout = true;
			hdmi->busy_wait_us = busy_wait_us;
		}

		status = hdmi_ddc_write(hdmi,
			ddc_data->dev_addr, ddc_data->offset,
			ddc_data->data_buf, ddc_data->data_len,
			false);

		if (ddc_data->hard_timeout)
			ddc_data->timeout_left = hdmi->timeout_count;

		if (ddc_data->hard_timeout && !hdmi->timeout_count) {
			HDMI_UTIL_ERROR("%s timout\n",  ddc_data->what);
			status = -ETIMEDOUT;
		}

	} while (status && ddc_data->retry--);

	if (status) {
		HDMI_UTIL_ERROR("%s: failed status = %d\n",
						ddc_data->what, status);
		goto error;
	}

	HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what);
error:
	return status;
} /* hdmi_ddc_write */
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#ifndef _SDE_HDMI_UTIL_H_
#define _SDE_HDMI_UTIL_H_

#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/msm_ext_display.h>

#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include "hdmi.h"
#include "sde_kms.h"
#include "sde_connector.h"
#include "msm_drv.h"
#include "sde_hdmi_regs.h"

#ifdef HDMI_UTIL_DEBUG_ENABLE
#define HDMI_UTIL_DEBUG(fmt, args...)   SDE_ERROR(fmt, ##args)
#else
#define HDMI_UTIL_DEBUG(fmt, args...)   SDE_DEBUG(fmt, ##args)
#endif

#define HDMI_UTIL_ERROR(fmt, args...)   SDE_ERROR(fmt, ##args)

struct sde_hdmi_tx_ddc_data {
	char *what;
	u8 *data_buf;
	u32 data_len;
	u32 dev_addr;
	u32 offset;
	u32 request_len;
	u32 retry_align;
	u32 hard_timeout;
	u32 timeout_left;
	int retry;
};

struct sde_hdmi_tx_ddc_ctrl {
	atomic_t rxstatus_busy_wait_done;
	struct dss_io_data *io;
	struct sde_hdmi_tx_ddc_data ddc_data;
};

/* DDC */
int sde_hdmi_ddc_write(void *cb_data);
int sde_hdmi_ddc_read(void *cb_data);

#endif /* _SDE_HDMI_UTIL_H_ */
Loading