Liangbin Lian 2026-01-22 16:28:35 +08:00
parent 8fa60fdb47
commit ade13ffdaa
26 changed files with 11243 additions and 1 deletions

View File

@ -0,0 +1,647 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
* Copyright (c) 2024 Collabora Ltd.
*
* Author: Algea Cao <algea.cao@rock-chips.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
*/
#include <linux/completion.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/workqueue.h>
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
#include <sound/hdmi-codec.h>
#include "dw-hdmi-qp.h"
#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30
#define HDMI14_MAX_TMDSCLK 340000000
#define SCRAMB_POLL_DELAY_MS 3000
struct dw_hdmi_qp_i2c {
struct i2c_adapter adap;
struct mutex lock; /* used to serialize data transfers */
struct completion cmp;
u8 stat;
u8 slave_reg;
bool is_regaddr;
bool is_segment;
};
struct dw_hdmi_qp {
struct drm_bridge bridge;
struct device *dev;
struct dw_hdmi_qp_i2c *i2c;
struct {
const struct dw_hdmi_qp_phy_ops *ops;
void *data;
} phy;
struct regmap *regm;
};
static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val,
int offset)
{
regmap_write(hdmi->regm, offset, val);
}
static unsigned int dw_hdmi_qp_read(struct dw_hdmi_qp *hdmi, int offset)
{
unsigned int val = 0;
regmap_read(hdmi->regm, offset, &val);
return val;
}
static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data,
unsigned int mask, unsigned int reg)
{
regmap_update_bits(hdmi->regm, reg, mask, data);
}
static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi,
unsigned char *buf, unsigned int length)
{
struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
int stat;
if (!i2c->is_regaddr) {
dev_dbg(hdmi->dev, "set read register address to 0\n");
i2c->slave_reg = 0x00;
i2c->is_regaddr = true;
}
while (length--) {
reinit_completion(&i2c->cmp);
dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
I2CM_INTERFACE_CONTROL0);
if (i2c->is_segment)
dw_hdmi_qp_mod(hdmi, I2CM_EXT_READ, I2CM_WR_MASK,
I2CM_INTERFACE_CONTROL0);
else
dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK,
I2CM_INTERFACE_CONTROL0);
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) {
dev_err(hdmi->dev, "i2c read timed out\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EAGAIN;
}
/* Check for error condition on the bus */
if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
dev_err(hdmi->dev, "i2c read error\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EIO;
}
*buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff;
dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
}
i2c->is_segment = false;
return 0;
}
static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi,
unsigned char *buf, unsigned int length)
{
struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
int stat;
if (!i2c->is_regaddr) {
/* Use the first write byte as register address */
i2c->slave_reg = buf[0];
length--;
buf++;
i2c->is_regaddr = true;
}
while (length--) {
reinit_completion(&i2c->cmp);
dw_hdmi_qp_write(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3);
dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
I2CM_INTERFACE_CONTROL0);
dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK,
I2CM_INTERFACE_CONTROL0);
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) {
dev_err(hdmi->dev, "i2c write time out!\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EAGAIN;
}
/* Check for error condition on the bus */
if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
dev_err(hdmi->dev, "i2c write nack!\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EIO;
}
dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
}
return 0;
}
static int dw_hdmi_qp_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap);
struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
u8 addr = msgs[0].addr;
int i, ret = 0;
if (addr == DDC_CI_ADDR)
/*
* The internal I2C controller does not support the multi-byte
* read and write operations needed for DDC/CI.
* FIXME: Blacklist the DDC/CI address until we filter out
* unsupported I2C operations.
*/
return -EOPNOTSUPP;
for (i = 0; i < num; i++) {
if (msgs[i].len == 0) {
dev_err(hdmi->dev,
"unsupported transfer %d/%d, no data\n",
i + 1, num);
return -EOPNOTSUPP;
}
}
guard(mutex)(&i2c->lock);
/* Unmute DONE and ERROR interrupts */
dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
MAINUNIT_1_INT_MASK_N);
/* Set slave device address taken from the first I2C message */
if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
addr = DDC_ADDR;
dw_hdmi_qp_mod(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0);
/* Set slave device register address on transfer */
i2c->is_regaddr = false;
/* Set segment pointer for I2C extended read mode operation */
i2c->is_segment = false;
for (i = 0; i < num; i++) {
if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
i2c->is_segment = true;
dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR,
I2CM_INTERFACE_CONTROL1);
dw_hdmi_qp_mod(hdmi, *msgs[i].buf << 7, I2CM_SEG_PTR,
I2CM_INTERFACE_CONTROL1);
} else {
if (msgs[i].flags & I2C_M_RD)
ret = dw_hdmi_qp_i2c_read(hdmi, msgs[i].buf,
msgs[i].len);
else
ret = dw_hdmi_qp_i2c_write(hdmi, msgs[i].buf,
msgs[i].len);
}
if (ret < 0)
break;
}
if (!ret)
ret = num;
/* Mute DONE and ERROR interrupts */
dw_hdmi_qp_mod(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N,
MAINUNIT_1_INT_MASK_N);
return ret;
}
static u32 dw_hdmi_qp_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm dw_hdmi_qp_algorithm = {
.master_xfer = dw_hdmi_qp_i2c_xfer,
.functionality = dw_hdmi_qp_i2c_func,
};
static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi)
{
struct dw_hdmi_qp_i2c *i2c;
struct i2c_adapter *adap;
int ret;
i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return ERR_PTR(-ENOMEM);
mutex_init(&i2c->lock);
init_completion(&i2c->cmp);
adap = &i2c->adap;
adap->owner = THIS_MODULE;
adap->dev.parent = hdmi->dev;
adap->algo = &dw_hdmi_qp_algorithm;
strscpy(adap->name, "DesignWare HDMI QP", sizeof(adap->name));
i2c_set_adapdata(adap, hdmi);
ret = devm_i2c_add_adapter(hdmi->dev, adap);
if (ret) {
dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
devm_kfree(hdmi->dev, i2c);
return ERR_PTR(ret);
}
hdmi->i2c = i2c;
dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
return adap;
}
static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi,
const u8 *buffer, size_t len)
{
u32 val, i, j;
if (len != HDMI_INFOFRAME_SIZE(AVI)) {
dev_err(hdmi->dev, "failed to configure avi infoframe\n");
return -EINVAL;
}
/*
* DW HDMI QP IP uses a different byte format from standard AVI info
* frames, though generally the bits are in the correct bytes.
*/
val = buffer[1] << 8 | buffer[2] << 16;
dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0);
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (i * 4 + j >= 14)
break;
if (!j)
val = buffer[i * 4 + j + 3];
val |= buffer[i * 4 + j + 3] << (8 * j);
}
dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
}
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
return 0;
}
static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi,
const u8 *buffer, size_t len)
{
u32 val, i;
if (len != HDMI_INFOFRAME_SIZE(DRM)) {
dev_err(hdmi->dev, "failed to configure drm infoframe\n");
return -EINVAL;
}
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
val = buffer[1] << 8 | buffer[2] << 16;
dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0);
for (i = 0; i <= buffer[2]; i++) {
if (i % 4 == 0)
val = buffer[3 + i];
val |= buffer[3 + i] << ((i % 4) * 8);
if ((i % 4 == 3) || i == buffer[2])
dw_hdmi_qp_write(hdmi, val,
PKT_DRMI_CONTENTS1 + ((i / 4) * 4));
}
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN,
PKTSCHED_PKT_EN);
return 0;
}
static int dw_hdmi_qp_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
int ret;
ret = drm_atomic_helper_connector_hdmi_check(conn_state->connector,
conn_state->state);
if (ret)
dev_dbg(hdmi->dev, "%s failed: %d\n", __func__, ret);
return ret;
}
static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
struct drm_atomic_state *state = old_state->base.state;
struct drm_connector_state *conn_state;
struct drm_connector *connector;
unsigned int op_mode;
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (WARN_ON(!connector))
return;
conn_state = drm_atomic_get_new_connector_state(state, connector);
if (WARN_ON(!conn_state))
return;
if (connector->display_info.is_hdmi) {
dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n",
__func__, conn_state->hdmi.tmds_char_rate);
op_mode = 0;
} else {
dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
op_mode = OPMODE_DVI;
}
hdmi->phy.ops->init(hdmi, hdmi->phy.data);
dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0);
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
}
static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
}
static enum drm_connector_status
dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
}
static const struct drm_edid *
dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
const struct drm_edid *drm_edid;
drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
if (!drm_edid)
dev_dbg(hdmi->dev, "failed to get edid\n");
return drm_edid;
}
static enum drm_mode_status
dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
unsigned long long rate;
rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
if (rate > HDMI14_MAX_TMDSCLK) {
dev_dbg(hdmi->dev, "Unsupported mode clock: %d\n", mode->clock);
return MODE_CLOCK_HIGH;
}
return MODE_OK;
}
static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
PKTSCHED_PKT_EN);
break;
case HDMI_INFOFRAME_TYPE_DRM:
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
break;
default:
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
}
return 0;
}
static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
case HDMI_INFOFRAME_TYPE_DRM:
return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
default:
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
return 0;
}
}
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_check = dw_hdmi_qp_bridge_atomic_check,
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
.detect = dw_hdmi_qp_bridge_detect,
.edid_read = dw_hdmi_qp_bridge_edid_read,
.mode_valid = dw_hdmi_qp_bridge_mode_valid,
.hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
.hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
};
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
{
struct dw_hdmi_qp *hdmi = dev_id;
struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
u32 stat;
stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS);
i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ |
I2CM_NACK_RCVD_IRQ);
if (i2c->stat) {
dw_hdmi_qp_write(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR);
complete(&i2c->cmp);
}
if (stat)
return IRQ_HANDLED;
return IRQ_NONE;
}
static const struct regmap_config dw_hdmi_qp_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = EARCRX_1_INT_FORCE,
};
static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
{
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
/* Software reset */
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
/* Clear DONE and ERROR interrupts */
dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
MAINUNIT_1_INT_CLEAR);
if (hdmi->phy.ops->setup_hpd)
hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
}
struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
struct drm_encoder *encoder,
const struct dw_hdmi_qp_plat_data *plat_data)
{
struct device *dev = &pdev->dev;
struct dw_hdmi_qp *hdmi;
void __iomem *regs;
int ret;
if (!plat_data->phy_ops || !plat_data->phy_ops->init ||
!plat_data->phy_ops->disable || !plat_data->phy_ops->read_hpd) {
dev_err(dev, "Missing platform PHY ops\n");
return ERR_PTR(-ENODEV);
}
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return ERR_PTR(-ENOMEM);
hdmi->dev = dev;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return ERR_CAST(regs);
hdmi->regm = devm_regmap_init_mmio(dev, regs, &dw_hdmi_qp_regmap_config);
if (IS_ERR(hdmi->regm)) {
dev_err(dev, "Failed to configure regmap\n");
return ERR_CAST(hdmi->regm);
}
hdmi->phy.ops = plat_data->phy_ops;
hdmi->phy.data = plat_data->phy_data;
dw_hdmi_qp_init_hw(hdmi);
ret = devm_request_threaded_irq(dev, plat_data->main_irq,
dw_hdmi_qp_main_hardirq, NULL,
IRQF_SHARED, dev_name(dev), hdmi);
if (ret)
return ERR_PTR(ret);
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Synopsys";
hdmi->bridge.product = "DW HDMI QP TX";
hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))
return ERR_CAST(hdmi->bridge.ddc);
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
if (ret)
return ERR_PTR(ret);
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ERR_PTR(ret);
return hdmi;
}
EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi)
{
dw_hdmi_qp_init_hw(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume);
MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>");
MODULE_DESCRIPTION("DW HDMI QP transmitter library");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,834 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) Rockchip Electronics Co.Ltd
* Author:
* Algea Cao <algea.cao@rock-chips.com>
*/
#ifndef __DW_HDMI_QP_H__
#define __DW_HDMI_QP_H__
#include <linux/bits.h>
/* Main Unit Registers */
#define CORE_ID 0x0
#define VER_NUMBER 0x4
#define VER_TYPE 0x8
#define CONFIG_REG 0xc
#define CONFIG_CEC BIT(28)
#define CONFIG_AUD_UD BIT(23)
#define CORE_TIMESTAMP_HHMM 0x14
#define CORE_TIMESTAMP_MMDD 0x18
#define CORE_TIMESTAMP_YYYY 0x1c
/* Reset Manager Registers */
#define GLOBAL_SWRESET_REQUEST 0x40
#define EARCRX_CMDC_SWINIT_P BIT(27)
#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10)
#define GLOBAL_SWDISABLE 0x44
#define CEC_SWDISABLE BIT(17)
#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10)
#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6)
#define RESET_MANAGER_CONFIG0 0x48
#define RESET_MANAGER_STATUS0 0x50
#define RESET_MANAGER_STATUS1 0x54
#define RESET_MANAGER_STATUS2 0x58
/* Timer Base Registers */
#define TIMER_BASE_CONFIG0 0x80
#define TIMER_BASE_STATUS0 0x84
/* CMU Registers */
#define CMU_CONFIG0 0xa0
#define CMU_CONFIG1 0xa4
#define CMU_CONFIG2 0xa8
#define CMU_CONFIG3 0xac
#define CMU_STATUS 0xb0
#define DISPLAY_CLK_MONITOR 0x3f
#define DISPLAY_CLK_LOCKED 0X15
#define EARC_BPCLK_OFF BIT(9)
#define AUDCLK_OFF BIT(7)
#define LINKQPCLK_OFF BIT(5)
#define VIDQPCLK_OFF BIT(3)
#define IPI_CLK_OFF BIT(1)
#define CMU_IPI_CLK_FREQ 0xb4
#define CMU_VIDQPCLK_FREQ 0xb8
#define CMU_LINKQPCLK_FREQ 0xbc
#define CMU_AUDQPCLK_FREQ 0xc0
#define CMU_EARC_BPCLK_FREQ 0xc4
/* I2CM Registers */
#define I2CM_SM_SCL_CONFIG0 0xe0
#define I2CM_FM_SCL_CONFIG0 0xe4
#define I2CM_CONFIG0 0xe8
#define I2CM_CONTROL0 0xec
#define I2CM_STATUS0 0xf0
#define I2CM_INTERFACE_CONTROL0 0xf4
#define I2CM_ADDR 0xff000
#define I2CM_SLVADDR 0xfe0
#define I2CM_WR_MASK 0x1e
#define I2CM_EXT_READ BIT(4)
#define I2CM_SHORT_READ BIT(3)
#define I2CM_FM_READ BIT(2)
#define I2CM_FM_WRITE BIT(1)
#define I2CM_FM_EN BIT(0)
#define I2CM_INTERFACE_CONTROL1 0xf8
#define I2CM_SEG_PTR 0x7f80
#define I2CM_SEG_ADDR 0x7f
#define I2CM_INTERFACE_WRDATA_0_3 0xfc
#define I2CM_INTERFACE_WRDATA_4_7 0x100
#define I2CM_INTERFACE_WRDATA_8_11 0x104
#define I2CM_INTERFACE_WRDATA_12_15 0x108
#define I2CM_INTERFACE_RDDATA_0_3 0x10c
#define I2CM_INTERFACE_RDDATA_4_7 0x110
#define I2CM_INTERFACE_RDDATA_8_11 0x114
#define I2CM_INTERFACE_RDDATA_12_15 0x118
/* SCDC Registers */
#define SCDC_CONFIG0 0x140
#define SCDC_I2C_FM_EN BIT(12)
#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6)
#define SCDC_UPD_FLAGS_POLL_EN BIT(4)
#define SCDC_CONTROL0 0x148
#define SCDC_STATUS0 0x150
#define STATUS_UPDATE BIT(0)
#define FRL_START BIT(4)
#define FLT_UPDATE BIT(5)
/* FLT Registers */
#define FLT_CONFIG0 0x160
#define FLT_CONFIG1 0x164
#define FLT_CONFIG2 0x168
#define FLT_CONTROL0 0x170
/* Main Unit 2 Registers */
#define MAINUNIT_STATUS0 0x180
/* Video Interface Registers */
#define VIDEO_INTERFACE_CONFIG0 0x800
#define VIDEO_INTERFACE_CONFIG1 0x804
#define VIDEO_INTERFACE_CONFIG2 0x808
#define VIDEO_INTERFACE_CONTROL0 0x80c
#define VIDEO_INTERFACE_STATUS0 0x814
/* Video Packing Registers */
#define VIDEO_PACKING_CONFIG0 0x81c
/* Audio Interface Registers */
#define AUDIO_INTERFACE_CONFIG0 0x820
#define AUD_IF_SEL_MSK 0x3
#define AUD_IF_SPDIF 0x2
#define AUD_IF_I2S 0x1
#define AUD_IF_PAI 0x0
#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2)
#define AUD_FIFO_INIT_ON_OVF_EN BIT(2)
#define I2S_LINES_EN_MSK GENMASK(7, 4)
#define I2S_LINES_EN(x) BIT((x) + 4)
#define I2S_BPCUV_RCV_MSK BIT(12)
#define I2S_BPCUV_RCV_EN BIT(12)
#define I2S_BPCUV_RCV_DIS 0
#define SPDIF_LINES_EN GENMASK(19, 16)
#define AUD_FORMAT_MSK GENMASK(26, 24)
#define AUD_3DOBA (0x7 << 24)
#define AUD_3DASP (0x6 << 24)
#define AUD_MSOBA (0x5 << 24)
#define AUD_MSASP (0x4 << 24)
#define AUD_HBR (0x3 << 24)
#define AUD_DST (0x2 << 24)
#define AUD_OBA (0x1 << 24)
#define AUD_ASP (0x0 << 24)
#define AUDIO_INTERFACE_CONFIG1 0x824
#define AUDIO_INTERFACE_CONTROL0 0x82c
#define AUDIO_FIFO_CLR_P BIT(0)
#define AUDIO_INTERFACE_STATUS0 0x834
/* Frame Composer Registers */
#define FRAME_COMPOSER_CONFIG0 0x840
#define FRAME_COMPOSER_CONFIG1 0x844
#define FRAME_COMPOSER_CONFIG2 0x848
#define FRAME_COMPOSER_CONFIG3 0x84c
#define FRAME_COMPOSER_CONFIG4 0x850
#define FRAME_COMPOSER_CONFIG5 0x854
#define FRAME_COMPOSER_CONFIG6 0x858
#define FRAME_COMPOSER_CONFIG7 0x85c
#define FRAME_COMPOSER_CONFIG8 0x860
#define FRAME_COMPOSER_CONFIG9 0x864
#define FRAME_COMPOSER_CONTROL0 0x86c
/* Video Monitor Registers */
#define VIDEO_MONITOR_CONFIG0 0x880
#define VIDEO_MONITOR_STATUS0 0x884
#define VIDEO_MONITOR_STATUS1 0x888
#define VIDEO_MONITOR_STATUS2 0x88c
#define VIDEO_MONITOR_STATUS3 0x890
#define VIDEO_MONITOR_STATUS4 0x894
#define VIDEO_MONITOR_STATUS5 0x898
#define VIDEO_MONITOR_STATUS6 0x89c
/* HDCP2 Logic Registers */
#define HDCP2LOGIC_CONFIG0 0x8e0
#define HDCP2_BYPASS BIT(0)
#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4
#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8
/* HDCP14 Registers */
#define HDCP14_CONFIG0 0x900
#define HDCP14_CONFIG1 0x904
#define HDCP14_CONFIG2 0x908
#define HDCP14_CONFIG3 0x90c
#define HDCP14_KEY_SEED 0x914
#define HDCP14_KEY_H 0x918
#define HDCP14_KEY_L 0x91c
#define HDCP14_KEY_STATUS 0x920
#define HDCP14_AKSV_H 0x924
#define HDCP14_AKSV_L 0x928
#define HDCP14_AN_H 0x92c
#define HDCP14_AN_L 0x930
#define HDCP14_STATUS0 0x934
#define HDCP14_STATUS1 0x938
/* Scrambler Registers */
#define SCRAMB_CONFIG0 0x960
/* Video Configuration Registers */
#define LINK_CONFIG0 0x968
#define OPMODE_FRL_4LANES BIT(8)
#define OPMODE_DVI BIT(4)
#define OPMODE_FRL BIT(0)
/* TMDS FIFO Registers */
#define TMDS_FIFO_CONFIG0 0x970
#define TMDS_FIFO_CONTROL0 0x974
/* FRL RSFEC Registers */
#define FRL_RSFEC_CONFIG0 0xa20
#define FRL_RSFEC_STATUS0 0xa30
/* FRL Packetizer Registers */
#define FRL_PKTZ_CONFIG0 0xa40
#define FRL_PKTZ_CONTROL0 0xa44
#define FRL_PKTZ_CONTROL1 0xa50
#define FRL_PKTZ_STATUS1 0xa54
/* Packet Scheduler Registers */
#define PKTSCHED_CONFIG0 0xa80
#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84
#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88
#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c
#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90
#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94
#define PKTSCHED_PKT_CONFIG0 0xa98
#define PKTSCHED_PKT_CONFIG1 0xa9c
#define PKTSCHED_DRMI_FIELDRATE BIT(13)
#define PKTSCHED_AVI_FIELDRATE BIT(12)
#define PKTSCHED_PKT_CONFIG2 0xaa0
#define PKTSCHED_PKT_CONFIG3 0xaa4
#define PKTSCHED_PKT_EN 0xaa8
#define PKTSCHED_DRMI_TX_EN BIT(17)
#define PKTSCHED_AUDI_TX_EN BIT(15)
#define PKTSCHED_AVI_TX_EN BIT(13)
#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10)
#define PKTSCHED_AMD_TX_EN BIT(8)
#define PKTSCHED_GCP_TX_EN BIT(3)
#define PKTSCHED_AUDS_TX_EN BIT(2)
#define PKTSCHED_ACR_TX_EN BIT(1)
#define PKTSCHED_NULL_TX_EN BIT(0)
#define PKTSCHED_PKT_CONTROL0 0xaac
#define PKTSCHED_PKT_SEND 0xab0
#define PKTSCHED_PKT_STATUS0 0xab4
#define PKTSCHED_PKT_STATUS1 0xab8
#define PKT_NULL_CONTENTS0 0xb00
#define PKT_NULL_CONTENTS1 0xb04
#define PKT_NULL_CONTENTS2 0xb08
#define PKT_NULL_CONTENTS3 0xb0c
#define PKT_NULL_CONTENTS4 0xb10
#define PKT_NULL_CONTENTS5 0xb14
#define PKT_NULL_CONTENTS6 0xb18
#define PKT_NULL_CONTENTS7 0xb1c
#define PKT_ACP_CONTENTS0 0xb20
#define PKT_ACP_CONTENTS1 0xb24
#define PKT_ACP_CONTENTS2 0xb28
#define PKT_ACP_CONTENTS3 0xb2c
#define PKT_ACP_CONTENTS4 0xb30
#define PKT_ACP_CONTENTS5 0xb34
#define PKT_ACP_CONTENTS6 0xb38
#define PKT_ACP_CONTENTS7 0xb3c
#define PKT_ISRC1_CONTENTS0 0xb40
#define PKT_ISRC1_CONTENTS1 0xb44
#define PKT_ISRC1_CONTENTS2 0xb48
#define PKT_ISRC1_CONTENTS3 0xb4c
#define PKT_ISRC1_CONTENTS4 0xb50
#define PKT_ISRC1_CONTENTS5 0xb54
#define PKT_ISRC1_CONTENTS6 0xb58
#define PKT_ISRC1_CONTENTS7 0xb5c
#define PKT_ISRC2_CONTENTS0 0xb60
#define PKT_ISRC2_CONTENTS1 0xb64
#define PKT_ISRC2_CONTENTS2 0xb68
#define PKT_ISRC2_CONTENTS3 0xb6c
#define PKT_ISRC2_CONTENTS4 0xb70
#define PKT_ISRC2_CONTENTS5 0xb74
#define PKT_ISRC2_CONTENTS6 0xb78
#define PKT_ISRC2_CONTENTS7 0xb7c
#define PKT_GMD_CONTENTS0 0xb80
#define PKT_GMD_CONTENTS1 0xb84
#define PKT_GMD_CONTENTS2 0xb88
#define PKT_GMD_CONTENTS3 0xb8c
#define PKT_GMD_CONTENTS4 0xb90
#define PKT_GMD_CONTENTS5 0xb94
#define PKT_GMD_CONTENTS6 0xb98
#define PKT_GMD_CONTENTS7 0xb9c
#define PKT_AMD_CONTENTS0 0xba0
#define PKT_AMD_CONTENTS1 0xba4
#define PKT_AMD_CONTENTS2 0xba8
#define PKT_AMD_CONTENTS3 0xbac
#define PKT_AMD_CONTENTS4 0xbb0
#define PKT_AMD_CONTENTS5 0xbb4
#define PKT_AMD_CONTENTS6 0xbb8
#define PKT_AMD_CONTENTS7 0xbbc
#define PKT_VSI_CONTENTS0 0xbc0
#define PKT_VSI_CONTENTS1 0xbc4
#define PKT_VSI_CONTENTS2 0xbc8
#define PKT_VSI_CONTENTS3 0xbcc
#define PKT_VSI_CONTENTS4 0xbd0
#define PKT_VSI_CONTENTS5 0xbd4
#define PKT_VSI_CONTENTS6 0xbd8
#define PKT_VSI_CONTENTS7 0xbdc
#define PKT_AVI_CONTENTS0 0xbe0
#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4)
#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04
#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08
#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80
#define PKT_AVI_CONTENTS1 0xbe4
#define PKT_AVI_CONTENTS2 0xbe8
#define PKT_AVI_CONTENTS3 0xbec
#define PKT_AVI_CONTENTS4 0xbf0
#define PKT_AVI_CONTENTS5 0xbf4
#define PKT_AVI_CONTENTS6 0xbf8
#define PKT_AVI_CONTENTS7 0xbfc
#define PKT_SPDI_CONTENTS0 0xc00
#define PKT_SPDI_CONTENTS1 0xc04
#define PKT_SPDI_CONTENTS2 0xc08
#define PKT_SPDI_CONTENTS3 0xc0c
#define PKT_SPDI_CONTENTS4 0xc10
#define PKT_SPDI_CONTENTS5 0xc14
#define PKT_SPDI_CONTENTS6 0xc18
#define PKT_SPDI_CONTENTS7 0xc1c
#define PKT_AUDI_CONTENTS0 0xc20
#define PKT_AUDI_CONTENTS1 0xc24
#define PKT_AUDI_CONTENTS2 0xc28
#define PKT_AUDI_CONTENTS3 0xc2c
#define PKT_AUDI_CONTENTS4 0xc30
#define PKT_AUDI_CONTENTS5 0xc34
#define PKT_AUDI_CONTENTS6 0xc38
#define PKT_AUDI_CONTENTS7 0xc3c
#define PKT_NVI_CONTENTS0 0xc40
#define PKT_NVI_CONTENTS1 0xc44
#define PKT_NVI_CONTENTS2 0xc48
#define PKT_NVI_CONTENTS3 0xc4c
#define PKT_NVI_CONTENTS4 0xc50
#define PKT_NVI_CONTENTS5 0xc54
#define PKT_NVI_CONTENTS6 0xc58
#define PKT_NVI_CONTENTS7 0xc5c
#define PKT_DRMI_CONTENTS0 0xc60
#define PKT_DRMI_CONTENTS1 0xc64
#define PKT_DRMI_CONTENTS2 0xc68
#define PKT_DRMI_CONTENTS3 0xc6c
#define PKT_DRMI_CONTENTS4 0xc70
#define PKT_DRMI_CONTENTS5 0xc74
#define PKT_DRMI_CONTENTS6 0xc78
#define PKT_DRMI_CONTENTS7 0xc7c
#define PKT_GHDMI1_CONTENTS0 0xc80
#define PKT_GHDMI1_CONTENTS1 0xc84
#define PKT_GHDMI1_CONTENTS2 0xc88
#define PKT_GHDMI1_CONTENTS3 0xc8c
#define PKT_GHDMI1_CONTENTS4 0xc90
#define PKT_GHDMI1_CONTENTS5 0xc94
#define PKT_GHDMI1_CONTENTS6 0xc98
#define PKT_GHDMI1_CONTENTS7 0xc9c
#define PKT_GHDMI2_CONTENTS0 0xca0
#define PKT_GHDMI2_CONTENTS1 0xca4
#define PKT_GHDMI2_CONTENTS2 0xca8
#define PKT_GHDMI2_CONTENTS3 0xcac
#define PKT_GHDMI2_CONTENTS4 0xcb0
#define PKT_GHDMI2_CONTENTS5 0xcb4
#define PKT_GHDMI2_CONTENTS6 0xcb8
#define PKT_GHDMI2_CONTENTS7 0xcbc
/* EMP Packetizer Registers */
#define PKT_EMP_CONFIG0 0xce0
#define PKT_EMP_CONTROL0 0xcec
#define PKT_EMP_CONTROL1 0xcf0
#define PKT_EMP_CONTROL2 0xcf4
#define PKT_EMP_VTEM_CONTENTS0 0xd00
#define PKT_EMP_VTEM_CONTENTS1 0xd04
#define PKT_EMP_VTEM_CONTENTS2 0xd08
#define PKT_EMP_VTEM_CONTENTS3 0xd0c
#define PKT_EMP_VTEM_CONTENTS4 0xd10
#define PKT_EMP_VTEM_CONTENTS5 0xd14
#define PKT_EMP_VTEM_CONTENTS6 0xd18
#define PKT_EMP_VTEM_CONTENTS7 0xd1c
#define PKT0_EMP_CVTEM_CONTENTS0 0xd20
#define PKT0_EMP_CVTEM_CONTENTS1 0xd24
#define PKT0_EMP_CVTEM_CONTENTS2 0xd28
#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c
#define PKT0_EMP_CVTEM_CONTENTS4 0xd30
#define PKT0_EMP_CVTEM_CONTENTS5 0xd34
#define PKT0_EMP_CVTEM_CONTENTS6 0xd38
#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c
#define PKT1_EMP_CVTEM_CONTENTS0 0xd40
#define PKT1_EMP_CVTEM_CONTENTS1 0xd44
#define PKT1_EMP_CVTEM_CONTENTS2 0xd48
#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c
#define PKT1_EMP_CVTEM_CONTENTS4 0xd50
#define PKT1_EMP_CVTEM_CONTENTS5 0xd54
#define PKT1_EMP_CVTEM_CONTENTS6 0xd58
#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c
#define PKT2_EMP_CVTEM_CONTENTS0 0xd60
#define PKT2_EMP_CVTEM_CONTENTS1 0xd64
#define PKT2_EMP_CVTEM_CONTENTS2 0xd68
#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c
#define PKT2_EMP_CVTEM_CONTENTS4 0xd70
#define PKT2_EMP_CVTEM_CONTENTS5 0xd74
#define PKT2_EMP_CVTEM_CONTENTS6 0xd78
#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c
#define PKT3_EMP_CVTEM_CONTENTS0 0xd80
#define PKT3_EMP_CVTEM_CONTENTS1 0xd84
#define PKT3_EMP_CVTEM_CONTENTS2 0xd88
#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c
#define PKT3_EMP_CVTEM_CONTENTS4 0xd90
#define PKT3_EMP_CVTEM_CONTENTS5 0xd94
#define PKT3_EMP_CVTEM_CONTENTS6 0xd98
#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c
#define PKT4_EMP_CVTEM_CONTENTS0 0xda0
#define PKT4_EMP_CVTEM_CONTENTS1 0xda4
#define PKT4_EMP_CVTEM_CONTENTS2 0xda8
#define PKT4_EMP_CVTEM_CONTENTS3 0xdac
#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0
#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4
#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8
#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc
#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0
#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4
#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8
#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc
#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0
#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4
#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8
#define PKT5_EMP_CVTEM_CONTENTS7 0xddc
/* Audio Packetizer Registers */
#define AUDPKT_CONTROL0 0xe20
#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12)
#define AUDPKT_PBIT_FORCE_EN BIT(12)
#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0)
#define AUDPKT_CHSTATUS_OVR_EN BIT(0)
#define AUDPKT_CONTROL1 0xe24
#define AUDPKT_ACR_CONTROL0 0xe40
#define AUDPKT_ACR_N_VALUE 0xfffff
#define AUDPKT_ACR_CONTROL1 0xe44
#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4)
#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4)
#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1)
#define AUDPKT_ACR_CTS_OVR_EN BIT(1)
#define AUDPKT_ACR_STATUS0 0xe4c
#define AUDPKT_CHSTATUS_OVR0 0xe60
#define AUDPKT_CHSTATUS_OVR1 0xe64
/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */
#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0)
#define AUDPKT_CHSTATUS_SR_22050 0x4
#define AUDPKT_CHSTATUS_SR_24000 0x6
#define AUDPKT_CHSTATUS_SR_32000 0x3
#define AUDPKT_CHSTATUS_SR_44100 0x0
#define AUDPKT_CHSTATUS_SR_48000 0x2
#define AUDPKT_CHSTATUS_SR_88200 0x8
#define AUDPKT_CHSTATUS_SR_96000 0xa
#define AUDPKT_CHSTATUS_SR_176400 0xc
#define AUDPKT_CHSTATUS_SR_192000 0xe
#define AUDPKT_CHSTATUS_SR_768000 0x9
#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1
/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */
#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12)
#define AUDPKT_CHSTATUS_OSR_8000 0x6
#define AUDPKT_CHSTATUS_OSR_11025 0xa
#define AUDPKT_CHSTATUS_OSR_12000 0x2
#define AUDPKT_CHSTATUS_OSR_16000 0x8
#define AUDPKT_CHSTATUS_OSR_22050 0xb
#define AUDPKT_CHSTATUS_OSR_24000 0x9
#define AUDPKT_CHSTATUS_OSR_32000 0xc
#define AUDPKT_CHSTATUS_OSR_44100 0xf
#define AUDPKT_CHSTATUS_OSR_48000 0xd
#define AUDPKT_CHSTATUS_OSR_88200 0x7
#define AUDPKT_CHSTATUS_OSR_96000 0x5
#define AUDPKT_CHSTATUS_OSR_176400 0x3
#define AUDPKT_CHSTATUS_OSR_192000 0x1
#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0
#define AUDPKT_CHSTATUS_OVR2 0xe68
#define AUDPKT_CHSTATUS_OVR3 0xe6c
#define AUDPKT_CHSTATUS_OVR4 0xe70
#define AUDPKT_CHSTATUS_OVR5 0xe74
#define AUDPKT_CHSTATUS_OVR6 0xe78
#define AUDPKT_CHSTATUS_OVR7 0xe7c
#define AUDPKT_CHSTATUS_OVR8 0xe80
#define AUDPKT_CHSTATUS_OVR9 0xe84
#define AUDPKT_CHSTATUS_OVR10 0xe88
#define AUDPKT_CHSTATUS_OVR11 0xe8c
#define AUDPKT_CHSTATUS_OVR12 0xe90
#define AUDPKT_CHSTATUS_OVR13 0xe94
#define AUDPKT_CHSTATUS_OVR14 0xe98
#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac
#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc
#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc
#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc
#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec
#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0
#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4
#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8
#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc
#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00
#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04
#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08
#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c
#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10
#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14
#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18
#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c
#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20
#define AUDPKT_VBIT_OVR0 0xf24
/* CEC Registers */
#define CEC_TX_CONTROL 0x1000
#define CEC_STATUS 0x1004
#define CEC_CONFIG 0x1008
#define CEC_ADDR 0x100c
#define CEC_TX_COUNT 0x1020
#define CEC_TX_DATA3_0 0x1024
#define CEC_TX_DATA7_4 0x1028
#define CEC_TX_DATA11_8 0x102c
#define CEC_TX_DATA15_12 0x1030
#define CEC_RX_COUNT_STATUS 0x1040
#define CEC_RX_DATA3_0 0x1044
#define CEC_RX_DATA7_4 0x1048
#define CEC_RX_DATA11_8 0x104c
#define CEC_RX_DATA15_12 0x1050
#define CEC_LOCK_CONTROL 0x1054
#define CEC_RXQUAL_BITTIME_CONFIG 0x1060
#define CEC_RX_BITTIME_CONFIG 0x1064
#define CEC_TX_BITTIME_CONFIG 0x1068
/* eARC RX CMDC Registers */
#define EARCRX_CMDC_CONFIG0 0x1800
#define EARCRX_XACTREAD_STOP_CFG BIT(26)
#define EARCRX_XACTREAD_RETRY_CFG BIT(25)
#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24)
#define EARCRX_CMDC_XACT_RESTART_EN BIT(18)
#define EARCRX_CMDC_CONFIG1 0x1804
#define EARCRX_CMDC_CONTROL 0x1808
#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4)
#define EARCRX_CMDC_DISCOVERY_EN BIT(3)
#define EARCRX_CONNECTOR_HPD BIT(1)
#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c
#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810
#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814
#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818
#define EARCRX_CMDC_STATUS 0x181c
#define EARCRX_CMDC_XACT_INFO 0x1820
#define EARCRX_CMDC_XACT_ACTION 0x1824
#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828
#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c
#define EARCRX_CMDC_XACT_WR0 0x1840
#define EARCRX_CMDC_XACT_WR1 0x1844
#define EARCRX_CMDC_XACT_WR2 0x1848
#define EARCRX_CMDC_XACT_WR3 0x184c
#define EARCRX_CMDC_XACT_WR4 0x1850
#define EARCRX_CMDC_XACT_WR5 0x1854
#define EARCRX_CMDC_XACT_WR6 0x1858
#define EARCRX_CMDC_XACT_WR7 0x185c
#define EARCRX_CMDC_XACT_WR8 0x1860
#define EARCRX_CMDC_XACT_WR9 0x1864
#define EARCRX_CMDC_XACT_WR10 0x1868
#define EARCRX_CMDC_XACT_WR11 0x186c
#define EARCRX_CMDC_XACT_WR12 0x1870
#define EARCRX_CMDC_XACT_WR13 0x1874
#define EARCRX_CMDC_XACT_WR14 0x1878
#define EARCRX_CMDC_XACT_WR15 0x187c
#define EARCRX_CMDC_XACT_WR16 0x1880
#define EARCRX_CMDC_XACT_WR17 0x1884
#define EARCRX_CMDC_XACT_WR18 0x1888
#define EARCRX_CMDC_XACT_WR19 0x188c
#define EARCRX_CMDC_XACT_WR20 0x1890
#define EARCRX_CMDC_XACT_WR21 0x1894
#define EARCRX_CMDC_XACT_WR22 0x1898
#define EARCRX_CMDC_XACT_WR23 0x189c
#define EARCRX_CMDC_XACT_WR24 0x18a0
#define EARCRX_CMDC_XACT_WR25 0x18a4
#define EARCRX_CMDC_XACT_WR26 0x18a8
#define EARCRX_CMDC_XACT_WR27 0x18ac
#define EARCRX_CMDC_XACT_WR28 0x18b0
#define EARCRX_CMDC_XACT_WR29 0x18b4
#define EARCRX_CMDC_XACT_WR30 0x18b8
#define EARCRX_CMDC_XACT_WR31 0x18bc
#define EARCRX_CMDC_XACT_WR32 0x18c0
#define EARCRX_CMDC_XACT_WR33 0x18c4
#define EARCRX_CMDC_XACT_WR34 0x18c8
#define EARCRX_CMDC_XACT_WR35 0x18cc
#define EARCRX_CMDC_XACT_WR36 0x18d0
#define EARCRX_CMDC_XACT_WR37 0x18d4
#define EARCRX_CMDC_XACT_WR38 0x18d8
#define EARCRX_CMDC_XACT_WR39 0x18dc
#define EARCRX_CMDC_XACT_WR40 0x18e0
#define EARCRX_CMDC_XACT_WR41 0x18e4
#define EARCRX_CMDC_XACT_WR42 0x18e8
#define EARCRX_CMDC_XACT_WR43 0x18ec
#define EARCRX_CMDC_XACT_WR44 0x18f0
#define EARCRX_CMDC_XACT_WR45 0x18f4
#define EARCRX_CMDC_XACT_WR46 0x18f8
#define EARCRX_CMDC_XACT_WR47 0x18fc
#define EARCRX_CMDC_XACT_WR48 0x1900
#define EARCRX_CMDC_XACT_WR49 0x1904
#define EARCRX_CMDC_XACT_WR50 0x1908
#define EARCRX_CMDC_XACT_WR51 0x190c
#define EARCRX_CMDC_XACT_WR52 0x1910
#define EARCRX_CMDC_XACT_WR53 0x1914
#define EARCRX_CMDC_XACT_WR54 0x1918
#define EARCRX_CMDC_XACT_WR55 0x191c
#define EARCRX_CMDC_XACT_WR56 0x1920
#define EARCRX_CMDC_XACT_WR57 0x1924
#define EARCRX_CMDC_XACT_WR58 0x1928
#define EARCRX_CMDC_XACT_WR59 0x192c
#define EARCRX_CMDC_XACT_WR60 0x1930
#define EARCRX_CMDC_XACT_WR61 0x1934
#define EARCRX_CMDC_XACT_WR62 0x1938
#define EARCRX_CMDC_XACT_WR63 0x193c
#define EARCRX_CMDC_XACT_WR64 0x1940
#define EARCRX_CMDC_XACT_RD0 0x1960
#define EARCRX_CMDC_XACT_RD1 0x1964
#define EARCRX_CMDC_XACT_RD2 0x1968
#define EARCRX_CMDC_XACT_RD3 0x196c
#define EARCRX_CMDC_XACT_RD4 0x1970
#define EARCRX_CMDC_XACT_RD5 0x1974
#define EARCRX_CMDC_XACT_RD6 0x1978
#define EARCRX_CMDC_XACT_RD7 0x197c
#define EARCRX_CMDC_XACT_RD8 0x1980
#define EARCRX_CMDC_XACT_RD9 0x1984
#define EARCRX_CMDC_XACT_RD10 0x1988
#define EARCRX_CMDC_XACT_RD11 0x198c
#define EARCRX_CMDC_XACT_RD12 0x1990
#define EARCRX_CMDC_XACT_RD13 0x1994
#define EARCRX_CMDC_XACT_RD14 0x1998
#define EARCRX_CMDC_XACT_RD15 0x199c
#define EARCRX_CMDC_XACT_RD16 0x19a0
#define EARCRX_CMDC_XACT_RD17 0x19a4
#define EARCRX_CMDC_XACT_RD18 0x19a8
#define EARCRX_CMDC_XACT_RD19 0x19ac
#define EARCRX_CMDC_XACT_RD20 0x19b0
#define EARCRX_CMDC_XACT_RD21 0x19b4
#define EARCRX_CMDC_XACT_RD22 0x19b8
#define EARCRX_CMDC_XACT_RD23 0x19bc
#define EARCRX_CMDC_XACT_RD24 0x19c0
#define EARCRX_CMDC_XACT_RD25 0x19c4
#define EARCRX_CMDC_XACT_RD26 0x19c8
#define EARCRX_CMDC_XACT_RD27 0x19cc
#define EARCRX_CMDC_XACT_RD28 0x19d0
#define EARCRX_CMDC_XACT_RD29 0x19d4
#define EARCRX_CMDC_XACT_RD30 0x19d8
#define EARCRX_CMDC_XACT_RD31 0x19dc
#define EARCRX_CMDC_XACT_RD32 0x19e0
#define EARCRX_CMDC_XACT_RD33 0x19e4
#define EARCRX_CMDC_XACT_RD34 0x19e8
#define EARCRX_CMDC_XACT_RD35 0x19ec
#define EARCRX_CMDC_XACT_RD36 0x19f0
#define EARCRX_CMDC_XACT_RD37 0x19f4
#define EARCRX_CMDC_XACT_RD38 0x19f8
#define EARCRX_CMDC_XACT_RD39 0x19fc
#define EARCRX_CMDC_XACT_RD40 0x1a00
#define EARCRX_CMDC_XACT_RD41 0x1a04
#define EARCRX_CMDC_XACT_RD42 0x1a08
#define EARCRX_CMDC_XACT_RD43 0x1a0c
#define EARCRX_CMDC_XACT_RD44 0x1a10
#define EARCRX_CMDC_XACT_RD45 0x1a14
#define EARCRX_CMDC_XACT_RD46 0x1a18
#define EARCRX_CMDC_XACT_RD47 0x1a1c
#define EARCRX_CMDC_XACT_RD48 0x1a20
#define EARCRX_CMDC_XACT_RD49 0x1a24
#define EARCRX_CMDC_XACT_RD50 0x1a28
#define EARCRX_CMDC_XACT_RD51 0x1a2c
#define EARCRX_CMDC_XACT_RD52 0x1a30
#define EARCRX_CMDC_XACT_RD53 0x1a34
#define EARCRX_CMDC_XACT_RD54 0x1a38
#define EARCRX_CMDC_XACT_RD55 0x1a3c
#define EARCRX_CMDC_XACT_RD56 0x1a40
#define EARCRX_CMDC_XACT_RD57 0x1a44
#define EARCRX_CMDC_XACT_RD58 0x1a48
#define EARCRX_CMDC_XACT_RD59 0x1a4c
#define EARCRX_CMDC_XACT_RD60 0x1a50
#define EARCRX_CMDC_XACT_RD61 0x1a54
#define EARCRX_CMDC_XACT_RD62 0x1a58
#define EARCRX_CMDC_XACT_RD63 0x1a5c
#define EARCRX_CMDC_XACT_RD64 0x1a60
#define EARCRX_CMDC_SYNC_CONFIG 0x1b00
/* eARC RX DMAC Registers */
#define EARCRX_DMAC_PHY_CONTROL 0x1c00
#define EARCRX_DMAC_CONFIG 0x1c08
#define EARCRX_DMAC_CONTROL0 0x1c0c
#define EARCRX_DMAC_AUDIO_EN BIT(1)
#define EARCRX_DMAC_EN BIT(0)
#define EARCRX_DMAC_CONTROL1 0x1c10
#define EARCRX_DMAC_STATUS 0x1c14
#define EARCRX_DMAC_CHSTATUS0 0x1c18
#define EARCRX_DMAC_CHSTATUS1 0x1c1c
#define EARCRX_DMAC_CHSTATUS2 0x1c20
#define EARCRX_DMAC_CHSTATUS3 0x1c24
#define EARCRX_DMAC_CHSTATUS4 0x1c28
#define EARCRX_DMAC_CHSTATUS5 0x1c2c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58
#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8
#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc
#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0
#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4
#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8
#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc
#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0
#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4
#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8
#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc
#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0
#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4
#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8
#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec
#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0
#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4
#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8
#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc
#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00
#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04
#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08
#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c
#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10
#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14
#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18
#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c
#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20
#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24
#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28
#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c
#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30
#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34
#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38
#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c
#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40
#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44
#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48
#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c
#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50
#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54
#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58
#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c
#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60
#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64
#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68
#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c
#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70
#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74
#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78
#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c
#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80
/* Main Unit Interrupt Registers */
#define MAIN_INTVEC_INDEX 0x3000
#define MAINUNIT_0_INT_STATUS 0x3010
#define MAINUNIT_0_INT_MASK_N 0x3014
#define MAINUNIT_0_INT_CLEAR 0x3018
#define MAINUNIT_0_INT_FORCE 0x301c
#define MAINUNIT_1_INT_STATUS 0x3020
#define FLT_EXIT_TO_LTSL_IRQ BIT(22)
#define FLT_EXIT_TO_LTS4_IRQ BIT(21)
#define FLT_EXIT_TO_LTSP_IRQ BIT(20)
#define SCDC_NACK_RCVD_IRQ BIT(12)
#define SCDC_RR_REPLY_STOP_IRQ BIT(11)
#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10)
#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9)
#define SCDC_UPD_FLAGS_RD_IRQ BIT(8)
#define I2CM_NACK_RCVD_IRQ BIT(2)
#define I2CM_READ_REQUEST_IRQ BIT(1)
#define I2CM_OP_DONE_IRQ BIT(0)
#define MAINUNIT_1_INT_MASK_N 0x3024
#define I2CM_NACK_RCVD_MASK_N BIT(2)
#define I2CM_READ_REQUEST_MASK_N BIT(1)
#define I2CM_OP_DONE_MASK_N BIT(0)
#define MAINUNIT_1_INT_CLEAR 0x3028
#define I2CM_NACK_RCVD_CLEAR BIT(2)
#define I2CM_READ_REQUEST_CLEAR BIT(1)
#define I2CM_OP_DONE_CLEAR BIT(0)
#define MAINUNIT_1_INT_FORCE 0x302c
/* AVPUNIT Interrupt Registers */
#define AVP_INTVEC_INDEX 0x3800
#define AVP_0_INT_STATUS 0x3810
#define AVP_0_INT_MASK_N 0x3814
#define AVP_0_INT_CLEAR 0x3818
#define AVP_0_INT_FORCE 0x381c
#define AVP_1_INT_STATUS 0x3820
#define AVP_1_INT_MASK_N 0x3824
#define HDCP14_AUTH_CHG_MASK_N BIT(6)
#define AVP_1_INT_CLEAR 0x3828
#define AVP_1_INT_FORCE 0x382c
#define AVP_2_INT_STATUS 0x3830
#define AVP_2_INT_MASK_N 0x3834
#define AVP_2_INT_CLEAR 0x3838
#define AVP_2_INT_FORCE 0x383c
#define AVP_3_INT_STATUS 0x3840
#define AVP_3_INT_MASK_N 0x3844
#define AVP_3_INT_CLEAR 0x3848
#define AVP_3_INT_FORCE 0x384c
#define AVP_4_INT_STATUS 0x3850
#define AVP_4_INT_MASK_N 0x3854
#define AVP_4_INT_CLEAR 0x3858
#define AVP_4_INT_FORCE 0x385c
#define AVP_5_INT_STATUS 0x3860
#define AVP_5_INT_MASK_N 0x3864
#define AVP_5_INT_CLEAR 0x3868
#define AVP_5_INT_FORCE 0x386c
#define AVP_6_INT_STATUS 0x3870
#define AVP_6_INT_MASK_N 0x3874
#define AVP_6_INT_CLEAR 0x3878
#define AVP_6_INT_FORCE 0x387c
/* CEC Interrupt Registers */
#define CEC_INT_STATUS 0x4000
#define CEC_INT_MASK_N 0x4004
#define CEC_INT_CLEAR 0x4008
#define CEC_INT_FORCE 0x400c
/* eARC RX Interrupt Registers */
#define EARCRX_INTVEC_INDEX 0x4800
#define EARCRX_0_INT_STATUS 0x4810
#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9)
#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8)
#define EARCRX_0_INT_MASK_N 0x4814
#define EARCRX_0_INT_CLEAR 0x4818
#define EARCRX_0_INT_FORCE 0x481c
#define EARCRX_1_INT_STATUS 0x4820
#define EARCRX_1_INT_MASK_N 0x4824
#define EARCRX_1_INT_CLEAR 0x4828
#define EARCRX_1_INT_FORCE 0x482c
#endif /* __DW_HDMI_QP_H__ */

