summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/rockchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/rockchip')
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig10
-rw-r--r--drivers/gpu/drm/rockchip/Makefile1
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c118
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.c6
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.h2
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.c2
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.h2
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c2
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c487
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c18
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c352
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.c42
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.h2
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.c4
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c13
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.c11
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c4
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c2012
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.h330
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.c82
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop2_reg.c2071
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.h2
32 files changed, 4096 insertions, 1498 deletions
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 3ac579615749..26c4410b2407 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -11,6 +11,7 @@ config DRM_ROCKCHIP
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 DRM_DW_MIPI_DSI2 if ROCKCHIP_DW_MIPI_DSI2
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
@@ -82,6 +83,15 @@ config ROCKCHIP_DW_MIPI_DSI
enable MIPI DSI on RK3288 or RK3399 based SoC, you should
select this option.
+config ROCKCHIP_DW_MIPI_DSI2
+ bool "Rockchip specific extensions for Synopsys DW MIPI DSI2"
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare DSI2 driver. If you want to
+ enable MIPI DSI on RK3576 or RK3588 based SoC, you should
+ select this option.
+
config ROCKCHIP_INNO_HDMI
bool "Rockchip specific extensions for Innosilicon HDMI"
select DRM_DISPLAY_HDMI_HELPER
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 3eab662a5a1d..2b867cebbc12 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -13,6 +13,7 @@ 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_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 546d13f19f9b..a8265a1bf9ff 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -2,7 +2,7 @@
/*
* Rockchip SoC DP (Display Port) interface driver.
*
- * Copyright (C) Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Andy Yan <andy.yan@rock-chips.com>
* Yakir Yang <ykk@rock-chips.com>
* Jeff Chen <jeff.chen@rock-chips.com>
@@ -32,27 +32,32 @@
#include "rockchip_drm_drv.h"
-#define RK3288_GRF_SOC_CON6 0x25c
-#define RK3288_EDP_LCDC_SEL BIT(5)
-#define RK3399_GRF_SOC_CON20 0x6250
-#define RK3399_EDP_LCDC_SEL BIT(5)
-
-#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
-
#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100
+#define GRF_REG_FIELD(_reg, _lsb, _msb) { \
+ .reg = _reg, \
+ .lsb = _lsb, \
+ .msb = _msb, \
+ .valid = true, \
+ }
+
+struct rockchip_grf_reg_field {
+ u32 reg;
+ u32 lsb;
+ u32 msb;
+ bool valid;
+};
+
/**
* struct rockchip_dp_chip_data - splite the grf setting of kind of chips
- * @lcdsel_grf_reg: grf register offset of lcdc select
- * @lcdsel_big: reg value of selecting vop big for eDP
- * @lcdsel_lit: reg value of selecting vop little for eDP
+ * @lcdc_sel: grf register field of lcdc_sel
* @chip_type: specific chip type
+ * @reg: register base address
*/
struct rockchip_dp_chip_data {
- u32 lcdsel_grf_reg;
- u32 lcdsel_big;
- u32 lcdsel_lit;
+ const struct rockchip_grf_reg_field lcdc_sel;
u32 chip_type;
+ u32 reg;
};
struct rockchip_dp_device {
@@ -84,6 +89,26 @@ static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_da
return container_of(plat_data, struct rockchip_dp_device, plat_data);
}
+static int rockchip_grf_write(struct regmap *grf, u32 reg, u32 mask, u32 val)
+{
+ return regmap_write(grf, reg, (mask << 16) | (val & mask));
+}
+
+static int rockchip_grf_field_write(struct regmap *grf,
+ const struct rockchip_grf_reg_field *field,
+ u32 val)
+{
+ u32 mask;
+
+ if (!field->valid)
+ return 0;
+
+ mask = GENMASK(field->msb, field->lsb);
+ val <<= field->lsb;
+
+ return rockchip_grf_write(grf, field->reg, mask, val);
+}
+
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
{
reset_control_assert(dp->rst);
@@ -181,7 +206,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
int ret;
- u32 val;
crtc = rockchip_dp_drm_get_new_crtc(encoder, state);
if (!crtc)
@@ -192,24 +216,19 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
- ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
- if (ret < 0)
- return;
-
- if (ret)
- val = dp->data->lcdsel_lit;
- else
- val = dp->data->lcdsel_big;
-
- DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
-
ret = clk_prepare_enable(dp->grfclk);
if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
return;
}
- ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
+ ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+ if (ret < 0)
+ return;
+
+ DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
+
+ ret = rockchip_grf_field_write(dp->grf, &dp->data->lcdc_sel, ret);
if (ret != 0)
DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
@@ -379,6 +398,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
const struct rockchip_dp_chip_data *dp_data;
struct drm_panel *panel = NULL;
struct rockchip_dp_device *dp;
+ struct resource *res;
+ int i;
int ret;
dp_data = of_device_get_match_data(dev);
@@ -386,16 +407,31 @@ static int rockchip_dp_probe(struct platform_device *pdev)
return -ENODEV;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
- if (ret < 0)
+ if (ret < 0 && ret != -ENODEV)
return ret;
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ i = 0;
+ while (dp_data[i].reg) {
+ if (dp_data[i].reg == res->start) {
+ dp->data = &dp_data[i];
+ break;
+ }
+
+ i++;
+ }
+
+ if (!dp->data)
+ return dev_err_probe(dev, -EINVAL, "no chip-data for %s node\n",
+ dev->of_node->name);
+
dp->dev = dev;
dp->adp = ERR_PTR(-ENODEV);
- dp->data = dp_data;
dp->plat_data.panel = panel;
dp->plat_data.dev_type = dp->data->chip_type;
dp->plat_data.power_on = rockchip_dp_poweron;
@@ -447,18 +483,22 @@ static int rockchip_dp_resume(struct device *dev)
static DEFINE_RUNTIME_DEV_PM_OPS(rockchip_dp_pm_ops, rockchip_dp_suspend,
rockchip_dp_resume, NULL);
-static const struct rockchip_dp_chip_data rk3399_edp = {
- .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
- .lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL),
- .lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL),
- .chip_type = RK3399_EDP,
+static const struct rockchip_dp_chip_data rk3399_edp[] = {
+ {
+ .lcdc_sel = GRF_REG_FIELD(0x6250, 5, 5),
+ .chip_type = RK3399_EDP,
+ .reg = 0xff970000,
+ },
+ { /* sentinel */ }
};
-static const struct rockchip_dp_chip_data rk3288_dp = {
- .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
- .lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL),
- .lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL),
- .chip_type = RK3288_DP,
+static const struct rockchip_dp_chip_data rk3288_dp[] = {
+ {
+ .lcdc_sel = GRF_REG_FIELD(0x025c, 5, 5),
+ .chip_type = RK3288_DP,
+ .reg = 0xff970000,
+ },
+ { /* sentinel */ }
};
static const struct of_device_id rockchip_dp_dt_ids[] = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index a7891a139c88..292c31de18f1 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Chris Zhong <zyw@rock-chips.com>
*/
@@ -275,7 +275,7 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector)
static enum drm_mode_status
cdn_dp_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct cdn_dp_device *dp = connector_to_dp(connector);
struct drm_display_info *display_info = &dp->connector.display_info;
@@ -885,7 +885,6 @@ static const struct hdmi_codec_ops audio_codec_ops = {
.mute_stream = cdn_dp_audio_mute_stream,
.get_eld = cdn_dp_audio_get_eld,
.hook_plugged_cb = cdn_dp_audio_hook_plugged_cb,
- .no_capture_mute = 1,
};
static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
@@ -896,6 +895,7 @@ static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
.spdif = 1,
.ops = &audio_codec_ops,
.max_i2s_channels = 8,
+ .no_capture_mute = 1,
};
dp->audio_pdev = platform_device_register_data(
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 8e6e95d269da..17498f576ce7 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2016 Chris Zhong <zyw@rock-chips.com>
- * Copyright (C) 2016 ROCKCHIP, Inc.
+ * Copyright (C) Rockchip Electronics Co., Ltd.
*/
#ifndef _CDN_DP_CORE_H
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 33fb4d05c506..924fb1d3ece2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Chris Zhong <zyw@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index c7780ae3272a..13ed8cbdbafa 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Chris Zhong <zyw@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 1b64b6e39cc8..3398160ad75e 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:
* Chris Zhong <zyw@rock-chips.com>
* Nickey Yang <nickey.yang@rock-chips.com>
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
new file mode 100644
index 000000000000..cdd490778756
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Rockchip Electronics Co., Ltd.
+ * Author:
+ * Guochun Huang <hero.huang@rock-chips.com>
+ * Heiko Stuebner <heiko.stuebner@cherry.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/media-bus-format.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <drm/bridge/dw_mipi_dsi2.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <uapi/linux/videodev2.h>
+
+#include "rockchip_drm_drv.h"
+
+#define PSEC_PER_SEC 1000000000000LL
+
+struct dsigrf_reg {
+ u16 offset;
+ u16 lsb;
+ u16 msb;
+};
+
+enum grf_reg_fields {
+ TXREQCLKHS_EN,
+ GATING_EN,
+ IPI_SHUTDN,
+ IPI_COLORM,
+ IPI_COLOR_DEPTH,
+ IPI_FORMAT,
+ MAX_FIELDS,
+};
+
+#define IPI_DEPTH_5_6_5_BITS 0x02
+#define IPI_DEPTH_6_BITS 0x03
+#define IPI_DEPTH_8_BITS 0x05
+#define IPI_DEPTH_10_BITS 0x06
+
+struct rockchip_dw_dsi2_chip_data {
+ u32 reg;
+ const struct dsigrf_reg *grf_regs;
+ unsigned long long max_bit_rate_per_lane;
+};
+
+struct dw_mipi_dsi2_rockchip {
+ struct device *dev;
+ struct rockchip_encoder encoder;
+ struct regmap *regmap;
+
+ unsigned int lane_mbps; /* per lane */
+ u32 format;
+
+ struct regmap *grf_regmap;
+ struct phy *phy;
+ union phy_configure_opts phy_opts;
+
+ struct dw_mipi_dsi2 *dmd;
+ struct dw_mipi_dsi2_plat_data pdata;
+ const struct rockchip_dw_dsi2_chip_data *cdata;
+};
+
+static inline struct dw_mipi_dsi2_rockchip *to_dsi2(struct drm_encoder *encoder)
+{
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ return container_of(rkencoder, struct dw_mipi_dsi2_rockchip, encoder);
+}
+
+static void grf_field_write(struct dw_mipi_dsi2_rockchip *dsi2, enum grf_reg_fields index,
+ unsigned int val)
+{
+ const struct dsigrf_reg *field = &dsi2->cdata->grf_regs[index];
+
+ if (!field)
+ return;
+
+ regmap_write(dsi2->grf_regmap, field->offset,
+ (val << field->lsb) | (GENMASK(field->msb, field->lsb) << 16));
+}
+
+static int dw_mipi_dsi2_phy_init(void *priv_data)
+{
+ return 0;
+}
+
+static void dw_mipi_dsi2_phy_power_on(void *priv_data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ int ret;
+
+ ret = phy_set_mode(dsi2->phy, PHY_MODE_MIPI_DPHY);
+ if (ret) {
+ dev_err(dsi2->dev, "Failed to set phy mode: %d\n", ret);
+ return;
+ }
+
+ phy_configure(dsi2->phy, &dsi2->phy_opts);
+ phy_power_on(dsi2->phy);
+}
+
+static void dw_mipi_dsi2_phy_power_off(void *priv_data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+
+ phy_power_off(dsi2->phy);
+}
+
+static int
+dw_mipi_dsi2_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format,
+ unsigned int *lane_mbps)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ u64 max_lane_rate, target_phyclk;
+ unsigned int lane_rate_kbps;
+ int bpp;
+
+ max_lane_rate = dsi2->cdata->max_bit_rate_per_lane;
+
+ dsi2->format = format;
+ bpp = mipi_dsi_pixel_format_to_bpp(format);
+ if (bpp < 0) {
+ dev_err(dsi2->dev, "failed to get bpp for pixel format %d\n", format);
+ return bpp;
+ }
+
+ lane_rate_kbps = mode->clock * bpp / lanes;
+
+ /*
+ * Set BW a little larger only in video burst mode in
+ * consideration of the protocol overhead and HS mode
+ * switching to BLLP mode, take 1 / 0.9, since Mbps must
+ * big than bandwidth of RGB
+ */
+ if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ lane_rate_kbps = (lane_rate_kbps * 10) / 9;
+
+ if (lane_rate_kbps > max_lane_rate) {
+ dev_err(dsi2->dev, "DPHY clock frequency is out of range\n");
+ return -ERANGE;
+ }
+
+ dsi2->lane_mbps = lane_rate_kbps / 1000;
+ *lane_mbps = dsi2->lane_mbps;
+
+ if (dsi2->phy) {
+ target_phyclk = DIV_ROUND_CLOSEST_ULL(lane_rate_kbps * lanes * 1000, bpp);
+ phy_mipi_dphy_get_default_config(target_phyclk, bpp, lanes,
+ &dsi2->phy_opts.mipi_dphy);
+ }
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_phy_get_iface(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface)
+{
+ /* PPI width is fixed to 16 bits in DCPHY */
+ iface->ppi_width = 16;
+ iface->phy_type = DW_MIPI_DSI2_DPHY;
+}
+
+static int
+dw_mipi_dsi2_phy_get_timing(void *priv_data, unsigned int lane_mbps,
+ struct dw_mipi_dsi2_phy_timing *timing)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ struct phy_configure_opts_mipi_dphy *cfg = &dsi2->phy_opts.mipi_dphy;
+ unsigned long long tmp, ui;
+ unsigned long long hstx_clk;
+
+ hstx_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16);
+
+ ui = ALIGN(PSEC_PER_SEC, hstx_clk);
+ do_div(ui, hstx_clk);
+
+ /* PHY_LP2HS_TIME = (TLPX + THS-PREPARE + THS-ZERO) / Tphy_hstx_clk */
+ tmp = cfg->lpx + cfg->hs_prepare + cfg->hs_zero;
+ tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui);
+ timing->data_lp2hs = tmp;
+
+ /* PHY_HS2LP_TIME = (THS-TRAIL + THS-EXIT) / Tphy_hstx_clk */
+ tmp = cfg->hs_trail + cfg->hs_exit;
+ tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui);
+ timing->data_hs2lp = tmp;
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi2_phy_ops dw_mipi_dsi2_rockchip_phy_ops = {
+ .init = dw_mipi_dsi2_phy_init,
+ .power_on = dw_mipi_dsi2_phy_power_on,
+ .power_off = dw_mipi_dsi2_phy_power_off,
+ .get_interface = dw_mipi_dsi2_phy_get_iface,
+ .get_lane_mbps = dw_mipi_dsi2_get_lane_mbps,
+ .get_timing = dw_mipi_dsi2_phy_get_timing,
+};
+
+static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder);
+ u32 color_depth;
+
+ switch (dsi2->format) {
+ case MIPI_DSI_FMT_RGB666:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ color_depth = IPI_DEPTH_6_BITS;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ color_depth = IPI_DEPTH_5_6_5_BITS;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ color_depth = IPI_DEPTH_8_BITS;
+ break;
+ default:
+ /* Should've been caught by atomic_check */
+ WARN_ON(1);
+ return;
+ }
+
+ grf_field_write(dsi2, IPI_COLOR_DEPTH, color_depth);
+}
+
+static int
+dw_mipi_dsi2_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);
+ struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder);
+ struct drm_connector *connector = conn_state->connector;
+ struct drm_display_info *info = &connector->display_info;
+
+ switch (dsi2->format) {
+ case MIPI_DSI_FMT_RGB666:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ s->output_mode = ROCKCHIP_OUT_MODE_P666;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ s->output_mode = ROCKCHIP_OUT_MODE_P565;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ s->output_mode = ROCKCHIP_OUT_MODE_P888;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (info->num_bus_formats)
+ s->bus_format = info->bus_formats[0];
+ else
+ s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ s->output_type = DRM_MODE_CONNECTOR_DSI;
+ s->bus_flags = info->bus_flags;
+ s->color_space = V4L2_COLORSPACE_DEFAULT;
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs
+dw_mipi_dsi2_encoder_helper_funcs = {
+ .atomic_enable = dw_mipi_dsi2_encoder_atomic_enable,
+ .atomic_check = dw_mipi_dsi2_encoder_atomic_check,
+};
+
+static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2,
+ struct drm_device *drm_dev)
+{
+ struct drm_encoder *encoder = &dsi2->encoder.encoder;
+ int ret;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+ dsi2->dev->of_node);
+
+ ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+ if (ret) {
+ dev_err(dsi2->dev, "Failed to initialize encoder with drm\n");
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &dw_mipi_dsi2_encoder_helper_funcs);
+
+ return 0;
+}
+
+static int dw_mipi_dsi2_rockchip_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ int ret;
+
+ ret = rockchip_dsi2_drm_create_encoder(dsi2, drm_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to create drm encoder\n");
+
+ rockchip_drm_encoder_set_crtc_endpoint_id(&dsi2->encoder,
+ dev->of_node, 0, 0);
+
+ ret = dw_mipi_dsi2_bind(dsi2->dmd, &dsi2->encoder.encoder);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to bind\n");
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_rockchip_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev);
+
+ dw_mipi_dsi2_unbind(dsi2->dmd);
+}
+
+static const struct component_ops dw_mipi_dsi2_rockchip_ops = {
+ .bind = dw_mipi_dsi2_rockchip_bind,
+ .unbind = dw_mipi_dsi2_rockchip_unbind,
+};
+
+static int dw_mipi_dsi2_rockchip_host_attach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ int ret;
+
+ ret = component_add(dsi2->dev, &dw_mipi_dsi2_rockchip_ops);
+ if (ret)
+ return dev_err_probe(dsi2->dev, ret, "Failed to register component\n");
+
+ return 0;
+}
+
+static int dw_mipi_dsi2_rockchip_host_detach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+
+ component_del(dsi2->dev, &dw_mipi_dsi2_rockchip_ops);
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi2_host_ops dw_mipi_dsi2_rockchip_host_ops = {
+ .attach = dw_mipi_dsi2_rockchip_host_attach,
+ .detach = dw_mipi_dsi2_rockchip_host_detach,
+};
+
+static const struct regmap_config dw_mipi_dsi2_rockchip_regmap_config = {
+ .name = "dsi2-host",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static int dw_mipi_dsi2_rockchip_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct rockchip_dw_dsi2_chip_data *cdata =
+ of_device_get_match_data(dev);
+ struct dw_mipi_dsi2_rockchip *dsi2;
+ struct resource *res;
+ void __iomem *base;
+ int i;
+
+ dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL);
+ if (!dsi2)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "Unable to get dsi registers\n");
+
+ dsi2->regmap = devm_regmap_init_mmio(dev, base, &dw_mipi_dsi2_rockchip_regmap_config);
+ if (IS_ERR(dsi2->regmap))
+ return dev_err_probe(dev, PTR_ERR(dsi2->regmap), "failed to init register map\n");
+
+ i = 0;
+ while (cdata[i].reg) {
+ if (cdata[i].reg == res->start) {
+ dsi2->cdata = &cdata[i];
+ break;
+ }
+
+ i++;
+ }
+
+ if (!dsi2->cdata)
+ return dev_err_probe(dev, -EINVAL, "No dsi-config for %s node\n", np->name);
+
+ dsi2->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(dsi2->grf_regmap))
+ return dev_err_probe(dsi2->dev, PTR_ERR(dsi2->grf_regmap), "Unable to get grf\n");
+
+ dsi2->phy = devm_phy_optional_get(dev, "dcphy");
+ if (IS_ERR(dsi2->phy))
+ return dev_err_probe(dev, PTR_ERR(dsi2->phy), "failed to get mipi phy\n");
+
+ dsi2->dev = dev;
+ dsi2->pdata.regmap = dsi2->regmap;
+ dsi2->pdata.max_data_lanes = 4;
+ dsi2->pdata.phy_ops = &dw_mipi_dsi2_rockchip_phy_ops;
+ dsi2->pdata.host_ops = &dw_mipi_dsi2_rockchip_host_ops;
+ dsi2->pdata.priv_data = dsi2;
+ platform_set_drvdata(pdev, dsi2);
+
+ dsi2->dmd = dw_mipi_dsi2_probe(pdev, &dsi2->pdata);
+ if (IS_ERR(dsi2->dmd))
+ return dev_err_probe(dev, PTR_ERR(dsi2->dmd), "Failed to probe dw_mipi_dsi2\n");
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_rockchip_remove(struct platform_device *pdev)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = platform_get_drvdata(pdev);
+
+ dw_mipi_dsi2_remove(dsi2->dmd);
+}
+
+static const struct dsigrf_reg rk3588_dsi0_grf_reg_fields[MAX_FIELDS] = {
+ [TXREQCLKHS_EN] = { 0x0000, 11, 11 },
+ [GATING_EN] = { 0x0000, 10, 10 },
+ [IPI_SHUTDN] = { 0x0000, 9, 9 },
+ [IPI_COLORM] = { 0x0000, 8, 8 },
+ [IPI_COLOR_DEPTH] = { 0x0000, 4, 7 },
+ [IPI_FORMAT] = { 0x0000, 0, 3 },
+};
+
+static const struct dsigrf_reg rk3588_dsi1_grf_reg_fields[MAX_FIELDS] = {
+ [TXREQCLKHS_EN] = { 0x0004, 11, 11 },
+ [GATING_EN] = { 0x0004, 10, 10 },
+ [IPI_SHUTDN] = { 0x0004, 9, 9 },
+ [IPI_COLORM] = { 0x0004, 8, 8 },
+ [IPI_COLOR_DEPTH] = { 0x0004, 4, 7 },
+ [IPI_FORMAT] = { 0x0004, 0, 3 },
+};
+
+static const struct rockchip_dw_dsi2_chip_data rk3588_chip_data[] = {
+ {
+ .reg = 0xfde20000,
+ .grf_regs = rk3588_dsi0_grf_reg_fields,
+ .max_bit_rate_per_lane = 4500000ULL,
+ },
+ {
+ .reg = 0xfde30000,
+ .grf_regs = rk3588_dsi1_grf_reg_fields,
+ .max_bit_rate_per_lane = 4500000ULL,
+ }
+};
+
+static const struct of_device_id dw_mipi_dsi2_rockchip_dt_ids[] = {
+ {
+ .compatible = "rockchip,rk3588-mipi-dsi2",
+ .data = &rk3588_chip_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi2_rockchip_dt_ids);
+
+struct platform_driver dw_mipi_dsi2_rockchip_driver = {
+ .probe = dw_mipi_dsi2_rockchip_probe,
+ .remove = dw_mipi_dsi2_rockchip_remove,
+ .driver = {
+ .of_match_table = dw_mipi_dsi2_rockchip_dt_ids,
+ .name = "dw-mipi-dsi2",
+ },
+};
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 42bda4ffbbbd..f737e7d46e66 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (c) 2014, Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
@@ -203,7 +203,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(hdmi->regmap)) {
- drm_err(hdmi, "Unable to get rockchip,grf\n");
+ dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
return PTR_ERR(hdmi->regmap);
}
@@ -214,7 +214,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
if (IS_ERR(hdmi->ref_clk)) {
ret = PTR_ERR(hdmi->ref_clk);
if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "failed to get reference clock\n");
+ dev_err(hdmi->dev, "failed to get reference clock\n");
return ret;
}
@@ -222,7 +222,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
if (IS_ERR(hdmi->grf_clk)) {
ret = PTR_ERR(hdmi->grf_clk);
if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "failed to get grf clock\n");
+ dev_err(hdmi->dev, "failed to get grf clock\n");
return ret;
}
@@ -302,16 +302,16 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
ret = clk_prepare_enable(hdmi->grf_clk);
if (ret < 0) {
- drm_err(hdmi, "failed to enable grfclk %d\n", ret);
+ dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
return;
}
ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
if (ret != 0)
- drm_err(hdmi, "Could not write to GRF: %d\n", ret);
+ dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret);
clk_disable_unprepare(hdmi->grf_clk);
- drm_dbg(hdmi, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
+ dev_dbg(hdmi->dev, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
}
static int
@@ -574,7 +574,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
ret = rockchip_hdmi_parse_dt(hdmi);
if (ret) {
if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "Unable to parse OF data\n");
+ dev_err(hdmi->dev, "Unable to parse OF data\n");
return ret;
}
@@ -582,7 +582,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
if (IS_ERR(hdmi->phy)) {
ret = PTR_ERR(hdmi->phy);
if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "failed to get phy\n");
+ dev_err(hdmi->dev, "failed to get phy\n");
return ret;
}
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index c8b362cc2b95..7d531b6f4c09 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -25,34 +25,82 @@
#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 clk *ref_clk;
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)
@@ -74,7 +122,6 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
if (crtc && crtc->state) {
rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
8, HDMI_COLORSPACE_RGB);
- clk_set_rate(hdmi->ref_clk, rate);
/*
* FIXME: Temporary workaround to pass pixel clock rate
* to the PHY driver until phy_configure_opts_hdmi
@@ -127,20 +174,24 @@ dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *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 & RK3588_HDMI0_LEVEL_INT ?
- connector_status_connected : connector_status_disconnected;
+ 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,
- 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 = {
@@ -150,6 +201,37 @@ static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
.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,
@@ -161,8 +243,45 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
if (drm) {
changed = drm_helper_hpd_irq_event(drm);
if (changed)
- drm_dbg(hdmi, "connector status changed\n");
+ 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)
@@ -173,8 +292,12 @@ static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
if (intr_stat) {
- val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
- RK3588_HDMI0_HPD_INT_MSK);
+ 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;
}
@@ -191,22 +314,115 @@ static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
if (!intr_stat)
return IRQ_NONE;
- val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
- RK3588_HDMI0_HPD_INT_CLR);
+ 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));
- val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+ 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,rk3588-dw-hdmi-qp",
- .data = &rk3588_hdmi_phy_ops },
+ {
+ .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);
@@ -214,19 +430,16 @@ 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)
{
- static const char * const clk_names[] = {
- "pclk", "earc", "aud", "hdp", "hclk_vo1",
- "ref" /* keep "ref" last */
- };
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 clk *clk;
+ struct resource *res;
+ struct clk_bulk_data *clks;
int ret, irq, i;
- u32 val;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -235,12 +448,38 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (!hdmi)
return -ENOMEM;
- plat_data.phy_ops = of_device_get_match_data(dev);
- if (!plat_data.phy_ops)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
return -ENODEV;
- plat_data.phy_data = hdmi;
+ 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);
@@ -259,35 +498,28 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(hdmi->regmap)) {
- drm_err(hdmi, "Unable to get rockchip,grf\n");
+ 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)) {
- drm_err(hdmi, "Unable to get rockchip,vo-grf\n");
+ dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n");
return PTR_ERR(hdmi->vo_regmap);
}
- for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
- clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]);
-
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "Failed to get %s clock: %d\n",
- clk_names[i], ret);
- return ret;
- }
+ 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->ref_clk = clk;
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);
- drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret);
+ dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret);
return ret;
}
@@ -295,26 +527,11 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (IS_ERR(hdmi->phy)) {
ret = PTR_ERR(hdmi->phy);
if (ret != -EPROBE_DEFER)
- drm_err(hdmi, "failed to get phy: %d\n", ret);
+ dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
return ret;
}
- 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, 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);
-
- val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
- RK3588_HDMI0_GRANT_SEL);
- regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
-
- val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
- regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ cfg->ctrl_ops->io_init(hdmi);
INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
@@ -327,8 +544,8 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
return irq;
ret = devm_request_threaded_irq(hdmi->dev, irq,
- dw_hdmi_qp_rk3588_hardirq,
- dw_hdmi_qp_rk3588_irq,
+ cfg->ctrl_ops->hardirq_callback,
+ cfg->ctrl_ops->irq_callback,
IRQF_SHARED, "dw-hdmi-qp-hpd",
hdmi);
if (ret)
@@ -349,7 +566,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector)) {
ret = PTR_ERR(connector);
- drm_err(hdmi, "failed to init bridge connector: %d\n", ret);
+ dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
return ret;
}
@@ -385,21 +602,8 @@ static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
- 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, 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);
-
- val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
- RK3588_HDMI0_GRANT_SEL);
- regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
+ hdmi->ctrl_ops->io_init(hdmi);
dw_hdmi_qp_resume(dev, hdmi->hdmi);
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index b58e2a29294b..87dfd3000158 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Zheng Yang <zhengyang@rock-chips.com>
* Yakir Yang <ykk@rock-chips.com>
*/
@@ -10,10 +10,12 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -29,8 +31,19 @@
#include "inno_hdmi.h"
+#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
+
#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
+#define RK3036_GRF_SOC_CON2 0x148
+#define RK3036_HDMI_PHSYNC BIT(4)
+#define RK3036_HDMI_PVSYNC BIT(5)
+
+enum inno_hdmi_dev_type {
+ RK3036_HDMI,
+ RK3128_HDMI,
+};
+
struct inno_hdmi_phy_config {
unsigned long pixelclock;
u8 pre_emphasis;
@@ -38,6 +51,7 @@ struct inno_hdmi_phy_config {
};
struct inno_hdmi_variant {
+ enum inno_hdmi_dev_type dev_type;
struct inno_hdmi_phy_config *phy_configs;
struct inno_hdmi_phy_config *default_phy_config;
};
@@ -58,6 +72,7 @@ struct inno_hdmi {
struct clk *pclk;
struct clk *refclk;
void __iomem *regs;
+ struct regmap *grf;
struct drm_connector connector;
struct rockchip_encoder encoder;
@@ -374,7 +389,15 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
- int value;
+ int value, psync;
+
+ if (hdmi->variant->dev_type == RK3036_HDMI) {
+ psync = mode->flags & DRM_MODE_FLAG_PHSYNC ? RK3036_HDMI_PHSYNC : 0;
+ value = HIWORD_UPDATE(psync, RK3036_HDMI_PHSYNC);
+ psync = mode->flags & DRM_MODE_FLAG_PVSYNC ? RK3036_HDMI_PVSYNC : 0;
+ value |= HIWORD_UPDATE(psync, RK3036_HDMI_PVSYNC);
+ regmap_write(hdmi->grf, RK3036_GRF_SOC_CON2, value);
+ }
/* Set detail external video timing polarity and interlace mode */
value = v_EXTERANL_VIDEO(1);
@@ -471,7 +494,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
}
static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
unsigned long mpixelclk, max_tolerance;
long rounded_refclk;
@@ -577,7 +600,7 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
static enum drm_mode_status
inno_hdmi_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
@@ -911,6 +934,15 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_pclk;
}
+ if (hdmi->variant->dev_type == RK3036_HDMI) {
+ hdmi->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(hdmi->grf)) {
+ ret = dev_err_probe(dev, PTR_ERR(hdmi->grf),
+ "Unable to get rockchip,grf\n");
+ goto err_disable_clk;
+ }
+ }
+
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
@@ -995,11 +1027,13 @@ static void inno_hdmi_remove(struct platform_device *pdev)
}
static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
+ .dev_type = RK3036_HDMI,
.phy_configs = rk3036_hdmi_phy_configs,
.default_phy_config = &rk3036_hdmi_phy_configs[1],
};
static const struct inno_hdmi_variant rk3128_inno_hdmi_variant = {
+ .dev_type = RK3128_HDMI,
.phy_configs = rk3128_hdmi_phy_configs,
.default_phy_config = &rk3128_hdmi_phy_configs[1],
};
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
index a7edf3559e60..8b7ef3fac485 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.h
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Zheng Yang <zhengyang@rock-chips.com>
* Yakir Yang <ykk@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index b0fc8ace2e41..f7a460190313 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Zheng Yang <zhengyang@rock-chips.com>
*/
@@ -482,7 +482,7 @@ static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
static enum drm_mode_status
rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
u32 vic = drm_match_cea_mode(mode);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.h b/drivers/gpu/drm/rockchip/rk3066_hdmi.h
index 39a31c62a428..c3598ba7428c 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.h
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Zheng Yang <zhengyang@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 32d8394c4c49..180fad5d49ad 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*
* based on exynos_drm_drv.c
@@ -17,7 +17,7 @@
#include <linux/console.h>
#include <linux/iommu.h>
-#include <drm/drm_client_setup.h>
+#include <drm/clients/drm_client_setup.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
@@ -39,7 +39,6 @@
#define DRIVER_NAME "rockchip"
#define DRIVER_DESC "RockChip Soc DRM"
-#define DRIVER_DATE "20140818"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
@@ -235,7 +234,6 @@ static const struct drm_driver rockchip_drm_driver = {
.fops = &rockchip_drm_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
- .date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
@@ -486,10 +484,11 @@ static void rockchip_drm_platform_remove(struct platform_device *pdev)
static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
{
- struct drm_device *drm = platform_get_drvdata(pdev);
+ if (component_master_is_bound(&pdev->dev, &rockchip_drm_ops)) {
+ struct drm_device *drm = platform_get_drvdata(pdev);
- if (drm)
drm_atomic_helper_shutdown(drm);
+ }
}
static const struct of_device_id rockchip_drm_dt_ids[] = {
@@ -536,6 +535,8 @@ static int __init rockchip_drm_init(void)
CONFIG_ROCKCHIP_DW_HDMI_QP);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver,
+ CONFIG_ROCKCHIP_DW_MIPI_DSI2);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
CONFIG_ROCKCHIP_RK3066_HDMI);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 24b4ce5ceaf1..c183e82a42a5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*
* based on exynos_drm_drv.h
@@ -90,6 +90,7 @@ 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 dw_mipi_dsi2_rockchip_driver;
extern struct platform_driver inno_hdmi_driver;
extern struct platform_driver rockchip_dp_driver;
extern struct platform_driver rockchip_lvds_driver;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index cfe8b793d344..5829ee061c61 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
@@ -52,16 +52,9 @@ rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
}
if (drm_is_afbc(mode_cmd->modifier[0])) {
- int ret, i;
-
ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
if (ret) {
- struct drm_gem_object **obj = afbc_fb->base.obj;
-
- for (i = 0; i < info->num_planes; ++i)
- drm_gem_object_put(obj[i]);
-
- kfree(afbc_fb);
+ drm_framebuffer_put(&afbc_fb->base);
return ERR_PTR(ret);
}
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
index bae4e079dfb1..5179026b12d6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 93ed841f5dce..6330b883efc3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
index 72f59ac6d258..cdeae36b91a1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 69900138295b..e3596e2b557d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
@@ -1072,7 +1072,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
}
static int vop_plane_atomic_async_check(struct drm_plane *plane,
- struct drm_atomic_state *state)
+ struct drm_atomic_state *state, bool flip)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 0cf512cc1614..f04c9731ae7b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 5880d87fe6b3..186f6452a7d3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -24,14 +24,15 @@
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_blend.h>
#include <drm/drm_crtc.h>
+#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_flip_work.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <uapi/linux/videodev2.h>
-#include <dt-bindings/soc/rockchip,vop2.h>
#include "rockchip_drm_gem.h"
#include "rockchip_drm_vop2.h"
@@ -100,154 +101,41 @@ enum vop2_afbc_format {
VOP2_AFBC_FMT_INVALID = -1,
};
-union vop2_alpha_ctrl {
- u32 val;
- struct {
- /* [0:1] */
- u32 color_mode:1;
- u32 alpha_mode:1;
- /* [2:3] */
- u32 blend_mode:2;
- u32 alpha_cal_mode:1;
- /* [5:7] */
- u32 factor_mode:3;
- /* [8:9] */
- u32 alpha_en:1;
- u32 src_dst_swap:1;
- u32 reserved:6;
- /* [16:23] */
- u32 glb_alpha:8;
- } bits;
-};
-
-struct vop2_alpha {
- union vop2_alpha_ctrl src_color_ctrl;
- union vop2_alpha_ctrl dst_color_ctrl;
- union vop2_alpha_ctrl src_alpha_ctrl;
- union vop2_alpha_ctrl dst_alpha_ctrl;
-};
-
-struct vop2_alpha_config {
- bool src_premulti_en;
- bool dst_premulti_en;
- bool src_pixel_alpha_en;
- bool dst_pixel_alpha_en;
- u16 src_glb_alpha_value;
- u16 dst_glb_alpha_value;
-};
-
-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;
+#define VOP2_MAX_DCLK_RATE 600000000
- enum drm_plane_type type;
-};
-
-struct vop2_video_port {
- struct drm_crtc crtc;
- struct vop2 *vop2;
- struct clk *dclk;
- 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;
+/*
+ * bus-format types.
+ */
+struct drm_bus_format_enum_list {
+ int type;
+ const char *name;
};
-struct vop2 {
- struct device *dev;
- struct drm_device *drm;
- struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
-
- const struct vop2_data *data;
- /*
- * Number of windows that are registered as plane, may be less than the
- * total number of hardware windows.
- */
- u32 registered_num_wins;
-
- 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;
-
- /* optional internal rgb encoder */
- struct rockchip_rgb *rgb;
-
- /* must be put at the end of the struct */
- struct vop2_win win[];
+static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = {
+ { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+ { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" },
+ { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" },
+ { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" },
+ { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" },
+ { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" },
+ { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" },
+ { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" },
+ { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" },
+ { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" },
+ { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" },
+ { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" },
+ { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" },
+ { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" },
+ { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" },
+ { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" },
+ { MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1X30" },
+ { MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" },
};
-#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)
+static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list)
static const struct regmap_config vop2_regmap_config;
-static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
-{
- return container_of(crtc, struct vop2_video_port, crtc);
-}
-
-static struct vop2_win *to_vop2_win(struct drm_plane *p)
-{
- return container_of(p, struct vop2_win, base);
-}
-
static void vop2_lock(struct vop2 *vop2)
{
mutex_lock(&vop2->vop2_lock);
@@ -258,54 +146,6 @@ static void vop2_unlock(struct vop2 *vop2)
mutex_unlock(&vop2->vop2_lock);
}
-static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
-{
- regmap_write(vop2->map, offset, v);
-}
-
-static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
-{
- regmap_write(vp->vop2->map, vp->data->offset + offset, v);
-}
-
-static u32 vop2_readl(struct vop2 *vop2, u32 offset)
-{
- u32 val;
-
- regmap_read(vop2->map, offset, &val);
-
- return val;
-}
-
-static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
-{
- regmap_field_write(win->reg[reg], v);
-}
-
-static bool vop2_cluster_window(const struct vop2_win *win)
-{
- return win->data->feature & WIN_FEATURE_CLUSTER;
-}
-
-/*
- * 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 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);
-}
-
static void vop2_win_disable(struct vop2_win *win)
{
vop2_win_write(win, VOP2_WIN_ENABLE, 0);
@@ -496,7 +336,7 @@ static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format)
{
- if (vop2->data->soc_id == 3588) {
+ if (vop2->version == VOP_VERSION_RK3588) {
if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
bus_format == MEDIA_BUS_FMT_YUV10_1X30)
return true;
@@ -549,7 +389,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
if (modifier == DRM_FORMAT_MOD_INVALID)
return false;
- if (vop2->data->soc_id == 3568 || vop2->data->soc_id == 3566) {
+ if (vop2->version == VOP_VERSION_RK3568) {
if (vop2_cluster_window(win)) {
if (modifier == DRM_FORMAT_MOD_LINEAR) {
drm_dbg_kms(vop2->drm,
@@ -560,7 +400,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
}
if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) {
- if (vop2->data->soc_id == 3588) {
+ if (vop2->version == VOP_VERSION_RK3588) {
if (!rockchip_afbc(plane, modifier)) {
drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n");
return false;
@@ -959,6 +799,7 @@ static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2)
static void vop2_enable(struct vop2 *vop2)
{
int ret;
+ u32 version;
ret = pm_runtime_resume_and_get(vop2->dev);
if (ret < 0) {
@@ -978,12 +819,27 @@ static void vop2_enable(struct vop2 *vop2)
return;
}
+ version = vop2_readl(vop2, RK3568_VERSION_INFO);
+ if (version != vop2->version) {
+ drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version);
+ return;
+ }
+
+ /*
+ * rk3566 share the same vop version with rk3568, so
+ * we need to use soc_id for identification here.
+ */
if (vop2->data->soc_id == 3566)
vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
- if (vop2->data->soc_id == 3588)
+ if (vop2->version == VOP_VERSION_RK3588)
rk3588_vop2_power_domain_enable_all(vop2);
+ if (vop2->version <= VOP_VERSION_RK3588) {
+ vop2->old_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+ vop2->old_port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
+ }
+
vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
/*
@@ -1016,6 +872,67 @@ static void vop2_disable(struct vop2 *vop2)
clk_disable_unprepare(vop2->hclk);
}
+static bool vop2_vp_dsp_lut_is_enabled(struct vop2_video_port *vp)
+{
+ u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
+
+ return dsp_ctrl & RK3568_VP_DSP_CTRL__DSP_LUT_EN;
+}
+
+static void vop2_vp_dsp_lut_disable(struct vop2_video_port *vp)
+{
+ u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
+
+ dsp_ctrl &= ~RK3568_VP_DSP_CTRL__DSP_LUT_EN;
+ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+}
+
+static bool vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port *vp)
+{
+ u32 dsp_ctrl;
+ int ret = readx_poll_timeout(vop2_vp_dsp_lut_is_enabled, vp, dsp_ctrl,
+ !dsp_ctrl, 5, 30 * 1000);
+ if (ret) {
+ drm_err(vp->vop2->drm, "display LUT RAM enable timeout!\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void vop2_vp_dsp_lut_enable(struct vop2_video_port *vp)
+{
+ u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
+
+ dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_LUT_EN;
+ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+}
+
+static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp)
+{
+ u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
+
+ dsp_ctrl |= RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN;
+ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+}
+
+static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2)
+{
+ return vop2->version != VOP_VERSION_RK3568;
+}
+
+static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp)
+{
+ const int nr_vps = vop2->data->nr_vps;
+ int gamma_en_vp_id;
+
+ for (gamma_en_vp_id = 0; gamma_en_vp_id < nr_vps; gamma_en_vp_id++)
+ if (vop2_vp_dsp_lut_is_enabled(&vop2->vps[gamma_en_vp_id]))
+ break;
+
+ return gamma_en_vp_id != nr_vps && gamma_en_vp_id != vp->id;
+}
+
static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
@@ -1051,6 +968,9 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
+ if (vp->dclk_src)
+ clk_set_parent(vp->dclk, vp->dclk_src);
+
clk_disable_unprepare(vp->dclk);
vop2->enable_count--;
@@ -1289,8 +1209,9 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
dsp_w = drm_rect_width(dest);
if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
- drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
- vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
+ drm_dbg_kms(vop2->drm,
+ "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
+ vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
dsp_w = adjusted_mode->hdisplay - dest->x1;
if (dsp_w < 4)
dsp_w = 4;
@@ -1300,8 +1221,9 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
dsp_h = drm_rect_height(dest);
if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
- drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
- vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
+ drm_dbg_kms(vop2->drm,
+ "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
+ vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
dsp_h = adjusted_mode->vdisplay - dest->y1;
if (dsp_h < 4)
dsp_h = 4;
@@ -1314,15 +1236,15 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
*/
if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
- drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
- vp->id, win->data->name, actual_w);
+ drm_dbg_kms(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
+ vp->id, win->data->name, actual_w);
actual_w -= 1;
}
}
if (afbc_en && actual_w % 4) {
- drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
- vp->id, win->data->name, actual_w);
+ drm_dbg_kms(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
+ vp->id, win->data->name, actual_w);
actual_w = ALIGN_DOWN(actual_w, 4);
}
@@ -1338,26 +1260,31 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
&fb->format->format,
afbc_en ? "AFBC" : "", &yrgb_mst);
- if (vop2->data->soc_id > 3568) {
+ if (vop2->version > VOP_VERSION_RK3568) {
vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id);
vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id);
vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id);
}
+ if (vop2->version >= VOP_VERSION_RK3576)
+ vop2_win_write(win, VOP2_WIN_VP_SEL, vp->id);
+
if (vop2_cluster_window(win))
vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en);
if (afbc_en) {
- u32 stride;
+ u32 stride, block_w;
+
+ /* the afbc superblock is 16 x 16 or 32 x 8 */
+ block_w = fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 ? 32 : 16;
- /* the afbc superblock is 16 x 16 */
afbc_format = vop2_convert_afbc_format(fb->format->format);
/* Enable color transform for YTR */
if (fb->modifier & AFBC_FORMAT_MOD_YTR)
afbc_format |= (1 << 4);
- afbc_tile_num = ALIGN(actual_w, 16) >> 4;
+ afbc_tile_num = ALIGN(actual_w, block_w) / block_w;
/*
* AFBC pic_vir_width is count by pixel, this is different
@@ -1365,8 +1292,11 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
*/
stride = (fb->pitches[0] << 3) / bpp;
if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
- drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
- vp->id, win->data->name, stride);
+ drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
+ vp->id, win->data->name, stride);
+
+ /* It's for head stride, each head size is 16 byte */
+ stride = ALIGN(stride, block_w) / block_w * 16;
uv_swap = vop2_afbc_uv_swap(fb->format->format);
/*
@@ -1393,16 +1323,25 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
* this bit is gating disable, we should write 1 to
* disable gating when enable afbc.
*/
- if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568)
+ if (vop2->version == VOP_VERSION_RK3568)
vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
else
vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
- vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
+ if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
+ vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 1);
+ else
+ vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
+
+ if (vop2->version >= VOP_VERSION_RK3576) {
+ vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET_EN, 1);
+ vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET, yrgb_mst);
+ }
+
transform_offset = vop2_afbc_transform_offset(pstate, half_block_en);
vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
- vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
+ vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, transform_offset);
vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
@@ -1413,7 +1352,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
} else {
if (vop2_cluster_window(win)) {
vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 0);
- vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, 0);
+ vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, 0);
}
vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
@@ -1432,10 +1371,8 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
rb_swap = vop2_win_rb_swap(fb->format->format);
vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
- if (!vop2_cluster_window(win)) {
- uv_swap = vop2_win_uv_swap(fb->format->format);
- vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
- }
+ uv_swap = vop2_win_uv_swap(fb->format->format);
+ vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
if (fb->format->is_yuv) {
vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
@@ -1506,6 +1443,77 @@ static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
+static void vop2_crtc_write_gamma_lut(struct vop2 *vop2, struct drm_crtc *crtc)
+{
+ const struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id];
+ struct drm_color_lut *lut = crtc->state->gamma_lut->data;
+ unsigned int i, bpc = ilog2(vp_data->gamma_lut_len);
+ u32 word;
+
+ for (i = 0; i < crtc->gamma_size; i++) {
+ word = (drm_color_lut_extract(lut[i].blue, bpc) << (2 * bpc)) |
+ (drm_color_lut_extract(lut[i].green, bpc) << bpc) |
+ drm_color_lut_extract(lut[i].red, bpc);
+
+ writel(word, vop2->lut_regs + i * 4);
+ }
+}
+
+static void vop2_crtc_atomic_set_gamma_seamless(struct vop2 *vop2,
+ struct vop2_video_port *vp,
+ struct drm_crtc *crtc)
+{
+ vop2_writel(vop2, RK3568_LUT_PORT_SEL,
+ FIELD_PREP(RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL, vp->id));
+ vop2_vp_dsp_lut_enable(vp);
+ vop2_crtc_write_gamma_lut(vop2, crtc);
+ vop2_vp_dsp_lut_update_enable(vp);
+}
+
+static void vop2_crtc_atomic_set_gamma_rk356x(struct vop2 *vop2,
+ struct vop2_video_port *vp,
+ struct drm_crtc *crtc)
+{
+ vop2_vp_dsp_lut_disable(vp);
+ vop2_cfg_done(vp);
+ if (!vop2_vp_dsp_lut_poll_disabled(vp))
+ return;
+
+ vop2_writel(vop2, RK3568_LUT_PORT_SEL, vp->id);
+ vop2_crtc_write_gamma_lut(vop2, crtc);
+ vop2_vp_dsp_lut_enable(vp);
+}
+
+static void vop2_crtc_atomic_try_set_gamma(struct vop2 *vop2,
+ struct vop2_video_port *vp,
+ struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ if (!vop2->lut_regs)
+ return;
+
+ if (!crtc_state->gamma_lut) {
+ vop2_vp_dsp_lut_disable(vp);
+ return;
+ }
+
+ if (vop2_supports_seamless_gamma_lut_update(vop2))
+ vop2_crtc_atomic_set_gamma_seamless(vop2, vp, crtc);
+ else
+ vop2_crtc_atomic_set_gamma_rk356x(vop2, vp, crtc);
+}
+
+static inline void vop2_crtc_atomic_try_set_gamma_locked(struct vop2 *vop2,
+ struct vop2_video_port *vp,
+ struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ vop2_lock(vop2);
+ vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
+ vop2_unlock(vop2);
+}
+
static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
{
struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
@@ -1538,6 +1546,7 @@ static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
static void vop2_post_config(struct drm_crtc *crtc)
{
struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ struct vop2 *vop2 = vp->vop2;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
u16 vtotal = mode->crtc_vtotal;
u16 hdisplay = mode->crtc_hdisplay;
@@ -1548,18 +1557,10 @@ static void vop2_post_config(struct drm_crtc *crtc)
u32 top_margin = 100, bottom_margin = 100;
u16 hsize = hdisplay * (left_margin + right_margin) / 200;
u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
- u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
u16 hact_end, vact_end;
u32 val;
- u32 bg_dly;
- u32 pre_scan_dly;
-
- bg_dly = vp->data->pre_scan_max_dly[3];
- vop2_writel(vp->vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
- FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
- pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
- vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+ vop2->ops->setup_bg_dly(vp);
vsize = rounddown(vsize, 2);
hsize = rounddown(hsize, 2);
@@ -1595,347 +1596,6 @@ static void vop2_post_config(struct drm_crtc *crtc)
vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
}
-static unsigned long rk3568_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
-{
- struct vop2 *vop2 = vp->vop2;
- struct drm_crtc *crtc = &vp->crtc;
- u32 die, dip;
-
- die = vop2_readl(vop2, RK3568_DSP_IF_EN);
- dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
-
- switch (id) {
- case ROCKCHIP_VOP2_EP_RGB0:
- die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_RGB |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
- if (polflags & POLFLAG_DCLK_INV)
- regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
- else
- regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
- break;
- case ROCKCHIP_VOP2_EP_HDMI0:
- die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_EDP0:
- die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_EDP |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_MIPI0:
- die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_MIPI1:
- die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_LVDS0:
- die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_LVDS1:
- die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
- die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
- FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
- dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
- break;
- default:
- drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
- return 0;
- }
-
- dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
-
- vop2_writel(vop2, RK3568_DSP_IF_EN, die);
- vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
-
- return crtc->state->adjusted_mode.crtc_clock * 1000LL;
-}
-
-/*
- * calc the dclk on rk3588
- * the available div of dclk is 1, 2, 4
- */
-static unsigned long rk3588_calc_dclk(unsigned long child_clk, unsigned long max_dclk)
-{
- if (child_clk * 4 <= max_dclk)
- return child_clk * 4;
- else if (child_clk * 2 <= max_dclk)
- return child_clk * 2;
- else if (child_clk <= max_dclk)
- return child_clk;
- else
- return 0;
-}
-
-/*
- * 4 pixclk/cycle on rk3588
- * RGB/eDP/HDMI: if_pixclk >= dclk_core
- * DP: dp_pixclk = dclk_out <= dclk_core
- * DSI: mipi_pixclk <= dclk_out <= dclk_core
- */
-static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id,
- int *dclk_core_div, int *dclk_out_div,
- int *if_pixclk_div, int *if_dclk_div)
-{
- struct vop2 *vop2 = vp->vop2;
- struct drm_crtc *crtc = &vp->crtc;
- struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
- struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
- int output_mode = vcstate->output_mode;
- unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */
- unsigned long dclk_core_rate = v_pixclk >> 2;
- unsigned long dclk_rate = v_pixclk;
- unsigned long dclk_out_rate;
- unsigned long if_pixclk_rate;
- int K = 1;
-
- if (vop2_output_if_is_hdmi(id)) {
- /*
- * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate
- * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate
- */
- if (output_mode == ROCKCHIP_OUT_MODE_YUV420) {
- dclk_rate = dclk_rate >> 1;
- K = 2;
- }
-
- if_pixclk_rate = (dclk_core_rate << 1) / K;
- /*
- * if_dclk_rate = dclk_core_rate / K;
- * *if_pixclk_div = dclk_rate / if_pixclk_rate;
- * *if_dclk_div = dclk_rate / if_dclk_rate;
- */
- *if_pixclk_div = 2;
- *if_dclk_div = 4;
- } else if (vop2_output_if_is_edp(id)) {
- /*
- * edp_pixclk = edp_dclk > dclk_core
- */
- if_pixclk_rate = v_pixclk / K;
- dclk_rate = if_pixclk_rate * K;
- /*
- * *if_pixclk_div = dclk_rate / if_pixclk_rate;
- * *if_dclk_div = *if_pixclk_div;
- */
- *if_pixclk_div = K;
- *if_dclk_div = K;
- } else if (vop2_output_if_is_dp(id)) {
- if (output_mode == ROCKCHIP_OUT_MODE_YUV420)
- dclk_out_rate = v_pixclk >> 3;
- else
- dclk_out_rate = v_pixclk >> 2;
-
- dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000);
- if (!dclk_rate) {
- drm_err(vop2->drm, "DP dclk_out_rate out of range, dclk_out_rate: %ld Hz\n",
- dclk_out_rate);
- return 0;
- }
- *dclk_out_div = dclk_rate / dclk_out_rate;
- } else if (vop2_output_if_is_mipi(id)) {
- if_pixclk_rate = dclk_core_rate / K;
- /*
- * dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4
- */
- dclk_out_rate = if_pixclk_rate;
- /*
- * dclk_rate = N * dclk_core_rate N = (1,2,4 ),
- * we get a little factor here
- */
- dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000);
- if (!dclk_rate) {
- drm_err(vop2->drm, "MIPI dclk out of range, dclk_out_rate: %ld Hz\n",
- dclk_out_rate);
- return 0;
- }
- *dclk_out_div = dclk_rate / dclk_out_rate;
- /*
- * mipi pixclk == dclk_out
- */
- *if_pixclk_div = 1;
- } else if (vop2_output_if_is_dpi(id)) {
- dclk_rate = v_pixclk;
- }
-
- *dclk_core_div = dclk_rate / dclk_core_rate;
- *if_pixclk_div = ilog2(*if_pixclk_div);
- *if_dclk_div = ilog2(*if_dclk_div);
- *dclk_core_div = ilog2(*dclk_core_div);
- *dclk_out_div = ilog2(*dclk_out_div);
-
- drm_dbg(vop2->drm, "dclk: %ld, pixclk_div: %d, dclk_div: %d\n",
- dclk_rate, *if_pixclk_div, *if_dclk_div);
-
- return dclk_rate;
-}
-
-/*
- * MIPI port mux on rk3588:
- * 0: Video Port2
- * 1: Video Port3
- * 3: Video Port 1(MIPI1 only)
- */
-static u32 rk3588_get_mipi_port_mux(int vp_id)
-{
- if (vp_id == 1)
- return 3;
- else if (vp_id == 3)
- return 1;
- else
- return 0;
-}
-
-static u32 rk3588_get_hdmi_pol(u32 flags)
-{
- u32 val;
-
- val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0;
- val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0;
-
- return val;
-}
-
-static unsigned long rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
-{
- struct vop2 *vop2 = vp->vop2;
- int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div;
- unsigned long clock;
- u32 die, dip, div, vp_clk_div, val;
-
- clock = rk3588_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div,
- &if_pixclk_div, &if_dclk_div);
- if (!clock)
- return 0;
-
- vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div);
- vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div);
-
- die = vop2_readl(vop2, RK3568_DSP_IF_EN);
- dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
- div = vop2_readl(vop2, RK3568_DSP_IF_CTRL);
-
- switch (id) {
- case ROCKCHIP_VOP2_EP_HDMI0:
- div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV;
- div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
- val = rk3588_get_hdmi_pol(polflags);
- regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1));
- regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5));
- break;
- case ROCKCHIP_VOP2_EP_HDMI1:
- div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV;
- div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div);
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
- val = rk3588_get_hdmi_pol(polflags);
- regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4));
- regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7));
- break;
- case ROCKCHIP_VOP2_EP_EDP0:
- div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV;
- div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_EDP0 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
- regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0));
- break;
- case ROCKCHIP_VOP2_EP_EDP1:
- div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV;
- div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
- div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_EDP1 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
- regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3));
- break;
- case ROCKCHIP_VOP2_EP_MIPI0:
- div &= ~RK3588_DSP_IF_MIPI0_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX;
- val = rk3588_get_mipi_port_mux(vp->id);
- die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val);
- break;
- case ROCKCHIP_VOP2_EP_MIPI1:
- div &= ~RK3588_DSP_IF_MIPI1_PCLK_DIV;
- div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div);
- die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX;
- val = rk3588_get_mipi_port_mux(vp->id);
- die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val);
- break;
- case ROCKCHIP_VOP2_EP_DP0:
- die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_DP0 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id);
- dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL;
- dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags);
- break;
- case ROCKCHIP_VOP2_EP_DP1:
- die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX;
- die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 |
- FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
- dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL;
- dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags);
- break;
- default:
- drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
- return 0;
- }
-
- dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
-
- vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div);
- vop2_writel(vop2, RK3568_DSP_IF_EN, die);
- vop2_writel(vop2, RK3568_DSP_IF_CTRL, div);
- vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
-
- return clock;
-}
-
-static unsigned long vop2_set_intf_mux(struct vop2_video_port *vp, int ep_id, u32 polflags)
-{
- struct vop2 *vop2 = vp->vop2;
-
- if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568)
- return rk3568_set_intf_mux(vp, ep_id, polflags);
- else if (vop2->data->soc_id == 3588)
- return rk3588_set_intf_mux(vp, ep_id, polflags);
- else
- return 0;
-}
-
static int us_to_vertical_line(struct drm_display_mode *mode, int us)
{
return us * mode->clock / mode->htotal / 1000;
@@ -2008,7 +1668,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
* process multi(1/2/4/8) pixels per cycle, so the dclk feed by the
* system cru may be the 1/2 or 1/4 of mode->clock.
*/
- clock = vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
+ clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
}
if (!clock) {
@@ -2073,6 +1733,44 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
+ /*
+ * Switch to HDMI PHY PLL as DCLK source for display modes up
+ * to 4K@60Hz, if available, otherwise keep using the system CRU.
+ */
+ if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) {
+ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
+ if (!vop2->pll_hdmiphy0)
+ break;
+
+ if (!vp->dclk_src)
+ vp->dclk_src = clk_get_parent(vp->dclk);
+
+ ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
+ if (ret < 0)
+ drm_warn(vop2->drm,
+ "Could not switch to HDMI0 PHY PLL: %d\n", ret);
+ break;
+ }
+
+ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
+ if (!vop2->pll_hdmiphy1)
+ break;
+
+ if (!vp->dclk_src)
+ vp->dclk_src = clk_get_parent(vp->dclk);
+
+ ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
+ if (ret < 0)
+ drm_warn(vop2->drm,
+ "Could not switch to HDMI1 PHY PLL: %d\n", ret);
+ break;
+ }
+ }
+ }
+
clk_set_rate(vp->dclk, clock);
vop2_post_config(crtc);
@@ -2081,11 +1779,40 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+ vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
+
drm_crtc_vblank_on(crtc);
vop2_unlock(vop2);
}
+static int vop2_crtc_atomic_check_gamma(struct vop2_video_port *vp,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state,
+ struct drm_crtc_state *crtc_state)
+{
+ struct vop2 *vop2 = vp->vop2;
+ unsigned int len;
+
+ if (!vp->vop2->lut_regs || !crtc_state->color_mgmt_changed ||
+ !crtc_state->gamma_lut)
+ return 0;
+
+ len = drm_color_lut_size(crtc_state->gamma_lut);
+ if (len != crtc->gamma_size) {
+ drm_dbg(vop2->drm, "Invalid LUT size; got %d, expected %d\n",
+ len, crtc->gamma_size);
+ return -EINVAL;
+ }
+
+ if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) {
+ drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
@@ -2093,6 +1820,11 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_plane *plane;
int nplanes = 0;
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ ret = vop2_crtc_atomic_check_gamma(vp, crtc, state, crtc_state);
+ if (ret)
+ return ret;
drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
nplanes++;
@@ -2103,485 +1835,272 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
-static bool is_opaque(u16 alpha)
+static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
- return (alpha >> 8) == 0xff;
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ struct vop2 *vop2 = vp->vop2;
+
+ vop2->ops->setup_overlay(vp);
}
-static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
- struct vop2_alpha *alpha)
+static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
- int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
- int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
- int src_color_mode = alpha_config->src_premulti_en ?
- ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
- int dst_color_mode = alpha_config->dst_premulti_en ?
- ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
-
- alpha->src_color_ctrl.val = 0;
- alpha->dst_color_ctrl.val = 0;
- alpha->src_alpha_ctrl.val = 0;
- alpha->dst_alpha_ctrl.val = 0;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ struct vop2 *vop2 = vp->vop2;
- if (!alpha_config->src_pixel_alpha_en)
- alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
- else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
- alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
- else
- alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+ /* In case of modeset, gamma lut update already happened in atomic enable */
+ if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed)
+ vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state);
- alpha->src_color_ctrl.bits.alpha_en = 1;
+ vop2_post_config(crtc);
- if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
- alpha->src_color_ctrl.bits.color_mode = src_color_mode;
- alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
- } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
- alpha->src_color_ctrl.bits.color_mode = src_color_mode;
- alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
- } else {
- alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
- alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
- }
- alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
- alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
- alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
-
- alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
- alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
- alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
- alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
- alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
- alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
-
- alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
- alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
- alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
- alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
-
- alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
- if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
- alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
- else
- alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
- alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
- alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
-}
+ vop2_cfg_done(vp);
-static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
-{
- struct vop2_video_port *vp;
- int used_layer = 0;
- int i;
+ spin_lock_irq(&crtc->dev->event_lock);
- for (i = 0; i < port_id; i++) {
- vp = &vop2->vps[i];
- used_layer += hweight32(vp->win_mask);
+ if (crtc->state->event) {
+ WARN_ON(drm_crtc_vblank_get(crtc));
+ vp->event = crtc->state->event;
+ crtc->state->event = NULL;
}
- return used_layer;
+ spin_unlock_irq(&crtc->dev->event_lock);
}
-static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
-{
- struct vop2_alpha_config alpha_config;
- struct vop2_alpha alpha;
- struct drm_plane_state *bottom_win_pstate;
- bool src_pixel_alpha_en = false;
- u16 src_glb_alpha_val, dst_glb_alpha_val;
- bool premulti_en = false;
- bool swap = false;
- u32 offset = 0;
-
- /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
- bottom_win_pstate = main_win->base.state;
- src_glb_alpha_val = 0;
- dst_glb_alpha_val = main_win->base.state->alpha;
-
- if (!bottom_win_pstate->fb)
- return;
-
- alpha_config.src_premulti_en = premulti_en;
- alpha_config.dst_premulti_en = false;
- alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
- alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
- alpha_config.src_glb_alpha_value = src_glb_alpha_val;
- alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
- vop2_parse_alpha(&alpha_config, &alpha);
+static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
+ .mode_fixup = vop2_crtc_mode_fixup,
+ .atomic_check = vop2_crtc_atomic_check,
+ .atomic_begin = vop2_crtc_atomic_begin,
+ .atomic_flush = vop2_crtc_atomic_flush,
+ .atomic_enable = vop2_crtc_atomic_enable,
+ .atomic_disable = vop2_crtc_atomic_disable,
+};
- alpha.src_color_ctrl.bits.src_dst_swap = swap;
+static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s)
+{
+ struct drm_connector_list_iter conn_iter;
+ struct drm_connector *connector;
- switch (main_win->data->phys_id) {
- case ROCKCHIP_VOP2_CLUSTER0:
- offset = 0x0;
- break;
- case ROCKCHIP_VOP2_CLUSTER1:
- offset = 0x10;
- break;
- case ROCKCHIP_VOP2_CLUSTER2:
- offset = 0x20;
- break;
- case ROCKCHIP_VOP2_CLUSTER3:
- offset = 0x30;
- break;
+ drm_connector_list_iter_begin(crtc->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (crtc->state->connector_mask & drm_connector_mask(connector))
+ seq_printf(s, " Connector: %s\n", connector->name);
}
-
- vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
- alpha.src_color_ctrl.val);
- vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
- alpha.dst_color_ctrl.val);
- vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
- alpha.src_alpha_ctrl.val);
- vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
- alpha.dst_alpha_ctrl.val);
+ drm_connector_list_iter_end(&conn_iter);
}
-static void vop2_setup_alpha(struct vop2_video_port *vp)
+static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane)
{
- struct vop2 *vop2 = vp->vop2;
+ struct vop2_win *win = to_vop2_win(plane);
+ struct drm_plane_state *pstate = plane->state;
+ struct drm_rect *src, *dst;
struct drm_framebuffer *fb;
- struct vop2_alpha_config alpha_config;
- struct vop2_alpha alpha;
- struct drm_plane *plane;
- int pixel_alpha_en;
- int premulti_en, gpremulti_en = 0;
- int mixer_id;
- u32 offset;
- bool bottom_layer_alpha_en = false;
- u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
-
- mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
- alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
-
- drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
- struct vop2_win *win = to_vop2_win(plane);
-
- if (plane->state->normalized_zpos == 0 &&
- !is_opaque(plane->state->alpha) &&
- !vop2_cluster_window(win)) {
- /*
- * If bottom layer have global alpha effect [except cluster layer,
- * because cluster have deal with bottom layer global alpha value
- * at cluster mix], bottom layer mix need deal with global alpha.
- */
- bottom_layer_alpha_en = true;
- dst_global_alpha = plane->state->alpha;
- }
- }
+ struct drm_gem_object *obj;
+ struct rockchip_gem_object *rk_obj;
+ bool xmirror;
+ bool ymirror;
+ bool rotate_270;
+ bool rotate_90;
+ dma_addr_t fb_addr;
+ int i;
- drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
- struct vop2_win *win = to_vop2_win(plane);
- int zpos = plane->state->normalized_zpos;
+ seq_printf(s, " %s: %s\n", win->data->name, !pstate ?
+ "DISABLED" : pstate->crtc ? "ACTIVE" : "DISABLED");
- /*
- * Need to configure alpha from second layer.
- */
- if (zpos == 0)
- continue;
-
- if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
- premulti_en = 1;
- else
- premulti_en = 0;
-
- plane = &win->base;
- fb = plane->state->fb;
-
- pixel_alpha_en = fb->format->has_alpha;
-
- alpha_config.src_premulti_en = premulti_en;
-
- if (bottom_layer_alpha_en && zpos == 1) {
- gpremulti_en = premulti_en;
- /* Cd = Cs + (1 - As) * Cd * Agd */
- alpha_config.dst_premulti_en = false;
- alpha_config.src_pixel_alpha_en = pixel_alpha_en;
- alpha_config.src_glb_alpha_value = plane->state->alpha;
- alpha_config.dst_glb_alpha_value = dst_global_alpha;
- } else if (vop2_cluster_window(win)) {
- /* Mix output data only have pixel alpha */
- alpha_config.dst_premulti_en = true;
- alpha_config.src_pixel_alpha_en = true;
- alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
- alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
- } else {
- /* Cd = Cs + (1 - As) * Cd */
- alpha_config.dst_premulti_en = true;
- alpha_config.src_pixel_alpha_en = pixel_alpha_en;
- alpha_config.src_glb_alpha_value = plane->state->alpha;
- alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
- }
+ if (!pstate || !pstate->fb)
+ return 0;
- vop2_parse_alpha(&alpha_config, &alpha);
-
- offset = (mixer_id + zpos - 1) * 0x10;
- vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
- alpha.src_color_ctrl.val);
- vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
- alpha.dst_color_ctrl.val);
- vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
- alpha.src_alpha_ctrl.val);
- vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
- alpha.dst_alpha_ctrl.val);
- }
-
- if (vp->id == 0) {
- if (bottom_layer_alpha_en) {
- /* Transfer pixel alpha to hdr mix */
- alpha_config.src_premulti_en = gpremulti_en;
- alpha_config.dst_premulti_en = true;
- alpha_config.src_pixel_alpha_en = true;
- alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
- alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
- vop2_parse_alpha(&alpha_config, &alpha);
-
- vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
- alpha.src_color_ctrl.val);
- vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
- alpha.dst_color_ctrl.val);
- vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
- alpha.src_alpha_ctrl.val);
- vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
- alpha.dst_alpha_ctrl.val);
- } else {
- vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
- }
+ fb = pstate->fb;
+ src = &pstate->src;
+ dst = &pstate->dst;
+ xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
+ ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
+ rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
+ rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
+
+ seq_printf(s, "\twin_id: %d\n", win->win_id);
+
+ seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n",
+ &fb->format->format,
+ drm_is_afbc(fb->modifier) ? "[AFBC]" : "",
+ pstate->alpha >> 8);
+ seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n",
+ xmirror, ymirror, rotate_90, rotate_270);
+ seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos);
+ seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16,
+ src->y1 >> 16, drm_rect_width(src) >> 16,
+ drm_rect_height(src) >> 16);
+ seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1,
+ drm_rect_width(dst), drm_rect_height(dst));
+
+ for (i = 0; i < fb->format->num_planes; i++) {
+ obj = fb->obj[i];
+ rk_obj = to_rockchip_obj(obj);
+ fb_addr = rk_obj->dma_addr + fb->offsets[i];
+
+ seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n",
+ i, &fb_addr, fb->pitches[i], fb->offsets[i]);
}
+
+ return 0;
}
-static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
+static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s)
{
- struct vop2 *vop2 = vp->vop2;
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ struct drm_crtc_state *cstate = crtc->state;
+ struct rockchip_crtc_state *vcstate;
+ struct drm_display_mode *mode;
struct drm_plane *plane;
- u32 layer_sel = 0;
- u32 port_sel;
- u8 layer_id;
- u8 old_layer_id;
- u8 layer_sel_id;
- unsigned int ofs;
- u32 ovl_ctrl;
- int i;
- struct vop2_video_port *vp0 = &vop2->vps[0];
- struct vop2_video_port *vp1 = &vop2->vps[1];
- struct vop2_video_port *vp2 = &vop2->vps[2];
- struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+ bool interlaced;
- ovl_ctrl = vop2_readl(vop2, RK3568_OVL_CTRL);
- ovl_ctrl |= RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD;
- if (vcstate->yuv_overlay)
- ovl_ctrl |= RK3568_OVL_CTRL__YUV_MODE(vp->id);
- else
- ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id);
-
- vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl);
+ seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ?
+ "DISABLED" : cstate->active ? "ACTIVE" : "DISABLED");
- port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
- port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
-
- if (vp0->nlayers)
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
- vp0->nlayers - 1);
- else
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
+ if (!cstate || !cstate->active)
+ return 0;
- if (vp1->nlayers)
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
- (vp0->nlayers + vp1->nlayers - 1));
- else
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+ mode = &crtc->state->adjusted_mode;
+ vcstate = to_rockchip_crtc_state(cstate);
+ interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+ vop2_dump_connector_on_crtc(crtc, s);
+ seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format,
+ drm_get_bus_format_name(vcstate->bus_format));
+ seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode);
+ seq_printf(s, " color_space[%d]\n", vcstate->color_space);
+ seq_printf(s, " Display mode: %dx%d%s%d\n",
+ mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p",
+ drm_mode_vrefresh(mode));
+ seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n",
+ mode->clock, mode->crtc_clock, mode->type, mode->flags);
+ seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal);
+ seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal);
- if (vp2->nlayers)
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
- (vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
- else
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, 8);
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ vop2_plane_state_dump(s, plane);
+ }
- layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+ return 0;
+}
- ofs = 0;
- for (i = 0; i < vp->id; i++)
- ofs += vop2->vps[i].nlayers;
+static int vop2_summary_show(struct seq_file *s, void *data)
+{
+ struct drm_info_node *node = s->private;
+ struct drm_minor *minor = node->minor;
+ struct drm_device *drm_dev = minor->dev;
+ struct drm_crtc *crtc;
- drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
- struct vop2_win *win = to_vop2_win(plane);
- struct vop2_win *old_win;
+ drm_modeset_lock_all(drm_dev);
+ drm_for_each_crtc(crtc, drm_dev) {
+ vop2_crtc_state_dump(crtc, s);
+ }
+ drm_modeset_unlock_all(drm_dev);
- layer_id = (u8)(plane->state->normalized_zpos + ofs);
+ return 0;
+}
- /*
- * Find the layer this win bind in old state.
- */
- for (old_layer_id = 0; old_layer_id < vop2->data->win_size; old_layer_id++) {
- layer_sel_id = (layer_sel >> (4 * old_layer_id)) & 0xf;
- if (layer_sel_id == win->data->layer_sel_id)
- break;
- }
+static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s,
+ const struct vop2_regs_dump *dump, bool active_only)
+{
+ resource_size_t start;
+ u32 val;
+ int i;
- /*
- * Find the win bind to this layer in old state
- */
- for (i = 0; i < vop2->data->win_size; i++) {
- old_win = &vop2->win[i];
- layer_sel_id = (layer_sel >> (4 * layer_id)) & 0xf;
- if (layer_sel_id == old_win->data->layer_sel_id)
- break;
- }
+ if (dump->en_mask && active_only) {
+ val = vop2_readl(vop2, dump->base + dump->en_reg);
+ if ((val & dump->en_mask) != dump->en_val)
+ return;
+ }
- switch (win->data->phys_id) {
- case ROCKCHIP_VOP2_CLUSTER0:
- port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
- break;
- case ROCKCHIP_VOP2_CLUSTER1:
- port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
- break;
- case ROCKCHIP_VOP2_CLUSTER2:
- port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER2;
- port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER2, vp->id);
- break;
- case ROCKCHIP_VOP2_CLUSTER3:
- port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER3;
- port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER3, vp->id);
- break;
- case ROCKCHIP_VOP2_ESMART0:
- port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
- break;
- case ROCKCHIP_VOP2_ESMART1:
- port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
- break;
- case ROCKCHIP_VOP2_ESMART2:
- port_sel &= ~RK3588_OVL_PORT_SEL__ESMART2;
- port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART2, vp->id);
- break;
- case ROCKCHIP_VOP2_ESMART3:
- port_sel &= ~RK3588_OVL_PORT_SEL__ESMART3;
- port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART3, vp->id);
- break;
- case ROCKCHIP_VOP2_SMART0:
- port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
- break;
- case ROCKCHIP_VOP2_SMART1:
- port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
- port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
- break;
- }
+ seq_printf(s, "\n%s:\n", dump->name);
- layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(layer_id, 0x7);
- layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id);
- /*
- * When we bind a window from layerM to layerN, we also need to move the old
- * window on layerN to layerM to avoid one window selected by two or more layers.
- */
- layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, 0x7);
- layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, old_win->data->layer_sel_id);
+ start = vop2->res->start + dump->base;
+ for (i = 0; i < dump->size >> 2; i += 4) {
+ seq_printf(s, "%08x: %08x %08x %08x %08x\n", (u32)start + i * 4,
+ vop2_readl(vop2, dump->base + (4 * i)),
+ vop2_readl(vop2, dump->base + (4 * (i + 1))),
+ vop2_readl(vop2, dump->base + (4 * (i + 2))),
+ vop2_readl(vop2, dump->base + (4 * (i + 3))));
}
-
- vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
- vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
}
-static void vop2_setup_dly_for_windows(struct vop2 *vop2)
+static void __vop2_regs_dump(struct seq_file *s, bool active_only)
{
- struct vop2_win *win;
- int i = 0;
- u32 cdly = 0, sdly = 0;
+ struct drm_info_node *node = s->private;
+ struct vop2 *vop2 = node->info_ent->data;
+ struct drm_minor *minor = node->minor;
+ struct drm_device *drm_dev = minor->dev;
+ const struct vop2_regs_dump *dump;
+ unsigned int i;
- for (i = 0; i < vop2->data->win_size; i++) {
- u32 dly;
+ drm_modeset_lock_all(drm_dev);
- win = &vop2->win[i];
- dly = win->delay;
+ regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
- switch (win->data->phys_id) {
- case ROCKCHIP_VOP2_CLUSTER0:
- cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
- cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
- break;
- case ROCKCHIP_VOP2_CLUSTER1:
- cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
- cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
- break;
- case ROCKCHIP_VOP2_ESMART0:
- sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
- break;
- case ROCKCHIP_VOP2_ESMART1:
- sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
- break;
- case ROCKCHIP_VOP2_SMART0:
- case ROCKCHIP_VOP2_ESMART2:
- sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
- break;
- case ROCKCHIP_VOP2_SMART1:
- case ROCKCHIP_VOP2_ESMART3:
- sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
- break;
+ if (vop2->enable_count) {
+ for (i = 0; i < vop2->data->regs_dump_size; i++) {
+ dump = &vop2->data->regs_dump[i];
+ vop2_regs_print(vop2, s, dump, active_only);
}
+ } else {
+ seq_puts(s, "VOP disabled\n");
}
-
- vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
- vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
+ drm_modeset_unlock_all(drm_dev);
}
-static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
+static int vop2_regs_show(struct seq_file *s, void *arg)
{
- struct vop2_video_port *vp = to_vop2_video_port(crtc);
- struct vop2 *vop2 = vp->vop2;
- struct drm_plane *plane;
+ __vop2_regs_dump(s, false);
- vp->win_mask = 0;
+ return 0;
+}
- drm_atomic_crtc_for_each_plane(plane, crtc) {
- struct vop2_win *win = to_vop2_win(plane);
+static int vop2_active_regs_show(struct seq_file *s, void *data)
+{
+ __vop2_regs_dump(s, true);
- win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+ return 0;
+}
- vp->win_mask |= BIT(win->data->phys_id);
+static struct drm_info_list vop2_debugfs_list[] = {
+ { "summary", vop2_summary_show, 0, NULL },
+ { "active_regs", vop2_active_regs_show, 0, NULL },
+ { "regs", vop2_regs_show, 0, NULL },
+};
- if (vop2_cluster_window(win))
- vop2_setup_cluster_alpha(vop2, win);
- }
+static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor)
+{
+ struct dentry *root;
+ unsigned int i;
- if (!vp->win_mask)
- return;
+ root = debugfs_create_dir("vop2", minor->debugfs_root);
+ if (!IS_ERR(root)) {
+ for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++)
+ vop2_debugfs_list[i].data = vop2;
- vop2_setup_layer_mixer(vp);
- vop2_setup_alpha(vp);
- vop2_setup_dly_for_windows(vop2);
+ drm_debugfs_create_files(vop2_debugfs_list,
+ ARRAY_SIZE(vop2_debugfs_list),
+ root, minor);
+ }
}
-static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
+static int vop2_crtc_late_register(struct drm_crtc *crtc)
{
struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ struct vop2 *vop2 = vp->vop2;
- vop2_post_config(crtc);
-
- vop2_cfg_done(vp);
-
- spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_index(crtc) == 0)
+ vop2_debugfs_init(vop2, crtc->dev->primary);
- if (crtc->state->event) {
- WARN_ON(drm_crtc_vblank_get(crtc));
- vp->event = crtc->state->event;
- crtc->state->event = NULL;
- }
-
- spin_unlock_irq(&crtc->dev->event_lock);
+ return 0;
}
-static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
- .mode_fixup = vop2_crtc_mode_fixup,
- .atomic_check = vop2_crtc_atomic_check,
- .atomic_begin = vop2_crtc_atomic_begin,
- .atomic_flush = vop2_crtc_atomic_flush,
- .atomic_enable = vop2_crtc_atomic_enable,
- .atomic_disable = vop2_crtc_atomic_disable,
-};
-
static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct rockchip_crtc_state *vcstate;
@@ -2631,8 +2150,55 @@ static const struct drm_crtc_funcs vop2_crtc_funcs = {
.atomic_destroy_state = vop2_crtc_destroy_state,
.enable_vblank = vop2_crtc_enable_vblank,
.disable_vblank = vop2_crtc_disable_vblank,
+ .late_register = vop2_crtc_late_register,
};
+static irqreturn_t rk3576_vp_isr(int irq, void *data)
+{
+ struct vop2_video_port *vp = data;
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ uint32_t irqs;
+ int ret = IRQ_NONE;
+
+ if (!pm_runtime_get_if_in_use(vop2->dev))
+ return IRQ_NONE;
+
+ irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
+ vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
+
+ if (irqs & VP_INT_DSP_HOLD_VALID) {
+ complete(&vp->dsp_hold_completion);
+ ret = IRQ_HANDLED;
+ }
+
+ if (irqs & VP_INT_FS_FIELD) {
+ drm_crtc_handle_vblank(crtc);
+ spin_lock(&crtc->dev->event_lock);
+ if (vp->event) {
+ u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+ if (!(val & BIT(vp->id))) {
+ drm_crtc_send_vblank_event(crtc, vp->event);
+ vp->event = NULL;
+ drm_crtc_vblank_put(crtc);
+ }
+ }
+ spin_unlock(&crtc->dev->event_lock);
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (irqs & VP_INT_POST_BUF_EMPTY) {
+ drm_err_ratelimited(vop2->drm, "POST_BUF_EMPTY irq err at vp%d\n", vp->id);
+ ret = IRQ_HANDLED;
+ }
+
+ pm_runtime_put(vop2->dev);
+
+ return ret;
+}
+
static irqreturn_t vop2_isr(int irq, void *data)
{
struct vop2 *vop2 = data;
@@ -2648,41 +2214,43 @@ static irqreturn_t vop2_isr(int irq, void *data)
if (!pm_runtime_get_if_in_use(vop2->dev))
return IRQ_NONE;
- for (i = 0; i < vop2_data->nr_vps; i++) {
- struct vop2_video_port *vp = &vop2->vps[i];
- struct drm_crtc *crtc = &vp->crtc;
- u32 irqs;
-
- irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
- vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
+ if (vop2->version < VOP_VERSION_RK3576) {
+ for (i = 0; i < vop2_data->nr_vps; i++) {
+ struct vop2_video_port *vp = &vop2->vps[i];
+ struct drm_crtc *crtc = &vp->crtc;
+ u32 irqs;
- if (irqs & VP_INT_DSP_HOLD_VALID) {
- complete(&vp->dsp_hold_completion);
- ret = IRQ_HANDLED;
- }
+ irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
+ vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
- if (irqs & VP_INT_FS_FIELD) {
- drm_crtc_handle_vblank(crtc);
- spin_lock(&crtc->dev->event_lock);
- if (vp->event) {
- u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+ if (irqs & VP_INT_DSP_HOLD_VALID) {
+ complete(&vp->dsp_hold_completion);
+ ret = IRQ_HANDLED;
+ }
- if (!(val & BIT(vp->id))) {
- drm_crtc_send_vblank_event(crtc, vp->event);
- vp->event = NULL;
- drm_crtc_vblank_put(crtc);
+ if (irqs & VP_INT_FS_FIELD) {
+ drm_crtc_handle_vblank(crtc);
+ spin_lock(&crtc->dev->event_lock);
+ if (vp->event) {
+ u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+ if (!(val & BIT(vp->id))) {
+ drm_crtc_send_vblank_event(crtc, vp->event);
+ vp->event = NULL;
+ drm_crtc_vblank_put(crtc);
+ }
}
- }
- spin_unlock(&crtc->dev->event_lock);
+ spin_unlock(&crtc->dev->event_lock);
- ret = IRQ_HANDLED;
- }
+ ret = IRQ_HANDLED;
+ }
- if (irqs & VP_INT_POST_BUF_EMPTY) {
- drm_err_ratelimited(vop2->drm,
- "POST_BUF_EMPTY irq err at vp%d\n",
- vp->id);
- ret = IRQ_HANDLED;
+ if (irqs & VP_INT_POST_BUF_EMPTY) {
+ drm_err_ratelimited(vop2->drm,
+ "POST_BUF_EMPTY irq err at vp%d\n",
+ vp->id);
+ ret = IRQ_HANDLED;
+ }
}
}
@@ -2736,22 +2304,29 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
return 0;
}
-static struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2)
+/*
+ * On RK3566 these windows don't have an independent
+ * framebuffer. They can only share/mirror the framebuffer
+ * with smart0, esmart0 and cluster0 respectively.
+ * And RK3566 share the same vop version with Rk3568, so we
+ * need to use soc_id for identification here.
+ */
+static bool vop2_is_mirror_win(struct vop2_win *win)
{
- int i;
-
- for (i = 0; i < vop2->data->nr_vps; i++) {
- struct vop2_video_port *vp = &vop2->vps[i];
-
- if (!vp->crtc.port)
- continue;
- if (vp->primary_plane)
- continue;
+ struct vop2 *vop2 = win->vop2;
- return vp;
+ if (vop2->data->soc_id == 3566) {
+ switch (win->data->phys_id) {
+ case ROCKCHIP_VOP2_SMART1:
+ case ROCKCHIP_VOP2_ESMART1:
+ case ROCKCHIP_VOP2_CLUSTER1:
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return false;
}
-
- return NULL;
}
static int vop2_create_crtcs(struct vop2 *vop2)
@@ -2762,7 +2337,9 @@ static int vop2_create_crtcs(struct vop2 *vop2)
struct drm_plane *plane;
struct device_node *port;
struct vop2_video_port *vp;
- int i, nvp, nvps = 0;
+ struct vop2_win *win;
+ u32 possible_crtcs;
+ int i, j, nvp, nvps = 0;
int ret;
for (i = 0; i < vop2_data->nr_vps; i++) {
@@ -2778,10 +2355,9 @@ static int vop2_create_crtcs(struct vop2 *vop2)
snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
vp->dclk = devm_clk_get(vop2->dev, dclk_name);
- if (IS_ERR(vp->dclk)) {
- drm_err(vop2->drm, "failed to get %s\n", dclk_name);
- return PTR_ERR(vp->dclk);
- }
+ if (IS_ERR(vp->dclk))
+ return dev_err_probe(drm->dev, PTR_ERR(vp->dclk),
+ "failed to get %s\n", dclk_name);
np = of_graph_get_remote_node(dev->of_node, i, -1);
if (!np) {
@@ -2791,55 +2367,83 @@ static int vop2_create_crtcs(struct vop2 *vop2)
of_node_put(np);
port = of_graph_get_port_by_id(dev->of_node, i);
- if (!port) {
- drm_err(vop2->drm, "no port node found for video_port%d\n", i);
- return -ENOENT;
- }
-
+ if (!port)
+ return dev_err_probe(drm->dev, -ENOENT,
+ "no port node found for video_port%d\n", i);
vp->crtc.port = port;
nvps++;
}
nvp = 0;
- for (i = 0; i < vop2->registered_num_wins; i++) {
- struct vop2_win *win = &vop2->win[i];
- u32 possible_crtcs = 0;
-
- if (vop2->data->soc_id == 3566) {
- /*
- * On RK3566 these windows don't have an independent
- * framebuffer. They share the framebuffer with smart0,
- * esmart0 and cluster0 respectively.
- */
- switch (win->data->phys_id) {
- case ROCKCHIP_VOP2_SMART1:
- case ROCKCHIP_VOP2_ESMART1:
- case ROCKCHIP_VOP2_CLUSTER1:
+ /* Register a primary plane for every crtc */
+ for (i = 0; i < vop2_data->nr_vps; i++) {
+ vp = &vop2->vps[i];
+
+ if (!vp->crtc.port)
+ continue;
+
+ for (j = 0; j < vop2->registered_num_wins; j++) {
+ win = &vop2->win[j];
+
+ /* Aready registered as primary plane */
+ if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ /* If this win can not attached to this VP */
+ if (!(win->data->possible_vp_mask & BIT(vp->id)))
+ continue;
+
+ if (vop2_is_mirror_win(win))
continue;
- }
- }
- if (win->type == DRM_PLANE_TYPE_PRIMARY) {
- vp = find_vp_without_primary(vop2);
- if (vp) {
+ if (win->type == DRM_PLANE_TYPE_PRIMARY) {
possible_crtcs = BIT(nvp);
vp->primary_plane = win;
+ ret = vop2_plane_init(vop2, win, possible_crtcs);
+ if (ret)
+ return dev_err_probe(drm->dev, ret,
+ "failed to init primary plane %s\n",
+ win->data->name);
nvp++;
- } else {
- /* change the unused primary window to overlay window */
- win->type = DRM_PLANE_TYPE_OVERLAY;
+ break;
}
}
- if (win->type == DRM_PLANE_TYPE_OVERLAY)
- possible_crtcs = (1 << nvps) - 1;
+ if (!vp->primary_plane)
+ return dev_err_probe(drm->dev, -ENOENT,
+ "no primary plane for vp %d\n", i);
+ }
- ret = vop2_plane_init(vop2, win, possible_crtcs);
- if (ret) {
- drm_err(vop2->drm, "failed to init plane %s: %d\n",
- win->data->name, ret);
- return ret;
+ /* Register all unused window as overlay plane */
+ for (i = 0; i < vop2->registered_num_wins; i++) {
+ win = &vop2->win[i];
+
+ /* Aready registered as primary plane */
+ if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ if (vop2_is_mirror_win(win))
+ continue;
+
+ win->type = DRM_PLANE_TYPE_OVERLAY;
+
+ possible_crtcs = 0;
+ nvp = 0;
+ for (j = 0; j < vop2_data->nr_vps; j++) {
+ vp = &vop2->vps[j];
+
+ if (!vp->crtc.port)
+ continue;
+
+ if (win->data->possible_vp_mask & BIT(vp->id))
+ possible_crtcs |= BIT(nvp);
+ nvp++;
}
+
+ ret = vop2_plane_init(vop2, win, possible_crtcs);
+ if (ret)
+ return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n",
+ win->data->name);
}
for (i = 0; i < vop2_data->nr_vps; i++) {
@@ -2853,13 +2457,17 @@ static int vop2_create_crtcs(struct vop2 *vop2)
ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
&vop2_crtc_funcs,
"video_port%d", vp->id);
- if (ret) {
- drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(drm->dev, ret,
+ "crtc init for video_port%d failed\n", i);
drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
+ if (vop2->lut_regs) {
+ const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+ drm_mode_crtc_set_gamma_size(&vp->crtc, vp_data->gamma_lut_len);
+ drm_crtc_enable_color_mgmt(&vp->crtc, 0, false, vp_data->gamma_lut_len);
+ }
init_completion(&vp->dsp_hold_completion);
}
@@ -2918,184 +2526,26 @@ static int vop2_find_rgb_encoder(struct vop2 *vop2)
return -ENOENT;
}
-static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
- [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
- [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
- [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
- [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
- [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
- [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
- [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
- [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
- [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
- [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
- [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
- [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
- [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
- [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
- [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
- [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 3),
- [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 8),
- /* RK3588 only, reserved bit on rk3568*/
- [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13),
-
- /* Scale */
- [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
- [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
- [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
- [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
- [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
- [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
- [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
-
- /* cluster regs */
- [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
- [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
- [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
-
- /* afbc regs */
- [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
- [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
- [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
- [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
- [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
- [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
- [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
- [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
- [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
- [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
- [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
- [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
- [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
- [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
- [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
- [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
- [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
- [VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
- [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
- [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
- [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
- [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
- [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
- [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
-};
-
-static int vop2_cluster_init(struct vop2_win *win)
+static int vop2_regmap_init(struct vop2_win *win, const struct reg_field *regs,
+ int nr_regs)
{
struct vop2 *vop2 = win->vop2;
- struct reg_field *cluster_regs;
- int ret, i;
-
- cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
- GFP_KERNEL);
- if (!cluster_regs)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
- if (cluster_regs[i].reg != 0xffffffff)
- cluster_regs[i].reg += win->offset;
-
- ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
- cluster_regs,
- ARRAY_SIZE(vop2_cluster_regs));
-
- kfree(cluster_regs);
-
- return ret;
-};
-
-static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
- [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
- [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
- [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
- [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
- [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
- [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
- [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
- [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
- [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
- [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
- [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
- [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
- [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
- [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
- [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
- [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
- [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
- [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
- [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
- [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8),
- [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16),
- /* RK3588 only, reserved register on rk3568 */
- [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1),
-
- /* Scale */
- [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
- [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
- [VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
- [VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
- [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
- [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
- [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
- [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
- [VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
- [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
- [VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
- [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
- [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
- [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
- [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
- [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
- [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
- [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
- [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
- [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
- [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
-};
-
-static int vop2_esmart_init(struct vop2_win *win)
-{
- struct vop2 *vop2 = win->vop2;
- struct reg_field *esmart_regs;
- int ret, i;
-
- esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
- GFP_KERNEL);
- if (!esmart_regs)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
- if (esmart_regs[i].reg != 0xffffffff)
- esmart_regs[i].reg += win->offset;
+ int i;
- ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
- esmart_regs,
- ARRAY_SIZE(vop2_esmart_regs));
+ for (i = 0; i < nr_regs; i++) {
+ const struct reg_field field = {
+ .reg = (regs[i].reg != 0xffffffff) ?
+ regs[i].reg + win->offset : regs[i].reg,
+ .lsb = regs[i].lsb,
+ .msb = regs[i].msb
+ };
- kfree(esmart_regs);
+ win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field);
+ if (IS_ERR(win->reg[i]))
+ return PTR_ERR(win->reg[i]);
+ }
- return ret;
+ return 0;
};
static int vop2_win_init(struct vop2 *vop2)
@@ -3114,9 +2564,11 @@ static int vop2_win_init(struct vop2 *vop2)
win->win_id = i;
win->vop2 = vop2;
if (vop2_cluster_window(win))
- ret = vop2_cluster_init(win);
+ ret = vop2_regmap_init(win, vop2->data->cluster_reg,
+ vop2->data->nr_cluster_regs);
else
- ret = vop2_esmart_init(win);
+ ret = vop2_regmap_init(win, vop2->data->smart_reg,
+ vop2->data->nr_smart_regs);
if (ret)
return ret;
}
@@ -3173,16 +2625,18 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
vop2->dev = dev;
vop2->data = vop2_data;
+ vop2->ops = vop2_data->ops;
+ vop2->version = vop2_data->version;
vop2->drm = drm;
dev_set_drvdata(dev, vop2);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop");
- if (!res) {
- drm_err(vop2->drm, "failed to get vop2 register byname\n");
- return -EINVAL;
- }
+ if (!res)
+ return dev_err_probe(drm->dev, -EINVAL,
+ "failed to get vop2 register byname\n");
+ vop2->res = res;
vop2->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(vop2->regs))
return PTR_ERR(vop2->regs);
@@ -3205,52 +2659,62 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_GRF) {
vop2->sys_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
if (IS_ERR(vop2->sys_grf))
- return dev_err_probe(dev, PTR_ERR(vop2->sys_grf), "cannot get sys_grf");
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_grf),
+ "cannot get sys_grf\n");
}
if (vop2_data->feature & VOP2_FEATURE_HAS_VOP_GRF) {
vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf");
if (IS_ERR(vop2->vop_grf))
- return dev_err_probe(dev, PTR_ERR(vop2->vop_grf), "cannot get vop_grf");
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->vop_grf),
+ "cannot get vop_grf\n");
}
if (vop2_data->feature & VOP2_FEATURE_HAS_VO1_GRF) {
vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf");
if (IS_ERR(vop2->vo1_grf))
- return dev_err_probe(dev, PTR_ERR(vop2->vo1_grf), "cannot get vo1_grf");
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->vo1_grf),
+ "cannot get vo1_grf\n");
}
if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_PMU) {
vop2->sys_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pmu");
if (IS_ERR(vop2->sys_pmu))
- return dev_err_probe(dev, PTR_ERR(vop2->sys_pmu), "cannot get sys_pmu");
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_pmu),
+ "cannot get sys_pmu\n");
}
vop2->hclk = devm_clk_get(vop2->dev, "hclk");
- if (IS_ERR(vop2->hclk)) {
- drm_err(vop2->drm, "failed to get hclk source\n");
- return PTR_ERR(vop2->hclk);
- }
+ if (IS_ERR(vop2->hclk))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->hclk),
+ "failed to get hclk source\n");
vop2->aclk = devm_clk_get(vop2->dev, "aclk");
- if (IS_ERR(vop2->aclk)) {
- drm_err(vop2->drm, "failed to get aclk source\n");
- return PTR_ERR(vop2->aclk);
- }
+ if (IS_ERR(vop2->aclk))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->aclk),
+ "failed to get aclk source\n");
vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk_vop");
- if (IS_ERR(vop2->pclk)) {
- drm_err(vop2->drm, "failed to get pclk source\n");
- return PTR_ERR(vop2->pclk);
- }
+ if (IS_ERR(vop2->pclk))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->pclk),
+ "failed to get pclk source\n");
+
+ vop2->pll_hdmiphy0 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy0");
+ if (IS_ERR(vop2->pll_hdmiphy0))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy0),
+ "failed to get pll_hdmiphy0\n");
+
+ vop2->pll_hdmiphy1 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy1");
+ if (IS_ERR(vop2->pll_hdmiphy1))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1),
+ "failed to get pll_hdmiphy1\n");
vop2->irq = platform_get_irq(pdev, 0);
- if (vop2->irq < 0) {
- drm_err(vop2->drm, "cannot find irq for vop2\n");
- return vop2->irq;
- }
+ if (vop2->irq < 0)
+ return dev_err_probe(drm->dev, vop2->irq, "cannot find irq for vop2\n");
mutex_init(&vop2->vop2_lock);
+ mutex_init(&vop2->ovl_lock);
ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
if (ret)
@@ -3260,6 +2724,30 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
+ if (vop2->version >= VOP_VERSION_RK3576) {
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, drm) {
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ int vp_irq;
+ const char *irq_name = devm_kasprintf(dev, GFP_KERNEL, "vp%d", vp->id);
+
+ if (!irq_name)
+ return -ENOMEM;
+
+ vp_irq = platform_get_irq_byname(pdev, irq_name);
+ if (vp_irq < 0)
+ return dev_err_probe(drm->dev, vp_irq,
+ "cannot find irq for vop2 vp%d\n", vp->id);
+
+ ret = devm_request_irq(dev, vp_irq, rk3576_vp_isr, IRQF_SHARED, irq_name,
+ vp);
+ if (ret)
+ dev_err_probe(drm->dev, ret,
+ "request irq for vop2 vp%d failed\n", vp->id);
+ }
+ }
+
ret = vop2_find_rgb_encoder(vop2);
if (ret >= 0) {
vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index 130aaa40316d..fa5c56f16047 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
@@ -9,9 +9,19 @@
#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)
@@ -34,6 +44,13 @@ enum win_dly_mode {
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,
@@ -58,6 +75,23 @@ enum vop2_scale_down_mode {
#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,
@@ -113,24 +147,40 @@ enum vop2_win_regs {
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_TRANSFORM_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;
@@ -139,9 +189,9 @@ struct vop2_win_data {
const unsigned int supported_rotations;
/**
- * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
+ * @layer_sel_id: defined by register OVERLAY_LAYER_SEL or PORTn_LAYER_SEL
*/
- unsigned int layer_sel_id;
+ unsigned int layer_sel_id[ROCKCHIP_MAX_CRTC];
uint64_t feature;
uint8_t axi_bus_id;
@@ -153,6 +203,23 @@ struct vop2_win_data {
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;
@@ -161,20 +228,129 @@ struct vop2_video_port_data {
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)
@@ -229,10 +405,13 @@ enum dst_factor_mode {
#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
@@ -252,6 +431,55 @@ enum dst_factor_mode {
#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
@@ -324,7 +552,7 @@ enum dst_factor_mode {
#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_AFBCD_TRANSFORM_OFFSET 0x3C
+#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
@@ -334,7 +562,11 @@ enum dst_factor_mode {
#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
@@ -385,6 +617,9 @@ enum dst_factor_mode {
#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
@@ -404,6 +639,7 @@ enum dst_factor_mode {
#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)
@@ -418,6 +654,8 @@ enum dst_factor_mode {
#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)
@@ -470,6 +708,8 @@ enum dst_factor_mode {
#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)
@@ -483,6 +723,7 @@ enum dst_factor_mode {
#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)
@@ -499,6 +740,7 @@ enum dst_factor_mode {
#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)
@@ -528,6 +770,17 @@ enum dst_factor_mode {
#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,
@@ -544,4 +797,71 @@ enum vop2_layer_phy_id {
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 */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index aba733736ff7..a673779de3d2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:
* Mark Yao <mark.yao@rock-chips.com>
* Sandy Huang <hjc@rock-chips.com>
@@ -448,17 +448,14 @@ struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = {
static int rk3288_lvds_probe(struct platform_device *pdev,
struct rockchip_lvds *lvds)
{
- int ret;
-
lvds->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lvds->regs))
return PTR_ERR(lvds->regs);
- lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds");
- if (IS_ERR(lvds->pclk)) {
- DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n");
- return PTR_ERR(lvds->pclk);
- }
+ lvds->pclk = devm_clk_get_prepared(lvds->dev, "pclk_lvds");
+ if (IS_ERR(lvds->pclk))
+ return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk),
+ "could not get or prepare pclk_lvds\n");
lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
GFP_KERNEL);
@@ -467,25 +464,19 @@ static int rk3288_lvds_probe(struct platform_device *pdev,
lvds->pins->p = devm_pinctrl_get(lvds->dev);
if (IS_ERR(lvds->pins->p)) {
- DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n");
+ dev_warn(lvds->dev, "no pinctrl handle\n");
devm_kfree(lvds->dev, lvds->pins);
lvds->pins = NULL;
} else {
lvds->pins->default_state =
pinctrl_lookup_state(lvds->pins->p, "lcdc");
if (IS_ERR(lvds->pins->default_state)) {
- DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n");
+ dev_warn(lvds->dev, "no default pinctrl state\n");
devm_kfree(lvds->dev, lvds->pins);
lvds->pins = NULL;
}
}
- ret = clk_prepare(lvds->pclk);
- if (ret < 0) {
- DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n");
- return ret;
- }
-
return 0;
}
@@ -555,11 +546,10 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
lvds->drm_dev = drm_dev;
port = of_graph_get_port_by_id(dev->of_node, 1);
- if (!port) {
- DRM_DEV_ERROR(dev,
- "can't found port point, please init lvds panel port!\n");
- return -EINVAL;
- }
+ if (!port)
+ return dev_err_probe(dev, -EINVAL,
+ "can't found port point, please init lvds panel port!\n");
+
for_each_child_of_node(port, endpoint) {
child_count++;
of_property_read_u32(endpoint, "reg", &endpoint_id);
@@ -571,8 +561,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
}
}
if (!child_count) {
- DRM_DEV_ERROR(dev, "lvds port does not have any children\n");
- ret = -EINVAL;
+ ret = dev_err_probe(dev, -EINVAL, "lvds port does not have any children\n");
goto err_put_port;
} else if (ret) {
dev_err_probe(dev, ret, "failed to find panel and bridge node\n");
@@ -589,8 +578,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
lvds->output = rockchip_lvds_name_to_output(name);
if (lvds->output < 0) {
- DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
- ret = lvds->output;
+ ret = dev_err_probe(dev, lvds->output, "invalid output type [%s]\n", name);
goto err_put_remote;
}
@@ -601,8 +589,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
lvds->format = rockchip_lvds_name_to_format(name);
if (lvds->format < 0) {
- DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
- ret = lvds->format;
+ ret = dev_err_probe(dev, lvds->format,
+ "invalid data-mapping format [%s]\n", name);
goto err_put_remote;
}
@@ -612,8 +600,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
if (ret < 0) {
- DRM_DEV_ERROR(drm_dev->dev,
- "failed to initialize encoder: %d\n", ret);
+ drm_err(drm_dev,
+ "failed to initialize encoder: %d\n", ret);
goto err_put_remote;
}
@@ -626,8 +614,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
&rockchip_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
if (ret < 0) {
- DRM_DEV_ERROR(drm_dev->dev,
- "failed to initialize connector: %d\n", ret);
+ drm_err(drm_dev,
+ "failed to initialize connector: %d\n", ret);
goto err_free_encoder;
}
@@ -641,9 +629,9 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
connector = drm_bridge_connector_init(lvds->drm_dev, encoder);
if (IS_ERR(connector)) {
- DRM_DEV_ERROR(drm_dev->dev,
- "failed to initialize bridge connector: %pe\n",
- connector);
+ drm_err(drm_dev,
+ "failed to initialize bridge connector: %pe\n",
+ connector);
ret = PTR_ERR(connector);
goto err_free_encoder;
}
@@ -651,8 +639,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
ret = drm_connector_attach_encoder(connector, encoder);
if (ret < 0) {
- DRM_DEV_ERROR(drm_dev->dev,
- "failed to attach encoder: %d\n", ret);
+ drm_err(drm_dev, "failed to attach encoder: %d\n", ret);
goto err_free_connector;
}
@@ -714,34 +701,25 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
- if (IS_ERR(lvds->grf)) {
- DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
- return PTR_ERR(lvds->grf);
- }
+ if (IS_ERR(lvds->grf))
+ return dev_err_probe(dev, PTR_ERR(lvds->grf), "missing rockchip,grf property\n");
ret = lvds->soc_data->probe(pdev, lvds);
- if (ret) {
- DRM_DEV_ERROR(dev, "Platform initialization failed\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Platform initialization failed\n");
dev_set_drvdata(dev, lvds);
ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
- if (ret < 0) {
- DRM_DEV_ERROR(dev, "failed to add component\n");
- clk_unprepare(lvds->pclk);
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add component\n");
- return ret;
+ return 0;
}
static void rockchip_lvds_remove(struct platform_device *pdev)
{
- struct rockchip_lvds *lvds = platform_get_drvdata(pdev);
-
component_del(&pdev->dev, &rockchip_lvds_component_ops);
- clk_unprepare(lvds->pclk);
}
struct platform_driver rockchip_lvds_driver = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
index 4ce967d23813..ca83d7b6bea7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.h
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:
* Sandy Huang <hjc@rock-chips.com>
* Mark Yao <mark.yao@rock-chips.com>
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index dbfbde24698e..811020665120 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:
* Sandy Huang <hjc@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.h b/drivers/gpu/drm/rockchip/rockchip_rgb.h
index 1bd4e20e91eb..116f958b894d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.h
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:
* Sandy Huang <hjc@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
index 6a6c15f2c9cc..45c5e3987813 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -1,20 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Andy Yan <andy.yan@rock-chips.com>
*/
+#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/component.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/of.h>
+#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include "rockchip_drm_vop2.h"
+union vop2_alpha_ctrl {
+ u32 val;
+ struct {
+ /* [0:1] */
+ u32 color_mode:1;
+ u32 alpha_mode:1;
+ /* [2:3] */
+ u32 blend_mode:2;
+ u32 alpha_cal_mode:1;
+ /* [5:7] */
+ u32 factor_mode:3;
+ /* [8:9] */
+ u32 alpha_en:1;
+ u32 src_dst_swap:1;
+ u32 reserved:6;
+ /* [16:23] */
+ u32 glb_alpha:8;
+ } bits;
+};
+
+struct vop2_alpha {
+ union vop2_alpha_ctrl src_color_ctrl;
+ union vop2_alpha_ctrl dst_color_ctrl;
+ union vop2_alpha_ctrl src_alpha_ctrl;
+ union vop2_alpha_ctrl dst_alpha_ctrl;
+};
+
+struct vop2_alpha_config {
+ bool src_premulti_en;
+ bool dst_premulti_en;
+ bool src_pixel_alpha_en;
+ bool dst_pixel_alpha_en;
+ u16 src_glb_alpha_value;
+ u16 dst_glb_alpha_value;
+};
+
static const uint32_t formats_cluster[] = {
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
@@ -32,6 +71,37 @@ static const uint32_t formats_cluster[] = {
DRM_FORMAT_Y210, /* yuv422_10bit non-Linear mode only */
};
+/*
+ * The cluster windows on rk3576 support:
+ * RGB: linear mode and afbc
+ * YUV: linear mode and rfbc
+ * rfbc is a rockchip defined non-linear mode, produced by
+ * Video decoder
+ */
+static const uint32_t formats_rk3576_cluster[] = {
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_NV12, /* yuv420_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV21, /* yvu420_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV16, /* yuv422_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV61, /* yvu422_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV24, /* yuv444_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV42, /* yvu444_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV15, /* yuv420_10bit linear mode, 2 plane, no padding */
+ DRM_FORMAT_NV20, /* yuv422_10bit linear mode, 2 plane, no padding */
+ DRM_FORMAT_NV30, /* yuv444_10bit linear mode, 2 plane, no padding */
+};
+
static const uint32_t formats_esmart[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
@@ -78,6 +148,41 @@ static const uint32_t formats_rk356x_esmart[] = {
DRM_FORMAT_VYUY, /* yuv422_8bit[VYUY] linear mode */
};
+/*
+ * Add XRGB2101010/ARGB2101010ARGB1555/XRGB1555
+ */
+static const uint32_t formats_rk3576_esmart[] = {
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_NV12, /* yuv420_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV21, /* yvu420_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV16, /* yuv422_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV61, /* yvu422_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV20, /* yuv422_10bit linear mode, 2 plane, no padding */
+ DRM_FORMAT_NV24, /* yuv444_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV42, /* yvu444_8bit linear mode, 2 plane */
+ DRM_FORMAT_NV30, /* yuv444_10bit linear mode, 2 plane, no padding */
+ DRM_FORMAT_NV15, /* yuv420_10bit linear mode, 2 plane, no padding */
+ DRM_FORMAT_YVYU, /* yuv422_8bit[YVYU] linear mode */
+ DRM_FORMAT_VYUY, /* yuv422_8bit[VYUY] linear mode */
+ DRM_FORMAT_YUYV, /* yuv422_8bit[YUYV] linear mode */
+ DRM_FORMAT_UYVY, /* yuv422_8bit[UYVY] linear mode */
+};
+
static const uint32_t formats_smart[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
@@ -131,6 +236,321 @@ static const uint64_t format_modifiers_afbc[] = {
DRM_FORMAT_MOD_INVALID,
};
+/* used from rk3576, afbc 32*8 half mode */
+static const uint64_t format_modifiers_rk3576_afbc[] = {
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_YTR |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_CBR |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_CBR |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_YTR |
+ AFBC_FORMAT_MOD_CBR |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_YTR |
+ AFBC_FORMAT_MOD_CBR |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+
+ /* SPLIT mandates SPARSE, RGB modes mandates YTR */
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+ AFBC_FORMAT_MOD_YTR |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID,
+};
+
+static const struct reg_field rk3568_vop_cluster_regs[VOP2_WIN_MAX_REG] = {
+ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
+ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
+ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
+ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
+ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
+ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
+ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
+ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
+ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
+ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
+ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
+ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
+ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
+ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
+ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
+ [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 3),
+ [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 8),
+ /* RK3588 only, reserved bit on rk3568*/
+ [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13),
+
+ /* Scale */
+ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
+ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
+ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
+ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
+ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
+ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
+ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
+
+ /* cluster regs */
+ [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
+ [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
+ [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
+
+ /* afbc regs */
+ [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
+ [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
+ [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
+ [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
+ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
+ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
+ [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
+ [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
+ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
+ [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
+ [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
+ [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
+ [VOP2_WIN_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_TRANSFORM_OFFSET, 0, 31),
+ [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
+ [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
+ [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
+ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
+ [VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
+ [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
+ [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+ [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
+ [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
+};
+
+static const struct reg_field rk3568_vop_smart_regs[VOP2_WIN_MAX_REG] = {
+ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
+ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
+ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
+ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
+ [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
+ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
+ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
+ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
+ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
+ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
+ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
+ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
+ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
+ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
+ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
+ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
+ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
+ [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
+ [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
+ [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8),
+ [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16),
+ /* RK3588 only, reserved register on rk3568 */
+ [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1),
+
+ /* Scale */
+ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
+ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
+ [VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
+ [VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
+ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
+ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
+ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
+ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
+ [VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
+ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
+ [VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
+ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
+ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
+ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
+ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
+ [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
+ [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
+ [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
+ [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
+};
+
+static const struct reg_field rk3576_vop_cluster_regs[VOP2_WIN_MAX_REG] = {
+ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
+ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
+ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
+ [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 17, 17),
+ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
+ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
+ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
+ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
+ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
+ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
+ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
+ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
+ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
+ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
+ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
+ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
+ [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 4),
+ [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 9),
+ /* Read only bit on rk3576, writing on this bit have no effect.*/
+ [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13),
+
+ [VOP2_WIN_VP_SEL] = REG_FIELD(RK3576_CLUSTER_PORT_SEL_IMD, 0, 1),
+ [VOP2_WIN_DLY_NUM] = REG_FIELD(RK3576_CLUSTER_DLY_NUM, 0, 7),
+
+ /* Scale */
+ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
+ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
+ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
+ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
+ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 22, 23),
+ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
+ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
+
+ /* cluster regs */
+ [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
+ [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
+ [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
+
+ /* afbc regs */
+ [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
+ [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
+ [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
+ [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
+ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
+ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
+ [VOP2_WIN_AFBC_PLD_OFFSET_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 16, 16),
+ [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
+ [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
+ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
+ [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
+ [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
+ [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
+ [VOP2_WIN_AFBC_PLD_OFFSET] = REG_FIELD(RK3576_CLUSTER_WIN_AFBCD_PLD_PTR_OFFSET, 0, 31),
+ [VOP2_WIN_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_TRANSFORM_OFFSET, 0, 31),
+ [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
+ [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
+ [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
+ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
+ [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
+ [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+ [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
+ [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
+};
+
+static const struct reg_field rk3576_vop_smart_regs[VOP2_WIN_MAX_REG] = {
+ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
+ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
+ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
+ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
+ [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
+ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
+ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
+ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
+ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
+ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
+ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
+ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
+ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
+ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
+ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
+ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
+ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
+ [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
+ [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
+ [VOP2_WIN_VP_SEL] = REG_FIELD(RK3576_SMART_PORT_SEL_IMD, 0, 1),
+ [VOP2_WIN_DLY_NUM] = REG_FIELD(RK3576_SMART_DLY_NUM, 0, 7),
+ [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8),
+ [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16),
+ [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1),
+
+ /* Scale */
+ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
+ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
+ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
+ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
+ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
+ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
+ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
+ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
+ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
+ [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
+ [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
+ [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
+
+ /* CBCR share the same scale factor as YRGB */
+ [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+ [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff},
+ [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff},
+ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff},
+
+ [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
+ [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
+ [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
+};
+
static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
{
.id = 0,
@@ -177,10 +597,12 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
.name = "Smart0-win0",
.phys_id = ROCKCHIP_VOP2_SMART0,
.base = 0x1c00,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_smart,
.nformats = ARRAY_SIZE(formats_smart),
.format_modifiers = format_modifiers,
- .layer_sel_id = 3,
+ /* 0xf means this layer can't attached to this VP */
+ .layer_sel_id = { 3, 3, 3, 0xf },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
.max_upscale_factor = 8,
@@ -189,11 +611,12 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
}, {
.name = "Smart1-win0",
.phys_id = ROCKCHIP_VOP2_SMART1,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_smart,
.nformats = ARRAY_SIZE(formats_smart),
.format_modifiers = format_modifiers,
.base = 0x1e00,
- .layer_sel_id = 7,
+ .layer_sel_id = { 7, 7, 7, 0xf },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
.max_upscale_factor = 8,
@@ -202,11 +625,12 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
}, {
.name = "Esmart1-win0",
.phys_id = ROCKCHIP_VOP2_ESMART1,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_rk356x_esmart,
.nformats = ARRAY_SIZE(formats_rk356x_esmart),
.format_modifiers = format_modifiers,
.base = 0x1a00,
- .layer_sel_id = 6,
+ .layer_sel_id = { 6, 6, 6, 0xf },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
.max_upscale_factor = 8,
@@ -215,11 +639,12 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
}, {
.name = "Esmart0-win0",
.phys_id = ROCKCHIP_VOP2_ESMART0,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_rk356x_esmart,
.nformats = ARRAY_SIZE(formats_rk356x_esmart),
.format_modifiers = format_modifiers,
.base = 0x1800,
- .layer_sel_id = 2,
+ .layer_sel_id = { 2, 2, 2, 0xf },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
.max_upscale_factor = 8,
@@ -229,10 +654,11 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
.name = "Cluster0-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER0,
.base = 0x1000,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 0,
+ .layer_sel_id = { 0, 0, 0, 0xf },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.max_upscale_factor = 4,
@@ -244,10 +670,11 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
.name = "Cluster1-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER1,
.base = 0x1200,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 1,
+ .layer_sel_id = { 1, 1, 1, 0xf },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_OVERLAY,
@@ -258,6 +685,374 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
},
};
+static const struct vop2_regs_dump rk3568_regs_dump[] = {
+ {
+ .name = "SYS",
+ .base = RK3568_REG_CFG_DONE,
+ .size = 0x100,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0
+ }, {
+ .name = "OVL",
+ .base = RK3568_OVL_CTRL,
+ .size = 0x100,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "VP0",
+ .base = RK3568_VP0_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP1",
+ .base = RK3568_VP1_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP2",
+ .base = RK3568_VP2_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+
+ }, {
+ .name = "Cluster0",
+ .base = RK3568_CLUSTER0_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Cluster1",
+ .base = RK3568_CLUSTER1_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Esmart0",
+ .base = RK3568_ESMART0_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart1",
+ .base = RK3568_ESMART1_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Smart0",
+ .base = RK3568_SMART0_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Smart1",
+ .base = RK3568_SMART1_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ },
+};
+
+static const struct vop2_video_port_data rk3576_vop_video_ports[] = {
+ {
+ .id = 0,
+ .feature = VOP2_VP_FEATURE_OUTPUT_10BIT,
+ .gamma_lut_len = 1024,
+ .cubic_lut_len = 9 * 9 * 9, /* 9x9x9 */
+ .max_output = { 4096, 2304 },
+ /* win layer_mix hdr */
+ .pre_scan_max_dly = { 10, 8, 2, 0 },
+ .offset = 0xc00,
+ .pixel_rate = 2,
+ }, {
+ .id = 1,
+ .feature = VOP2_VP_FEATURE_OUTPUT_10BIT,
+ .gamma_lut_len = 1024,
+ .cubic_lut_len = 729, /* 9x9x9 */
+ .max_output = { 2560, 1600 },
+ /* win layer_mix hdr */
+ .pre_scan_max_dly = { 10, 6, 0, 0 },
+ .offset = 0xd00,
+ .pixel_rate = 1,
+ }, {
+ .id = 2,
+ .gamma_lut_len = 1024,
+ .max_output = { 1920, 1080 },
+ /* win layer_mix hdr */
+ .pre_scan_max_dly = { 10, 6, 0, 0 },
+ .offset = 0xe00,
+ .pixel_rate = 1,
+ },
+};
+
+/*
+ * rk3576 vop with 2 cluster, 4 esmart win.
+ * Every cluster can work as 4K win or split into two 2K win.
+ * All win in cluster support AFBCD.
+ *
+ * Every esmart win support 4 Multi-region.
+ *
+ * VP0 can use Cluster0/1 and Esmart0/2
+ * VP1 can use Cluster0/1 and Esmart1/3
+ * VP2 can use Esmart0/1/2/3
+ *
+ * Scale filter mode:
+ *
+ * * Cluster:
+ * * Support prescale down:
+ * * H/V: gt2/avg2 or gt4/avg4
+ * * After prescale down:
+ * * nearest-neighbor/bilinear/multi-phase filter for scale up
+ * * nearest-neighbor/bilinear/multi-phase filter for scale down
+ *
+ * * Esmart:
+ * * Support prescale down:
+ * * H: gt2/avg2 or gt4/avg4
+ * * V: gt2 or gt4
+ * * After prescale down:
+ * * nearest-neighbor/bilinear/bicubic for scale up
+ * * nearest-neighbor/bilinear for scale down
+ *
+ * AXI config::
+ *
+ * * Cluster0 win0: 0xa, 0xb [AXI0]
+ * * Cluster0 win1: 0xc, 0xd [AXI0]
+ * * Cluster1 win0: 0x6, 0x7 [AXI0]
+ * * Cluster1 win1: 0x8, 0x9 [AXI0]
+ * * Esmart0: 0x10, 0x11 [AXI0]
+ * * Esmart1: 0x12, 0x13 [AXI0]
+ * * Esmart2: 0xa, 0xb [AXI1]
+ * * Esmart3: 0xc, 0xd [AXI1]
+ * * Lut dma rid: 0x1, 0x2, 0x3 [AXI0]
+ * * DCI dma rid: 0x4 [AXI0]
+ * * Metadata rid: 0x5 [AXI0]
+ *
+ * * Limit:
+ * * (1) Cluster0/1 are fixed on AXI0 by IC design
+ * * (2) 0x0 and 0xf can't be used;
+ * * (3) 5 Bits ID for eache axi bus
+ * * (3) cluster and lut/dci/metadata rid must smaller than 0xf,
+ * * if Cluster rid is bigger than 0xf, VOP will dead at the
+ * * system bandwidth very terrible scene.
+ */
+static const struct vop2_win_data rk3576_vop_win_data[] = {
+ {
+ .name = "Cluster0-win0",
+ .phys_id = ROCKCHIP_VOP2_CLUSTER0,
+ .base = 0x1000,
+ .possible_vp_mask = BIT(0) | BIT(1),
+ .formats = formats_rk3576_cluster,
+ .nformats = ARRAY_SIZE(formats_rk3576_cluster),
+ .format_modifiers = format_modifiers_rk3576_afbc,
+ .layer_sel_id = { 0, 0, 0xf, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .axi_bus_id = 0,
+ .axi_yrgb_r_id = 0xa,
+ .axi_uv_r_id = 0xb,
+ .max_upscale_factor = 4,
+ .max_downscale_factor = 4,
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+ }, {
+ .name = "Cluster1-win0",
+ .phys_id = ROCKCHIP_VOP2_CLUSTER1,
+ .base = 0x1200,
+ .possible_vp_mask = BIT(0) | BIT(1),
+ .formats = formats_rk3576_cluster,
+ .nformats = ARRAY_SIZE(formats_rk3576_cluster),
+ .format_modifiers = format_modifiers_rk3576_afbc,
+ .layer_sel_id = { 1, 1, 0xf, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .axi_bus_id = 0,
+ .axi_yrgb_r_id = 6,
+ .axi_uv_r_id = 7,
+ .max_upscale_factor = 4,
+ .max_downscale_factor = 4,
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+ }, {
+ .name = "Esmart0-win0",
+ .phys_id = ROCKCHIP_VOP2_ESMART0,
+ .base = 0x1800,
+ .possible_vp_mask = BIT(0) | BIT(2),
+ .formats = formats_rk3576_esmart,
+ .nformats = ARRAY_SIZE(formats_rk3576_esmart),
+ .format_modifiers = format_modifiers,
+ .layer_sel_id = { 2, 0xf, 0, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .axi_bus_id = 0,
+ .axi_yrgb_r_id = 0x10,
+ .axi_uv_r_id = 0x11,
+ .max_upscale_factor = 8,
+ .max_downscale_factor = 8,
+ }, {
+ .name = "Esmart1-win0",
+ .phys_id = ROCKCHIP_VOP2_ESMART1,
+ .base = 0x1a00,
+ .possible_vp_mask = BIT(1) | BIT(2),
+ .formats = formats_rk3576_esmart,
+ .nformats = ARRAY_SIZE(formats_rk3576_esmart),
+ .format_modifiers = format_modifiers,
+ .layer_sel_id = { 0xf, 2, 1, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .axi_bus_id = 0,
+ .axi_yrgb_r_id = 0x12,
+ .axi_uv_r_id = 0x13,
+ .max_upscale_factor = 8,
+ .max_downscale_factor = 8,
+ }, {
+ .name = "Esmart2-win0",
+ .phys_id = ROCKCHIP_VOP2_ESMART2,
+ .base = 0x1c00,
+ .possible_vp_mask = BIT(0) | BIT(2),
+ .formats = formats_rk3576_esmart,
+ .nformats = ARRAY_SIZE(formats_rk3576_esmart),
+ .format_modifiers = format_modifiers,
+ .layer_sel_id = { 3, 0xf, 2, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .axi_bus_id = 1,
+ .axi_yrgb_r_id = 0x0a,
+ .axi_uv_r_id = 0x0b,
+ .max_upscale_factor = 8,
+ .max_downscale_factor = 8,
+ }, {
+ .name = "Esmart3-win0",
+ .phys_id = ROCKCHIP_VOP2_ESMART3,
+ .base = 0x1e00,
+ .possible_vp_mask = BIT(1) | BIT(2),
+ .formats = formats_rk3576_esmart,
+ .nformats = ARRAY_SIZE(formats_rk3576_esmart),
+ .format_modifiers = format_modifiers,
+ .layer_sel_id = { 0xf, 3, 3, 0xf },
+ .supported_rotations = DRM_MODE_REFLECT_Y,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .axi_bus_id = 1,
+ .axi_yrgb_r_id = 0x0c,
+ .axi_uv_r_id = 0x0d,
+ .max_upscale_factor = 8,
+ .max_downscale_factor = 8,
+ },
+};
+
+static const struct vop2_regs_dump rk3576_regs_dump[] = {
+ {
+ .name = "SYS",
+ .base = RK3568_REG_CFG_DONE,
+ .size = 0x200,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0
+ }, {
+ .name = "OVL_SYS",
+ .base = RK3576_SYS_EXTRA_ALPHA_CTRL,
+ .size = 0x50,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "OVL_VP0",
+ .base = RK3576_OVL_CTRL(0),
+ .size = 0x80,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "OVL_VP1",
+ .base = RK3576_OVL_CTRL(1),
+ .size = 0x80,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "OVL_VP2",
+ .base = RK3576_OVL_CTRL(2),
+ .size = 0x80,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "VP0",
+ .base = RK3568_VP0_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP1",
+ .base = RK3568_VP1_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP2",
+ .base = RK3568_VP2_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "Cluster0",
+ .base = RK3568_CLUSTER0_CTRL_BASE,
+ .size = 0x200,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Cluster1",
+ .base = RK3568_CLUSTER1_CTRL_BASE,
+ .size = 0x200,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Esmart0",
+ .base = RK3568_ESMART0_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart1",
+ .base = RK3568_ESMART1_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart2",
+ .base = RK3588_ESMART2_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart3",
+ .base = RK3588_ESMART3_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ },
+};
+
static const struct vop2_video_port_data rk3588_vop_video_ports[] = {
{
.id = 0,
@@ -327,10 +1122,11 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.name = "Cluster0-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER0,
.base = 0x1000,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 0,
+ .layer_sel_id = { 0, 0, 0, 0 },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.axi_bus_id = 0,
@@ -345,10 +1141,11 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.name = "Cluster1-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER1,
.base = 0x1200,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 1,
+ .layer_sel_id = { 1, 1, 1, 1 },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
@@ -363,10 +1160,11 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.name = "Cluster2-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER2,
.base = 0x1400,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 4,
+ .layer_sel_id = { 4, 4, 4, 4 },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
@@ -381,10 +1179,11 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.name = "Cluster3-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER3,
.base = 0x1600,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_cluster,
.nformats = ARRAY_SIZE(formats_cluster),
.format_modifiers = format_modifiers_afbc,
- .layer_sel_id = 5,
+ .layer_sel_id = { 5, 5, 5, 5 },
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_PRIMARY,
@@ -398,11 +1197,12 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
}, {
.name = "Esmart0-win0",
.phys_id = ROCKCHIP_VOP2_ESMART0,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_esmart,
.nformats = ARRAY_SIZE(formats_esmart),
.format_modifiers = format_modifiers,
.base = 0x1800,
- .layer_sel_id = 2,
+ .layer_sel_id = { 2, 2, 2, 2 },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_OVERLAY,
.axi_bus_id = 0,
@@ -414,11 +1214,12 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
}, {
.name = "Esmart1-win0",
.phys_id = ROCKCHIP_VOP2_ESMART1,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_esmart,
.nformats = ARRAY_SIZE(formats_esmart),
.format_modifiers = format_modifiers,
.base = 0x1a00,
- .layer_sel_id = 3,
+ .layer_sel_id = { 3, 3, 3, 3 },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_OVERLAY,
.axi_bus_id = 0,
@@ -431,10 +1232,11 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.name = "Esmart2-win0",
.phys_id = ROCKCHIP_VOP2_ESMART2,
.base = 0x1c00,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_esmart,
.nformats = ARRAY_SIZE(formats_esmart),
.format_modifiers = format_modifiers,
- .layer_sel_id = 6,
+ .layer_sel_id = { 6, 6, 6, 6 },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_OVERLAY,
.axi_bus_id = 1,
@@ -446,11 +1248,12 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
}, {
.name = "Esmart3-win0",
.phys_id = ROCKCHIP_VOP2_ESMART3,
+ .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
.formats = formats_esmart,
.nformats = ARRAY_SIZE(formats_esmart),
.format_modifiers = format_modifiers,
.base = 0x1e00,
- .layer_sel_id = 7,
+ .layer_sel_id = { 7, 7, 7, 7 },
.supported_rotations = DRM_MODE_REFLECT_Y,
.type = DRM_PLANE_TYPE_OVERLAY,
.axi_bus_id = 1,
@@ -462,7 +1265,1200 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
},
};
+static const struct vop2_regs_dump rk3588_regs_dump[] = {
+ {
+ .name = "SYS",
+ .base = RK3568_REG_CFG_DONE,
+ .size = 0x100,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0
+ }, {
+ .name = "OVL",
+ .base = RK3568_OVL_CTRL,
+ .size = 0x100,
+ .en_reg = 0,
+ .en_val = 0,
+ .en_mask = 0,
+ }, {
+ .name = "VP0",
+ .base = RK3568_VP0_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP1",
+ .base = RK3568_VP1_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "VP2",
+ .base = RK3568_VP2_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+
+ }, {
+ .name = "VP3",
+ .base = RK3588_VP3_CTRL_BASE,
+ .size = 0x100,
+ .en_reg = RK3568_VP_DSP_CTRL,
+ .en_val = 0,
+ .en_mask = RK3568_VP_DSP_CTRL__STANDBY,
+ }, {
+ .name = "Cluster0",
+ .base = RK3568_CLUSTER0_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Cluster1",
+ .base = RK3568_CLUSTER1_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Cluster2",
+ .base = RK3588_CLUSTER2_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Cluster3",
+ .base = RK3588_CLUSTER3_CTRL_BASE,
+ .size = 0x110,
+ .en_reg = RK3568_CLUSTER_WIN_CTRL0,
+ .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN,
+ }, {
+ .name = "Esmart0",
+ .base = RK3568_ESMART0_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart1",
+ .base = RK3568_ESMART1_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart2",
+ .base = RK3588_ESMART2_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ }, {
+ .name = "Esmart3",
+ .base = RK3588_ESMART3_CTRL_BASE,
+ .size = 0xf0,
+ .en_reg = RK3568_SMART_REGION0_CTRL,
+ .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN,
+ },
+};
+
+static unsigned long rk3568_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ u32 die, dip;
+
+ die = vop2_readl(vop2, RK3568_DSP_IF_EN);
+ dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
+
+ switch (id) {
+ case ROCKCHIP_VOP2_EP_RGB0:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_RGB |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+ if (polflags & POLFLAG_DCLK_INV)
+ regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
+ else
+ regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
+ break;
+ case ROCKCHIP_VOP2_EP_HDMI0:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_EDP0:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_EDP |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_MIPI0:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_MIPI1:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_LVDS0:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_LVDS1:
+ die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
+ die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
+ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
+ dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+ break;
+ default:
+ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
+ return 0;
+ }
+
+ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
+
+ vop2_writel(vop2, RK3568_DSP_IF_EN, die);
+ vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
+
+ return crtc->state->adjusted_mode.crtc_clock * 1000LL;
+}
+
+static unsigned long rk3576_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+ u8 port_pix_rate = vp->data->pixel_rate;
+ int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_sel;
+ u32 ctrl, vp_clk_div, reg, dclk_div;
+ unsigned long dclk_in_rate, dclk_core_rate;
+
+ if (vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV420 || adjusted_mode->crtc_clock > 600000)
+ dclk_div = 2;
+ else
+ dclk_div = 1;
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
+ dclk_core_rate = adjusted_mode->crtc_clock / 2;
+ else
+ dclk_core_rate = adjusted_mode->crtc_clock / port_pix_rate;
+
+ dclk_in_rate = adjusted_mode->crtc_clock / dclk_div;
+
+ dclk_core_div = dclk_in_rate > dclk_core_rate ? 1 : 0;
+
+ if (vop2_output_if_is_edp(id))
+ if_pixclk_div = port_pix_rate == 2 ? RK3576_DSP_IF_PCLK_DIV : 0;
+ else
+ if_pixclk_div = port_pix_rate == 1 ? RK3576_DSP_IF_PCLK_DIV : 0;
+
+ if (vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV420) {
+ if_dclk_sel = RK3576_DSP_IF_DCLK_SEL_OUT;
+ dclk_out_div = 1;
+ } else {
+ if_dclk_sel = 0;
+ dclk_out_div = 0;
+ }
+
+ switch (id) {
+ case ROCKCHIP_VOP2_EP_HDMI0:
+ reg = RK3576_HDMI0_IF_CTRL;
+ break;
+ case ROCKCHIP_VOP2_EP_EDP0:
+ reg = RK3576_EDP0_IF_CTRL;
+ break;
+ case ROCKCHIP_VOP2_EP_MIPI0:
+ reg = RK3576_MIPI0_IF_CTRL;
+ break;
+ case ROCKCHIP_VOP2_EP_DP0:
+ reg = RK3576_DP0_IF_CTRL;
+ break;
+ case ROCKCHIP_VOP2_EP_DP1:
+ reg = RK3576_DP1_IF_CTRL;
+ break;
+ default:
+ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
+ return 0;
+ }
+
+ ctrl = vop2_readl(vop2, reg);
+ ctrl &= ~RK3576_DSP_IF_DCLK_SEL_OUT;
+ ctrl &= ~RK3576_DSP_IF_PCLK_DIV;
+ ctrl &= ~RK3576_DSP_IF_MUX;
+ ctrl |= RK3576_DSP_IF_CFG_DONE_IMD;
+ ctrl |= if_dclk_sel | if_pixclk_div;
+ ctrl |= RK3576_DSP_IF_CLK_OUT_EN | RK3576_DSP_IF_EN;
+ ctrl |= FIELD_PREP(RK3576_DSP_IF_MUX, vp->id);
+ ctrl |= FIELD_PREP(RK3576_DSP_IF_PIN_POL, polflags);
+ vop2_writel(vop2, reg, ctrl);
+
+ vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div);
+ vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div);
+
+ vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div);
+
+ return dclk_in_rate * 1000LL;
+}
+
+/*
+ * calc the dclk on rk3588
+ * the available div of dclk is 1, 2, 4
+ */
+static unsigned long rk3588_calc_dclk(unsigned long child_clk, unsigned long max_dclk)
+{
+ if (child_clk * 4 <= max_dclk)
+ return child_clk * 4;
+ else if (child_clk * 2 <= max_dclk)
+ return child_clk * 2;
+ else if (child_clk <= max_dclk)
+ return child_clk;
+ else
+ return 0;
+}
+
+/*
+ * 4 pixclk/cycle on rk3588
+ * RGB/eDP/HDMI: if_pixclk >= dclk_core
+ * DP: dp_pixclk = dclk_out <= dclk_core
+ * DSI: mipi_pixclk <= dclk_out <= dclk_core
+ */
+static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id,
+ int *dclk_core_div, int *dclk_out_div,
+ int *if_pixclk_div, int *if_dclk_div)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+ int output_mode = vcstate->output_mode;
+ unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */
+ unsigned long dclk_core_rate = v_pixclk >> 2;
+ unsigned long dclk_rate = v_pixclk;
+ unsigned long dclk_out_rate;
+ unsigned long if_pixclk_rate;
+ int K = 1;
+
+ if (vop2_output_if_is_hdmi(id)) {
+ /*
+ * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate
+ * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate
+ */
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) {
+ dclk_rate = dclk_rate >> 1;
+ K = 2;
+ }
+
+ /*
+ * if_pixclk_rate = (dclk_core_rate << 1) / K;
+ * if_dclk_rate = dclk_core_rate / K;
+ * *if_pixclk_div = dclk_rate / if_pixclk_rate;
+ * *if_dclk_div = dclk_rate / if_dclk_rate;
+ */
+ *if_pixclk_div = 2;
+ *if_dclk_div = 4;
+ } else if (vop2_output_if_is_edp(id)) {
+ /*
+ * edp_pixclk = edp_dclk > dclk_core
+ */
+ if_pixclk_rate = v_pixclk / K;
+ dclk_rate = if_pixclk_rate * K;
+ /*
+ * *if_pixclk_div = dclk_rate / if_pixclk_rate;
+ * *if_dclk_div = *if_pixclk_div;
+ */
+ *if_pixclk_div = K;
+ *if_dclk_div = K;
+ } else if (vop2_output_if_is_dp(id)) {
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420)
+ dclk_out_rate = v_pixclk >> 3;
+ else
+ dclk_out_rate = v_pixclk >> 2;
+
+ dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000);
+ if (!dclk_rate) {
+ drm_err(vop2->drm, "DP dclk_out_rate out of range, dclk_out_rate: %ld Hz\n",
+ dclk_out_rate);
+ return 0;
+ }
+ *dclk_out_div = dclk_rate / dclk_out_rate;
+ } else if (vop2_output_if_is_mipi(id)) {
+ if_pixclk_rate = dclk_core_rate / K;
+ /*
+ * dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4
+ */
+ dclk_out_rate = if_pixclk_rate;
+ /*
+ * dclk_rate = N * dclk_core_rate N = (1,2,4 ),
+ * we get a little factor here
+ */
+ dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000);
+ if (!dclk_rate) {
+ drm_err(vop2->drm, "MIPI dclk out of range, dclk_out_rate: %ld Hz\n",
+ dclk_out_rate);
+ return 0;
+ }
+ *dclk_out_div = dclk_rate / dclk_out_rate;
+ /*
+ * mipi pixclk == dclk_out
+ */
+ *if_pixclk_div = 1;
+ } else if (vop2_output_if_is_dpi(id)) {
+ dclk_rate = v_pixclk;
+ }
+
+ *dclk_core_div = dclk_rate / dclk_core_rate;
+ *if_pixclk_div = ilog2(*if_pixclk_div);
+ *if_dclk_div = ilog2(*if_dclk_div);
+ *dclk_core_div = ilog2(*dclk_core_div);
+ *dclk_out_div = ilog2(*dclk_out_div);
+
+ drm_dbg(vop2->drm, "dclk: %ld, pixclk_div: %d, dclk_div: %d\n",
+ dclk_rate, *if_pixclk_div, *if_dclk_div);
+
+ return dclk_rate;
+}
+
+/*
+ * MIPI port mux on rk3588:
+ * 0: Video Port2
+ * 1: Video Port3
+ * 3: Video Port 1(MIPI1 only)
+ */
+static u32 rk3588_get_mipi_port_mux(int vp_id)
+{
+ if (vp_id == 1)
+ return 3;
+ else if (vp_id == 3)
+ return 1;
+ else
+ return 0;
+}
+
+static u32 rk3588_get_hdmi_pol(u32 flags)
+{
+ u32 val;
+
+ val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0;
+ val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0;
+
+ return val;
+}
+
+static unsigned long rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
+{
+ struct vop2 *vop2 = vp->vop2;
+ int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div;
+ unsigned long clock;
+ u32 die, dip, div, vp_clk_div, val;
+
+ clock = rk3588_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div,
+ &if_pixclk_div, &if_dclk_div);
+ if (!clock)
+ return 0;
+
+ vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div);
+ vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div);
+
+ die = vop2_readl(vop2, RK3568_DSP_IF_EN);
+ dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
+ div = vop2_readl(vop2, RK3568_DSP_IF_CTRL);
+
+ switch (id) {
+ case ROCKCHIP_VOP2_EP_HDMI0:
+ div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV;
+ div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
+ val = rk3588_get_hdmi_pol(polflags);
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1));
+ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5));
+ break;
+ case ROCKCHIP_VOP2_EP_HDMI1:
+ div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV;
+ div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div);
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
+ val = rk3588_get_hdmi_pol(polflags);
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4));
+ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7));
+ break;
+ case ROCKCHIP_VOP2_EP_EDP0:
+ div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV;
+ div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_EDP0 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0));
+ break;
+ case ROCKCHIP_VOP2_EP_EDP1:
+ div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV;
+ div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_EDP1 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3));
+ break;
+ case ROCKCHIP_VOP2_EP_MIPI0:
+ div &= ~RK3588_DSP_IF_MIPI0_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX;
+ val = rk3588_get_mipi_port_mux(vp->id);
+ die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val);
+ break;
+ case ROCKCHIP_VOP2_EP_MIPI1:
+ div &= ~RK3588_DSP_IF_MIPI1_PCLK_DIV;
+ div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div);
+ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX;
+ val = rk3588_get_mipi_port_mux(vp->id);
+ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val);
+ break;
+ case ROCKCHIP_VOP2_EP_DP0:
+ die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_DP0 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id);
+ dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL;
+ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags);
+ break;
+ case ROCKCHIP_VOP2_EP_DP1:
+ die &= ~RK3588_SYS_DSP_INFACE_EN_DP1_MUX;
+ die |= RK3588_SYS_DSP_INFACE_EN_DP1 |
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP1_MUX, vp->id);
+ dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL;
+ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags);
+ break;
+ default:
+ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
+ return 0;
+ }
+
+ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
+
+ vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div);
+ vop2_writel(vop2, RK3568_DSP_IF_EN, die);
+ vop2_writel(vop2, RK3568_DSP_IF_CTRL, div);
+ vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
+
+ return clock;
+}
+
+static bool is_opaque(u16 alpha)
+{
+ return (alpha >> 8) == 0xff;
+}
+
+static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
+ struct vop2_alpha *alpha)
+{
+ int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
+ int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
+ int src_color_mode = alpha_config->src_premulti_en ?
+ ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+ int dst_color_mode = alpha_config->dst_premulti_en ?
+ ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+
+ alpha->src_color_ctrl.val = 0;
+ alpha->dst_color_ctrl.val = 0;
+ alpha->src_alpha_ctrl.val = 0;
+ alpha->dst_alpha_ctrl.val = 0;
+
+ if (!alpha_config->src_pixel_alpha_en)
+ alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+ else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
+ alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+ else
+ alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+
+ alpha->src_color_ctrl.bits.alpha_en = 1;
+
+ if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
+ alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+ } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
+ alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
+ } else {
+ alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
+ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+ }
+ alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
+ alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+ alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+
+ alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+ alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+ alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+ alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
+ alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
+ alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+
+ alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+ alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
+ alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+ alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
+
+ alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+ if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
+ alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+ else
+ alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+ alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
+ alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+}
+
+static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
+{
+ struct vop2_video_port *vp;
+ int used_layer = 0;
+ int i;
+
+ for (i = 0; i < port_id; i++) {
+ vp = &vop2->vps[i];
+ used_layer += hweight32(vp->win_mask);
+ }
+
+ return used_layer;
+}
+
+static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
+{
+ struct vop2_alpha_config alpha_config;
+ struct vop2_alpha alpha;
+ struct drm_plane_state *bottom_win_pstate;
+ bool src_pixel_alpha_en = false;
+ u16 src_glb_alpha_val, dst_glb_alpha_val;
+ u32 src_color_ctrl_reg, dst_color_ctrl_reg, src_alpha_ctrl_reg, dst_alpha_ctrl_reg;
+ u32 offset = 0;
+ bool premulti_en = false;
+ bool swap = false;
+
+ /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
+ bottom_win_pstate = main_win->base.state;
+ src_glb_alpha_val = 0;
+ dst_glb_alpha_val = main_win->base.state->alpha;
+
+ if (!bottom_win_pstate->fb)
+ return;
+
+ alpha_config.src_premulti_en = premulti_en;
+ alpha_config.dst_premulti_en = false;
+ alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
+ alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+ alpha_config.src_glb_alpha_value = src_glb_alpha_val;
+ alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
+ vop2_parse_alpha(&alpha_config, &alpha);
+
+ alpha.src_color_ctrl.bits.src_dst_swap = swap;
+
+ switch (main_win->data->phys_id) {
+ case ROCKCHIP_VOP2_CLUSTER0:
+ offset = 0x0;
+ break;
+ case ROCKCHIP_VOP2_CLUSTER1:
+ offset = 0x10;
+ break;
+ case ROCKCHIP_VOP2_CLUSTER2:
+ offset = 0x20;
+ break;
+ case ROCKCHIP_VOP2_CLUSTER3:
+ offset = 0x30;
+ break;
+ }
+
+ if (vop2->version <= VOP_VERSION_RK3588) {
+ src_color_ctrl_reg = RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL;
+ dst_color_ctrl_reg = RK3568_CLUSTER0_MIX_DST_COLOR_CTRL;
+ src_alpha_ctrl_reg = RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL;
+ dst_alpha_ctrl_reg = RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL;
+ } else {
+ src_color_ctrl_reg = RK3576_CLUSTER0_MIX_SRC_COLOR_CTRL;
+ dst_color_ctrl_reg = RK3576_CLUSTER0_MIX_DST_COLOR_CTRL;
+ src_alpha_ctrl_reg = RK3576_CLUSTER0_MIX_SRC_ALPHA_CTRL;
+ dst_alpha_ctrl_reg = RK3576_CLUSTER0_MIX_DST_ALPHA_CTRL;
+ }
+
+ vop2_writel(vop2, src_color_ctrl_reg + offset, alpha.src_color_ctrl.val);
+ vop2_writel(vop2, dst_color_ctrl_reg + offset, alpha.dst_color_ctrl.val);
+ vop2_writel(vop2, src_alpha_ctrl_reg + offset, alpha.src_alpha_ctrl.val);
+ vop2_writel(vop2, dst_alpha_ctrl_reg + offset, alpha.dst_alpha_ctrl.val);
+}
+
+static void vop2_setup_alpha(struct vop2_video_port *vp)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_framebuffer *fb;
+ struct vop2_alpha_config alpha_config;
+ struct vop2_alpha alpha;
+ struct drm_plane *plane;
+ int pixel_alpha_en;
+ int premulti_en, gpremulti_en = 0;
+ int mixer_id;
+ u32 src_color_ctrl_reg, dst_color_ctrl_reg, src_alpha_ctrl_reg, dst_alpha_ctrl_reg;
+ u32 offset;
+ bool bottom_layer_alpha_en = false;
+ u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
+
+ if (vop2->version <= VOP_VERSION_RK3588)
+ mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
+ else
+ mixer_id = 0;
+
+ alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+
+ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+
+ if (plane->state->normalized_zpos == 0 &&
+ !is_opaque(plane->state->alpha) &&
+ !vop2_cluster_window(win)) {
+ /*
+ * If bottom layer have global alpha effect [except cluster layer,
+ * because cluster have deal with bottom layer global alpha value
+ * at cluster mix], bottom layer mix need deal with global alpha.
+ */
+ bottom_layer_alpha_en = true;
+ dst_global_alpha = plane->state->alpha;
+ }
+ }
+
+ if (vop2->version <= VOP_VERSION_RK3588) {
+ src_color_ctrl_reg = RK3568_MIX0_SRC_COLOR_CTRL;
+ dst_color_ctrl_reg = RK3568_MIX0_DST_COLOR_CTRL;
+ src_alpha_ctrl_reg = RK3568_MIX0_SRC_ALPHA_CTRL;
+ dst_alpha_ctrl_reg = RK3568_MIX0_DST_ALPHA_CTRL;
+ } else {
+ src_color_ctrl_reg = RK3576_OVL_MIX0_SRC_COLOR_CTRL(vp->id);
+ dst_color_ctrl_reg = RK3576_OVL_MIX0_DST_COLOR_CTRL(vp->id);
+ src_alpha_ctrl_reg = RK3576_OVL_MIX0_SRC_ALPHA_CTRL(vp->id);
+ dst_alpha_ctrl_reg = RK3576_OVL_MIX0_DST_ALPHA_CTRL(vp->id);
+ }
+
+ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+ int zpos = plane->state->normalized_zpos;
+
+ /*
+ * Need to configure alpha from second layer.
+ */
+ if (zpos == 0)
+ continue;
+
+ if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
+ premulti_en = 1;
+ else
+ premulti_en = 0;
+
+ plane = &win->base;
+ fb = plane->state->fb;
+
+ pixel_alpha_en = fb->format->has_alpha;
+
+ alpha_config.src_premulti_en = premulti_en;
+
+ if (bottom_layer_alpha_en && zpos == 1) {
+ gpremulti_en = premulti_en;
+ /* Cd = Cs + (1 - As) * Cd * Agd */
+ alpha_config.dst_premulti_en = false;
+ alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+ alpha_config.src_glb_alpha_value = plane->state->alpha;
+ alpha_config.dst_glb_alpha_value = dst_global_alpha;
+ } else if (vop2_cluster_window(win)) {
+ /* Mix output data only have pixel alpha */
+ alpha_config.dst_premulti_en = true;
+ alpha_config.src_pixel_alpha_en = true;
+ alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+ } else {
+ /* Cd = Cs + (1 - As) * Cd */
+ alpha_config.dst_premulti_en = true;
+ alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+ alpha_config.src_glb_alpha_value = plane->state->alpha;
+ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+ }
+
+ vop2_parse_alpha(&alpha_config, &alpha);
+
+ offset = (mixer_id + zpos - 1) * 0x10;
+
+ vop2_writel(vop2, src_color_ctrl_reg + offset, alpha.src_color_ctrl.val);
+ vop2_writel(vop2, dst_color_ctrl_reg + offset, alpha.dst_color_ctrl.val);
+ vop2_writel(vop2, src_alpha_ctrl_reg + offset, alpha.src_alpha_ctrl.val);
+ vop2_writel(vop2, dst_alpha_ctrl_reg + offset, alpha.dst_alpha_ctrl.val);
+ }
+
+ if (vp->id == 0) {
+ if (vop2->version <= VOP_VERSION_RK3588) {
+ src_color_ctrl_reg = RK3568_HDR0_SRC_COLOR_CTRL;
+ dst_color_ctrl_reg = RK3568_HDR0_DST_COLOR_CTRL;
+ src_alpha_ctrl_reg = RK3568_HDR0_SRC_ALPHA_CTRL;
+ dst_alpha_ctrl_reg = RK3568_HDR0_DST_ALPHA_CTRL;
+ } else {
+ src_color_ctrl_reg = RK3576_OVL_HDR_SRC_COLOR_CTRL(vp->id);
+ dst_color_ctrl_reg = RK3576_OVL_HDR_DST_COLOR_CTRL(vp->id);
+ src_alpha_ctrl_reg = RK3576_OVL_HDR_SRC_ALPHA_CTRL(vp->id);
+ dst_alpha_ctrl_reg = RK3576_OVL_HDR_DST_ALPHA_CTRL(vp->id);
+ }
+
+ if (bottom_layer_alpha_en) {
+ /* Transfer pixel alpha to hdr mix */
+ alpha_config.src_premulti_en = gpremulti_en;
+ alpha_config.dst_premulti_en = true;
+ alpha_config.src_pixel_alpha_en = true;
+ alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+
+ vop2_parse_alpha(&alpha_config, &alpha);
+
+ vop2_writel(vop2, src_color_ctrl_reg, alpha.src_color_ctrl.val);
+ vop2_writel(vop2, dst_color_ctrl_reg, alpha.dst_color_ctrl.val);
+ vop2_writel(vop2, src_alpha_ctrl_reg, alpha.src_alpha_ctrl.val);
+ vop2_writel(vop2, dst_alpha_ctrl_reg, alpha.dst_alpha_ctrl.val);
+ } else {
+ vop2_writel(vop2, src_color_ctrl_reg, 0);
+ }
+ }
+}
+
+static u32 rk3568_vop2_read_port_mux(struct vop2 *vop2)
+{
+ return vop2_readl(vop2, RK3568_OVL_PORT_SEL);
+}
+
+static void rk3568_vop2_wait_for_port_mux_done(struct vop2 *vop2)
+{
+ u32 port_mux_sel;
+ int ret;
+
+ /*
+ * Spin until the previous port_mux figuration is done.
+ */
+ ret = readx_poll_timeout_atomic(rk3568_vop2_read_port_mux, vop2, port_mux_sel,
+ port_mux_sel == vop2->old_port_sel, 0, 50 * 1000);
+ if (ret)
+ DRM_DEV_ERROR(vop2->dev, "wait port_mux done timeout: 0x%x--0x%x\n",
+ port_mux_sel, vop2->old_port_sel);
+}
+
+static u32 rk3568_vop2_read_layer_cfg(struct vop2 *vop2)
+{
+ return vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+}
+
+static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2, u32 cfg)
+{
+ u32 atv_layer_cfg;
+ int ret;
+
+ /*
+ * Spin until the previous layer configuration is done.
+ */
+ ret = readx_poll_timeout_atomic(rk3568_vop2_read_layer_cfg, vop2, atv_layer_cfg,
+ atv_layer_cfg == cfg, 0, 50 * 1000);
+ if (ret)
+ DRM_DEV_ERROR(vop2->dev, "wait layer cfg done timeout: 0x%x--0x%x\n",
+ atv_layer_cfg, cfg);
+}
+
+static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_plane *plane;
+ u32 layer_sel = 0;
+ u32 port_sel;
+ u32 old_layer_sel = 0;
+ u32 atv_layer_sel = 0;
+ u32 old_port_sel = 0;
+ u8 layer_id;
+ u8 old_layer_id;
+ u8 layer_sel_id;
+ unsigned int ofs;
+ u32 ovl_ctrl;
+ int i;
+ struct vop2_video_port *vp0 = &vop2->vps[0];
+ struct vop2_video_port *vp1 = &vop2->vps[1];
+ struct vop2_video_port *vp2 = &vop2->vps[2];
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+
+ mutex_lock(&vop2->ovl_lock);
+ ovl_ctrl = vop2_readl(vop2, RK3568_OVL_CTRL);
+ ovl_ctrl &= ~RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD;
+ ovl_ctrl &= ~RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL;
+
+ if (vcstate->yuv_overlay)
+ ovl_ctrl |= RK3568_OVL_CTRL__YUV_MODE(vp->id);
+ else
+ ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id);
+
+ old_port_sel = vop2->old_port_sel;
+ port_sel = old_port_sel;
+ port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
+
+ if (vp0->nlayers)
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
+ vp0->nlayers - 1);
+ else
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
+
+ if (vp1->nlayers)
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
+ (vp0->nlayers + vp1->nlayers - 1));
+ else
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+ if (vp2->nlayers)
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
+ (vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
+ else
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, 8);
+
+ /* Fixed value for rk3588 */
+ if (vop2->version == VOP_VERSION_RK3588)
+ port_sel |= FIELD_PREP(RK3588_OVL_PORT_SET__PORT3_MUX, 7);
+
+ atv_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+ old_layer_sel = vop2->old_layer_sel;
+ layer_sel = old_layer_sel;
+
+ ofs = 0;
+ for (i = 0; i < vp->id; i++)
+ ofs += vop2->vps[i].nlayers;
+
+ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+ struct vop2_win *old_win;
+
+ layer_id = (u8)(plane->state->normalized_zpos + ofs);
+ /*
+ * Find the layer this win bind in old state.
+ */
+ for (old_layer_id = 0; old_layer_id < vop2->data->win_size; old_layer_id++) {
+ layer_sel_id = (layer_sel >> (4 * old_layer_id)) & 0xf;
+ if (layer_sel_id == win->data->layer_sel_id[vp->id])
+ break;
+ }
+
+ /*
+ * Find the win bind to this layer in old state
+ */
+ for (i = 0; i < vop2->data->win_size; i++) {
+ old_win = &vop2->win[i];
+ layer_sel_id = (layer_sel >> (4 * layer_id)) & 0xf;
+ if (layer_sel_id == old_win->data->layer_sel_id[vp->id])
+ break;
+ }
+
+ switch (win->data->phys_id) {
+ case ROCKCHIP_VOP2_CLUSTER0:
+ port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
+ break;
+ case ROCKCHIP_VOP2_CLUSTER1:
+ port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
+ break;
+ case ROCKCHIP_VOP2_CLUSTER2:
+ port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER2;
+ port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER2, vp->id);
+ break;
+ case ROCKCHIP_VOP2_CLUSTER3:
+ port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER3;
+ port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER3, vp->id);
+ break;
+ case ROCKCHIP_VOP2_ESMART0:
+ port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
+ break;
+ case ROCKCHIP_VOP2_ESMART1:
+ port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
+ break;
+ case ROCKCHIP_VOP2_ESMART2:
+ port_sel &= ~RK3588_OVL_PORT_SEL__ESMART2;
+ port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART2, vp->id);
+ break;
+ case ROCKCHIP_VOP2_ESMART3:
+ port_sel &= ~RK3588_OVL_PORT_SEL__ESMART3;
+ port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART3, vp->id);
+ break;
+ case ROCKCHIP_VOP2_SMART0:
+ port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
+ break;
+ case ROCKCHIP_VOP2_SMART1:
+ port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
+ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
+ break;
+ }
+
+ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(layer_id, 0x7);
+ layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id[vp->id]);
+ /*
+ * When we bind a window from layerM to layerN, we also need to move the old
+ * window on layerN to layerM to avoid one window selected by two or more layers.
+ */
+ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, 0x7);
+ layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id,
+ old_win->data->layer_sel_id[vp->id]);
+ }
+
+ vop2->old_layer_sel = layer_sel;
+ vop2->old_port_sel = port_sel;
+ /*
+ * As the RK3568_OVL_LAYER_SEL and RK3568_OVL_PORT_SEL are shared by all Video Ports,
+ * and the configuration take effect by one Video Port's vsync.
+ * When performing layer migration or change the zpos of layers, there are two things
+ * to be observed and followed:
+ * 1. When a layer is migrated from one VP to another, the configuration of the layer
+ * can only take effect after the Port mux configuration is enabled.
+ *
+ * 2. When we change the zpos of layers, we must ensure that the change for the previous
+ * VP takes effect before we proceed to change the next VP. Otherwise, the new
+ * configuration might overwrite the previous one for the previous VP, or it could
+ * lead to the configuration of the previous VP being take effect along with the VSYNC
+ * of the new VP.
+ */
+ if (layer_sel != old_layer_sel || port_sel != old_port_sel)
+ ovl_ctrl |= FIELD_PREP(RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL, vp->id);
+ vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl);
+
+ if (port_sel != old_port_sel) {
+ vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
+ vop2_cfg_done(vp);
+ rk3568_vop2_wait_for_port_mux_done(vop2);
+ }
+
+ if (layer_sel != old_layer_sel && atv_layer_sel != old_layer_sel)
+ rk3568_vop2_wait_for_layer_cfg_done(vop2, vop2->old_layer_sel);
+
+ vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
+ mutex_unlock(&vop2->ovl_lock);
+}
+
+static void rk3568_vop2_setup_dly_for_windows(struct vop2_video_port *vp)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct vop2_win *win;
+ int i = 0;
+ u32 cdly = 0, sdly = 0;
+
+ for (i = 0; i < vop2->data->win_size; i++) {
+ u32 dly;
+
+ win = &vop2->win[i];
+ dly = win->delay;
+
+ switch (win->data->phys_id) {
+ case ROCKCHIP_VOP2_CLUSTER0:
+ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
+ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
+ break;
+ case ROCKCHIP_VOP2_CLUSTER1:
+ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
+ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
+ break;
+ case ROCKCHIP_VOP2_ESMART0:
+ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
+ break;
+ case ROCKCHIP_VOP2_ESMART1:
+ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
+ break;
+ case ROCKCHIP_VOP2_SMART0:
+ case ROCKCHIP_VOP2_ESMART2:
+ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
+ break;
+ case ROCKCHIP_VOP2_SMART1:
+ case ROCKCHIP_VOP2_ESMART3:
+ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
+ break;
+ }
+ }
+
+ vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
+ vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
+}
+
+static void rk3568_vop2_setup_overlay(struct vop2_video_port *vp)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_plane *plane;
+
+ vp->win_mask = 0;
+
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+
+ win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+
+ vp->win_mask |= BIT(win->data->phys_id);
+
+ if (vop2_cluster_window(win))
+ vop2_setup_cluster_alpha(vop2, win);
+ }
+
+ if (!vp->win_mask)
+ return;
+
+ rk3568_vop2_setup_layer_mixer(vp);
+ vop2_setup_alpha(vp);
+ rk3568_vop2_setup_dly_for_windows(vp);
+}
+
+static void rk3576_vop2_setup_layer_mixer(struct vop2_video_port *vp)
+{
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_plane *plane;
+ u32 layer_sel = 0xffff; /* 0xf means this layer is disabled */
+ u32 ovl_ctrl;
+
+ ovl_ctrl = vop2_readl(vop2, RK3576_OVL_CTRL(vp->id));
+ if (vcstate->yuv_overlay)
+ ovl_ctrl |= RK3576_OVL_CTRL__YUV_MODE;
+ else
+ ovl_ctrl &= ~RK3576_OVL_CTRL__YUV_MODE;
+
+ vop2_writel(vop2, RK3576_OVL_CTRL(vp->id), ovl_ctrl);
+
+ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+
+ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos,
+ 0xf);
+ layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos,
+ win->data->layer_sel_id[vp->id]);
+ }
+
+ vop2_writel(vop2, RK3576_OVL_LAYER_SEL(vp->id), layer_sel);
+}
+
+static void rk3576_vop2_setup_dly_for_windows(struct vop2_video_port *vp)
+{
+ struct drm_plane *plane;
+ struct vop2_win *win;
+
+ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+ win = to_vop2_win(plane);
+ vop2_win_write(win, VOP2_WIN_DLY_NUM, 0);
+ }
+}
+
+static void rk3576_vop2_setup_overlay(struct vop2_video_port *vp)
+{
+ struct vop2 *vop2 = vp->vop2;
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_plane *plane;
+
+ vp->win_mask = 0;
+
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ struct vop2_win *win = to_vop2_win(plane);
+
+ win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+ vp->win_mask |= BIT(win->data->phys_id);
+
+ if (vop2_cluster_window(win))
+ vop2_setup_cluster_alpha(vop2, win);
+ }
+
+ if (!vp->win_mask)
+ return;
+
+ rk3576_vop2_setup_layer_mixer(vp);
+ vop2_setup_alpha(vp);
+ rk3576_vop2_setup_dly_for_windows(vp);
+}
+
+static void rk3568_vop2_setup_bg_dly(struct vop2_video_port *vp)
+{
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ u16 hdisplay = mode->crtc_hdisplay;
+ u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ u32 bg_dly;
+ u32 pre_scan_dly;
+
+ bg_dly = vp->data->pre_scan_max_dly[3];
+ vop2_writel(vp->vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
+ FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
+
+ pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
+ vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+}
+
+static void rk3576_vop2_setup_bg_dly(struct vop2_video_port *vp)
+{
+ struct drm_crtc *crtc = &vp->crtc;
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ u16 hdisplay = mode->crtc_hdisplay;
+ u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ u32 bg_dly;
+ u32 pre_scan_dly;
+
+ bg_dly = vp->data->pre_scan_max_dly[VOP2_DLY_WIN] +
+ vp->data->pre_scan_max_dly[VOP2_DLY_LAYER_MIX] +
+ vp->data->pre_scan_max_dly[VOP2_DLY_HDR_MIX];
+
+ vop2_writel(vp->vop2, RK3576_OVL_BG_MIX_CTRL(vp->id),
+ FIELD_PREP(RK3576_OVL_BG_MIX_CTRL__BG_DLY, bg_dly));
+
+ pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
+ vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+}
+
+static const struct vop2_ops rk3568_vop_ops = {
+ .setup_intf_mux = rk3568_set_intf_mux,
+ .setup_bg_dly = rk3568_vop2_setup_bg_dly,
+ .setup_overlay = rk3568_vop2_setup_overlay,
+};
+
+static const struct vop2_ops rk3576_vop_ops = {
+ .setup_intf_mux = rk3576_set_intf_mux,
+ .setup_bg_dly = rk3576_vop2_setup_bg_dly,
+ .setup_overlay = rk3576_vop2_setup_overlay,
+};
+
+static const struct vop2_ops rk3588_vop_ops = {
+ .setup_intf_mux = rk3588_set_intf_mux,
+ .setup_bg_dly = rk3568_vop2_setup_bg_dly,
+ .setup_overlay = rk3568_vop2_setup_overlay,
+};
+
static const struct vop2_data rk3566_vop = {
+ .version = VOP_VERSION_RK3568,
.feature = VOP2_FEATURE_HAS_SYS_GRF,
.nr_vps = 3,
.max_input = { 4096, 2304 },
@@ -470,10 +2466,18 @@ static const struct vop2_data rk3566_vop = {
.vp = rk3568_vop_video_ports,
.win = rk3568_vop_win_data,
.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+ .cluster_reg = rk3568_vop_cluster_regs,
+ .nr_cluster_regs = ARRAY_SIZE(rk3568_vop_cluster_regs),
+ .smart_reg = rk3568_vop_smart_regs,
+ .nr_smart_regs = ARRAY_SIZE(rk3568_vop_smart_regs),
+ .regs_dump = rk3568_regs_dump,
+ .regs_dump_size = ARRAY_SIZE(rk3568_regs_dump),
+ .ops = &rk3568_vop_ops,
.soc_id = 3566,
};
static const struct vop2_data rk3568_vop = {
+ .version = VOP_VERSION_RK3568,
.feature = VOP2_FEATURE_HAS_SYS_GRF,
.nr_vps = 3,
.max_input = { 4096, 2304 },
@@ -481,10 +2485,37 @@ static const struct vop2_data rk3568_vop = {
.vp = rk3568_vop_video_ports,
.win = rk3568_vop_win_data,
.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+ .cluster_reg = rk3568_vop_cluster_regs,
+ .nr_cluster_regs = ARRAY_SIZE(rk3568_vop_cluster_regs),
+ .smart_reg = rk3568_vop_smart_regs,
+ .nr_smart_regs = ARRAY_SIZE(rk3568_vop_smart_regs),
+ .regs_dump = rk3568_regs_dump,
+ .regs_dump_size = ARRAY_SIZE(rk3568_regs_dump),
+ .ops = &rk3568_vop_ops,
.soc_id = 3568,
};
+static const struct vop2_data rk3576_vop = {
+ .version = VOP_VERSION_RK3576,
+ .feature = VOP2_FEATURE_HAS_SYS_PMU,
+ .nr_vps = 3,
+ .max_input = { 4096, 4320 },
+ .max_output = { 4096, 4320 },
+ .vp = rk3576_vop_video_ports,
+ .win = rk3576_vop_win_data,
+ .win_size = ARRAY_SIZE(rk3576_vop_win_data),
+ .cluster_reg = rk3576_vop_cluster_regs,
+ .nr_cluster_regs = ARRAY_SIZE(rk3576_vop_cluster_regs),
+ .smart_reg = rk3576_vop_smart_regs,
+ .nr_smart_regs = ARRAY_SIZE(rk3576_vop_smart_regs),
+ .regs_dump = rk3576_regs_dump,
+ .regs_dump_size = ARRAY_SIZE(rk3576_regs_dump),
+ .ops = &rk3576_vop_ops,
+ .soc_id = 3576,
+};
+
static const struct vop2_data rk3588_vop = {
+ .version = VOP_VERSION_RK3588,
.feature = VOP2_FEATURE_HAS_SYS_GRF | VOP2_FEATURE_HAS_VO1_GRF |
VOP2_FEATURE_HAS_VOP_GRF | VOP2_FEATURE_HAS_SYS_PMU,
.nr_vps = 4,
@@ -493,6 +2524,13 @@ static const struct vop2_data rk3588_vop = {
.vp = rk3588_vop_video_ports,
.win = rk3588_vop_win_data,
.win_size = ARRAY_SIZE(rk3588_vop_win_data),
+ .cluster_reg = rk3568_vop_cluster_regs,
+ .nr_cluster_regs = ARRAY_SIZE(rk3568_vop_cluster_regs),
+ .smart_reg = rk3568_vop_smart_regs,
+ .nr_smart_regs = ARRAY_SIZE(rk3568_vop_smart_regs),
+ .regs_dump = rk3588_regs_dump,
+ .regs_dump_size = ARRAY_SIZE(rk3588_regs_dump),
+ .ops = &rk3588_vop_ops,
.soc_id = 3588,
};
@@ -504,6 +2542,9 @@ static const struct of_device_id vop2_dt_match[] = {
.compatible = "rockchip,rk3568-vop",
.data = &rk3568_vop,
}, {
+ .compatible = "rockchip,rk3576-vop",
+ .data = &rk3576_vop,
+ }, {
.compatible = "rockchip,rk3588-vop",
.data = &rk3588_vop
}, {
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 8998967f0c00..4e2099d86517 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index fbf1bcc68625..addf8ca085f6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author:Mark Yao <mark.yao@rock-chips.com>
*/