Allwinner: linux: Add 10-bit HDMI and Cedrus support

This commit is contained in:
Jernej Skrabec 2023-10-09 22:11:20 +02:00
parent 3855985fae
commit 6cddae6dee
29 changed files with 3567 additions and 226 deletions

View File

@ -4202,7 +4202,7 @@ CONFIG_DRM_PANEL_BRIDGE=y
#
# CONFIG_DRM_CHIPONE_ICN6211 is not set
# CONFIG_DRM_CHRONTEL_CH7033 is not set
# CONFIG_DRM_DISPLAY_CONNECTOR is not set
CONFIG_DRM_DISPLAY_CONNECTOR=y
# CONFIG_DRM_ITE_IT6505 is not set
# CONFIG_DRM_LONTIUM_LT8912B is not set
# CONFIG_DRM_LONTIUM_LT9211 is not set

View File

@ -3928,7 +3928,7 @@ CONFIG_DRM_PANEL_BRIDGE=y
#
# CONFIG_DRM_CHIPONE_ICN6211 is not set
# CONFIG_DRM_CHRONTEL_CH7033 is not set
CONFIG_DRM_DISPLAY_CONNECTOR=m
CONFIG_DRM_DISPLAY_CONNECTOR=y
# CONFIG_DRM_ITE_IT6505 is not set
# CONFIG_DRM_LONTIUM_LT8912B is not set
# CONFIG_DRM_LONTIUM_LT9211 is not set

View File

@ -27,7 +27,7 @@ new file mode 100644
index 000000000000..6a5df1103a90
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
@@ -0,0 +1,315 @@
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+// Copyright (C) 2023 Jernej Skrabec <jernej.skrabec@gmail.com>
+// Based on sun50i-h6-orangepi-3.dts, which is:
@ -37,6 +37,7 @@ index 000000000000..6a5df1103a90
+
+#include "sun50i-h6.dtsi"
+#include "sun50i-h6-cpu-opp.dtsi"
+#include "sun50i-h6-gpu-opp.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+

View File

@ -0,0 +1,75 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mingjia Zhang <mingjia.zhang@mediatek.com>
Date: Sat, 29 Jul 2023 11:41:10 +0800
Subject: [PATCH] media: mediatek: vcodec: Add capture format to support 10bit
tile mode
Define one uncompressed capture format V4L2_PIX_FMT_MT2110T in order to
support 10bit for AV1/VP9/HEVC in mt8195.
Signed-off-by: Mingjia Zhang <mingjia.zhang@mediatek.com>
Co-developed-by: Yunfei Dong <yunfei.dong@mediatek.com>
Signed-off-by: Yunfei Dong <yunfei.dong@mediatek.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Documentation/userspace-api/media/v4l/pixfmt-reserved.rst | 7 +++++++
drivers/media/v4l2-core/v4l2-common.c | 2 ++
drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
include/uapi/linux/videodev2.h | 1 +
4 files changed, 11 insertions(+)
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
index 58f6ae25b2e7..0bc69639baaa 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
@@ -275,6 +275,13 @@ please make a proposal on the linux-media mailing list.
Decoder's implementation can be found here,
`aspeed_codec <https://github.com/AspeedTech-BMC/aspeed_codec/>`__
+ * .. _V4L2-PIX-FMT-MT2110T:
+
+ - ``V4L2_PIX_FMT_MT2110T``
+ - 'MT2110T'
+ - This format is two-planar 10-Bit tile mode and having similitude with
+ ``V4L2_PIX_FMT_MM21`` in term of alignment and tiling. Used for VP9, AV1
+ and HEVC.
.. raw:: latex
\normalsize
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index bee1535b04d3..869fc09a210b 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -262,6 +262,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
/* YUV planar formats */
{ .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 01ba27f2ef87..f465c0e3d6e3 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1508,6 +1508,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break;
case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
+ case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break;
default:
if (fmt->description[0])
return;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 3af6a82d0cad..8c7d71afbdc7 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -796,6 +796,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_Z16 v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */
#define V4L2_PIX_FMT_MT21C v4l2_fourcc('M', 'T', '2', '1') /* Mediatek compressed block mode */
#define V4L2_PIX_FMT_MM21 v4l2_fourcc('M', 'M', '2', '1') /* Mediatek 8-bit block mode, two non-contiguous planes */
+#define V4L2_PIX_FMT_MT2110T v4l2_fourcc('M', 'T', '2', 'T') /* Mediatek 10-bit block tile mode */
#define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
#define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */

View File

@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mingjia Zhang <mingjia.zhang@mediatek.com>
Date: Sat, 29 Jul 2023 11:41:11 +0800
Subject: [PATCH] media: mediatek: vcodec: Add capture format to support 10bit
raster mode
Define one uncompressed capture format V4L2_PIX_FMT_MT2110R in order to
support 10bit for H264 in mt8195.
Signed-off-by: Mingjia Zhang <mingjia.zhang@mediatek.com>
Co-developed-by: Yunfei Dong <yunfei.dong@mediatek.com>
Signed-off-by: Yunfei Dong <yunfei.dong@mediatek.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Documentation/userspace-api/media/v4l/pixfmt-reserved.rst | 6 ++++++
drivers/media/v4l2-core/v4l2-common.c | 2 ++
drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
include/uapi/linux/videodev2.h | 1 +
4 files changed, 10 insertions(+)
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
index 0bc69639baaa..296ad2025e8d 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
@@ -282,6 +282,12 @@ please make a proposal on the linux-media mailing list.
- This format is two-planar 10-Bit tile mode and having similitude with
``V4L2_PIX_FMT_MM21`` in term of alignment and tiling. Used for VP9, AV1
and HEVC.
+ * .. _V4L2-PIX-FMT-MT2110R:
+
+ - ``V4L2_PIX_FMT_MT2110R``
+ - 'MT2110R'
+ - This format is two-planar 10-Bit raster mode and having similitude with
+ ``V4L2_PIX_FMT_MM21`` in term of alignment and tiling. Used for AVC.
.. raw:: latex
\normalsize
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 869fc09a210b..3a4b15a98e02 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -264,6 +264,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
.block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
+ { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
/* YUV planar formats */
{ .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index f465c0e3d6e3..f4d9d6279094 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1509,6 +1509,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break;
case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break;
+ case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break;
default:
if (fmt->description[0])
return;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 8c7d71afbdc7..78260e5d9985 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -797,6 +797,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_MT21C v4l2_fourcc('M', 'T', '2', '1') /* Mediatek compressed block mode */
#define V4L2_PIX_FMT_MM21 v4l2_fourcc('M', 'M', '2', '1') /* Mediatek 8-bit block mode, two non-contiguous planes */
#define V4L2_PIX_FMT_MT2110T v4l2_fourcc('M', 'T', '2', 'T') /* Mediatek 10-bit block tile mode */
+#define V4L2_PIX_FMT_MT2110R v4l2_fourcc('M', 'T', '2', 'R') /* Mediatek 10-bit block raster mode */
#define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
#define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */

View File

@ -1,131 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Fri, 14 Oct 2022 23:35:13 +0200
Subject: [PATCH] Revert "drm/sun4i: dw-hdmi: Fix ddc-en GPIO consumer
conflict"
This reverts commit 920169041baa0a7497ed702aa97d6a2d6285efd3.
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 54 +++++++++++++++++++++++++--
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 +
2 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 477cb6985b4d..a8d75fd7e9f4 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -93,10 +93,34 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
return crtcs;
}
+static int sun8i_dw_hdmi_find_connector_pdev(struct device *dev,
+ struct platform_device **pdev_out)
+{
+ struct platform_device *pdev;
+ struct device_node *remote;
+
+ remote = of_graph_get_remote_node(dev->of_node, 1, -1);
+ if (!remote)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(remote, "hdmi-connector")) {
+ of_node_put(remote);
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(remote);
+ of_node_put(remote);
+ if (!pdev)
+ return -ENODEV;
+
+ *pdev_out = pdev;
+ return 0;
+}
+
static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
+ struct platform_device *pdev = to_platform_device(dev), *connector_pdev;
struct dw_hdmi_plat_data *plat_data;
struct drm_device *drm = data;
struct device_node *phy_node;
@@ -143,16 +167,30 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
return dev_err_probe(dev, PTR_ERR(hdmi->regulator),
"Couldn't get regulator\n");
+ ret = sun8i_dw_hdmi_find_connector_pdev(dev, &connector_pdev);
+ if (!ret) {
+ hdmi->ddc_en = gpiod_get_optional(&connector_pdev->dev,
+ "ddc-en", GPIOD_OUT_HIGH);
+ platform_device_put(connector_pdev);
+
+ if (IS_ERR(hdmi->ddc_en)) {
+ dev_err(dev, "Couldn't get ddc-en gpio\n");
+ return PTR_ERR(hdmi->ddc_en);
+ }
+ }
+
ret = regulator_enable(hdmi->regulator);
if (ret) {
dev_err(dev, "Failed to enable regulator\n");
- return ret;
+ goto err_unref_ddc_en;
}
+ gpiod_set_value(hdmi->ddc_en, 1);
+
ret = reset_control_deassert(hdmi->rst_ctrl);
if (ret) {
dev_err(dev, "Could not deassert ctrl reset control\n");
- goto err_disable_regulator;
+ goto err_disable_ddc_en;
}
ret = clk_prepare_enable(hdmi->clk_tmds);
@@ -207,8 +245,12 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
clk_disable_unprepare(hdmi->clk_tmds);
err_assert_ctrl_reset:
reset_control_assert(hdmi->rst_ctrl);
-err_disable_regulator:
+err_disable_ddc_en:
+ gpiod_set_value(hdmi->ddc_en, 0);
regulator_disable(hdmi->regulator);
+err_unref_ddc_en:
+ if (hdmi->ddc_en)
+ gpiod_put(hdmi->ddc_en);
return ret;
}
@@ -222,7 +264,11 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master,
sun8i_hdmi_phy_deinit(hdmi->phy);
clk_disable_unprepare(hdmi->clk_tmds);
reset_control_assert(hdmi->rst_ctrl);
+ gpiod_set_value(hdmi->ddc_en, 0);
regulator_disable(hdmi->regulator);
+
+ if (hdmi->ddc_en)
+ gpiod_put(hdmi->ddc_en);
}
static const struct component_ops sun8i_dw_hdmi_ops = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index ab80d52a70bb..f082b8ecfe2c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -9,6 +9,7 @@
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_encoder.h>
#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -187,6 +188,7 @@ struct sun8i_dw_hdmi {
struct regulator *regulator;
const struct sun8i_dw_hdmi_quirks *quirks;
struct reset_control *rst_ctrl;
+ struct gpio_desc *ddc_en;
};
extern struct platform_driver sun8i_hdmi_phy_driver;

View File

@ -0,0 +1,31 @@
From 519b9e72303cabcb87f4d5b8792069ca20b7f13b Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 23 Sep 2023 10:23:45 +0200
Subject: [PATCH 01/23] drm/sun4i: dw-hdmi: Deinit PHY in fail path
Commit 9bf3797796f5 ("drm/sun4i: dw-hdmi: Make HDMI PHY into a platform
device") removed code for PHY deinitialization in fail path.
Add it back.
Fixes: 9bf3797796f5 ("drm/sun4i: dw-hdmi: Make HDMI PHY into a platform device")
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 4727dfaa8fb9..0b647b030b15 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -203,6 +203,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
cleanup_encoder:
drm_encoder_cleanup(encoder);
+ sun8i_hdmi_phy_deinit(hdmi->phy);
err_disable_clk_tmds:
clk_disable_unprepare(hdmi->clk_tmds);
err_assert_ctrl_reset:
--
2.42.0

View File

@ -0,0 +1,80 @@
From 9047008f1c8cebcc33cb068d9961fcfb8ff0eea7 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 11 Dec 2021 18:17:49 +0100
Subject: [PATCH 02/23] drm/sun4i: dw-hdmi: Remove double encoder cleanup
It turns out that comment is wrong - dw hdmi driver never does any
encoder cleanup. In fact, cleanup is done automatically, in destroy
callback of encoder. Even more, encoder memory will be freed when hdmi
device is destroyed. However, encoder will be cleaned up after that, in
drm_mode_config_cleanup(), which is called later. This will cause use
after free bug.
Remove redundant encoder cleanup, switch memory allocation to live as
long as drm object and while at it, check return code of encoder
initialization.
Fixes: b7c7436a5ff0 ("drm/sun4i: Implement A83T HDMI driver")
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 0b647b030b15..8f8d3bdba5ce 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -8,6 +8,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_simple_kms_helper.h>
@@ -107,7 +108,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
if (!pdev->dev.of_node)
return -ENODEV;
- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
@@ -180,7 +181,9 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_clk_tmds;
drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs);
- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ if (ret)
+ goto err_deinit_phy;
plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
@@ -189,20 +192,14 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
platform_set_drvdata(pdev, hdmi);
hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
-
- /*
- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
- * which would have called the encoder cleanup. Do it manually.
- */
if (IS_ERR(hdmi->hdmi)) {
ret = PTR_ERR(hdmi->hdmi);
- goto cleanup_encoder;
+ goto err_deinit_phy;
}
return 0;
-cleanup_encoder:
- drm_encoder_cleanup(encoder);
+err_deinit_phy:
sun8i_hdmi_phy_deinit(hdmi->phy);
err_disable_clk_tmds:
clk_disable_unprepare(hdmi->clk_tmds);
--
2.42.0

View File