View File

@ -0,0 +1,628 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
* Copyright (c) 2024 Collabora Ltd.
*
* Author: Algea Cao <algea.cao@rock-chips.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
*/
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <linux/workqueue.h>
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
#define RK3576_IOC_MISC_CON0 0xa400
#define RK3576_HDMI_HPD_INT_MSK BIT(2)
#define RK3576_HDMI_HPD_INT_CLR BIT(1)
#define RK3576_IOC_HDMI_HPD_STATUS 0xa440
#define RK3576_HDMI_LEVEL_INT BIT(3)
#define RK3576_VO0_GRF_SOC_CON1 0x0004
#define RK3576_HDMI_FRL_MOD BIT(0)
#define RK3576_HDMI_HDCP14_MEM_EN BIT(15)
#define RK3576_VO0_GRF_SOC_CON8 0x0020
#define RK3576_COLOR_FORMAT_MASK (0xf << 4)
#define RK3576_COLOR_DEPTH_MASK (0xf << 8)
#define RK3576_RGB (0 << 4)
#define RK3576_YUV422 (0x1 << 4)
#define RK3576_YUV444 (0x2 << 4)
#define RK3576_YUV420 (0x3 << 4)
#define RK3576_8BPC (0x0 << 8)
#define RK3576_10BPC (0x6 << 8)
#define RK3576_CECIN_MASK BIT(3)
#define RK3576_VO0_GRF_SOC_CON12 0x0030
#define RK3576_GRF_OSDA_DLYN (0xf << 12)
#define RK3576_GRF_OSDA_DIV (0x7f << 1)
#define RK3576_GRF_OSDA_DLY_EN BIT(0)
#define RK3576_VO0_GRF_SOC_CON14 0x0038
#define RK3576_I2S_SEL_MASK BIT(0)
#define RK3576_SPDIF_SEL_MASK BIT(1)
#define HDCP0_P1_GPIO_IN BIT(2)
#define RK3576_SCLIN_MASK BIT(4)
#define RK3576_SDAIN_MASK BIT(5)
#define RK3576_HDMI_GRANT_SEL BIT(6)
#define RK3588_GRF_SOC_CON2 0x0308
#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
#define RK3588_HDMI1_HPD_INT_MSK BIT(15)
#define RK3588_HDMI1_HPD_INT_CLR BIT(14)
#define RK3588_GRF_SOC_CON7 0x031c
#define RK3588_SET_HPD_PATH_MASK GENMASK(13, 12)
#define RK3588_GRF_SOC_STATUS1 0x0384
#define RK3588_HDMI0_LEVEL_INT BIT(16)
#define RK3588_HDMI1_LEVEL_INT BIT(24)
#define RK3588_GRF_VO1_CON3 0x000c
#define RK3588_GRF_VO1_CON6 0x0018
#define RK3588_SCLIN_MASK BIT(9)
#define RK3588_SDAIN_MASK BIT(10)
#define RK3588_MODE_MASK BIT(11)
#define RK3588_I2S_SEL_MASK BIT(13)
#define RK3588_GRF_VO1_CON9 0x0024
#define RK3588_HDMI0_GRANT_SEL BIT(10)
#define RK3588_HDMI1_GRANT_SEL BIT(12)
#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
#define HOTPLUG_DEBOUNCE_MS 150
#define MAX_HDMI_PORT_NUM 2
struct rockchip_hdmi_qp {
struct device *dev;
struct regmap *regmap;
struct regmap *vo_regmap;
struct rockchip_encoder encoder;
struct dw_hdmi_qp *hdmi;
struct phy *phy;
struct gpio_desc *enable_gpio;
struct delayed_work hpd_work;
int port_id;
const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
};
struct rockchip_hdmi_qp_ctrl_ops {
void (*io_init)(struct rockchip_hdmi_qp *hdmi);
irqreturn_t (*irq_callback)(int irq, void *dev_id);
irqreturn_t (*hardirq_callback)(int irq, void *dev_id);
};
static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
{
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
}
static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
struct drm_crtc *crtc = encoder->crtc;
unsigned long long rate;
/* Unconditionally switch to TMDS as FRL is not yet supported */
gpiod_set_value(hdmi->enable_gpio, 1);
if (crtc && crtc->state) {
rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
8, HDMI_COLORSPACE_RGB);
/*
* FIXME: Temporary workaround to pass pixel clock rate
* to the PHY driver until phy_configure_opts_hdmi
* becomes available in the PHY API. See also the related
* comment in rk_hdptx_phy_power_on() from
* drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
*/
phy_set_bus_width(hdmi->phy, div_u64(rate, 100));
}
}
static int
dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
return 0;
}
static const struct
drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
.enable = dw_hdmi_qp_rockchip_encoder_enable,
.atomic_check = dw_hdmi_qp_rockchip_encoder_atomic_check,
};
static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
return phy_power_on(hdmi->phy);
}
static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
phy_power_off(hdmi->phy);
}
static enum drm_connector_status
dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
u32 val;
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
val &= hdmi->port_id ? RK3588_HDMI1_LEVEL_INT : RK3588_HDMI0_LEVEL_INT;
return val ? connector_status_connected : connector_status_disconnected;
}
static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
u32 val;
if (hdmi->port_id)
val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
RK3588_HDMI1_HPD_INT_CLR | RK3588_HDMI1_HPD_INT_MSK);
else
val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
RK3588_HDMI0_HPD_INT_CLR | RK3588_HDMI0_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
}
static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
.init = dw_hdmi_qp_rk3588_phy_init,
.disable = dw_hdmi_qp_rk3588_phy_disable,
.read_hpd = dw_hdmi_qp_rk3588_read_hpd,
.setup_hpd = dw_hdmi_qp_rk3588_setup_hpd,
};
static enum drm_connector_status
dw_hdmi_qp_rk3576_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
u32 val;
regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &val);
return val & RK3576_HDMI_LEVEL_INT ?
connector_status_connected : connector_status_disconnected;
}
static void dw_hdmi_qp_rk3576_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
u32 val;
val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_CLR,
RK3576_HDMI_HPD_INT_CLR | RK3576_HDMI_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
regmap_write(hdmi->regmap, 0xa404, 0xffff0102);
}
static const struct dw_hdmi_qp_phy_ops rk3576_hdmi_phy_ops = {
.init = dw_hdmi_qp_rk3588_phy_init,
.disable = dw_hdmi_qp_rk3588_phy_disable,
.read_hpd = dw_hdmi_qp_rk3576_read_hpd,
.setup_hpd = dw_hdmi_qp_rk3576_setup_hpd,
};
static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
{
struct rockchip_hdmi_qp *hdmi = container_of(work,
struct rockchip_hdmi_qp,
hpd_work.work);
struct drm_device *drm = hdmi->encoder.encoder.dev;
bool changed;
if (drm) {
changed = drm_helper_hpd_irq_event(drm);
if (changed)
dev_dbg(hdmi->dev, "connector status changed\n");
}
}
static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
if (intr_stat) {
val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_MSK, RK3576_HDMI_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
return IRQ_WAKE_THREAD;
}
return IRQ_NONE;
}
static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
if (!intr_stat)
return IRQ_NONE;
val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_CLR, RK3576_HDMI_HPD_INT_CLR);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
mod_delayed_work(system_wq, &hdmi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
val = HIWORD_UPDATE(0, RK3576_HDMI_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
return IRQ_HANDLED;
}
static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
if (intr_stat) {
if (hdmi->port_id)
val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK,
RK3588_HDMI1_HPD_INT_MSK);
else
val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
RK3588_HDMI0_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
return IRQ_WAKE_THREAD;
}
return IRQ_NONE;
}
static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
if (!intr_stat)
return IRQ_NONE;
if (hdmi->port_id)
val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
RK3588_HDMI1_HPD_INT_CLR);
else
val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
RK3588_HDMI0_HPD_INT_CLR);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
mod_delayed_work(system_wq, &hdmi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
if (hdmi->port_id)
val |= HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
else
val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
return IRQ_HANDLED;
}
static void dw_hdmi_qp_rk3576_io_init(struct rockchip_hdmi_qp *hdmi)
{
u32 val;
val = HIWORD_UPDATE(RK3576_SCLIN_MASK, RK3576_SCLIN_MASK) |
HIWORD_UPDATE(RK3576_SDAIN_MASK, RK3576_SDAIN_MASK) |
HIWORD_UPDATE(RK3576_HDMI_GRANT_SEL, RK3576_HDMI_GRANT_SEL) |
HIWORD_UPDATE(RK3576_I2S_SEL_MASK, RK3576_I2S_SEL_MASK);
regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON14, val);
val = HIWORD_UPDATE(0, RK3576_HDMI_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
}
static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
{
u32 val;
val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
regmap_write(hdmi->vo_regmap,
hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
val);
val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, RK3588_SET_HPD_PATH_MASK);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
if (hdmi->port_id)
val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, RK3588_HDMI1_GRANT_SEL);
else
val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, RK3588_HDMI0_GRANT_SEL);
regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
if (hdmi->port_id)
val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK);
else
val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
}
static const struct rockchip_hdmi_qp_ctrl_ops rk3576_hdmi_ctrl_ops = {
.io_init = dw_hdmi_qp_rk3576_io_init,
.irq_callback = dw_hdmi_qp_rk3576_irq,
.hardirq_callback = dw_hdmi_qp_rk3576_hardirq,
};
static const struct rockchip_hdmi_qp_ctrl_ops rk3588_hdmi_ctrl_ops = {
.io_init = dw_hdmi_qp_rk3588_io_init,
.irq_callback = dw_hdmi_qp_rk3588_irq,
.hardirq_callback = dw_hdmi_qp_rk3588_hardirq,
};
struct rockchip_hdmi_qp_cfg {
unsigned int num_ports;
unsigned int port_ids[MAX_HDMI_PORT_NUM];
const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
const struct dw_hdmi_qp_phy_ops *phy_ops;
};
static const struct rockchip_hdmi_qp_cfg rk3576_hdmi_cfg = {
.num_ports = 1,
.port_ids = {
0x27da0000,
},
.ctrl_ops = &rk3576_hdmi_ctrl_ops,
.phy_ops = &rk3576_hdmi_phy_ops,
};
static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = {
.num_ports = 2,
.port_ids = {
0xfde80000,
0xfdea0000,
},
.ctrl_ops = &rk3588_hdmi_ctrl_ops,
.phy_ops = &rk3588_hdmi_phy_ops,
};
static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
{
.compatible = "rockchip,rk3576-dw-hdmi-qp",
.data = &rk3576_hdmi_cfg
}, {
.compatible = "rockchip,rk3588-dw-hdmi-qp",
.data = &rk3588_hdmi_cfg
},
{},
};
MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
struct platform_device *pdev = to_platform_device(dev);
const struct rockchip_hdmi_qp_cfg *cfg;
struct dw_hdmi_qp_plat_data plat_data;
struct drm_device *drm = data;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct rockchip_hdmi_qp *hdmi;
struct resource *res;
struct clk_bulk_data *clks;
int ret, irq, i;
if (!pdev->dev.of_node)
return -ENODEV;
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
cfg = of_device_get_match_data(dev);
if (!cfg)
return -ENODEV;
if (!cfg->ctrl_ops || !cfg->ctrl_ops->io_init ||
!cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback) {
dev_err(dev, "Missing platform ctrl ops\n");
return -ENODEV;
}
hdmi->ctrl_ops = cfg->ctrl_ops;
hdmi->dev = &pdev->dev;
hdmi->port_id = -ENODEV;
/* Identify port ID by matching base IO address */
for (i = 0; i < cfg->num_ports; i++) {
if (res->start == cfg->port_ids[i]) {
hdmi->port_id = i;
break;
}
}
if (hdmi->port_id < 0) {
dev_err(hdmi->dev, "Failed to match HDMI port ID\n");
return hdmi->port_id;
}
plat_data.phy_ops = cfg->phy_ops;
plat_data.phy_data = hdmi;
encoder = &hdmi->encoder.encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
dev->of_node, 0, 0);
/*
* If we failed to find the CRTC(s) which this encoder is
* supposed to be connected to, it's because the CRTC has
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (encoder->possible_crtcs == 0)
return -EPROBE_DEFER;
hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(hdmi->regmap)) {
dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
return PTR_ERR(hdmi->regmap);
}
hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,vo-grf");
if (IS_ERR(hdmi->vo_regmap)) {
dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n");
return PTR_ERR(hdmi->vo_regmap);
}
ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
if (ret < 0) {
dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
return ret;
}
hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
GPIOD_OUT_HIGH);
if (IS_ERR(hdmi->enable_gpio)) {
ret = PTR_ERR(hdmi->enable_gpio);
dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret);
return ret;
}
hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
if (IS_ERR(hdmi->phy)) {
ret = PTR_ERR(hdmi->phy);
if (ret != -EPROBE_DEFER)
dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
return ret;
}
cfg->ctrl_ops->io_init(hdmi);
INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
plat_data.main_irq = platform_get_irq_byname(pdev, "main");
if (plat_data.main_irq < 0)
return plat_data.main_irq;
irq = platform_get_irq_byname(pdev, "hpd");
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(hdmi->dev, irq,
cfg->ctrl_ops->hardirq_callback,
cfg->ctrl_ops->irq_callback,
IRQF_SHARED, "dw-hdmi-qp-hpd",
hdmi);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
platform_set_drvdata(pdev, hdmi);
hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
if (IS_ERR(hdmi->hdmi)) {
ret = PTR_ERR(hdmi->hdmi);
drm_encoder_cleanup(encoder);
return ret;
}
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector)) {
ret = PTR_ERR(connector);
dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
return ret;
}
return drm_connector_attach_encoder(connector, encoder);
}
static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
struct device *master,
void *data)
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
cancel_delayed_work_sync(&hdmi->hpd_work);
drm_encoder_cleanup(&hdmi->encoder.encoder);
}
static const struct component_ops dw_hdmi_qp_rockchip_ops = {
.bind = dw_hdmi_qp_rockchip_bind,
.unbind = dw_hdmi_qp_rockchip_unbind,
};
static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
}
static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
}
static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
hdmi->ctrl_ops->io_init(hdmi);
dw_hdmi_qp_resume(dev, hdmi->hdmi);
if (hdmi->encoder.encoder.dev)
drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
return 0;
}
static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume)
};
struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
.probe = dw_hdmi_qp_rockchip_probe,
.remove = dw_hdmi_qp_rockchip_remove,
.driver = {
.name = "dwhdmiqp-rockchip",
.pm = &dw_hdmi_qp_rockchip_pm,
.of_match_table = dw_hdmi_qp_rockchip_dt_ids,
},
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,867 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
#ifndef _ROCKCHIP_DRM_VOP2_H
#define _ROCKCHIP_DRM_VOP2_H
#include <linux/regmap.h>
#include <drm/drm_modes.h>
#include <dt-bindings/soc/rockchip,vop2.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_vop.h"
#define VOP2_VERSION(major, minor, build) ((major) << 24 | (minor) << 16 | (build))
/* The VOP version of new SoC is bigger than the old */
#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023)
#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786)
#define VOP_VERSION_RK3528 VOP2_VERSION(0x50, 0x17, 0x1263)
#define VOP_VERSION_RK3562 VOP2_VERSION(0x50, 0x17, 0x4350)
#define VOP_VERSION_RK3576 VOP2_VERSION(0x50, 0x19, 0x9765)
#define VOP2_VP_FEATURE_OUTPUT_10BIT BIT(0)
#define VOP2_FEATURE_HAS_SYS_GRF BIT(0)
#define VOP2_FEATURE_HAS_VO0_GRF BIT(1)
#define VOP2_FEATURE_HAS_VO1_GRF BIT(2)
#define VOP2_FEATURE_HAS_VOP_GRF BIT(3)
#define VOP2_FEATURE_HAS_SYS_PMU BIT(4)
#define WIN_FEATURE_AFBDC BIT(0)
#define WIN_FEATURE_CLUSTER BIT(1)
#define HIWORD_UPDATE(v, h, l) ((GENMASK(h, l) << 16) | ((v) << (l)))
/*
* the delay number of a window in different mode.
*/
enum win_dly_mode {
VOP2_DLY_MODE_DEFAULT, /**< default mode */
VOP2_DLY_MODE_HISO_S, /** HDR in SDR out mode, as a SDR window */
VOP2_DLY_MODE_HIHO_H, /** HDR in HDR out mode, as a HDR window */
VOP2_DLY_MODE_MAX,
};
enum vop2_dly_module {
VOP2_DLY_WIN, /** Win delay cycle for this VP */
VOP2_DLY_LAYER_MIX, /** Layer Mix delay cycle for this VP */
VOP2_DLY_HDR_MIX, /** HDR delay cycle for this VP */
VOP2_DLY_MAX,
};
enum vop2_scale_up_mode {
VOP2_SCALE_UP_NRST_NBOR,
VOP2_SCALE_UP_BIL,
VOP2_SCALE_UP_BIC,
};
enum vop2_scale_down_mode {
VOP2_SCALE_DOWN_NRST_NBOR,
VOP2_SCALE_DOWN_BIL,
VOP2_SCALE_DOWN_AVG,
};
/*
* vop2 internal power domain id,
* should be all none zero, 0 will be treat as invalid;
*/
#define VOP2_PD_CLUSTER0 BIT(0)
#define VOP2_PD_CLUSTER1 BIT(1)
#define VOP2_PD_CLUSTER2 BIT(2)
#define VOP2_PD_CLUSTER3 BIT(3)
#define VOP2_PD_DSC_8K BIT(5)
#define VOP2_PD_DSC_4K BIT(6)
#define VOP2_PD_ESMART BIT(7)
#define vop2_output_if_is_hdmi(x) ((x) == ROCKCHIP_VOP2_EP_HDMI0 || \
(x) == ROCKCHIP_VOP2_EP_HDMI1)
#define vop2_output_if_is_dp(x) ((x) == ROCKCHIP_VOP2_EP_DP0 || \
(x) == ROCKCHIP_VOP2_EP_DP1)
#define vop2_output_if_is_edp(x) ((x) == ROCKCHIP_VOP2_EP_EDP0 || \
(x) == ROCKCHIP_VOP2_EP_EDP1)
#define vop2_output_if_is_mipi(x) ((x) == ROCKCHIP_VOP2_EP_MIPI0 || \
(x) == ROCKCHIP_VOP2_EP_MIPI1)
#define vop2_output_if_is_lvds(x) ((x) == ROCKCHIP_VOP2_EP_LVDS0 || \
(x) == ROCKCHIP_VOP2_EP_LVDS1)
#define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0)
enum vop2_win_regs {
VOP2_WIN_ENABLE,
VOP2_WIN_FORMAT,
VOP2_WIN_CSC_MODE,
VOP2_WIN_XMIRROR,
VOP2_WIN_YMIRROR,
VOP2_WIN_RB_SWAP,
VOP2_WIN_UV_SWAP,
VOP2_WIN_ACT_INFO,
VOP2_WIN_DSP_INFO,
VOP2_WIN_DSP_ST,
VOP2_WIN_YRGB_MST,
VOP2_WIN_UV_MST,
VOP2_WIN_YRGB_VIR,
VOP2_WIN_UV_VIR,
VOP2_WIN_YUV_CLIP,
VOP2_WIN_Y2R_EN,
VOP2_WIN_R2Y_EN,
VOP2_WIN_COLOR_KEY,
VOP2_WIN_COLOR_KEY_EN,
VOP2_WIN_DITHER_UP,
VOP2_WIN_AXI_BUS_ID,
VOP2_WIN_AXI_YRGB_R_ID,
VOP2_WIN_AXI_UV_R_ID,
/* scale regs */
VOP2_WIN_SCALE_YRGB_X,
VOP2_WIN_SCALE_YRGB_Y,
VOP2_WIN_SCALE_CBCR_X,
VOP2_WIN_SCALE_CBCR_Y,
VOP2_WIN_YRGB_HOR_SCL_MODE,
VOP2_WIN_YRGB_HSCL_FILTER_MODE,
VOP2_WIN_YRGB_VER_SCL_MODE,
VOP2_WIN_YRGB_VSCL_FILTER_MODE,
VOP2_WIN_CBCR_VER_SCL_MODE,
VOP2_WIN_CBCR_HSCL_FILTER_MODE,
VOP2_WIN_CBCR_HOR_SCL_MODE,
VOP2_WIN_CBCR_VSCL_FILTER_MODE,
VOP2_WIN_VSD_CBCR_GT2,
VOP2_WIN_VSD_CBCR_GT4,
VOP2_WIN_VSD_YRGB_GT2,
VOP2_WIN_VSD_YRGB_GT4,
VOP2_WIN_BIC_COE_SEL,
/* cluster regs */
VOP2_WIN_CLUSTER_ENABLE,
VOP2_WIN_AFBC_ENABLE,
VOP2_WIN_CLUSTER_LB_MODE,
/* afbc regs */
VOP2_WIN_AFBC_FORMAT,
VOP2_WIN_AFBC_RB_SWAP,
VOP2_WIN_AFBC_UV_SWAP,
VOP2_WIN_AFBC_AUTO_GATING_EN,
VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
VOP2_WIN_AFBC_PLD_OFFSET_EN,
VOP2_WIN_AFBC_PIC_VIR_WIDTH,
VOP2_WIN_AFBC_TILE_NUM,
VOP2_WIN_AFBC_PIC_OFFSET,
VOP2_WIN_AFBC_PIC_SIZE,
VOP2_WIN_AFBC_DSP_OFFSET,
VOP2_WIN_AFBC_PLD_OFFSET,
VOP2_WIN_TRANSFORM_OFFSET,
VOP2_WIN_AFBC_HDR_PTR,
VOP2_WIN_AFBC_HALF_BLOCK_EN,
VOP2_WIN_AFBC_ROTATE_270,
VOP2_WIN_AFBC_ROTATE_90,
VOP2_WIN_VP_SEL,
VOP2_WIN_DLY_NUM,
VOP2_WIN_MAX_REG,
};
struct vop2_regs_dump {
const char *name;
u32 base;
u32 size;
u32 en_reg;
u32 en_val;
u32 en_mask;
};
struct vop2_win_data {
const char *name;
unsigned int phys_id;
u32 base;
u32 possible_vp_mask;
enum drm_plane_type type;
u32 nformats;
const u32 *formats;
const uint64_t *format_modifiers;
const unsigned int supported_rotations;
/**
* @layer_sel_id: defined by register OVERLAY_LAYER_SEL or PORTn_LAYER_SEL
*/
unsigned int layer_sel_id[ROCKCHIP_MAX_CRTC];
uint64_t feature;
uint8_t axi_bus_id;
uint8_t axi_yrgb_r_id;
uint8_t axi_uv_r_id;
unsigned int max_upscale_factor;
unsigned int max_downscale_factor;
const u8 dly[VOP2_DLY_MODE_MAX];
};
struct vop2_win {
struct vop2 *vop2;
struct drm_plane base;
const struct vop2_win_data *data;
struct regmap_field *reg[VOP2_WIN_MAX_REG];
/**
* @win_id: graphic window id, a cluster may be split into two
* graphics windows.
*/
u8 win_id;
u8 delay;
u32 offset;
enum drm_plane_type type;
};
struct vop2_video_port_data {
unsigned int id;
u32 feature;
u16 gamma_lut_len;
u16 cubic_lut_len;
struct vop_rect max_output;
const u8 pre_scan_max_dly[4];
unsigned int offset;
/**
* @pixel_rate: pixel per cycle
*/
u8 pixel_rate;
};
struct vop2_video_port {
struct drm_crtc crtc;
struct vop2 *vop2;
struct clk *dclk;
struct clk *dclk_src;
unsigned int id;
const struct vop2_video_port_data *data;
struct completion dsp_hold_completion;
/**
* @win_mask: Bitmask of windows attached to the video port;
*/
u32 win_mask;
struct vop2_win *primary_plane;
struct drm_pending_vblank_event *event;
unsigned int nlayers;
};
/**
* struct vop2_ops - helper operations for vop2 hardware
*
* These hooks are used by the common part of the vop2 driver to
* implement the proper behaviour of different variants.
*/
struct vop2_ops {
unsigned long (*setup_intf_mux)(struct vop2_video_port *vp, int ep_id, u32 polflags);
void (*setup_bg_dly)(struct vop2_video_port *vp);
void (*setup_overlay)(struct vop2_video_port *vp);
};
struct vop2_data {
u8 nr_vps;
u64 feature;
u32 version;
const struct vop2_ops *ops;
const struct vop2_win_data *win;
const struct vop2_video_port_data *vp;
const struct reg_field *cluster_reg;
const struct reg_field *smart_reg;
const struct vop2_regs_dump *regs_dump;
struct vop_rect max_input;
struct vop_rect max_output;
unsigned int nr_cluster_regs;
unsigned int nr_smart_regs;
unsigned int win_size;
unsigned int regs_dump_size;
unsigned int soc_id;
};
struct vop2 {
u32 version;
struct device *dev;
struct drm_device *drm;
struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
const struct vop2_data *data;
const struct vop2_ops *ops;
/*
* Number of windows that are registered as plane, may be less than the
* total number of hardware windows.
*/
u32 registered_num_wins;
struct resource *res;
void __iomem *regs;
struct regmap *map;
struct regmap *sys_grf;
struct regmap *vop_grf;
struct regmap *vo1_grf;
struct regmap *sys_pmu;
/* physical map length of vop2 register */
u32 len;
void __iomem *lut_regs;
/* protects crtc enable/disable */
struct mutex vop2_lock;
int irq;
/*
* Some global resources are shared between all video ports(crtcs), so
* we need a ref counter here.
*/
unsigned int enable_count;
struct clk *hclk;
struct clk *aclk;
struct clk *pclk;
struct clk *pll_hdmiphy0;
struct clk *pll_hdmiphy1;
/* optional internal rgb encoder */
struct rockchip_rgb *rgb;
/*
* Used to record layer selection configuration on rk356x/rk3588
* as register RK3568_OVL_LAYER_SEL and RK3568_OVL_PORT_SEL are
* shared for all the Video Ports.
*/
u32 old_layer_sel;
u32 old_port_sel;
/*
* Ensure that the updates to these two registers(RKK3568_OVL_LAYER_SEL/RK3568_OVL_PORT_SEL)
* take effect in sequence.
*/
struct mutex ovl_lock;
/* must be put at the end of the struct */
struct vop2_win win[];
};
/* interrupt define */
#define FS_NEW_INTR BIT(4)
#define ADDR_SAME_INTR BIT(5)
#define LINE_FLAG1_INTR BIT(6)
#define WIN0_EMPTY_INTR BIT(7)
#define WIN1_EMPTY_INTR BIT(8)
#define WIN2_EMPTY_INTR BIT(9)
#define WIN3_EMPTY_INTR BIT(10)
#define HWC_EMPTY_INTR BIT(11)
#define POST_BUF_EMPTY_INTR BIT(12)
#define PWM_GEN_INTR BIT(13)
#define DMA_FINISH_INTR BIT(14)
#define FS_FIELD_INTR BIT(15)
#define FE_INTR BIT(16)
#define WB_UV_FIFO_FULL_INTR BIT(17)
#define WB_YRGB_FIFO_FULL_INTR BIT(18)
#define WB_COMPLETE_INTR BIT(19)
enum vop_csc_format {
CSC_BT601L,
CSC_BT709L,
CSC_BT601F,
CSC_BT2020,
};
enum src_factor_mode {
SRC_FAC_ALPHA_ZERO,
SRC_FAC_ALPHA_ONE,
SRC_FAC_ALPHA_DST,
SRC_FAC_ALPHA_DST_INVERSE,
SRC_FAC_ALPHA_SRC,
SRC_FAC_ALPHA_SRC_GLOBAL,
};
enum dst_factor_mode {
DST_FAC_ALPHA_ZERO,
DST_FAC_ALPHA_ONE,
DST_FAC_ALPHA_SRC,
DST_FAC_ALPHA_SRC_INVERSE,
DST_FAC_ALPHA_DST,
DST_FAC_ALPHA_DST_GLOBAL,
};
#define RK3568_GRF_VO_CON1 0x0364
#define RK3588_GRF_SOC_CON1 0x0304
#define RK3588_GRF_VOP_CON2 0x08
#define RK3588_GRF_VO1_CON0 0x00
/* System registers definition */
#define RK3568_REG_CFG_DONE 0x000
#define RK3568_VERSION_INFO 0x004
#define RK3568_SYS_AUTO_GATING_CTRL 0x008
#define RK3576_SYS_MMU_CTRL_IMD 0x020
#define RK3568_SYS_AXI_LUT_CTRL 0x024
#define RK3568_DSP_IF_EN 0x028
#define RK3576_SYS_PORT_CTRL_IMD 0x028
#define RK3568_DSP_IF_CTRL 0x02c
#define RK3568_DSP_IF_POL 0x030
#define RK3576_SYS_CLUSTER_PD_CTRL_IMD 0x030
#define RK3588_SYS_PD_CTRL 0x034
#define RK3568_WB_CTRL 0x40
#define RK3568_WB_XSCAL_FACTOR 0x44
#define RK3568_WB_YRGB_MST 0x48
#define RK3568_WB_CBR_MST 0x4C
#define RK3568_OTP_WIN_EN 0x050
#define RK3568_LUT_PORT_SEL 0x058
#define RK3568_SYS_STATUS0 0x060
#define RK3568_VP_LINE_FLAG(vp) (0x70 + (vp) * 0x4)
#define RK3568_SYS0_INT_EN 0x80
#define RK3568_SYS0_INT_CLR 0x84
#define RK3568_SYS0_INT_STATUS 0x88
#define RK3568_SYS1_INT_EN 0x90
#define RK3568_SYS1_INT_CLR 0x94
#define RK3568_SYS1_INT_STATUS 0x98
#define RK3568_VP_INT_EN(vp) (0xA0 + (vp) * 0x10)
#define RK3568_VP_INT_CLR(vp) (0xA4 + (vp) * 0x10)
#define RK3568_VP_INT_STATUS(vp) (0xA8 + (vp) * 0x10)
#define RK3568_VP_INT_RAW_STATUS(vp) (0xAC + (vp) * 0x10)
#define RK3576_WB_CTRL 0x100
#define RK3576_WB_XSCAL_FACTOR 0x104
#define RK3576_WB_YRGB_MST 0x108
#define RK3576_WB_CBR_MST 0x10C
#define RK3576_WB_VIR_STRIDE 0x110
#define RK3576_WB_TIMEOUT_CTRL 0x114
#define RK3576_MIPI0_IF_CTRL 0x180
#define RK3576_HDMI0_IF_CTRL 0x184
#define RK3576_EDP0_IF_CTRL 0x188
#define RK3576_DP0_IF_CTRL 0x18C
#define RK3576_RGB_IF_CTRL 0x194
#define RK3576_DP1_IF_CTRL 0x1A4
#define RK3576_DP2_IF_CTRL 0x1B0
/* Extra OVL register definition */
#define RK3576_SYS_EXTRA_ALPHA_CTRL 0x500
#define RK3576_CLUSTER0_MIX_SRC_COLOR_CTRL 0x530
#define RK3576_CLUSTER0_MIX_DST_COLOR_CTRL 0x534
#define RK3576_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x538
#define RK3576_CLUSTER0_MIX_DST_ALPHA_CTRL 0x53c
#define RK3576_CLUSTER1_MIX_SRC_COLOR_CTRL 0x540
#define RK3576_CLUSTER1_MIX_DST_COLOR_CTRL 0x544
#define RK3576_CLUSTER1_MIX_SRC_ALPHA_CTRL 0x548
#define RK3576_CLUSTER1_MIX_DST_ALPHA_CTRL 0x54c
/* OVL registers for Video Port definition */
#define RK3576_OVL_CTRL(vp) (0x600 + (vp) * 0x100)
#define RK3576_OVL_LAYER_SEL(vp) (0x604 + (vp) * 0x100)
#define RK3576_OVL_MIX0_SRC_COLOR_CTRL(vp) (0x620 + (vp) * 0x100)
#define RK3576_OVL_MIX0_DST_COLOR_CTRL(vp) (0x624 + (vp) * 0x100)
#define RK3576_OVL_MIX0_SRC_ALPHA_CTRL(vp) (0x628 + (vp) * 0x100)
#define RK3576_OVL_MIX0_DST_ALPHA_CTRL(vp) (0x62C + (vp) * 0x100)
#define RK3576_OVL_MIX1_SRC_COLOR_CTRL(vp) (0x630 + (vp) * 0x100)
#define RK3576_OVL_MIX1_DST_COLOR_CTRL(vp) (0x634 + (vp) * 0x100)
#define RK3576_OVL_MIX1_SRC_ALPHA_CTRL(vp) (0x638 + (vp) * 0x100)
#define RK3576_OVL_MIX1_DST_ALPHA_CTRL(vp) (0x63C + (vp) * 0x100)
#define RK3576_OVL_MIX2_SRC_COLOR_CTRL(vp) (0x640 + (vp) * 0x100)
#define RK3576_OVL_MIX2_DST_COLOR_CTRL(vp) (0x644 + (vp) * 0x100)
#define RK3576_OVL_MIX2_SRC_ALPHA_CTRL(vp) (0x648 + (vp) * 0x100)
#define RK3576_OVL_MIX2_DST_ALPHA_CTRL(vp) (0x64C + (vp) * 0x100)
#define RK3576_EXTRA_OVL_SRC_COLOR_CTRL(vp) (0x650 + (vp) * 0x100)
#define RK3576_EXTRA_OVL_DST_COLOR_CTRL(vp) (0x654 + (vp) * 0x100)
#define RK3576_EXTRA_OVL_SRC_ALPHA_CTRL(vp) (0x658 + (vp) * 0x100)
#define RK3576_EXTRA_OVL_DST_ALPHA_CTRL(vp) (0x65C + (vp) * 0x100)
#define RK3576_OVL_HDR_SRC_COLOR_CTRL(vp) (0x660 + (vp) * 0x100)
#define RK3576_OVL_HDR_DST_COLOR_CTRL(vp) (0x664 + (vp) * 0x100)
#define RK3576_OVL_HDR_SRC_ALPHA_CTRL(vp) (0x668 + (vp) * 0x100)
#define RK3576_OVL_HDR_DST_ALPHA_CTRL(vp) (0x66C + (vp) * 0x100)
#define RK3576_OVL_BG_MIX_CTRL(vp) (0x670 + (vp) * 0x100)
/* Video Port registers definition */
#define RK3568_VP0_CTRL_BASE 0x0C00
#define RK3568_VP1_CTRL_BASE 0x0D00
#define RK3568_VP2_CTRL_BASE 0x0E00
#define RK3588_VP3_CTRL_BASE 0x0F00
#define RK3568_VP_DSP_CTRL 0x00
#define RK3568_VP_MIPI_CTRL 0x04
#define RK3568_VP_COLOR_BAR_CTRL 0x08
#define RK3588_VP_CLK_CTRL 0x0C
#define RK3568_VP_3D_LUT_CTRL 0x10
#define RK3568_VP_3D_LUT_MST 0x20
#define RK3568_VP_DSP_BG 0x2C
#define RK3568_VP_PRE_SCAN_HTIMING 0x30
#define RK3568_VP_POST_DSP_HACT_INFO 0x34
#define RK3568_VP_POST_DSP_VACT_INFO 0x38
#define RK3568_VP_POST_SCL_FACTOR_YRGB 0x3C
#define RK3568_VP_POST_SCL_CTRL 0x40
#define RK3568_VP_POST_DSP_VACT_INFO_F1 0x44
#define RK3568_VP_DSP_HTOTAL_HS_END 0x48
#define RK3568_VP_DSP_HACT_ST_END 0x4C
#define RK3568_VP_DSP_VTOTAL_VS_END 0x50
#define RK3568_VP_DSP_VACT_ST_END 0x54
#define RK3568_VP_DSP_VS_ST_END_F1 0x58
#define RK3568_VP_DSP_VACT_ST_END_F1 0x5C
#define RK3568_VP_BCSH_CTRL 0x60
#define RK3568_VP_BCSH_BCS 0x64
#define RK3568_VP_BCSH_H 0x68
#define RK3568_VP_BCSH_COLOR_BAR 0x6C
/* Overlay registers definition */
#define RK3568_OVL_CTRL 0x600
#define RK3568_OVL_LAYER_SEL 0x604
#define RK3568_OVL_PORT_SEL 0x608
#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL 0x610
#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL 0x614
#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x618
#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL 0x61C
#define RK3568_MIX0_SRC_COLOR_CTRL 0x650
#define RK3568_MIX0_DST_COLOR_CTRL 0x654
#define RK3568_MIX0_SRC_ALPHA_CTRL 0x658
#define RK3568_MIX0_DST_ALPHA_CTRL 0x65C
#define RK3568_HDR0_SRC_COLOR_CTRL 0x6C0
#define RK3568_HDR0_DST_COLOR_CTRL 0x6C4
#define RK3568_HDR0_SRC_ALPHA_CTRL 0x6C8
#define RK3568_HDR0_DST_ALPHA_CTRL 0x6CC
#define RK3568_VP_BG_MIX_CTRL(vp) (0x6E0 + (vp) * 4)
#define RK3568_CLUSTER_DLY_NUM 0x6F0
#define RK3568_SMART_DLY_NUM 0x6F8
/* Cluster register definition, offset relative to window base */
#define RK3568_CLUSTER0_CTRL_BASE 0x1000
#define RK3568_CLUSTER1_CTRL_BASE 0x1200
#define RK3588_CLUSTER2_CTRL_BASE 0x1400
#define RK3588_CLUSTER3_CTRL_BASE 0x1600
#define RK3568_ESMART0_CTRL_BASE 0x1800
#define RK3568_ESMART1_CTRL_BASE 0x1A00
#define RK3568_SMART0_CTRL_BASE 0x1C00
#define RK3568_SMART1_CTRL_BASE 0x1E00
#define RK3588_ESMART2_CTRL_BASE 0x1C00
#define RK3588_ESMART3_CTRL_BASE 0x1E00
#define RK3568_CLUSTER_WIN_CTRL0 0x00
#define RK3568_CLUSTER_WIN_CTRL1 0x04
#define RK3568_CLUSTER_WIN_CTRL2 0x08
#define RK3568_CLUSTER_WIN_YRGB_MST 0x10
#define RK3568_CLUSTER_WIN_CBR_MST 0x14
#define RK3568_CLUSTER_WIN_VIR 0x18
#define RK3568_CLUSTER_WIN_ACT_INFO 0x20
#define RK3568_CLUSTER_WIN_DSP_INFO 0x24
#define RK3568_CLUSTER_WIN_DSP_ST 0x28
#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB 0x30
#define RK3568_CLUSTER_WIN_TRANSFORM_OFFSET 0x3C
#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL 0x50
#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE 0x54
#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR 0x58
#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH 0x5C
#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE 0x60
#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET 0x64
#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET 0x68
#define RK3568_CLUSTER_WIN_AFBCD_CTRL 0x6C
#define RK3576_CLUSTER_WIN_AFBCD_PLD_PTR_OFFSET 0x78
#define RK3568_CLUSTER_CTRL 0x100
#define RK3576_CLUSTER_PORT_SEL_IMD 0x1F4
#define RK3576_CLUSTER_DLY_NUM 0x1F8
/* (E)smart register definition, offset relative to window base */
#define RK3568_SMART_CTRL0 0x00
#define RK3568_SMART_CTRL1 0x04
#define RK3588_SMART_AXI_CTRL 0x08
#define RK3568_SMART_REGION0_CTRL 0x10
#define RK3568_SMART_REGION0_YRGB_MST 0x14
#define RK3568_SMART_REGION0_CBR_MST 0x18
#define RK3568_SMART_REGION0_VIR 0x1C
#define RK3568_SMART_REGION0_ACT_INFO 0x20
#define RK3568_SMART_REGION0_DSP_INFO 0x24
#define RK3568_SMART_REGION0_DSP_ST 0x28
#define RK3568_SMART_REGION0_SCL_CTRL 0x30
#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB 0x34
#define RK3568_SMART_REGION0_SCL_FACTOR_CBR 0x38
#define RK3568_SMART_REGION0_SCL_OFFSET 0x3C
#define RK3568_SMART_REGION1_CTRL 0x40
#define RK3568_SMART_REGION1_YRGB_MST 0x44
#define RK3568_SMART_REGION1_CBR_MST 0x48
#define RK3568_SMART_REGION1_VIR 0x4C
#define RK3568_SMART_REGION1_ACT_INFO 0x50
#define RK3568_SMART_REGION1_DSP_INFO 0x54
#define RK3568_SMART_REGION1_DSP_ST 0x58
#define RK3568_SMART_REGION1_SCL_CTRL 0x60
#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB 0x64
#define RK3568_SMART_REGION1_SCL_FACTOR_CBR 0x68
#define RK3568_SMART_REGION1_SCL_OFFSET 0x6C
#define RK3568_SMART_REGION2_CTRL 0x70
#define RK3568_SMART_REGION2_YRGB_MST 0x74
#define RK3568_SMART_REGION2_CBR_MST 0x78
#define RK3568_SMART_REGION2_VIR 0x7C
#define RK3568_SMART_REGION2_ACT_INFO 0x80
#define RK3568_SMART_REGION2_DSP_INFO 0x84
#define RK3568_SMART_REGION2_DSP_ST 0x88
#define RK3568_SMART_REGION2_SCL_CTRL 0x90
#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB 0x94
#define RK3568_SMART_REGION2_SCL_FACTOR_CBR 0x98
#define RK3568_SMART_REGION2_SCL_OFFSET 0x9C
#define RK3568_SMART_REGION3_CTRL 0xA0
#define RK3568_SMART_REGION3_YRGB_MST 0xA4
#define RK3568_SMART_REGION3_CBR_MST 0xA8
#define RK3568_SMART_REGION3_VIR 0xAC
#define RK3568_SMART_REGION3_ACT_INFO 0xB0
#define RK3568_SMART_REGION3_DSP_INFO 0xB4
#define RK3568_SMART_REGION3_DSP_ST 0xB8
#define RK3568_SMART_REGION3_SCL_CTRL 0xC0
#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB 0xC4
#define RK3568_SMART_REGION3_SCL_FACTOR_CBR 0xC8
#define RK3568_SMART_REGION3_SCL_OFFSET 0xCC
#define RK3568_SMART_COLOR_KEY_CTRL 0xD0
#define RK3576_SMART_ALPHA_MAP 0xD8
#define RK3576_SMART_PORT_SEL_IMD 0xF4
#define RK3576_SMART_DLY_NUM 0xF8
/* HDR register definition */
#define RK3568_HDR_LUT_CTRL 0x2000
#define RK3568_HDR_LUT_MST 0x2004
#define RK3568_SDR2HDR_CTRL 0x2010
#define RK3568_HDR2SDR_CTRL 0x2020
#define RK3568_HDR2SDR_SRC_RANGE 0x2024
#define RK3568_HDR2SDR_NORMFACEETF 0x2028
#define RK3568_HDR2SDR_DST_RANGE 0x202C
#define RK3568_HDR2SDR_NORMFACCGAMMA 0x2030
#define RK3568_HDR_EETF_OETF_Y0 0x203C
#define RK3568_HDR_SAT_Y0 0x20C0
#define RK3568_HDR_EOTF_OETF_Y0 0x20F0
#define RK3568_HDR_OETF_DX_POW1 0x2200
#define RK3568_HDR_OETF_XN1 0x2300
#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN BIT(15)
#define RK3568_VP_DSP_CTRL__STANDBY BIT(31)
#define RK3568_VP_DSP_CTRL__DSP_LUT_EN BIT(28)
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE BIT(20)
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL GENMASK(19, 18)
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN BIT(17)
#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN BIT(16)
#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y BIT(15)
#define RK3568_VP_DSP_CTRL__DSP_RG_SWAP BIT(10)
#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP BIT(9)
#define RK3568_VP_DSP_CTRL__DSP_BG_SWAP BIT(8)
#define RK3568_VP_DSP_CTRL__DSP_INTERLACE BIT(7)
#define RK3568_VP_DSP_CTRL__DSP_FILED_POL BIT(6)
#define RK3568_VP_DSP_CTRL__P2I_EN BIT(5)
#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV BIT(4)
#define RK3568_VP_DSP_CTRL__OUT_MODE GENMASK(3, 0)
#define RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN BIT(22)
#define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2)
#define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0)
#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN BIT(1)
#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN BIT(0)
#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX GENMASK(26, 25)
#define RK3568_SYS_DSP_INFACE_EN_LVDS1 BIT(24)
#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21)
#define RK3568_SYS_DSP_INFACE_EN_MIPI1 BIT(20)
#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX GENMASK(19, 18)
#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(17, 16)
#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX GENMASK(15, 14)
#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX GENMASK(11, 10)
#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX GENMASK(9, 8)
#define RK3568_SYS_DSP_INFACE_EN_LVDS0 BIT(5)
#define RK3568_SYS_DSP_INFACE_EN_MIPI0 BIT(4)
#define RK3568_SYS_DSP_INFACE_EN_EDP BIT(3)
#define RK3568_SYS_DSP_INFACE_EN_HDMI BIT(1)
#define RK3568_SYS_DSP_INFACE_EN_RGB BIT(0)
#define RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21)
#define RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(20, 20)
#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX GENMASK(19, 18)
#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX GENMASK(17, 16)
#define RK3588_SYS_DSP_INFACE_EN_DP1_MUX GENMASK(15, 14)
#define RK3588_SYS_DSP_INFACE_EN_DP0_MUX GENMASK(13, 12)
#define RK3588_SYS_DSP_INFACE_EN_DPI GENMASK(9, 8)
#define RK3588_SYS_DSP_INFACE_EN_MIPI1 BIT(7)
#define RK3588_SYS_DSP_INFACE_EN_MIPI0 BIT(6)
#define RK3588_SYS_DSP_INFACE_EN_HDMI1 BIT(5)
#define RK3588_SYS_DSP_INFACE_EN_EDP1 BIT(4)
#define RK3588_SYS_DSP_INFACE_EN_HDMI0 BIT(3)
#define RK3588_SYS_DSP_INFACE_EN_EDP0 BIT(2)
#define RK3588_SYS_DSP_INFACE_EN_DP1 BIT(1)
#define RK3588_SYS_DSP_INFACE_EN_DP0 BIT(0)
#define RK3588_DSP_IF_MIPI1_PCLK_DIV GENMASK(27, 26)
#define RK3588_DSP_IF_MIPI0_PCLK_DIV GENMASK(25, 24)
#define RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV GENMASK(22, 22)
#define RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV GENMASK(21, 20)
#define RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV GENMASK(18, 18)
#define RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV GENMASK(17, 16)
#define RK3568_DSP_IF_POL__MIPI_PIN_POL GENMASK(19, 16)
#define RK3568_DSP_IF_POL__EDP_PIN_POL GENMASK(15, 12)
#define RK3568_DSP_IF_POL__HDMI_PIN_POL GENMASK(7, 4)
#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL GENMASK(3, 0)
#define RK3588_DSP_IF_POL__DP1_PIN_POL GENMASK(14, 12)
#define RK3588_DSP_IF_POL__DP0_PIN_POL GENMASK(10, 8)
#define RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL GENMASK(13, 12)
#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK BIT(5)
#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2 BIT(4)
#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN BIT(31)
#define RK3568_DSP_IF_POL__CFG_DONE_IMD BIT(28)
#define VOP2_SYS_AXI_BUS_NUM 2
#define VOP2_CLUSTER_YUV444_10 0x12
#define VOP2_COLOR_KEY_MASK BIT(31)
#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL GENMASK(31, 30)
#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD BIT(28)
#define RK3568_OVL_CTRL__YUV_MODE(vp) BIT(vp)
#define RK3568_VP_BG_MIX_CTRL__BG_DLY GENMASK(31, 24)
#define RK3568_OVL_PORT_SEL__SEL_PORT GENMASK(31, 16)
#define RK3568_OVL_PORT_SEL__SMART1 GENMASK(31, 30)
#define RK3568_OVL_PORT_SEL__SMART0 GENMASK(29, 28)
#define RK3588_OVL_PORT_SEL__ESMART3 GENMASK(31, 30)
#define RK3588_OVL_PORT_SEL__ESMART2 GENMASK(29, 28)
#define RK3568_OVL_PORT_SEL__ESMART1 GENMASK(27, 26)
#define RK3568_OVL_PORT_SEL__ESMART0 GENMASK(25, 24)
#define RK3588_OVL_PORT_SEL__CLUSTER3 GENMASK(23, 22)
#define RK3588_OVL_PORT_SEL__CLUSTER2 GENMASK(21, 20)
#define RK3568_OVL_PORT_SEL__CLUSTER1 GENMASK(19, 18)
#define RK3568_OVL_PORT_SEL__CLUSTER0 GENMASK(17, 16)
#define RK3588_OVL_PORT_SET__PORT3_MUX GENMASK(15, 12)
#define RK3568_OVL_PORT_SET__PORT2_MUX GENMASK(11, 8)
#define RK3568_OVL_PORT_SET__PORT1_MUX GENMASK(7, 4)
#define RK3568_OVL_PORT_SET__PORT0_MUX GENMASK(3, 0)
#define RK3568_OVL_LAYER_SEL__LAYER(layer, x) ((x) << ((layer) * 4))
#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1 GENMASK(31, 24)
#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0 GENMASK(23, 16)
#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1 GENMASK(15, 8)
#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0 GENMASK(7, 0)
#define RK3568_CLUSTER_WIN_CTRL0__WIN0_EN BIT(0)
#define RK3568_SMART_REGION0_CTRL__WIN0_EN BIT(0)
#define RK3568_SMART_DLY_NUM__SMART1 GENMASK(31, 24)
#define RK3568_SMART_DLY_NUM__SMART0 GENMASK(23, 16)
#define RK3568_SMART_DLY_NUM__ESMART1 GENMASK(15, 8)
#define RK3568_SMART_DLY_NUM__ESMART0 GENMASK(7, 0)
#define VP_INT_DSP_HOLD_VALID BIT(6)
#define VP_INT_FS_FIELD BIT(5)
#define VP_INT_POST_BUF_EMPTY BIT(4)
#define VP_INT_LINE_FLAG1 BIT(3)
#define VP_INT_LINE_FLAG0 BIT(2)
#define VOP2_INT_BUS_ERRPR BIT(1)
#define VP_INT_FS BIT(0)
#define POLFLAG_DCLK_INV BIT(3)
#define RK3576_OVL_CTRL__YUV_MODE BIT(0)
#define RK3576_OVL_BG_MIX_CTRL__BG_DLY GENMASK(31, 24)
#define RK3576_DSP_IF_CFG_DONE_IMD BIT(31)
#define RK3576_DSP_IF_DCLK_SEL_OUT BIT(21)
#define RK3576_DSP_IF_PCLK_DIV BIT(20)
#define RK3576_DSP_IF_PIN_POL GENMASK(5, 4)
#define RK3576_DSP_IF_MUX GENMASK(3, 2)
#define RK3576_DSP_IF_CLK_OUT_EN BIT(1)
#define RK3576_DSP_IF_EN BIT(0)
enum vop2_layer_phy_id {
ROCKCHIP_VOP2_CLUSTER0 = 0,
ROCKCHIP_VOP2_CLUSTER1,
ROCKCHIP_VOP2_ESMART0,
ROCKCHIP_VOP2_ESMART1,
ROCKCHIP_VOP2_SMART0,
ROCKCHIP_VOP2_SMART1,
ROCKCHIP_VOP2_CLUSTER2,
ROCKCHIP_VOP2_CLUSTER3,
ROCKCHIP_VOP2_ESMART2,
ROCKCHIP_VOP2_ESMART3,
ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
};
extern const struct component_ops vop2_component_ops;
static inline void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
{
regmap_write(vop2->map, offset, v);
}
static inline void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
{
regmap_write(vp->vop2->map, vp->data->offset + offset, v);
}
static inline u32 vop2_readl(struct vop2 *vop2, u32 offset)
{
u32 val;
regmap_read(vop2->map, offset, &val);
return val;
}
static inline u32 vop2_vp_read(struct vop2_video_port *vp, u32 offset)
{
u32 val;
regmap_read(vp->vop2->map, vp->data->offset + offset, &val);
return val;
}
static inline void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
{
regmap_field_write(win->reg[reg], v);
}
static inline bool vop2_cluster_window(const struct vop2_win *win)
{
return win->data->feature & WIN_FEATURE_CLUSTER;
}
static inline struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
{
return container_of(crtc, struct vop2_video_port, crtc);
}
static inline struct vop2_win *to_vop2_win(struct drm_plane *p)
{
return container_of(p, struct vop2_win, base);
}
/*
* Note:
* The write mask function is documented but missing on rk3566/8, writes
* to these bits have no effect. For newer soc(rk3588 and following) the
* write mask is needed for register writes.
*
* GLB_CFG_DONE_EN has no write mask bit.
*
*/
static inline void vop2_cfg_done(struct vop2_video_port *vp)
{
struct vop2 *vop2 = vp->vop2;
u32 val = RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN;
val |= BIT(vp->id) | (BIT(vp->id) << 16);
regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, val);
}
#endif /* _ROCKCHIP_DRM_VOP2_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
* Copyright (c) 2024 Collabora Ltd.
*/
#ifndef __DW_HDMI_QP__
#define __DW_HDMI_QP__
struct device;
struct drm_encoder;
struct dw_hdmi_qp;
struct platform_device;
struct dw_hdmi_qp_phy_ops {
int (*init)(struct dw_hdmi_qp *hdmi, void *data);
void (*disable)(struct dw_hdmi_qp *hdmi, void *data);
enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, void *data);
void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data);
};
struct dw_hdmi_qp_plat_data {
const struct dw_hdmi_qp_phy_ops *phy_ops;
void *phy_data;
int main_irq;
};
struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
struct drm_encoder *encoder,
const struct dw_hdmi_qp_plat_data *plat_data);
void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi);
#endif /* __DW_HDMI_QP__ */

