// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/rational.h>
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_print.h>
#include "dp_catalog.h"
#include "dp_reg.h"
#define POLLING_SLEEP_US 1000
#define POLLING_TIMEOUT_US 10000
#define SCRAMBLER_RESET_COUNT_VALUE 0xFC
#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
#define DP_INTERRUPT_STATUS_MASK_SHIFT 2
#define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4)
#define DP_INTERRUPT_STATUS1 \
(DP_INTR_AUX_XFER_DONE| \
DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \
DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \
DP_INTR_PLL_UNLOCKED | DP_INTR_AUX_ERROR)
#define DP_INTERRUPT_STATUS1_ACK \
(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_ACK_SHIFT)
#define DP_INTERRUPT_STATUS1_MASK \
(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_MASK_SHIFT)
#define DP_INTERRUPT_STATUS2 \
(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED)
#define DP_INTERRUPT_STATUS2_ACK \
(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_ACK_SHIFT)
#define DP_INTERRUPT_STATUS2_MASK \
(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)
#define DP_INTERRUPT_STATUS4 \
(PSR_UPDATE_INT | PSR_CAPTURE_INT | PSR_EXIT_INT | \
PSR_UPDATE_ERROR_INT | PSR_WAKE_ERROR_INT)
#define DP_INTERRUPT_MASK4 \
(PSR_UPDATE_MASK | PSR_CAPTURE_MASK | PSR_EXIT_MASK | \
PSR_UPDATE_ERROR_MASK | PSR_WAKE_ERROR_MASK)
#define DP_DEFAULT_AHB_OFFSET 0x0000
#define DP_DEFAULT_AHB_SIZE 0x0200
#define DP_DEFAULT_AUX_OFFSET 0x0200
#define DP_DEFAULT_AUX_SIZE 0x0200
#define DP_DEFAULT_LINK_OFFSET 0x0400
#define DP_DEFAULT_LINK_SIZE 0x0C00
#define DP_DEFAULT_P0_OFFSET 0x1000
#define DP_DEFAULT_P0_SIZE 0x0400
struct dss_io_region {
size_t len;
void __iomem *base;
};
struct dss_io_data {
struct dss_io_region ahb;
struct dss_io_region aux;
struct dss_io_region link;
struct dss_io_region p0;
};
struct dp_catalog_private {
struct device *dev;
struct drm_device *drm_dev;
struct dss_io_data io;
u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_catalog dp_catalog;
};
void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *disp_state)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
struct dss_io_data *dss = &catalog->io;
msm_disp_snapshot_add_block(disp_state, dss->ahb.len, dss->ahb.base, "dp_ahb");
msm_disp_snapshot_add_block(disp_state, dss->aux.len, dss->aux.base, "dp_aux");
msm_disp_snapshot_add_block(disp_state, dss->link.len, dss->link.base, "dp_link");
msm_disp_snapshot_add_block(disp_state, dss->p0.len, dss->p0.base, "dp_p0");
}
static inline u32 dp_read_aux(struct dp_catalog_private *catalog, u32 offset)
{
return readl_relaxed(catalog->io.aux.base + offset);
}
static inline void dp_write_aux(struct dp_catalog_private *catalog,
u32 offset, u32 data)
{
/*
* To make sure aux reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
writel(data, catalog->io.aux.base + offset);
}
static