18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
138c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
148c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
188c2ecf20Sopenharmony_ci#include <linux/reset.h>
198c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "dp.h"
258c2ecf20Sopenharmony_ci#include "dpaux.h"
268c2ecf20Sopenharmony_ci#include "drm.h"
278c2ecf20Sopenharmony_ci#include "trace.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dpaux_lock);
308c2ecf20Sopenharmony_cistatic LIST_HEAD(dpaux_list);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct tegra_dpaux_soc {
338c2ecf20Sopenharmony_ci	unsigned int cmh;
348c2ecf20Sopenharmony_ci	unsigned int drvz;
358c2ecf20Sopenharmony_ci	unsigned int drvi;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct tegra_dpaux {
398c2ecf20Sopenharmony_ci	struct drm_dp_aux aux;
408c2ecf20Sopenharmony_ci	struct device *dev;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	const struct tegra_dpaux_soc *soc;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	void __iomem *regs;
458c2ecf20Sopenharmony_ci	int irq;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	struct tegra_output *output;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	struct reset_control *rst;
508c2ecf20Sopenharmony_ci	struct clk *clk_parent;
518c2ecf20Sopenharmony_ci	struct clk *clk;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	struct regulator *vdd;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	struct completion complete;
568c2ecf20Sopenharmony_ci	struct work_struct work;
578c2ecf20Sopenharmony_ci	struct list_head list;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_PINCONF
608c2ecf20Sopenharmony_ci	struct pinctrl_dev *pinctrl;
618c2ecf20Sopenharmony_ci	struct pinctrl_desc desc;
628c2ecf20Sopenharmony_ci#endif
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return container_of(aux, struct tegra_dpaux, aux);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return container_of(work, struct tegra_dpaux, work);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux,
768c2ecf20Sopenharmony_ci				    unsigned int offset)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	u32 value = readl(dpaux->regs + (offset << 2));
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	trace_dpaux_readl(dpaux->dev, offset, value);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return value;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
868c2ecf20Sopenharmony_ci				      u32 value, unsigned int offset)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	trace_dpaux_writel(dpaux->dev, offset, value);
898c2ecf20Sopenharmony_ci	writel(value, dpaux->regs + (offset << 2));
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
938c2ecf20Sopenharmony_ci				   size_t size)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	size_t i, j;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
988c2ecf20Sopenharmony_ci		size_t num = min_t(size_t, size - i * 4, 4);
998c2ecf20Sopenharmony_ci		u32 value = 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		for (j = 0; j < num; j++)
1028c2ecf20Sopenharmony_ci			value |= buffer[i * 4 + j] << (j * 8);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXDATA_WRITE(i));
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
1098c2ecf20Sopenharmony_ci				  size_t size)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	size_t i, j;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
1148c2ecf20Sopenharmony_ci		size_t num = min_t(size_t, size - i * 4, 4);
1158c2ecf20Sopenharmony_ci		u32 value;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i));
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		for (j = 0; j < num; j++)
1208c2ecf20Sopenharmony_ci			buffer[i * 4 + j] = value >> (j * 8);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
1258c2ecf20Sopenharmony_ci				    struct drm_dp_aux_msg *msg)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(250);
1288c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
1298c2ecf20Sopenharmony_ci	unsigned long status;
1308c2ecf20Sopenharmony_ci	ssize_t ret = 0;
1318c2ecf20Sopenharmony_ci	u8 reply = 0;
1328c2ecf20Sopenharmony_ci	u32 value;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
1358c2ecf20Sopenharmony_ci	if (msg->size > 16)
1368c2ecf20Sopenharmony_ci		return -EINVAL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * Allow zero-sized messages only for I2C, in which case they specify
1408c2ecf20Sopenharmony_ci	 * address-only transactions.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	if (msg->size < 1) {
1438c2ecf20Sopenharmony_ci		switch (msg->request & ~DP_AUX_I2C_MOT) {
1448c2ecf20Sopenharmony_ci		case DP_AUX_I2C_WRITE_STATUS_UPDATE:
1458c2ecf20Sopenharmony_ci		case DP_AUX_I2C_WRITE:
1468c2ecf20Sopenharmony_ci		case DP_AUX_I2C_READ:
1478c2ecf20Sopenharmony_ci			value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
1488c2ecf20Sopenharmony_ci			break;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		default:
1518c2ecf20Sopenharmony_ci			return -EINVAL;
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci	} else {
1548c2ecf20Sopenharmony_ci		/* For non-zero-sized messages, set the CMDLEN field. */
1558c2ecf20Sopenharmony_ci		value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	switch (msg->request & ~DP_AUX_I2C_MOT) {
1598c2ecf20Sopenharmony_ci	case DP_AUX_I2C_WRITE:
1608c2ecf20Sopenharmony_ci		if (msg->request & DP_AUX_I2C_MOT)
1618c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_MOT_WR;
1628c2ecf20Sopenharmony_ci		else
1638c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_I2C_WR;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	case DP_AUX_I2C_READ:
1688c2ecf20Sopenharmony_ci		if (msg->request & DP_AUX_I2C_MOT)
1698c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_MOT_RD;
1708c2ecf20Sopenharmony_ci		else
1718c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_I2C_RD;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
1768c2ecf20Sopenharmony_ci		if (msg->request & DP_AUX_I2C_MOT)
1778c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
1788c2ecf20Sopenharmony_ci		else
1798c2ecf20Sopenharmony_ci			value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	case DP_AUX_NATIVE_WRITE:
1848c2ecf20Sopenharmony_ci		value |= DPAUX_DP_AUXCTL_CMD_AUX_WR;
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	case DP_AUX_NATIVE_READ:
1888c2ecf20Sopenharmony_ci		value |= DPAUX_DP_AUXCTL_CMD_AUX_RD;
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	default:
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
1968c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if ((msg->request & DP_AUX_I2C_READ) == 0) {
1998c2ecf20Sopenharmony_ci		tegra_dpaux_write_fifo(dpaux, msg->buffer, msg->size);
2008c2ecf20Sopenharmony_ci		ret = msg->size;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* start transaction */
2048c2ecf20Sopenharmony_ci	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL);
2058c2ecf20Sopenharmony_ci	value |= DPAUX_DP_AUXCTL_TRANSACTREQ;
2068c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	status = wait_for_completion_timeout(&dpaux->complete, timeout);
2098c2ecf20Sopenharmony_ci	if (!status)
2108c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* read status and clear errors */
2138c2ecf20Sopenharmony_ci	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
2148c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, 0xf00, DPAUX_DP_AUXSTAT);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR)
2178c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) ||
2208c2ecf20Sopenharmony_ci	    (value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) ||
2218c2ecf20Sopenharmony_ci	    (value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR))
2228c2ecf20Sopenharmony_ci		return -EIO;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) {
2258c2ecf20Sopenharmony_ci	case 0x00:
2268c2ecf20Sopenharmony_ci		reply = DP_AUX_NATIVE_REPLY_ACK;
2278c2ecf20Sopenharmony_ci		break;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	case 0x01:
2308c2ecf20Sopenharmony_ci		reply = DP_AUX_NATIVE_REPLY_NACK;
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	case 0x02:
2348c2ecf20Sopenharmony_ci		reply = DP_AUX_NATIVE_REPLY_DEFER;
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	case 0x04:
2388c2ecf20Sopenharmony_ci		reply = DP_AUX_I2C_REPLY_NACK;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	case 0x08:
2428c2ecf20Sopenharmony_ci		reply = DP_AUX_I2C_REPLY_DEFER;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) {
2478c2ecf20Sopenharmony_ci		if (msg->request & DP_AUX_I2C_READ) {
2488c2ecf20Sopenharmony_ci			size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci			/*
2518c2ecf20Sopenharmony_ci			 * There might be a smarter way to do this, but since
2528c2ecf20Sopenharmony_ci			 * the DP helpers will already retry transactions for
2538c2ecf20Sopenharmony_ci			 * an -EBUSY return value, simply reuse that instead.
2548c2ecf20Sopenharmony_ci			 */
2558c2ecf20Sopenharmony_ci			if (count != msg->size) {
2568c2ecf20Sopenharmony_ci				ret = -EBUSY;
2578c2ecf20Sopenharmony_ci				goto out;
2588c2ecf20Sopenharmony_ci			}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci			tegra_dpaux_read_fifo(dpaux, msg->buffer, count);
2618c2ecf20Sopenharmony_ci			ret = count;
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	msg->reply = reply;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciout:
2688c2ecf20Sopenharmony_ci	return ret;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void tegra_dpaux_hotplug(struct work_struct *work)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = work_to_dpaux(work);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (dpaux->output)
2768c2ecf20Sopenharmony_ci		drm_helper_hpd_irq_event(dpaux->output->connector.dev);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic irqreturn_t tegra_dpaux_irq(int irq, void *data)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = data;
2828c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_HANDLED;
2838c2ecf20Sopenharmony_ci	u32 value;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* clear interrupts */
2868c2ecf20Sopenharmony_ci	value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
2878c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT))
2908c2ecf20Sopenharmony_ci		schedule_work(&dpaux->work);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (value & DPAUX_INTR_IRQ_EVENT) {
2938c2ecf20Sopenharmony_ci		/* TODO: handle this */
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (value & DPAUX_INTR_AUX_DONE)
2978c2ecf20Sopenharmony_ci		complete(&dpaux->complete);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return ret;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cienum tegra_dpaux_functions {
3038c2ecf20Sopenharmony_ci	DPAUX_PADCTL_FUNC_AUX,
3048c2ecf20Sopenharmony_ci	DPAUX_PADCTL_FUNC_I2C,
3058c2ecf20Sopenharmony_ci	DPAUX_PADCTL_FUNC_OFF,
3068c2ecf20Sopenharmony_ci};
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void tegra_dpaux_pad_power_up(struct tegra_dpaux *dpaux)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	u32 value;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (function) {
3318c2ecf20Sopenharmony_ci	case DPAUX_PADCTL_FUNC_AUX:
3328c2ecf20Sopenharmony_ci		value = DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) |
3338c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) |
3348c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) |
3358c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
3368c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_MODE_AUX;
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	case DPAUX_PADCTL_FUNC_I2C:
3408c2ecf20Sopenharmony_ci		value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
3418c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
3428c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) |
3438c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) |
3448c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) |
3458c2ecf20Sopenharmony_ci			DPAUX_HYBRID_PADCTL_MODE_I2C;
3468c2ecf20Sopenharmony_ci		break;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	case DPAUX_PADCTL_FUNC_OFF:
3498c2ecf20Sopenharmony_ci		tegra_dpaux_pad_power_down(dpaux);
3508c2ecf20Sopenharmony_ci		return 0;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	default:
3538c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
3578c2ecf20Sopenharmony_ci	tegra_dpaux_pad_power_up(dpaux);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_PINCONF
3638c2ecf20Sopenharmony_cistatic const struct pinctrl_pin_desc tegra_dpaux_pins[] = {
3648c2ecf20Sopenharmony_ci	PINCTRL_PIN(0, "DP_AUX_CHx_P"),
3658c2ecf20Sopenharmony_ci	PINCTRL_PIN(1, "DP_AUX_CHx_N"),
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 };
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const char * const tegra_dpaux_groups[] = {
3718c2ecf20Sopenharmony_ci	"dpaux-io",
3728c2ecf20Sopenharmony_ci};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic const char * const tegra_dpaux_functions[] = {
3758c2ecf20Sopenharmony_ci	"aux",
3768c2ecf20Sopenharmony_ci	"i2c",
3778c2ecf20Sopenharmony_ci	"off",
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	return ARRAY_SIZE(tegra_dpaux_groups);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl,
3868c2ecf20Sopenharmony_ci					      unsigned int group)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	return tegra_dpaux_groups[group];
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl,
3928c2ecf20Sopenharmony_ci				      unsigned group, const unsigned **pins,
3938c2ecf20Sopenharmony_ci				      unsigned *num_pins)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	*pins = tegra_dpaux_pin_numbers;
3968c2ecf20Sopenharmony_ci	*num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct pinctrl_ops tegra_dpaux_pinctrl_ops = {
4028c2ecf20Sopenharmony_ci	.get_groups_count = tegra_dpaux_get_groups_count,
4038c2ecf20Sopenharmony_ci	.get_group_name = tegra_dpaux_get_group_name,
4048c2ecf20Sopenharmony_ci	.get_group_pins = tegra_dpaux_get_group_pins,
4058c2ecf20Sopenharmony_ci	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
4068c2ecf20Sopenharmony_ci	.dt_free_map = pinconf_generic_dt_free_map,
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	return ARRAY_SIZE(tegra_dpaux_functions);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl,
4158c2ecf20Sopenharmony_ci						 unsigned int function)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	return tegra_dpaux_functions[function];
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl,
4218c2ecf20Sopenharmony_ci					   unsigned int function,
4228c2ecf20Sopenharmony_ci					   const char * const **groups,
4238c2ecf20Sopenharmony_ci					   unsigned * const num_groups)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	*num_groups = ARRAY_SIZE(tegra_dpaux_groups);
4268c2ecf20Sopenharmony_ci	*groups = tegra_dpaux_groups;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl,
4328c2ecf20Sopenharmony_ci			       unsigned int function, unsigned int group)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pinctrl);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return tegra_dpaux_pad_config(dpaux, function);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic const struct pinmux_ops tegra_dpaux_pinmux_ops = {
4408c2ecf20Sopenharmony_ci	.get_functions_count = tegra_dpaux_get_functions_count,
4418c2ecf20Sopenharmony_ci	.get_function_name = tegra_dpaux_get_function_name,
4428c2ecf20Sopenharmony_ci	.get_function_groups = tegra_dpaux_get_function_groups,
4438c2ecf20Sopenharmony_ci	.set_mux = tegra_dpaux_set_mux,
4448c2ecf20Sopenharmony_ci};
4458c2ecf20Sopenharmony_ci#endif
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int tegra_dpaux_probe(struct platform_device *pdev)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux;
4508c2ecf20Sopenharmony_ci	struct resource *regs;
4518c2ecf20Sopenharmony_ci	u32 value;
4528c2ecf20Sopenharmony_ci	int err;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL);
4558c2ecf20Sopenharmony_ci	if (!dpaux)
4568c2ecf20Sopenharmony_ci		return -ENOMEM;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	dpaux->soc = of_device_get_match_data(&pdev->dev);
4598c2ecf20Sopenharmony_ci	INIT_WORK(&dpaux->work, tegra_dpaux_hotplug);
4608c2ecf20Sopenharmony_ci	init_completion(&dpaux->complete);
4618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dpaux->list);
4628c2ecf20Sopenharmony_ci	dpaux->dev = &pdev->dev;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4658c2ecf20Sopenharmony_ci	dpaux->regs = devm_ioremap_resource(&pdev->dev, regs);
4668c2ecf20Sopenharmony_ci	if (IS_ERR(dpaux->regs))
4678c2ecf20Sopenharmony_ci		return PTR_ERR(dpaux->regs);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	dpaux->irq = platform_get_irq(pdev, 0);
4708c2ecf20Sopenharmony_ci	if (dpaux->irq < 0)
4718c2ecf20Sopenharmony_ci		return dpaux->irq;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (!pdev->dev.pm_domain) {
4748c2ecf20Sopenharmony_ci		dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
4758c2ecf20Sopenharmony_ci		if (IS_ERR(dpaux->rst)) {
4768c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
4778c2ecf20Sopenharmony_ci				"failed to get reset control: %ld\n",
4788c2ecf20Sopenharmony_ci				PTR_ERR(dpaux->rst));
4798c2ecf20Sopenharmony_ci			return PTR_ERR(dpaux->rst);
4808c2ecf20Sopenharmony_ci		}
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	dpaux->clk = devm_clk_get(&pdev->dev, NULL);
4848c2ecf20Sopenharmony_ci	if (IS_ERR(dpaux->clk)) {
4858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get module clock: %ld\n",
4868c2ecf20Sopenharmony_ci			PTR_ERR(dpaux->clk));
4878c2ecf20Sopenharmony_ci		return PTR_ERR(dpaux->clk);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
4918c2ecf20Sopenharmony_ci	if (IS_ERR(dpaux->clk_parent)) {
4928c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
4938c2ecf20Sopenharmony_ci			PTR_ERR(dpaux->clk_parent));
4948c2ecf20Sopenharmony_ci		return PTR_ERR(dpaux->clk_parent);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	err = clk_set_rate(dpaux->clk_parent, 270000000);
4988c2ecf20Sopenharmony_ci	if (err < 0) {
4998c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
5008c2ecf20Sopenharmony_ci			err);
5018c2ecf20Sopenharmony_ci		return err;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	dpaux->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
5058c2ecf20Sopenharmony_ci	if (IS_ERR(dpaux->vdd)) {
5068c2ecf20Sopenharmony_ci		if (PTR_ERR(dpaux->vdd) != -ENODEV) {
5078c2ecf20Sopenharmony_ci			if (PTR_ERR(dpaux->vdd) != -EPROBE_DEFER)
5088c2ecf20Sopenharmony_ci				dev_err(&pdev->dev,
5098c2ecf20Sopenharmony_ci					"failed to get VDD supply: %ld\n",
5108c2ecf20Sopenharmony_ci					PTR_ERR(dpaux->vdd));
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci			return PTR_ERR(dpaux->vdd);
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		dpaux->vdd = NULL;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dpaux);
5198c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
5208c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
5238c2ecf20Sopenharmony_ci			       dev_name(dpaux->dev), dpaux);
5248c2ecf20Sopenharmony_ci	if (err < 0) {
5258c2ecf20Sopenharmony_ci		dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
5268c2ecf20Sopenharmony_ci			dpaux->irq, err);
5278c2ecf20Sopenharmony_ci		return err;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	disable_irq(dpaux->irq);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	dpaux->aux.transfer = tegra_dpaux_transfer;
5338c2ecf20Sopenharmony_ci	dpaux->aux.dev = &pdev->dev;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	err = drm_dp_aux_register(&dpaux->aux);
5368c2ecf20Sopenharmony_ci	if (err < 0)
5378c2ecf20Sopenharmony_ci		return err;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	/*
5408c2ecf20Sopenharmony_ci	 * Assume that by default the DPAUX/I2C pads will be used for HDMI,
5418c2ecf20Sopenharmony_ci	 * so power them up and configure them in I2C mode.
5428c2ecf20Sopenharmony_ci	 *
5438c2ecf20Sopenharmony_ci	 * The DPAUX code paths reconfigure the pads in AUX mode, but there
5448c2ecf20Sopenharmony_ci	 * is no possibility to perform the I2C mode configuration in the
5458c2ecf20Sopenharmony_ci	 * HDMI path.
5468c2ecf20Sopenharmony_ci	 */
5478c2ecf20Sopenharmony_ci	err = tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_I2C);
5488c2ecf20Sopenharmony_ci	if (err < 0)
5498c2ecf20Sopenharmony_ci		return err;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_PINCONF
5528c2ecf20Sopenharmony_ci	dpaux->desc.name = dev_name(&pdev->dev);
5538c2ecf20Sopenharmony_ci	dpaux->desc.pins = tegra_dpaux_pins;
5548c2ecf20Sopenharmony_ci	dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins);
5558c2ecf20Sopenharmony_ci	dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops;
5568c2ecf20Sopenharmony_ci	dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops;
5578c2ecf20Sopenharmony_ci	dpaux->desc.owner = THIS_MODULE;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux);
5608c2ecf20Sopenharmony_ci	if (IS_ERR(dpaux->pinctrl)) {
5618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register pincontrol\n");
5628c2ecf20Sopenharmony_ci		return PTR_ERR(dpaux->pinctrl);
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci#endif
5658c2ecf20Sopenharmony_ci	/* enable and clear all interrupts */
5668c2ecf20Sopenharmony_ci	value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
5678c2ecf20Sopenharmony_ci		DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
5688c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX);
5698c2ecf20Sopenharmony_ci	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	mutex_lock(&dpaux_lock);
5728c2ecf20Sopenharmony_ci	list_add_tail(&dpaux->list, &dpaux_list);
5738c2ecf20Sopenharmony_ci	mutex_unlock(&dpaux_lock);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int tegra_dpaux_remove(struct platform_device *pdev)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	cancel_work_sync(&dpaux->work);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* make sure pads are powered down when not in use */
5858c2ecf20Sopenharmony_ci	tegra_dpaux_pad_power_down(dpaux);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
5888c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	drm_dp_aux_unregister(&dpaux->aux);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	mutex_lock(&dpaux_lock);
5938c2ecf20Sopenharmony_ci	list_del(&dpaux->list);
5948c2ecf20Sopenharmony_ci	mutex_unlock(&dpaux_lock);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	return 0;
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
6008c2ecf20Sopenharmony_cistatic int tegra_dpaux_suspend(struct device *dev)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
6038c2ecf20Sopenharmony_ci	int err = 0;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (dpaux->rst) {
6068c2ecf20Sopenharmony_ci		err = reset_control_assert(dpaux->rst);
6078c2ecf20Sopenharmony_ci		if (err < 0) {
6088c2ecf20Sopenharmony_ci			dev_err(dev, "failed to assert reset: %d\n", err);
6098c2ecf20Sopenharmony_ci			return err;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	usleep_range(1000, 2000);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	clk_disable_unprepare(dpaux->clk_parent);
6168c2ecf20Sopenharmony_ci	clk_disable_unprepare(dpaux->clk);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return err;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic int tegra_dpaux_resume(struct device *dev)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
6248c2ecf20Sopenharmony_ci	int err;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dpaux->clk);
6278c2ecf20Sopenharmony_ci	if (err < 0) {
6288c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable clock: %d\n", err);
6298c2ecf20Sopenharmony_ci		return err;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dpaux->clk_parent);
6338c2ecf20Sopenharmony_ci	if (err < 0) {
6348c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable parent clock: %d\n", err);
6358c2ecf20Sopenharmony_ci		goto disable_clk;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	usleep_range(1000, 2000);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (dpaux->rst) {
6418c2ecf20Sopenharmony_ci		err = reset_control_deassert(dpaux->rst);
6428c2ecf20Sopenharmony_ci		if (err < 0) {
6438c2ecf20Sopenharmony_ci			dev_err(dev, "failed to deassert reset: %d\n", err);
6448c2ecf20Sopenharmony_ci			goto disable_parent;
6458c2ecf20Sopenharmony_ci		}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	return 0;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cidisable_parent:
6538c2ecf20Sopenharmony_ci	clk_disable_unprepare(dpaux->clk_parent);
6548c2ecf20Sopenharmony_cidisable_clk:
6558c2ecf20Sopenharmony_ci	clk_disable_unprepare(dpaux->clk);
6568c2ecf20Sopenharmony_ci	return err;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci#endif
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra_dpaux_pm_ops = {
6618c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL)
6628c2ecf20Sopenharmony_ci};
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic const struct tegra_dpaux_soc tegra124_dpaux_soc = {
6658c2ecf20Sopenharmony_ci	.cmh = 0x02,
6668c2ecf20Sopenharmony_ci	.drvz = 0x04,
6678c2ecf20Sopenharmony_ci	.drvi = 0x18,
6688c2ecf20Sopenharmony_ci};
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic const struct tegra_dpaux_soc tegra210_dpaux_soc = {
6718c2ecf20Sopenharmony_ci	.cmh = 0x02,
6728c2ecf20Sopenharmony_ci	.drvz = 0x04,
6738c2ecf20Sopenharmony_ci	.drvi = 0x30,
6748c2ecf20Sopenharmony_ci};
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic const struct tegra_dpaux_soc tegra194_dpaux_soc = {
6778c2ecf20Sopenharmony_ci	.cmh = 0x02,
6788c2ecf20Sopenharmony_ci	.drvz = 0x04,
6798c2ecf20Sopenharmony_ci	.drvi = 0x2c,
6808c2ecf20Sopenharmony_ci};
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_dpaux_of_match[] = {
6838c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra194-dpaux", .data = &tegra194_dpaux_soc },
6848c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra186-dpaux", .data = &tegra210_dpaux_soc },
6858c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-dpaux", .data = &tegra210_dpaux_soc },
6868c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra124-dpaux", .data = &tegra124_dpaux_soc },
6878c2ecf20Sopenharmony_ci	{ },
6888c2ecf20Sopenharmony_ci};
6898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_dpaux_of_match);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistruct platform_driver tegra_dpaux_driver = {
6928c2ecf20Sopenharmony_ci	.driver = {
6938c2ecf20Sopenharmony_ci		.name = "tegra-dpaux",
6948c2ecf20Sopenharmony_ci		.of_match_table = tegra_dpaux_of_match,
6958c2ecf20Sopenharmony_ci		.pm = &tegra_dpaux_pm_ops,
6968c2ecf20Sopenharmony_ci	},
6978c2ecf20Sopenharmony_ci	.probe = tegra_dpaux_probe,
6988c2ecf20Sopenharmony_ci	.remove = tegra_dpaux_remove,
6998c2ecf20Sopenharmony_ci};
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistruct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	mutex_lock(&dpaux_lock);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	list_for_each_entry(dpaux, &dpaux_list, list)
7088c2ecf20Sopenharmony_ci		if (np == dpaux->dev->of_node) {
7098c2ecf20Sopenharmony_ci			mutex_unlock(&dpaux_lock);
7108c2ecf20Sopenharmony_ci			return &dpaux->aux;
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	mutex_unlock(&dpaux_lock);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return NULL;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ciint drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
7218c2ecf20Sopenharmony_ci	unsigned long timeout;
7228c2ecf20Sopenharmony_ci	int err;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	output->connector.polled = DRM_CONNECTOR_POLL_HPD;
7258c2ecf20Sopenharmony_ci	dpaux->output = output;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (output->panel) {
7288c2ecf20Sopenharmony_ci		enum drm_connector_status status;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		if (dpaux->vdd) {
7318c2ecf20Sopenharmony_ci			err = regulator_enable(dpaux->vdd);
7328c2ecf20Sopenharmony_ci			if (err < 0)
7338c2ecf20Sopenharmony_ci				return err;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(250);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		while (time_before(jiffies, timeout)) {
7398c2ecf20Sopenharmony_ci			status = drm_dp_aux_detect(aux);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci			if (status == connector_status_connected)
7428c2ecf20Sopenharmony_ci				break;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci			usleep_range(1000, 2000);
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		if (status != connector_status_connected)
7488c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	enable_irq(dpaux->irq);
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ciint drm_dp_aux_detach(struct drm_dp_aux *aux)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
7588c2ecf20Sopenharmony_ci	unsigned long timeout;
7598c2ecf20Sopenharmony_ci	int err;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	disable_irq(dpaux->irq);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (dpaux->output->panel) {
7648c2ecf20Sopenharmony_ci		enum drm_connector_status status;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		if (dpaux->vdd) {
7678c2ecf20Sopenharmony_ci			err = regulator_disable(dpaux->vdd);
7688c2ecf20Sopenharmony_ci			if (err < 0)
7698c2ecf20Sopenharmony_ci				return err;
7708c2ecf20Sopenharmony_ci		}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(250);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		while (time_before(jiffies, timeout)) {
7758c2ecf20Sopenharmony_ci			status = drm_dp_aux_detect(aux);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci			if (status == connector_status_disconnected)
7788c2ecf20Sopenharmony_ci				break;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci			usleep_range(1000, 2000);
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		if (status != connector_status_disconnected)
7848c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci		dpaux->output = NULL;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return 0;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cienum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
7958c2ecf20Sopenharmony_ci	u32 value;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (value & DPAUX_DP_AUXSTAT_HPD_STATUS)
8008c2ecf20Sopenharmony_ci		return connector_status_connected;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	return connector_status_disconnected;
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ciint drm_dp_aux_enable(struct drm_dp_aux *aux)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	return tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_AUX);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ciint drm_dp_aux_disable(struct drm_dp_aux *aux)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct tegra_dpaux *dpaux = to_dpaux(aux);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	tegra_dpaux_pad_power_down(dpaux);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	return 0;
8198c2ecf20Sopenharmony_ci}
820