View File

@ -28,11 +28,14 @@ define KernelPackage/drm-rockchip
CONFIG_DRM_DISPLAY_CONNECTOR \
CONFIG_HDMI=y \
CONFIG_DRM_DW_HDMI \
CONFIG_DRM_DW_HDMI_QP \
CONFIG_DRM_DW_HDMI_CEC \
CONFIG_DRM_DW_HDMI_GP_AUDIO=n \
CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX \
CONFIG_ROCKCHIP_ANALOGIX_DP=n \
CONFIG_ROCKCHIP_CDN_DP=n \
CONFIG_ROCKCHIP_DW_HDMI=y \
CONFIG_ROCKCHIP_DW_HDMI_QP=y \
CONFIG_ROCKCHIP_DW_MIPI_DSI=n \
CONFIG_ROCKCHIP_INNO_HDMI=n \
CONFIG_ROCKCHIP_LVDS=n \
@ -44,11 +47,13 @@ define KernelPackage/drm-rockchip
CONFIG_DRM_PANEL_BRIDGE=y
FILES:= \
$(LINUX_DIR)/drivers/gpu/drm/bridge/synopsys/dw-hdmi.ko \
$(LINUX_DIR)/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.ko \
$(LINUX_DIR)/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.ko \
$(LINUX_DIR)/drivers/gpu/drm/bridge/display-connector.ko \
$(LINUX_DIR)/drivers/gpu/drm/rockchip/rockchipdrm.ko \
$(LINUX_DIR)/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.ko \
$(LINUX_DIR)/drivers/media/cec/core/cec.ko
AUTOLOAD:=$(call AutoLoad,80,dw-hdmi-cec rockchipdrm display-connector)
AUTOLOAD:=$(call AutoLoad,80,phy-rockchip-samsung-hdptx dw-hdmi-cec rockchipdrm display-connector)
endef
define KernelPackage/drm-rockchip/description