@ -0,0 +1,213 @@
From 8d284097c6b9e7513e2946e17567aa490e19b779 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 23 Sep 2023 12:55:32 +0200
Subject: [PATCH 03/23] drm/sun4i: dw-hdmi: Switch to bridge functions
Since ddc-en property handling was moved from sun8i dw-hdmi driver to
display connector driver, probe order of drivers determines if EDID is
properly read at boot time or not.
In order to fix this, let's switch to bridge functions which allows us
to build proper chain and defer execution until all drivers are probed.
Fixes: 920169041baa ("drm/sun4i: dw-hdmi: Fix ddc-en GPIO consumer conflict")
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 114 +++++++++++++++++++++++++-
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 5 ++
2 files changed, 117 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 8f8d3bdba5ce..93831cdf1917 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -8,14 +8,82 @@
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_simple_kms_helper.h>
+#include <media/cec-notifier.h>
+
#include "sun8i_dw_hdmi.h"
#include "sun8i_tcon_top.h"
+#define bridge_to_sun8i_dw_hdmi(x) \
+ container_of(x, struct sun8i_dw_hdmi, enc_bridge)
+
+static int sun8i_hdmi_enc_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
+
+ return drm_bridge_attach(&hdmi->encoder, hdmi->hdmi_bridge,
+ &hdmi->enc_bridge, flags);
+}
+
+static void sun8i_hdmi_enc_detach(struct drm_bridge *bridge)
+{
+ struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
+
+ cec_notifier_conn_unregister(hdmi->cec_notifier);
+ hdmi->cec_notifier = NULL;
+}
+
+static void sun8i_hdmi_enc_hpd_notify(struct drm_bridge *bridge,
+ enum drm_connector_status status)
+{
+ struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
+ struct edid *edid;
+
+ if (!hdmi->cec_notifier)
+ return;
+
+ if (status == connector_status_connected) {
+ edid = drm_bridge_get_edid(hdmi->hdmi_bridge, hdmi->connector);
+ if (edid)
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier,
+ edid);
+ } else {
+ cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
+ }
+}
+
+static int sun8i_hdmi_enc_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_connector_state *old_conn_state =
+ drm_atomic_get_old_connector_state(conn_state->state,
+ conn_state->connector);
+
+ if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state))
+ crtc_state->mode_changed = true;
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs sun8i_hdmi_enc_bridge_funcs = {
+ .attach = sun8i_hdmi_enc_attach,
+ .detach = sun8i_hdmi_enc_detach,
+ .hpd_notify = sun8i_hdmi_enc_hpd_notify,
+ .atomic_check = sun8i_hdmi_enc_atomic_check,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+};
+
static void sun8i_dw_hdmi_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
@@ -99,6 +167,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_hdmi_plat_data *plat_data;
+ struct cec_connector_info conn_info;
+ struct drm_connector *connector;
struct drm_device *drm = data;
struct device_node *phy_node;
struct drm_encoder *encoder;
@@ -187,18 +257,57 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
+ plat_data->output_port = 1;
sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
platform_set_drvdata(pdev, hdmi);
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi->hdmi)) {
ret = PTR_ERR(hdmi->hdmi);
goto err_deinit_phy;
}
+ hdmi->hdmi_bridge = of_drm_find_bridge(dev->of_node);
+
+ hdmi->enc_bridge.funcs = &sun8i_hdmi_enc_bridge_funcs;
+ hdmi->enc_bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ hdmi->enc_bridge.interlace_allowed = true;
+
+ drm_bridge_add(&hdmi->enc_bridge);
+
+ ret = drm_bridge_attach(encoder, &hdmi->enc_bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
+ goto err_remove_dw_hdmi;
+
+ connector = drm_bridge_connector_init(drm, encoder);
+ if (IS_ERR(connector)) {
+ dev_err(dev, "Unable to create HDMI bridge connector\n");
+ ret = PTR_ERR(connector);
+ goto err_remove_dw_hdmi;
+ }
+
+ hdmi->connector = connector;
+ drm_connector_attach_encoder(connector, encoder);
+
+ if (hdmi->quirks->use_drm_infoframe)
+ drm_connector_attach_hdr_output_metadata_property(connector);
+
+ cec_fill_conn_info_from_drm(&conn_info, connector);
+
+ hdmi->cec_notifier = cec_notifier_conn_register(&pdev->dev, NULL,
+ &conn_info);
+ if (!hdmi->cec_notifier) {
+ ret = -ENOMEM;
+ goto err_remove_dw_hdmi;
+ }
+
return 0;
+err_remove_dw_hdmi:
+ drm_bridge_remove(&hdmi->enc_bridge);
+ dw_hdmi_remove(hdmi->hdmi);
err_deinit_phy:
sun8i_hdmi_phy_deinit(hdmi->phy);
err_disable_clk_tmds:
@@ -216,7 +325,8 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master,
{
struct sun8i_dw_hdmi *hdmi = dev_get_drvdata(dev);
- dw_hdmi_unbind(hdmi->hdmi);
+ drm_bridge_remove(&hdmi->enc_bridge);
+ dw_hdmi_remove(hdmi->hdmi);
sun8i_hdmi_phy_deinit(hdmi->phy);
clk_disable_unprepare(hdmi->clk_tmds);
reset_control_assert(hdmi->rst_ctrl);
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index ab80d52a70bb..18ffc1b4841f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -7,6 +7,7 @@
#define _SUN8I_DW_HDMI_H_
#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_encoder.h>
#include <linux/clk.h>
#include <linux/regmap.h>
@@ -178,9 +179,13 @@ struct sun8i_dw_hdmi_quirks {
};
struct sun8i_dw_hdmi {
+ struct cec_notifier *cec_notifier;
struct clk *clk_tmds;
+ struct drm_connector *connector;
struct device *dev;
+ struct drm_bridge enc_bridge;
struct dw_hdmi *hdmi;
+ struct drm_bridge *hdmi_bridge;
struct drm_encoder encoder;
struct sun8i_hdmi_phy *phy;
struct dw_hdmi_plat_data plat_data;
--
2.42.0

View File

@ -0,0 +1,30 @@
From 5cd81f7bbbd50529e8534986e3ea40276c4a26cc Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 23 Sep 2023 13:31:23 +0200
Subject: [PATCH 04/23] drm/sun4i: Don't show error for deferred probes.
Drivers probing in display pipeline can be deferred for many reasons.
Don't print error for such cases.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun4i_drv.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 6a8dfc022d3c..b4816a1b0be3 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -88,7 +88,8 @@ static int sun4i_drv_bind(struct device *dev)
ret = component_bind_all(drm->dev, drm);
if (ret) {
- dev_err(drm->dev, "Couldn't bind all pipelines components\n");
+ dev_err_probe(drm->dev, ret,
+ "Couldn't bind all pipelines components\n");
goto cleanup_mode_config;
}
--
2.42.0

View File

@ -0,0 +1,85 @@
From 873d6afcf4f64ac7380e7f18cd3604d47bea7570 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 23 Sep 2023 17:52:08 +0200
Subject: [PATCH 05/23] drm/sun4i: dw-hdmi: Make sun8i_hdmi_phy_get() more
intuitive
Let's make sun8i_hdmi_phy_get() to behave more like other kernel
functions and return phy pointer instead of setting field in struct.
This also makes function more universal.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 5 +++--
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 +-
drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 10 ++++------
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 93831cdf1917..50cffdbc4b5c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -239,10 +239,11 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_clk_tmds;
}
- ret = sun8i_hdmi_phy_get(hdmi, phy_node);
+ hdmi->phy = sun8i_hdmi_phy_get(phy_node);
of_node_put(phy_node);
- if (ret) {
+ if (IS_ERR(hdmi->phy)) {
dev_err(dev, "Couldn't get the HDMI PHY\n");
+ ret = PTR_ERR(hdmi->phy);
goto err_disable_clk_tmds;
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index 18ffc1b4841f..5383d9267a4d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -202,7 +202,7 @@ encoder_to_sun8i_dw_hdmi(struct drm_encoder *encoder)
return container_of(encoder, struct sun8i_dw_hdmi, encoder);
}
-int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node);
+struct sun8i_hdmi_phy *sun8i_hdmi_phy_get(struct device_node *node);
int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy);
void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy);
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 489ea94693ff..581233d6eaf2 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -650,25 +650,23 @@ static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
{ /* sentinel */ }
};
-int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
+struct sun8i_hdmi_phy *sun8i_hdmi_phy_get(struct device_node *node)
{
struct platform_device *pdev = of_find_device_by_node(node);
struct sun8i_hdmi_phy *phy;
if (!pdev)
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
phy = platform_get_drvdata(pdev);
if (!phy) {
put_device(&pdev->dev);
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
}
- hdmi->phy = phy;
-
put_device(&pdev->dev);
- return 0;
+ return phy;
}
static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
--
2.42.0

View File

@ -0,0 +1,97 @@
From c3f41c49cd55dbb6fa23472e35673875a09e2cb7 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 23 Sep 2023 18:07:07 +0200
Subject: [PATCH 06/23] drm/sun4i: dw-hdmi: check for phy device first
Let's check for phy device first. Since it uses much of the same clocks
and resets it also lowers amount of possible deferred probes.
While at it, don't report error for deferred phy probe.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 35 +++++++++++++--------------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 50cffdbc4b5c..22e084989ee6 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -173,11 +173,24 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
struct device_node *phy_node;
struct drm_encoder *encoder;
struct sun8i_dw_hdmi *hdmi;
+ struct sun8i_hdmi_phy *phy;
int ret;
if (!pdev->dev.of_node)
return -ENODEV;
+ phy_node = of_parse_phandle(dev->of_node, "phys", 0);
+ if (!phy_node) {
+ dev_err(dev, "Can't find PHY phandle\n");
+ return -EINVAL;
+ }
+
+ phy = sun8i_hdmi_phy_get(phy_node);
+ of_node_put(phy_node);
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy),
+ "Couldn't get the HDMI PHY\n");
+
hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
@@ -185,6 +198,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
plat_data = &hdmi->plat_data;
hdmi->dev = &pdev->dev;
encoder = &hdmi->encoder;
+ hdmi->phy = phy;
hdmi->quirks = of_device_get_match_data(dev);
@@ -232,22 +246,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
goto err_assert_ctrl_reset;
}
- phy_node = of_parse_phandle(dev->of_node, "phys", 0);
- if (!phy_node) {
- dev_err(dev, "Can't found PHY phandle\n");
- ret = -EINVAL;
- goto err_disable_clk_tmds;
- }
-
- hdmi->phy = sun8i_hdmi_phy_get(phy_node);
- of_node_put(phy_node);
- if (IS_ERR(hdmi->phy)) {
- dev_err(dev, "Couldn't get the HDMI PHY\n");
- ret = PTR_ERR(hdmi->phy);
- goto err_disable_clk_tmds;
- }
-
- ret = sun8i_hdmi_phy_init(hdmi->phy);
+ ret = sun8i_hdmi_phy_init(phy);
if (ret)
goto err_disable_clk_tmds;
@@ -259,7 +258,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
plat_data->output_port = 1;
- sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
+ sun8i_hdmi_phy_set_ops(phy, plat_data);
platform_set_drvdata(pdev, hdmi);
@@ -310,7 +309,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
drm_bridge_remove(&hdmi->enc_bridge);
dw_hdmi_remove(hdmi->hdmi);
err_deinit_phy:
- sun8i_hdmi_phy_deinit(hdmi->phy);
+ sun8i_hdmi_phy_deinit(phy);
err_disable_clk_tmds:
clk_disable_unprepare(hdmi->clk_tmds);
err_assert_ctrl_reset:
--
2.42.0

View File

@ -0,0 +1,176 @@
From 846aad8037db9e4503f89007c0d9a8e79d7fc816 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Tue, 18 Feb 2020 22:07:37 +0100
Subject: [PATCH 07/23] drm/sun4i: de2/de3: Change CSC argument
Currently, CSC module takes care only for converting YUV to RGB.
However, DE3 is more suited to work in YUV color space. Change CSC mode
argument to format type to be more neutral. New argument only tells
layer format type and doesn't imply output type.
This commit doesn't make any functional change.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_csc.c | 22 +++++++++++-----------
drivers/gpu/drm/sun4i/sun8i_csc.h | 10 +++++-----
drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 16 ++++++++--------
3 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 58480d8e4f70..6ebd1c3aa3ab 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -108,7 +108,7 @@ static const u32 yuv2rgb_de3[2][3][12] = {
};
static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
- enum sun8i_csc_mode mode,
+ enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range)
{
@@ -118,12 +118,12 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
table = yuv2rgb[range][encoding];
- switch (mode) {
- case SUN8I_CSC_MODE_YUV2RGB:
+ switch (fmt_type) {
+ case FORMAT_TYPE_YUV:
base_reg = SUN8I_CSC_COEFF(base, 0);
regmap_bulk_write(map, base_reg, table, 12);
break;
- case SUN8I_CSC_MODE_YVU2RGB:
+ case FORMAT_TYPE_YVU:
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
base_reg = SUN8I_CSC_COEFF(base, i + 1);
@@ -141,7 +141,7 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
}
static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
- enum sun8i_csc_mode mode,
+ enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range)
{
@@ -151,12 +151,12 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
table = yuv2rgb_de3[range][encoding];
- switch (mode) {
- case SUN8I_CSC_MODE_YUV2RGB:
+ switch (fmt_type) {
+ case FORMAT_TYPE_YUV:
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
regmap_bulk_write(map, addr, table, 12);
break;
- case SUN8I_CSC_MODE_YVU2RGB:
+ case FORMAT_TYPE_YVU:
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
@@ -206,7 +206,7 @@ static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable)
}
void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum sun8i_csc_mode mode,
+ enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range)
{
@@ -214,14 +214,14 @@ void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
if (mixer->cfg->is_de3) {
sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer,
- mode, encoding, range);
+ fmt_type, encoding, range);
return;
}
base = ccsc_base[mixer->cfg->ccsc][layer];
sun8i_csc_set_coefficients(mixer->engine.regs, base,
- mode, encoding, range);
+ fmt_type, encoding, range);
}
void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 828b86fd0cab..7322770f39f0 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -22,14 +22,14 @@ struct sun8i_mixer;
#define SUN8I_CSC_CTRL_EN BIT(0)
-enum sun8i_csc_mode {
- SUN8I_CSC_MODE_OFF,
- SUN8I_CSC_MODE_YUV2RGB,
- SUN8I_CSC_MODE_YVU2RGB,
+enum format_type {
+ FORMAT_TYPE_RGB,
+ FORMAT_TYPE_YUV,
+ FORMAT_TYPE_YVU,
};
void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum sun8i_csc_mode mode,
+ enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range);
void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable);
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index f9c0a56d3a14..76e2d3ec0a78 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -242,19 +242,19 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
return 0;
}
-static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
+static u32 sun8i_vi_layer_get_format_type(const struct drm_format_info *format)
{
if (!format->is_yuv)
- return SUN8I_CSC_MODE_OFF;
+ return FORMAT_TYPE_RGB;
switch (format->format) {
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YVU444:
- return SUN8I_CSC_MODE_YVU2RGB;
+ return FORMAT_TYPE_YVU;
default:
- return SUN8I_CSC_MODE_YUV2RGB;
+ return FORMAT_TYPE_YUV;
}
}
@@ -262,7 +262,7 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
int overlay, struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
- u32 val, ch_base, csc_mode, hw_fmt;
+ u32 val, ch_base, fmt_type, hw_fmt;
const struct drm_format_info *fmt;
int ret;
@@ -280,9 +280,9 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
- csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
- if (csc_mode != SUN8I_CSC_MODE_OFF) {
- sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
+ fmt_type = sun8i_vi_layer_get_format_type(fmt);
+ if (fmt_type != FORMAT_TYPE_RGB) {
+ sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_type,
state->color_encoding,
state->color_range);
sun8i_csc_enable_ccsc(mixer, channel, true);
--
2.42.0

View File

@ -0,0 +1,220 @@
From 9a4784752427c56839353f09446c5344a3b84641 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Tue, 18 Feb 2020 23:19:48 +0100
Subject: [PATCH 08/23] drm/sun4i: de2/de3: Merge CSC functions into one
Merging both function into one lets this one decide on it's own if CSC
should be enabled or not. Currently heuristics for that is pretty simple
- enable it for YUV formats and disable for RGB. However, DE3 can have
whole pipeline in RGB or YUV format. YUV pipeline will be supported in
later commits.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_csc.c | 89 ++++++++++----------------
drivers/gpu/drm/sun4i/sun8i_csc.h | 9 ++-
drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 11 +---
3 files changed, 40 insertions(+), 69 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 6ebd1c3aa3ab..0dcbc0866ae8 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -107,23 +107,28 @@ static const u32 yuv2rgb_de3[2][3][12] = {
},
};
-static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
- enum format_type fmt_type,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+static void sun8i_csc_setup(struct regmap *map, u32 base,
+ enum format_type fmt_type,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
{
+ u32 base_reg, val;
const u32 *table;
- u32 base_reg;
int i;
table = yuv2rgb[range][encoding];
switch (fmt_type) {
+ case FORMAT_TYPE_RGB:
+ val = 0;
+ break;
case FORMAT_TYPE_YUV:
+ val = SUN8I_CSC_CTRL_EN;
base_reg = SUN8I_CSC_COEFF(base, 0);
regmap_bulk_write(map, base_reg, table, 12);
break;
case FORMAT_TYPE_YVU:
+ val = SUN8I_CSC_CTRL_EN;
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
base_reg = SUN8I_CSC_COEFF(base, i + 1);
@@ -135,28 +140,37 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
}
break;
default:
+ val = 0;
DRM_WARN("Wrong CSC mode specified.\n");
return;
}
+
+ regmap_write(map, SUN8I_CSC_CTRL(base), val);
}
-static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
- enum format_type fmt_type,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+static void sun8i_de3_ccsc_setup(struct regmap *map, int layer,
+ enum format_type fmt_type,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
{
+ u32 addr, val, mask;
const u32 *table;
- u32 addr;
int i;
+ mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
table = yuv2rgb_de3[range][encoding];
switch (fmt_type) {
+ case FORMAT_TYPE_RGB:
+ val = 0;
+ break;
case FORMAT_TYPE_YUV:
+ val = mask;
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
regmap_bulk_write(map, addr, table, 12);
break;
case FORMAT_TYPE_YVU:
+ val = mask;
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
@@ -173,67 +187,30 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
}
break;
default:
+ val = 0;
DRM_WARN("Wrong CSC mode specified.\n");
return;
}
-}
-
-static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable)
-{
- u32 val;
-
- if (enable)
- val = SUN8I_CSC_CTRL_EN;
- else
- val = 0;
-
- regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val);
-}
-
-static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable)
-{
- u32 val, mask;
-
- mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
-
- if (enable)
- val = mask;
- else
- val = 0;
regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE),
mask, val);
}
-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum format_type fmt_type,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer,
+ enum format_type fmt_type,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
{
u32 base;
if (mixer->cfg->is_de3) {
- sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer,
- fmt_type, encoding, range);
+ sun8i_de3_ccsc_setup(mixer->engine.regs, layer,
+ fmt_type, encoding, range);
return;
}
base = ccsc_base[mixer->cfg->ccsc][layer];
- sun8i_csc_set_coefficients(mixer->engine.regs, base,
- fmt_type, encoding, range);
-}
-
-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
-{
- u32 base;
-
- if (mixer->cfg->is_de3) {
- sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable);
- return;
- }
-
- base = ccsc_base[mixer->cfg->ccsc][layer];
-
- sun8i_csc_enable(mixer->engine.regs, base, enable);
+ sun8i_csc_setup(mixer->engine.regs, base,
+ fmt_type, encoding, range);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 7322770f39f0..b7546e06e315 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -28,10 +28,9 @@ enum format_type {
FORMAT_TYPE_YVU,
};
-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum format_type fmt_type,
- enum drm_color_encoding encoding,
- enum drm_color_range range);
-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable);
+void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer,
+ enum format_type fmt_type,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range);
#endif
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 76e2d3ec0a78..6ee3790a2a81 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -281,14 +281,9 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
fmt_type = sun8i_vi_layer_get_format_type(fmt);
- if (fmt_type != FORMAT_TYPE_RGB) {
- sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_type,
- state->color_encoding,
- state->color_range);
- sun8i_csc_enable_ccsc(mixer, channel, true);
- } else {
- sun8i_csc_enable_ccsc(mixer, channel, false);
- }
+ sun8i_csc_set_ccsc(mixer, channel, fmt_type,
+ state->color_encoding,
+ state->color_range);
if (!fmt->is_yuv)
val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
--
2.42.0

