// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
* Author: Chris Zhong <zyw@rock-chips.com>
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/extcon.h>
#include <linux/firmware.h>
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/hdmi-codec.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
#include "rockchip_drm_vop.h"
#define connector_to_dp(c) \
container_of(c, struct cdn_dp_device, connector)
#define encoder_to_dp(c) \
container_of(c, struct cdn_dp_device, encoder)
#define GRF_SOC_CON9 0x6224
#define DP_SEL_VOP_LIT BIT(12)
#define GRF_SOC_CON26 0x6268
#define DPTX_HPD_SEL (3 << 12)
#define DPTX_HPD_DEL (2 << 12)
#define DPTX_HPD_SEL_MASK (3 << 28)
#define CDN_FW_TIMEOUT_MS (64 * 1000)
#define CDN_DPCD_TIMEOUT_MS 5000
#define CDN_DP_FIRMWARE "rockchip/dptx.bin"
MODULE_FIRMWARE(CDN_DP_FIRMWARE);
struct cdn_dp_data {
u8 max_phy;
};
struct cdn_dp_data rk3399_cdn_dp = {
.max_phy = 2,
};
static const struct of_device_id cdn_dp_dt_ids[] = {
{ .compatible = "rockchip,rk3399-cdn-dp",
.data = (void *)&rk3399_cdn_dp },
{}
};
MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
static int cdn_dp_grf_write(struct cdn_dp_device *dp,
unsigned int reg, unsigned int val)
{
int ret;
ret = clk_prepare_enable(dp->grf_clk);
if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n");
return ret;
}
ret = regmap_write(dp->grf, reg, val);
if (ret) {
DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
clk_disable_unprepare(dp->grf_clk);
return ret;
}
clk_disable_unprepare(dp->grf_clk);
return 0;
}
static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
{
int ret;
unsigned long rate;
ret = clk_prepare_enable(dp->pclk);
if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret);
goto err_pclk;
}
ret = clk_prepare_enable(dp->core_clk);
if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret);
goto err_core_clk;
}
ret = pm_runtime_get_sync(dp->dev);
if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret);
goto err_pm_runtime_get;
}
reset_control_assert(dp->core_rst);
reset_control_assert(dp->dptx_rst);
reset_control_assert(dp->apb_rst);
reset_control_deassert(dp->core_rst);
reset_control_deassert(dp->dptx_rst);
reset_control_deassert(dp->apb_rst);
rate = clk_get_rate(dp->core_clk);
if (!rate) {
DRM_DEV_ERROR(dp->dev, "get clk rate failed\n");
ret = -EINVAL;
goto err_set_rate;
}
cdn_dp_set_fw_clk(dp, rate);
cdn_dp_clock_reset(dp);
return 0;
err_set_rate:
pm_runtime_put(dp->dev);
err_pm_runtime_get:
clk_disable_unprepare(dp->core_clk);
err_core_clk:
clk_disable_unprepare(dp->pclk);
err_pclk:
return ret;
}
static void cdn_dp_clk_disable(struct cdn_dp_device *dp)
{
pm_runtime_put_sync(