View File

@ -0,0 +1,297 @@
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -378,6 +378,22 @@
};
};
+ hdmi0_sound: hdmi0-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,mclk-fs = <128>;
+ simple-audio-card,name = "hdmi0";
+ status = "disabled";
+
+ simple-audio-card,codec {
+ sound-dai = <&hdmi0>;
+ };
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s5_8ch>;
+ };
+ };
+
pmu-a55 {
compatible = "arm,cortex-a55-pmu";
interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH &ppi_partition0>;
@@ -1146,14 +1162,16 @@
<&cru DCLK_VOP1>,
<&cru DCLK_VOP2>,
<&cru DCLK_VOP3>,
- <&cru PCLK_VOP_ROOT>;
+ <&cru PCLK_VOP_ROOT>,
+ <&hdptxphy0>;
clock-names = "aclk",
"hclk",
"dclk_vp0",
"dclk_vp1",
"dclk_vp2",
"dclk_vp3",
- "pclk_vop";
+ "pclk_vop",
+ "pll_hdmiphy0";
iommus = <&vop_mmu>;
power-domains = <&power RK3588_PD_VOP>;
rockchip,grf = <&sys_grf>;
@@ -1254,6 +1272,48 @@
status = "disabled";
};
+ hdmi0: hdmi@fde80000 {
+ compatible = "rockchip,rk3588-dw-hdmi-qp";
+ reg = <0x0 0xfde80000 0x0 0x20000>;
+ clocks = <&cru PCLK_HDMITX0>,
+ <&cru CLK_HDMITX0_EARC>,
+ <&cru CLK_HDMITX0_REF>,
+ <&cru MCLK_I2S5_8CH_TX>,
+ <&cru CLK_HDMIHDP0>,
+ <&cru HCLK_VO1>;
+ clock-names = "pclk", "earc", "ref", "aud", "hdp", "hclk_vo1";
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "avp", "cec", "earc", "main", "hpd";
+ phys = <&hdptxphy0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd
+ &hdmim0_tx0_scl &hdmim0_tx0_sda>;
+ power-domains = <&power RK3588_PD_VO1>;
+ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>;
+ reset-names = "ref", "hdp";
+ rockchip,grf = <&sys_grf>;
+ rockchip,vo-grf = <&vo1_grf>;
+ #sound-dai-cells = <0>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi0_in: port@0 {
+ reg = <0>;
+ };
+
+ hdmi0_out: port@1 {
+ reg = <1>;
+ };
+ };
+ };
+
qos_gpu_m0: qos@fdf35000 {
compatible = "rockchip,rk3588-qos", "syscon";
reg = <0x0 0xfdf35000 0x0 0x20>;
@@ -2657,11 +2717,12 @@
#dma-cells = <1>;
};
- hdptxphy_hdmi0: phy@fed60000 {
+ hdptxphy0: phy@fed60000 {
compatible = "rockchip,rk3588-hdptx-phy";
reg = <0x0 0xfed60000 0x0 0x2000>;
clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>;
clock-names = "ref", "apb";
+ #clock-cells = <0>;
#phy-cells = <0>;
resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>,
<&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>,
--- a/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
@@ -7,6 +7,46 @@
#include "rk3588-extra-pinctrl.dtsi"
/ {
+ hdmi1_sound: hdmi1-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,mclk-fs = <128>;
+ simple-audio-card,name = "hdmi1";
+ status = "disabled";
+
+ simple-audio-card,codec {
+ sound-dai = <&hdmi1>;
+ };
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s6_8ch>;
+ };
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ /*
+ * The 4k HDMI capture controller works only with 32bit
+ * phys addresses and doesn't support IOMMU. HDMI RX CMA
+ * must be reserved below 4GB.
+ * The size of 160MB was determined as follows:
+ * (3840 * 2160 pixels) * (4 bytes/pixel) * (2 frames/buffer) / 10^6 = 66MB
+ * To ensure sufficient support for practical use-cases,
+ * we doubled the 66MB value.
+ */
+ hdmi_receiver_cma: hdmi-receiver-cma {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x0 0x0 0xffffffff>;
+ size = <0x0 (160 * 0x100000)>; /* 160MiB */
+ alignment = <0x0 0x40000>; /* 64K */
+ no-map;
+ status = "disabled";
+ };
+ };
+
usb_host1_xhci: usb@fc400000 {
compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
reg = <0x0 0xfc400000 0x0 0x400000>;
@@ -67,6 +107,11 @@
};
};
+ hdptxphy1_grf: syscon@fd5e4000 {
+ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon";
+ reg = <0x0 0xfd5e4000 0x0 0x100>;
+ };
+
i2s8_8ch: i2s@fddc8000 {
compatible = "rockchip,rk3588-i2s-tdm";
reg = <0x0 0xfddc8000 0x0 0x1000>;
@@ -135,6 +180,79 @@
status = "disabled";
};
+ hdmi1: hdmi@fdea0000 {
+ compatible = "rockchip,rk3588-dw-hdmi-qp";
+ reg = <0x0 0xfdea0000 0x0 0x20000>;
+ clocks = <&cru PCLK_HDMITX1>,
+ <&cru CLK_HDMITX1_EARC>,
+ <&cru CLK_HDMITX1_REF>,
+ <&cru MCLK_I2S6_8CH_TX>,
+ <&cru CLK_HDMIHDP1>,
+ <&cru HCLK_VO1>;
+ clock-names = "pclk", "earc", "ref", "aud", "hdp", "hclk_vo1";
+ interrupts = <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 175 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 361 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "avp", "cec", "earc", "main", "hpd";
+ phys = <&hdptxphy1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmim2_tx1_cec &hdmim0_tx1_hpd
+ &hdmim1_tx1_scl &hdmim1_tx1_sda>;
+ power-domains = <&power RK3588_PD_VO1>;
+ resets = <&cru SRST_HDMITX1_REF>, <&cru SRST_HDMIHDP1>;
+ reset-names = "ref", "hdp";
+ rockchip,grf = <&sys_grf>;
+ rockchip,vo-grf = <&vo1_grf>;
+ #sound-dai-cells = <0>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi1_in: port@0 {
+ reg = <0>;
+ };
+
+ hdmi1_out: port@1 {
+ reg = <1>;
+ };
+ };
+ };
+
+ hdmi_receiver: hdmi_receiver@fdee0000 {
+ compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
+ reg = <0x0 0xfdee0000 0x0 0x6000>;
+ interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "cec", "hdmi", "dma";
+ clocks = <&cru ACLK_HDMIRX>,
+ <&cru CLK_HDMIRX_AUD>,
+ <&cru CLK_CR_PARA>,
+ <&cru PCLK_HDMIRX>,
+ <&cru CLK_HDMIRX_REF>,
+ <&cru PCLK_S_HDMIRX>,
+ <&cru HCLK_VO1>;
+ clock-names = "aclk",
+ "audio",
+ "cr_para",
+ "pclk",
+ "ref",
+ "hclk_s_hdmirx",
+ "hclk_vo1";
+ memory-region = <&hdmi_receiver_cma>;
+ power-domains = <&power RK3588_PD_VO1>;
+ resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
+ <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
+ reset-names = "axi", "apb", "ref", "biu";
+ rockchip,grf = <&sys_grf>;
+ rockchip,vo1-grf = <&vo1_grf>;
+ status = "disabled";
+ };
+
pcie3x4: pcie@fe150000 {
compatible = "rockchip,rk3588-pcie", "rockchip,rk3568-pcie";
#address-cells = <3>;
@@ -360,6 +478,23 @@
};
};
+ hdptxphy1: phy@fed70000 {
+ compatible = "rockchip,rk3588-hdptx-phy";
+ reg = <0x0 0xfed70000 0x0 0x2000>;
+ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX1>;
+ clock-names = "ref", "apb";
+ #clock-cells = <0>;
+ #phy-cells = <0>;
+ resets = <&cru SRST_HDPTX1>, <&cru SRST_P_HDPTX1>,
+ <&cru SRST_HDPTX1_INIT>, <&cru SRST_HDPTX1_CMN>,
+ <&cru SRST_HDPTX1_LANE>, <&cru SRST_HDPTX1_ROPLL>,
+ <&cru SRST_HDPTX1_LCPLL>;
+ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll",
+ "lcpll";
+ rockchip,grf = <&hdptxphy1_grf>;
+ status = "disabled";
+ };
+
usbdp_phy1: phy@fed90000 {
compatible = "rockchip,rk3588-usbdp-phy";
reg = <0x0 0xfed90000 0x0 0x10000>;
@@ -411,3 +546,24 @@
status = "disabled";
};
};
+
+&vop {
+ clocks = <&cru ACLK_VOP>,
+ <&cru HCLK_VOP>,
+ <&cru DCLK_VOP0>,
+ <&cru DCLK_VOP1>,
+ <&cru DCLK_VOP2>,
+ <&cru DCLK_VOP3>,
+ <&cru PCLK_VOP_ROOT>,
+ <&hdptxphy0>,
+ <&hdptxphy1>;
+ clock-names = "aclk",
+ "hclk",
+ "dclk_vp0",
+ "dclk_vp1",
+ "dclk_vp2",
+ "dclk_vp3",
+ "pclk_vop",
+ "pll_hdmiphy0",
+ "pll_hdmiphy1";
+};