View File

@ -0,0 +1,63 @@
From f2a3b64aec5e23c89f5ae5a5121a4226b8ad2ee9 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Wed, 19 Feb 2020 20:59:27 +0100
Subject: [PATCH 09/23] drm/sun4i: de2/de3: call csc setup also for UI layer
Currently, only VI layer calls CSC setup function. This comes from DE2
limitation, which doesn't have CSC unit for UI layers. However, DE3 has
separate CSC units for each layer. This allows display pipeline to make
output signal in different color spaces. To support both use cases, add
a call to CSC setup function also in UI layer code. For DE2, this will
be a no-op, but it will allow DE3 to output signal in multiple formats.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_csc.c | 8 +++++---
drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 6 ++++++
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 0dcbc0866ae8..68d955c63b05 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -209,8 +209,10 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer,
return;
}
- base = ccsc_base[mixer->cfg->ccsc][layer];
+ if (layer < mixer->cfg->vi_num) {
+ base = ccsc_base[mixer->cfg->ccsc][layer];
- sun8i_csc_setup(mixer->engine.regs, base,
- fmt_type, encoding, range);
+ sun8i_csc_setup(mixer->engine.regs, base,
+ fmt_type, encoding, range);
+ }
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index ca75ca0835a6..884abe3cf773 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -20,6 +20,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_probe_helper.h>
+#include "sun8i_csc.h"
#include "sun8i_mixer.h"
#include "sun8i_ui_layer.h"
#include "sun8i_ui_scaler.h"
@@ -184,6 +185,11 @@ static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+ /* Note: encoding and range arguments are ignored for RGB */
+ sun8i_csc_set_ccsc(mixer, channel, FORMAT_TYPE_RGB,
+ DRM_COLOR_YCBCR_BT601,
+ DRM_COLOR_YCBCR_FULL_RANGE);
+
return 0;
}
--
2.42.0

View File

@ -0,0 +1,80 @@
From c792bfdebef49c299a2043f7aecf4b44e376c332 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 18:00:44 +0000
Subject: [PATCH 10/23] drm/bridge: dw-hdmi: add mtmdsclock parameter to phy
configure ops
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 10 ++++++----
drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c | 3 ++-
include/drm/bridge/dw_hdmi.h | 3 ++-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 9721bdc2b7a3..c00fb616b587 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -124,7 +124,8 @@ struct dw_hdmi_phy_data {
bool has_svsret;
int (*configure)(struct dw_hdmi *hdmi,
const struct dw_hdmi_plat_data *pdata,
- unsigned long mpixelclock);
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock);
};
struct dw_hdmi {
@@ -1571,7 +1572,8 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
*/
static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_plat_data *pdata,
- unsigned long mpixelclock)
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock)
{
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
@@ -1648,9 +1650,9 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
/* Write to the PHY as configured by the platform */
if (pdata->configure_phy)
- ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
+ ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock, mtmdsclock);
else
- ret = phy->configure(hdmi, pdata, mpixelclock);
+ ret = phy->configure(hdmi, pdata, mpixelclock, mtmdsclock);
if (ret) {
dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
mpixelclock);
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
index 119d69d20b23..3d0e7f94ed77 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
@@ -53,7 +53,8 @@ rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
}
static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data,
- unsigned long mpixelclock)
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock)
{
const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 6a46baa0737c..4c0f850ce3c7 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -159,7 +159,8 @@ struct dw_hdmi_plat_data {
const struct dw_hdmi_curr_ctrl *cur_ctr;
const struct dw_hdmi_phy_config *phy_config;
int (*configure_phy)(struct dw_hdmi *hdmi, void *data,
- unsigned long mpixelclock);
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock);
unsigned int disable_cec : 1;
};
--
2.42.0

View File

@ -0,0 +1,63 @@
From 760dd4bfec9f57bb1260f7b3219054a2ad28ceb3 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 21:34:48 +0000
Subject: [PATCH 11/23] drm/bridge: dw-hdmi: support configuring phy for deep
color
Q: Should we rename dw_hdmi_curr_ctrl and dw_hdmi_phy_config mpixelclock to mtmdsclock ?
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index c00fb616b587..da2641a65e75 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1578,6 +1578,7 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+ int depth;
/* TOFIX Will need 420 specific PHY configuration tables */
@@ -1587,11 +1588,11 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
break;
for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
- if (mpixelclock <= curr_ctrl->mpixelclock)
+ if (mtmdsclock <= curr_ctrl->mpixelclock)
break;
for (; phy_config->mpixelclock != ~0UL; phy_config++)
- if (mpixelclock <= phy_config->mpixelclock)
+ if (mtmdsclock <= phy_config->mpixelclock)
break;
if (mpll_config->mpixelclock == ~0UL ||
@@ -1599,11 +1600,17 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
phy_config->mpixelclock == ~0UL)
return -EINVAL;
- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
+ depth = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+ if (depth > 8 && mpixelclock != mtmdsclock)
+ depth = fls(depth - 8) - 1;
+ else
+ depth = 0;
+
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
HDMI_3D_TX_PHY_CPCE_CTRL);
- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
HDMI_3D_TX_PHY_GMPCTRL);
- dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
HDMI_3D_TX_PHY_CURRCTRL);
dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
--
2.42.0

View File

@ -0,0 +1,211 @@
From e9a6924289b7ba7ca604272cbe52756f385d26df Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH 12/23] WIP: drm/bridge: dw-hdmi: limit mode and bus format to
max_tmds_clock
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 113 ++++++++++++++--------
1 file changed, 73 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index da2641a65e75..9c8c17827956 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1989,6 +1989,21 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
}
+static unsigned int
+hdmi_get_tmdsclock(unsigned int bus_format, unsigned int pixelclock)
+{
+ int color_depth = hdmi_bus_fmt_color_depth(bus_format);
+ unsigned int tmdsclock = pixelclock;
+
+ if (!hdmi_bus_fmt_is_yuv422(bus_format) && color_depth > 8)
+ tmdsclock = (u64)pixelclock * color_depth / 8;
+
+ if (hdmi_bus_fmt_is_yuv420(bus_format))
+ tmdsclock /= 2;
+
+ return tmdsclock;
+}
+
static void hdmi_av_composer(struct dw_hdmi *hdmi,
const struct drm_display_info *display,
const struct drm_display_mode *mode)
@@ -2000,29 +2015,11 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
unsigned int vdisplay, hdisplay;
vmode->mpixelclock = mode->clock * 1000;
+ vmode->mtmdsclock =
+ hdmi_get_tmdsclock(hdmi->hdmi_data.enc_out_bus_format,
+ vmode->mpixelclock);
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
- vmode->mtmdsclock = vmode->mpixelclock;
-
- if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
- switch (hdmi_bus_fmt_color_depth(
- hdmi->hdmi_data.enc_out_bus_format)) {
- case 16:
- vmode->mtmdsclock = vmode->mpixelclock * 2;
- break;
- case 12:
- vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
- break;
- case 10:
- vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
- break;
- }
- }
-
- if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
- vmode->mtmdsclock /= 2;
-
dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
/* Set up HDMI_FC_INVIDCONF */
@@ -2646,8 +2643,21 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
* - MEDIA_BUS_FMT_RGB888_1X24,
*/
-/* Can return a maximum of 11 possible output formats for a mode/connector */
-#define MAX_OUTPUT_SEL_FORMATS 11
+/* Can return a maximum of 15 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 15
+
+static bool is_tmds_allowed(struct drm_display_info *info,
+ struct drm_display_mode *mode,
+ u32 bus_format)
+{
+ unsigned long tmdsclock = hdmi_get_tmdsclock(bus_format, mode->clock);
+ int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+
+ if (max_tmds_clock >= tmdsclock)
+ return true;
+
+ return false;
+}
static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -2690,19 +2700,23 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
/* Order bus formats from 16bit to 8bit if supported */
if (max_bpc >= 16 && info->bpc == 16 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY16_0_5X48))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
if (max_bpc >= 12 && info->bpc >= 12 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY12_0_5X36))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
if (max_bpc >= 10 && info->bpc >= 10 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY10_0_5X30))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
/* Default 8bit fallback */
- output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY8_0_5X24))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
if (drm_mode_is_420_only(info, mode)) {
*num_output_fmts = i;
@@ -2715,42 +2729,52 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
* if supported. In any case the default RGB888 format is added
*/
- /* Default 8bit RGB fallback */
- output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
-
if (max_bpc >= 16 && info->bpc == 16) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV16_1X48))
output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB161616_1X48))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
}
if (max_bpc >= 12 && info->bpc >= 12) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY12_1X24))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV12_1X36))
output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB121212_1X36))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
}
if (max_bpc >= 10 && info->bpc >= 10) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY10_1X20))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV10_1X30))
output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB101010_1X30))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
}
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY8_1X16))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
- if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV8_1X24))
output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ /* Default 8bit RGB fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+
*num_output_fmts = i;
return output_fmts;
@@ -2930,11 +2954,20 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
struct dw_hdmi *hdmi = bridge->driver_private;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
+ int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+ int clock = mode->clock;
/* We don't support double-clocked modes */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
+ if (pdata->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+ (info->color_formats & DRM_COLOR_FORMAT_YCBCR420))
+ clock /= 2;
+
+ if (clock > max_tmds_clock)
+ return MODE_CLOCK_HIGH;
+
if (pdata->mode_valid)
mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
mode);
--
2.42.0

View File

