summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/meson
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/meson')
-rw-r--r--drivers/gpu/drm/meson/Kconfig17
-rw-r--r--drivers/gpu/drm/meson/Makefile4
-rw-r--r--drivers/gpu/drm/meson/meson_drv.c218
-rw-r--r--drivers/gpu/drm/meson/meson_drv.h11
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c458
-rw-r--r--drivers/gpu/drm/meson/meson_dw_mipi_dsi.c358
-rw-r--r--drivers/gpu/drm/meson/meson_dw_mipi_dsi.h160
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.c298
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.h (renamed from drivers/gpu/drm/meson/meson_venc_cvbs.h)3
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.c171
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.h13
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.c496
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.h13
-rw-r--r--drivers/gpu/drm/meson/meson_osd_afbcd.c41
-rw-r--r--drivers/gpu/drm/meson/meson_osd_afbcd.h1
-rw-r--r--drivers/gpu/drm/meson/meson_overlay.c23
-rw-r--r--drivers/gpu/drm/meson/meson_plane.c36
-rw-r--r--drivers/gpu/drm/meson/meson_registers.h30
-rw-r--r--drivers/gpu/drm/meson/meson_vclk.c226
-rw-r--r--drivers/gpu/drm/meson/meson_vclk.h13
-rw-r--r--drivers/gpu/drm/meson/meson_venc.c248
-rw-r--r--drivers/gpu/drm/meson/meson_venc.h6
-rw-r--r--drivers/gpu/drm/meson/meson_venc_cvbs.c293
-rw-r--r--drivers/gpu/drm/meson/meson_viu.c36
-rw-r--r--drivers/gpu/drm/meson/meson_vpp.c2
-rw-r--r--drivers/gpu/drm/meson/meson_vpp.h2
26 files changed, 2185 insertions, 992 deletions
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig
index 9f9281dd49f8..417f79829cf8 100644
--- a/drivers/gpu/drm/meson/Kconfig
+++ b/drivers/gpu/drm/meson/Kconfig
@@ -1,14 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_MESON
tristate "DRM Support for Amlogic Meson Display Controller"
- depends on DRM && OF && (ARM || ARM64)
+ depends on DRM && OF && (ARM || ARM64 || COMPILE_TEST)
depends on ARCH_MESON || COMPILE_TEST
+ select DRM_CLIENT_SELECTION
select DRM_KMS_HELPER
- select DRM_KMS_CMA_HELPER
- select DRM_GEM_CMA_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_BRIDGE_CONNECTOR
+ select DRM_GEM_DMA_HELPER
+ select DRM_DISPLAY_CONNECTOR
select VIDEOMODE_HELPERS
select REGMAP_MMIO
select MESON_CANVAS
+ select CEC_CORE if CEC_NOTIFIER
config DRM_MESON_DW_HDMI
tristate "HDMI Synopsys Controller support for Amlogic Meson Display"
@@ -16,3 +20,10 @@ config DRM_MESON_DW_HDMI
default y if DRM_MESON
select DRM_DW_HDMI
imply DRM_DW_HDMI_I2S_AUDIO
+
+config DRM_MESON_DW_MIPI_DSI
+ tristate "MIPI DSI Synopsys Controller support for Amlogic Meson Display"
+ depends on DRM_MESON
+ default y if DRM_MESON
+ select DRM_DW_MIPI_DSI
+ select GENERIC_PHY_MIPI_DPHY
diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile
index 28a519cdf66b..43071bdbd4b9 100644
--- a/drivers/gpu/drm/meson/Makefile
+++ b/drivers/gpu/drm/meson/Makefile
@@ -1,7 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
-meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
+meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_encoder_cvbs.o
meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o
meson-drm-y += meson_rdma.o meson_osd_afbcd.o
+meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o
obj-$(CONFIG_DRM_MESON) += meson-drm.o
obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
+obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index a7388bf7c838..49ff9f1f16d3 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -8,6 +8,7 @@
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
+#include <linux/aperture.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of_graph.h>
@@ -15,14 +16,14 @@
#include <linux/platform_device.h>
#include <linux/soc/amlogic/meson-canvas.h>
-#include <drm/drm_aperture.h>
+#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_irq.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -32,7 +33,9 @@
#include "meson_plane.h"
#include "meson_osd_afbcd.h"
#include "meson_registers.h"
-#include "meson_venc_cvbs.h"
+#include "meson_encoder_cvbs.h"
+#include "meson_encoder_hdmi.h"
+#include "meson_encoder_dsi.h"
#include "meson_viu.h"
#include "meson_vpp.h"
#include "meson_rdma.h"
@@ -86,25 +89,22 @@ static int meson_dumb_create(struct drm_file *file, struct drm_device *dev,
args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64);
args->size = PAGE_ALIGN(args->pitch * args->height);
- return drm_gem_cma_dumb_create_internal(file, dev, args);
+ return drm_gem_dma_dumb_create_internal(file, dev, args);
}
-DEFINE_DRM_GEM_CMA_FOPS(fops);
+DEFINE_DRM_GEM_DMA_FOPS(fops);
static const struct drm_driver meson_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
- /* IRQ */
- .irq_handler = meson_irq,
-
- /* CMA Ops */
- DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
+ /* DMA Ops */
+ DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
+ DRM_FBDEV_DMA_DRIVER_OPS,
/* Misc */
.fops = &fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
- .date = "20161109",
.major = 1,
.minor = 0,
};
@@ -117,14 +117,17 @@ static bool meson_vpu_has_available_connectors(struct device *dev)
for_each_endpoint_of_node(dev->of_node, ep) {
/* If the endpoint node exists, consider it enabled */
remote = of_graph_get_remote_port(ep);
- if (remote)
+ if (remote) {
+ of_node_put(remote);
+ of_node_put(ep);
return true;
+ }
}
return false;
}
-static struct regmap_config meson_regmap_config = {
+static const struct regmap_config meson_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
@@ -166,11 +169,11 @@ static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
/* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
{
.limits = {
- .max_hdmi_phy_freq = 1650000,
+ .max_hdmi_phy_freq = 1650000000,
},
.attrs = (const struct soc_device_attribute []) {
{ .soc_id = "GXL (S805*)", },
- { /* sentinel */ },
+ { /* sentinel */ }
}
},
};
@@ -210,8 +213,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
priv->compat = match->compat;
priv->afbcd.ops = match->afbcd_ops;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
- regs = devm_ioremap_resource(dev, res);
+ regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto free_drm;
@@ -249,29 +251,20 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (ret)
goto free_drm;
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
- if (ret) {
- meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
- goto free_drm;
- }
+ if (ret)
+ goto free_canvas_osd1;
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
- if (ret) {
- meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
- meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
- goto free_drm;
- }
+ if (ret)
+ goto free_canvas_vd1_0;
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
- if (ret) {
- meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
- meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
- meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
- goto free_drm;
- }
+ if (ret)
+ goto free_canvas_vd1_1;
priv->vsync_irq = platform_get_irq(pdev, 0);
ret = drm_vblank_init(drm, 1);
if (ret)
- goto free_drm;
+ goto free_canvas_vd1_2;
/* Assign limits per soc revision/package */
for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) {
@@ -285,13 +278,13 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
* Remove early framebuffers (ie. simplefb). The framebuffer can be
* located anywhere in RAM
*/
- ret = drm_aperture_remove_framebuffers(false, "meson-drm-fb");
+ ret = aperture_remove_all_conflicting_devices(meson_driver.name);
if (ret)
- goto free_drm;
+ goto free_canvas_vd1_2;
ret = drmm_mode_config_init(drm);
if (ret)
- goto free_drm;
+ goto free_canvas_vd1_2;
drm->mode_config.max_width = 3840;
drm->mode_config.max_height = 2160;
drm->mode_config.funcs = &meson_mode_config_funcs;
@@ -306,38 +299,50 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (priv->afbcd.ops) {
ret = priv->afbcd.ops->init(priv);
if (ret)
- return ret;
+ goto free_canvas_vd1_2;
}
/* Encoder Initialization */
- ret = meson_venc_cvbs_create(priv);
+ ret = meson_encoder_cvbs_probe(priv);
if (ret)
- goto free_drm;
+ goto exit_afbcd;
if (has_components) {
- ret = component_bind_all(drm->dev, drm);
+ ret = component_bind_all(dev, drm);
if (ret) {
dev_err(drm->dev, "Couldn't bind all components\n");
- goto free_drm;
+ /* Do not try to unbind */
+ has_components = false;
+ goto exit_afbcd;
}
}
+ ret = meson_encoder_hdmi_probe(priv);
+ if (ret)
+ goto exit_afbcd;
+
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
+ ret = meson_encoder_dsi_probe(priv);
+ if (ret)
+ goto exit_afbcd;
+ }
+
ret = meson_plane_create(priv);
if (ret)
- goto free_drm;
+ goto exit_afbcd;
ret = meson_overlay_create(priv);
if (ret)
- goto free_drm;
+ goto exit_afbcd;
ret = meson_crtc_create(priv);
if (ret)
- goto free_drm;
+ goto exit_afbcd;
- ret = drm_irq_install(drm, priv->vsync_irq);
+ ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm);
if (ret)
- goto free_drm;
+ goto exit_afbcd;
drm_mode_config_reset(drm);
@@ -349,15 +354,33 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (ret)
goto uninstall_irq;
- drm_fbdev_generic_setup(drm, 32);
+ drm_client_setup(drm, NULL);
return 0;
uninstall_irq:
- drm_irq_uninstall(drm);
+ free_irq(priv->vsync_irq, drm);
+exit_afbcd:
+ if (priv->afbcd.ops)
+ priv->afbcd.ops->exit(priv);
+free_canvas_vd1_2:
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
+free_canvas_vd1_1:
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
+free_canvas_vd1_0:
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
+free_canvas_osd1:
+ meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
free_drm:
drm_dev_put(drm);
+ meson_encoder_dsi_remove(priv);
+ meson_encoder_hdmi_remove(priv);
+ meson_encoder_cvbs_remove(priv);
+
+ if (has_components)
+ component_unbind_all(dev, drm);
+
return ret;
}
@@ -381,14 +404,17 @@ static void meson_drv_unbind(struct device *dev)
drm_dev_unregister(drm);
drm_kms_helper_poll_fini(drm);
drm_atomic_helper_shutdown(drm);
- component_unbind_all(dev, drm);
- drm_irq_uninstall(drm);
+ free_irq(priv->vsync_irq, drm);
drm_dev_put(drm);
- if (priv->afbcd.ops) {
- priv->afbcd.ops->reset(priv);
- meson_rdma_free(priv);
- }
+ meson_encoder_dsi_remove(priv);
+ meson_encoder_hdmi_remove(priv);
+ meson_encoder_cvbs_remove(priv);
+
+ component_unbind_all(dev, drm);
+
+ if (priv->afbcd.ops)
+ priv->afbcd.ops->exit(priv);
}
static const struct component_master_ops meson_drv_master_ops = {
@@ -423,54 +449,6 @@ static int __maybe_unused meson_drv_pm_resume(struct device *dev)
return drm_mode_config_helper_resume(priv->drm);
}
-static int compare_of(struct device *dev, void *data)
-{
- DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n",
- dev->of_node, data);
-
- return dev->of_node == data;
-}
-
-/* Possible connectors nodes to ignore */
-static const struct of_device_id connectors_match[] = {
- { .compatible = "composite-video-connector" },
- { .compatible = "svideo-connector" },
- { .compatible = "hdmi-connector" },
- { .compatible = "dvi-connector" },
- {}
-};
-
-static int meson_probe_remote(struct platform_device *pdev,
- struct component_match **match,
- struct device_node *parent,
- struct device_node *remote)
-{
- struct device_node *ep, *remote_node;
- int count = 1;
-
- /* If node is a connector, return and do not add to match table */
- if (of_match_node(connectors_match, remote))
- return 1;
-
- component_match_add(&pdev->dev, match, compare_of, remote);
-
- for_each_endpoint_of_node(remote, ep) {
- remote_node = of_graph_get_remote_port_parent(ep);
- if (!remote_node ||
- remote_node == parent || /* Ignore parent endpoint */
- !of_device_is_available(remote_node)) {
- of_node_put(remote_node);
- continue;
- }
-
- count += meson_probe_remote(pdev, match, remote, remote_node);
-
- of_node_put(remote_node);
- }
-
- return count;
-}
-
static void meson_drv_shutdown(struct platform_device *pdev)
{
struct meson_drm *priv = dev_get_drvdata(&pdev->dev);
@@ -482,6 +460,20 @@ static void meson_drv_shutdown(struct platform_device *pdev)
drm_atomic_helper_shutdown(priv->drm);
}
+/*
+ * Only devices to use as components
+ * TOFIX: get rid of components when we can finally
+ * get meson_dx_hdmi to stop using the meson_drm
+ * private structure for HHI registers.
+ */
+static const struct of_device_id components_dev_match[] = {
+ { .compatible = "amlogic,meson-gxbb-dw-hdmi" },
+ { .compatible = "amlogic,meson-gxl-dw-hdmi" },
+ { .compatible = "amlogic,meson-gxm-dw-hdmi" },
+ { .compatible = "amlogic,meson-g12a-dw-hdmi" },
+ {}
+};
+
static int meson_drv_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
@@ -496,8 +488,16 @@ static int meson_drv_probe(struct platform_device *pdev)
continue;
}
- count += meson_probe_remote(pdev, &match, np, remote);
+ if (of_match_node(components_dev_match, remote)) {
+ component_match_add(&pdev->dev, &match, component_compare_of, remote);
+
+ dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n",
+ np, remote, dev_name(&pdev->dev));
+ }
+
of_node_put(remote);
+
+ ++count;
}
if (count && !match)
@@ -516,6 +516,11 @@ static int meson_drv_probe(struct platform_device *pdev)
return 0;
};
+static void meson_drv_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &meson_drv_master_ops);
+}
+
static struct meson_drm_match_data meson_drm_gxbb_data = {
.compat = VPU_COMPATIBLE_GXBB,
};
@@ -553,6 +558,7 @@ static const struct dev_pm_ops meson_drv_pm_ops = {
static struct platform_driver meson_drm_platform_driver = {
.probe = meson_drv_probe,
+ .remove = meson_drv_remove,
.shutdown = meson_drv_shutdown,
.driver = {
.name = "meson-drm",
@@ -561,7 +567,7 @@ static struct platform_driver meson_drm_platform_driver = {
},
};
-module_platform_driver(meson_drm_platform_driver);
+drm_module_platform_driver(meson_drm_platform_driver);
MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
index 177dac3ca3be..be4b0e4df6e1 100644
--- a/drivers/gpu/drm/meson/meson_drv.h
+++ b/drivers/gpu/drm/meson/meson_drv.h
@@ -9,7 +9,6 @@
#include <linux/device.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
struct drm_crtc;
@@ -25,13 +24,20 @@ enum vpu_compatible {
VPU_COMPATIBLE_G12A = 3,
};
+enum {
+ MESON_ENC_CVBS = 0,
+ MESON_ENC_HDMI,
+ MESON_ENC_DSI,
+ MESON_ENC_LAST,
+};
+
struct meson_drm_match_data {
enum vpu_compatible compat;
struct meson_afbcd_ops *afbcd_ops;
};
struct meson_drm_soc_limits {
- unsigned int max_hdmi_phy_freq;
+ unsigned long long max_hdmi_phy_freq;
};
struct meson_drm {
@@ -51,6 +57,7 @@ struct meson_drm {
struct drm_crtc *crtc;
struct drm_plane *primary_plane;
struct drm_plane *overlay_plane;
+ void *encoders[MESON_ENC_LAST];
const struct meson_drm_soc_limits *limits;
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 2ed87cfdd735..0d7c68b29dff 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -9,8 +9,9 @@
#include <linux/component.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_graph.h>
+#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -22,14 +23,11 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_print.h>
-#include <linux/media-bus-format.h>
#include <linux/videodev2.h>
#include "meson_drv.h"
#include "meson_dw_hdmi.h"
#include "meson_registers.h"
-#include "meson_vclk.h"
-#include "meson_venc.h"
#define DRIVER_NAME "meson-dw-hdmi"
#define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver"
@@ -108,6 +106,8 @@
#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */
#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */
#define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
+#define PHY_CNTL1_INIT 0x03900000
+#define PHY_INVERT BIT(17)
#define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
#define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
#define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */
@@ -132,11 +132,11 @@ struct meson_dw_hdmi_data {
unsigned int addr);
void (*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data);
+ u32 cntl0_init;
+ u32 cntl1_init;
};
struct meson_dw_hdmi {
- struct drm_encoder encoder;
- struct drm_bridge bridge;
struct dw_hdmi_plat_data dw_plat_data;
struct meson_drm *priv;
struct device *dev;
@@ -145,15 +145,10 @@ struct meson_dw_hdmi {
struct reset_control *hdmitx_apb;
struct reset_control *hdmitx_ctrl;
struct reset_control *hdmitx_phy;
- struct regulator *hdmi_supply;
u32 irq_stat;
struct dw_hdmi *hdmi;
- unsigned long output_bus_fmt;
+ struct drm_bridge *bridge;
};
-#define encoder_to_meson_dw_hdmi(x) \
- container_of(x, struct meson_dw_hdmi, encoder)
-#define bridge_to_meson_dw_hdmi(x) \
- container_of(x, struct meson_dw_hdmi, bridge)
static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
const char *compat)
@@ -277,32 +272,18 @@ static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
writeb(data, dw_hdmi->hdmitx + addr);
}
-/* Helper to change specific bits in controller registers */
-static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
- unsigned int addr,
- unsigned int mask,
- unsigned int val)
-{
- unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr);
-
- data &= ~mask;
- data |= val;
-
- dw_hdmi->data->dwc_write(dw_hdmi, addr, data);
-}
-
/* Bridge */
/* Setup PHY bandwidth modes */
static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
- const struct drm_display_mode *mode)
+ const struct drm_display_mode *mode,
+ bool mode_is_420)
{
struct meson_drm *priv = dw_hdmi->priv;
unsigned int pixel_clock = mode->clock;
/* For 420, pixel clock is half unlike venc clock */
- if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
- pixel_clock /= 2;
+ if (mode_is_420) pixel_clock /= 2;
if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) {
@@ -374,91 +355,27 @@ static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
mdelay(2);
}
-static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
- const struct drm_display_mode *mode)
-{
- struct meson_drm *priv = dw_hdmi->priv;
- int vic = drm_match_cea_mode(mode);
- unsigned int phy_freq;
- unsigned int vclk_freq;
- unsigned int venc_freq;
- unsigned int hdmi_freq;
-
- vclk_freq = mode->clock;
-
- /* For 420, pixel clock is half unlike venc clock */
- if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
- vclk_freq /= 2;
-
- /* TMDS clock is pixel_clock * 10 */
- phy_freq = vclk_freq * 10;
-
- if (!vic) {
- meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
- vclk_freq, vclk_freq, vclk_freq, false);
- return;
- }
-
- /* 480i/576i needs global pixel doubling */
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- vclk_freq *= 2;
-
- venc_freq = vclk_freq;
- hdmi_freq = vclk_freq;
-
- /* VENC double pixels for 1080i, 720p and YUV420 modes */
- if (meson_venc_hdmi_venc_repeat(vic) ||
- dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
- venc_freq *= 2;
-
- vclk_freq = max(venc_freq, hdmi_freq);
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- venc_freq /= 2;
-
- DRM_DEBUG_DRIVER("vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n",
- phy_freq, vclk_freq, venc_freq, hdmi_freq,
- priv->venc.hdmi_use_enci);
-
- meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
- venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
-}
-
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *display,
const struct drm_display_mode *mode)
{
struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
+ bool is_hdmi2_sink = display->hdmi.scdc.supported;
struct meson_drm *priv = dw_hdmi->priv;
unsigned int wr_clk =
readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING));
+ bool mode_is_420 = false;
DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name,
mode->clock > 340000 ? 40 : 10);
- /* Enable clocks */
- regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
-
- /* Bring HDMITX MEM output of power down */
- regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
-
- /* Bring out of reset */
- dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0);
-
- /* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
- dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
- 0x3, 0x3);
-
- /* Enable cec_clk and hdcp22_tmdsclk_en */
- dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
- 0x3 << 4, 0x3 << 4);
-
- /* Enable normal output to PHY */
- dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
+ if (drm_mode_is_420_only(display, mode) ||
+ (!is_hdmi2_sink && drm_mode_is_420_also(display, mode)) ||
+ dw_hdmi_bus_fmt_is_420(hdmi))
+ mode_is_420 = true;
/* TMDS pattern setup */
- if (mode->clock > 340000 &&
- dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_YUV8_1X24) {
+ if (mode->clock > 340000 && !mode_is_420) {
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
0);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
@@ -476,21 +393,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
/* Setup PHY parameters */
- meson_hdmi_phy_setup_mode(dw_hdmi, mode);
-
- /* Setup PHY */
- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
- 0xffff << 16, 0x0390 << 16);
-
- /* BIT_INVERT */
- if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
- dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
- dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
- BIT(17), 0);
- else
- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
- BIT(17), BIT(17));
+ meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420);
/* Disable clock, fifo, fifo_wr */
regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
@@ -545,7 +448,9 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
DRM_DEBUG_DRIVER("\n");
- regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0);
+ /* Fallback to init mode */
+ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, dw_hdmi->data->cntl1_init);
+ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, dw_hdmi->data->cntl0_init);
}
static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
@@ -622,214 +527,15 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected,
hpd_connected);
- drm_helper_hpd_irq_event(dw_hdmi->encoder.dev);
+ drm_helper_hpd_irq_event(dw_hdmi->bridge->dev);
+ drm_bridge_hpd_notify(dw_hdmi->bridge,
+ hpd_connected ? connector_status_connected
+ : connector_status_disconnected);
}
return IRQ_HANDLED;
}
-static enum drm_mode_status
-dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
- const struct drm_display_info *display_info,
- const struct drm_display_mode *mode)
-{
- struct meson_dw_hdmi *dw_hdmi = data;
- struct meson_drm *priv = dw_hdmi->priv;
- bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
- unsigned int phy_freq;
- unsigned int vclk_freq;
- unsigned int venc_freq;
- unsigned int hdmi_freq;
- int vic = drm_match_cea_mode(mode);
- enum drm_mode_status status;
-
- DRM_DEBUG_DRIVER("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
-
- /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */
- if (display_info->max_tmds_clock &&
- mode->clock > display_info->max_tmds_clock &&
- !drm_mode_is_420_only(display_info, mode) &&
- !drm_mode_is_420_also(display_info, mode))
- return MODE_BAD;
-
- /* Check against non-VIC supported modes */
- if (!vic) {
- status = meson_venc_hdmi_supported_mode(mode);
- if (status != MODE_OK)
- return status;
-
- return meson_vclk_dmt_supported_freq(priv, mode->clock);
- /* Check against supported VIC modes */
- } else if (!meson_venc_hdmi_supported_vic(vic))
- return MODE_BAD;
-
- vclk_freq = mode->clock;
-
- /* For 420, pixel clock is half unlike venc clock */
- if (drm_mode_is_420_only(display_info, mode) ||
- (!is_hdmi2_sink &&
- drm_mode_is_420_also(display_info, mode)))
- vclk_freq /= 2;
-
- /* TMDS clock is pixel_clock * 10 */
- phy_freq = vclk_freq * 10;
-
- /* 480i/576i needs global pixel doubling */
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- vclk_freq *= 2;
-
- venc_freq = vclk_freq;
- hdmi_freq = vclk_freq;
-
- /* VENC double pixels for 1080i, 720p and YUV420 modes */
- if (meson_venc_hdmi_venc_repeat(vic) ||
- drm_mode_is_420_only(display_info, mode) ||
- (!is_hdmi2_sink &&
- drm_mode_is_420_also(display_info, mode)))
- venc_freq *= 2;
-
- vclk_freq = max(venc_freq, hdmi_freq);
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- venc_freq /= 2;
-
- dev_dbg(dw_hdmi->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
- __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
-
- return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
-}
-
-/* Encoder */
-
-static const u32 meson_dw_hdmi_out_bus_fmts[] = {
- MEDIA_BUS_FMT_YUV8_1X24,
- MEDIA_BUS_FMT_UYYVYY8_0_5X24,
-};
-
-static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder)
-{
- drm_encoder_cleanup(encoder);
-}
-
-static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = {
- .destroy = meson_venc_hdmi_encoder_destroy,
-};
-
-static u32 *
-meson_venc_hdmi_encoder_get_inp_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)
-{
- u32 *input_fmts = NULL;
- int i;
-
- *num_input_fmts = 0;
-
- for (i = 0 ; i < ARRAY_SIZE(meson_dw_hdmi_out_bus_fmts) ; ++i) {
- if (output_fmt == meson_dw_hdmi_out_bus_fmts[i]) {
- *num_input_fmts = 1;
- input_fmts = kcalloc(*num_input_fmts,
- sizeof(*input_fmts),
- GFP_KERNEL);
- if (!input_fmts)
- return NULL;
-
- input_fmts[0] = output_fmt;
-
- break;
- }
- }
-
- return input_fmts;
-}
-
-static int meson_venc_hdmi_encoder_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 meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
-
- dw_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format;
-
- DRM_DEBUG_DRIVER("output_bus_fmt %lx\n", dw_hdmi->output_bus_fmt);
-
- return 0;
-}
-
-static void meson_venc_hdmi_encoder_disable(struct drm_bridge *bridge)
-{
- struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
- struct meson_drm *priv = dw_hdmi->priv;
-
- DRM_DEBUG_DRIVER("\n");
-
- writel_bits_relaxed(0x3, 0,
- priv->io_base + _REG(VPU_HDMI_SETTING));
-
- writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
- writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
-}
-
-static void meson_venc_hdmi_encoder_enable(struct drm_bridge *bridge)
-{
- struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
- struct meson_drm *priv = dw_hdmi->priv;
-
- DRM_DEBUG_DRIVER("%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
-
- if (priv->venc.hdmi_use_enci)
- writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
- else
- writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
-}
-
-static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
-{
- struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
- struct meson_drm *priv = dw_hdmi->priv;
- int vic = drm_match_cea_mode(mode);
- unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR;
- bool yuv420_mode = false;
-
- DRM_DEBUG_DRIVER("\"%s\" vic %d\n", mode->name, vic);
-
- if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
- ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
- yuv420_mode = true;
- }
-
- /* VENC + VENC-DVI Mode setup */
- meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);
-
- /* VCLK Set clock */
- dw_hdmi_set_vclk(dw_hdmi, mode);
-
- if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
- /* Setup YUV420 to HDMI-TX, no 10bit diphering */
- writel_relaxed(2 | (2 << 2),
- priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
- else
- /* Setup YUV444 to HDMI-TX, no 10bit diphering */
- writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
-}
-
-static const struct drm_bridge_funcs meson_venc_hdmi_encoder_bridge_funcs = {
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_get_input_bus_fmts = meson_venc_hdmi_encoder_get_inp_bus_fmts,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_check = meson_venc_hdmi_encoder_atomic_check,
- .enable = meson_venc_hdmi_encoder_enable,
- .disable = meson_venc_hdmi_encoder_disable,
- .mode_set = meson_venc_hdmi_encoder_mode_set,
-};
-
/* DW HDMI Regmap */
static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
@@ -862,11 +568,22 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = {
.fast_io = true,
};
-static const struct meson_dw_hdmi_data meson_dw_hdmi_gx_data = {
+static const struct meson_dw_hdmi_data meson_dw_hdmi_gxbb_data = {
+ .top_read = dw_hdmi_top_read,
+ .top_write = dw_hdmi_top_write,
+ .dwc_read = dw_hdmi_dwc_read,
+ .dwc_write = dw_hdmi_dwc_write,
+ .cntl0_init = 0x0,
+ .cntl1_init = PHY_CNTL1_INIT | PHY_INVERT,
+};
+
+static const struct meson_dw_hdmi_data meson_dw_hdmi_gxl_data = {
.top_read = dw_hdmi_top_read,
.top_write = dw_hdmi_top_write,
.dwc_read = dw_hdmi_dwc_read,
.dwc_write = dw_hdmi_dwc_write,
+ .cntl0_init = 0x0,
+ .cntl1_init = PHY_CNTL1_INIT,
};
static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
@@ -874,30 +591,10 @@ static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
.top_write = dw_hdmi_g12a_top_write,
.dwc_read = dw_hdmi_g12a_dwc_read,
.dwc_write = dw_hdmi_g12a_dwc_write,
+ .cntl0_init = 0x000b4242, /* Bandgap */
+ .cntl1_init = PHY_CNTL1_INIT,
};
-static bool meson_hdmi_connector_is_available(struct device *dev)
-{
- struct device_node *ep, *remote;
-
- /* HDMI Connector is on the second port, first endpoint */
- ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, 0);
- if (!ep)
- return false;
-
- /* If the endpoint node exists, consider it enabled */
- remote = of_graph_get_remote_port(ep);
- if (remote) {
- of_node_put(ep);
- return true;
- }
-
- of_node_put(ep);
- of_node_put(remote);
-
- return false;
-}
-
static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
{
struct meson_drm *priv = meson_dw_hdmi->priv;
@@ -930,6 +627,13 @@ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
meson_dw_hdmi->data->top_write(meson_dw_hdmi,
HDMITX_TOP_CLK_CNTL, 0xff);
+ /* Enable normal output to PHY */
+ meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
+
+ /* Setup PHY */
+ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, meson_dw_hdmi->data->cntl1_init);
+ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, meson_dw_hdmi->data->cntl0_init);
+
/* Enable HDMI-TX Interrupt */
meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
HDMITX_TOP_INTR_CORE);
@@ -939,11 +643,6 @@ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
}
-static void meson_disable_regulator(void *data)
-{
- regulator_disable(data);
-}
-
static void meson_disable_clk(void *data)
{
clk_disable_unprepare(data);
@@ -976,19 +675,11 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct meson_drm *priv = drm->dev_private;
struct dw_hdmi_plat_data *dw_plat_data;
- struct drm_bridge *next_bridge;
- struct drm_encoder *encoder;
- struct resource *res;
int irq;
int ret;
DRM_DEBUG_DRIVER("\n");
- if (!meson_hdmi_connector_is_available(dev)) {
- dev_info(drm->dev, "HDMI Output connector not available\n");
- return -ENODEV;
- }
-
match = of_device_get_match_data(&pdev->dev);
if (!match) {
dev_err(&pdev->dev, "failed to get match data\n");
@@ -1004,22 +695,10 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
meson_dw_hdmi->dev = dev;
meson_dw_hdmi->data = match;
dw_plat_data = &meson_dw_hdmi->dw_plat_data;
- encoder = &meson_dw_hdmi->encoder;
- meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi");
- if (IS_ERR(meson_dw_hdmi->hdmi_supply)) {
- if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- meson_dw_hdmi->hdmi_supply = NULL;
- } else {
- ret = regulator_enable(meson_dw_hdmi->hdmi_supply);
- if (ret)
- return ret;
- ret = devm_add_action_or_reset(dev, meson_disable_regulator,
- meson_dw_hdmi->hdmi_supply);
- if (ret)
- return ret;
- }
+ ret = devm_regulator_get_enable_optional(dev, "hdmi");
+ if (ret < 0 && ret != -ENODEV)
+ return ret;
meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev,
"hdmitx_apb");
@@ -1042,8 +721,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
return PTR_ERR(meson_dw_hdmi->hdmitx_phy);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- meson_dw_hdmi->hdmitx = devm_ioremap_resource(dev, res);
+ meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(meson_dw_hdmi->hdmitx))
return PTR_ERR(meson_dw_hdmi->hdmitx);
@@ -1076,34 +754,18 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
return ret;
}
- /* Encoder */
-
- ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs,
- DRM_MODE_ENCODER_TMDS, "meson_hdmi");
- if (ret) {
- dev_err(priv->dev, "Failed to init HDMI encoder\n");
- return ret;
- }
-
- meson_dw_hdmi->bridge.funcs = &meson_venc_hdmi_encoder_bridge_funcs;
- drm_bridge_attach(encoder, &meson_dw_hdmi->bridge, NULL, 0);
-
- encoder->possible_crtcs = BIT(0);
-
meson_dw_hdmi_init(meson_dw_hdmi);
- DRM_DEBUG_DRIVER("encoder initialized\n");
-
/* Bridge / Connector */
dw_plat_data->priv_data = meson_dw_hdmi;
- dw_plat_data->mode_valid = dw_hdmi_mode_valid;
dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
dw_plat_data->phy_name = "meson_dw_hdmi_phy";
dw_plat_data->phy_data = meson_dw_hdmi;
dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
dw_plat_data->ycbcr_420_allowed = true;
dw_plat_data->disable_cec = true;
+ dw_plat_data->output_port = 1;
if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
@@ -1112,15 +774,11 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
platform_set_drvdata(pdev, meson_dw_hdmi);
- meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev,
- &meson_dw_hdmi->dw_plat_data);
+ meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data);
if (IS_ERR(meson_dw_hdmi->hdmi))
return PTR_ERR(meson_dw_hdmi->hdmi);
- next_bridge = of_drm_find_bridge(pdev->dev.of_node);
- if (next_bridge)
- drm_bridge_attach(encoder, next_bridge,
- &meson_dw_hdmi->bridge, 0);
+ meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node);
DRM_DEBUG_DRIVER("HDMI controller initialized\n");
@@ -1173,11 +831,9 @@ static int meson_dw_hdmi_probe(struct platform_device *pdev)
return component_add(&pdev->dev, &meson_dw_hdmi_ops);
}
-static int meson_dw_hdmi_remove(struct platform_device *pdev)
+static void meson_dw_hdmi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &meson_dw_hdmi_ops);
-
- return 0;
}
static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
@@ -1187,11 +843,11 @@ static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
static const struct of_device_id meson_dw_hdmi_of_table[] = {
{ .compatible = "amlogic,meson-gxbb-dw-hdmi",
- .data = &meson_dw_hdmi_gx_data },
+ .data = &meson_dw_hdmi_gxbb_data },
{ .compatible = "amlogic,meson-gxl-dw-hdmi",
- .data = &meson_dw_hdmi_gx_data },
+ .data = &meson_dw_hdmi_gxl_data },
{ .compatible = "amlogic,meson-gxm-dw-hdmi",
- .data = &meson_dw_hdmi_gx_data },
+ .data = &meson_dw_hdmi_gxl_data },
{ .compatible = "amlogic,meson-g12a-dw-hdmi",
.data = &meson_dw_hdmi_g12a_data },
{ }
diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
new file mode 100644
index 000000000000..66c73c512b0e
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2021 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/bitfield.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/bridge/dw_mipi_dsi.h>
+#include <drm/drm_mipi_dsi.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_print.h>
+
+#include "meson_drv.h"
+#include "meson_dw_mipi_dsi.h"
+#include "meson_registers.h"
+#include "meson_venc.h"
+
+#define DRIVER_NAME "meson-dw-mipi-dsi"
+#define DRIVER_DESC "Amlogic Meson MIPI-DSI DRM driver"
+
+struct meson_dw_mipi_dsi {
+ struct meson_drm *priv;
+ struct device *dev;
+ void __iomem *base;
+ struct phy *phy;
+ union phy_configure_opts phy_opts;
+ struct dw_mipi_dsi *dmd;
+ struct dw_mipi_dsi_plat_data pdata;
+ struct mipi_dsi_device *dsi_device;
+ const struct drm_display_mode *mode;
+ struct clk *bit_clk;
+ struct clk *px_clk;
+ struct reset_control *top_rst;
+};
+
+#define encoder_to_meson_dw_mipi_dsi(x) \
+ container_of(x, struct meson_dw_mipi_dsi, encoder)
+
+static void meson_dw_mipi_dsi_hw_init(struct meson_dw_mipi_dsi *mipi_dsi)
+{
+ /* Software reset */
+ writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR |
+ MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING,
+ MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR |
+ MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING,
+ mipi_dsi->base + MIPI_DSI_TOP_SW_RESET);
+ writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR |
+ MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING,
+ 0, mipi_dsi->base + MIPI_DSI_TOP_SW_RESET);
+
+ /* Enable clocks */
+ writel_bits_relaxed(MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN,
+ MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN,
+ mipi_dsi->base + MIPI_DSI_TOP_CLK_CNTL);
+
+ /* Take memory out of power down */
+ writel_relaxed(0, mipi_dsi->base + MIPI_DSI_TOP_MEM_PD);
+}
+
+static int dw_mipi_dsi_phy_init(void *priv_data)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+ unsigned int dpi_data_format, venc_data_width;
+ int ret;
+
+ /* Set the bit clock rate to hs_clk_rate */
+ ret = clk_set_rate(mipi_dsi->bit_clk,
+ mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate);
+ if (ret) {
+ dev_err(mipi_dsi->dev, "Failed to set DSI Bit clock rate %lu (ret %d)\n",
+ mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, ret);
+ return ret;
+ }
+
+ /* Make sure the rate of the bit clock is not modified by someone else */
+ ret = clk_rate_exclusive_get(mipi_dsi->bit_clk);
+ if (ret) {
+ dev_err(mipi_dsi->dev,
+ "Failed to set the exclusivity on the bit clock rate (ret %d)\n", ret);
+ return ret;
+ }
+
+ clk_disable_unprepare(mipi_dsi->px_clk);
+ ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000);
+
+ if (ret) {
+ dev_err(mipi_dsi->dev, "Failed to set DSI Pixel clock rate %u (%d)\n",
+ mipi_dsi->mode->clock * 1000, ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(mipi_dsi->px_clk);
+ if (ret) {
+ dev_err(mipi_dsi->dev, "Failed to enable DSI Pixel clock (ret %d)\n", ret);
+ return ret;
+ }
+
+ switch (mipi_dsi->dsi_device->format) {
+ case MIPI_DSI_FMT_RGB888:
+ dpi_data_format = DPI_COLOR_24BIT;
+ venc_data_width = VENC_IN_COLOR_24B;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ dpi_data_format = DPI_COLOR_18BIT_CFG_2;
+ venc_data_width = VENC_IN_COLOR_18B;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ case MIPI_DSI_FMT_RGB565:
+ return -EINVAL;
+ }
+
+ /* Configure color format for DPI register */
+ writel_relaxed(FIELD_PREP(MIPI_DSI_TOP_DPI_COLOR_MODE, dpi_data_format) |
+ FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) |
+ FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) |
+ FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) |
+ FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0),
+ mipi_dsi->base + MIPI_DSI_TOP_CNTL);
+
+ return phy_configure(mipi_dsi->phy, &mipi_dsi->phy_opts);
+}
+
+static void dw_mipi_dsi_phy_power_on(void *priv_data)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+
+ if (phy_power_on(mipi_dsi->phy))
+ dev_warn(mipi_dsi->dev, "Failed to power on PHY\n");
+}
+
+static void dw_mipi_dsi_phy_power_off(void *priv_data)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+
+ if (phy_power_off(mipi_dsi->phy))
+ dev_warn(mipi_dsi->dev, "Failed to power off PHY\n");
+
+ /* Remove the exclusivity on the bit clock rate */
+ clk_rate_exclusive_put(mipi_dsi->bit_clk);
+}
+
+static int
+dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format,
+ unsigned int *lane_mbps)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+ int bpp;
+
+ mipi_dsi->mode = mode;
+
+ bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->dsi_device->format);
+
+ phy_mipi_dphy_get_default_config(mode->clock * 1000,
+ bpp, mipi_dsi->dsi_device->lanes,
+ &mipi_dsi->phy_opts.mipi_dphy);
+
+ *lane_mbps = DIV_ROUND_UP(mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC);
+
+ return 0;
+}
+
+static int
+dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
+ struct dw_mipi_dsi_dphy_timing *timing)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+
+ switch (mipi_dsi->mode->hdisplay) {
+ case 240:
+ case 768:
+ case 1920:
+ case 2560:
+ timing->clk_lp2hs = 23;
+ timing->clk_hs2lp = 38;
+ timing->data_lp2hs = 15;
+ timing->data_hs2lp = 9;
+ break;
+
+ default:
+ timing->clk_lp2hs = 37;
+ timing->clk_hs2lp = 135;
+ timing->data_lp2hs = 50;
+ timing->data_hs2lp = 3;
+ }
+
+ return 0;
+}
+
+static int
+dw_mipi_dsi_get_esc_clk_rate(void *priv_data, unsigned int *esc_clk_rate)
+{
+ *esc_clk_rate = 4; /* Mhz */
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops meson_dw_mipi_dsi_phy_ops = {
+ .init = dw_mipi_dsi_phy_init,
+ .power_on = dw_mipi_dsi_phy_power_on,
+ .power_off = dw_mipi_dsi_phy_power_off,
+ .get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
+ .get_timing = dw_mipi_dsi_phy_get_timing,
+ .get_esc_clk_rate = dw_mipi_dsi_get_esc_clk_rate,
+};
+
+static int meson_dw_mipi_dsi_host_attach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+ int ret;
+
+ mipi_dsi->dsi_device = device;
+
+ switch (device->format) {
+ case MIPI_DSI_FMT_RGB888:
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ case MIPI_DSI_FMT_RGB565:
+ dev_err(mipi_dsi->dev, "invalid pixel format %d\n", device->format);
+ return -EINVAL;
+ }
+
+ ret = phy_init(mipi_dsi->phy);
+ if (ret)
+ return ret;
+
+ meson_dw_mipi_dsi_hw_init(mipi_dsi);
+
+ return 0;
+}
+
+static int meson_dw_mipi_dsi_host_detach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = priv_data;
+
+ if (device == mipi_dsi->dsi_device)
+ mipi_dsi->dsi_device = NULL;
+ else
+ return -EINVAL;
+
+ return phy_exit(mipi_dsi->phy);
+}
+
+static const struct dw_mipi_dsi_host_ops meson_dw_mipi_dsi_host_ops = {
+ .attach = meson_dw_mipi_dsi_host_attach,
+ .detach = meson_dw_mipi_dsi_host_detach,
+};
+
+static int meson_dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi;
+ struct device *dev = &pdev->dev;
+
+ mipi_dsi = devm_kzalloc(dev, sizeof(*mipi_dsi), GFP_KERNEL);
+ if (!mipi_dsi)
+ return -ENOMEM;
+
+ mipi_dsi->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mipi_dsi->base))
+ return PTR_ERR(mipi_dsi->base);
+
+ mipi_dsi->phy = devm_phy_get(dev, "dphy");
+ if (IS_ERR(mipi_dsi->phy))
+ return dev_err_probe(dev, PTR_ERR(mipi_dsi->phy),
+ "failed to get mipi dphy\n");
+
+ mipi_dsi->bit_clk = devm_clk_get_enabled(dev, "bit");
+ if (IS_ERR(mipi_dsi->bit_clk)) {
+ int ret = PTR_ERR(mipi_dsi->bit_clk);
+
+ /* TOFIX GP0 on some platforms fails to lock in early boot, defer probe */
+ if (ret == -EIO)
+ ret = -EPROBE_DEFER;
+
+ return dev_err_probe(dev, ret, "Unable to get enabled bit_clk\n");
+ }
+
+ mipi_dsi->px_clk = devm_clk_get_enabled(dev, "px");
+ if (IS_ERR(mipi_dsi->px_clk))
+ return dev_err_probe(dev, PTR_ERR(mipi_dsi->px_clk),
+ "Unable to get enabled px_clk\n");
+
+ /*
+ * We use a TOP reset signal because the APB reset signal
+ * is handled by the TOP control registers.
+ */
+ mipi_dsi->top_rst = devm_reset_control_get_exclusive(dev, "top");
+ if (IS_ERR(mipi_dsi->top_rst))
+ return dev_err_probe(dev, PTR_ERR(mipi_dsi->top_rst),
+ "Unable to get reset control\n");
+
+ reset_control_assert(mipi_dsi->top_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(mipi_dsi->top_rst);
+
+ /* MIPI DSI Controller */
+
+ mipi_dsi->dev = dev;
+ mipi_dsi->pdata.base = mipi_dsi->base;
+ mipi_dsi->pdata.max_data_lanes = 4;
+ mipi_dsi->pdata.phy_ops = &meson_dw_mipi_dsi_phy_ops;
+ mipi_dsi->pdata.host_ops = &meson_dw_mipi_dsi_host_ops;
+ mipi_dsi->pdata.priv_data = mipi_dsi;
+ platform_set_drvdata(pdev, mipi_dsi);
+
+ mipi_dsi->dmd = dw_mipi_dsi_probe(pdev, &mipi_dsi->pdata);
+ if (IS_ERR(mipi_dsi->dmd))
+ return dev_err_probe(dev, PTR_ERR(mipi_dsi->dmd),
+ "Failed to probe dw_mipi_dsi\n");
+
+ return 0;
+}
+
+static void meson_dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+ struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev);
+
+ dw_mipi_dsi_remove(mipi_dsi->dmd);
+}
+
+static const struct of_device_id meson_dw_mipi_dsi_of_table[] = {
+ { .compatible = "amlogic,meson-g12a-dw-mipi-dsi", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table);
+
+static struct platform_driver meson_dw_mipi_dsi_platform_driver = {
+ .probe = meson_dw_mipi_dsi_probe,
+ .remove = meson_dw_mipi_dsi_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = meson_dw_mipi_dsi_of_table,
+ },
+};
+module_platform_driver(meson_dw_mipi_dsi_platform_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h
new file mode 100644
index 000000000000..e1bd6b85d6a3
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2018 Amlogic, Inc. All rights reserved.
+ */
+
+#ifndef __MESON_DW_MIPI_DSI_H
+#define __MESON_DW_MIPI_DSI_H
+
+/* Top-level registers */
+/* [31: 4] Reserved. Default 0.
+ * [3] RW timing_rst_n: Default 1.
+ * 1=Assert SW reset of timing feature. 0=Release reset.
+ * [2] RW dpi_rst_n: Default 1.
+ * 1=Assert SW reset on mipi_dsi_host_dpi block. 0=Release reset.
+ * [1] RW intr_rst_n: Default 1.
+ * 1=Assert SW reset on mipi_dsi_host_intr block. 0=Release reset.
+ * [0] RW dwc_rst_n: Default 1.
+ * 1=Assert SW reset on IP core. 0=Release reset.
+ */
+#define MIPI_DSI_TOP_SW_RESET 0x3c0
+
+#define MIPI_DSI_TOP_SW_RESET_DWC BIT(0)
+#define MIPI_DSI_TOP_SW_RESET_INTR BIT(1)
+#define MIPI_DSI_TOP_SW_RESET_DPI BIT(2)
+#define MIPI_DSI_TOP_SW_RESET_TIMING BIT(3)
+
+/* [31: 5] Reserved. Default 0.
+ * [4] RW manual_edpihalt: Default 0.
+ * 1=Manual suspend VencL; 0=do not suspend VencL.
+ * [3] RW auto_edpihalt_en: Default 0.
+ * 1=Enable IP's edpihalt signal to suspend VencL;
+ * 0=IP's edpihalt signal does not affect VencL.
+ * [2] RW clock_freerun: Apply to auto-clock gate only. Default 0.
+ * 0=Default, use auto-clock gating to save power;
+ * 1=use free-run clock, disable auto-clock gating, for debug mode.
+ * [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not
+ * have auto-clock gating. 1=Enable pixclk. Default 0.
+ * [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not
+ * have auto-clock gating. 1=Enable sysclk. Default 0.
+ */
+#define MIPI_DSI_TOP_CLK_CNTL 0x3c4
+
+#define MIPI_DSI_TOP_CLK_SYSCLK_EN BIT(0)
+#define MIPI_DSI_TOP_CLK_PIXCLK_EN BIT(1)
+
+/* [31:24] Reserved. Default 0.
+ * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0.
+ * 0=16-bit RGB565 config 1;
+ * 1=16-bit RGB565 config 2;
+ * 2=16-bit RGB565 config 3;
+ * 3=18-bit RGB666 config 1;
+ * 4=18-bit RGB666 config 2;
+ * 5=24-bit RGB888;
+ * 6=20-bit YCbCr 4:2:2;
+ * 7=24-bit YCbCr 4:2:2;
+ * 8=16-bit YCbCr 4:2:2;
+ * 9=30-bit RGB;
+ * 10=36-bit RGB;
+ * 11=12-bit YCbCr 4:2:0.
+ * [19] Reserved. Default 0.
+ * [18:16] RW in_color_mode: Define VENC data width. Default 0.
+ * 0=30-bit pixel;
+ * 1=24-bit pixel;
+ * 2=18-bit pixel, RGB666;
+ * 3=16-bit pixel, RGB565.
+ * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0.
+ * Applicable to YUV422 or YUV420 only.
+ * 0=Use even pixel's chroma;
+ * 1=Use odd pixel's chroma;
+ * 2=Use averaged value between even and odd pair.
+ * [13:12] RW comp2_sel: Select which component to be Cr or B: Default 2.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [11:10] RW comp1_sel: Select which component to be Cb or G: Default 1.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [9: 8] RW comp0_sel: Select which component to be Y or R: Default 0.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [7] Reserved. Default 0.
+ * [6] RW de_pol: Default 0.
+ * If DE input is active low, set to 1 to invert to active high.
+ * [5] RW hsync_pol: Default 0.
+ * If HS input is active low, set to 1 to invert to active high.
+ * [4] RW vsync_pol: Default 0.
+ * If VS input is active low, set to 1 to invert to active high.
+ * [3] RW dpicolorm: Signal to IP. Default 0.
+ * [2] RW dpishutdn: Signal to IP. Default 0.
+ * [1] Reserved. Default 0.
+ * [0] Reserved. Default 0.
+ */
+#define MIPI_DSI_TOP_CNTL 0x3c8
+
+/* VENC data width */
+#define VENC_IN_COLOR_30B 0x0
+#define VENC_IN_COLOR_24B 0x1
+#define VENC_IN_COLOR_18B 0x2
+#define VENC_IN_COLOR_16B 0x3
+
+/* DPI pixel format */
+#define DPI_COLOR_16BIT_CFG_1 0
+#define DPI_COLOR_16BIT_CFG_2 1
+#define DPI_COLOR_16BIT_CFG_3 2
+#define DPI_COLOR_18BIT_CFG_1 3
+#define DPI_COLOR_18BIT_CFG_2 4
+#define DPI_COLOR_24BIT 5
+#define DPI_COLOR_20BIT_YCBCR_422 6
+#define DPI_COLOR_24BIT_YCBCR_422 7
+#define DPI_COLOR_16BIT_YCBCR_422 8
+#define DPI_COLOR_30BIT 9
+#define DPI_COLOR_36BIT 10
+#define DPI_COLOR_12BIT_YCBCR_420 11
+
+#define MIPI_DSI_TOP_DPI_COLOR_MODE GENMASK(23, 20)
+#define MIPI_DSI_TOP_IN_COLOR_MODE GENMASK(18, 16)
+#define MIPI_DSI_TOP_CHROMA_SUBSAMPLE GENMASK(15, 14)
+#define MIPI_DSI_TOP_COMP2_SEL GENMASK(13, 12)
+#define MIPI_DSI_TOP_COMP1_SEL GENMASK(11, 10)
+#define MIPI_DSI_TOP_COMP0_SEL GENMASK(9, 8)
+#define MIPI_DSI_TOP_DE_INVERT BIT(6)
+#define MIPI_DSI_TOP_HSYNC_INVERT BIT(5)
+#define MIPI_DSI_TOP_VSYNC_INVERT BIT(4)
+#define MIPI_DSI_TOP_DPICOLORM BIT(3)
+#define MIPI_DSI_TOP_DPISHUTDN BIT(2)
+
+#define MIPI_DSI_TOP_SUSPEND_CNTL 0x3cc
+#define MIPI_DSI_TOP_SUSPEND_LINE 0x3d0
+#define MIPI_DSI_TOP_SUSPEND_PIX 0x3d4
+#define MIPI_DSI_TOP_MEAS_CNTL 0x3d8
+/* [0] R stat_edpihalt: edpihalt signal from IP. Default 0. */
+#define MIPI_DSI_TOP_STAT 0x3dc
+#define MIPI_DSI_TOP_MEAS_STAT_TE0 0x3e0
+#define MIPI_DSI_TOP_MEAS_STAT_TE1 0x3e4
+#define MIPI_DSI_TOP_MEAS_STAT_VS0 0x3e8
+#define MIPI_DSI_TOP_MEAS_STAT_VS1 0x3ec
+/* [31:16] RW intr_stat/clr. Default 0.
+ * For each bit, read as this interrupt level status,
+ * write 1 to clear.
+ * [31:22] Reserved
+ * [ 21] stat/clr of eof interrupt
+ * [ 21] vde_fall interrupt
+ * [ 19] stat/clr of de_rise interrupt
+ * [ 18] stat/clr of vs_fall interrupt
+ * [ 17] stat/clr of vs_rise interrupt
+ * [ 16] stat/clr of dwc_edpite interrupt
+ * [15: 0] RW intr_enable. Default 0.
+ * For each bit, 1=enable this interrupt, 0=disable.
+ * [15: 6] Reserved
+ * [ 5] eof interrupt
+ * [ 4] de_fall interrupt
+ * [ 3] de_rise interrupt
+ * [ 2] vs_fall interrupt
+ * [ 1] vs_rise interrupt
+ * [ 0] dwc_edpite interrupt
+ */
+#define MIPI_DSI_TOP_INTR_CNTL_STAT 0x3f0
+// 31: 2 Reserved. Default 0.
+// 1: 0 RW mem_pd. Default 3.
+#define MIPI_DSI_TOP_MEM_PD 0x3f4
+
+#endif /* __MESON_DW_MIPI_DSI_H */
diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
new file mode 100644
index 000000000000..dc374bfc5951
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ * Copyright (C) 2014 Endless Mobile
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#include <linux/export.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "meson_registers.h"
+#include "meson_vclk.h"
+#include "meson_encoder_cvbs.h"
+
+/* HHI VDAC Registers */
+#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
+#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */
+#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
+#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */
+
+struct meson_encoder_cvbs {
+ struct drm_encoder encoder;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct meson_drm *priv;
+};
+
+#define bridge_to_meson_encoder_cvbs(x) \
+ container_of(x, struct meson_encoder_cvbs, bridge)
+
+/* Supported Modes */
+
+struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
+ { /* PAL */
+ .enci = &meson_cvbs_enci_pal,
+ .mode = {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
+ 720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ },
+ { /* NTSC */
+ .enci = &meson_cvbs_enci_ntsc,
+ .mode = {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
+ 720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ },
+};
+
+static const struct meson_cvbs_mode *
+meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
+{
+ int i;
+
+ for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
+ struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
+
+ if (drm_mode_match(req_mode, &meson_mode->mode,
+ DRM_MODE_MATCH_TIMINGS |
+ DRM_MODE_MATCH_CLOCK |
+ DRM_MODE_MATCH_FLAGS |
+ DRM_MODE_MATCH_3D_FLAGS))
+ return meson_mode;
+ }
+
+ return NULL;
+}
+
+static int meson_encoder_cvbs_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct meson_encoder_cvbs *meson_encoder_cvbs =
+ bridge_to_meson_encoder_cvbs(bridge);
+
+ return drm_bridge_attach(encoder, meson_encoder_cvbs->next_bridge,
+ &meson_encoder_cvbs->bridge, flags);
+}
+
+static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct meson_encoder_cvbs *meson_encoder_cvbs =
+ bridge_to_meson_encoder_cvbs(bridge);
+ struct meson_drm *priv = meson_encoder_cvbs->priv;
+ struct drm_display_mode *mode;
+ int i;
+
+ for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
+ struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
+
+ mode = drm_mode_duplicate(priv->drm, &meson_mode->mode);
+ if (!mode) {
+ dev_err(priv->dev, "Failed to create a new display mode\n");
+ return 0;
+ }
+
+ drm_mode_probed_add(connector, mode);
+ }
+
+ return i;
+}
+
+static enum drm_mode_status
+meson_encoder_cvbs_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *display_info,
+ const struct drm_display_mode *mode)
+{
+ if (meson_cvbs_get_mode(mode))
+ return MODE_OK;
+
+ return MODE_BAD;
+}
+
+static int meson_encoder_cvbs_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ if (meson_cvbs_get_mode(&crtc_state->mode))
+ return 0;
+
+ return -EINVAL;
+}
+
+static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_cvbs *encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge);
+ struct meson_drm *priv = encoder_cvbs->priv;
+ const struct meson_cvbs_mode *meson_mode;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
+ return;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ meson_mode = meson_cvbs_get_mode(&crtc_state->adjusted_mode);
+ if (WARN_ON(!meson_mode))
+ return;
+
+ meson_venci_cvbs_mode_set(priv, meson_mode->enci);
+
+ /* Setup 27MHz vclk2 for ENCI and VDAC */
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
+ MESON_VCLK_CVBS, MESON_VCLK_CVBS,
+ MESON_VCLK_CVBS, MESON_VCLK_CVBS,
+ true);
+
+ /* VDAC0 source is not from ATV */
+ writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
+ priv->io_base + _REG(VENC_VDAC_DACSEL0));
+
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
+ regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
+ regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
+ } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
+ regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
+ regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
+ } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
+ regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
+ regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
+ }
+}
+
+static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_cvbs *meson_encoder_cvbs =
+ bridge_to_meson_encoder_cvbs(bridge);
+ struct meson_drm *priv = meson_encoder_cvbs->priv;
+
+ /* Disable CVBS VDAC */
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
+ regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
+ regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
+ } else {
+ regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
+ regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
+ }
+}
+
+static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
+ .attach = meson_encoder_cvbs_attach,
+ .mode_valid = meson_encoder_cvbs_mode_valid,
+ .get_modes = meson_encoder_cvbs_get_modes,
+ .atomic_enable = meson_encoder_cvbs_atomic_enable,
+ .atomic_disable = meson_encoder_cvbs_atomic_disable,
+ .atomic_check = meson_encoder_cvbs_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,
+};
+
+int meson_encoder_cvbs_probe(struct meson_drm *priv)
+{
+ struct drm_device *drm = priv->drm;
+ struct meson_encoder_cvbs *meson_encoder_cvbs;
+ struct drm_connector *connector;
+ struct device_node *remote;
+ int ret;
+
+ meson_encoder_cvbs = devm_drm_bridge_alloc(priv->dev,
+ struct meson_encoder_cvbs,
+ bridge,
+ &meson_encoder_cvbs_bridge_funcs);
+ if (IS_ERR(meson_encoder_cvbs))
+ return PTR_ERR(meson_encoder_cvbs);
+
+ /* CVBS Connector Bridge */
+ remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0);
+ if (!remote) {
+ dev_info(drm->dev, "CVBS Output connector not available\n");
+ return 0;
+ }
+
+ meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
+ of_node_put(remote);
+ if (!meson_encoder_cvbs->next_bridge)
+ return dev_err_probe(priv->dev, -EPROBE_DEFER,
+ "Failed to find CVBS Connector bridge\n");
+
+ /* CVBS Encoder Bridge */
+ meson_encoder_cvbs->bridge.of_node = priv->dev->of_node;
+ meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite;
+ meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES;
+ meson_encoder_cvbs->bridge.interlace_allowed = true;
+
+ drm_bridge_add(&meson_encoder_cvbs->bridge);
+
+ meson_encoder_cvbs->priv = priv;
+
+ /* Encoder */
+ ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
+ DRM_MODE_ENCODER_TVDAC);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to init CVBS encoder\n");
+
+ meson_encoder_cvbs->encoder.possible_crtcs = BIT(0);
+
+ /* Attach CVBS Encoder Bridge to Encoder */
+ ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
+ return ret;
+ }
+
+ /* Initialize & attach Bridge Connector */
+ connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder);
+ if (IS_ERR(connector))
+ return dev_err_probe(priv->dev, PTR_ERR(connector),
+ "Unable to create CVBS bridge connector\n");
+
+ drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder);
+
+ priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs;
+
+ return 0;
+}
+
+void meson_encoder_cvbs_remove(struct meson_drm *priv)
+{
+ struct meson_encoder_cvbs *meson_encoder_cvbs;
+
+ if (priv->encoders[MESON_ENC_CVBS]) {
+ meson_encoder_cvbs = priv->encoders[MESON_ENC_CVBS];
+ drm_bridge_remove(&meson_encoder_cvbs->bridge);
+ }
+}
diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.h b/drivers/gpu/drm/meson/meson_encoder_cvbs.h
index ab7f76ba469c..7b7bc85c03f7 100644
--- a/drivers/gpu/drm/meson/meson_venc_cvbs.h
+++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.h
@@ -24,6 +24,7 @@ struct meson_cvbs_mode {
/* Modes supported by the CVBS output */
extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT];
-int meson_venc_cvbs_create(struct meson_drm *priv);
+int meson_encoder_cvbs_probe(struct meson_drm *priv);
+void meson_encoder_cvbs_remove(struct meson_drm *priv);
#endif /* __MESON_VENC_CVBS_H */
diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c
new file mode 100644
index 000000000000..6c6624f9ba24
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_probe_helper.h>
+
+#include "meson_drv.h"
+#include "meson_encoder_dsi.h"
+#include "meson_registers.h"
+#include "meson_venc.h"
+#include "meson_vclk.h"
+
+struct meson_encoder_dsi {
+ struct drm_encoder encoder;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct meson_drm *priv;
+};
+
+#define bridge_to_meson_encoder_dsi(x) \
+ container_of(x, struct meson_encoder_dsi, bridge)
+
+static int meson_encoder_dsi_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);
+
+ return drm_bridge_attach(encoder, encoder_dsi->next_bridge,
+ &encoder_dsi->bridge, flags);
+}
+
+static void meson_encoder_dsi_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);
+ struct meson_drm *priv = encoder_dsi->priv;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
+ return;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ /* ENCL clock setup is handled by CCF */
+
+ meson_venc_mipi_dsi_mode_set(priv, &crtc_state->adjusted_mode);
+ meson_encl_load_gamma(priv);
+
+ writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
+
+ writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, ENCL_VIDEO_MODE_ADV_VFIFO_EN,
+ priv->io_base + _REG(ENCL_VIDEO_MODE_ADV));
+ writel_relaxed(0, priv->io_base + _REG(ENCL_TST_EN));
+
+ writel_bits_relaxed(BIT(0), 0, priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
+
+ writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN));
+}
+
+static void meson_encoder_dsi_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_dsi *meson_encoder_dsi =
+ bridge_to_meson_encoder_dsi(bridge);
+ struct meson_drm *priv = meson_encoder_dsi->priv;
+
+ writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
+
+ writel_bits_relaxed(BIT(0), BIT(0), priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
+}
+
+static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {
+ .attach = meson_encoder_dsi_attach,
+ .atomic_enable = meson_encoder_dsi_atomic_enable,
+ .atomic_disable = meson_encoder_dsi_atomic_disable,
+ .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,
+};
+
+int meson_encoder_dsi_probe(struct meson_drm *priv)
+{
+ struct meson_encoder_dsi *meson_encoder_dsi;
+ struct device_node *remote;
+ int ret;
+
+ meson_encoder_dsi = devm_drm_bridge_alloc(priv->dev,
+ struct meson_encoder_dsi,
+ bridge,
+ &meson_encoder_dsi_bridge_funcs);
+ if (IS_ERR(meson_encoder_dsi))
+ return PTR_ERR(meson_encoder_dsi);
+
+ /* DSI Transceiver Bridge */
+ remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0);
+ if (!remote) {
+ dev_err(priv->dev, "DSI transceiver device is disabled");
+ return 0;
+ }
+
+ meson_encoder_dsi->next_bridge = of_drm_find_bridge(remote);
+ if (!meson_encoder_dsi->next_bridge)
+ return dev_err_probe(priv->dev, -EPROBE_DEFER,
+ "Failed to find DSI transceiver bridge\n");
+
+ /* DSI Encoder Bridge */
+ meson_encoder_dsi->bridge.of_node = priv->dev->of_node;
+ meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
+
+ drm_bridge_add(&meson_encoder_dsi->bridge);
+
+ meson_encoder_dsi->priv = priv;
+
+ /* Encoder */
+ ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder,
+ DRM_MODE_ENCODER_DSI);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to init DSI encoder\n");
+
+ meson_encoder_dsi->encoder.possible_crtcs = BIT(0);
+
+ /* Attach DSI Encoder Bridge to Encoder */
+ ret = drm_bridge_attach(&meson_encoder_dsi->encoder, &meson_encoder_dsi->bridge, NULL, 0);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to attach bridge\n");
+
+ /*
+ * We should have now in place:
+ * encoder->[dsi encoder bridge]->[dw-mipi-dsi bridge]->[panel bridge]->[panel]
+ */
+
+ priv->encoders[MESON_ENC_DSI] = meson_encoder_dsi;
+
+ dev_dbg(priv->dev, "DSI encoder initialized\n");
+
+ return 0;
+}
+
+void meson_encoder_dsi_remove(struct meson_drm *priv)
+{
+ struct meson_encoder_dsi *meson_encoder_dsi;
+
+ if (priv->encoders[MESON_ENC_DSI]) {
+ meson_encoder_dsi = priv->encoders[MESON_ENC_DSI];
+ drm_bridge_remove(&meson_encoder_dsi->bridge);
+ }
+}
diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.h b/drivers/gpu/drm/meson/meson_encoder_dsi.h
new file mode 100644
index 000000000000..85d5b61805f2
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_encoder_dsi.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __MESON_ENCODER_DSI_H
+#define __MESON_ENCODER_DSI_H
+
+int meson_encoder_dsi_probe(struct meson_drm *priv);
+void meson_encoder_dsi_remove(struct meson_drm *priv);
+
+#endif /* __MESON_ENCODER_DSI_H */
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
new file mode 100644
index 000000000000..8205ee56a691
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include <media/cec-notifier.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <linux/media-bus-format.h>
+#include <linux/videodev2.h>
+
+#include "meson_drv.h"
+#include "meson_registers.h"
+#include "meson_vclk.h"
+#include "meson_venc.h"
+#include "meson_encoder_hdmi.h"
+
+struct meson_encoder_hdmi {
+ struct drm_encoder encoder;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct drm_connector *connector;
+ struct meson_drm *priv;
+ unsigned long output_bus_fmt;
+ struct cec_notifier *cec_notifier;
+};
+
+#define bridge_to_meson_encoder_hdmi(x) \
+ container_of(x, struct meson_encoder_hdmi, bridge)
+
+static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+
+ return drm_bridge_attach(encoder, encoder_hdmi->next_bridge,
+ &encoder_hdmi->bridge, flags);
+}
+
+static void meson_encoder_hdmi_detach(struct drm_bridge *bridge)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+
+ cec_notifier_conn_unregister(encoder_hdmi->cec_notifier);
+ encoder_hdmi->cec_notifier = NULL;
+}
+
+static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
+ const struct drm_display_mode *mode)
+{
+ struct meson_drm *priv = encoder_hdmi->priv;
+ int vic = drm_match_cea_mode(mode);
+ unsigned long long phy_freq;
+ unsigned long long vclk_freq;
+ unsigned long long venc_freq;
+ unsigned long long hdmi_freq;
+
+ vclk_freq = mode->clock * 1000ULL;
+
+ /* For 420, pixel clock is half unlike venc clock */
+ if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ vclk_freq /= 2;
+
+ /* TMDS clock is pixel_clock * 10 */
+ phy_freq = vclk_freq * 10;
+
+ if (!vic) {
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
+ vclk_freq, vclk_freq, vclk_freq, false);
+ return;
+ }
+
+ /* 480i/576i needs global pixel doubling */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ vclk_freq *= 2;
+
+ venc_freq = vclk_freq;
+ hdmi_freq = vclk_freq;
+
+ /* VENC double pixels for 1080i, 720p and YUV420 modes */
+ if (meson_venc_hdmi_venc_repeat(vic) ||
+ encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ venc_freq *= 2;
+
+ vclk_freq = max(venc_freq, hdmi_freq);
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ venc_freq /= 2;
+
+ dev_dbg(priv->dev,
+ "phy:%lluHz vclk=%lluHz venc=%lluHz hdmi=%lluHz enci=%d\n",
+ phy_freq, vclk_freq, venc_freq, hdmi_freq,
+ priv->venc.hdmi_use_enci);
+
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
+ venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
+}
+
+static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *display_info,
+ const struct drm_display_mode *mode)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+ struct meson_drm *priv = encoder_hdmi->priv;
+ bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
+ unsigned long long clock = mode->clock * 1000ULL;
+ unsigned long long phy_freq;
+ unsigned long long vclk_freq;
+ unsigned long long venc_freq;
+ unsigned long long hdmi_freq;
+ int vic = drm_match_cea_mode(mode);
+ enum drm_mode_status status;
+
+ dev_dbg(priv->dev, "Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+
+ /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */
+ if (display_info->max_tmds_clock &&
+ mode->clock > display_info->max_tmds_clock &&
+ !drm_mode_is_420_only(display_info, mode) &&
+ !drm_mode_is_420_also(display_info, mode))
+ return MODE_BAD;
+
+ /* Check against non-VIC supported modes */
+ if (!vic) {
+ status = meson_venc_hdmi_supported_mode(mode);
+ if (status != MODE_OK)
+ return status;
+
+ return meson_vclk_dmt_supported_freq(priv, clock);
+ /* Check against supported VIC modes */
+ } else if (!meson_venc_hdmi_supported_vic(vic))
+ return MODE_BAD;
+
+ vclk_freq = clock;
+
+ /* For 420, pixel clock is half unlike venc clock */
+ if (drm_mode_is_420_only(display_info, mode) ||
+ (!is_hdmi2_sink &&
+ drm_mode_is_420_also(display_info, mode)))
+ vclk_freq /= 2;
+
+ /* TMDS clock is pixel_clock * 10 */
+ phy_freq = vclk_freq * 10;
+
+ /* 480i/576i needs global pixel doubling */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ vclk_freq *= 2;
+
+ venc_freq = vclk_freq;
+ hdmi_freq = vclk_freq;
+
+ /* VENC double pixels for 1080i, 720p and YUV420 modes */
+ if (meson_venc_hdmi_venc_repeat(vic) ||
+ drm_mode_is_420_only(display_info, mode) ||
+ (!is_hdmi2_sink &&
+ drm_mode_is_420_also(display_info, mode)))
+ venc_freq *= 2;
+
+ vclk_freq = max(venc_freq, hdmi_freq);
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ venc_freq /= 2;
+
+ dev_dbg(priv->dev,
+ "%s: vclk:%lluHz phy=%lluHz venc=%lluHz hdmi=%lluHz\n",
+ __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
+
+ return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
+}
+
+static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+ unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR;
+ struct meson_drm *priv = encoder_hdmi->priv;
+ struct drm_connector_state *conn_state;
+ const struct drm_display_mode *mode;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector *connector;
+ bool yuv420_mode = false;
+ int vic;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
+ return;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ mode = &crtc_state->adjusted_mode;
+
+ vic = drm_match_cea_mode(mode);
+
+ dev_dbg(priv->dev, "\"%s\" vic %d\n", mode->name, vic);
+
+ if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
+ ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
+ yuv420_mode = true;
+ } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16)
+ ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
+
+ /* VENC + VENC-DVI Mode setup */
+ meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);
+
+ /* VCLK Set clock */
+ meson_encoder_hdmi_set_vclk(encoder_hdmi, mode);
+
+ if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ /* Setup YUV420 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(2 | (2 << 2),
+ priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+ else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16)
+ /* Setup YUV422 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(1 | (2 << 2),
+ priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+ else
+ /* Setup YUV444 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+
+ dev_dbg(priv->dev, "%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
+
+ if (priv->venc.hdmi_use_enci)
+ writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
+ else
+ writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
+}
+
+static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+ struct meson_drm *priv = encoder_hdmi->priv;
+
+ writel_bits_relaxed(0x3, 0,
+ priv->io_base + _REG(VPU_HDMI_SETTING));
+
+ writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
+ writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
+}
+
+static const u32 meson_encoder_hdmi_out_bus_fmts[] = {
+ MEDIA_BUS_FMT_YUV8_1X24,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_UYYVYY8_0_5X24,
+};
+
+static u32 *
+meson_encoder_hdmi_get_inp_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)
+{
+ u32 *input_fmts = NULL;
+ int i;
+
+ *num_input_fmts = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(meson_encoder_hdmi_out_bus_fmts) ; ++i) {
+ if (output_fmt == meson_encoder_hdmi_out_bus_fmts[i]) {
+ *num_input_fmts = 1;
+ input_fmts = kcalloc(*num_input_fmts,
+ sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ input_fmts[0] = output_fmt;
+
+ break;
+ }
+ }
+
+ return input_fmts;
+}
+
+static int meson_encoder_hdmi_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 meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+ struct drm_connector_state *old_conn_state =
+ drm_atomic_get_old_connector_state(conn_state->state, conn_state->connector);
+ struct meson_drm *priv = encoder_hdmi->priv;
+
+ encoder_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format;
+
+ dev_dbg(priv->dev, "output_bus_fmt %lx\n", encoder_hdmi->output_bus_fmt);
+
+ if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state))
+ crtc_state->mode_changed = true;
+
+ return 0;
+}
+
+static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
+ enum drm_connector_status status)
+{
+ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+
+ if (!encoder_hdmi->cec_notifier)
+ return;
+
+ if (status == connector_status_connected) {
+ const struct drm_edid *drm_edid;
+ const struct edid *edid;
+
+ drm_edid = drm_bridge_edid_read(encoder_hdmi->next_bridge,
+ encoder_hdmi->connector);
+ if (!drm_edid)
+ return;
+
+ /*
+ * FIXME: The CEC physical address should be set using
+ * cec_notifier_set_phys_addr(encoder_hdmi->cec_notifier,
+ * connector->display_info.source_physical_address) from a path
+ * that has read the EDID and called
+ * drm_edid_connector_update().
+ */
+ edid = drm_edid_raw(drm_edid);
+
+ cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid);
+
+ drm_edid_free(drm_edid);
+ } else
+ cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier);
+}
+
+static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
+ .attach = meson_encoder_hdmi_attach,
+ .detach = meson_encoder_hdmi_detach,
+ .mode_valid = meson_encoder_hdmi_mode_valid,
+ .hpd_notify = meson_encoder_hdmi_hpd_notify,
+ .atomic_enable = meson_encoder_hdmi_atomic_enable,
+ .atomic_disable = meson_encoder_hdmi_atomic_disable,
+ .atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
+ .atomic_check = meson_encoder_hdmi_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,
+};
+
+int meson_encoder_hdmi_probe(struct meson_drm *priv)
+{
+ struct meson_encoder_hdmi *meson_encoder_hdmi;
+ struct platform_device *pdev;
+ struct device_node *remote;
+ int ret;
+
+ meson_encoder_hdmi = devm_drm_bridge_alloc(priv->dev,
+ struct meson_encoder_hdmi,
+ bridge,
+ &meson_encoder_hdmi_bridge_funcs);
+ if (IS_ERR(meson_encoder_hdmi))
+ return PTR_ERR(meson_encoder_hdmi);
+
+ /* HDMI Transceiver Bridge */
+ remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0);
+ if (!remote) {
+ dev_err(priv->dev, "HDMI transceiver device is disabled");
+ return 0;
+ }
+
+ meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
+ if (!meson_encoder_hdmi->next_bridge) {
+ ret = dev_err_probe(priv->dev, -EPROBE_DEFER,
+ "Failed to find HDMI transceiver bridge\n");
+ goto err_put_node;
+ }
+
+ /* HDMI Encoder Bridge */
+ meson_encoder_hdmi->bridge.of_node = priv->dev->of_node;
+ meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ meson_encoder_hdmi->bridge.interlace_allowed = true;
+
+ drm_bridge_add(&meson_encoder_hdmi->bridge);
+
+ meson_encoder_hdmi->priv = priv;
+
+ /* Encoder */
+ ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder,
+ DRM_MODE_ENCODER_TMDS);
+ if (ret) {
+ dev_err_probe(priv->dev, ret, "Failed to init HDMI encoder\n");
+ goto err_put_node;
+ }
+
+ meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
+
+ /* Attach HDMI Encoder Bridge to Encoder */
+ ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err_probe(priv->dev, ret, "Failed to attach bridge\n");
+ goto err_put_node;
+ }
+
+ /* Initialize & attach Bridge Connector */
+ meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm,
+ &meson_encoder_hdmi->encoder);
+ if (IS_ERR(meson_encoder_hdmi->connector)) {
+ ret = dev_err_probe(priv->dev,
+ PTR_ERR(meson_encoder_hdmi->connector),
+ "Unable to create HDMI bridge connector\n");
+ goto err_put_node;
+ }
+ drm_connector_attach_encoder(meson_encoder_hdmi->connector,
+ &meson_encoder_hdmi->encoder);
+
+ /*
+ * We should have now in place:
+ * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector]
+ */
+
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector);
+
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
+ drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector);
+
+ drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8);
+
+ /* Handle this here until handled by drm_bridge_connector_init() */
+ meson_encoder_hdmi->connector->ycbcr_420_allowed = true;
+
+ pdev = of_find_device_by_node(remote);
+ of_node_put(remote);
+ if (pdev) {
+ struct cec_connector_info conn_info;
+ struct cec_notifier *notifier;
+
+ cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector);
+
+ notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info);
+ if (!notifier) {
+ put_device(&pdev->dev);
+ return -ENOMEM;
+ }
+
+ meson_encoder_hdmi->cec_notifier = notifier;
+ }
+
+ priv->encoders[MESON_ENC_HDMI] = meson_encoder_hdmi;
+
+ dev_dbg(priv->dev, "HDMI encoder initialized\n");
+
+ return 0;
+
+err_put_node:
+ of_node_put(remote);
+ return ret;
+}
+
+void meson_encoder_hdmi_remove(struct meson_drm *priv)
+{
+ struct meson_encoder_hdmi *meson_encoder_hdmi;
+
+ if (priv->encoders[MESON_ENC_HDMI]) {
+ meson_encoder_hdmi = priv->encoders[MESON_ENC_HDMI];
+ drm_bridge_remove(&meson_encoder_hdmi->bridge);
+ }
+}
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.h b/drivers/gpu/drm/meson/meson_encoder_hdmi.h
new file mode 100644
index 000000000000..fd5485875db8
--- /dev/null
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __MESON_ENCODER_HDMI_H
+#define __MESON_ENCODER_HDMI_H
+
+int meson_encoder_hdmi_probe(struct meson_drm *priv);
+void meson_encoder_hdmi_remove(struct meson_drm *priv);
+
+#endif /* __MESON_ENCODER_HDMI_H */
diff --git a/drivers/gpu/drm/meson/meson_osd_afbcd.c b/drivers/gpu/drm/meson/meson_osd_afbcd.c
index ffc6b584dbf8..0cdbe899402f 100644
--- a/drivers/gpu/drm/meson/meson_osd_afbcd.c
+++ b/drivers/gpu/drm/meson/meson_osd_afbcd.c
@@ -79,11 +79,6 @@ static bool meson_gxm_afbcd_supported_fmt(u64 modifier, uint32_t format)
return meson_gxm_afbcd_pixel_fmt(modifier, format) >= 0;
}
-static int meson_gxm_afbcd_init(struct meson_drm *priv)
-{
- return 0;
-}
-
static int meson_gxm_afbcd_reset(struct meson_drm *priv)
{
writel_relaxed(VIU_SW_RESET_OSD1_AFBCD,
@@ -93,6 +88,16 @@ static int meson_gxm_afbcd_reset(struct meson_drm *priv)
return 0;
}
+static int meson_gxm_afbcd_init(struct meson_drm *priv)
+{
+ return 0;
+}
+
+static void meson_gxm_afbcd_exit(struct meson_drm *priv)
+{
+ meson_gxm_afbcd_reset(priv);
+}
+
static int meson_gxm_afbcd_enable(struct meson_drm *priv)
{
writel_relaxed(FIELD_PREP(OSD1_AFBCD_ID_FIFO_THRD, 0x40) |
@@ -172,6 +177,7 @@ static int meson_gxm_afbcd_setup(struct meson_drm *priv)
struct meson_afbcd_ops meson_afbcd_gxm_ops = {
.init = meson_gxm_afbcd_init,
+ .exit = meson_gxm_afbcd_exit,
.reset = meson_gxm_afbcd_reset,
.enable = meson_gxm_afbcd_enable,
.disable = meson_gxm_afbcd_disable,
@@ -269,6 +275,18 @@ static bool meson_g12a_afbcd_supported_fmt(u64 modifier, uint32_t format)
return meson_g12a_afbcd_pixel_fmt(modifier, format) >= 0;
}
+static int meson_g12a_afbcd_reset(struct meson_drm *priv)
+{
+ meson_rdma_reset(priv);
+
+ meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB |
+ VIU_SW_RESET_G12A_OSD1_AFBCD,
+ VIU_SW_RESET);
+ meson_rdma_writel_sync(priv, 0, VIU_SW_RESET);
+
+ return 0;
+}
+
static int meson_g12a_afbcd_init(struct meson_drm *priv)
{
int ret;
@@ -286,16 +304,10 @@ static int meson_g12a_afbcd_init(struct meson_drm *priv)
return 0;
}
-static int meson_g12a_afbcd_reset(struct meson_drm *priv)
+static void meson_g12a_afbcd_exit(struct meson_drm *priv)
{
- meson_rdma_reset(priv);
-
- meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB |
- VIU_SW_RESET_G12A_OSD1_AFBCD,
- VIU_SW_RESET);
- meson_rdma_writel_sync(priv, 0, VIU_SW_RESET);
-
- return 0;
+ meson_g12a_afbcd_reset(priv);
+ meson_rdma_free(priv);
}
static int meson_g12a_afbcd_enable(struct meson_drm *priv)
@@ -380,6 +392,7 @@ static int meson_g12a_afbcd_setup(struct meson_drm *priv)
struct meson_afbcd_ops meson_afbcd_g12a_ops = {
.init = meson_g12a_afbcd_init,
+ .exit = meson_g12a_afbcd_exit,
.reset = meson_g12a_afbcd_reset,
.enable = meson_g12a_afbcd_enable,
.disable = meson_g12a_afbcd_disable,
diff --git a/drivers/gpu/drm/meson/meson_osd_afbcd.h b/drivers/gpu/drm/meson/meson_osd_afbcd.h
index 5e5523304f42..e77ddeb6416f 100644
--- a/drivers/gpu/drm/meson/meson_osd_afbcd.h
+++ b/drivers/gpu/drm/meson/meson_osd_afbcd.h
@@ -14,6 +14,7 @@
struct meson_afbcd_ops {
int (*init)(struct meson_drm *priv);
+ void (*exit)(struct meson_drm *priv);
int (*reset)(struct meson_drm *priv);
int (*enable)(struct meson_drm *priv);
int (*disable)(struct meson_drm *priv);
diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c
index ed063152aecd..783572b16963 100644
--- a/drivers/gpu/drm/meson/meson_overlay.c
+++ b/drivers/gpu/drm/meson/meson_overlay.c
@@ -9,12 +9,14 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
#include <drm/drm_device.h>
-#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "meson_overlay.h"
#include "meson_registers.h"
@@ -475,7 +477,7 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
plane);
struct drm_framebuffer *fb = new_state->fb;
struct meson_drm *priv = meson_overlay->priv;
- struct drm_gem_cma_object *gem;
+ struct drm_gem_dma_object *gem;
unsigned long flags;
bool interlace_mode;
@@ -649,8 +651,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
switch (priv->viu.vd1_planes) {
case 3:
- gem = drm_fb_cma_get_gem_obj(fb, 2);
- priv->viu.vd1_addr2 = gem->paddr + fb->offsets[2];
+ gem = drm_fb_dma_get_gem_obj(fb, 2);
+ priv->viu.vd1_addr2 = gem->dma_addr + fb->offsets[2];
priv->viu.vd1_stride2 = fb->pitches[2];
priv->viu.vd1_height2 =
drm_format_info_plane_height(fb->format,
@@ -661,8 +663,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
priv->viu.vd1_height2);
fallthrough;
case 2:
- gem = drm_fb_cma_get_gem_obj(fb, 1);
- priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1];
+ gem = drm_fb_dma_get_gem_obj(fb, 1);
+ priv->viu.vd1_addr1 = gem->dma_addr + fb->offsets[1];
priv->viu.vd1_stride1 = fb->pitches[1];
priv->viu.vd1_height1 =
drm_format_info_plane_height(fb->format,
@@ -673,8 +675,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
priv->viu.vd1_height1);
fallthrough;
case 1:
- gem = drm_fb_cma_get_gem_obj(fb, 0);
- priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0];
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
+ priv->viu.vd1_addr0 = gem->dma_addr + fb->offsets[0];
priv->viu.vd1_stride0 = fb->pitches[0];
priv->viu.vd1_height0 =
drm_format_info_plane_height(fb->format,
@@ -747,7 +749,6 @@ static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = {
.atomic_check = meson_overlay_atomic_check,
.atomic_disable = meson_overlay_atomic_disable,
.atomic_update = meson_overlay_atomic_update,
- .prepare_fb = drm_gem_plane_helper_prepare_fb,
};
static bool meson_overlay_format_mod_supported(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
index a18510dae4c8..f8d0e0874a5d 100644
--- a/drivers/gpu/drm/meson/meson_plane.c
+++ b/drivers/gpu/drm/meson/meson_plane.c
@@ -13,12 +13,14 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
#include <drm/drm_device.h>
-#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "meson_plane.h"
#include "meson_registers.h"
@@ -93,7 +95,7 @@ static int meson_plane_atomic_check(struct drm_plane *plane,
return drm_atomic_helper_check_plane_state(new_plane_state,
crtc_state,
FRAC_16_16(1, 5),
- DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
false, true);
}
@@ -138,7 +140,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
struct drm_rect dest = drm_plane_state_dest(new_state);
struct meson_drm *priv = meson_plane->priv;
struct drm_framebuffer *fb = new_state->fb;
- struct drm_gem_cma_object *gem;
+ struct drm_gem_dma_object *gem;
unsigned long flags;
int vsc_ini_rcv_num, vsc_ini_rpt_p0_num;
int vsc_bot_rcv_num, vsc_bot_rpt_p0_num;
@@ -168,7 +170,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
/* Enable OSD and BLK0, set max global alpha */
priv->viu.osd1_ctrl_stat = OSD_ENABLE |
- (0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
+ (0x100 << OSD_GLOBAL_ALPHA_SHIFT) |
OSD_BLK0_ENABLE;
priv->viu.osd1_ctrl_stat2 = readl(priv->io_base +
@@ -364,9 +366,9 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
}
/* Update Canvas with buffer address */
- gem = drm_fb_cma_get_gem_obj(fb, 0);
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
- priv->viu.osd1_addr = gem->paddr;
+ priv->viu.osd1_addr = gem->dma_addr;
priv->viu.osd1_stride = fb->pitches[0];
priv->viu.osd1_height = fb->height;
priv->viu.osd1_width = fb->width;
@@ -422,7 +424,6 @@ static const struct drm_plane_helper_funcs meson_plane_helper_funcs = {
.atomic_check = meson_plane_atomic_check,
.atomic_disable = meson_plane_atomic_disable,
.atomic_update = meson_plane_atomic_update,
- .prepare_fb = drm_gem_plane_helper_prepare_fb,
};
static bool meson_plane_format_mod_supported(struct drm_plane *plane,
@@ -534,6 +535,7 @@ int meson_plane_create(struct meson_drm *priv)
struct meson_plane *meson_plane;
struct drm_plane *plane;
const uint64_t *format_modifiers = format_modifiers_default;
+ int ret;
meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane),
GFP_KERNEL);
@@ -548,12 +550,16 @@ int meson_plane_create(struct meson_drm *priv)
else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
format_modifiers = format_modifiers_afbc_g12a;
- drm_universal_plane_init(priv->drm, plane, 0xFF,
- &meson_plane_funcs,
- supported_drm_formats,
- ARRAY_SIZE(supported_drm_formats),
- format_modifiers,
- DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane");
+ ret = drm_universal_plane_init(priv->drm, plane, 0xFF,
+ &meson_plane_funcs,
+ supported_drm_formats,
+ ARRAY_SIZE(supported_drm_formats),
+ format_modifiers,
+ DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane");
+ if (ret) {
+ devm_kfree(priv->drm->dev, meson_plane);
+ return ret;
+ }
drm_plane_helper_add(plane, &meson_plane_helper_funcs);
diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h
index 446e7961da48..3d73d00a1f4c 100644
--- a/drivers/gpu/drm/meson/meson_registers.h
+++ b/drivers/gpu/drm/meson/meson_registers.h
@@ -634,6 +634,11 @@
#define VPP_WRAP_OSD3_MATRIX_PRE_OFFSET2 0x3dbc
#define VPP_WRAP_OSD3_MATRIX_EN_CTRL 0x3dbd
+/* osd1 HDR */
+#define OSD1_HDR2_CTRL 0x38a0
+#define OSD1_HDR2_CTRL_VDIN0_HDR2_TOP_EN BIT(13)
+#define OSD1_HDR2_CTRL_REG_ONLY_MAT BIT(16)
+
/* osd2 scaler */
#define OSD2_VSC_PHASE_STEP 0x3d00
#define OSD2_VSC_INI_PHASE 0x3d01
@@ -807,6 +812,7 @@
#define VENC_STATA 0x1b6d
#define VENC_INTCTRL 0x1b6e
#define VENC_INTCTRL_ENCI_LNRST_INT_EN BIT(1)
+#define VENC_INTCTRL_ENCP_LNRST_INT_EN BIT(9)
#define VENC_INTFLAG 0x1b6f
#define VENC_VIDEO_TST_EN 0x1b70
#define VENC_VIDEO_TST_MDSEL 0x1b71
@@ -1187,7 +1193,11 @@
#define ENCL_VIDEO_PB_OFFST 0x1ca5
#define ENCL_VIDEO_PR_OFFST 0x1ca6
#define ENCL_VIDEO_MODE 0x1ca7
+#define ENCL_PX_LN_CNT_SHADOW_EN BIT(15)
#define ENCL_VIDEO_MODE_ADV 0x1ca8
+#define ENCL_VIDEO_MODE_ADV_VFIFO_EN BIT(3)
+#define ENCL_VIDEO_MODE_ADV_GAIN_HDTV BIT(4)
+#define ENCL_SEL_GAMMA_RGB_IN BIT(10)
#define ENCL_DBG_PX_RST 0x1ca9
#define ENCL_DBG_LN_RST 0x1caa
#define ENCL_DBG_PX_INT 0x1cab
@@ -1214,11 +1224,14 @@
#define ENCL_VIDEO_VOFFST 0x1cc0
#define ENCL_VIDEO_RGB_CTRL 0x1cc1
#define ENCL_VIDEO_FILT_CTRL 0x1cc2
+#define ENCL_VIDEO_FILT_CTRL_BYPASS_FILTER BIT(12)
#define ENCL_VIDEO_OFLD_VPEQ_OFST 0x1cc3
#define ENCL_VIDEO_OFLD_VOAV_OFST 0x1cc4
#define ENCL_VIDEO_MATRIX_CB 0x1cc5
#define ENCL_VIDEO_MATRIX_CR 0x1cc6
#define ENCL_VIDEO_RGBIN_CTRL 0x1cc7
+#define ENCL_VIDEO_RGBIN_RGB BIT(0)
+#define ENCL_VIDEO_RGBIN_ZBLK BIT(1)
#define ENCL_MAX_LINE_SWITCH_POINT 0x1cc8
#define ENCL_DACSEL_0 0x1cc9
#define ENCL_DACSEL_1 0x1cca
@@ -1295,13 +1308,28 @@
#define RDMA_STATUS2 0x1116
#define RDMA_STATUS3 0x1117
#define L_GAMMA_CNTL_PORT 0x1400
+#define L_GAMMA_CNTL_PORT_VCOM_POL BIT(7) /* RW */
+#define L_GAMMA_CNTL_PORT_RVS_OUT BIT(6) /* RW */
+#define L_GAMMA_CNTL_PORT_ADR_RDY BIT(5) /* Read Only */
+#define L_GAMMA_CNTL_PORT_WR_RDY BIT(4) /* Read Only */
+#define L_GAMMA_CNTL_PORT_RD_RDY BIT(3) /* Read Only */
+#define L_GAMMA_CNTL_PORT_TR BIT(2) /* RW */
+#define L_GAMMA_CNTL_PORT_SET BIT(1) /* RW */
+#define L_GAMMA_CNTL_PORT_EN BIT(0) /* RW */
#define L_GAMMA_DATA_PORT 0x1401
#define L_GAMMA_ADDR_PORT 0x1402
+#define L_GAMMA_ADDR_PORT_RD BIT(12)
+#define L_GAMMA_ADDR_PORT_AUTO_INC BIT(11)
+#define L_GAMMA_ADDR_PORT_SEL_R BIT(10)
+#define L_GAMMA_ADDR_PORT_SEL_G BIT(9)
+#define L_GAMMA_ADDR_PORT_SEL_B BIT(8)
+#define L_GAMMA_ADDR_PORT_ADDR GENMASK(7, 0)
#define L_GAMMA_VCOM_HSWITCH_ADDR 0x1403
#define L_RGB_BASE_ADDR 0x1405
#define L_RGB_COEFF_ADDR 0x1406
#define L_POL_CNTL_ADDR 0x1407
#define L_DITH_CNTL_ADDR 0x1408
+#define L_DITH_CNTL_DITH10_EN BIT(10)
#define L_GAMMA_PROBE_CTRL 0x1409
#define L_GAMMA_PROBE_COLOR_L 0x140a
#define L_GAMMA_PROBE_COLOR_H 0x140b
@@ -1358,6 +1386,8 @@
#define L_LCD_PWM1_HI_ADDR 0x143f
#define L_INV_CNT_ADDR 0x1440
#define L_TCON_MISC_SEL_ADDR 0x1441
+#define L_TCON_MISC_SEL_STV1 BIT(4)
+#define L_TCON_MISC_SEL_STV2 BIT(5)
#define L_DUAL_PORT_CNTL_ADDR 0x1442
#define MLVDS_CLK_CTL1_HI 0x1443
#define MLVDS_CLK_CTL1_LO 0x1444
diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
index 2a82119eb58e..dfe0c28a0f05 100644
--- a/drivers/gpu/drm/meson/meson_vclk.c
+++ b/drivers/gpu/drm/meson/meson_vclk.c
@@ -110,7 +110,7 @@
#define HDMI_PLL_LOCK BIT(31)
#define HDMI_PLL_LOCK_G12A (3 << 30)
-#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001)
+#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST_ULL((_freq) * 1000ULL, 1001ULL)
/* VID PLL Dividers */
enum {
@@ -360,11 +360,11 @@ enum {
};
struct meson_vclk_params {
- unsigned int pll_freq;
- unsigned int phy_freq;
- unsigned int vclk_freq;
- unsigned int venc_freq;
- unsigned int pixel_freq;
+ unsigned long long pll_freq;
+ unsigned long long phy_freq;
+ unsigned long long vclk_freq;
+ unsigned long long venc_freq;
+ unsigned long long pixel_freq;
unsigned int pll_od1;
unsigned int pll_od2;
unsigned int pll_od3;
@@ -372,11 +372,11 @@ struct meson_vclk_params {
unsigned int vclk_div;
} params[] = {
[MESON_VCLK_HDMI_ENCI_54000] = {
- .pll_freq = 4320000,
- .phy_freq = 270000,
- .vclk_freq = 54000,
- .venc_freq = 54000,
- .pixel_freq = 54000,
+ .pll_freq = 4320000000,
+ .phy_freq = 270000000,
+ .vclk_freq = 54000000,
+ .venc_freq = 54000000,
+ .pixel_freq = 54000000,
.pll_od1 = 4,
.pll_od2 = 4,
.pll_od3 = 1,
@@ -384,11 +384,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_DDR_54000] = {
- .pll_freq = 4320000,
- .phy_freq = 270000,
- .vclk_freq = 54000,
- .venc_freq = 54000,
- .pixel_freq = 27000,
+ .pll_freq = 4320000000,
+ .phy_freq = 270000000,
+ .vclk_freq = 54000000,
+ .venc_freq = 54000000,
+ .pixel_freq = 27000000,
.pll_od1 = 4,
.pll_od2 = 4,
.pll_od3 = 1,
@@ -396,11 +396,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_DDR_148500] = {
- .pll_freq = 2970000,
- .phy_freq = 742500,
- .vclk_freq = 148500,
- .venc_freq = 148500,
- .pixel_freq = 74250,
+ .pll_freq = 2970000000,
+ .phy_freq = 742500000,
+ .vclk_freq = 148500000,
+ .venc_freq = 148500000,
+ .pixel_freq = 74250000,
.pll_od1 = 4,
.pll_od2 = 1,
.pll_od3 = 1,
@@ -408,11 +408,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_74250] = {
- .pll_freq = 2970000,
- .phy_freq = 742500,
- .vclk_freq = 74250,
- .venc_freq = 74250,
- .pixel_freq = 74250,
+ .pll_freq = 2970000000,
+ .phy_freq = 742500000,
+ .vclk_freq = 74250000,
+ .venc_freq = 74250000,
+ .pixel_freq = 74250000,
.pll_od1 = 2,
.pll_od2 = 2,
.pll_od3 = 2,
@@ -420,11 +420,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_148500] = {
- .pll_freq = 2970000,
- .phy_freq = 1485000,
- .vclk_freq = 148500,
- .venc_freq = 148500,
- .pixel_freq = 148500,
+ .pll_freq = 2970000000,
+ .phy_freq = 1485000000,
+ .vclk_freq = 148500000,
+ .venc_freq = 148500000,
+ .pixel_freq = 148500000,
.pll_od1 = 1,
.pll_od2 = 2,
.pll_od3 = 2,
@@ -432,11 +432,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_297000] = {
- .pll_freq = 5940000,
- .phy_freq = 2970000,
- .venc_freq = 297000,
- .vclk_freq = 297000,
- .pixel_freq = 297000,
+ .pll_freq = 5940000000,
+ .phy_freq = 2970000000,
+ .venc_freq = 297000000,
+ .vclk_freq = 297000000,
+ .pixel_freq = 297000000,
.pll_od1 = 2,
.pll_od2 = 1,
.pll_od3 = 1,
@@ -444,11 +444,11 @@ struct meson_vclk_params {
.vclk_div = 2,
},
[MESON_VCLK_HDMI_594000] = {
- .pll_freq = 5940000,
- .phy_freq = 5940000,
- .venc_freq = 594000,
- .vclk_freq = 594000,
- .pixel_freq = 594000,
+ .pll_freq = 5940000000,
+ .phy_freq = 5940000000,
+ .venc_freq = 594000000,
+ .vclk_freq = 594000000,
+ .pixel_freq = 594000000,
.pll_od1 = 1,
.pll_od2 = 1,
.pll_od3 = 2,
@@ -456,11 +456,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_594000_YUV420] = {
- .pll_freq = 5940000,
- .phy_freq = 2970000,
- .venc_freq = 594000,
- .vclk_freq = 594000,
- .pixel_freq = 297000,
+ .pll_freq = 5940000000,
+ .phy_freq = 2970000000,
+ .venc_freq = 594000000,
+ .vclk_freq = 594000000,
+ .pixel_freq = 297000000,
.pll_od1 = 2,
.pll_od2 = 1,
.pll_od3 = 1,
@@ -617,16 +617,16 @@ static void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
3 << 20, pll_od_to_reg(od3) << 20);
}
-#define XTAL_FREQ 24000
+#define XTAL_FREQ (24 * 1000 * 1000)
static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
- unsigned int pll_freq)
+ unsigned long long pll_freq)
{
/* The GXBB PLL has a /2 pre-multiplier */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
- pll_freq /= 2;
+ pll_freq = DIV_ROUND_DOWN_ULL(pll_freq, 2);
- return pll_freq / XTAL_FREQ;
+ return DIV_ROUND_DOWN_ULL(pll_freq, XTAL_FREQ);
}
#define HDMI_FRAC_MAX_GXBB 4096
@@ -635,12 +635,13 @@ static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
unsigned int m,
- unsigned int pll_freq)
+ unsigned long long pll_freq)
{
- unsigned int parent_freq = XTAL_FREQ;
+ unsigned long long parent_freq = XTAL_FREQ;
unsigned int frac_max = HDMI_FRAC_MAX_GXL;
unsigned int frac_m;
unsigned int frac;
+ u32 remainder;
/* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
@@ -652,11 +653,11 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
frac_max = HDMI_FRAC_MAX_G12A;
/* We can have a perfect match !*/
- if (pll_freq / m == parent_freq &&
- pll_freq % m == 0)
+ if (div_u64_rem(pll_freq, m, &remainder) == parent_freq &&
+ remainder == 0)
return 0;
- frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq);
+ frac = mul_u64_u64_div_u64(pll_freq, frac_max, parent_freq);
frac_m = m * frac_max;
if (frac_m > frac)
return frac_max;
@@ -666,7 +667,7 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
}
static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
- unsigned int m,
+ unsigned long long m,
unsigned int frac)
{
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
@@ -694,7 +695,7 @@ static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
}
static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
- unsigned int freq,
+ unsigned long long freq,
unsigned int *m,
unsigned int *frac,
unsigned int *od)
@@ -706,7 +707,7 @@ static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
continue;
*frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od);
- DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n",
+ DRM_DEBUG_DRIVER("PLL params for %lluHz: m=%x frac=%x od=%d\n",
freq, *m, *frac, *od);
if (meson_hdmi_pll_validate_params(priv, *m, *frac))
@@ -718,7 +719,7 @@ static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
/* pll_freq is the frequency after the OD dividers */
enum drm_mode_status
-meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq)
+meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned long long freq)
{
unsigned int od, m, frac;
@@ -741,7 +742,7 @@ EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq);
/* pll_freq is the frequency after the OD dividers */
static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
- unsigned int pll_freq)
+ unsigned long long pll_freq)
{
unsigned int od, m, frac, od1, od2, od3;
@@ -756,7 +757,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
od1 = od / od2;
}
- DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n",
+ DRM_DEBUG_DRIVER("PLL params for %lluHz: m=%x frac=%x od=%d/%d/%d\n",
pll_freq, m, frac, od1, od2, od3);
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
@@ -764,17 +765,48 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
return;
}
- DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n",
+ DRM_ERROR("Fatal, unable to find parameters for PLL freq %lluHz\n",
pll_freq);
}
+static bool meson_vclk_freqs_are_matching_param(unsigned int idx,
+ unsigned long long phy_freq,
+ unsigned long long vclk_freq)
+{
+ DRM_DEBUG_DRIVER("i = %d vclk_freq = %lluHz alt = %lluHz\n",
+ idx, params[idx].vclk_freq,
+ FREQ_1000_1001(params[idx].vclk_freq));
+ DRM_DEBUG_DRIVER("i = %d phy_freq = %lluHz alt = %lluHz\n",
+ idx, params[idx].phy_freq,
+ FREQ_1000_1001(params[idx].phy_freq));
+
+ /* Match strict frequency */
+ if (phy_freq == params[idx].phy_freq &&
+ vclk_freq == params[idx].vclk_freq)
+ return true;
+
+ /* Match 1000/1001 variant: vclk deviation has to be less than 1kHz
+ * (drm EDID is defined in 1kHz steps, so everything smaller must be
+ * rounding error) and the PHY freq deviation has to be less than
+ * 10kHz (as the TMDS clock is 10 times the pixel clock, so anything
+ * smaller must be rounding error as well).
+ */
+ if (abs(vclk_freq - FREQ_1000_1001(params[idx].vclk_freq)) < 1000 &&
+ abs(phy_freq - FREQ_1000_1001(params[idx].phy_freq)) < 10000)
+ return true;
+
+ /* no match */
+ return false;
+}
+
enum drm_mode_status
-meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
- unsigned int vclk_freq)
+meson_vclk_vic_supported_freq(struct meson_drm *priv,
+ unsigned long long phy_freq,
+ unsigned long long vclk_freq)
{
int i;
- DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n",
+ DRM_DEBUG_DRIVER("phy_freq = %lluHz vclk_freq = %lluHz\n",
phy_freq, vclk_freq);
/* Check against soc revision/package limits */
@@ -785,19 +817,7 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
}
for (i = 0 ; params[i].pixel_freq ; ++i) {
- DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
- i, params[i].pixel_freq,
- FREQ_1000_1001(params[i].pixel_freq));
- DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n",
- i, params[i].phy_freq,
- FREQ_1000_1001(params[i].phy_freq/10)*10);
- /* Match strict frequency */
- if (phy_freq == params[i].phy_freq &&
- vclk_freq == params[i].vclk_freq)
- return MODE_OK;
- /* Match 1000/1001 variant */
- if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) &&
- vclk_freq == FREQ_1000_1001(params[i].vclk_freq))
+ if (meson_vclk_freqs_are_matching_param(i, phy_freq, vclk_freq))
return MODE_OK;
}
@@ -805,8 +825,9 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
}
EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_freq);
-static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
- unsigned int od1, unsigned int od2, unsigned int od3,
+static void meson_vclk_set(struct meson_drm *priv,
+ unsigned long long pll_base_freq, unsigned int od1,
+ unsigned int od2, unsigned int od3,
unsigned int vid_pll_div, unsigned int vclk_div,
unsigned int hdmi_tx_div, unsigned int venc_div,
bool hdmi_use_enci, bool vic_alternate_clock)
@@ -826,15 +847,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
meson_hdmi_pll_generic_set(priv, pll_base_freq);
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
switch (pll_base_freq) {
- case 2970000:
+ case 2970000000:
m = 0x3d;
frac = vic_alternate_clock ? 0xd02 : 0xe00;
break;
- case 4320000:
+ case 4320000000:
m = vic_alternate_clock ? 0x59 : 0x5a;
frac = vic_alternate_clock ? 0xe8f : 0;
break;
- case 5940000:
+ case 5940000000:
m = 0x7b;
frac = vic_alternate_clock ? 0xa05 : 0xc00;
break;
@@ -844,15 +865,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
switch (pll_base_freq) {
- case 2970000:
+ case 2970000000:
m = 0x7b;
frac = vic_alternate_clock ? 0x281 : 0x300;
break;
- case 4320000:
+ case 4320000000:
m = vic_alternate_clock ? 0xb3 : 0xb4;
frac = vic_alternate_clock ? 0x347 : 0;
break;
- case 5940000:
+ case 5940000000:
m = 0xf7;
frac = vic_alternate_clock ? 0x102 : 0x200;
break;
@@ -861,15 +882,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
switch (pll_base_freq) {
- case 2970000:
+ case 2970000000:
m = 0x7b;
frac = vic_alternate_clock ? 0x140b4 : 0x18000;
break;
- case 4320000:
+ case 4320000000:
m = vic_alternate_clock ? 0xb3 : 0xb4;
frac = vic_alternate_clock ? 0x1a3ee : 0;
break;
- case 5940000:
+ case 5940000000:
m = 0xf7;
frac = vic_alternate_clock ? 0x8148 : 0x10000;
break;
@@ -1025,14 +1046,14 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
}
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
- unsigned int phy_freq, unsigned int vclk_freq,
- unsigned int venc_freq, unsigned int dac_freq,
+ unsigned long long phy_freq, unsigned long long vclk_freq,
+ unsigned long long venc_freq, unsigned long long dac_freq,
bool hdmi_use_enci)
{
bool vic_alternate_clock = false;
- unsigned int freq;
- unsigned int hdmi_tx_div;
- unsigned int venc_div;
+ unsigned long long freq;
+ unsigned long long hdmi_tx_div;
+ unsigned long long venc_div;
if (target == MESON_VCLK_TARGET_CVBS) {
meson_venci_cvbs_clock_config(priv);
@@ -1052,27 +1073,25 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
return;
}
- hdmi_tx_div = vclk_freq / dac_freq;
+ hdmi_tx_div = DIV_ROUND_DOWN_ULL(vclk_freq, dac_freq);
if (hdmi_tx_div == 0) {
- pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
+ pr_err("Fatal Error, invalid HDMI-TX freq %lluHz\n",
dac_freq);
return;
}
- venc_div = vclk_freq / venc_freq;
+ venc_div = DIV_ROUND_DOWN_ULL(vclk_freq, venc_freq);
if (venc_div == 0) {
- pr_err("Fatal Error, invalid HDMI venc freq %d\n",
+ pr_err("Fatal Error, invalid HDMI venc freq %lluHz\n",
venc_freq);
return;
}
for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
- if ((phy_freq == params[freq].phy_freq ||
- phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) &&
- (vclk_freq == params[freq].vclk_freq ||
- vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) {
+ if (meson_vclk_freqs_are_matching_param(freq, phy_freq,
+ vclk_freq)) {
if (vclk_freq != params[freq].vclk_freq)
vic_alternate_clock = true;
else
@@ -1098,7 +1117,8 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
}
if (!params[freq].pixel_freq) {
- pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq);
+ pr_err("Fatal Error, invalid HDMI vclk freq %lluHz\n",
+ vclk_freq);
return;
}
diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h
index 60617aaf18dd..7ac55744e574 100644
--- a/drivers/gpu/drm/meson/meson_vclk.h
+++ b/drivers/gpu/drm/meson/meson_vclk.h
@@ -20,17 +20,18 @@ enum {
};
/* 27MHz is the CVBS Pixel Clock */
-#define MESON_VCLK_CVBS 27000
+#define MESON_VCLK_CVBS (27 * 1000 * 1000)
enum drm_mode_status
-meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq);
+meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned long long freq);
enum drm_mode_status
-meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
- unsigned int vclk_freq);
+meson_vclk_vic_supported_freq(struct meson_drm *priv,
+ unsigned long long phy_freq,
+ unsigned long long vclk_freq);
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
- unsigned int phy_freq, unsigned int vclk_freq,
- unsigned int venc_freq, unsigned int dac_freq,
+ unsigned long long phy_freq, unsigned long long vclk_freq,
+ unsigned long long venc_freq, unsigned long long dac_freq,
bool hdmi_use_enci);
#endif /* __MESON_VCLK_H */
diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
index 3c55ed003359..3bf0d6e4fc30 100644
--- a/drivers/gpu/drm/meson/meson_venc.c
+++ b/drivers/gpu/drm/meson/meson_venc.c
@@ -5,7 +5,9 @@
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*/
+#include <linux/bitfield.h>
#include <linux/export.h>
+#include <linux/iopoll.h>
#include <drm/drm_modes.h>
@@ -186,7 +188,7 @@ union meson_hdmi_venc_mode {
} encp;
};
-union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = {
+static union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = {
.enci = {
.hso_begin = 5,
.hso_end = 129,
@@ -206,7 +208,7 @@ union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = {
+static union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = {
.enci = {
.hso_begin = 3,
.hso_end = 129,
@@ -226,7 +228,7 @@ union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = {
.encp = {
.dvi_settings = 0x21,
.video_mode = 0x4000,
@@ -272,7 +274,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = {
.encp = {
.dvi_settings = 0x21,
.video_mode = 0x4000,
@@ -318,7 +320,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = {
.encp = {
.dvi_settings = 0x2029,
.video_mode = 0x4040,
@@ -360,7 +362,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = {
.encp = {
.dvi_settings = 0x202d,
.video_mode = 0x4040,
@@ -405,7 +407,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = {
.encp = {
.dvi_settings = 0x2029,
.video_mode = 0x5ffc,
@@ -454,7 +456,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = {
.encp = {
.dvi_settings = 0x202d,
.video_mode = 0x5ffc,
@@ -503,7 +505,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = {
.encp = {
.dvi_settings = 0xd,
.video_mode = 0x4040,
@@ -552,7 +554,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = {
.encp = {
.dvi_settings = 0x1,
.video_mode = 0x4040,
@@ -596,7 +598,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = {
.encp = {
.dvi_settings = 0xd,
.video_mode = 0x4040,
@@ -644,7 +646,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = {
.encp = {
.dvi_settings = 0x1,
.video_mode = 0x4040,
@@ -688,7 +690,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = {
.encp = {
.dvi_settings = 0x1,
.video_mode = 0x4040,
@@ -730,7 +732,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = {
.encp = {
.dvi_settings = 0x1,
.video_mode = 0x4040,
@@ -772,7 +774,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = {
},
};
-union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = {
+static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = {
.encp = {
.dvi_settings = 0x1,
.video_mode = 0x4040,
@@ -814,7 +816,7 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = {
},
};
-struct meson_hdmi_venc_vic_mode {
+static struct meson_hdmi_venc_vic_mode {
unsigned int vic;
union meson_hdmi_venc_mode *mode;
} meson_hdmi_venc_vic_modes[] = {
@@ -866,10 +868,10 @@ meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode)
DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))
return MODE_BAD;
- if (mode->hdisplay < 640 || mode->hdisplay > 1920)
+ if (mode->hdisplay < 400 || mode->hdisplay > 1920)
return MODE_BAD_HVALUE;
- if (mode->vdisplay < 480 || mode->vdisplay > 1200)
+ if (mode->vdisplay < 480 || mode->vdisplay > 1920)
return MODE_BAD_VVALUE;
return MODE_OK;
@@ -1557,6 +1559,205 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
}
EXPORT_SYMBOL_GPL(meson_venc_hdmi_mode_set);
+static unsigned short meson_encl_gamma_table[256] = {
+ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 260, 264, 268, 272, 276, 280, 284, 288, 292, 296, 300, 304, 308, 312, 316,
+ 320, 324, 328, 332, 336, 340, 344, 348, 352, 356, 360, 364, 368, 372, 376, 380,
+ 384, 388, 392, 396, 400, 404, 408, 412, 416, 420, 424, 428, 432, 436, 440, 444,
+ 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, 488, 492, 496, 500, 504, 508,
+ 512, 516, 520, 524, 528, 532, 536, 540, 544, 548, 552, 556, 560, 564, 568, 572,
+ 576, 580, 584, 588, 592, 596, 600, 604, 608, 612, 616, 620, 624, 628, 632, 636,
+ 640, 644, 648, 652, 656, 660, 664, 668, 672, 676, 680, 684, 688, 692, 696, 700,
+ 704, 708, 712, 716, 720, 724, 728, 732, 736, 740, 744, 748, 752, 756, 760, 764,
+ 768, 772, 776, 780, 784, 788, 792, 796, 800, 804, 808, 812, 816, 820, 824, 828,
+ 832, 836, 840, 844, 848, 852, 856, 860, 864, 868, 872, 876, 880, 884, 888, 892,
+ 896, 900, 904, 908, 912, 916, 920, 924, 928, 932, 936, 940, 944, 948, 952, 956,
+ 960, 964, 968, 972, 976, 980, 984, 988, 992, 996, 1000, 1004, 1008, 1012, 1016, 1020,
+};
+
+static void meson_encl_set_gamma_table(struct meson_drm *priv, u16 *data,
+ u32 rgb_mask)
+{
+ int i, ret;
+ u32 reg;
+
+ writel_bits_relaxed(L_GAMMA_CNTL_PORT_EN, 0,
+ priv->io_base + _REG(L_GAMMA_CNTL_PORT));
+
+ ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT),
+ reg, reg & L_GAMMA_CNTL_PORT_ADR_RDY, 10, 10000);
+ if (ret)
+ pr_warn("%s: GAMMA ADR_RDY timeout\n", __func__);
+
+ writel_relaxed(L_GAMMA_ADDR_PORT_AUTO_INC | rgb_mask |
+ FIELD_PREP(L_GAMMA_ADDR_PORT_ADDR, 0),
+ priv->io_base + _REG(L_GAMMA_ADDR_PORT));
+
+ for (i = 0; i < 256; i++) {
+ ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT),
+ reg, reg & L_GAMMA_CNTL_PORT_WR_RDY,
+ 10, 10000);
+ if (ret)
+ pr_warn_once("%s: GAMMA WR_RDY timeout\n", __func__);
+
+ writel_relaxed(data[i], priv->io_base + _REG(L_GAMMA_DATA_PORT));
+ }
+
+ ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT),
+ reg, reg & L_GAMMA_CNTL_PORT_ADR_RDY, 10, 10000);
+ if (ret)
+ pr_warn("%s: GAMMA ADR_RDY timeout\n", __func__);
+
+ writel_relaxed(L_GAMMA_ADDR_PORT_AUTO_INC | rgb_mask |
+ FIELD_PREP(L_GAMMA_ADDR_PORT_ADDR, 0x23),
+ priv->io_base + _REG(L_GAMMA_ADDR_PORT));
+}
+
+void meson_encl_load_gamma(struct meson_drm *priv)
+{
+ meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_R);
+ meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_G);
+ meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_B);
+
+ writel_bits_relaxed(L_GAMMA_CNTL_PORT_EN, L_GAMMA_CNTL_PORT_EN,
+ priv->io_base + _REG(L_GAMMA_CNTL_PORT));
+}
+
+void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv,
+ const struct drm_display_mode *mode)
+{
+ unsigned int max_pxcnt;
+ unsigned int max_lncnt;
+ unsigned int havon_begin;
+ unsigned int havon_end;
+ unsigned int vavon_bline;
+ unsigned int vavon_eline;
+ unsigned int hso_begin;
+ unsigned int hso_end;
+ unsigned int vso_begin;
+ unsigned int vso_end;
+ unsigned int vso_bline;
+ unsigned int vso_eline;
+
+ max_pxcnt = mode->htotal - 1;
+ max_lncnt = mode->vtotal - 1;
+ havon_begin = mode->htotal - mode->hsync_start;
+ havon_end = havon_begin + mode->hdisplay - 1;
+ vavon_bline = mode->vtotal - mode->vsync_start;
+ vavon_eline = vavon_bline + mode->vdisplay - 1;
+ hso_begin = 0;
+ hso_end = mode->hsync_end - mode->hsync_start;
+ vso_begin = 0;
+ vso_end = 0;
+ vso_bline = 0;
+ vso_eline = mode->vsync_end - mode->vsync_start;
+
+ meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCL);
+
+ writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
+
+ writel_relaxed(ENCL_PX_LN_CNT_SHADOW_EN, priv->io_base + _REG(ENCL_VIDEO_MODE));
+ writel_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN |
+ ENCL_VIDEO_MODE_ADV_GAIN_HDTV |
+ ENCL_SEL_GAMMA_RGB_IN, priv->io_base + _REG(ENCL_VIDEO_MODE_ADV));
+
+ writel_relaxed(ENCL_VIDEO_FILT_CTRL_BYPASS_FILTER,
+ priv->io_base + _REG(ENCL_VIDEO_FILT_CTRL));
+ writel_relaxed(max_pxcnt, priv->io_base + _REG(ENCL_VIDEO_MAX_PXCNT));
+ writel_relaxed(max_lncnt, priv->io_base + _REG(ENCL_VIDEO_MAX_LNCNT));
+ writel_relaxed(havon_begin, priv->io_base + _REG(ENCL_VIDEO_HAVON_BEGIN));
+ writel_relaxed(havon_end, priv->io_base + _REG(ENCL_VIDEO_HAVON_END));
+ writel_relaxed(vavon_bline, priv->io_base + _REG(ENCL_VIDEO_VAVON_BLINE));
+ writel_relaxed(vavon_eline, priv->io_base + _REG(ENCL_VIDEO_VAVON_ELINE));
+
+ writel_relaxed(hso_begin, priv->io_base + _REG(ENCL_VIDEO_HSO_BEGIN));
+ writel_relaxed(hso_end, priv->io_base + _REG(ENCL_VIDEO_HSO_END));
+ writel_relaxed(vso_begin, priv->io_base + _REG(ENCL_VIDEO_VSO_BEGIN));
+ writel_relaxed(vso_end, priv->io_base + _REG(ENCL_VIDEO_VSO_END));
+ writel_relaxed(vso_bline, priv->io_base + _REG(ENCL_VIDEO_VSO_BLINE));
+ writel_relaxed(vso_eline, priv->io_base + _REG(ENCL_VIDEO_VSO_ELINE));
+ writel_relaxed(ENCL_VIDEO_RGBIN_RGB | ENCL_VIDEO_RGBIN_ZBLK,
+ priv->io_base + _REG(ENCL_VIDEO_RGBIN_CTRL));
+
+ /* default black pattern */
+ writel_relaxed(0, priv->io_base + _REG(ENCL_TST_MDSEL));
+ writel_relaxed(0, priv->io_base + _REG(ENCL_TST_Y));
+ writel_relaxed(0, priv->io_base + _REG(ENCL_TST_CB));
+ writel_relaxed(0, priv->io_base + _REG(ENCL_TST_CR));
+ writel_relaxed(1, priv->io_base + _REG(ENCL_TST_EN));
+ writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, 0,
+ priv->io_base + _REG(ENCL_VIDEO_MODE_ADV));
+
+ writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN));
+
+ writel_relaxed(0, priv->io_base + _REG(L_RGB_BASE_ADDR));
+ writel_relaxed(0x400, priv->io_base + _REG(L_RGB_COEFF_ADDR)); /* Magic value */
+
+ writel_relaxed(L_DITH_CNTL_DITH10_EN, priv->io_base + _REG(L_DITH_CNTL_ADDR));
+
+ /* DE signal for TTL */
+ writel_relaxed(havon_begin, priv->io_base + _REG(L_OEH_HS_ADDR));
+ writel_relaxed(havon_end + 1, priv->io_base + _REG(L_OEH_HE_ADDR));
+ writel_relaxed(vavon_bline, priv->io_base + _REG(L_OEH_VS_ADDR));
+ writel_relaxed(vavon_eline, priv->io_base + _REG(L_OEH_VE_ADDR));
+
+ /* DE signal for TTL */
+ writel_relaxed(havon_begin, priv->io_base + _REG(L_OEV1_HS_ADDR));
+ writel_relaxed(havon_end + 1, priv->io_base + _REG(L_OEV1_HE_ADDR));
+ writel_relaxed(vavon_bline, priv->io_base + _REG(L_OEV1_VS_ADDR));
+ writel_relaxed(vavon_eline, priv->io_base + _REG(L_OEV1_VE_ADDR));
+
+ /* Hsync signal for TTL */
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC) {
+ writel_relaxed(hso_begin, priv->io_base + _REG(L_STH1_HS_ADDR));
+ writel_relaxed(hso_end, priv->io_base + _REG(L_STH1_HE_ADDR));
+ } else {
+ writel_relaxed(hso_end, priv->io_base + _REG(L_STH1_HS_ADDR));
+ writel_relaxed(hso_begin, priv->io_base + _REG(L_STH1_HE_ADDR));
+ }
+ writel_relaxed(0, priv->io_base + _REG(L_STH1_VS_ADDR));
+ writel_relaxed(max_lncnt, priv->io_base + _REG(L_STH1_VE_ADDR));
+
+ /* Vsync signal for TTL */
+ writel_relaxed(vso_begin, priv->io_base + _REG(L_STV1_HS_ADDR));
+ writel_relaxed(vso_end, priv->io_base + _REG(L_STV1_HE_ADDR));
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
+ writel_relaxed(vso_bline, priv->io_base + _REG(L_STV1_VS_ADDR));
+ writel_relaxed(vso_eline, priv->io_base + _REG(L_STV1_VE_ADDR));
+ } else {
+ writel_relaxed(vso_eline, priv->io_base + _REG(L_STV1_VS_ADDR));
+ writel_relaxed(vso_bline, priv->io_base + _REG(L_STV1_VE_ADDR));
+ }
+
+ /* DE signal */
+ writel_relaxed(havon_begin, priv->io_base + _REG(L_DE_HS_ADDR));
+ writel_relaxed(havon_end + 1, priv->io_base + _REG(L_DE_HE_ADDR));
+ writel_relaxed(vavon_bline, priv->io_base + _REG(L_DE_VS_ADDR));
+ writel_relaxed(vavon_eline, priv->io_base + _REG(L_DE_VE_ADDR));
+
+ /* Hsync signal */
+ writel_relaxed(hso_begin, priv->io_base + _REG(L_HSYNC_HS_ADDR));
+ writel_relaxed(hso_end, priv->io_base + _REG(L_HSYNC_HE_ADDR));
+ writel_relaxed(0, priv->io_base + _REG(L_HSYNC_VS_ADDR));
+ writel_relaxed(max_lncnt, priv->io_base + _REG(L_HSYNC_VE_ADDR));
+
+ /* Vsync signal */
+ writel_relaxed(vso_begin, priv->io_base + _REG(L_VSYNC_HS_ADDR));
+ writel_relaxed(vso_end, priv->io_base + _REG(L_VSYNC_HE_ADDR));
+ writel_relaxed(vso_bline, priv->io_base + _REG(L_VSYNC_VS_ADDR));
+ writel_relaxed(vso_eline, priv->io_base + _REG(L_VSYNC_VE_ADDR));
+
+ writel_relaxed(0, priv->io_base + _REG(L_INV_CNT_ADDR));
+ writel_relaxed(L_TCON_MISC_SEL_STV1 | L_TCON_MISC_SEL_STV2,
+ priv->io_base + _REG(L_TCON_MISC_SEL_ADDR));
+
+ priv->venc.current_mode = MESON_VENC_MODE_MIPI_DSI;
+}
+EXPORT_SYMBOL_GPL(meson_venc_mipi_dsi_mode_set);
+
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
struct meson_cvbs_enci_mode *mode)
{
@@ -1747,8 +1948,15 @@ unsigned int meson_venci_get_field(struct meson_drm *priv)
void meson_venc_enable_vsync(struct meson_drm *priv)
{
- writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN,
- priv->io_base + _REG(VENC_INTCTRL));
+ switch (priv->venc.current_mode) {
+ case MESON_VENC_MODE_MIPI_DSI:
+ writel_relaxed(VENC_INTCTRL_ENCP_LNRST_INT_EN,
+ priv->io_base + _REG(VENC_INTCTRL));
+ break;
+ default:
+ writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN,
+ priv->io_base + _REG(VENC_INTCTRL));
+ }
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25));
}
diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
index 9138255ffc9e..0f59adb1c6db 100644
--- a/drivers/gpu/drm/meson/meson_venc.h
+++ b/drivers/gpu/drm/meson/meson_venc.h
@@ -21,6 +21,7 @@ enum {
MESON_VENC_MODE_CVBS_PAL,
MESON_VENC_MODE_CVBS_NTSC,
MESON_VENC_MODE_HDMI,
+ MESON_VENC_MODE_MIPI_DSI,
};
struct meson_cvbs_enci_mode {
@@ -47,6 +48,9 @@ struct meson_cvbs_enci_mode {
unsigned int analog_sync_adj;
};
+/* LCD Encoder gamma setup */
+void meson_encl_load_gamma(struct meson_drm *priv);
+
/* HDMI Clock parameters */
enum drm_mode_status
meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode);
@@ -63,6 +67,8 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
unsigned int ycrcb_map,
bool yuv420_mode,
const struct drm_display_mode *mode);
+void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv,
+ const struct drm_display_mode *mode);
unsigned int meson_venci_get_field(struct meson_drm *priv);
void meson_venc_enable_vsync(struct meson_drm *priv);
diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c
deleted file mode 100644
index f1747fde1fe0..000000000000
--- a/drivers/gpu/drm/meson/meson_venc_cvbs.c
+++ /dev/null
@@ -1,293 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2016 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
- * Copyright (C) 2014 Endless Mobile
- *
- * Written by:
- * Jasper St. Pierre <jstpierre@mecheye.net>
- */
-
-#include <linux/export.h>
-#include <linux/of_graph.h>
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_device.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_print.h>
-
-#include "meson_registers.h"
-#include "meson_vclk.h"
-#include "meson_venc_cvbs.h"
-
-/* HHI VDAC Registers */
-#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
-#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */
-#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
-#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */
-
-struct meson_venc_cvbs {
- struct drm_encoder encoder;
- struct drm_connector connector;
- struct meson_drm *priv;
-};
-#define encoder_to_meson_venc_cvbs(x) \
- container_of(x, struct meson_venc_cvbs, encoder)
-
-#define connector_to_meson_venc_cvbs(x) \
- container_of(x, struct meson_venc_cvbs, connector)
-
-/* Supported Modes */
-
-struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
- { /* PAL */
- .enci = &meson_cvbs_enci_pal,
- .mode = {
- DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
- 720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
- DRM_MODE_FLAG_INTERLACE),
- .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
- },
- },
- { /* NTSC */
- .enci = &meson_cvbs_enci_ntsc,
- .mode = {
- DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
- 720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
- DRM_MODE_FLAG_INTERLACE),
- .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
- },
- },
-};
-
-static const struct meson_cvbs_mode *
-meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
-{
- int i;
-
- for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
- struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
-
- if (drm_mode_match(req_mode, &meson_mode->mode,
- DRM_MODE_MATCH_TIMINGS |
- DRM_MODE_MATCH_CLOCK |
- DRM_MODE_MATCH_FLAGS |
- DRM_MODE_MATCH_3D_FLAGS))
- return meson_mode;
- }
-
- return NULL;
-}
-
-/* Connector */
-
-static void meson_cvbs_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status
-meson_cvbs_connector_detect(struct drm_connector *connector, bool force)
-{
- /* FIXME: Add load-detect or jack-detect if possible */
- return connector_status_connected;
-}
-
-static int meson_cvbs_connector_get_modes(struct drm_connector *connector)
-{
- struct drm_device *dev = connector->dev;
- struct drm_display_mode *mode;
- int i;
-
- for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
- struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
-
- mode = drm_mode_duplicate(dev, &meson_mode->mode);
- if (!mode) {
- DRM_ERROR("Failed to create a new display mode\n");
- return 0;
- }
-
- drm_mode_probed_add(connector, mode);
- }
-
- return i;
-}
-
-static int meson_cvbs_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /* Validate the modes added in get_modes */
- return MODE_OK;
-}
-
-static const struct drm_connector_funcs meson_cvbs_connector_funcs = {
- .detect = meson_cvbs_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = meson_cvbs_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const
-struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = {
- .get_modes = meson_cvbs_connector_get_modes,
- .mode_valid = meson_cvbs_connector_mode_valid,
-};
-
-/* Encoder */
-
-static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder)
-{
- drm_encoder_cleanup(encoder);
-}
-
-static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = {
- .destroy = meson_venc_cvbs_encoder_destroy,
-};
-
-static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- if (meson_cvbs_get_mode(&crtc_state->mode))
- return 0;
-
- return -EINVAL;
-}
-
-static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
-{
- struct meson_venc_cvbs *meson_venc_cvbs =
- encoder_to_meson_venc_cvbs(encoder);
- struct meson_drm *priv = meson_venc_cvbs->priv;
-
- /* Disable CVBS VDAC */
- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
- regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
- regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
- } else {
- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
- }
-}
-
-static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
-{
- struct meson_venc_cvbs *meson_venc_cvbs =
- encoder_to_meson_venc_cvbs(encoder);
- struct meson_drm *priv = meson_venc_cvbs->priv;
-
- /* VDAC0 source is not from ATV */
- writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
- priv->io_base + _REG(VENC_VDAC_DACSEL0));
-
- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
- } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
- meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
- } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
- regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
- regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
- }
-}
-
-static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode);
- struct meson_venc_cvbs *meson_venc_cvbs =
- encoder_to_meson_venc_cvbs(encoder);
- struct meson_drm *priv = meson_venc_cvbs->priv;
-
- if (meson_mode) {
- meson_venci_cvbs_mode_set(priv, meson_mode->enci);
-
- /* Setup 27MHz vclk2 for ENCI and VDAC */
- meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
- MESON_VCLK_CVBS, MESON_VCLK_CVBS,
- MESON_VCLK_CVBS, MESON_VCLK_CVBS,
- true);
- }
-}
-
-static const struct drm_encoder_helper_funcs
- meson_venc_cvbs_encoder_helper_funcs = {
- .atomic_check = meson_venc_cvbs_encoder_atomic_check,
- .disable = meson_venc_cvbs_encoder_disable,
- .enable = meson_venc_cvbs_encoder_enable,
- .mode_set = meson_venc_cvbs_encoder_mode_set,
-};
-
-static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv)
-{
- struct device_node *remote;
-
- remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0);
- if (!remote)
- return false;
-
- of_node_put(remote);
- return true;
-}
-
-int meson_venc_cvbs_create(struct meson_drm *priv)
-{
- struct drm_device *drm = priv->drm;
- struct meson_venc_cvbs *meson_venc_cvbs;
- struct drm_connector *connector;
- struct drm_encoder *encoder;
- int ret;
-
- if (!meson_venc_cvbs_connector_is_available(priv)) {
- dev_info(drm->dev, "CVBS Output connector not available\n");
- return 0;
- }
-
- meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs),
- GFP_KERNEL);
- if (!meson_venc_cvbs)
- return -ENOMEM;
-
- meson_venc_cvbs->priv = priv;
- encoder = &meson_venc_cvbs->encoder;
- connector = &meson_venc_cvbs->connector;
-
- /* Connector */
-
- drm_connector_helper_add(connector,
- &meson_cvbs_connector_helper_funcs);
-
- ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs,
- DRM_MODE_CONNECTOR_Composite);
- if (ret) {
- dev_err(priv->dev, "Failed to init CVBS connector\n");
- return ret;
- }
-
- connector->interlace_allowed = 1;
-
- /* Encoder */
-
- drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs);
-
- ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs,
- DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs");
- if (ret) {
- dev_err(priv->dev, "Failed to init CVBS encoder\n");
- return ret;
- }
-
- encoder->possible_crtcs = BIT(0);
-
- drm_connector_attach_encoder(connector, encoder);
-
- return 0;
-}
diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
index aede0c67a57f..cd399b0b7181 100644
--- a/drivers/gpu/drm/meson/meson_viu.c
+++ b/drivers/gpu/drm/meson/meson_viu.c
@@ -94,7 +94,7 @@ static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
- writel((m[11] & 0x1fff) << 16,
+ writel((m[11] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
@@ -425,21 +425,25 @@ void meson_viu_init(struct meson_drm *priv)
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
meson_viu_load_matrix(priv);
- else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
+ else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
true);
+ /* fix green/pink color distortion from vendor u-boot */
+ writel_bits_relaxed(OSD1_HDR2_CTRL_REG_ONLY_MAT |
+ OSD1_HDR2_CTRL_VDIN0_HDR2_TOP_EN, 0,
+ priv->io_base + _REG(OSD1_HDR2_CTRL));
+ }
/* Initialize OSD1 fifo control register */
reg = VIU_OSD_DDR_PRIORITY_URGENT |
- VIU_OSD_HOLD_FIFO_LINES(31) |
VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */
VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
- reg |= VIU_OSD_BURST_LENGTH_32;
+ reg |= (VIU_OSD_BURST_LENGTH_32 | VIU_OSD_HOLD_FIFO_LINES(31));
else
- reg |= VIU_OSD_BURST_LENGTH_64;
+ reg |= (VIU_OSD_BURST_LENGTH_64 | VIU_OSD_HOLD_FIFO_LINES(4));
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
@@ -464,17 +468,17 @@ void meson_viu_init(struct meson_drm *priv)
priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
- writel_relaxed(VIU_OSD_BLEND_REORDER(0, 1) |
- VIU_OSD_BLEND_REORDER(1, 0) |
- VIU_OSD_BLEND_REORDER(2, 0) |
- VIU_OSD_BLEND_REORDER(3, 0) |
- VIU_OSD_BLEND_DIN_EN(1) |
- VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 |
- VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 |
- VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 |
- VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) |
- VIU_OSD_BLEND_HOLD_LINES(4),
- priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
+ u32 val = (u32)VIU_OSD_BLEND_REORDER(0, 1) |
+ (u32)VIU_OSD_BLEND_REORDER(1, 0) |
+ (u32)VIU_OSD_BLEND_REORDER(2, 0) |
+ (u32)VIU_OSD_BLEND_REORDER(3, 0) |
+ (u32)VIU_OSD_BLEND_DIN_EN(1) |
+ (u32)VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 |
+ (u32)VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 |
+ (u32)VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 |
+ (u32)VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) |
+ (u32)VIU_OSD_BLEND_HOLD_LINES(4);
+ writel_relaxed(val, priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE,
priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c
index 154837688ab0..5df1957c8e41 100644
--- a/drivers/gpu/drm/meson/meson_vpp.c
+++ b/drivers/gpu/drm/meson/meson_vpp.c
@@ -100,6 +100,8 @@ void meson_vpp_init(struct meson_drm *priv)
priv->io_base + _REG(VPP_DOLBY_CTRL));
writel_relaxed(0x1020080,
priv->io_base + _REG(VPP_DUMMY_DATA1));
+ writel_relaxed(0x42020,
+ priv->io_base + _REG(VPP_DUMMY_DATA));
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
writel_relaxed(0xf, priv->io_base + _REG(DOLBY_PATH_CTRL));
diff --git a/drivers/gpu/drm/meson/meson_vpp.h b/drivers/gpu/drm/meson/meson_vpp.h
index afc9553ed8d3..b790042a1650 100644
--- a/drivers/gpu/drm/meson/meson_vpp.h
+++ b/drivers/gpu/drm/meson/meson_vpp.h
@@ -12,6 +12,8 @@
struct drm_rect;
struct meson_drm;
+/* Mux VIU/VPP to ENCL */
+#define MESON_VIU_VPP_MUX_ENCL 0x0
/* Mux VIU/VPP to ENCI */
#define MESON_VIU_VPP_MUX_ENCI 0x5
/* Mux VIU/VPP to ENCP */