View File

@ -0,0 +1,31 @@
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -91,6 +91,18 @@ config PHY_ROCKCHIP_PCIE
help
Enable this to support the Rockchip PCIe PHY.
+config PHY_ROCKCHIP_SAMSUNG_HDPTX
+ tristate "Rockchip Samsung HDMI/eDP Combo PHY driver"
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
+ depends on COMMON_CLK
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ select MFD_SYSCON
+ select RATIONAL
+ help
+ Enable this to support the Rockchip HDMI/eDP Combo PHY
+ with Samsung IP block.
+
config PHY_ROCKCHIP_SNPS_PCIE3
tristate "Rockchip Snps PCIe3 PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) +=
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
+obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o
obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o

View File

@ -0,0 +1,54 @@
From bf34d14a2d506e4f94e57f70861b45cb2b9e85dc Mon Sep 17 00:00:00 2001
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Date: Thu, 6 Mar 2025 20:29:23 +0100
Subject: [PATCH 01/11] phy: phy-rockchip-samsung-hdptx: Add support for RK3576
Despite the compatible already being listed in the bindings, the PHY
driver never gained explicit support for it. This is especially a
problem because the explicitly listed PHY addresses need to be specified
for each SoC.
To solve this, add the compatible, and a PHY config, with the address
gleaned from rk3576.dtsi.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Reviewed-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Heiko Stuebner <heiko@sntech.de>
Link: https://lore.kernel.org/r/20250306-rk3576-hdptx-phy-v1-1-288cc4b0611a@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index c3c8bd23beae..d793454a7512 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -1159,6 +1159,13 @@ static const struct dev_pm_ops rk_hdptx_phy_pm_ops = {
rk_hdptx_phy_runtime_resume, NULL)
};
+static const struct rk_hdptx_phy_cfg rk3576_hdptx_phy_cfgs = {
+ .num_phys = 1,
+ .phy_ids = {
+ 0x2b000000,
+ },
+};
+
static const struct rk_hdptx_phy_cfg rk3588_hdptx_phy_cfgs = {
.num_phys = 2,
.phy_ids = {
@@ -1168,6 +1175,10 @@ static const struct rk_hdptx_phy_cfg rk3588_hdptx_phy_cfgs = {
};
static const struct of_device_id rk_hdptx_phy_of_match[] = {
+ {
+ .compatible = "rockchip,rk3576-hdptx-phy",
+ .data = &rk3576_hdptx_phy_cfgs
+ },
{
.compatible = "rockchip,rk3588-hdptx-phy",
.data = &rk3588_hdptx_phy_cfgs
--
2.51.0

View File

@ -0,0 +1,46 @@
From ff2555a62e27661472f2777b976eeefed4e06ee8 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:37 +0200
Subject: [PATCH 02/11] phy: rockchip: samsung-hdptx: Fix clock ratio setup
The switch from 1/10 to 1/40 clock ratio must happen when exceeding the
340 MHz rate limit of HDMI 1.4, i.e. when entering the HDMI 2.0 domain,
and not before.
Therefore, use the correct comparison operator '>' instead of '>=' when
checking the max rate. While at it, introduce a define for this rate
limit constant.
Fixes: 553be2830c5f ("phy: rockchip: Add Samsung HDMI/eDP Combo PHY driver")
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-3-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index d793454a7512..2e1c50b2895d 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -192,6 +192,7 @@
#define LN3_TX_SER_RATE_SEL_HBR2_MASK BIT(3)
#define LN3_TX_SER_RATE_SEL_HBR3_MASK BIT(2)
+#define HDMI14_MAX_RATE 340000000
#define HDMI20_MAX_RATE 600000000
struct lcpll_config {
@@ -837,7 +838,7 @@ static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
regmap_write(hdptx->regmap, LNTOP_REG(0200), 0x06);
- if (rate >= 3400000) {
+ if (rate > HDMI14_MAX_RATE / 100) {
/* For 1/40 bitrate clk */
rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lntop_highbr_seq);
} else {
--
2.51.0

View File

@ -0,0 +1,48 @@
From 9d47e7deb8f1d42c220afb43ff965a4b11f6c7dc Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:38 +0200
Subject: [PATCH 03/11] phy: rockchip: samsung-hdptx: Do no set
rk_hdptx_phy->rate in case of errors
Ensure rk_hdptx_ropll_tmds_cmn_config() updates hdptx->rate only after
all the other operations have been successful.
Fixes: c4b09c562086 ("phy: phy-rockchip-samsung-hdptx: Add clock provider support")
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-4-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 2e1c50b2895d..6a062e509a01 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -771,9 +771,7 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
{
const struct ropll_config *cfg = NULL;
struct ropll_config rc = {0};
- int i;
-
- hdptx->rate = rate * 100;
+ int ret, i;
for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++)
if (rate == ropll_tmds_cfg[i].bit_rate) {
@@ -828,7 +826,11 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_EN_MASK,
FIELD_PREP(PLL_PCG_CLK_EN_MASK, 0x1));
- return rk_hdptx_post_enable_pll(hdptx);
+ ret = rk_hdptx_post_enable_pll(hdptx);
+ if (!ret)
+ hdptx->rate = rate * 100;
+
+ return ret;
}
static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
--
2.51.0

View File

@ -0,0 +1,62 @@
From 23261a7c4c546ae151344efa5bbf0c8a6b8806f6 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:39 +0200
Subject: [PATCH 04/11] phy: rockchip: samsung-hdptx: Drop unused struct
lcpll_config
This is just a leftover from downstream support for HDMI 2.1.
Remove the unused struct for now.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-5-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
.../phy/rockchip/phy-rockchip-samsung-hdptx.c | 31 -------------------
1 file changed, 31 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 6a062e509a01..59d7ced84032 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -195,37 +195,6 @@
#define HDMI14_MAX_RATE 340000000
#define HDMI20_MAX_RATE 600000000
-struct lcpll_config {
- u32 bit_rate;
- u8 lcvco_mode_en;
- u8 pi_en;
- u8 clk_en_100m;
- u8 pms_mdiv;
- u8 pms_mdiv_afc;
- u8 pms_pdiv;
- u8 pms_refdiv;
- u8 pms_sdiv;
- u8 pi_cdiv_rstn;
- u8 pi_cdiv_sel;
- u8 sdm_en;
- u8 sdm_rstn;
- u8 sdc_frac_en;
- u8 sdc_rstn;
- u8 sdm_deno;
- u8 sdm_num_sign;
- u8 sdm_num;
- u8 sdc_n;
- u8 sdc_n2;
- u8 sdc_num;
- u8 sdc_deno;
- u8 sdc_ndiv_rstn;
- u8 ssc_en;
- u8 ssc_fm_dev;
- u8 ssc_fm_freq;
- u8 ssc_clk_div_sel;
- u8 cd_tx_ser_rate_sel;
-};
-
struct ropll_config {
u32 bit_rate;
u8 pms_mdiv;
--
2.51.0

View File

@ -0,0 +1,32 @@
From a51cb3b76218a09777c62b8421ba1aab569f51ff Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:40 +0200
Subject: [PATCH 05/11] phy: rockchip: samsung-hdptx: Drop unused phy_cfg
driver data
There is no usage of phy_cfg in the upstream driver data, nor in the
downstream one, hence remove it.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-6-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 59d7ced84032..4e082ab31cae 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -250,7 +250,6 @@ struct rk_hdptx_phy {
int phy_id;
struct phy *phy;
- struct phy_config *phy_cfg;
struct clk_bulk_data *clks;
int nr_clks;
struct reset_control_bulk_data rsts[RST_MAX];
--
2.51.0

View File

@ -0,0 +1,66 @@
From 7562f48e8f344af19a71d0a6c626bc16f22e6ad3 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:41 +0200
Subject: [PATCH 06/11] phy: rockchip: samsung-hdptx: Drop superfluous cfgs
driver data
The ->cfgs member has been introduced via commit f08d1c085638 ("phy:
phy-rockchip-samsung-hdptx: Don't use dt aliases to determine phy-id"),
but it is only used during probe() in order to setup ->phy_id.
Use a probe() local variable to store device match data and remove the
now unnecessary member from struct rk_hdptx_phy.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-7-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 4e082ab31cae..de333b743810 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -245,10 +245,7 @@ struct rk_hdptx_phy {
struct regmap *regmap;
struct regmap *grf;
- /* PHY const config */
- const struct rk_hdptx_phy_cfg *cfgs;
int phy_id;
-
struct phy *phy;
struct clk_bulk_data *clks;
int nr_clks;
@@ -1036,6 +1033,7 @@ static int rk_hdptx_phy_runtime_resume(struct device *dev)
static int rk_hdptx_phy_probe(struct platform_device *pdev)
{
+ const struct rk_hdptx_phy_cfg *cfgs;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct rk_hdptx_phy *hdptx;
@@ -1054,14 +1052,14 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(regs),
"Failed to ioremap resource\n");
- hdptx->cfgs = device_get_match_data(dev);
- if (!hdptx->cfgs)
+ cfgs = device_get_match_data(dev);
+ if (!cfgs)
return dev_err_probe(dev, -EINVAL, "missing match data\n");
/* find the phy-id from the io address */
hdptx->phy_id = -ENODEV;
- for (id = 0; id < hdptx->cfgs->num_phys; id++) {
- if (res->start == hdptx->cfgs->phy_ids[id]) {
+ for (id = 0; id < cfgs->num_phys; id++) {
+ if (res->start == cfgs->phy_ids[id]) {
hdptx->phy_id = id;
break;
}
--
2.51.0

View File

@ -0,0 +1,241 @@
From 85bc910629a9f31b879aeb4f1a1c5fa211406f4a Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:42 +0200
Subject: [PATCH 07/11] phy: rockchip: samsung-hdptx: Avoid Hz<->hHz unit
conversion overhead
The ropll_tmds_cfg table used to identify the configuration params for
the supported rates expects the search key, i.e. bit_rate member of
struct ropll_config, to be provided in hHz rather than Hz (1 hHz = 100
Hz). This requires multiple conversions between these units being
performed at runtime.
Improve implementation clarity and efficiency by consistently using the
Hz unit throughout driver's internal data structures and functions.
Also rename the rather misleading struct member.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-8-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
.../phy/rockchip/phy-rockchip-samsung-hdptx.c | 79 +++++++++----------
1 file changed, 39 insertions(+), 40 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index de333b743810..36b01a2e414b 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -196,7 +196,7 @@
#define HDMI20_MAX_RATE 600000000
struct ropll_config {
- u32 bit_rate;
+ unsigned long long rate;
u8 pms_mdiv;
u8 pms_mdiv_afc;
u8 pms_pdiv;
@@ -259,45 +259,45 @@ struct rk_hdptx_phy {
};
static const struct ropll_config ropll_tmds_cfg[] = {
- { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 594000000ULL, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 371250000ULL, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 297000000ULL, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10,
+ { 162000000ULL, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10,
1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 185625000ULL, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1,
+ { 154000000ULL, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5,
+ { 148500000ULL, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5,
0x10, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1,
+ { 146250000ULL, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1,
1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1,
+ { 119000000ULL, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1,
1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1,
+ { 106500000ULL, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1,
1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
+ { 108000000ULL, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1,
+ { 85500000ULL, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0,
+ { 83500000ULL, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 92812500ULL, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
+ { 74250000ULL, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1,
+ { 65000000ULL, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5,
+ { 33750000ULL, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5,
1, 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
+ { 40000000ULL, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
+ { 27000000ULL, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
- { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, 1,
+ { 25175000ULL, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, 1,
1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
};
@@ -659,10 +659,10 @@ static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx)
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
}
-static bool rk_hdptx_phy_clk_pll_calc(unsigned int data_rate,
+static bool rk_hdptx_phy_clk_pll_calc(unsigned long long rate,
struct ropll_config *cfg)
{
- const unsigned int fout = data_rate / 2, fref = 24000;
+ const unsigned int fout = div_u64(rate, 200), fref = 24000;
unsigned long k = 0, lc, k_sub, lc_sub;
unsigned int fvco, sdc;
u32 mdiv, sdiv, n = 8;
@@ -732,14 +732,14 @@ static bool rk_hdptx_phy_clk_pll_calc(unsigned int data_rate,
}
static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
- unsigned int rate)
+ unsigned long long rate)
{
const struct ropll_config *cfg = NULL;
struct ropll_config rc = {0};
int ret, i;
for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++)
- if (rate == ropll_tmds_cfg[i].bit_rate) {
+ if (rate == ropll_tmds_cfg[i].rate) {
cfg = &ropll_tmds_cfg[i];
break;
}
@@ -753,8 +753,8 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
}
}
- dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u, sdm_en=%u, k_sign=%u, k=%u, lc=%u\n",
- cfg->pms_mdiv, cfg->pms_sdiv + 1, cfg->sdm_en,
+ dev_dbg(hdptx->dev, "%s rate=%llu mdiv=%u sdiv=%u sdm_en=%u k_sign=%u k=%u lc=%u\n",
+ __func__, rate, cfg->pms_mdiv, cfg->pms_sdiv + 1, cfg->sdm_en,
cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno);
rk_hdptx_pre_power_up(hdptx);
@@ -793,19 +793,19 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
ret = rk_hdptx_post_enable_pll(hdptx);
if (!ret)
- hdptx->rate = rate * 100;
+ hdptx->rate = rate;
return ret;
}
static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
- unsigned int rate)
+ unsigned long long rate)
{
rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_sb_init_seq);
regmap_write(hdptx->regmap, LNTOP_REG(0200), 0x06);
- if (rate > HDMI14_MAX_RATE / 100) {
+ if (rate > HDMI14_MAX_RATE) {
/* For 1/40 bitrate clk */
rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lntop_highbr_seq);
} else {
@@ -823,7 +823,7 @@ static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
}
static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx,
- unsigned int rate)
+ unsigned long long rate)
{
u32 status;
int ret;
@@ -881,7 +881,7 @@ static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
static int rk_hdptx_phy_power_on(struct phy *phy)
{
struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
- int bus_width = phy_get_bus_width(hdptx->phy);
+ unsigned long long rate;
int ret;
/*
@@ -889,10 +889,10 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
* from the HDMI bridge driver until phy_configure_opts_hdmi
* becomes available in the PHY API.
*/
- unsigned int rate = bus_width & 0xfffffff;
+ rate = phy_get_bus_width(hdptx->phy) & 0xfffffff;
+ rate *= 100;
- dev_dbg(hdptx->dev, "%s bus_width=%x rate=%u\n",
- __func__, bus_width, rate);
+ dev_dbg(hdptx->dev, "%s rate=%llu\n", __func__, rate);
ret = rk_hdptx_phy_consumer_get(hdptx, rate);
if (ret)
@@ -927,7 +927,7 @@ static int rk_hdptx_phy_clk_prepare(struct clk_hw *hw)
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- return rk_hdptx_phy_consumer_get(hdptx, hdptx->rate / 100);
+ return rk_hdptx_phy_consumer_get(hdptx, hdptx->rate);
}
static void rk_hdptx_phy_clk_unprepare(struct clk_hw *hw)
@@ -948,18 +948,17 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
static long rk_hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- u32 bit_rate = rate / 100;
int i;
if (rate > HDMI20_MAX_RATE)
return rate;
for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++)
- if (bit_rate == ropll_tmds_cfg[i].bit_rate)
+ if (rate == ropll_tmds_cfg[i].rate)
break;
if (i == ARRAY_SIZE(ropll_tmds_cfg) &&
- !rk_hdptx_phy_clk_pll_calc(bit_rate, NULL))
+ !rk_hdptx_phy_clk_pll_calc(rate, NULL))
return -EINVAL;
return rate;
@@ -970,7 +969,7 @@ static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- return rk_hdptx_ropll_tmds_cmn_config(hdptx, rate / 100);
+ return rk_hdptx_ropll_tmds_cmn_config(hdptx, rate);
}
static const struct clk_ops hdptx_phy_clk_ops = {
--
2.51.0

View File

@ -0,0 +1,63 @@
From beaf90d69c97acc54f1b20533650499c9ca8ab01 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 18 Mar 2025 14:35:46 +0200
Subject: [PATCH 08/11] phy: rockchip: samsung-hdptx: Rename ambiguous
rk_hdptx_phy->rate
The main purpose of the ->rate member of struct rk_hdptx_phy is to
implement rk_hdptx_phy_clk_recalc_rate() by providing the actual rate
programmed in hardware. Hence the current naming is too generic and
rather ambiguous.
Improve clarity by renaming ->rate to ->hw_rate.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-12-8cb1678e7663@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 36b01a2e414b..4c713616963a 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -253,7 +253,7 @@ struct rk_hdptx_phy {
/* clk provider */
struct clk_hw hw;
- unsigned long rate;
+ unsigned long hw_rate;
atomic_t usage_count;
};
@@ -793,7 +793,7 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
ret = rk_hdptx_post_enable_pll(hdptx);
if (!ret)
- hdptx->rate = rate;
+ hdptx->hw_rate = rate;
return ret;
}
@@ -927,7 +927,7 @@ static int rk_hdptx_phy_clk_prepare(struct clk_hw *hw)
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- return rk_hdptx_phy_consumer_get(hdptx, hdptx->rate);
+ return rk_hdptx_phy_consumer_get(hdptx, hdptx->hw_rate);
}
static void rk_hdptx_phy_clk_unprepare(struct clk_hw *hw)
@@ -942,7 +942,7 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- return hdptx->rate;
+ return hdptx->hw_rate;
}
static long rk_hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
--
2.51.0

View File

@ -0,0 +1,41 @@
From 36afb795a27c8631a2e34ba1b761f0435eaaed25 Mon Sep 17 00:00:00 2001
From: Algea Cao <algea.cao@rock-chips.com>
Date: Sun, 27 Apr 2025 17:51:24 +0800
Subject: [PATCH 09/11] phy: phy-rockchip-samsung-hdptx: Fix PHY PLL output
50.25MHz error
When using HDMI PLL frequency division coefficient at 50.25MHz
that is calculated by rk_hdptx_phy_clk_pll_calc(), it fails to
get PHY LANE lock. Although the calculated values are within the
allowable range of PHY PLL configuration.
In order to fix the PHY LANE lock error and provide the expected
50.25MHz output, manually compute the required PHY PLL frequency
division coefficient and add it to ropll_tmds_cfg configuration
table.
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Reviewed-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Link: https://lore.kernel.org/r/20250427095124.3354439-1-algea.cao@rock-chips.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 4c713616963a..be504439c767 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -291,6 +291,8 @@ static const struct ropll_config ropll_tmds_cfg[] = {
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
{ 65000000ULL, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1,
1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
+ { 50250000ULL, 84, 84, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 11, 1, 4, 5,
+ 4, 11, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
{ 33750000ULL, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5,
1, 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
{ 40000000ULL, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0,
--
2.51.0

View File

@ -0,0 +1,49 @@
From 6bde631bd5caf3a5b6719c535d62b96fbd068a5e Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 28 Oct 2025 10:00:55 +0200
Subject: [PATCH 10/11] phy: rockchip: samsung-hdptx: Reduce ROPLL loop
bandwidth
Due to its relatively low frequency, a noise stemming from the 24MHz PLL
reference clock may traverse the low-pass loop filter of ROPLL, which
could potentially generate some HDMI flash artifacts.
Reduce ROPLL loop bandwidth in an attempt to mitigate the problem.
Fixes: 553be2830c5f ("phy: rockchip: Add Samsung HDMI/eDP Combo PHY driver")
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patch.msgid.link/20251028-phy-hdptx-fixes-v1-2-ecc642a59d94@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index be504439c767..bdc52495774d 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -347,9 +347,7 @@ static const struct reg_sequence rk_hdtpx_common_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0043), 0x00),
REG_SEQ0(CMN_REG(0044), 0x46),
REG_SEQ0(CMN_REG(0045), 0x24),
- REG_SEQ0(CMN_REG(0046), 0xff),
REG_SEQ0(CMN_REG(0047), 0x00),
- REG_SEQ0(CMN_REG(0048), 0x44),
REG_SEQ0(CMN_REG(0049), 0xfa),
REG_SEQ0(CMN_REG(004a), 0x08),
REG_SEQ0(CMN_REG(004b), 0x00),
@@ -422,6 +420,8 @@ static const struct reg_sequence rk_hdtpx_tmds_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0034), 0x00),
REG_SEQ0(CMN_REG(003d), 0x40),
REG_SEQ0(CMN_REG(0042), 0x78),
+ REG_SEQ0(CMN_REG(0046), 0xdd),
+ REG_SEQ0(CMN_REG(0048), 0x11),
REG_SEQ0(CMN_REG(004e), 0x34),
REG_SEQ0(CMN_REG(005c), 0x25),
REG_SEQ0(CMN_REG(005e), 0x4f),
--
2.51.0

View File

@ -0,0 +1,54 @@
From 402fc28c3a92e42200b15d5fac63f78e7a50d2e8 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Tue, 28 Oct 2025 10:00:56 +0200
Subject: [PATCH 11/11] phy: rockchip: samsung-hdptx: Prevent Inter-Pair Skew
from exceeding the limits
Fixup PHY deskew FIFO to prevent the phase of D2 lane going ahead of
other lanes. It's worth noting this might only happen when dealing with
HDMI 2.0 rates.
Fixes: 553be2830c5f ("phy: rockchip: Add Samsung HDMI/eDP Combo PHY driver")
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patch.msgid.link/20251028-phy-hdptx-fixes-v1-3-ecc642a59d94@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index bdc52495774d..47c2a3811685 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -515,13 +515,9 @@ static const struct reg_sequence rk_hdtpx_common_lane_init_seq[] = {
static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0312), 0x00),
- REG_SEQ0(LANE_REG(031e), 0x00),
REG_SEQ0(LANE_REG(0412), 0x00),
- REG_SEQ0(LANE_REG(041e), 0x00),
REG_SEQ0(LANE_REG(0512), 0x00),
- REG_SEQ0(LANE_REG(051e), 0x00),
REG_SEQ0(LANE_REG(0612), 0x00),
- REG_SEQ0(LANE_REG(061e), 0x08),
REG_SEQ0(LANE_REG(0303), 0x2f),
REG_SEQ0(LANE_REG(0403), 0x2f),
REG_SEQ0(LANE_REG(0503), 0x2f),
@@ -534,6 +530,11 @@ static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0406), 0x1c),
REG_SEQ0(LANE_REG(0506), 0x1c),
REG_SEQ0(LANE_REG(0606), 0x1c),
+ /* Keep Inter-Pair Skew in the limits */
+ REG_SEQ0(LANE_REG(031e), 0x02),
+ REG_SEQ0(LANE_REG(041e), 0x02),
+ REG_SEQ0(LANE_REG(051e), 0x02),
+ REG_SEQ0(LANE_REG(061e), 0x0a),
};
static bool rk_hdptx_phy_is_rw_reg(struct device *dev, unsigned int reg)
--
2.51.0

View File

@ -0,0 +1,258 @@
--- a/drivers/gpu/drm/bridge/synopsys/Kconfig
+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig
@@ -46,6 +46,14 @@ config DRM_DW_HDMI_CEC
Support the CE interface which is part of the Synopsys
Designware HDMI block.
+config DRM_DW_HDMI_QP
+ tristate
+ select DRM_DISPLAY_HDMI_HELPER
+ select DRM_DISPLAY_HDMI_STATE_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_KMS_HELPER
+ select REGMAP_MMIO
+
config DRM_DW_MIPI_DSI
tristate
select DRM_KMS_HELPER
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -5,4 +5,6 @@ obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
+obj-$(CONFIG_DRM_DW_HDMI_QP) += dw-hdmi-qp.o
+
obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -17,7 +17,7 @@
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
-#include <drm/display/drm_hdmi_state_helper.h>
+//#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@@ -292,89 +292,18 @@ static struct i2c_adapter *dw_hdmi_qp_i2
return adap;
}
-static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi,
- const u8 *buffer, size_t len)
-{
- u32 val, i, j;
-
- if (len != HDMI_INFOFRAME_SIZE(AVI)) {
- dev_err(hdmi->dev, "failed to configure avi infoframe\n");
- return -EINVAL;
- }
-
- /*
- * DW HDMI QP IP uses a different byte format from standard AVI info
- * frames, though generally the bits are in the correct bytes.
- */
- val = buffer[1] << 8 | buffer[2] << 16;
- dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0);
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- if (i * 4 + j >= 14)
- break;
- if (!j)
- val = buffer[i * 4 + j + 3];
- val |= buffer[i * 4 + j + 3] << (8 * j);
- }
-
- dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
- }
-
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
-
- dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
- PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
-
- return 0;
-}
-
-static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi,
- const u8 *buffer, size_t len)
-{
- u32 val, i;
-
- if (len != HDMI_INFOFRAME_SIZE(DRM)) {
- dev_err(hdmi->dev, "failed to configure drm infoframe\n");
- return -EINVAL;
- }
-
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
-
- val = buffer[1] << 8 | buffer[2] << 16;
- dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0);
-
- for (i = 0; i <= buffer[2]; i++) {
- if (i % 4 == 0)
- val = buffer[3 + i];
- val |= buffer[3 + i] << ((i % 4) * 8);
-
- if ((i % 4 == 3) || i == buffer[2])
- dw_hdmi_qp_write(hdmi, val,
- PKT_DRMI_CONTENTS1 + ((i / 4) * 4));
- }
-
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
- dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN,
- PKTSCHED_PKT_EN);
-
- return 0;
-}
-
static int dw_hdmi_qp_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- int ret;
- ret = drm_atomic_helper_connector_hdmi_check(conn_state->connector,
- conn_state->state);
- if (ret)
- dev_dbg(hdmi->dev, "%s failed: %d\n", __func__, ret);
+ dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
+ bridge_state->input_bus_cfg.format,
+ bridge_state->output_bus_cfg.format);
- return ret;
+ return 0;
}
static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
@@ -395,8 +324,7 @@ static void dw_hdmi_qp_bridge_atomic_ena
return;
if (connector->display_info.is_hdmi) {
- dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n",
- __func__, conn_state->hdmi.tmds_char_rate);
+ dev_dbg(hdmi->dev, "%s mode=HDMI\n", __func__);
op_mode = 0;
} else {
dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
@@ -408,7 +336,7 @@ static void dw_hdmi_qp_bridge_atomic_ena
dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0);
- drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+ //drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
}
static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
@@ -441,6 +369,26 @@ dw_hdmi_qp_bridge_edid_read(struct drm_b
return drm_edid;
}
+
+static unsigned long long
+dw_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc,
+ enum hdmi_colorspace fmt)
+{
+ unsigned long long clock = mode->clock * 1000ULL;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock = clock * 2;
+
+ if (fmt == HDMI_COLORSPACE_YUV422)
+ bpc = 8;
+
+ clock = clock * bpc;
+ do_div(clock, 8);
+
+ return clock;
+}
+
static enum drm_mode_status
dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
@@ -449,7 +397,7 @@ dw_hdmi_qp_bridge_mode_valid(struct drm_
struct dw_hdmi_qp *hdmi = bridge->driver_private;
unsigned long long rate;
- rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
+ rate = dw_hdmi_encoder_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
if (rate > HDMI14_MAX_TMDSCLK) {
dev_dbg(hdmi->dev, "Unsupported mode clock: %d\n", mode->clock);
return MODE_CLOCK_HIGH;
@@ -458,49 +406,6 @@ dw_hdmi_qp_bridge_mode_valid(struct drm_
return MODE_OK;
}
-static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type)
-{
- struct dw_hdmi_qp *hdmi = bridge->driver_private;
-
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
- PKTSCHED_PKT_EN);
- break;
-
- case HDMI_INFOFRAME_TYPE_DRM:
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
- break;
-
- default:
- dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
- }
-
- return 0;
-}
-
-static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type,
- const u8 *buffer, size_t len)
-{
- struct dw_hdmi_qp *hdmi = bridge->driver_private;
-
- dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
-
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
-
- case HDMI_INFOFRAME_TYPE_DRM:
- return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
-
- default:
- dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
- return 0;
- }
-}
-
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -511,8 +416,6 @@ static const struct drm_bridge_funcs dw_
.detect = dw_hdmi_qp_bridge_detect,
.edid_read = dw_hdmi_qp_bridge_edid_read,
.mode_valid = dw_hdmi_qp_bridge_mode_valid,
- .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
- .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
};
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
@@ -611,12 +514,9 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struc
hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
- DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
- hdmi->bridge.vendor = "Synopsys";
- hdmi->bridge.product = "DW HDMI QP TX";
hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))