@ -0,0 +1,886 @@
From d8126b4e7ec771a5be4ab9513aed390461a70d3d Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sun, 24 Sep 2023 13:20:12 +0200
Subject: [PATCH 13/23] WIP: drm/sun4i: de3: Add support for YUV420 output
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 24 ++-
drivers/gpu/drm/drm_atomic_state_helper.c | 7 +
drivers/gpu/drm/sun4i/Makefile | 3 +-
drivers/gpu/drm/sun4i/sun4i_tcon.c | 26 +++-
drivers/gpu/drm/sun4i/sun50i_fmt.c | 74 ++++++++++
drivers/gpu/drm/sun4i/sun50i_fmt.h | 30 ++++
drivers/gpu/drm/sun4i/sun8i_csc.c | 172 +++++++++++++++++++++-
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 97 +++++++++++-
drivers/gpu/drm/sun4i/sun8i_mixer.c | 51 ++++++-
drivers/gpu/drm/sun4i/sun8i_mixer.h | 2 +
drivers/gpu/drm/sun4i/sunxi_engine.h | 48 ++++++
11 files changed, 509 insertions(+), 25 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun50i_fmt.c
create mode 100644 drivers/gpu/drm/sun4i/sun50i_fmt.h
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 9c8c17827956..8b4b4d45c1c9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1019,19 +1019,15 @@ static void hdmi_video_sample(struct dw_hdmi *hdmi)
color_format = 0x07;
break;
- case MEDIA_BUS_FMT_YUV8_1X24:
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
color_format = 0x09;
break;
- case MEDIA_BUS_FMT_YUV10_1X30:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
color_format = 0x0B;
break;
- case MEDIA_BUS_FMT_YUV12_1X36:
case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
color_format = 0x0D;
break;
- case MEDIA_BUS_FMT_YUV16_1X48:
case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
color_format = 0x0F;
break;
@@ -1046,6 +1042,19 @@ static void hdmi_video_sample(struct dw_hdmi *hdmi)
color_format = 0x12;
break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ color_format = 0x17;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ color_format = 0x19;
+ break;
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ color_format = 0x1B;
+ break;
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ color_format = 0x1D;
+ break;
+
default:
return;
}
@@ -1165,7 +1174,7 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi)
if (is_color_space_interpolation(hdmi))
interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
else if (is_color_space_decimation(hdmi))
- decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+ decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1;
switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
case 8:
@@ -1207,7 +1216,6 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
u8 val, vp_conf;
u8 clear_gcp_auto = 0;
-
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
@@ -1803,7 +1811,9 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
frame.colorspace = HDMI_COLORSPACE_RGB;
/* Set up colorimetry */
- if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
+ if (connector->colorspace_property) {
+ drm_hdmi_avi_infoframe_colorimetry(&frame, connector->state);
+ } else if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
switch (hdmi->hdmi_data.enc_out_encoding) {
case V4L2_YCBCR_ENC_601:
if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 784e63d70a42..1f9cf2bc6445 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -415,7 +415,14 @@ void
__drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state,
struct drm_connector *connector)
{
+ struct drm_property *prop;
+
conn_state->connector = connector;
+ prop = connector->max_bpc_property;
+ if (prop) {
+ conn_state->max_bpc = prop->values[1];
+ conn_state->max_requested_bpc = prop->values[1];
+ }
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset);
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index bad7497a0d11..3f516329f51e 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -16,7 +16,8 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o
sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \
sun8i_vi_layer.o sun8i_ui_scaler.o \
- sun8i_vi_scaler.o sun8i_csc.o
+ sun8i_vi_scaler.o sun8i_csc.o \
+ sun50i_fmt.o
sun4i-tcon-y += sun4i_crtc.o
sun4i-tcon-y += sun4i_tcon_dclk.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index a1a2c845ade0..e39926e9f0b5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -598,14 +598,26 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
const struct drm_display_mode *mode)
{
- unsigned int bp, hsync, vsync, vtotal;
+ unsigned int bp, hsync, vsync, vtotal, div;
+ struct sun4i_crtc *scrtc = tcon->crtc;
+ struct sunxi_engine *engine = scrtc->engine;
u8 clk_delay;
u32 val;
WARN_ON(!tcon->quirks->has_channel_1);
+ switch (engine->format) {
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ div = 2;
+ break;
+ default:
+ div = 1;
+ break;
+ }
+
/* Configure the dot clock */
- clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
+ clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000 / div);
/* Adjust clock delay */
clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
@@ -624,17 +636,17 @@ static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
/* Set the input resolution */
regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,
- SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay / div) |
SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));
/* Set the upscaling resolution */
regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,
- SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay / div) |
SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));
/* Set the output resolution */
regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,
- SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay / div) |
SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
/* Set horizontal display timings */
@@ -642,8 +654,8 @@ static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
mode->htotal, bp);
regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
- SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
- SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
+ SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal / div) |
+ SUN4I_TCON1_BASIC3_H_BACKPORCH(bp / div));
bp = mode->crtc_vtotal - mode->crtc_vsync_start;
DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.c b/drivers/gpu/drm/sun4i/sun50i_fmt.c
new file mode 100644
index 000000000000..18a8d5032ddc
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_fmt.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <uapi/linux/media-bus-format.h>
+
+#include "sun50i_fmt.h"
+
+static bool sun50i_fmt_is_10bit(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static u32 sun50i_fmt_get_colorspace(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ return SUN50I_FMT_CS_YUV420;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ return SUN50I_FMT_CS_YUV422;
+ default:
+ return SUN50I_FMT_CS_YUV444RGB;
+ }
+}
+
+void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width,
+ u16 height, u32 format)
+{
+ u32 colorspace, limit[3];
+ bool bit10;
+
+ colorspace = sun50i_fmt_get_colorspace(format);
+ bit10 = sun50i_fmt_is_10bit(format);
+
+ regmap_write(mixer->engine.regs, SUN50I_FMT_CTRL, 0);
+
+ regmap_write(mixer->engine.regs, SUN50I_FMT_SIZE,
+ SUN8I_MIXER_SIZE(width, height));
+ regmap_write(mixer->engine.regs, SUN50I_FMT_SWAP, 0);
+ regmap_write(mixer->engine.regs, SUN50I_FMT_DEPTH, bit10);
+ regmap_write(mixer->engine.regs, SUN50I_FMT_FORMAT, colorspace);
+ regmap_write(mixer->engine.regs, SUN50I_FMT_COEF, 0);
+
+ if (colorspace != SUN50I_FMT_CS_YUV444RGB) {
+ limit[0] = SUN50I_FMT_LIMIT(64, 940);
+ limit[1] = SUN50I_FMT_LIMIT(64, 960);
+ limit[2] = SUN50I_FMT_LIMIT(64, 960);
+ } else if (bit10) {
+ limit[0] = SUN50I_FMT_LIMIT(0, 1023);
+ limit[1] = SUN50I_FMT_LIMIT(0, 1023);
+ limit[2] = SUN50I_FMT_LIMIT(0, 1023);
+ } else {
+ limit[0] = SUN50I_FMT_LIMIT(0, 1021);
+ limit[1] = SUN50I_FMT_LIMIT(0, 1021);
+ limit[2] = SUN50I_FMT_LIMIT(0, 1021);
+ }
+
+ regmap_write(mixer->engine.regs, SUN50I_FMT_LMT_Y, limit[0]);
+ regmap_write(mixer->engine.regs, SUN50I_FMT_LMT_C0, limit[1]);
+ regmap_write(mixer->engine.regs, SUN50I_FMT_LMT_C1, limit[2]);
+
+ regmap_write(mixer->engine.regs, SUN50I_FMT_CTRL, 1);
+}
diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.h b/drivers/gpu/drm/sun4i/sun50i_fmt.h
new file mode 100644
index 000000000000..0fa1d2d22e59
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_fmt.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#ifndef _SUN50I_FMT_H_
+#define _SUN50I_FMT_H_
+
+#include "sun8i_mixer.h"
+
+#define SUN50I_FMT_CTRL 0xa8000
+#define SUN50I_FMT_SIZE 0xa8004
+#define SUN50I_FMT_SWAP 0xa8008
+#define SUN50I_FMT_DEPTH 0xa800c
+#define SUN50I_FMT_FORMAT 0xa8010
+#define SUN50I_FMT_COEF 0xa8014
+#define SUN50I_FMT_LMT_Y 0xa8020
+#define SUN50I_FMT_LMT_C0 0xa8024
+#define SUN50I_FMT_LMT_C1 0xa8028
+
+#define SUN50I_FMT_LIMIT(low, high) (((high) << 16) | (low))
+
+#define SUN50I_FMT_CS_YUV444RGB 0
+#define SUN50I_FMT_CS_YUV422 1
+#define SUN50I_FMT_CS_YUV420 2
+
+void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width,
+ u16 height, u32 format);
+
+#endif
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 68d955c63b05..3b022bfb85ad 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -5,6 +5,8 @@
#include <drm/drm_print.h>
+#include <uapi/linux/media-bus-format.h>
+
#include "sun8i_csc.h"
#include "sun8i_mixer.h"
@@ -107,12 +109,141 @@ static const u32 yuv2rgb_de3[2][3][12] = {
},
};
+/* always convert to limited mode */
+static const u32 rgb2yuv_de3[3][12] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x0000837A, 0x0001021D, 0x00003221, 0x00000040,
+ 0xFFFFB41C, 0xFFFF6B03, 0x0000E0E1, 0x00000200,
+ 0x0000E0E1, 0xFFFF43B1, 0xFFFFDB6E, 0x00000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x00005D7C, 0x00013A7C, 0x00001FBF, 0x00000040,
+ 0xFFFFCC78, 0xFFFF52A7, 0x0000E0E1, 0x00000200,
+ 0x0000E0E1, 0xFFFF33BE, 0xFFFFEB61, 0x00000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x00007384, 0x00012A21, 0x00001A13, 0x00000040,
+ 0xFFFFC133, 0xFFFF5DEC, 0x0000E0E1, 0x00000200,
+ 0x0000E0E1, 0xFFFF3135, 0xFFFFEDEA, 0x00000200,
+ },
+};
+
+/* always convert to limited mode */
+static const u32 yuv2yuv_de3[2][3][3][12] = {
+ [DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x00020000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00020000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00020000, 0x00000000,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x00020000, 0xFFFFC4D7, 0xFFFF9589, 0xFFC00040,
+ 0x00000000, 0x0002098B, 0x00003AAF, 0xFE000200,
+ 0x00000000, 0x0000266D, 0x00020CF8, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x00020000, 0xFFFFBFCE, 0xFFFFC5FF, 0xFFC00040,
+ 0x00000000, 0x00020521, 0x00001F89, 0xFE000200,
+ 0x00000000, 0x00002C87, 0x00020F07, 0xFE000200,
+ },
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x00020000, 0x000032D9, 0x00006226, 0xFFC00040,
+ 0x00000000, 0x0001FACE, 0xFFFFC759, 0xFE000200,
+ 0x00000000, 0xFFFFDAE7, 0x0001F780, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x00020000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00020000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00020000, 0x00000000,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x00020000, 0xFFFFF782, 0x00003036, 0xFFC00040,
+ 0x00000000, 0x0001FD99, 0xFFFFE5CA, 0xFE000200,
+ 0x00000000, 0x000005E4, 0x0002015A, 0xFE000200,
+ },
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x00020000, 0x00003B03, 0x000034D2, 0xFFC00040,
+ 0x00000000, 0x0001FD8C, 0xFFFFE183, 0xFE000200,
+ 0x00000000, 0xFFFFD4F3, 0x0001F3FA, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x00020000, 0x00000916, 0xFFFFD061, 0xFFC00040,
+ 0x00000000, 0x0002021C, 0x00001A40, 0xFE000200,
+ 0x00000000, 0xFFFFFA19, 0x0001FE5A, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x00020000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00020000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00020000, 0x00000000,
+ },
+ },
+ },
+ [DRM_COLOR_YCBCR_FULL_RANGE] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040,
+ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200,
+ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x0001B7B8, 0xFFFFCC08, 0xFFFFA27B, 0x00000040,
+ 0x00000000, 0x0001CA24, 0x0000338D, 0xFE000200,
+ 0x00000000, 0x000021C1, 0x0001CD26, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x0001B7B8, 0xFFFFC79C, 0xFFFFCD0C, 0x00000040,
+ 0x00000000, 0x0001C643, 0x00001BB4, 0xFE000200,
+ 0x00000000, 0x0000271D, 0x0001CEF5, 0xFE000200,
+ },
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x0001B7B8, 0x00002CAB, 0x00005638, 0x00000040,
+ 0x00000000, 0x0001BD32, 0xFFFFCE3C, 0xFE000200,
+ 0x00000000, 0xFFFFDF6A, 0x0001BA4A, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040,
+ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200,
+ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x0001B7B8, 0xFFFFF88A, 0x00002A5A, 0x00000040,
+ 0x00000000, 0x0001BFA5, 0xFFFFE8FA, 0xFE000200,
+ 0x00000000, 0x0000052D, 0x0001C2F1, 0xFE000200,
+ },
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ [DRM_COLOR_YCBCR_BT601] = {
+ 0x0001B7B8, 0x000033D6, 0x00002E66, 0x00000040,
+ 0x00000000, 0x0001BF9A, 0xFFFFE538, 0xFE000200,
+ 0x00000000, 0xFFFFDA2F, 0x0001B732, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT709] = {
+ 0x0001B7B8, 0x000007FB, 0xFFFFD62B, 0x00000040,
+ 0x00000000, 0x0001C39D, 0x0000170F, 0xFE000200,
+ 0x00000000, 0xFFFFFAD1, 0x0001C04F, 0xFE000200,
+ },
+ [DRM_COLOR_YCBCR_BT2020] = {
+ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040,
+ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200,
+ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200,
+ },
+ },
+ },
+};
+
static void sun8i_csc_setup(struct regmap *map, u32 base,
enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range)
{
- u32 base_reg, val;
+ u32 base_reg, val = 0;
const u32 *table;
int i;
@@ -148,28 +279,59 @@ static void sun8i_csc_setup(struct regmap *map, u32 base,
regmap_write(map, SUN8I_CSC_CTRL(base), val);
}
-static void sun8i_de3_ccsc_setup(struct regmap *map, int layer,
+static const u32 *sun8i_csc_get_de3_yuv_table(enum drm_color_encoding in_enc,
+ enum drm_color_range in_range,
+ u32 out_format,
+ enum drm_color_encoding out_enc)
+{
+ if (out_format == MEDIA_BUS_FMT_RGB888_1X24)
+ return yuv2rgb_de3[in_range][in_enc];
+
+ /* check for identity transformation */
+ if (in_range == DRM_COLOR_YCBCR_LIMITED_RANGE && out_enc == in_enc)
+ return NULL;
+
+ return yuv2yuv_de3[in_range][in_enc][out_enc];
+}
+
+static void sun8i_de3_ccsc_setup(struct sunxi_engine *engine, int layer,
enum format_type fmt_type,
enum drm_color_encoding encoding,
enum drm_color_range range)
{
- u32 addr, val, mask;
+ u32 addr, val = 0, mask;
+ struct regmap *map;
const u32 *table;
int i;
mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
table = yuv2rgb_de3[range][encoding];
+ map = engine->regs;
switch (fmt_type) {
case FORMAT_TYPE_RGB:
- val = 0;
+ if (engine->format == MEDIA_BUS_FMT_RGB888_1X24)
+ break;
+ val = mask;
+ addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
+ regmap_bulk_write(map, addr, rgb2yuv_de3[engine->encoding], 12);
break;
case FORMAT_TYPE_YUV:
+ table = sun8i_csc_get_de3_yuv_table(encoding, range,
+ engine->format,
+ engine->encoding);
+ if (!table)
+ break;
val = mask;
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
regmap_bulk_write(map, addr, table, 12);
break;
case FORMAT_TYPE_YVU:
+ table = sun8i_csc_get_de3_yuv_table(encoding, range,
+ engine->format,
+ engine->encoding);
+ if (!table)
+ table = yuv2yuv_de3[range][encoding][encoding];
val = mask;
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
@@ -204,7 +366,7 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer,
u32 base;
if (mixer->cfg->is_de3) {
- sun8i_de3_ccsc_setup(mixer->engine.regs, layer,
+ sun8i_de3_ccsc_setup(&mixer->engine, layer,
fmt_type, encoding, range);
return;
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 22e084989ee6..7309590feb56 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/videodev2.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge_connector.h>
@@ -17,8 +18,10 @@
#include <media/cec-notifier.h>
+#include "sun4i_crtc.h"
#include "sun8i_dw_hdmi.h"
#include "sun8i_tcon_top.h"
+#include "sunxi_engine.h"
#define bridge_to_sun8i_dw_hdmi(x) \
container_of(x, struct sun8i_dw_hdmi, enc_bridge)
@@ -64,16 +67,89 @@ static int sun8i_hdmi_enc_atomic_check(struct drm_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
- struct drm_connector_state *old_conn_state =
+ struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(crtc_state->crtc);
+ struct sunxi_engine *engine = crtc->engine;
+ struct drm_connector_state *old_conn_state;
+ enum drm_color_encoding encoding;
+
+ old_conn_state =
drm_atomic_get_old_connector_state(conn_state->state,
conn_state->connector);
+ switch (conn_state->colorspace) {
+ case DRM_MODE_COLORIMETRY_SMPTE_170M_YCC:
+ case DRM_MODE_COLORIMETRY_XVYCC_601:
+ case DRM_MODE_COLORIMETRY_SYCC_601:
+ case DRM_MODE_COLORIMETRY_OPYCC_601:
+ case DRM_MODE_COLORIMETRY_BT601_YCC:
+ encoding = DRM_COLOR_YCBCR_BT601;
+ break;
+
+ default:
+ case DRM_MODE_COLORIMETRY_NO_DATA:
+ case DRM_MODE_COLORIMETRY_BT709_YCC:
+ case DRM_MODE_COLORIMETRY_XVYCC_709:
+ case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED:
+ case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT:
+ encoding = DRM_COLOR_YCBCR_BT709;
+ break;
+
+ case DRM_MODE_COLORIMETRY_BT2020_CYCC:
+ case DRM_MODE_COLORIMETRY_BT2020_YCC:
+ case DRM_MODE_COLORIMETRY_BT2020_RGB:
+ case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65:
+ case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER:
+ encoding = DRM_COLOR_YCBCR_BT2020;
+ break;
+ }
+
+ if (crtc->engine->format != bridge_state->output_bus_cfg.format)
+ crtc_state->mode_changed = true;
+
+ sunxi_engine_set_format(engine, bridge_state->output_bus_cfg.format,
+ encoding);
+
if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state))
crtc_state->mode_changed = true;
return 0;
}
+static u32 *
+sun8i_hdmi_enc_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(crtc_state->crtc);
+ u32 *input_fmt, *supported, count, i;
+
+ *num_input_fmts = 0;
+ input_fmt = NULL;
+
+ supported = sunxi_engine_get_supported_formats(crtc->engine, &count);
+ if (count == 0 || !supported)
+ return NULL;
+
+ for (i = 0; i < count; i++)
+ if (output_fmt == supported[i]) {
+ input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
+ if (!input_fmt)
+ break;
+
+ *num_input_fmts = 1;
+ *input_fmt = output_fmt;
+
+ break;
+ }
+
+ kfree(supported);
+
+ return input_fmt;
+}
+
static const struct drm_bridge_funcs sun8i_hdmi_enc_bridge_funcs = {
.attach = sun8i_hdmi_enc_attach,
.detach = sun8i_hdmi_enc_detach,
@@ -81,6 +157,7 @@ static const struct drm_bridge_funcs sun8i_hdmi_enc_bridge_funcs = {
.atomic_check = sun8i_hdmi_enc_atomic_check,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = sun8i_hdmi_enc_get_input_bus_fmts,
.atomic_reset = drm_atomic_helper_bridge_reset,
};
@@ -114,6 +191,11 @@ sun8i_dw_hdmi_mode_valid_h6(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
+ unsigned long clock = mode->crtc_clock * 1000;
+
+ if (drm_mode_is_420(info, mode))
+ clock /= 2;
+
/*
* Controller support maximum of 594 MHz, which correlates to
* 4K@60Hz 4:4:4 or RGB.
@@ -257,6 +339,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
+ plat_data->ycbcr_420_allowed = hdmi->quirks->use_drm_infoframe;
+ plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
plat_data->output_port = 1;
sun8i_hdmi_phy_set_ops(phy, plat_data);
@@ -291,8 +375,17 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
hdmi->connector = connector;
drm_connector_attach_encoder(connector, encoder);
- if (hdmi->quirks->use_drm_infoframe)
+ drm_atomic_helper_connector_reset(connector);
+
+ drm_mode_create_hdmi_colorspace_property(connector, 0);
+
+ if (hdmi->quirks->use_drm_infoframe) {
drm_connector_attach_hdr_output_metadata_property(connector);
+ drm_connector_attach_max_bpc_property(connector, 8, 12);
+ drm_connector_attach_colorspace_property(connector);
+ }
+
+ connector->ycbcr_420_allowed = hdmi->quirks->use_drm_infoframe;
cec_fill_conn_info_from_drm(&conn_info, connector);
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 01382860aaee..8e32d0b24ac9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -22,7 +22,10 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_probe_helper.h>
+#include <uapi/linux/media-bus-format.h>
+
#include "sun4i_drv.h"
+#include "sun50i_fmt.h"
#include "sun8i_mixer.h"
#include "sun8i_ui_layer.h"
#include "sun8i_vi_layer.h"
@@ -326,12 +329,51 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
interlaced ? "on" : "off");
+
+ if (engine->format == MEDIA_BUS_FMT_RGB888_1X24)
+ val = SUN8I_MIXER_BLEND_COLOR_BLACK;
+ else
+ val = 0xff108080;
+
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_BLEND_BKCOLOR(bld_base), val);
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_BLEND_ATTR_FCOLOR(bld_base, 0), val);
+
+ if (mixer->cfg->has_formatter)
+ sun50i_fmt_setup(mixer, mode->hdisplay,
+ mode->vdisplay, mixer->engine.format);
+}
+
+static u32 *sun8i_mixer_get_supported_fmts(struct sunxi_engine *engine, u32 *num)
+{
+ struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+ u32 *formats, count;
+
+ count = 0;
+
+ formats = kcalloc(8, sizeof(*formats), GFP_KERNEL);
+ if (!formats)
+ return NULL;
+
+ if (mixer->cfg->has_formatter) {
+ formats[count++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ formats[count++] = MEDIA_BUS_FMT_YUV8_1X24;
+ formats[count++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ }
+
+ formats[count++] = MEDIA_BUS_FMT_RGB888_1X24;
+
+ *num = count;
+
+ return formats;
}
static const struct sunxi_engine_ops sun8i_engine_ops = {
- .commit = sun8i_mixer_commit,
- .layers_init = sun8i_layers_init,
- .mode_set = sun8i_mixer_mode_set,
+ .commit = sun8i_mixer_commit,
+ .layers_init = sun8i_layers_init,
+ .mode_set = sun8i_mixer_mode_set,
+ .get_supported_fmts = sun8i_mixer_get_supported_fmts,
};
static const struct regmap_config sun8i_mixer_regmap_config = {
@@ -392,6 +434,8 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
dev_set_drvdata(dev, mixer);
mixer->engine.ops = &sun8i_engine_ops;
mixer->engine.node = dev->of_node;
+ /* default output format, supported by all mixers */
+ mixer->engine.format = MEDIA_BUS_FMT_RGB888_1X24;
if (of_property_present(dev->of_node, "iommus")) {
/*
@@ -653,6 +697,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
.ccsc = CCSC_MIXER0_LAYOUT,
.is_de3 = true,
+ .has_formatter = 1,
.mod_rate = 600000000,
.scaler_mask = 0xf,
.scanline_yuv = 4096,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 85c94884fb9a..13401643c7bf 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -162,6 +162,7 @@ enum {
* @mod_rate: module clock rate that needs to be set in order to have
* a functional block.
* @is_de3: true, if this is next gen display engine 3.0, false otherwise.
+ * @has_formatter: true, if mixer has formatter core, for 10-bit and YUV handling
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
*/
struct sun8i_mixer_cfg {
@@ -171,6 +172,7 @@ struct sun8i_mixer_cfg {
int ccsc;
unsigned long mod_rate;
unsigned int is_de3 : 1;
+ unsigned int has_formatter : 1;
unsigned int scanline_yuv;
};
diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h b/drivers/gpu/drm/sun4i/sunxi_engine.h
index ec8cf9b2bda4..5e64cda5675a 100644
--- a/drivers/gpu/drm/sun4i/sunxi_engine.h
+++ b/drivers/gpu/drm/sun4i/sunxi_engine.h
@@ -6,6 +6,8 @@
#ifndef _SUNXI_ENGINE_H_
#define _SUNXI_ENGINE_H_
+#include <drm/drm_color_mgmt.h>
+
struct drm_plane;
struct drm_device;
struct drm_crtc_state;
@@ -120,6 +122,20 @@ struct sunxi_engine_ops {
*/
void (*mode_set)(struct sunxi_engine *engine,
const struct drm_display_mode *mode);
+
+ /**
+ * @get_supported_fmts
+ *
+ * This callback is used to enumerate all supported output
+ * formats by the engine. They are used for bridge format
+ * negotiation.
+ *
+ * This function is optional.
+ */
+ u32 *(*get_supported_fmts)(struct sunxi_engine *engine, u32 *num);
+
+ void (*set_format)(struct sunxi_engine *engine, u32 format,
+ enum drm_color_encoding encoding);
};
/**
@@ -137,6 +153,9 @@ struct sunxi_engine {
int id;
+ u32 format;
+ enum drm_color_encoding encoding;
+
/* Engine list management */
struct list_head list;
};
@@ -208,4 +227,33 @@ sunxi_engine_mode_set(struct sunxi_engine *engine,
if (engine->ops && engine->ops->mode_set)
engine->ops->mode_set(engine, mode);
}
+
+/**
+ * sunxi_engine_get_supported_formats - Provide array of supported formats
+ * @engine: pointer to the engine
+ * @num: pointer to variable, which will hold number of formats
+ *
+ * This list can be used for format negotiation by bridge.
+ */
+static inline u32 *
+sunxi_engine_get_supported_formats(struct sunxi_engine *engine, u32 *num)
+{
+ if (engine->ops && engine->ops->get_supported_fmts)
+ return engine->ops->get_supported_fmts(engine, num);
+
+ *num = 0;
+
+ return NULL;
+}
+
+static inline void
+sunxi_engine_set_format(struct sunxi_engine *engine, u32 format,
+ enum drm_color_encoding encoding)
+{
+ if (engine->ops && engine->ops->set_format)
+ return engine->ops->set_format(engine, format, encoding);
+
+ engine->format = format;
+ engine->encoding = encoding;
+}
#endif /* _SUNXI_ENGINE_H_ */
--
2.42.0

View File

@ -0,0 +1,65 @@
From 6fba61d436ed9d610e1001860e2fcdf2ccf96803 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 7 Oct 2023 08:36:47 +0200
Subject: [PATCH 14/23] media: cedrus: h265: Fix configuring bitstream size
bit_size field holds size of slice, not slice + header. Because of HW
quirks, driver can't program in just slice, but also preceeding header.
But that means that currently used bit_size is wrong (too small).
Instead, just use size of whole buffer. There is no harm in doing this.
Fixes: 86caab29da78 ("media: cedrus: Add HEVC/H.265 decoding support")
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/staging/media/sunxi/cedrus/cedrus_h265.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index fc9297232456..16c822637dc6 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -427,11 +427,11 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
unsigned int ctb_addr_x, ctb_addr_y;
struct cedrus_buffer *cedrus_buf;
dma_addr_t src_buf_addr;
- dma_addr_t src_buf_end_addr;
u32 chroma_log2_weight_denom;
u32 num_entry_point_offsets;
u32 output_pic_list_index;
u32 pic_order_cnt[2];
+ size_t slice_bytes;
u8 padding;
int count;
u32 reg;
@@ -443,6 +443,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
pred_weight_table = &slice_params->pred_weight_table;
num_entry_point_offsets = slice_params->num_entry_point_offsets;
cedrus_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf);
+ slice_bytes = vb2_get_plane_payload(&run->src->vb2_buf, 0);
/*
* If entry points offsets are present, we should get them
@@ -490,7 +491,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
cedrus_write(dev, VE_DEC_H265_BITS_OFFSET, 0);
- reg = slice_params->bit_size;
+ reg = slice_bytes * 8;
cedrus_write(dev, VE_DEC_H265_BITS_LEN, reg);
/* Source beginning and end addresses. */
@@ -504,10 +505,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
cedrus_write(dev, VE_DEC_H265_BITS_ADDR, reg);
- src_buf_end_addr = src_buf_addr +
- DIV_ROUND_UP(slice_params->bit_size, 8);
-
- reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_end_addr);
+ reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_addr + slice_bytes);
cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg);
/* Coding tree block address */
--
2.42.0

View File

@ -0,0 +1,45 @@
From b60d442e96407bf90126e455b6466465438a5381 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 7 Oct 2023 09:43:35 +0200
Subject: [PATCH 15/23] media: Add NV12 and P010 AFBC compressed formats
Cedrus supports AFBC compressed NV12 and P010 formats, which
considerably speed up H265 decoding. Add them.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
include/uapi/linux/videodev2.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index f4d9d6279094..1e07066fa129 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1510,6 +1510,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break;
case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break;
+ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT: descr = "YUV 4:2:0 (AFBC 16x16)"; break;
+ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT: descr = "10-bit YUV 4:2:0 (AFBC 16x16)"; break;
default:
if (fmt->description[0])
return;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 78260e5d9985..968cdb3d0306 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -683,6 +683,10 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_NV12M_8L128 v4l2_fourcc('N', 'A', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
#define V4L2_PIX_FMT_NV12M_10BE_8L128 v4l2_fourcc_be('N', 'T', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
+/* AFBC YUV formats */
+#define V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT v4l2_fourcc('A', 'S', '1', '2') /* YUV420 AFBC compressed, 16x16 macroblocks, split */
+#define V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT v4l2_fourcc('A', 'S', '0', '1') /* YUV420 10-bit AFBC compressed, 16x16 macroblocks, split */
+
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
--
2.42.0

View File

@ -0,0 +1,51 @@
From ef817a8f5bfd38d91f9250b138a90edafb7b21a0 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sat, 7 Oct 2023 09:45:25 +0200
Subject: [PATCH 16/23] media: cedrus: add format filtering based on depth and
src format
Some Cedrus variant, like that found in H6, support special output
formats only with specific codecs, like H265.
Add extra filtering fields based on bit depth and source format.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/staging/media/sunxi/cedrus/cedrus_video.c | 7 +++++++
drivers/staging/media/sunxi/cedrus/cedrus_video.h | 2 ++
2 files changed, 9 insertions(+)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index b00feaf4072c..75b6f2e85a5f 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -87,6 +87,13 @@ static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx,
!(fmt->directions & directions))
continue;
+ if (fmt->depth && fmt->depth != ctx->bit_depth)
+ continue;
+
+ if (fmt->src_format &&
+ fmt->src_format != ctx->src_fmt.pixelformat)
+ continue;
+
if (fmt->pixelformat == pixelformat)
break;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h
index 8e1afc16a6a1..c8e9909ecdee 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h
@@ -20,6 +20,8 @@ struct cedrus_format {
u32 pixelformat;
u32 directions;
unsigned int capabilities;
+ unsigned int depth;
+ u32 src_format;
};
extern const struct v4l2_ioctl_ops cedrus_ioctl_ops;
--
2.42.0

View File

@ -0,0 +1,169 @@
From eaa6dd41f9c4c9a12d7f81a6ead80cc5d7146afa Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sun, 8 Oct 2023 12:44:59 +0200
Subject: [PATCH 17/23] media: cedrus: Implement AFBC YUV420 formats for H265
AFBC output formats are more performant, since they are optimized for
more efficient memory operations and transfers.
Add support for them.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/staging/media/sunxi/cedrus/cedrus.h | 11 ++++++
.../staging/media/sunxi/cedrus/cedrus_h265.c | 3 +-
.../staging/media/sunxi/cedrus/cedrus_hw.c | 16 +++++++++
.../staging/media/sunxi/cedrus/cedrus_regs.h | 6 ++++
.../staging/media/sunxi/cedrus/cedrus_video.c | 36 +++++++++++++++++++
5 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index 522c184e2afc..c7ec4dee8630 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -268,6 +268,17 @@ cedrus_is_capable(struct cedrus_ctx *ctx, unsigned int capabilities)
return (ctx->dev->capabilities & capabilities) == capabilities;
}
+static inline bool is_afbc_format(u32 format)
+{
+ switch (format) {
+ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT:
+ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id);
u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index 16c822637dc6..2372fafab475 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -120,7 +120,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
{
struct cedrus_dev *dev = ctx->dev;
dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0);
- dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1);
+ dma_addr_t dst_chroma_addr = is_afbc_format(ctx->dst_fmt.pixelformat) ?
+ 0 : cedrus_dst_buf_addr(ctx, buf, 1);
dma_addr_t mv_col_buf_addr[2] = {
cedrus_h265_frame_info_mv_col_buf_addr(buf, 0),
cedrus_h265_frame_info_mv_col_buf_addr(buf, field_pic ? 1 : 0)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index b696bf884cbd..ac0a88d47e10 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -65,6 +65,18 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx)
reg |= VE_MODE_PIC_WIDTH_IS_4096;
if (ctx->src_fmt.width > 2048)
reg |= VE_MODE_PIC_WIDTH_MORE_2048;
+ /*
+ * NOTE: Not sure if RGB default color feature is part of official
+ * AFBC standard or not and if it is, which feature that is. However,
+ * in order to render it properly with display engine, default color
+ * has to be set to white there.
+ */
+ if (is_afbc_format(ctx->dst_fmt.pixelformat))
+ reg |= VE_MODE_COMPRESS_EN |
+ VE_MODE_MIN_VAL_WRAP_EN |
+ VE_MODE_RGB_DEF_COLOR_EN |
+ VE_MODE_BODYBUF_1K_ALIGNED |
+ VE_MODE_COMPRESS_MODE_AFBC;
cedrus_write(ctx->dev, VE_MODE, reg);
@@ -85,6 +97,10 @@ void cedrus_dst_format_set(struct cedrus_dev *dev,
u32 reg;
switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT:
+ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT:
+ /* format is already set in cedrus_engine_enable() */
+ break;
case V4L2_PIX_FMT_NV12:
chroma_size = ALIGN(width, 16) * ALIGN(height, 16) / 2;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
index 05e6cbc548ab..c3dcd93a29eb 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
@@ -35,12 +35,18 @@
#define VE_MODE 0x00
+#define VE_MODE_COMPRESS_EN BIT(29)
+#define VE_MODE_MIN_VAL_WRAP_EN BIT(27)
+#define VE_MODE_RGB_DEF_COLOR_EN BIT(26)
#define VE_MODE_PIC_WIDTH_IS_4096 BIT(22)
#define VE_MODE_PIC_WIDTH_MORE_2048 BIT(21)
#define VE_MODE_REC_WR_MODE_2MB (0x01 << 20)
#define VE_MODE_REC_WR_MODE_1MB (0x00 << 20)
#define VE_MODE_DDR_MODE_BW_128 (0x03 << 16)
#define VE_MODE_DDR_MODE_BW_256 (0x02 << 16)
+#define VE_MODE_BODYBUF_1K_ALIGNED BIT(12)
+#define VE_MODE_COMPRESS_MODE_LOSSLESS (0x00 << 4)
+#define VE_MODE_COMPRESS_MODE_AFBC (0x01 << 4)
#define VE_MODE_DISABLED (0x07 << 0)
#define VE_MODE_DEC_H265 (0x04 << 0)
#define VE_MODE_DEC_H264 (0x01 << 0)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index 75b6f2e85a5f..a61b30946204 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -55,6 +55,22 @@ static struct cedrus_format cedrus_formats[] = {
.directions = CEDRUS_DECODE_SRC,
.capabilities = CEDRUS_CAPABILITY_VP8_DEC,
},
+ {
+ .pixelformat = V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT,
+ .directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED |
+ CEDRUS_CAPABILITY_H265_10_DEC,
+ .depth = 10,
+ .src_format = V4L2_PIX_FMT_HEVC_SLICE,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT,
+ .directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED |
+ CEDRUS_CAPABILITY_H265_10_DEC,
+ .depth = 8,
+ .src_format = V4L2_PIX_FMT_HEVC_SLICE,
+ },
{
.pixelformat = V4L2_PIX_FMT_NV12,
.directions = CEDRUS_DECODE_DST,
@@ -160,6 +176,26 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
sizeimage += bytesperline * height / 2;
break;
+
+ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT:
+ /* Zero bytes per line for compressed destination. */
+ bytesperline = 0;
+
+ sizeimage = DIV_ROUND_UP(width, 16) *
+ DIV_ROUND_UP(height + 4, 16) * (512 + 16) +
+ 32 + SZ_1K;
+
+ break;
+
+ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT:
+ /* Zero bytes per line for compressed destination. */
+ bytesperline = 0;
+
+ sizeimage = DIV_ROUND_UP(width, 16) *
+ DIV_ROUND_UP(height + 4, 16) * (384 + 16) +
+ 32 + SZ_1K;
+
+ break;
}
pix_fmt->width = width;
--
2.42.0

View File

@ -0,0 +1,68 @@
From 678c304daf5a489a781179b25fe72e4e856d0c6c Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sun, 8 Oct 2023 12:48:12 +0200
Subject: [PATCH 18/23] drm/sun4i: de2: Initialize layer fields earlier
drm_universal_plane_init() can already call some callbacks, like
format_mod_supported, during initialization. Because of that, fields
should be initialized beforehand.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 7 ++++---
drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 7 ++++---
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 884abe3cf773..91781b5bbbbc 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -365,6 +365,10 @@ struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
if (!layer)
return ERR_PTR(-ENOMEM);
+ layer->mixer = mixer;
+ layer->channel = channel;
+ layer->overlay = 0;
+
if (index == 0)
type = DRM_PLANE_TYPE_PRIMARY;
@@ -395,9 +399,6 @@ struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
}
drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
- layer->mixer = mixer;
- layer->channel = channel;
- layer->overlay = 0;
return layer;
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 6ee3790a2a81..329e8bf8cd20 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -549,6 +549,10 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
if (!layer)
return ERR_PTR(-ENOMEM);
+ layer->mixer = mixer;
+ layer->channel = index;
+ layer->overlay = 0;
+
if (mixer->cfg->is_de3) {
formats = sun8i_vi_layer_de3_formats;
format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
@@ -607,9 +611,6 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
}
drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
- layer->mixer = mixer;
- layer->channel = index;
- layer->overlay = 0;
return layer;
}
--
2.42.0

View File

@ -0,0 +1,571 @@
From 75f9a78d91a37c0eadf952e97e93d3f51bfda1b6 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Sun, 8 Oct 2023 13:01:01 +0200
Subject: [PATCH 19/23] drm/sun4i: de3: Implement AFBC support
Buffers, compressed with AFBC, are generally more efficient for memory
transfers. Add support for them.
Currently it's implemented only for VI layers, but vendor code and
documentation suggest UI layers can have them too. However, I haven't
observed any SoC with such feature.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/Makefile | 2 +-
drivers/gpu/drm/sun4i/sun50i_afbc.c | 240 +++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun50i_afbc.h | 87 +++++++++
drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 81 +++++++--
4 files changed, 397 insertions(+), 13 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.c
create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.h
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 3f516329f51e..78290f1660fb 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -17,7 +17,7 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o
sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \
sun8i_vi_layer.o sun8i_ui_scaler.o \
sun8i_vi_scaler.o sun8i_csc.o \
- sun50i_fmt.o
+ sun50i_fmt.o sun50i_afbc.o
sun4i-tcon-y += sun4i_crtc.o
sun4i-tcon-y += sun4i_tcon_dclk.o
diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.c b/drivers/gpu/drm/sun4i/sun50i_afbc.c
new file mode 100644
index 000000000000..27a771608eef
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_afbc.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <drm/drm_blend.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_plane.h>
+#include <uapi/drm/drm_fourcc.h>
+
+#include "sun50i_afbc.h"
+#include "sun8i_mixer.h"
+
+bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer,
+ u32 format, u64 modifier)
+{
+ u64 mode;
+
+ if (modifier == DRM_FORMAT_MOD_INVALID)
+ return false;
+
+ if (modifier == DRM_FORMAT_MOD_LINEAR) {
+ if (format == DRM_FORMAT_YUV420_8BIT ||
+ format == DRM_FORMAT_YUV420_10BIT ||
+ format == DRM_FORMAT_Y210)
+ return false;
+ return true;
+ }
+
+ if (!mixer->cfg->is_de3)
+ return false;
+
+ mode = AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT;
+
+ switch (format) {
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_RGBA1010102:
+ mode |= AFBC_FORMAT_MOD_YTR;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_Y210:
+ case DRM_FORMAT_YUV420_8BIT:
+ case DRM_FORMAT_YUV420_10BIT:
+ break;
+ default:
+ return false;
+ }
+
+ return modifier == DRM_FORMAT_MOD_ARM_AFBC(mode);
+}
+
+void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ const struct drm_format_info *format = fb->format;
+ struct drm_gem_dma_object *gem;
+ u32 base, val, src_w, src_h;
+ u32 def_color0, def_color1;
+ struct regmap *regs;
+ dma_addr_t dma_addr;
+
+ base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET;
+ regs = mixer->engine.regs;
+
+ src_w = drm_rect_width(&state->src) >> 16;
+ src_h = drm_rect_height(&state->src) >> 16;
+
+ val = SUN50I_FBD_SIZE_HEIGHT(src_h);
+ val |= SUN50I_FBD_SIZE_WIDTH(src_w);
+ regmap_write(regs, SUN50I_FBD_SIZE(base), val);
+
+ val = SUN50I_FBD_BLK_SIZE_HEIGHT(DIV_ROUND_UP(src_h, 16));
+ val = SUN50I_FBD_BLK_SIZE_WIDTH(DIV_ROUND_UP(src_w, 16));
+ regmap_write(regs, SUN50I_FBD_BLK_SIZE(base), val);
+
+ val = SUN50I_FBD_SRC_CROP_TOP(0);
+ val |= SUN50I_FBD_SRC_CROP_LEFT(0);
+ regmap_write(regs, SUN50I_FBD_SRC_CROP(base), val);
+
+ val = SUN50I_FBD_LAY_CROP_TOP(state->src.y1 >> 16);
+ val |= SUN50I_FBD_LAY_CROP_LEFT(state->src.x1 >> 16);
+ regmap_write(regs, SUN50I_FBD_LAY_CROP(base), val);
+
+ /*
+ * Default color is always set to white, in colorspace and bitness
+ * that coresponds to used format. If it is actually used or not
+ * depends on AFBC buffer. At least in Cedrus it can be turned on
+ * or off.
+ * NOTE: G and B channels are off by 1 (up). It's unclear if this
+ * is because HW need such value or it is due to good enough code
+ * in vendor driver and HW clips the value anyway.
+ */
+ def_color0 = 0;
+ def_color1 = 0;
+
+ val = 0;
+ switch (format->format) {
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YUV420_10BIT:
+ val |= SUN50I_FBD_FMT_SBS1(2);
+ val |= SUN50I_FBD_FMT_SBS0(1);
+ break;
+ case DRM_FORMAT_Y210:
+ val |= SUN50I_FBD_FMT_SBS1(3);
+ val |= SUN50I_FBD_FMT_SBS0(2);
+ break;
+ default:
+ val |= SUN50I_FBD_FMT_SBS1(1);
+ val |= SUN50I_FBD_FMT_SBS0(1);
+ break;
+ }
+ switch (format->format) {
+ case DRM_FORMAT_RGBA8888:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_8888);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(255) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(255);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(256);
+ break;
+ case DRM_FORMAT_RGB888:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_888);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(255);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(256);
+ break;
+ case DRM_FORMAT_RGB565:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_565);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(31);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(64) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(32);
+ break;
+ case DRM_FORMAT_RGBA4444:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_4444);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(15) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(15);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(16) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(16);
+ break;
+ case DRM_FORMAT_RGBA5551:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_5551);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(1) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(31);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(32) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(32);
+ break;
+ case DRM_FORMAT_RGBA1010102:
+ val |= SUN50I_FBD_FMT_YUV_TRAN;
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA1010102);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(3) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(1023);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(1024) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(1024);
+ break;
+ case DRM_FORMAT_YUV420_8BIT:
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV420);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(255);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(128);
+ break;
+ case DRM_FORMAT_YUYV:
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV422);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(255);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(128);
+ break;
+ case DRM_FORMAT_YUV420_10BIT:
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P010);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(1023);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(512);
+ break;
+ case DRM_FORMAT_Y210:
+ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P210);
+ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) |
+ SUN50I_FBD_DEFAULT_COLOR0_YR(1023);
+ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) |
+ SUN50I_FBD_DEFAULT_COLOR1_VB(512);
+ break;
+ }
+ regmap_write(regs, SUN50I_FBD_FMT(base), val);
+
+ /* Get the physical address of the buffer in memory */
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
+
+ DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
+
+ /* Compute the start of the displayed memory */
+ dma_addr = gem->dma_addr + fb->offsets[0];
+
+ regmap_write(regs, SUN50I_FBD_LADDR(base), lower_32_bits(dma_addr));
+ regmap_write(regs, SUN50I_FBD_HADDR(base), upper_32_bits(dma_addr));
+
+ val = SUN50I_FBD_OVL_SIZE_HEIGHT(src_h);
+ val |= SUN50I_FBD_OVL_SIZE_WIDTH(src_w);
+ regmap_write(regs, SUN50I_FBD_OVL_SIZE(base), val);
+
+ val = SUN50I_FBD_OVL_COOR_Y(0);
+ val |= SUN50I_FBD_OVL_COOR_X(0);
+ regmap_write(regs, SUN50I_FBD_OVL_COOR(base), val);
+
+ regmap_write(regs, SUN50I_FBD_OVL_BG_COLOR(base),
+ SUN8I_MIXER_BLEND_COLOR_BLACK);
+ regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR0(base), def_color0);
+ regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR1(base), def_color1);
+
+ val = SUN50I_FBD_CTL_GLB_ALPHA(state->alpha >> 16);
+ val |= SUN50I_FBD_CTL_CLK_GATE;
+ val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
+ SUN50I_FBD_CTL_ALPHA_MODE_PIXEL :
+ SUN50I_FBD_CTL_ALPHA_MODE_COMBINED;
+ val |= SUN50I_FBD_CTL_FBD_EN;
+ regmap_write(regs, SUN50I_FBD_CTL(base), val);
+}
+
+void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel)
+{
+ u32 base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET;
+
+ regmap_write(mixer->engine.regs, SUN50I_FBD_CTL(base), 0);
+}
diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.h b/drivers/gpu/drm/sun4i/sun50i_afbc.h
new file mode 100644
index 000000000000..cea685c86855
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_afbc.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#ifndef _SUN50I_AFBC_H_
+#define _SUN50I_AFBC_H_
+
+#include <linux/types.h>
+
+#define SUN50I_AFBC_CH_OFFSET 0x300
+
+#define SUN50I_AFBC_RGBA_8888 0x02
+#define SUN50I_AFBC_RGB_888 0x08
+#define SUN50I_AFBC_RGB_565 0x0a
+#define SUN50I_AFBC_RGBA_4444 0x0e
+#define SUN50I_AFBC_RGBA_5551 0x12
+#define SUN50I_AFBC_RGBA1010102 0x16
+#define SUN50I_AFBC_YUV422 0x26
+#define SUN50I_AFBC_YUV420 0x2a
+#define SUN50I_AFBC_P010 0x30
+#define SUN50I_AFBC_P210 0x32
+
+#define SUN50I_FBD_CTL(base) ((base) + 0x00)
+#define SUN50I_FBD_CTL_GLB_ALPHA(v) ((v) << 24)
+#define SUN50I_FBD_CTL_CLK_GATE BIT(4)
+#define SUN50I_FBD_CTL_ALPHA_MODE_PIXEL ((0) << 2)
+#define SUN50I_FBD_CTL_ALPHA_MODE_LAYER ((1) << 2)
+#define SUN50I_FBD_CTL_ALPHA_MODE_COMBINED ((2) << 2)
+#define SUN50I_FBD_CTL_FBD_FCEN BIT(1)
+#define SUN50I_FBD_CTL_FBD_EN BIT(0)
+
+#define SUN50I_FBD_SIZE(base) ((base) + 0x08)
+#define SUN50I_FBD_SIZE_HEIGHT(v) (((v) - 1) << 16)
+#define SUN50I_FBD_SIZE_WIDTH(v) (((v) - 1) << 0)
+
+#define SUN50I_FBD_BLK_SIZE(base) ((base) + 0x0c)
+#define SUN50I_FBD_BLK_SIZE_HEIGHT(v) ((v) << 16)
+#define SUN50I_FBD_BLK_SIZE_WIDTH(v) ((v) << 0)
+
+#define SUN50I_FBD_SRC_CROP(base) ((base) + 0x10)
+#define SUN50I_FBD_SRC_CROP_TOP(v) ((v) << 16)
+#define SUN50I_FBD_SRC_CROP_LEFT(v) ((v) << 0)
+
+#define SUN50I_FBD_LAY_CROP(base) ((base) + 0x14)
+#define SUN50I_FBD_LAY_CROP_TOP(v) ((v) << 16)
+#define SUN50I_FBD_LAY_CROP_LEFT(v) ((v) << 0)
+
+#define SUN50I_FBD_FMT(base) ((base) + 0x18)
+#define SUN50I_FBD_FMT_SBS1(v) ((v) << 18)
+#define SUN50I_FBD_FMT_SBS0(v) ((v) << 16)
+#define SUN50I_FBD_FMT_YUV_TRAN BIT(7)
+#define SUN50I_FBD_FMT_IN_FMT(v) ((v) << 0)
+
+#define SUN50I_FBD_LADDR(base) ((base) + 0x20)
+#define SUN50I_FBD_HADDR(base) ((base) + 0x24)
+
+#define SUN50I_FBD_OVL_SIZE(base) ((base) + 0x30)
+#define SUN50I_FBD_OVL_SIZE_HEIGHT(v) (((v) - 1) << 16)
+#define SUN50I_FBD_OVL_SIZE_WIDTH(v) (((v) - 1) << 0)
+
+#define SUN50I_FBD_OVL_COOR(base) ((base) + 0x34)
+#define SUN50I_FBD_OVL_COOR_Y(v) ((v) << 16)
+#define SUN50I_FBD_OVL_COOR_X(v) ((v) << 0)
+
+#define SUN50I_FBD_OVL_BG_COLOR(base) ((base) + 0x38)
+#define SUN50I_FBD_OVL_FILL_COLOR(base) ((base) + 0x3c)
+
+#define SUN50I_FBD_DEFAULT_COLOR0(base) ((base) + 0x50)
+#define SUN50I_FBD_DEFAULT_COLOR0_ALPHA(v) ((v) << 16)
+#define SUN50I_FBD_DEFAULT_COLOR0_YR(v) ((v) << 0)
+
+#define SUN50I_FBD_DEFAULT_COLOR1(base) ((base) + 0x54)
+#define SUN50I_FBD_DEFAULT_COLOR1_VB(v) ((v) << 16)
+#define SUN50I_FBD_DEFAULT_COLOR1_UG(v) ((v) << 0)
+
+struct sun8i_mixer;
+struct drm_plane;
+
+bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer,
+ u32 format, u64 modifier);
+
+void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel,
+ struct drm_plane *plane);
+void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel);
+
+#endif
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 329e8bf8cd20..28a2c44843e6 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -11,8 +11,10 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
+#include "sun50i_afbc.h"
#include "sun8i_csc.h"
#include "sun8i_mixer.h"
#include "sun8i_vi_layer.h"
@@ -99,7 +101,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
int overlay, struct drm_plane *plane,
- unsigned int zpos)
+ unsigned int zpos, bool afbc)
{
struct drm_plane_state *state = plane->state;
const struct drm_format_info *format = state->fb->format;
@@ -182,7 +184,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
required = src_h * 100 / dst_h;
- if (ability < required) {
+ if (!afbc && ability < required) {
DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
vm = src_h;
vn = (u32)ability * dst_h / 100;
@@ -192,7 +194,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
/* it seems that every RGB scaler has buffer for 2048 pixels */
scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
- if (src_w > scanline) {
+ if (!afbc && src_w > scanline) {
DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
hm = src_w;
hn = scanline;
@@ -356,6 +358,15 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
return 0;
}
+static void sun8i_vi_layer_prepare_non_linear(struct sun8i_mixer *mixer,
+ int channel, int overlay)
+{
+ u32 base = sun8i_channel_base(mixer, channel);
+
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_CHAN_VI_LAYER_ATTR(base, overlay), 0);
+}
+
static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
@@ -399,6 +410,7 @@ static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
old_zpos);
+ sun50i_afbc_disable(mixer, layer->channel);
}
static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
@@ -411,26 +423,51 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
unsigned int zpos = new_state->normalized_zpos;
unsigned int old_zpos = old_state->normalized_zpos;
+ struct drm_framebuffer *fb = plane->state->fb;
struct sun8i_mixer *mixer = layer->mixer;
+ bool afbc = drm_is_afbc(fb->modifier);
if (!new_state->visible) {
sun8i_vi_layer_enable(mixer, layer->channel,
layer->overlay, false, 0, old_zpos);
+ sun50i_afbc_disable(mixer, layer->channel);
return;
}
+ if (afbc) {
+ u32 fmt_type;
+
+ sun8i_vi_layer_prepare_non_linear(mixer, layer->channel,
+ layer->overlay);
+ sun50i_afbc_atomic_update(mixer, layer->channel, plane);
+
+ fmt_type = sun8i_vi_layer_get_format_type(fb->format);
+ sun8i_csc_set_ccsc(mixer, layer->channel, fmt_type,
+ plane->state->color_encoding,
+ plane->state->color_range);
+ } else {
+ sun50i_afbc_disable(mixer, layer->channel);
+ sun8i_vi_layer_update_alpha(mixer, layer->channel,
+ layer->overlay, plane);
+ sun8i_vi_layer_update_formats(mixer, layer->channel,
+ layer->overlay, plane);
+ sun8i_vi_layer_update_buffer(mixer, layer->channel,
+ layer->overlay, plane);
+ }
sun8i_vi_layer_update_coord(mixer, layer->channel,
- layer->overlay, plane, zpos);
- sun8i_vi_layer_update_alpha(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_vi_layer_update_formats(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_vi_layer_update_buffer(mixer, layer->channel,
- layer->overlay, plane);
+ layer->overlay, plane, zpos, afbc);
sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
true, zpos, old_zpos);
}
+static bool sun8i_vi_layer_format_mod_supported(struct drm_plane *plane,
+ u32 format, u64 modifier)
+{
+ struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
+
+ return sun50i_afbc_format_mod_supported(layer->mixer, format, modifier);
+}
+
static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
.atomic_check = sun8i_vi_layer_atomic_check,
.atomic_disable = sun8i_vi_layer_atomic_disable,
@@ -444,6 +481,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
.disable_plane = drm_atomic_helper_disable_plane,
.reset = drm_atomic_helper_plane_reset,
.update_plane = drm_atomic_helper_update_plane,
+ .format_mod_supported = sun8i_vi_layer_format_mod_supported,
};
/*
@@ -527,6 +565,11 @@ static const u32 sun8i_vi_layer_de3_formats[] = {
DRM_FORMAT_YVU411,
DRM_FORMAT_YVU420,
DRM_FORMAT_YVU422,
+
+ /* AFBC only formats */
+ DRM_FORMAT_YUV420_8BIT,
+ DRM_FORMAT_YUV420_10BIT,
+ DRM_FORMAT_Y210,
};
static const uint64_t sun8i_layer_modifiers[] = {
@@ -534,6 +577,18 @@ static const uint64_t sun8i_layer_modifiers[] = {
DRM_FORMAT_MOD_INVALID
};
+static const uint64_t sun50i_layer_de3_modifiers[] = {
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+ AFBC_FORMAT_MOD_YTR |
+ AFBC_FORMAT_MOD_SPARSE |
+ AFBC_FORMAT_MOD_SPLIT),
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
struct sun8i_mixer *mixer,
int index)
@@ -542,6 +597,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
u32 supported_encodings, supported_ranges;
unsigned int plane_cnt, format_count;
struct sun8i_vi_layer *layer;
+ const uint64_t *modifiers;
const u32 *formats;
int ret;
@@ -556,9 +612,11 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
if (mixer->cfg->is_de3) {
formats = sun8i_vi_layer_de3_formats;
format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
+ modifiers = sun50i_layer_de3_modifiers;
} else {
formats = sun8i_vi_layer_formats;
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
+ modifiers = sun8i_layer_modifiers;
}
if (!mixer->cfg->ui_num && index == 0)
@@ -568,8 +626,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
ret = drm_universal_plane_init(drm, &layer->plane, 0,
&sun8i_vi_layer_funcs,
formats, format_count,
- sun8i_layer_modifiers,
- type, NULL);
+ modifiers, type, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialize layer\n");
return ERR_PTR(ret);
--
2.42.0

View File

@ -0,0 +1,29 @@
From c7999f0a3a7f275756a10a87849bb8fa47d315d3 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Mon, 9 Oct 2023 20:16:27 +0200
Subject: [PATCH 20/23] media: cedrus: Increase H6 clock rate
Vendor driver runs Cedrus at 648 MHz, supposedly to be able to decode
4k HEVC at 60 fps.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/staging/media/sunxi/cedrus/cedrus.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 8e248d4a0aec..8d1ad841f66f 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -646,7 +646,7 @@ static const struct cedrus_variant sun50i_h6_cedrus_variant = {
CEDRUS_CAPABILITY_H265_DEC |
CEDRUS_CAPABILITY_H265_10_DEC |
CEDRUS_CAPABILITY_VP8_DEC,
- .mod_rate = 600000000,
+ .mod_rate = 648000000,
};
static const struct of_device_id cedrus_dt_match[] = {
--
2.42.0

View File

@ -0,0 +1,67 @@
From c0652e260579791f38bc1d7c9c634135a1065c82 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Mon, 9 Oct 2023 20:55:25 +0200
Subject: [PATCH 21/23] arm64: dts: allwinner: h6: Add GPU OPP to all boards
All H6 boards have GPU. Let's enable OPP table for it on all boards.
That allows either better performance or lower power consumption.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts | 1 +
arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi | 1 +
arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts | 1 +
arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi | 1 +
4 files changed, 4 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
index 6fc65e8db220..817976ba20f3 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
@@ -5,6 +5,7 @@
#include "sun50i-h6.dtsi"
#include "sun50i-h6-cpu-opp.dtsi"
+#include "sun50i-h6-gpu-opp.dtsi"
#include <dt-bindings/gpio/gpio.h>
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi
index 92745128fcfe..6a0e3bf9bdb7 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi
@@ -5,6 +5,7 @@
/dts-v1/;
#include "sun50i-h6.dtsi"
+#include "sun50i-h6-gpu-opp.dtsi"
#include <dt-bindings/gpio/gpio.h>
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
index 1ffd68f43f87..ee31303dc886 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
@@ -5,6 +5,7 @@
#include "sun50i-h6.dtsi"
#include "sun50i-h6-cpu-opp.dtsi"
+#include "sun50i-h6-gpu-opp.dtsi"
#include <dt-bindings/gpio/gpio.h>
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
index c3faefa651d9..531c8f2b423d 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
@@ -5,6 +5,7 @@
#include "sun50i-h6.dtsi"
#include "sun50i-h6-cpu-opp.dtsi"
+#include "sun50i-h6-gpu-opp.dtsi"
#include <dt-bindings/gpio/gpio.h>
--
2.42.0

View File

@ -1,23 +1,23 @@
From 24545292c40900c0871381b8697ade70aa9e3bdf Mon Sep 17 00:00:00 2001
From d7b2340cd84ba5ce3494e8acbf52191e1d85bbe8 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Wed, 20 Jan 2021 22:15:36 +0100
Subject: [PATCH] wip h3/h5 cvbs
Subject: [PATCH 22/23] wip h3/h5 cvbs
---
arch/arm/boot/dts/allwinner/sun8i-h3.dtsi | 22 +++++
arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 95 +++++++++++++++++++-
arch/arm/boot/dts/allwinner/sun8i-h3.dtsi | 22 +++++
arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 95 +++++++++++++++++++-
arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 22 +++++
drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 14 ++-
drivers/gpu/drm/sun4i/sun4i_tv.c | 35 +++++++-
drivers/gpu/drm/sun4i/sun8i_mixer.c | 44 ++++++++-
drivers/gpu/drm/sun4i/sun8i_mixer.c | 38 ++++++++
drivers/gpu/drm/sun4i/sun8i_mixer.h | 5 +-
7 files changed, 226 insertions(+), 11 deletions(-)
7 files changed, 223 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
index 448dd325f8c3..d896dc5502f5 100644
index eac2349a2380..411640fca509 100644
--- a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
@@ -252,6 +252,20 @@ ths: thermal-sensor@1c25000 {
@@ -239,6 +239,20 @@ ths: thermal-sensor@1c25000 {
nvmem-cell-names = "calibration";
#thermal-sensor-cells = <0>;
};
@ -38,7 +38,7 @@ index 448dd325f8c3..d896dc5502f5 100644
};
thermal-zones {
@@ -299,6 +313,10 @@ &mbus {
@@ -286,6 +300,10 @@ &mbus {
compatible = "allwinner,sun8i-h3-mbus";
};
@ -49,7 +49,7 @@ index 448dd325f8c3..d896dc5502f5 100644
&mmc0 {
compatible = "allwinner,sun7i-a20-mmc";
clocks = <&ccu CLK_BUS_MMC0>,
@@ -346,3 +364,7 @@ &rtc {
@@ -333,3 +351,7 @@ &rtc {
&sid {
compatible = "allwinner,sun8i-h3-sid";
};
@ -58,10 +58,10 @@ index 448dd325f8c3..d896dc5502f5 100644
+ remote-endpoint = <&tve_in_tcon1>;
+};
diff --git a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
index 3d37a6a586b6..152029413784 100644
index ade1cd50e445..dc2ae4270347 100644
--- a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
@@ -119,7 +119,7 @@ osc32k: osc32k_clk {
@@ -102,7 +102,7 @@ osc32k: osc32k_clk {
de: display-engine {
compatible = "allwinner,sun8i-h3-display-engine";
@ -70,7 +70,7 @@ index 3d37a6a586b6..152029413784 100644
status = "disabled";
};
@@ -163,11 +163,50 @@ ports {
@@ -139,11 +139,50 @@ ports {
#size-cells = <0>;
mixer0_out: port@1 {
@ -122,7 +122,7 @@ index 3d37a6a586b6..152029413784 100644
};
};
};
@@ -196,11 +235,19 @@ ports {
@@ -172,11 +211,19 @@ ports {
#size-cells = <0>;
tcon0_in: port@0 {
@ -143,7 +143,7 @@ index 3d37a6a586b6..152029413784 100644
};
tcon0_out: port@1 {
@@ -216,6 +263,48 @@ tcon0_out_hdmi: endpoint@1 {
@@ -192,6 +239,48 @@ tcon0_out_hdmi: endpoint@1 {
};
};
@ -193,10 +193,10 @@ index 3d37a6a586b6..152029413784 100644
/* compatible and clocks are in per SoC .dtsi file */
reg = <0x01c0f000 0x1000>;
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
index 4f00ae227cce..d30c85948ac5 100644
index d3caf27b6a55..31e67b91e4fa 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -197,6 +197,20 @@ ths: thermal-sensor@1c25000 {
@@ -184,6 +184,20 @@ ths: thermal-sensor@1c25000 {
nvmem-cell-names = "calibration";
#thermal-sensor-cells = <1>;
};
@ -217,7 +217,7 @@ index 4f00ae227cce..d30c85948ac5 100644
};
thermal-zones {
@@ -250,6 +264,10 @@ &mbus {
@@ -237,6 +251,10 @@ &mbus {
compatible = "allwinner,sun50i-h5-mbus";
};
@ -228,7 +228,7 @@ index 4f00ae227cce..d30c85948ac5 100644
&mmc0 {
compatible = "allwinner,sun50i-h5-mmc",
"allwinner,sun50i-a64-mmc";
@@ -285,3 +303,7 @@ &rtc {
@@ -272,3 +290,7 @@ &rtc {
&sid {
compatible = "allwinner,sun50i-h5-sid";
};
@ -237,10 +237,10 @@ index 4f00ae227cce..d30c85948ac5 100644
+ remote-endpoint = <&tve_in_tcon1>;
+};
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index e058cf691aea..0b0df6d6bc9c 100644
index 74274c17efb3..c4f4ba836408 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -458,8 +458,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents,
@@ -463,8 +463,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents,
CLK_SET_RATE_PARENT);
static const char * const tve_parents[] = { "pll-de", "pll-periph1" };
@ -262,7 +262,7 @@ index e058cf691aea..0b0df6d6bc9c 100644
static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" };
static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents,
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 94883abe0dfd..9c7090a0d52a 100644
index ec65d9d59de7..d2235d5a7416 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -10,6 +10,7 @@
@ -273,7 +273,7 @@ index 94883abe0dfd..9c7090a0d52a 100644
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -167,6 +168,11 @@ struct tv_mode {
@@ -159,6 +160,11 @@ struct tv_mode {
const struct resync_parameters *resync_params;
};
@ -285,7 +285,7 @@ index 94883abe0dfd..9c7090a0d52a 100644
struct sun4i_tv {
struct drm_connector connector;
struct drm_encoder encoder;
@@ -527,7 +533,7 @@ static const struct regmap_config sun4i_tv_regmap_config = {
@@ -419,7 +425,7 @@ static const struct regmap_config sun4i_tv_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
@ -294,7 +294,7 @@ index 94883abe0dfd..9c7090a0d52a 100644
.name = "tv-encoder",
};
@@ -537,13 +543,19 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
@@ -429,13 +435,19 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
@ -314,7 +314,7 @@ index 94883abe0dfd..9c7090a0d52a 100644
tv->drv = drv;
dev_set_drvdata(dev, tv);
@@ -580,6 +592,11 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
@@ -472,6 +484,11 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
}
clk_prepare_enable(tv->clk);
@ -326,8 +326,8 @@ index 94883abe0dfd..9c7090a0d52a 100644
drm_encoder_helper_add(&tv->encoder,
&sun4i_tv_helper_funcs);
ret = drm_simple_encoder_init(drm, &tv->encoder,
@@ -648,8 +665,22 @@ static int sun4i_tv_remove(struct platform_device *pdev)
return 0;
@@ -551,8 +568,22 @@ static void sun4i_tv_remove(struct platform_device *pdev)
component_del(&pdev->dev, &sun4i_tv_ops);
}
+static const struct sun4i_tv_quirks a10_quirks = {
@ -351,10 +351,10 @@ index 94883abe0dfd..9c7090a0d52a 100644
};
MODULE_DEVICE_TABLE(of, sun4i_tv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index f5291170bf5e..490e8e74450f 100644
index 8e32d0b24ac9..854f5ab76463 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -32,6 +32,12 @@ struct de2_fmt_info {
@@ -36,6 +36,12 @@ struct de2_fmt_info {
u32 de2_fmt;
};
@ -367,8 +367,8 @@ index f5291170bf5e..490e8e74450f 100644
static const struct de2_fmt_info de2_formats[] = {
{
.drm_fmt = DRM_FORMAT_ARGB8888,
@@ -327,10 +333,29 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
interlaced ? "on" : "off");
@@ -369,11 +375,30 @@ static u32 *sun8i_mixer_get_supported_fmts(struct sunxi_engine *engine, u32 *num
return formats;
}
+static void sun8i_mixer_apply_color_correction(struct sunxi_engine *engine)
@ -389,18 +389,16 @@ index f5291170bf5e..490e8e74450f 100644
+}
+
static const struct sunxi_engine_ops sun8i_engine_ops = {
- .commit = sun8i_mixer_commit,
- .layers_init = sun8i_layers_init,
- .mode_set = sun8i_mixer_mode_set,
+ .commit = sun8i_mixer_commit,
+ .layers_init = sun8i_layers_init,
+ .mode_set = sun8i_mixer_mode_set,
.commit = sun8i_mixer_commit,
.layers_init = sun8i_layers_init,
.mode_set = sun8i_mixer_mode_set,
.get_supported_fmts = sun8i_mixer_get_supported_fmts,
+ .apply_color_correction = sun8i_mixer_apply_color_correction,
+ .disable_color_correction = sun8i_mixer_disable_color_correction,
};
static bool sun8i_mixer_volatile_reg(struct device *dev, unsigned int reg)
@@ -600,6 +625,15 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
static const struct regmap_config sun8i_mixer_regmap_config = {
@@ -631,6 +656,15 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
.vi_num = 1,
};
@ -416,7 +414,7 @@ index f5291170bf5e..490e8e74450f 100644
static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
.ccsc = CCSC_MIXER0_LAYOUT,
.mod_rate = 297000000,
@@ -686,6 +720,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = {
@@ -718,6 +752,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = {
.compatible = "allwinner,sun8i-h3-de2-mixer-0",
.data = &sun8i_h3_mixer0_cfg,
},
@ -428,7 +426,7 @@ index f5291170bf5e..490e8e74450f 100644
.compatible = "allwinner,sun8i-r40-de2-mixer-0",
.data = &sun8i_r40_mixer0_cfg,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 85c94884fb9a..28cdaf0044d9 100644
index 13401643c7bf..5951adb66e69 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -118,6 +118,10 @@
@ -451,5 +449,5 @@ index 85c94884fb9a..28cdaf0044d9 100644
#define SUN50I_MIXER_FCE_EN 0x70000
#define SUN50I_MIXER_PEAK_EN 0x70800
--
2.37.3
2.42.0

View File

@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From 24edf1dbd1ea0935d425333ab1e1041de2b4874c Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Mon, 11 Oct 2021 20:13:41 +0200
Subject: [PATCH] HACK: SW CEC implementation for H3
Subject: [PATCH 23/23] HACK: SW CEC implementation for H3
Many H3 boards lack 32768 Hz external oscillator, so internal RC is used
instead. However, it's too unstable for CEC. Use SW implementation
@ -10,25 +10,41 @@ instead. That makes it usable, albeit sensitive to cpufreq changes.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
drivers/gpu/drm/sun4i/Kconfig | 2 +
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 13 ++++
drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 83 +++++++++++++++++++++++++-
3 files changed, 96 insertions(+), 2 deletions(-)
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 4 ++
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 14 ++++
drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 98 +++++++++++++++++++++++++-
4 files changed, 116 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 5755f0432e77..b93eb2fb52ce 100644
index 4741d9f6544c..4dac2dddf707 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -56,6 +56,8 @@ config DRM_SUN8I_DW_HDMI
tristate "Support for Allwinner version of DesignWare HDMI"
@@ -59,6 +59,8 @@ config DRM_SUN8I_DW_HDMI
depends on DRM_SUN4I
default DRM_SUN4I
select DRM_DW_HDMI
+ select CEC_CORE
+ select CEC_PIN
help
Choose this option if you have an Allwinner SoC with the
DesignWare HDMI controller with custom HDMI PHY. If M is
DesignWare HDMI controller. SoCs that support HDMI and
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 7309590feb56..bde6b81def55 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -273,6 +273,10 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
return dev_err_probe(dev, PTR_ERR(phy),
"Couldn't get the HDMI PHY\n");
+ ret = sun8i_hdmi_phy_register_cec(phy, dev);
+ if (ret)
+ return ret;
+
hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index bffe1b9cd3dc..61c97619cba1 100644
index 5383d9267a4d..9d1158f24ddb 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -13,6 +13,8 @@
@ -54,7 +70,7 @@ index bffe1b9cd3dc..61c97619cba1 100644
struct sun8i_hdmi_phy;
@@ -164,6 +173,8 @@ struct sun8i_hdmi_phy_variant {
@@ -159,6 +168,8 @@ struct sun8i_hdmi_phy_variant {
};
struct sun8i_hdmi_phy {
@ -63,7 +79,7 @@ index bffe1b9cd3dc..61c97619cba1 100644
struct clk *clk_bus;
struct clk *clk_mod;
struct clk *clk_phy;
@@ -174,6 +185,8 @@ struct sun8i_hdmi_phy {
@@ -169,6 +180,8 @@ struct sun8i_hdmi_phy {
struct regmap *regs;
struct reset_control *rst_phy;
const struct sun8i_hdmi_phy_variant *variant;
@ -72,11 +88,18 @@ index bffe1b9cd3dc..61c97619cba1 100644
};
struct sun8i_dw_hdmi_quirks {
@@ -211,5 +224,6 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev,
bool second_parent);
+int sun8i_hdmi_phy_register_cec(struct sun8i_hdmi_phy *phy, struct device *dev);
#endif /* _SUN8I_DW_HDMI_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index b64d93da651d..e2936e7745b8 100644
index 581233d6eaf2..a5771b5d9b67 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -506,8 +506,9 @@
@@ -507,8 +507,9 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
@ -88,7 +111,7 @@ index b64d93da651d..e2936e7745b8 100644
/* read calibration data */
regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
@@ -584,8 +585,47 @@
@@ -585,8 +586,47 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
plat_data->cur_ctr = variant->cur_ctr;
plat_data->phy_config = variant->phy_cfg;
}
@ -136,40 +159,36 @@ index b64d93da651d..e2936e7745b8 100644
static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -653,6 +693,7 @@
{
struct platform_device *pdev = of_find_device_by_node(node);
struct sun8i_hdmi_phy *phy;
@@ -669,6 +709,41 @@ struct sun8i_hdmi_phy *sun8i_hdmi_phy_get(struct device_node *node)
return phy;
}
+int sun8i_hdmi_phy_register_cec(struct sun8i_hdmi_phy *phy, struct device *dev)
+{
+ int ret;
if (!pdev)
return -EPROBE_DEFER;
@@ -666,8 +707,35 @@
hdmi->phy = phy;
put_device(&pdev->dev);
+
+ if (phy->bit_bang_cec) {
+ phy->cec_adapter =
+ cec_pin_allocate_adapter(&sun8i_hdmi_phy_cec_pin_ops,
+ phy, "sun8i-cec",
+ CEC_CAP_DEFAULTS);
+ ret = PTR_ERR_OR_ZERO(phy->cec_adapter);
+ if (ret < 0)
+ return 0;
+
+ phy->cec_notifier = cec_notifier_cec_adap_register(hdmi->dev, NULL, phy->cec_adapter);
+ if (!phy->cec_notifier) {
+ ret = -ENOMEM;
+ goto err_delete_cec_adapter;
+ }
+ if (!phy->bit_bang_cec)
+ return 0;
+
+ ret = cec_register_adapter(phy->cec_adapter, hdmi->dev);
+ if (ret < 0)
+ goto err_put_cec_notifier;
+ phy->cec_adapter =
+ cec_pin_allocate_adapter(&sun8i_hdmi_phy_cec_pin_ops,
+ phy, "sun8i-cec",
+ CEC_CAP_DEFAULTS);
+ if (IS_ERR(phy->cec_adapter))
+ return PTR_ERR(phy->cec_adapter);
+
+ phy->cec_notifier = cec_notifier_cec_adap_register(dev, NULL,
+ phy->cec_adapter);
+ if (!phy->cec_notifier) {
+ ret = -ENOMEM;
+ goto err_delete_cec_adapter;
+ }
return 0;
+
+ ret = cec_register_adapter(phy->cec_adapter, dev);
+ if (ret < 0)
+ goto err_put_cec_notifier;
+
+ return 0;
+
+err_put_cec_notifier:
+ cec_notifier_cec_adap_unregister(phy->cec_notifier, phy->cec_adapter);
@ -177,10 +196,12 @@ index b64d93da651d..e2936e7745b8 100644
+ cec_delete_adapter(phy->cec_adapter);
+
+ return ret;
}
+}
+
static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
@@ -682,6 +750,14 @@
{
struct device *dev = &pdev->dev;
@@ -681,6 +756,14 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
phy->variant = of_device_get_match_data(dev);
phy->dev = dev;
@ -195,7 +216,7 @@ index b64d93da651d..e2936e7745b8 100644
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
@@ -728,8 +804,19 @@
@@ -727,8 +810,19 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
return 0;
}
@ -215,3 +236,6 @@ index b64d93da651d..e2936e7745b8 100644
.driver = {
.name = "sun8i-hdmi-phy",
.of_match_table = sun8i_hdmi_phy_of_table,
--
2.42.0