View File

@ -0,0 +1,36 @@
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -355,18 +355,18 @@ dw_hdmi_qp_bridge_detect(struct drm_brid
return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
}
-static const struct drm_edid *
-dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge,
+static struct edid *
+dw_hdmi_qp_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- const struct drm_edid *drm_edid;
+ struct edid *edid;
- drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
- if (!drm_edid)
+ edid = drm_get_edid(connector, bridge->ddc);
+ if (!edid)
dev_dbg(hdmi->dev, "failed to get edid\n");
- return drm_edid;
+ return edid;
}
@@ -414,7 +414,7 @@ static const struct drm_bridge_funcs dw_
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
.detect = dw_hdmi_qp_bridge_detect,
- .edid_read = dw_hdmi_qp_bridge_edid_read,
+ .get_edid = dw_hdmi_qp_bridge_get_edid,
.mode_valid = dw_hdmi_qp_bridge_mode_valid,
};

View File

@ -0,0 +1,57 @@
From 728c15b4b5f3369cbde73d5e0f14701ab370f985 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 23 Oct 2023 17:37:14 +0000
Subject: [PATCH] drm/fourcc: Add NV20 and NV30 YUV formats
DRM_FORMAT_NV20 and DRM_FORMAT_NV30 formats is the 2x1 and non-subsampled
variant of NV15, a 10-bit 2-plane YUV format that has no padding between
components. Instead, luminance and chrominance samples are grouped into 4s
so that each group is packed into an integer number of bytes:
YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes
The '20' and '30' suffix refers to the optimum effective bits per pixel
which is achieved when the total number of luminance samples is a multiple
of 4.
V2: Added NV30 format
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Sandy Huang <hjc@rock-chips.com>
Reviewed-by: Christopher Obbard <chris.obbard@collabora.com>
Tested-by: Christopher Obbard <chris.obbard@collabora.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20231023173718.188102-2-jonas@kwiboo.se
---
drivers/gpu/drm/drm_fourcc.c | 8 ++++++++
include/uapi/drm/drm_fourcc.h | 2 ++
2 files changed, 10 insertions(+)
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -299,6 +299,14 @@ const struct drm_format_info *__drm_form
.num_planes = 2, .char_per_block = { 5, 5, 0 },
.block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
.vsub = 2, .is_yuv = true },
+ { .format = DRM_FORMAT_NV20, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
+ .vsub = 1, .is_yuv = true },
+ { .format = DRM_FORMAT_NV30, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1,
+ .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_Q410, .depth = 0,
.num_planes = 3, .char_per_block = { 2, 2, 2 },
.block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 1,
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -323,6 +323,8 @@ extern "C" {
* index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian
*/
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
/*
* 2 plane YCbCr MSB aligned

View File

@ -0,0 +1,235 @@
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -8,6 +8,7 @@ config DRM_ROCKCHIP
select VIDEOMODE_HELPERS
select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
+ select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP
select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
@@ -63,6 +64,14 @@ config ROCKCHIP_DW_HDMI
enable HDMI on RK3288 or RK3399 based SoC, you should select
this option.
+config ROCKCHIP_DW_HDMI_QP
+ bool "Rockchip specific extensions for Synopsys DW HDMI QP"
+ select DRM_KMS_HELPER
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare HDMI QP driver. If you want to
+ enable HDMI on RK3588 based SoC, you should select this option.
+
config ROCKCHIP_DW_MIPI_DSI
bool "Rockchip specific extensions for Synopsys DW MIPI DSI"
select GENERIC_PHY_MIPI_DPHY
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -11,6 +11,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += ro
rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -19,6 +19,7 @@
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -110,6 +111,66 @@ static struct rockchip_hdmi_qp *to_rockc
return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
}
+/**
+ * drm_hdmi_compute_mode_clock() - Computes the TMDS Character Rate
+ * @mode: Display mode to compute the clock for
+ * @bpc: Bits per character
+ * @fmt: Output Pixel Format used
+ *
+ * Returns the TMDS Character Rate for a given mode, bpc count and output format.
+ *
+ * RETURNS:
+ * The TMDS Character Rate, in Hertz, or 0 on error.
+ */
+static unsigned long long
+rk_hdmi_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc, enum hdmi_colorspace fmt)
+{
+ unsigned long long clock = mode->clock * 1000ULL;
+ unsigned int vic = drm_match_cea_mode(mode);
+
+ /*
+ * CTA-861-G Spec, section 5.4 - Color Coding and Quantization
+ * mandates that VIC 1 always uses 8 bpc.
+ */
+ if (vic == 1 && bpc != 8)
+ return 0;
+
+ if (fmt == HDMI_COLORSPACE_YUV422) {
+ /*
+ * HDMI 1.0 Spec, section 6.5 - Pixel Encoding states that
+ * YUV422 sends 24 bits over three channels, with Cb and Cr
+ * components being sent on odd and even pixels, respectively.
+ *
+ * If fewer than 12 bpc are sent, data are left justified.
+ */
+ if (bpc > 12)
+ return 0;
+
+ /*
+ * HDMI 1.0 Spec, section 6.5 - Pixel Encoding
+ * specifies that YUV422 sends two 12-bits components over
+ * three TMDS channels per pixel clock, which is equivalent to
+ * three 8-bits components over three channels used by RGB as
+ * far as the clock rate goes.
+ */
+ bpc = 8;
+ }
+
+ /*
+ * HDMI 2.0 Spec, Section 7.1 - YCbCr 4:2:0 Pixel Encoding
+ * specifies that YUV420 encoding is carried at a TMDS Character Rate
+ * equal to half the pixel clock rate.
+ */
+ if (fmt == HDMI_COLORSPACE_YUV420)
+ clock = clock / 2;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock = clock * 2;
+
+ return DIV_ROUND_CLOSEST_ULL(clock * bpc, 8);
+}
+
static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
@@ -120,7 +181,7 @@ static void dw_hdmi_qp_rockchip_encoder_
gpiod_set_value(hdmi->enable_gpio, 1);
if (crtc && crtc->state) {
- rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
+ rate = rk_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
8, HDMI_COLORSPACE_RGB);
/*
* FIXME: Temporary workaround to pass pixel clock rate
@@ -427,6 +488,51 @@ static const struct of_device_id dw_hdmi
};
MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
+struct clk_bulk_devres {
+ struct clk_bulk_data *clks;
+ int num_clks;
+};
+
+static void rk_devm_clk_bulk_release_all_enable(struct device *dev, void *res)
+{
+ struct clk_bulk_devres *devres = res;
+
+ clk_bulk_disable_unprepare(devres->num_clks, devres->clks);
+ clk_bulk_put_all(devres->num_clks, devres->clks);
+}
+
+static int __must_check rk_devm_clk_bulk_get_all_enabled(struct device *dev,
+ struct clk_bulk_data **clks)
+{
+ struct clk_bulk_devres *devres;
+ int ret;
+
+ devres = devres_alloc(rk_devm_clk_bulk_release_all_enable,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = clk_bulk_get_all(dev, &devres->clks);
+ if (ret > 0) {
+ *clks = devres->clks;
+ devres->num_clks = ret;
+ } else {
+ devres_free(devres);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(devres->num_clks, *clks);
+ if (!ret) {
+ devres_add(dev, devres);
+ } else {
+ clk_bulk_put_all(devres->num_clks, devres->clks);
+ devres_free(devres);
+ return ret;
+ }
+
+ return devres->num_clks;
+}
+
static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
@@ -509,7 +615,7 @@ static int dw_hdmi_qp_rockchip_bind(stru
return PTR_ERR(hdmi->vo_regmap);
}
- ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
+ ret = rk_devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
if (ret < 0) {
dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
return ret;
@@ -619,7 +725,7 @@ static const struct dev_pm_ops dw_hdmi_q
struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
.probe = dw_hdmi_qp_rockchip_probe,
- .remove = dw_hdmi_qp_rockchip_remove,
+ .remove_new = dw_hdmi_qp_rockchip_remove,
.driver = {
.name = "dwhdmiqp-rockchip",
.pm = &dw_hdmi_qp_rockchip_pm,
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -503,6 +503,8 @@ static int __init rockchip_drm_init(void
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
CONFIG_ROCKCHIP_DW_HDMI);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
+ CONFIG_ROCKCHIP_DW_HDMI_QP);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -87,6 +87,7 @@ int rockchip_drm_encoder_set_crtc_endpoi
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
+extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver;
extern struct platform_driver dw_mipi_dsi_rockchip_driver;
extern struct platform_driver inno_hdmi_driver;
extern struct platform_driver rockchip_dp_driver;
--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -2566,7 +2566,7 @@ static void vop2_remove(struct platform_
struct platform_driver vop2_platform_driver = {
.probe = vop2_probe,
- .remove = vop2_remove,
+ .remove_new = vop2_remove,
.driver = {
.name = "rockchip-vop2",
.of_match_table = vop2_dt_match,
--- a/include/dt-bindings/soc/rockchip,vop2.h
+++ b/include/dt-bindings/soc/rockchip,vop2.h
@@ -10,5 +10,9 @@
#define ROCKCHIP_VOP2_EP_LVDS0 5
#define ROCKCHIP_VOP2_EP_MIPI1 6
#define ROCKCHIP_VOP2_EP_LVDS1 7
+#define ROCKCHIP_VOP2_EP_HDMI1 8
+#define ROCKCHIP_VOP2_EP_EDP1 9
+#define ROCKCHIP_VOP2_EP_DP0 10
+#define ROCKCHIP_VOP2_EP_DP1 11
#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */