Ported from vendor kernel [1].
[1] https://gitee.com/thead-yocto/kernel.git
Change-Id: I66e5fc772a89f9bcccac678a0cd2652d3dee1357
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
source "drivers/gpu/drm/sprd/Kconfig"
+source "drivers/gpu/drm/verisilicon/Kconfig"
+
config DRM_HYPERV
tristate "DRM Support for Hyper-V synthetic video device"
depends on DRM && PCI && MMU && HYPERV
obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
obj-$(CONFIG_DRM_LOONGSON) += loongson/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_VERISILICON
+ tristate "DRM Support for VeriSilicon"
+ depends on DRM
+ select DRM_KMS_HELPER
+ select VERISILICON_DW_MIPI_DSI
+ select VERISILICON_DW_HDMI_LIGHT
+ help
+ Choose this option if you have a VeriSilicon soc chipset.
+ This driver provides VeriSilicon kernel mode
+ setting and buffer management. It does not
+ provide 2D or 3D acceleration.
+
+config VERISILICON_VIRTUAL_DISPLAY
+ bool "display content output to debugfs file"
+ depends on DRM_VERISILICON
+ select DEBUG_FS
+ help
+ This is a debug feature which capture video content output
+ from display controller. Output path is debugfs/dri/connector/.
+ The content format is ARGB which Alpha is 0 for 8bits.
+ Disabled in default.
+
+config VERISILICON_DW_MIPI_DSI
+ bool "VeriSilicon specific driver for Synopsys DW MIPI DSI"
+ depends on DRM_VERISILICON
+ select DRM_MIPI_DSI
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ select PHY_DW_DPHY
+ help
+ This driver supports Synopsys DW MIPI DSI controller.
+ MIPI DSI controller is a bridge between encoder
+ and panel. Also it is a MIPI DSI host and can work
+ in dual mode.
+
+config VERISILICON_DW_HDMI_LIGHT
+ bool "THEAD sepcific driver for Synopsys DW HDMI on Light"
+ depends on DRM_VERISILICON
+ select DRM_DW_HDMI
+ help
+ This driver supports Synopsys DW HDMI controller on Light
+ platform from THEAD Corp.
+
+config VERISILICON_MMU
+ bool "MMU support for VeriSilicon display controller"
+ depends on DRM_VERISILICON
+ help
+ This is a memory management function which translate virtual address
+ to physical address. DPU MMU only do address translate, doesn't
+ support shareable.
+
+config VERISILICON_DEC
+ bool "DEC support for VeriSilicon display controller"
+ depends on DRM_VERISILICON
+ help
+ This is a decompression function which reads compressed pixels from
+ external memory (DDR or SRAM) under DPU's control, then it decompress
+ comprssed pixels before returing these pixels to DPU.
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+
+vs_drm-objs := vs_dc_hw.o \
+ vs_dc.o \
+ vs_crtc.o \
+ vs_drv.o \
+ vs_fb.o \
+ vs_gem.o \
+ vs_plane.o \
+ vs_simple_enc.o
+
+vs_drm-$(CONFIG_VERISILICON_VIRTUAL_DISPLAY) += vs_virtual.o
+vs_drm-$(CONFIG_VERISILICON_DW_MIPI_DSI) += dw_mipi_dsi.o
+vs_drm-$(CONFIG_VERISILICON_DW_HDMI_LIGHT) += dw_hdmi-light.o
+vs_drm-$(CONFIG_VERISILICON_MMU) += vs_dc_mmu.o
+vs_drm-$(CONFIG_VERISILICON_DEC) += vs_dc_dec.o
+
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_of.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw_hdmi_tx_phy_gen2.h"
+
+struct light_hdmi {
+ struct device *dev;
+ struct drm_encoder encoder;
+ struct dw_hdmi *dw_hdmi;
+ /* TODO: may not be used */
+ struct regmap *sysreg;
+};
+
+static void dw_hdmi_light_encoder_enable(struct drm_encoder *encoder)
+{
+}
+
+static int dw_hdmi_light_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return 0;
+}
+
+static struct dw_hdmi_plat_data light_hdmi_drv_data = {
+ .mode_valid = dw_hdmi_tx_phy_gen2_mode_valid,
+ .configure_phy = dw_hdmi_tx_phy_gen2_configure,
+};
+
+static const struct of_device_id dw_hdmi_light_dt_ids[] = {
+ {
+ .compatible = "thead,light-hdmi-tx",
+ .data = &light_hdmi_drv_data,
+ },
+ {
+ /* sentinel */
+ },
+};
+
+static const struct drm_encoder_helper_funcs dw_hdmi_light_encoder_helper_funcs = {
+ .enable = dw_hdmi_light_encoder_enable,
+ .atomic_check = dw_hdmi_light_encoder_atomic_check,
+};
+
+struct dw_hdmi_light_private dw_hdmi_priv_data = {
+ .max_pixclock = 594000,
+ .max_width = 0,
+ .max_height = 0,
+};
+
+static int dw_hdmi_light_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int ret = 0;
+ u32 max_pixclock = 0;
+ u16 max_width = 0;
+ u16 max_height = 0;
+ int property_ret = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *match;
+ const struct dw_hdmi_plat_data *plat_data;
+ struct light_hdmi *hdmi;
+ struct drm_device *drm = data;
+ struct drm_encoder *encoder;
+
+ if (!np)
+ return -ENODEV;
+ hdmi = dev_get_drvdata(dev);
+
+ match = of_match_node(dw_hdmi_light_dt_ids, np);
+ if (unlikely(!match))
+ return -ENODEV;
+
+ property_ret = of_property_read_u32(np, "max_pixclock", &max_pixclock);
+ if(property_ret == 0){
+ printk(KERN_INFO "Hdmi max pixel clock = %d\n", max_pixclock);
+ if(dw_hdmi_priv_data.max_pixclock > max_pixclock){
+ dw_hdmi_priv_data.max_pixclock = max_pixclock;
+ printk(KERN_INFO"Hdmi max pixel clock adjust to %d\n", max_pixclock);
+ }
+ else{
+ printk(KERN_INFO"Hdmi max pixel clock not take effect\n");
+ }
+ }
+
+ property_ret = of_property_read_u16(np, "max_width", &max_width);
+ if(property_ret == 0){
+ dw_hdmi_priv_data.max_width = max_width;
+ }
+
+ property_ret = of_property_read_u16(np, "max_height", &max_height);
+ if(property_ret == 0){
+ dw_hdmi_priv_data.max_height = max_height;
+ }
+
+ light_hdmi_drv_data.priv_data = (void*)&dw_hdmi_priv_data;
+
+ plat_data = match->data;
+ hdmi->dev = &pdev->dev;
+ encoder = &hdmi->encoder;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, np);
+ if (!encoder->possible_crtcs)
+ return -EPROBE_DEFER;
+
+ drm_encoder_helper_add(encoder, &dw_hdmi_light_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+ hdmi->dw_hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ if (IS_ERR(hdmi->dw_hdmi)) {
+ ret = PTR_ERR(hdmi->dw_hdmi);
+ drm_encoder_cleanup(encoder);
+ }
+ pm_runtime_enable(dev);
+
+ return ret;
+}
+
+static void dw_hdmi_light_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+ pm_runtime_disable(dev);
+
+ dw_hdmi_unbind(hdmi->dw_hdmi);
+}
+
+static const struct component_ops dw_hdmi_light_ops = {
+ .bind = dw_hdmi_light_bind,
+ .unbind = dw_hdmi_light_unbind,
+};
+
+static int dw_hdmi_light_probe(struct platform_device *pdev)
+{
+ struct light_hdmi *hdmi;
+
+ int ret_comp;
+
+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hdmi);
+
+
+ ret_comp = component_add(&pdev->dev, &dw_hdmi_light_ops);
+
+ return ret_comp;
+}
+
+static int dw_hdmi_light_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dw_hdmi_light_ops);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdmi_runtime_suspend(struct device *dev)
+{
+ struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+ return dw_hdmi_runtime_suspend(hdmi->dw_hdmi);
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+ struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+ return dw_hdmi_runtime_resume(hdmi->dw_hdmi);
+}
+#endif
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_resume(struct device *dev)
+{
+ struct light_hdmi *hdmi = dev_get_drvdata(dev);
+ dev_info(dev,"hdmi resume\n");
+ dw_hdmi_resume(hdmi->dw_hdmi);
+ return 0;
+}
+#endif
+static const struct dev_pm_ops dw_hdmi_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+ #ifdef CONFIG_PM_SLEEP
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,hdmi_resume)
+ #endif
+};
+
+struct platform_driver dw_hdmi_light_platform_driver = {
+ .probe = dw_hdmi_light_probe,
+ .remove = dw_hdmi_light_remove,
+ .driver = {
+ .name = "dwhdmi-light",
+ .of_match_table = dw_hdmi_light_dt_ids,
+ .pm = &dw_hdmi_pm_ops,
+ },
+};
+
+MODULE_AUTHOR("You Xiao <youxiao.fc@linux.alibaba.com>");
+MODULE_DESCRIPTION("Light Platforms Specific DW-HDMI Driver Extention");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwhdmi-light");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __DW_HDMI_LIGHT_H__
+#define __DW_HDMI_LIGHT_H__
+
+extern struct platform_driver dw_hdmi_light_platform_driver;
+#endif
--- /dev/null
+#ifndef _DW_HDMI_PHY_GEN2_H_
+#define _DW_HDMI_PHY_GEN2_H_
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <drm/bridge/dw_hdmi.h>
+
+/* PHY registers */
+#define DW_HDMI_TX_PHY_GEN2_PWRCTRL 0x00
+#define DW_HDMI_TX_PHY_GEN2_SERDIVCTRL 0x01
+#define DW_HDMI_TX_PHY_GEN2_SERCKCTRL 0x02
+#define DW_HDMI_TX_PHY_GEN2_SERCKKILLCTRL 0x03
+#define DW_HDMI_TX_PHY_GEN2_TXRESCALCTRL 0x04
+#define DW_HDMI_TX_PHY_GEN2_OPMODE_PLLCFG 0x06
+#define DW_HDMI_TX_PHY_GEN2_TXCKMEASCTRL 0x07
+#define DW_HDMI_TX_PHY_GEN2_TXMEASCTRL 0x08
+#define DW_HDMI_TX_PHY_GEN2_CKSYMTXCTRL 0x09
+#define DW_HDMI_TX_PHY_GEN2_CMPSEQCTRL 0x0A
+#define DW_HDMI_TX_PHY_GEN2_CMPPWRCTRL 0x0B
+#define DW_HDMI_TX_PHY_GEN2_CMPMODECTRL 0x0C
+#define DW_HDMI_TX_PHY_GEN2_MEASCTRL 0x0D
+#define DW_HDMI_TX_PHY_GEN2_VLEVCTRL_PLLMEASCTRL 0x0E
+#define DW_HDMI_TX_PHY_GEN2_D2ACTRL 0x0F
+#define DW_HDMI_TX_PHY_GEN2_PLLCURRGMPCTRL 0x10
+#define DW_HDMI_TX_PHY_GEN2_PLLDIVCTRL 0x11
+#define DW_HDMI_TX_PHY_GEN2_SUPPLLBGCTRL 0x12
+#define DW_HDMI_TX_PHY_GEN2_PLLCONFCTRL 0x13
+#define DW_HDMI_TX_PHY_GEN2_PLLCTRL 0x14
+#define DW_HDMI_TX_PHY_GEN2_PLLDIGTESTCTRL 0x15
+#define DW_HDMI_TX_PHY_GEN2_PLLMEASCTRL 0x16
+#define DW_HDMI_TX_PHY_GEN2_PLLCLKBISTPHASE 0x17
+#define DW_HDMI_TX_PHY_GEN2_COMPRCAL 0x18
+#define DW_HDMI_TX_PHY_GEN2_TXTERM 0x19
+#define DW_HDMI_TX_PHY_GEN2_PWRSEQ_PATGENSKIP 0x1A
+#define DW_HDMI_TX_PHY_GEN2_PATTERNGEN_BISTSEL 0x1B
+#define DW_HDMI_TX_PHY_GEN2_CTRL_CLKALIGN_PGMODE_BISTPG 0x1C
+#define DW_HDMI_TX_PHY_GEN2_BISTCHSTATUS_CLKALIGN_RCAL 0x1D
+#define DW_HDMI_TX_PHY_GEN2_DIGTXMODE 0x1E
+#define DW_HDMI_TX_PHY_GEN2_CLKALIGN_RCAL_SOFTRESET 0x1F
+#define DW_HDMI_TX_PHY_GEN2_PWRSEQ_PATGENSKIP_RD 0x2E
+#define DW_HDMI_TX_PHY_GEN2_PLLCLKBISTPHASE_RD 0x2F
+#define DW_HDMI_TX_PHY_GEN2_PLLSTATUS 0x30
+#define DW_HDMI_TX_PHY_GEN2_SUPPLLBGSTATUS 0x31
+#define DW_HDMI_TX_PHY_GEN2_D2ASTATUS 0x32
+#define DW_HDMI_TX_PHY_GEN2_CMPMODESTATUS 0x33
+#define DW_HDMI_TX_PHY_GEN2_CMPPWRSTATUS 0x34
+#define DW_HDMI_TX_PHY_GEN2_CMPSEQSTATUS 0x35
+#define DW_HDMI_TX_PHY_GEN2_CKSYMTXSTATUS 0x36
+#define DW_HDMI_TX_PHY_GEN2_TXRESCALSTATUS 0x37
+#define DW_HDMI_TX_PHY_GEN2_SERCKKILLSTATUS 0x38
+#define DW_HDMI_TX_PHY_GEN2_SERCKSTATUS 0x39
+#define DW_HDMI_TX_PHY_GEN2_SERDIVSTATUS 0x3A
+#define DW_HDMI_TX_PHY_GEN2_PWRSTATUS 0x3B
+
+/* PHY register bitfields */
+#define PWRCTRL_OVERRIDE_0 BIT(15)
+#define PWRCTRL_BIAS_ON BIT(5)
+#define PWRCTRL_TX_PWRON BIT(4)
+#define PWRCTRL_TX_PWRON0 BIT(3)
+#define PWRCTRL_TX_PWRON1 BIT(2)
+#define PWRCTRL_TX_PWRON2 BIT(1)
+#define PWRCTRL_CK_PWRON BIT(0)
+
+#define OPMODE_PLLCFG_PREP_DIV GENMASK(13, 12)
+#define OPMODE_PLLCFG_MPLL_CKO_DIV GENMASK(10, 9)
+#define OPMODE_PLLCFG_OPMODE GENMASK( 7, 6)
+#define OPMODE_PLLCFG_REF_CNTRL GENMASK( 4, 3)
+#define OPMODE_PLLCFG_MPLL_N_CNTRL GENMASK( 1, 0)
+
+#define CKSYMTXCTRL_OVERRIDE_5 BIT(15)
+#define CKSYMTXCTRL_SLOPE_BOOST GENMASK(13, 10)
+#define CKSYMTXCTRL_TX_TRAON BIT(9)
+#define CKSYMTXCTRL_TX_TRBON BIT(8)
+#define CKSYMTXCTRL_TX_SYMON GENMASK( 7, 4)
+#define CKSYMTXCTRL_CK_SYMON GENMASK( 3, 0)
+
+#define VLEVCTRL_PLLMEASCTRL_MPLL_MEAS_17_14 GENMASK(13, 10)
+#define VLEVCTRL_PLLMEASCTRL_SUP_TX_LVL GENMASK( 9, 5)
+
+#define PLLCURRGMPCTRL_MPLL_GMP_CNTRL GENMASK(13, 12)
+#define PLLCURRGMPCTRL_MPLL_PROP_CNTRL GENMASK(11, 6)
+#define PLLCURRGMPCTRL_MPLL_INT_CNTRL GENMASK( 5, 0)
+
+#define PLLDIVCTRL_VCO_CNTRL GENMASK(12, 9)
+#define PLLDIVCTRL_MPLL_MULTIPLIER GENMASK( 7, 0)
+
+#define TXTERM_D_TX_TERM GENMASK( 2, 0)
+
+#define TXTERM_50_OHM 0x0
+#define TXTERM_57_14_OHM 0x1
+#define TXTERM_66_67_OHM 0x2
+#define TXTERM_80_OHM 0x3
+#define TXTERM_100_OHM 0x4
+#define TXTERM_133_33_OHM 0x5
+#define TXTERM_200_OHM 0x6
+#define TXTERM_OPEN_CIRCUIT 0x7
+
+#define OPMODE_HDMI_14 0
+#define OPMODE_HDMI_20 1
+
+struct dw_hdmi_mpll_gen_config{
+ u64 pixelclock; /* in kHz */
+ u8 colordepth;
+ u16 opmode:2;
+ struct {
+ u16 prep_div:2;
+ u16 mpll_cko_div:2;
+ u16 ref_cntrl:2;
+ u16 mpll_n_cntrl:2;
+ u16 vco_cntrl:4;
+ u16 mpll_multiplier:8;
+ } divider;
+ struct {
+ u16 gmp_cntrl:2;
+ u16 prop_cntrl:6;
+ u16 int_cntrl:6;
+ } charge_pump;
+ struct {
+ u16 txterm:3;
+ u16 sup_txlvl:5;
+ u16 tx_traon:1;
+ u16 tx_trbon:1;
+ u16 tx_symon:4;
+ u16 ck_symon:4;
+ } voltage;
+};
+
+struct dw_hdmi_light_private {
+ u32 max_pixclock;
+ u16 max_width;
+ u16 max_height;
+};
+
+static const struct dw_hdmi_mpll_gen_config mpll_configs[] = {
+ {
+ .pixelclock = 25175,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 27000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 31500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 33750,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 35500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 36000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 40000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 44900,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+ .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 49500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 50000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 50350,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 54000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 56250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 59400,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 65000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 68250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 71000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 72000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 73250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 74250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 75000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 78750,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 79500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 82500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 83500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 85500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 88750,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 90000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+ .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 94500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 99000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 100700,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 101000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 102250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 106500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 108000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 115500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 117500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 118800,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 119000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 121750,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 148500,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 154000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 162000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 165000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+ .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 185625,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x3, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0x9, .tx_traon = 0x1,
+ .tx_trbon = 0x1, .tx_symon = 0xc, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 198000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x3, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0x9, .tx_traon = 0x1,
+ .tx_trbon = 0x1, .tx_symon = 0xc, .ck_symon = 0x8, },
+ }, {
+ .pixelclock = 297000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_14,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x1, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x1, },
+ .voltage = { .txterm = TXTERM_133_33_OHM, .sup_txlvl = 0x10, .tx_traon = 0x0,
+ .tx_trbon = 0x1, .tx_symon = 0xd, .ck_symon = 0xc, },
+ }, {
+ .pixelclock = 371250,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_20,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x1, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x1, },
+ .voltage = { .txterm = TXTERM_50_OHM, .sup_txlvl = 0xa, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0x6, },
+ }, {
+ .pixelclock = 495000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_20,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x0, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_50_OHM, .sup_txlvl = 0xa, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0x6, },
+ }, {
+ .pixelclock = 594000,
+ .colordepth = 8,
+ .opmode = OPMODE_HDMI_20,
+ .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+ .mpll_n_cntrl = 0x0, .vco_cntrl = 0x0, .mpll_multiplier = 0x5, },
+ .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+ .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0xf, .tx_traon = 0x0,
+ .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0xa, },
+ },
+};
+
+static enum drm_mode_status
+dw_hdmi_tx_phy_gen2_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ int i;
+
+ u32 max_clock = 594000;
+ u16 max_height = 0;
+ u16 max_width = 0;
+
+ struct dw_hdmi_light_private *dw_hdmi_private = (struct dw_hdmi_light_private*)data;
+ if(dw_hdmi_private != NULL){
+ max_clock = dw_hdmi_private->max_pixclock;
+ max_height = dw_hdmi_private->max_height;
+ max_width = dw_hdmi_private->max_width;
+ }
+
+ if (mode->clock < 13500)
+ {
+ return MODE_CLOCK_LOW;
+ }
+ else if (mode->clock > max_clock)
+ {
+ return MODE_CLOCK_HIGH;
+ }
+
+ if((max_width > 0) && (mode->hdisplay > max_width)){
+ return MODE_H_ILLEGAL;
+ }
+
+ if((max_height > 0) && (mode->vdisplay > max_height)){
+ return MODE_V_ILLEGAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
+ if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
+ return MODE_OK;
+ else if (mode->clock < mpll_configs[i].pixelclock)
+ break;
+ }
+
+ return MODE_NOMODE;
+}
+
+static int dw_hdmi_tx_phy_gen2_configure(struct dw_hdmi *hdmi, void *data,
+ unsigned long mpixelclock)
+{
+ int i;
+ u16 opmode_pllcfg, pllcurrgmpctrl, plldivctrl;
+ u16 txterm, vlevctrl_pllmeasctrl, cksymtxctrl;
+ u64 pixclk = mpixelclock / 1000;
+ const struct dw_hdmi_mpll_gen_config *config;
+
+ for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
+ config = &mpll_configs[i];
+ /* TODO: add colordepth check later */
+ if (abs(config->pixelclock - pixclk) <= 100)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mpll_configs))
+ return -1;
+
+ opmode_pllcfg = FIELD_PREP(OPMODE_PLLCFG_PREP_DIV, config->divider.prep_div) |
+ FIELD_PREP(OPMODE_PLLCFG_MPLL_CKO_DIV, config->divider.mpll_cko_div) |
+ FIELD_PREP(OPMODE_PLLCFG_OPMODE, config->opmode) |
+ FIELD_PREP(OPMODE_PLLCFG_REF_CNTRL, config->divider.ref_cntrl) |
+ FIELD_PREP(OPMODE_PLLCFG_MPLL_N_CNTRL, config->divider.mpll_n_cntrl);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ opmode_pllcfg,
+ DW_HDMI_TX_PHY_GEN2_OPMODE_PLLCFG
+ );
+
+ pllcurrgmpctrl = FIELD_PREP(PLLCURRGMPCTRL_MPLL_GMP_CNTRL, config->charge_pump.gmp_cntrl) |
+ FIELD_PREP(PLLCURRGMPCTRL_MPLL_PROP_CNTRL, config->charge_pump.prop_cntrl) |
+ FIELD_PREP(PLLCURRGMPCTRL_MPLL_INT_CNTRL, config->charge_pump.int_cntrl);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ pllcurrgmpctrl,
+ DW_HDMI_TX_PHY_GEN2_PLLCURRGMPCTRL
+ );
+
+ plldivctrl = FIELD_PREP(PLLDIVCTRL_VCO_CNTRL, config->divider.vco_cntrl) |
+ FIELD_PREP(PLLDIVCTRL_MPLL_MULTIPLIER, config->divider.mpll_multiplier);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ plldivctrl,
+ DW_HDMI_TX_PHY_GEN2_PLLDIVCTRL
+ );
+
+ txterm = FIELD_PREP(TXTERM_D_TX_TERM, config->voltage.txterm);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ txterm,
+ DW_HDMI_TX_PHY_GEN2_TXTERM
+ );
+
+ vlevctrl_pllmeasctrl = FIELD_PREP(VLEVCTRL_PLLMEASCTRL_SUP_TX_LVL, config->voltage.sup_txlvl);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ vlevctrl_pllmeasctrl,
+ DW_HDMI_TX_PHY_GEN2_VLEVCTRL_PLLMEASCTRL
+ );
+
+ cksymtxctrl = FIELD_PREP(CKSYMTXCTRL_OVERRIDE_5, 0x1) |
+ FIELD_PREP(CKSYMTXCTRL_TX_TRAON, config->voltage.tx_traon) |
+ FIELD_PREP(CKSYMTXCTRL_TX_TRBON, config->voltage.tx_trbon) |
+ FIELD_PREP(CKSYMTXCTRL_TX_SYMON, config->voltage.tx_symon) |
+ FIELD_PREP(CKSYMTXCTRL_CK_SYMON, config->voltage.ck_symon);
+ dw_hdmi_phy_i2c_write(
+ hdmi,
+ cksymtxctrl,
+ DW_HDMI_TX_PHY_GEN2_CKSYMTXCTRL
+ );
+
+ return 0;
+}
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ *
+ * refer to bridge/synopsys/dw-mipi-dsi.c
+ */
+
+#include <linux/version.h>
+#include <linux/bitfield.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_mipi_dsi.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_print.h>
+#else
+#include <drm/drmP.h>
+#endif
+
+#include "dw_mipi_dsi.h"
+
+#define DSI_VERSION 0x00
+
+#define DSI_PWR_UP 0x04
+#define RESET 0
+#define POWERUP BIT(0)
+
+#define DSI_CLKMGR_CFG 0x08
+#define TO_CLK_DIVISION(div) (((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVISION(div) ((div) & 0xff)
+
+#define DSI_DPI_VCID 0x0c
+#define DPI_VCID(vcid) ((vcid) & 0x3)
+
+#define DSI_DPI_COLOR_CODING 0x10
+#define LOOSELY18_EN BIT(8)
+#define DPI_COLOR_CODING_16BIT_1 0x0
+#define DPI_COLOR_CODING_16BIT_2 0x1
+#define DPI_COLOR_CODING_16BIT_3 0x2
+#define DPI_COLOR_CODING_18BIT_1 0x3
+#define DPI_COLOR_CODING_18BIT_2 0x4
+#define DPI_COLOR_CODING_24BIT 0x5
+
+#define DSI_DPI_CFG_POL 0x14
+#define COLORM_ACTIVE_LOW BIT(4)
+#define SHUTD_ACTIVE_LOW BIT(3)
+#define HSYNC_ACTIVE_LOW BIT(2)
+#define VSYNC_ACTIVE_LOW BIT(1)
+#define DATAEN_ACTIVE_LOW BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM 0x18
+#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p) ((p) & 0xff)
+
+#define DSI_DBI_VCID 0x1c
+#define DSI_DBI_CFG 0x20
+#define DSI_DBI_PARTITIONING_EN 0x24
+#define DSI_DBI_CMDSIZE 0x28
+
+#define DSI_PCKHDL_CFG 0x2c
+#define EOTP_TX_LP_EN BIT(5)
+#define CRC_RX_EN BIT(4)
+#define ECC_RX_EN BIT(3)
+#define BTA_EN BIT(2)
+#define EOTP_RX_EN BIT(1)
+#define EOTP_TX_EN BIT(0)
+
+#define DSI_GEN_VCID 0x30
+
+#define DSI_MODE_CFG 0x34
+#define ENABLE_VIDEO_MODE 0
+#define ENABLE_CMD_MODE BIT(0)
+
+#define DSI_VID_MODE_CFG 0x38
+#define VPG_ORIENTATION BIT(24)
+#define VPG_MODE BIT(20)
+#define VPG_ENABLE BIT(16)
+#define LP_CMD_EN BIT(15)
+#define FRAME_BTA_ACK_EN BIT(14)
+#define LP_HFP_EN BIT(13)
+#define LP_HBP_EN BIT(12)
+#define LP_VACT_EN BIT(11)
+#define LP_VFP_EN BIT(10)
+#define LP_VBP_EN BIT(9)
+#define LP_VSA_EN BIT(8)
+#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES 0x0
+#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS 0x1
+#define VID_MODE_TYPE_BURST 0x2
+#define VID_MODE_TYPE_MASK 0x3
+
+#define DSI_VID_PKT_SIZE 0x3c
+#define VID_PKT_SIZE(p) ((p) & 0x3fff)
+
+#define DSI_VID_NUM_CHUNKS 0x40
+#define VID_NUM_CHUNKS(c) ((c) & 0x1fff)
+
+#define DSI_VID_NULL_SIZE 0x44
+#define VID_NULL_SIZE(b) ((b) & 0x1fff)
+
+#define DSI_VID_HSA_TIME 0x48
+#define DSI_VID_HBP_TIME 0x4c
+#define DSI_VID_HLINE_TIME 0x50
+#define DSI_VID_VSA_LINES 0x54
+#define DSI_VID_VBP_LINES 0x58
+#define DSI_VID_VFP_LINES 0x5c
+#define DSI_VID_VACTIVE_LINES 0x60
+#define DSI_EDPI_CMD_SIZE 0x64
+
+#define DSI_CMD_MODE_CFG 0x68
+#define MAX_RD_PKT_SIZE_LP BIT(24)
+#define DCS_LW_TX_LP BIT(19)
+#define DCS_SR_0P_TX_LP BIT(18)
+#define DCS_SW_1P_TX_LP BIT(17)
+#define DCS_SW_0P_TX_LP BIT(16)
+#define GEN_LW_TX_LP BIT(14)
+#define GEN_SR_2P_TX_LP BIT(13)
+#define GEN_SR_1P_TX_LP BIT(12)
+#define GEN_SR_0P_TX_LP BIT(11)
+#define GEN_SW_2P_TX_LP BIT(10)
+#define GEN_SW_1P_TX_LP BIT(9)
+#define GEN_SW_0P_TX_LP BIT(8)
+#define ACK_RQST_EN BIT(1)
+#define TEAR_FX_EN BIT(0)
+
+#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \
+ DCS_LW_TX_LP | \
+ DCS_SR_0P_TX_LP | \
+ DCS_SW_1P_TX_LP | \
+ DCS_SW_0P_TX_LP | \
+ GEN_LW_TX_LP | \
+ GEN_SR_2P_TX_LP | \
+ GEN_SR_1P_TX_LP | \
+ GEN_SR_0P_TX_LP | \
+ GEN_SW_2P_TX_LP | \
+ GEN_SW_1P_TX_LP | \
+ GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR 0x6c
+#define DSI_GEN_PLD_DATA 0x70
+
+#define DSI_CMD_PKT_STATUS 0x74
+#define GEN_RD_CMD_BUSY BIT(6)
+#define GEN_PLD_R_FULL BIT(5)
+#define GEN_PLD_R_EMPTY BIT(4)
+#define GEN_PLD_W_FULL BIT(3)
+#define GEN_PLD_W_EMPTY BIT(2)
+#define GEN_CMD_FULL BIT(1)
+#define GEN_CMD_EMPTY BIT(0)
+
+#define DSI_TO_CNT_CFG 0x78
+#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p) ((p) & 0xffff)
+
+#define DSI_HS_RD_TO_CNT 0x7c
+#define DSI_LP_RD_TO_CNT 0x80
+#define DSI_HS_WR_TO_CNT 0x84
+#define DSI_LP_WR_TO_CNT 0x88
+#define DSI_BTA_TO_CNT 0x8c
+
+#define DSI_LPCLK_CTRL 0x94
+#define AUTO_CLKLANE_CTRL BIT(1)
+#define PHY_TXREQUESTCLKHS BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG 0x98
+#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG 0x9c
+#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
+#define PHY_LP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
+
+#define DSI_PHY_RSTZ 0xa0
+#define PHY_DISFORCEPLL 0
+#define PHY_ENFORCEPLL BIT(3)
+#define PHY_DISABLECLK 0
+#define PHY_ENABLECLK BIT(2)
+#define PHY_RSTZ 0
+#define PHY_UNRSTZ BIT(1)
+#define PHY_SHUTDOWNZ 0
+#define PHY_UNSHUTDOWNZ BIT(0)
+
+#define DSI_PHY_IF_CFG 0xa4
+#define PHY_STOP_WAIT_TIME(cycle) (((cycle) & 0xff) << 8)
+#define N_LANES(n) (((n) - 1) & 0x3)
+
+#define DSI_PHY_ULPS_CTRL 0xa8
+#define DSI_PHY_TX_TRIGGERS 0xac
+
+#define DSI_PHY_STATUS 0xb0
+#define PHY_STOP_STATE_CLK_LANE BIT(2)
+#define PHY_LOCK BIT(0)
+
+#define DSI_INT_ST0 0xbc
+#define DSI_INT_ST1 0xc0
+#define DSI_INT_MSK0 0xc4
+#define DSI_INT_MSK1 0xc8
+
+#define DSI_PHY_TMR_RD_CFG 0xf4
+#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff)
+
+#define PHY_STATUS_TIMEOUT_US 10000
+#define CMD_PKT_STATUS_TIMEOUT_US 20000
+
+#define MAX_LANE_COUNT 4
+
+struct dw_mipi_dsi;
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi);
+
+struct dw_mipi_dsi_funcs {
+ struct dw_mipi_dsi *(*get_dsi)(struct device *dev);
+ int (*bind)(struct dw_mipi_dsi *dsi);
+ void (*unbind)(struct dw_mipi_dsi *dsi);
+};
+
+struct dw_mipi_dsi {
+ struct device *dev;
+ struct regmap *regmap;
+ /* apb slave bus clock */
+ struct clk *pclk;
+ struct clk *pixclk;
+ struct phy *dphy;
+ union phy_configure_opts phy_opts;
+
+ struct mipi_dsi_host host;
+ unsigned int channel;
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+
+ unsigned int lane_link_rate; /* kHz */
+
+ const struct dw_mipi_dsi_funcs *funcs;
+};
+
+struct dw_mipi_dsi_primary {
+ struct dw_mipi_dsi dsi;
+ struct dw_mipi_dsi *secondary_dsi;
+
+ struct drm_bridge bridge;
+ struct drm_bridge *panel_bridge;
+ u32 bus_format;
+};
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+ return container_of(host, struct dw_mipi_dsi, host);
+}
+
+static inline struct
+dw_mipi_dsi_primary *dsi_to_primary(struct dw_mipi_dsi *dsi)
+{
+ return container_of(dsi, struct dw_mipi_dsi_primary, dsi);
+}
+
+static inline struct
+dw_mipi_dsi_primary *bridge_to_primary(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct dw_mipi_dsi_primary, bridge);
+}
+
+static inline int dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+ int ret;
+
+ WARN_ON(ret = regmap_write(dsi->regmap, reg, val));
+
+ return ret;
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg, u32 *val)
+{
+ int ret;
+
+ WARN_ON(ret = regmap_read(dsi->regmap, reg, val));
+
+ return ret;
+}
+
+static int dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+ if (device->lanes > MAX_LANE_COUNT) {
+ DRM_ERROR("the number of data lanes(%u) is too many\n",
+ device->lanes);
+ return -EINVAL;
+ }
+
+ if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST))
+ DRM_WARN("This DSI driver only support burst mode\n");
+
+ dsi->lanes = device->lanes;
+ dsi->channel = device->channel;
+ dsi->format = device->format;
+ dsi->mode_flags = device->mode_flags;
+
+ return 0;
+}
+
+static int dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ return 0;
+}
+
+static void dw_mipi_dsi_lpclk_config(struct dw_mipi_dsi *dsi, bool hsclk)
+{
+ u32 lpclk_ctrl = 0;
+
+ lpclk_ctrl |= hsclk ? PHY_TXREQUESTCLKHS : 0;
+ lpclk_ctrl |= dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS ?
+ AUTO_CLKLANE_CTRL : 0;
+
+ dsi_write(dsi, DSI_LPCLK_CTRL, lpclk_ctrl);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+ unsigned long mode_flags)
+{
+ u32 mode_cfg;
+
+ mode_cfg = mode_flags & MIPI_DSI_MODE_VIDEO ?
+ ENABLE_VIDEO_MODE : ENABLE_CMD_MODE;
+ dsi_write(dsi, DSI_MODE_CFG, mode_cfg);
+}
+
+static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
+ u32 val = 0;
+
+ if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
+ val |= ACK_RQST_EN;
+ if (lpm)
+ val |= CMD_MODE_ALL_LP | MAX_RD_PKT_SIZE_LP;
+
+ dsi_write(dsi, DSI_CMD_MODE_CFG, val);
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
+{
+ int ret;
+ u32 val, mask;
+
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_CMD_FULL), 1000,
+ CMD_PKT_STATUS_TIMEOUT_US);
+
+ if (ret) {
+ DRM_ERROR("failed to get available command FIFO\n");
+ return ret;
+ }
+
+ dsi_write(dsi, DSI_GEN_HDR, hdr_val);
+
+ mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+ val, (val & mask) == mask,
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ DRM_ERROR("failed to write command FIFO\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_packet *packet)
+{
+ const u8 *tx_buf = packet->payload;
+ int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
+ __le32 word;
+ u32 val;
+
+ while (len) {
+ if (len < pld_data_bytes) {
+ word = 0;
+ memcpy(&word, tx_buf, len);
+ dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+ len = 0;
+ } else {
+ memcpy(&word, tx_buf, pld_data_bytes);
+ dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+ tx_buf += pld_data_bytes;
+ len -= pld_data_bytes;
+ }
+
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_PLD_W_FULL), 1000,
+ CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ DRM_ERROR("failed to get write payload FIFO\n");
+ return ret;
+ }
+ }
+
+ word = 0;
+ memcpy(&word, packet->header, sizeof(packet->header));
+ return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
+}
+
+static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ int i, j, ret, len = msg->rx_len;
+ u8 *buf = msg->rx_buf;
+ u32 val;
+
+ /* Wait end of the read operation */
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_RD_CMD_BUSY),
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ DRM_ERROR("Timeout during read operation\n");
+ return ret;
+ }
+
+ for (i = 0; i < len; i += 4) {
+ /* Read fifo must not be empty before all bytes are read */
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_PLD_R_EMPTY),
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ DRM_ERROR("Read payload FIFO is empty\n");
+ return ret;
+ }
+
+ ret = dsi_read(dsi, DSI_GEN_PLD_DATA, &val);
+ if (unlikely(ret))
+ return ret;
+
+ for (j = 0; j < 4 && j + i < len; j++)
+ buf[i + j] = val >> (8 * j);
+ }
+
+ return ret;
+}
+
+static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct dw_mipi_dsi *dsi = host_to_dsi(host);
+ struct mipi_dsi_packet packet;
+ int ret, nb_bytes;
+
+ ret = mipi_dsi_create_packet(&packet, msg);
+ if (ret) {
+ DRM_ERROR("failed to create packet: %d\n", ret);
+ return ret;
+ }
+
+ dw_mipi_message_config(dsi, msg);
+
+ ret = dw_mipi_dsi_write(dsi, &packet);
+ if (ret)
+ return ret;
+
+ if (msg->rx_buf && msg->rx_len) {
+ ret = dw_mipi_dsi_read(dsi, msg);
+ if (ret)
+ return ret;
+ nb_bytes = msg->rx_len;
+ } else {
+ nb_bytes = packet.size;
+ }
+
+ return nb_bytes;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+ .attach = dsi_host_attach,
+ .detach = dsi_host_detach,
+ .transfer = dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_power_config(struct dw_mipi_dsi *dsi, bool on)
+{
+ dsi_write(dsi, DSI_PWR_UP, on ? POWERUP : RESET);
+}
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+ u32 vid_mode_cfg = 0;
+
+ /* configure low-power transitions */
+ vid_mode_cfg |= LP_HFP_EN |
+ LP_HBP_EN |
+ LP_VACT_EN |
+ LP_VFP_EN |
+ LP_VBP_EN |
+ LP_VSA_EN;
+
+ /* configure commands transfer in high speed mode */
+ vid_mode_cfg &= ~LP_CMD_EN;
+
+ /* configure burs-mode or non-burst mode */
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ vid_mode_cfg |= VID_MODE_TYPE_BURST;
+ else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ vid_mode_cfg |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+ else
+ vid_mode_cfg |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
+
+ #if 0
+ /* enable colorbar mode */
+ vid_mode_cfg |= VPG_ENABLE;
+ #endif
+
+ dsi_write(dsi, DSI_VID_MODE_CFG, vid_mode_cfg);
+}
+
+static void dw_mipi_dsi_set_esc_div(struct dw_mipi_dsi *dsi)
+{
+ u8 esc_clk;
+ u32 esc_clk_division;
+
+ esc_clk = 20; /* 20MHz */
+
+ /*
+ * The maximum permitted escape clock is 20MHz and it is derived from
+ * lanebyteclk, which is running at "lane_link_rate / 8". Thus we want:
+ *
+ * (lane_link_rate >> 3) / esc_clk_division < 20
+ * which is:
+ * (lane_link_rate >> 3) / 20 > esc_clk_division
+ */
+ esc_clk_division = ((dsi->lane_link_rate / 1000) >> 3) / esc_clk + 1;
+
+ /*
+ * TODO dw drv improvements
+ * timeout clock division should be computed with the
+ * high speed transmission counter timeout and byte lane...
+ */
+ dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
+ TX_ESC_CLK_DIVISION(esc_clk_division));
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+ const struct drm_display_mode *mode)
+{
+ u32 dpi_cfg_pol = 0, dpi_color_coding = 0;
+
+ dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ dpi_color_coding = DPI_COLOR_CODING_24BIT;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ dpi_color_coding = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ dpi_color_coding = DPI_COLOR_CODING_18BIT_1;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ dpi_color_coding = DPI_COLOR_CODING_16BIT_1;
+ break;
+ }
+ dsi_write(dsi, DSI_DPI_COLOR_CODING, dpi_color_coding);
+
+ /* TODO: negotiate bus flags for DE polarity */
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ dpi_cfg_pol |= VSYNC_ACTIVE_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ dpi_cfg_pol |= HSYNC_ACTIVE_LOW;
+ dsi_write(dsi, DSI_DPI_CFG_POL, dpi_cfg_pol);
+
+ /*
+ * TODO dw drv improvements
+ * largest packet sizes during hfp or during vsa/vpb/vfp
+ * should be computed according to byte lane, lane number and only
+ * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
+ */
+ dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+ | INVACT_LPCMD_TIME(4));
+}
+
+static void __maybe_unused dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+ u32 pkthdl_cfg;
+
+ pkthdl_cfg = EOTP_TX_LP_EN | EOTP_TX_EN | BTA_EN;
+ dsi_write(dsi, DSI_PCKHDL_CFG, pkthdl_cfg);
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+ u32 cmd_mode_cfg;
+
+ /*
+ * TODO dw drv improvements
+ * compute high speed transmission counter timeout according
+ * to the timeout clock division (TO_CLK_DIVISION) and byte lane...
+ */
+ dsi_write(dsi, DSI_TO_CNT_CFG,
+ HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+ /*
+ * TODO dw drv improvements
+ * the Bus-Turn-Around Timeout Counter should be computed
+ * according to byte lane...
+ */
+ dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+
+ /* TODO use low power mode to transfer
+ * commands in command mode in default
+ */
+ cmd_mode_cfg = MAX_RD_PKT_SIZE_LP |
+ DCS_LW_TX_LP |
+ DCS_SR_0P_TX_LP |
+ DCS_SW_1P_TX_LP |
+ DCS_SW_0P_TX_LP |
+ GEN_LW_TX_LP |
+ GEN_SR_2P_TX_LP |
+ GEN_SR_1P_TX_LP |
+ GEN_SR_0P_TX_LP |
+ GEN_SW_2P_TX_LP |
+ GEN_SW_1P_TX_LP |
+ GEN_SW_0P_TX_LP;
+ dsi_write(dsi, DSI_CMD_MODE_CFG, cmd_mode_cfg);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+ const struct drm_display_mode *mode,
+ u32 hcomponent)
+{
+ u32 frac, lbcc;
+
+ lbcc = hcomponent * dsi->lane_link_rate / 8;
+
+ frac = lbcc % mode->clock;
+ lbcc = lbcc / mode->clock;
+ if (frac)
+ lbcc++;
+
+ return lbcc;
+}
+
+static void dw_mipi_dsi_timing_config(struct dw_mipi_dsi *dsi,
+ const struct drm_display_mode *mode)
+{
+ u32 htotal, hsa, hbp, lbcc;
+
+ htotal = mode->htotal;
+ hsa = mode->hsync_end - mode->hsync_start;
+ hbp = mode->htotal - mode->hsync_end;
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, htotal);
+ dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hsa);
+ dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hbp);
+ dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+
+ dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+
+ /* vertical */
+ dsi_write(dsi, DSI_VID_VACTIVE_LINES, mode->vdisplay);
+ dsi_write(dsi, DSI_VID_VSA_LINES, mode->vsync_end - mode->vsync_start);
+ dsi_write(dsi, DSI_VID_VFP_LINES, mode->vsync_start - mode->vdisplay);
+ dsi_write(dsi, DSI_VID_VBP_LINES, mode->vtotal - mode->vsync_end);
+}
+
+static void __maybe_unused dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+ /*
+ * TODO dw drv improvements
+ * stop wait time should be the maximum between host dsi
+ * and panel stop wait times
+ */
+ dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+ N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
+{
+ u32 val;
+ int ret;
+
+ phy_power_on(dsi->dphy);
+
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS, val,
+ val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+ if (ret)
+ DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
+
+ ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS,
+ val, val & PHY_STOP_STATE_CLK_LANE, 1000,
+ PHY_STATUS_TIMEOUT_US);
+ if (ret)
+ DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void __maybe_unused dw_mipi_dsi_wait_for_two_frames(const struct drm_display_mode *mode)
+{
+ int refresh, two_frames;
+
+ refresh = drm_mode_vrefresh(mode);
+ two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+ msleep(two_frames);
+}
+
+static void dw_mipi_dsi_dphy_init(struct dw_mipi_dsi *dsi)
+{
+ regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_UNRSTZ, PHY_RSTZ);
+ regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_UNSHUTDOWNZ, PHY_SHUTDOWNZ);
+
+ phy_init(dsi->dphy);
+ phy_configure(dsi->dphy, &dsi->phy_opts);
+ dw_mipi_dsi_set_esc_div(dsi);
+}
+
+static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
+ const struct drm_display_mode *mode)
+{
+ pm_runtime_get_sync(dsi->dev);
+
+ /* configure DSI hosts */
+ dw_mipi_dsi_dpi_config(dsi, mode);
+ dw_mipi_dsi_video_mode_config(dsi);
+ dw_mipi_dsi_timing_config(dsi, mode);
+ dw_mipi_dsi_packet_handler_config(dsi);
+ dw_mipi_dsi_command_mode_config(dsi);
+
+ dw_mipi_dsi_dphy_init(dsi);
+ dw_mipi_dsi_wait_for_two_frames(mode);
+
+ /* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
+ dw_mipi_dsi_set_mode(dsi, 0);
+
+ dw_mipi_dsi_power_config(dsi, true);
+
+ pm_runtime_put(dsi->dev);
+}
+
+static enum drm_mode_status bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ int bpp, ret;
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+ struct dw_mipi_dsi *dsi = &primary->dsi;
+ struct phy_configure_opts_mipi_dphy *dphy_cfg = &dsi->phy_opts.mipi_dphy;
+
+ bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+ if (bpp < 0)
+ return MODE_BAD;
+
+ phy_mipi_dphy_get_default_config(mode->clock * 1000, bpp,
+ dsi->lanes, dphy_cfg);
+
+ ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &dsi->phy_opts);
+ if (!ret)
+ dsi->lane_link_rate = dphy_cfg->hs_clk_rate / 1000;
+
+ return ret ? MODE_BAD : MODE_OK;
+}
+
+static void bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+ struct drm_display_mode *new_mode =
+ drm_mode_duplicate(bridge->dev, adjusted_mode);
+
+ if (primary->secondary_dsi) {
+ new_mode->hdisplay /= 2;
+ new_mode->hsync_start /= 2;
+ new_mode->hsync_end /= 2;
+ new_mode->htotal /= 2;
+ new_mode->clock /= 2;
+ }
+
+ dw_mipi_dsi_mode_set(&primary->dsi, new_mode);
+ if (primary->secondary_dsi)
+ dw_mipi_dsi_mode_set(primary->secondary_dsi, new_mode);
+
+ drm_mode_destroy(bridge->dev, new_mode);
+}
+
+static void bridge_enable(struct drm_bridge *bridge)
+{
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+ struct dw_mipi_dsi *dsi = &primary->dsi;
+
+#if 0
+ if (primary->secondary_dsi)
+ dw_mipi_dsi_mode_config(primary->secondary_dsi,
+ MIPI_DSI_MODE_VIDEO);
+#endif
+
+ pm_runtime_get_sync(dsi->dev);
+
+ dw_mipi_dsi_dphy_enable(dsi);
+ dw_mipi_dsi_power_config(dsi, true);
+ dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
+ dw_mipi_dsi_lpclk_config(dsi, true);
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+ dw_mipi_dsi_power_config(dsi, false);
+ phy_power_off(dsi->dphy);
+
+ pm_runtime_put(dsi->dev);
+}
+
+static void bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+
+ /*
+ * Switch to command mode before panel-bridge post_disable &
+ * panel unprepare.
+ * Note: panel-bridge disable & panel disable has been called
+ * before by the drm framework.
+ */
+ dw_mipi_dsi_set_mode(&primary->dsi, 0);
+
+ /*
+ * TODO Only way found to call panel-bridge post_disable &
+ * panel unprepare before the dsi "final" disable...
+ * This needs to be fixed in the drm_bridge framework and the API
+ * needs to be updated to manage our own call chains...
+ */
+ primary->panel_bridge->funcs->post_disable(primary->panel_bridge);
+
+ if (primary->secondary_dsi)
+ dw_mipi_dsi_disable(primary->secondary_dsi);
+
+ dw_mipi_dsi_disable(&primary->dsi);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+static int bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+#else
+static int bridge_attach(struct drm_bridge *bridge)
+#endif
+{
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found\n");
+ return -ENODEV;
+ }
+
+ /* Attach the panel-bridge to the dsi bridge */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+ return drm_bridge_attach(bridge->encoder, primary->panel_bridge,
+ bridge, 0);
+#else
+ return drm_bridge_attach(bridge->encoder, primary->panel_bridge,
+ bridge);
+#endif
+}
+
+static void bridge_update_bus_format(struct drm_bridge *bridge)
+{
+ struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct drm_encoder *encoder;
+ u32 bus_format;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+ int i;
+#endif
+
+ /* set bus format according to DSI format */
+ switch (primary->dsi.format) {
+ case MIPI_DSI_FMT_RGB888:
+ bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ bus_format = MEDIA_BUS_FMT_RGB565_1X16;
+ break;
+ default:
+ bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ }
+
+ if (bus_format == primary->bus_format)
+ return;
+
+ drm_connector_list_iter_begin(bridge->dev, &conn_iter);
+
+ drm_for_each_connector_iter(connector, &conn_iter) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+ drm_connector_for_each_possible_encoder(connector, encoder, i) {
+#else
+ drm_connector_for_each_possible_encoder(connector, encoder) {
+#endif
+ if (encoder == bridge->encoder) {
+ drm_display_info_set_bus_formats(
+ &connector->display_info,
+ &bus_format, 1);
+ primary->bus_format = bus_format;
+ drm_connector_list_iter_end(&conn_iter);
+ return;
+ }
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+#if 1
+static bool bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ bridge_update_bus_format(bridge);
+ return true;
+}
+#else
+static int bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return 0;
+}
+#endif
+
+static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
+ .mode_valid = bridge_mode_valid,
+ .mode_set = bridge_mode_set,
+ .enable = bridge_enable,
+ .post_disable = bridge_post_disable,
+ .attach = bridge_attach,
+#if 1
+ .mode_fixup = bridge_mode_fixup,
+#else
+ .atomic_check = bridge_atomic_check,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = bridge_atomic_get_input_bus_fmts,
+#endif
+};
+
+static int dsi_attach_primary(struct dw_mipi_dsi *secondary, struct device *dev)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+ struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+
+ if (!of_device_is_compatible(dev->of_node, "verisilicon,dw-mipi-dsi0"))
+ return -EINVAL;
+
+ primary->secondary_dsi = secondary;
+ return 0;
+}
+
+struct dw_mipi_dsi *get_primary_dsi(struct device *dev)
+{
+ struct dw_mipi_dsi_primary *primary;
+
+ primary = devm_kzalloc(dev, sizeof(*primary), GFP_KERNEL);
+ if (!primary)
+ return NULL;
+
+ primary->dsi.dev = dev;
+
+ return &primary->dsi;
+}
+
+static int primary_bind(struct dw_mipi_dsi *dsi)
+{
+ struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+ struct device *dev = primary->dsi.dev;
+ struct drm_bridge *bridge;
+ struct drm_panel *panel;
+ int ret;
+
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
+ if (ret)
+ return ret;
+
+ if (panel) {
+ bridge = drm_panel_bridge_add(panel);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+
+ primary->panel_bridge = bridge;
+
+ primary->bridge.funcs = &dw_mipi_dsi_bridge_funcs;
+ primary->bridge.of_node = dev->of_node;
+ drm_bridge_add(&primary->bridge);
+ return 0;
+}
+
+static void primary_unbind(struct dw_mipi_dsi *dsi)
+{
+ struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+ struct device *dev = primary->dsi.dev;
+
+ drm_bridge_remove(&primary->bridge);
+ drm_of_panel_bridge_remove(dev->of_node, 1, 0);
+}
+
+static const struct dw_mipi_dsi_funcs primary = {
+ .get_dsi = &get_primary_dsi,
+ .bind = &primary_bind,
+ .unbind = &primary_unbind,
+};
+
+struct dw_mipi_dsi *get_secondary_dsi(struct device *dev)
+{
+ struct dw_mipi_dsi *dsi;
+
+ dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return NULL;
+
+ dsi->dev = dev;
+ return dsi;
+}
+
+static int secondary_bind(struct dw_mipi_dsi *dsi)
+{
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_find_compatible_node(NULL, NULL, "verisilicon,dw-mipi-dsi0");
+ if (!np)
+ return -ENODEV;
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev)
+ return -ENODEV;
+
+ return dsi_attach_primary(dsi, &pdev->dev);
+}
+
+static void secondary_unbind(struct dw_mipi_dsi *dsi)
+{
+
+}
+
+static const struct dw_mipi_dsi_funcs secondary = {
+ .get_dsi = &get_secondary_dsi,
+ .bind = &secondary_bind,
+ .unbind = &secondary_unbind,
+};
+
+static int dsi_bind(struct device *dev, struct device *primary, void *data)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ return dsi->funcs->bind(dsi);
+}
+
+static void dsi_unbind(struct device *dev, struct device *primary, void *data)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ dsi->funcs->unbind(dsi);
+}
+
+static const struct component_ops dsi_component_ops = {
+ .bind = dsi_bind,
+ .unbind = dsi_unbind,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_match[] = {
+ { .compatible = "verisilicon,dw-mipi-dsi", .data = &primary},
+ { .compatible = "verisilicon,dw-mipi-dsi-2nd", .data = &secondary},
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_match);
+
+static int dsi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct dw_mipi_dsi_funcs *funcs;
+ struct dw_mipi_dsi *dsi;
+ int ret;
+
+ funcs = of_device_get_match_data(dev);
+ dsi = funcs->get_dsi(dev);
+ if (!dsi)
+ return -ENOMEM;
+
+ dsi->regmap = syscon_regmap_lookup_by_phandle(np, "regmap");
+ if (IS_ERR(dsi->regmap))
+ return PTR_ERR(dsi->regmap);
+
+ dsi->pclk = devm_clk_get_optional(dev, "pclk");
+ if (IS_ERR(dsi->pclk))
+ return PTR_ERR(dsi->pclk);
+
+ dsi->pixclk = devm_clk_get_optional(dev, "pixclk");
+ if (IS_ERR(dsi->pixclk))
+ return PTR_ERR(dsi->pixclk);
+
+ dsi->dphy = devm_phy_get(dev, "dphy");
+ if (IS_ERR(dsi->dphy))
+ return PTR_ERR(dsi->dphy);
+
+ dsi->host.ops = &dw_mipi_dsi_host_ops;
+ dsi->host.dev = dev;
+ ret = mipi_dsi_host_register(&dsi->host);
+ if (ret)
+ return ret;
+
+ dsi->funcs = funcs;
+ dev_set_drvdata(dev, dsi);
+
+ pm_runtime_enable(dev);
+
+ ret = component_add(dev, &dsi_component_ops);
+ if (ret)
+ goto host_unregister;
+
+ return 0;
+
+host_unregister:
+ pm_runtime_disable(dev);
+
+ mipi_dsi_host_unregister(&dsi->host);
+
+ return ret;
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ mipi_dsi_host_unregister(&dsi->host);
+
+ pm_runtime_disable(dev);
+
+ component_del(dev, &dsi_component_ops);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dsi_runtime_suspend(struct device *dev)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(dsi->pixclk);
+ clk_disable_unprepare(dsi->pclk);
+
+ return 0;
+}
+
+static int dsi_runtime_resume(struct device *dev)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ clk_prepare_enable(dsi->pclk);
+ clk_prepare_enable(dsi->pixclk);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dsi_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dsi_runtime_suspend, dsi_runtime_resume, NULL)
+};
+
+struct platform_driver dw_mipi_dsi_driver = {
+ .probe = dsi_probe,
+ .remove = dsi_remove,
+ .driver = {
+ .name = "dw-mipi-dsi",
+ .of_match_table = of_match_ptr(dw_mipi_dsi_dt_match),
+ .pm = &dsi_pm_ops,
+ },
+};
+
+MODULE_DESCRIPTION("DW MIPI DSI Controller Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __DW_MIPI_DSI_H_
+#define __DW_MIPI_DSI_H_
+
+extern struct platform_driver dw_mipi_dsi_driver;
+#endif /* __DW_MIPI_DSI_H_ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/vs_drm.h>
+
+#include "vs_crtc.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_vblank.h>
+#endif
+
+void vs_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(vs_crtc);
+}
+
+static void vs_crtc_reset(struct drm_crtc *crtc)
+{
+ struct vs_crtc_state *state;
+
+ if (crtc->state) {
+ __drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+ state = to_vs_crtc_state(crtc->state);
+ kfree(state);
+ crtc->state = NULL;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+
+ __drm_atomic_helper_crtc_reset(crtc, &state->base);
+
+ state->sync_mode = VS_SINGLE_DC;
+ state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+ state->encoder_type = DRM_MODE_ENCODER_NONE;
+#ifdef CONFIG_VERISILICON_MMU
+ state->mmu_prefetch = VS_MMU_PREFETCH_DISABLE;
+#endif
+}
+
+static struct drm_crtc_state *
+vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+ struct vs_crtc_state *ori_state;
+ struct vs_crtc_state *state;
+
+ if (WARN_ON(!crtc->state))
+ return NULL;
+
+ ori_state = to_vs_crtc_state(crtc->state);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+ state->sync_mode = ori_state->sync_mode;
+ state->output_fmt = ori_state->output_fmt;
+ state->encoder_type = ori_state->encoder_type;
+ state->bg_color = ori_state->bg_color;
+ state->bpp = ori_state->bpp;
+ state->sync_enable = ori_state->sync_enable;
+ state->dither_enable = ori_state->dither_enable;
+ state->underflow = ori_state->underflow;
+#ifdef CONFIG_VERISILICON_MMU
+ state->mmu_prefetch = ori_state->mmu_prefetch;
+#endif
+
+ return &state->base;
+}
+
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ __drm_atomic_helper_crtc_destroy_state(state);
+ kfree(to_vs_crtc_state(state));
+}
+
+static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
+ struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+ struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
+
+ if (property == vs_crtc->sync_mode)
+ vs_crtc_state->sync_mode = val;
+ else if (property == vs_crtc->mmu_prefetch)
+ vs_crtc_state->mmu_prefetch = val;
+ else if (property == vs_crtc->bg_color)
+ vs_crtc_state->bg_color = val;
+ else if (property == vs_crtc->panel_sync)
+ vs_crtc_state->sync_enable = val;
+ else if (property == vs_crtc->dither)
+ vs_crtc_state->dither_enable = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
+ const struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+ const struct vs_crtc_state *vs_crtc_state =
+ container_of(state, const struct vs_crtc_state, base);
+
+ if (property == vs_crtc->sync_mode)
+ *val = vs_crtc_state->sync_mode;
+ else if (property == vs_crtc->mmu_prefetch)
+ *val = vs_crtc_state->mmu_prefetch;
+ else if (property == vs_crtc->bg_color)
+ *val = vs_crtc_state->bg_color;
+ else if (property == vs_crtc->panel_sync)
+ *val = vs_crtc_state->sync_enable;
+ else if (property == vs_crtc->dither)
+ *val = vs_crtc_state->dither_enable;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int vs_crtc_debugfs_show(struct seq_file *s, void *data)
+{
+ struct drm_crtc *crtc = s->private;
+ struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+
+ seq_printf(s, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
+ seq_printf(s, "\tactive = %d\n", crtc->state->active);
+ seq_printf(s, "\tsize = %dx%d\n", mode->hdisplay, mode->vdisplay);
+ seq_printf(s, "\tbpp = %u\n", crtc_state->bpp);
+ seq_printf(s, "\tunderflow = %d\n", crtc_state->underflow);
+
+ return 0;
+}
+
+static int vs_crtc_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, vs_crtc_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations vs_crtc_debugfs_fops = {
+ .open = vs_crtc_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+ debugfs_create_file("status", 0444, crtc->debugfs_entry,
+ crtc, &vs_crtc_debugfs_fops);
+
+ return 0;
+}
+#else
+static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int vs_crtc_late_register(struct drm_crtc *crtc)
+{
+ return vs_crtc_debugfs_init(crtc);
+}
+
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ vs_crtc->funcs->enable_vblank(vs_crtc->dev, crtc, true);
+
+ return 0;
+}
+
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ vs_crtc->funcs->enable_vblank(vs_crtc->dev, crtc, false);
+}
+
+static const struct drm_crtc_funcs vs_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = vs_crtc_destroy,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = vs_crtc_reset,
+ .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
+ .atomic_destroy_state = vs_crtc_atomic_destroy_state,
+ .atomic_set_property = vs_crtc_atomic_set_property,
+ .atomic_get_property = vs_crtc_atomic_get_property,
+ .late_register = vs_crtc_late_register,
+ .enable_vblank = vs_crtc_enable_vblank,
+ .disable_vblank = vs_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+ u8 bpp;
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ bpp = 16;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ bpp = 18;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ bpp = 20;
+ break;
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ bpp = 24;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ bpp = 30;
+ break;
+ default:
+ bpp = 24;
+ break;
+ }
+
+ return bpp;
+}
+
+static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ return vs_crtc->funcs->mode_fixup(vs_crtc->dev, crtc, mode, adjusted_mode);
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+ struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+ vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
+
+ pm_runtime_get_sync(vs_crtc->dev);
+
+ vs_crtc->funcs->enable(vs_crtc->dev, crtc);
+
+ drm_crtc_vblank_on(crtc);
+}
+
+static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ drm_crtc_vblank_off(crtc);
+
+ vs_crtc->funcs->disable(vs_crtc->dev, crtc);
+
+ pm_runtime_put(vs_crtc->dev);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event && !crtc->state->active) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+ struct device *dev = vs_crtc->dev;
+ struct drm_property_blob *blob = crtc->state->gamma_lut;
+ struct drm_color_lut *lut;
+
+ if (crtc->state->color_mgmt_changed) {
+ if ((blob) && (blob->length)) {
+ lut = blob->data;
+ vs_crtc->funcs->set_gamma(dev, crtc, lut,
+ blob->length / sizeof(*lut));
+ vs_crtc->funcs->enable_gamma(dev, crtc, true);
+ } else {
+ vs_crtc->funcs->enable_gamma(dev, crtc, false);
+ }
+ }
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ vs_crtc->funcs->commit(vs_crtc->dev);
+
+ if (event) {
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_arm_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ crtc->state->event = NULL;
+ }
+}
+
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
+ .mode_fixup = vs_crtc_mode_fixup,
+ .atomic_enable = vs_crtc_atomic_enable,
+ .atomic_disable = vs_crtc_atomic_disable,
+ .atomic_begin = vs_crtc_atomic_begin,
+ .atomic_flush = vs_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
+ { VS_SINGLE_DC, "single dc mode" },
+ { VS_MULTI_DC_PRIMARY, "primary dc for multi dc mode" },
+ { VS_MULTI_DC_SECONDARY, "secondary dc for multi dc mode" },
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+static const struct drm_prop_enum_list vs_mmu_prefetch_enum_list[] = {
+ { VS_MMU_PREFETCH_DISABLE, "disable mmu prefetch" },
+ { VS_MMU_PREFETCH_ENABLE, "enable mmu prefetch" },
+};
+#endif
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+ struct vs_dc_info *info)
+{
+ struct vs_crtc *crtc;
+ int ret;
+
+ if (!info)
+ return NULL;
+
+ crtc = kzalloc(sizeof(struct vs_crtc), GFP_KERNEL);
+ if (!crtc)
+ return NULL;
+
+ ret = drm_crtc_init_with_planes(drm_dev, &crtc->base,
+ NULL, NULL, &vs_crtc_funcs,
+ info->name ? info->name : NULL);
+ if (ret)
+ goto err_free_crtc;
+
+ drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
+
+ /* Set up the crtc properties */
+ if (info->pipe_sync) {
+ crtc->sync_mode = drm_property_create_enum(drm_dev, 0,
+ "SYNC_MODE",
+ vs_sync_mode_enum_list,
+ ARRAY_SIZE(vs_sync_mode_enum_list));
+
+ if (!crtc->sync_mode)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base,
+ crtc->sync_mode,
+ VS_SINGLE_DC);
+ }
+
+#if 0
+ if (info->gamma_size) {
+ ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+ info->gamma_size);
+ if (ret)
+ goto err_cleanup_crts;
+
+ drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+ info->gamma_size);
+ }
+#endif
+
+ if (info->background) {
+ crtc->bg_color = drm_property_create_range(drm_dev, 0,
+ "BG_COLOR", 0, 0xffffffff);
+
+ if (!crtc->bg_color)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
+ }
+
+ if (info->panel_sync) {
+ crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED");
+
+ if (!crtc->panel_sync)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0);
+ }
+
+ crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
+ if (!crtc->dither)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
+
+#ifdef CONFIG_VERISILICON_MMU
+ if (info->mmu_prefetch) {
+ crtc->mmu_prefetch = drm_property_create_enum(drm_dev, 0,
+ "MMU_PREFETCH",
+ vs_mmu_prefetch_enum_list,
+ ARRAY_SIZE(vs_mmu_prefetch_enum_list));
+ if (!crtc->mmu_prefetch)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base,
+ crtc->mmu_prefetch,
+ VS_MMU_PREFETCH_DISABLE);
+ }
+#endif
+
+ crtc->max_bpc = info->max_bpc;
+ crtc->color_formats = info->color_formats;
+ return crtc;
+
+err_cleanup_crts:
+ drm_crtc_cleanup(&crtc->base);
+
+err_free_crtc:
+ kfree(crtc);
+ return NULL;
+}
+
+void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+ struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+ drm_crtc_handle_vblank(crtc);
+
+ vs_crtc_state->underflow = underflow;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_CRTC_H__
+#define __VS_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vs_type.h"
+
+struct vs_crtc_funcs {
+ void (*enable)(struct device *dev, struct drm_crtc *crtc);
+ void (*disable)(struct device *dev, struct drm_crtc *crtc);
+ bool (*mode_fixup)(struct device *dev,
+ struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*set_gamma)(struct device *dev, struct drm_crtc *crtc,
+ struct drm_color_lut *lut, unsigned int size);
+ void (*enable_gamma)(struct device *dev, struct drm_crtc *crtc,
+ bool enable);
+ void (*enable_vblank)(struct device *dev, struct drm_crtc *crtc,
+ bool enable);
+ void (*commit)(struct device *dev);
+};
+
+struct vs_crtc_state {
+ struct drm_crtc_state base;
+
+ u32 sync_mode;
+ u32 output_fmt;
+ u32 bg_color;
+ u8 encoder_type;
+ u8 mmu_prefetch;
+ u8 bpp;
+ bool sync_enable;
+ bool dither_enable;
+ bool underflow;
+};
+
+struct vs_crtc {
+ struct drm_crtc base;
+ struct device *dev;
+ struct drm_pending_vblank_event *event;
+ unsigned int max_bpc;
+ unsigned int color_formats; /* supported color format */
+
+ struct drm_property *sync_mode;
+ struct drm_property *mmu_prefetch;
+ struct drm_property *bg_color;
+ struct drm_property *panel_sync;
+ struct drm_property *dither;
+
+ const struct vs_crtc_funcs *funcs;
+};
+
+void vs_crtc_destroy(struct drm_crtc *crtc);
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+ struct vs_dc_info *info);
+void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow);
+
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
+{
+ return container_of(crtc, struct vs_crtc, base);
+}
+
+static inline struct vs_crtc_state *
+to_vs_crtc_state(struct drm_crtc_state *state)
+{
+ return container_of(state, struct vs_crtc_state, base);
+}
+#endif /* __VS_CRTC_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/of_graph.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/vs_drm.h>
+
+#include "vs_type.h"
+#include "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_crtc.h"
+#include "vs_drv.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>
+#endif
+
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
+{
+ u8 f = FORMAT_A8R8G8B8;
+
+ switch (format) {
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_BGRX4444:
+ f = FORMAT_X4R4G4B4;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_BGRA4444:
+ f = FORMAT_A4R4G4B4;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_BGRX5551:
+ f = FORMAT_X1R5G5B5;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_BGRA5551:
+ f = FORMAT_A1R5G5B5;
+ break;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ f = FORMAT_R5G6B5;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_BGRX8888:
+ f = FORMAT_X8R8G8B8;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_BGRA8888:
+ f = FORMAT_A8R8G8B8;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ f = FORMAT_YUY2;
+ break;
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ f = FORMAT_UYVY;
+ break;
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ f = FORMAT_YV12;
+ break;
+ case DRM_FORMAT_NV21:
+ f = FORMAT_NV12;
+ break;
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ f = FORMAT_NV16;
+ break;
+ case DRM_FORMAT_P010:
+ f = FORMAT_P010;
+ break;
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_BGRA1010102:
+ f = FORMAT_A2R10G10B10;
+ break;
+ case DRM_FORMAT_NV12:
+ if (fourcc_mod_vs_get_type(mod) ==
+ DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+ f = FORMAT_NV12_10BIT;
+ else
+ f = FORMAT_NV12;
+ break;
+ case DRM_FORMAT_YUV444:
+ if (fourcc_mod_vs_get_type(mod) ==
+ DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+ f = FORMAT_YUV444_10BIT;
+ else
+ f = FORMAT_YUV444;
+ break;
+ default:
+ break;
+ }
+
+ fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+ fb->swizzle = SWIZZLE_ARGB;
+ fb->uv_swizzle = 0;
+
+ switch (format) {
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBA1010102:
+ fb->swizzle = SWIZZLE_RGBA;
+ break;
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_ABGR2101010:
+ fb->swizzle = SWIZZLE_ABGR;
+ break;
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_BGRA1010102:
+ fb->swizzle = SWIZZLE_BGRA;
+ break;
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV61:
+ fb->uv_swizzle = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void update_watermark(struct drm_property_blob *watermark,
+ struct dc_hw_fb *fb)
+{
+ struct drm_vs_watermark *data;
+ fb->water_mark = 0;
+
+ if (watermark) {
+ data = watermark->data;
+ fb->water_mark = data->watermark & 0xFFFFF;
+ }
+}
+
+static inline u8 to_vs_rotation(unsigned int rotation)
+{
+ u8 rot;
+
+ switch (rotation & DRM_MODE_REFLECT_MASK) {
+ case DRM_MODE_REFLECT_X:
+ rot = FLIP_X;
+ return rot;
+ case DRM_MODE_REFLECT_Y:
+ rot = FLIP_Y;
+ return rot;
+ case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+ rot = FLIP_XY;
+ return rot;
+ default:
+ break;
+ }
+
+ switch (rotation & DRM_MODE_ROTATE_MASK) {
+ case DRM_MODE_ROTATE_0:
+ rot = ROT_0;
+ break;
+ case DRM_MODE_ROTATE_90:
+ rot = ROT_90;
+ break;
+ case DRM_MODE_ROTATE_180:
+ rot = ROT_180;
+ break;
+ case DRM_MODE_ROTATE_270:
+ rot = ROT_270;
+ break;
+ default:
+ rot = ROT_0;
+ break;
+ }
+
+ return rot;
+}
+
+static inline u8 to_vs_yuv_color_space(u32 color_space)
+{
+ u8 cs;
+
+ switch (color_space) {
+ case DRM_COLOR_YCBCR_BT601:
+ cs = COLOR_SPACE_601;
+ break;
+ case DRM_COLOR_YCBCR_BT709:
+ cs = COLOR_SPACE_709;
+ break;
+ case DRM_COLOR_YCBCR_BT2020:
+ cs = COLOR_SPACE_2020;
+ break;
+ default:
+ cs = COLOR_SPACE_601;
+ break;
+ }
+
+ return cs;
+}
+
+static inline u8 to_vs_tile_mode(u64 modifier)
+{
+ return (u8)(modifier & DRM_FORMAT_MOD_VS_NORM_MODE_MASK);
+}
+
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+ u8 panel_num = dc->hw.info->panel_num;
+ u32 index = drm_crtc_index(crtc);
+ int i;
+
+ for (i = 0; i < panel_num; i++) {
+ if (index == dc->crtc[i]->base.index)
+ return i;
+ }
+
+ return 0;
+}
+
+static void dc_deinit(struct device *dev)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+
+ dc_hw_enable_interrupt(&dc->hw, 0);
+ dc_hw_deinit(&dc->hw);
+
+ pm_runtime_put(dev);
+}
+
+static int dc_init(struct device *dev)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ int ret = 0, i;
+
+ dc->first_frame = true;
+
+ for (i = 0; i < DC_DISPLAY_NUM; i++)
+ dc->pix_clk_rate[i] = clk_get_rate(dc->pixclk[i]) / 1000;
+
+ pm_runtime_get_sync(dev);
+ ret = dc_hw_init(&dc->hw);
+ if (ret) {
+ dev_err(dev, "failed to init DC HW\n");
+ goto out;
+ }
+
+out:
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static void vs_dc_dump_enable(struct device *dev, dma_addr_t addr,
+ unsigned int pitch)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_dump(&dc->hw, addr, pitch);
+}
+
+static void vs_dc_dump_disable(struct device *dev)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_disable_dump(&dc->hw);
+}
+
+static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct dc_hw_display display;
+
+ display.bus_format = crtc_state->output_fmt;
+ display.h_active = mode->hdisplay;
+ display.h_total = mode->htotal;
+ display.h_sync_start = mode->hsync_start;
+ display.h_sync_end = mode->hsync_end;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ display.h_sync_polarity = true;
+ else
+ display.h_sync_polarity = false;
+
+ display.v_active = mode->vdisplay;
+ display.v_total = mode->vtotal;
+ display.v_sync_start = mode->vsync_start;
+ display.v_sync_end = mode->vsync_end;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ display.v_sync_polarity = true;
+ else
+ display.v_sync_polarity = false;
+
+ display.sync_mode = crtc_state->sync_mode;
+ display.bg_color = crtc_state->bg_color;
+
+ display.id = to_vs_display_id(dc, crtc);
+ display.sync_enable = crtc_state->sync_enable;
+ display.dither_enable = crtc_state->dither_enable;
+
+ display.enable = true;
+
+ if (dc->pix_clk_rate[display.id] != mode->clock) {
+ clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
+ dc->pix_clk_rate[display.id] = mode->clock;
+ }
+
+ if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI ||
+ crtc_state->encoder_type == DRM_MODE_ENCODER_DPI)
+ dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
+ else
+ dc_hw_set_out(&dc->hw, OUT_DP, display.id);
+
+#ifdef CONFIG_VERISILICON_MMU
+ if (crtc_state->mmu_prefetch == VS_MMU_PREFETCH_ENABLE)
+ dc_hw_enable_mmu_prefetch(&dc->hw, true);
+ else
+ dc_hw_enable_mmu_prefetch(&dc->hw, false);
+#endif
+
+ dc_hw_setup_display(&dc->hw, &display);
+}
+
+static void vs_dc_disable(struct device *dev, struct drm_crtc *crtc)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ struct dc_hw_display display;
+
+ display.id = to_vs_display_id(dc, crtc);
+ display.enable = false;
+
+ dc_hw_setup_display(&dc->hw, &display);
+}
+
+static bool vs_dc_mode_fixup(struct device *dev,
+ struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ int id = to_vs_display_id(dc, crtc);
+ long clk_rate;
+
+ if (unlikely(id >= DC_DISPLAY_NUM)) {
+ dev_err(dev, "invalid display id : %d\n", id);
+ return false;
+ }
+
+ if (dc->pixclk[id]) {
+ clk_rate = clk_round_rate(dc->pixclk[id],
+ adjusted_mode->clock * 1000);
+ adjusted_mode->clock = DIV_ROUND_UP(clk_rate, 1000);
+ }
+
+ return true;
+}
+
+static void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc,
+ struct drm_color_lut *lut, unsigned int size)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ u16 i, r, g, b;
+ u8 bits, id;
+
+ if (size != dc->hw.info->gamma_size) {
+ dev_err(dev, "gamma size does not match!\n");
+ return;
+ }
+
+ id = to_vs_display_id(dc, crtc);
+
+ bits = dc->hw.info->gamma_bits;
+ for (i = 0; i < size; i++) {
+ r = drm_color_lut_extract(lut[i].red, bits);
+ g = drm_color_lut_extract(lut[i].green, bits);
+ b = drm_color_lut_extract(lut[i].blue, bits);
+ dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
+ }
+}
+
+static void vs_dc_enable_gamma(struct device *dev, struct drm_crtc *crtc,
+ bool enable)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ u8 id;
+
+ id = to_vs_display_id(dc, crtc);
+ dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+
+static void vs_dc_enable_vblank(struct device *dev, struct drm_crtc *crtc,
+ bool enable)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_irq(&dc->hw, crtc->index, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+ u32 factor = 1 << 16;
+
+ if ((src > 1) && (dest > 1))
+ factor = ((src - 1) << 16) / (dest - 1);
+
+ return factor;
+}
+
+static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
+ struct dc_hw_scale *scale)
+{
+ int dst_w = drm_rect_width(&state->dst);
+ int dst_h = drm_rect_height(&state->dst);
+ int src_w, src_h, temp;
+ scale->enable = false;
+
+ if (roi->enable) {
+ src_w = roi->width;
+ src_h = roi->height;
+ } else {
+ src_w = drm_rect_width(&state->src) >> 16;
+ src_h = drm_rect_height(&state->src) >> 16;
+ }
+
+ if (drm_rotation_90_or_270(state->rotation)) {
+ temp = src_w;
+ src_w = src_h;
+ src_h = temp;
+ }
+
+ if (src_w != dst_w) {
+ scale->scale_factor_x = calc_factor(src_w, dst_w);
+ scale->enable = true;
+ } else {
+ scale->scale_factor_x = 1 << 16;
+ }
+ if (src_h != dst_h) {
+ scale->scale_factor_y = calc_factor(src_h, dst_h);
+ scale->enable = true;
+ } else {
+ scale->scale_factor_y = 1 << 16;
+ }
+}
+
+static void update_fb(struct vs_plane *plane, u8 display_id,
+ struct dc_hw_fb *fb)
+{
+ struct drm_plane_state *state = plane->base.state;
+ struct vs_plane_state *plane_state = to_vs_plane_state(state);
+ struct drm_framebuffer *drm_fb = state->fb;
+ struct drm_rect *src = &state->src;
+ u32 src_x, src_y;
+
+ src_y = src->y1 >> 16;
+ src_x = src->x1 >> 16;
+
+ fb->display_id = display_id;
+ fb->y_address = plane->dma_addr[0];
+ if (src_y)
+ fb->y_address += src_y * drm_fb->pitches[0];
+ if (src_x)
+ fb->y_address += src_x * drm_fb->format->cpp[0];
+
+ fb->y_stride = drm_fb->pitches[0];
+ if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+ fb->u_address = plane->dma_addr[2];
+ if (src_y)
+ fb->u_address += src_y * drm_fb->pitches[2];
+ if (src_x)
+ fb->u_address += src_x * drm_fb->format->cpp[2];
+ fb->v_address = plane->dma_addr[1];
+ if (src_y)
+ fb->v_address += src_y * drm_fb->pitches[1];
+ if (src_x)
+ fb->v_address += src_x * drm_fb->format->cpp[1];
+ fb->u_stride = drm_fb->pitches[2];
+ fb->v_stride = drm_fb->pitches[1];
+ } else {
+ fb->u_address = plane->dma_addr[1];
+ if (src_y)
+ fb->u_address += src_y * drm_fb->pitches[1];
+ if (src_x)
+ fb->u_address += src_x * drm_fb->format->cpp[1];
+ fb->v_address = plane->dma_addr[2];
+ if (src_y)
+ fb->v_address += src_y * drm_fb->pitches[2];
+ if (src_x)
+ fb->v_address += src_x * drm_fb->format->cpp[2];
+ fb->u_stride = drm_fb->pitches[1];
+ fb->v_stride = drm_fb->pitches[2];
+ }
+ fb->width = drm_rect_width(src) >> 16;
+ fb->height = drm_rect_height(src) >> 16;
+ fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
+ fb->rotation = to_vs_rotation(state->rotation);
+ fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
+ fb->zpos = state->zpos;
+ fb->enable = state->visible;
+ update_format(drm_fb->format->format, drm_fb->modifier, fb);
+ update_swizzle(drm_fb->format->format, fb);
+ update_watermark(plane_state->watermark, fb);
+
+ plane_state->status.tile_mode = fb->tile_mode;
+}
+
+#ifdef CONFIG_VERISILICON_DEC
+static u8 get_stream_base(u8 id)
+{
+ u8 stream_base = 0;
+
+ switch (id) {
+ case OVERLAY_PLANE_0:
+ stream_base = 3;
+ break;
+ case OVERLAY_PLANE_1:
+ stream_base = 6;
+ break;
+ case PRIMARY_PLANE_1:
+ stream_base = 16;
+ break;
+ case OVERLAY_PLANE_2:
+ stream_base = 19;
+ break;
+ case OVERLAY_PLANE_3:
+ stream_base = 22;
+ break;
+ default:
+ break;
+ }
+
+ return stream_base;
+}
+
+static void update_fbc(struct vs_dc *dc, struct vs_plane *plane, bool *enable)
+{
+ struct dc_dec_fb dec_fb;
+ struct drm_plane_state *state = plane->base.state;
+ struct drm_framebuffer *drm_fb = state->fb;
+ struct vs_dc_plane *dc_plane = &dc->planes[plane->id];
+ u8 i, stream_id;
+
+ if (!dc->hw.info->cap_dec) {
+ *enable = false;
+ return;
+ }
+
+ stream_id = get_stream_base(dc_plane->id);
+ memset(&dec_fb, 0, sizeof(struct dc_dec_fb));
+ dec_fb.fb = drm_fb;
+
+ if (fourcc_mod_vs_get_type(drm_fb->modifier) !=
+ DRM_FORMAT_MOD_VS_TYPE_COMPRESSED) {
+ *enable = false;
+ } else {
+ *enable = true;
+
+ for (i = 0; i < DEC_PLANE_MAX; i++) {
+ dec_fb.addr[i] = plane->dma_addr[i];
+ dec_fb.stride[i] = drm_fb->pitches[i];
+ }
+ }
+
+ dc_dec_config(&dc->dec400l, &dec_fb, stream_id);
+}
+
+static void disable_fbc(struct vs_dc *dc, struct vs_plane *plane)
+{
+ struct vs_dc_plane *dc_plane = &dc->planes[plane->id];
+ u8 stream_id;
+
+ if (!dc->hw.info->cap_dec)
+ return;
+
+ stream_id = get_stream_base(dc_plane->id);
+ dc_dec_config(&dc->dec400l, NULL, stream_id);
+}
+#endif
+
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
+ struct vs_plane_state *plane_state)
+{
+ dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+ plane_state->degamma_changed = false;
+}
+
+static void update_roi(struct vs_dc *dc, u8 id,
+ struct vs_plane_state *plane_state,
+ struct dc_hw_roi *roi)
+{
+ struct drm_vs_roi *data;
+ struct drm_rect *src = &plane_state->base.src;
+ u16 src_w = drm_rect_width(src) >> 16;
+ u16 src_h = drm_rect_height(src) >> 16;
+
+ if (plane_state->roi) {
+ data = plane_state->roi->data;
+
+ if (data->enable) {
+ roi->x = data->roi_x;
+ roi->y = data->roi_y;
+ roi->width = (data->roi_x + data->roi_w > src_w) ?
+ (src_w - data->roi_x) : data->roi_w;
+ roi->height = (data->roi_y + data->roi_h > src_h) ?
+ (src_h - data->roi_y) : data->roi_h;
+ roi->enable = true;
+ } else {
+ roi->enable = false;
+ }
+
+ dc_hw_update_roi(&dc->hw, id, roi);
+ } else {
+ roi->enable = false;
+ }
+}
+
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
+ struct dc_hw_fb *fb,
+ struct vs_plane_state *plane_state)
+{
+ struct drm_vs_color_mgmt *data;
+ struct dc_hw_colorkey colorkey;
+
+ if (plane_state->color_mgmt) {
+ data = plane_state->color_mgmt->data;
+
+ fb->clear_enable = data->clear_enable;
+ fb->clear_value = data->clear_value;
+
+ if (data->colorkey > data->colorkey_high)
+ data->colorkey = data->colorkey_high;
+
+ colorkey.colorkey = data->colorkey;
+ colorkey.colorkey_high = data->colorkey_high;
+ colorkey.transparency = (data->transparency) ?
+ DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
+ dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+ }
+}
+
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane)
+{
+ struct dc_hw_fb fb = {0};
+ struct dc_hw_scale scale;
+ struct dc_hw_position pos;
+ struct dc_hw_blend blend;
+ struct dc_hw_roi roi;
+ struct drm_plane_state *state = plane->base.state;
+ struct vs_plane_state *plane_state = to_vs_plane_state(state);
+ struct drm_rect *dest = &state->dst;
+ bool dec_enable = false;
+ u8 display_id = 0;
+
+#ifdef CONFIG_VERISILICON_DEC
+ update_fbc(dc, plane, &dec_enable);
+#endif
+
+ display_id = to_vs_display_id(dc, state->crtc);
+ update_fb(plane, display_id, &fb);
+ fb.dec_enable = dec_enable;
+
+
+ update_roi(dc, plane->id, plane_state, &roi);
+
+ update_scale(state, &roi, &scale);
+
+ if (plane_state->degamma_changed)
+ update_degamma(dc, plane, plane_state);
+
+ pos.start_x = dest->x1;
+ pos.start_y = dest->y1;
+ pos.end_x = dest->x2;
+ pos.end_y = dest->y2;
+
+ blend.alpha = (u8)(state->alpha >> 8);
+ blend.blend_mode = (u8)(state->pixel_blend_mode);
+
+ update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+ dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane)
+{
+ struct drm_plane_state *state = plane->base.state;
+ struct vs_plane_state *plane_state = to_vs_plane_state(state);
+ struct drm_vs_watermark *data;
+ struct dc_hw_qos qos;
+
+ if (plane_state->watermark){
+ data = plane_state->watermark->data;
+
+ if (data->qos_high) {
+ if (data->qos_low > data->qos_high)
+ data->qos_low = data->qos_high;
+
+ qos.low_value = data->qos_low & 0x0F;
+ qos.high_value = data->qos_high & 0x0F;
+ dc_hw_update_qos(&dc->hw, &qos);
+ }
+ }
+}
+
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
+{
+ u8 size_type;
+
+ switch (state->crtc_w) {
+ case 32:
+ size_type = CURSOR_SIZE_32X32;
+ break;
+ case 64:
+ size_type = CURSOR_SIZE_64X64;
+ break;
+ default:
+ size_type = CURSOR_SIZE_32X32;
+ break;
+ }
+
+ cursor->size = size_type;
+}
+
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane)
+{
+ struct drm_plane_state *state = plane->base.state;
+ struct dc_hw_cursor cursor;
+
+ cursor.address = plane->dma_addr[0];
+ if (state->crtc_x > 0) {
+ cursor.x = state->crtc_x;
+ cursor.hot_x = 0;
+ } else {
+ cursor.hot_x = -state->crtc_x;
+ cursor.x = 0;
+ }
+ if (state->crtc_y > 0) {
+ cursor.y = state->crtc_y;
+ cursor.hot_y = 0;
+ } else {
+ cursor.hot_y = -state->crtc_y;
+ cursor.y = 0;
+ }
+ cursor.display_id = to_vs_display_id(dc, state->crtc);
+ update_cursor_size(state, &cursor);
+ cursor.enable = true;
+
+ dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+static void vs_dc_update_plane(struct device *dev, struct vs_plane *plane)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ enum drm_plane_type type = plane->base.type;
+
+ switch (type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ case DRM_PLANE_TYPE_OVERLAY:
+ update_plane(dc, plane);
+ update_qos(dc, plane);
+ break;
+ case DRM_PLANE_TYPE_CURSOR:
+ update_cursor_plane(dc, plane);
+ break;
+ default:
+ break;
+ }
+}
+
+static void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ enum drm_plane_type type = plane->base.type;
+ struct dc_hw_fb fb = {0};
+ struct dc_hw_cursor cursor = {0};
+
+ switch (type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ case DRM_PLANE_TYPE_OVERLAY:
+ fb.enable = false;
+ dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
+#ifdef CONFIG_VERISILICON_DEC
+ disable_fbc(dc, plane);
+#endif
+ break;
+ case DRM_PLANE_TYPE_CURSOR:
+ cursor.enable = false;
+ cursor.display_id = to_vs_display_id(dc, old_state->crtc);
+ dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
+ u64 modifier)
+{
+ const u64 *mods;
+
+ if (plane_info->modifiers == NULL)
+ return false;
+
+ for(mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
+ if (*mods == modifier)
+ return true;
+ }
+
+ return false;
+}
+
+static int vs_dc_check_plane(struct device *dev, struct vs_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ struct drm_framebuffer *fb = state->fb;
+ const struct vs_plane_info *plane_info;
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_crtc_state *crtc_state;
+
+ plane_info = &dc->hw.info->planes[plane->id];
+ if (plane_info == NULL)
+ return -EINVAL;
+
+ if (fb->width < plane_info->min_width ||
+ fb->width > plane_info->max_width ||
+ fb->height < plane_info->min_height ||
+ fb->height > plane_info->max_height)
+ dev_err_once(dev, "buffer size may not support on plane%d.\n",
+ plane->id);
+
+ if ((plane->base.type != DRM_PLANE_TYPE_CURSOR) &&
+ (!vs_dc_mod_supported(plane_info, fb->modifier))) {
+ dev_err(dev, "unsupported modifier on plane%d.\n", plane->id);
+ return -EINVAL;
+ }
+
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+ if (IS_ERR(crtc_state))
+ return -EINVAL;
+
+ /* 90/270 degree rotation requires non-linear fb */
+ if ((state->rotation == DRM_MODE_ROTATE_90 ||
+ state->rotation == DRM_MODE_ROTATE_270) &&
+ (fb->modifier & DRM_FORMAT_MOD_LINEAR) == DRM_FORMAT_MOD_LINEAR)
+ return -EINVAL;
+
+ return drm_atomic_helper_check_plane_state(state, crtc_state,
+ plane_info->min_scale,
+ plane_info->max_scale,
+ true, true);
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+ struct vs_dc *dc = data;
+ struct vs_dc_info *dc_info = dc->hw.info;
+ u32 i, intr_vec;
+
+ intr_vec = dc_hw_get_interrupt(&dc->hw);
+
+ for (i = 0; i < dc_info->panel_num; i++) {
+ if (intr_vec & BIT(i))
+ vs_crtc_handle_vblank(&dc->crtc[i]->base,
+ dc_hw_check_underflow(&dc->hw));
+ }
+
+ if (intr_vec & ~0x3)
+ pr_warn("%s: unhandled interrupt %#x\n", __func__, intr_vec);
+
+ return IRQ_HANDLED;
+}
+
+static void vs_dc_commit(struct device *dev)
+{
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+#ifdef CONFIG_VERISILICON_DEC
+ if (dc->hw.info->cap_dec)
+ dc_dec_commit(&dc->dec400l, &dc->hw);
+#endif
+
+ dc_hw_enable_shadow_register(&dc->hw, false);
+
+ dc_hw_commit(&dc->hw);
+
+ if (dc->first_frame)
+ dc->first_frame = false;
+
+ dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static const struct vs_crtc_funcs dc_crtc_funcs = {
+ .enable = vs_dc_enable,
+ .disable = vs_dc_disable,
+ .mode_fixup = vs_dc_mode_fixup,
+ .set_gamma = vs_dc_set_gamma,
+ .enable_gamma = vs_dc_enable_gamma,
+ .enable_vblank = vs_dc_enable_vblank,
+ .commit = vs_dc_commit,
+};
+
+static const struct vs_plane_funcs dc_plane_funcs = {
+ .update = vs_dc_update_plane,
+ .disable = vs_dc_disable_plane,
+ .check = vs_dc_check_plane,
+};
+
+static const struct vs_dc_funcs dc_funcs = {
+ .dump_enable = vs_dc_dump_enable,
+ .dump_disable = vs_dc_dump_disable,
+};
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+#ifdef CONFIG_VERISILICON_MMU
+ struct vs_drm_private *priv = drm_dev->dev_private;
+#endif
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ struct device_node *port;
+ struct vs_crtc *crtc;
+ struct drm_crtc *drm_crtc;
+ struct vs_dc_info *dc_info;
+ struct vs_plane *plane;
+ struct drm_plane *drm_plane, *tmp;
+ struct vs_plane_info *plane_info;
+ int i, ret;
+ u32 ctrc_mask = 0;
+ if (!drm_dev || !dc) {
+ dev_err(dev, "devices are not created.\n");
+ return -ENODEV;
+ }
+
+ ret = dc_init(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize DC hardware.\n");
+ return ret;
+ }
+
+#ifdef CONFIG_VERISILICON_MMU
+ ret = dc_mmu_construct(priv->dma_dev, &priv->mmu);
+ if (ret) {
+ dev_err(dev, "failed to construct DC MMU\n");
+ goto err_clean_dc;
+ }
+
+ ret = dc_hw_mmu_init(&dc->hw, priv->mmu);
+ if (ret) {
+ dev_err(dev, "failed to init DC MMU\n");
+ goto err_clean_dc;
+ }
+#endif
+
+ ret = vs_drm_iommu_attach_device(drm_dev, dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attached iommu device.\n");
+ goto err_clean_dc;
+ }
+
+ if (!of_graph_is_present(dev->of_node)) {
+ dev_err(dev, "no port node found\n");
+ ret = -ENODEV;
+ goto err_detach_dev;
+ }
+
+ dc_info = dc->hw.info;
+
+ for(i = 0; i < dc_info->panel_num; i++) {
+ port = of_graph_get_port_by_id(dev->of_node, i);
+ if (!port) {
+ dev_err(dev, "no port node found for panel %d\n", i);
+ ret = -ENODEV;
+ goto err_detach_dev;
+ };
+
+ crtc = vs_crtc_create(drm_dev, dc_info);
+ if (!crtc) {
+ dev_err(dev, "Failed to create CRTC.\n");
+ ret = -ENOMEM;
+ goto err_detach_dev;
+ }
+
+ crtc->base.port = port;
+ crtc->dev = dev;
+ crtc->funcs = &dc_crtc_funcs;
+ dc->crtc[i] = crtc;
+ ctrc_mask |= drm_crtc_mask(&crtc->base);
+ }
+
+ for (i = 0; i < dc_info->plane_num; i++) {
+ plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+ if (!strcmp(plane_info->name, "Primary") ||
+ !strcmp(plane_info->name, "Cursor"))
+ plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+ drm_crtc_mask(&dc->crtc[0]->base));
+ else if (!strcmp(plane_info->name, "Primary_1") ||
+ !strcmp(plane_info->name, "Cursor_1"))
+ plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+ drm_crtc_mask(&dc->crtc[1]->base));
+ else
+ plane = vs_plane_create(drm_dev, plane_info,
+ dc_info->layer_num, ctrc_mask);
+
+ if (!plane)
+ goto err_cleanup_planes;
+
+ plane->id = i;
+ dc->planes[i].id = plane_info->id;
+
+ plane->funcs = &dc_plane_funcs;
+
+ if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+ if (!strcmp(plane_info->name, "Primary"))
+ dc->crtc[0]->base.primary = &plane->base;
+ else
+ dc->crtc[1]->base.primary = &plane->base;
+ drm_dev->mode_config.min_width = plane_info->min_width;
+ drm_dev->mode_config.min_height =
+ plane_info->min_height;
+ drm_dev->mode_config.max_width = plane_info->max_width;
+ drm_dev->mode_config.max_height =
+ plane_info->max_height;
+ }
+
+ if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+ if (!strcmp(plane_info->name, "Cursor"))
+ dc->crtc[0]->base.cursor = &plane->base;
+ else
+ dc->crtc[1]->base.cursor = &plane->base;
+ drm_dev->mode_config.cursor_width =
+ plane_info->max_width;
+ drm_dev->mode_config.cursor_height =
+ plane_info->max_height;
+ }
+ }
+
+ dc->funcs = &dc_funcs;
+ vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+ return 0;
+
+err_cleanup_planes:
+ list_for_each_entry_safe(drm_plane, tmp,
+ &drm_dev->mode_config.plane_list, head)
+ if (drm_plane->possible_crtcs & ctrc_mask)
+ vs_plane_destory(drm_plane);
+
+ drm_for_each_crtc(drm_crtc, drm_dev)
+ vs_crtc_destroy(drm_crtc);
+err_detach_dev:
+ vs_drm_iommu_detach_device(drm_dev, dev);
+err_clean_dc:
+ dc_deinit(dev);
+ return ret;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+
+ dc_deinit(dev);
+
+ vs_drm_iommu_detach_device(drm_dev, dev);
+}
+
+const struct component_ops dc_component_ops = {
+ .bind = dc_bind,
+ .unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+ { .compatible = "verisilicon,dc8200", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static void dc_get_display_pll(struct device *dev, struct vs_dc *dc)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_name(NULL, "dw-mipi-dsi0");
+ if (!np)
+ dev_err(dev, "Failed to get dsi0\n");
+ else
+ dc->dpu0pll_on = of_device_is_available(np);
+
+ np = of_find_node_by_name(NULL, "dw-mipi-dsi1");
+ if (!np)
+ dev_err(dev, "Failed to get dsi1\n");
+ else
+ dc->dpu1pll_on = of_device_is_available(np);
+
+ /* dsi1/hdmi share the same pll1, hdmi detect again if dsi1 not use */
+ if (!dc->dpu1pll_on) {
+ np = of_find_node_by_name(NULL, "dw-hdmi-tx");
+ if (!np)
+ dev_err(dev, "Failed to get hdmi\n");
+ else
+ dc->dpu1pll_on = of_device_is_available(np);
+ }
+
+
+ dev_info(dev, "dpu0pll_on:%d dpu1pll_on:%d\n", dc->dpu0pll_on,
+ dc->dpu1pll_on);
+}
+
+static int dc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vs_dc *dc;
+ int irq, ret, i;
+ char pixclk[16];
+ struct device_node *np = dev->of_node;
+
+ dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+ if (!dc)
+ return -ENOMEM;
+
+ dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dc->hw.hi_base))
+ return PTR_ERR(dc->hw.hi_base);
+
+ dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(dc->hw.reg_base))
+ return PTR_ERR(dc->hw.reg_base);
+
+#ifdef CONFIG_VERISILICON_MMU
+ dc->hw.mmu_base = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(dc->hw.mmu_base))
+ return PTR_ERR(dc->hw.mmu_base);
+#endif
+
+ dc->hw.vosys_regmap = syscon_regmap_lookup_by_phandle(np, "vosys-regmap");
+ if (IS_ERR(dc->hw.vosys_regmap))
+ {
+ dev_err(dev, "Failed to remap vosys\n");
+ return PTR_ERR(dc->hw.vosys_regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to install irq:%u.\n", irq);
+ return ret;
+ }
+
+ dc->core_clk = devm_clk_get_optional(dev, "core_clk");
+ if (IS_ERR(dc->core_clk)) {
+ dev_err(dev, "failed to get core_clk source\n");
+ return PTR_ERR(dc->core_clk);
+ }
+
+ for (i = 0; i < DC_DISPLAY_NUM; i++) {
+ snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
+ dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
+
+ if (IS_ERR(dc->pix_clk[i])) {
+ dev_err(dev, "failed to get pix_clk %d source\n", i);
+ return PTR_ERR(dc->pix_clk[i]);
+ }
+
+ snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
+ dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
+ if (IS_ERR(dc->pixclk[i])) {
+ dev_err(dev, "failed to get pixclk %d source\n", i);
+ return PTR_ERR(dc->pixclk[i]);
+ }
+ }
+
+ dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
+ if (IS_ERR(dc->axi_clk)) {
+ dev_err(dev, "failed to get axi_clk source\n");
+ return PTR_ERR(dc->axi_clk);
+ }
+
+ dc->cfg_clk = devm_clk_get_optional(dev, "cfg_clk");
+ if (IS_ERR(dc->cfg_clk)) {
+ dev_err(dev, "failed to get cfg_clk source\n");
+ return PTR_ERR(dc->cfg_clk);
+ }
+
+ dc->dpu0pll_clk = devm_clk_get_optional(dev, "dpu0_pll_foutpostdiv");
+ if (IS_ERR(dc->dpu0pll_clk)) {
+ dev_err(dev, "failed to get dpu0pll_clk source\n");
+ return PTR_ERR(dc->dpu0pll_clk);
+ }
+
+ dc->dpu1pll_clk = devm_clk_get_optional(dev, "dpu1_pll_foutpostdiv");
+ if (IS_ERR(dc->dpu1pll_clk)) {
+ dev_err(dev, "failed to get dpu1pll_clk source\n");
+ return PTR_ERR(dc->dpu1pll_clk);
+ }
+
+ dc->def_pix_clk_rate[0] = clk_get_rate(dc->dpu0pll_clk)/1000;
+ dc->def_pix_clk_rate[1] = clk_get_rate(dc->dpu1pll_clk)/1000;
+
+ dc_get_display_pll(dev, dc);
+
+ dev_set_drvdata(dev, dc);
+ pm_runtime_enable(dev);
+
+ return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &dc_component_ops);
+
+ pm_runtime_disable(dev);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dc_runtime_suspend(struct device *dev)
+{
+ int i;
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ clk_disable_unprepare(dc->axi_clk);
+
+ for (i = 0; i < DC_DISPLAY_NUM; i++){
+ clk_disable_unprepare(dc->pix_clk[i]);
+ clk_disable_unprepare(dc->pixclk[i]);
+ }
+
+ clk_disable_unprepare(dc->core_clk);
+
+ clk_disable_unprepare(dc->cfg_clk);
+
+ if (dc->dpu0pll_on)
+ clk_disable_unprepare(dc->dpu0pll_clk);
+ if (dc->dpu1pll_on)
+ clk_disable_unprepare(dc->dpu1pll_clk);
+
+ return 0;
+}
+
+static int dc_runtime_resume(struct device *dev)
+{
+ int i, ret;
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (dc->dpu0pll_on)
+ clk_prepare_enable(dc->dpu0pll_clk);
+ if (dc->dpu1pll_on)
+ clk_prepare_enable(dc->dpu1pll_clk);
+
+ ret = clk_prepare_enable(dc->cfg_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable cfg_clk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dc->core_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable core_clk\n");
+ return ret;
+ }
+
+ for (i = 0; i < DC_DISPLAY_NUM; i++) {
+ ret = clk_prepare_enable(dc->pix_clk[i]);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable pix_clk %d\n", i);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dc->pixclk[i]);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable pixclk %d\n", i);
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(dc->axi_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable axi_clk\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+#ifdef CONFIG_PM_SLEEP
+
+static int dc_suspend(struct device *dev)
+{
+ int i;
+ struct vs_dc *dc = dev_get_drvdata(dev);
+
+ for (i = 0; i < DC_DISPLAY_NUM; i++){
+ dc->pix_clk_rate[i] = dc->def_pix_clk_rate[i];
+ clk_set_rate(dc->pixclk[i], dc->pix_clk_rate[i] * 1000);
+ }
+
+ return 0;
+}
+
+static int dc_resume(struct device *dev)
+{
+ int i, ret;
+ struct vs_dc *dc = dev_get_drvdata(dev);
+ dev_info(dev,"dc resume\n");
+ regmap_write(dc->hw.vosys_regmap,0x4,0x7); //release dpu reset
+
+ ret = dc_hw_init(&dc->hw);
+ if (ret)
+ dev_err(dev, "failed to init DC HW\n");
+ return 0;
+}
+
+#endif
+static const struct dev_pm_ops dc_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL)
+ #ifdef CONFIG_PM_SLEEP
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume)
+ #endif
+};
+
+struct platform_driver dc_platform_driver = {
+ .probe = dc_probe,
+ .remove = dc_remove,
+ .driver = {
+ .name = "vs-dc",
+ .of_match_table = of_match_ptr(dc_driver_dt_match),
+ .pm = &dc_pm_ops,
+ },
+};
+
+MODULE_DESCRIPTION("VeriSilicon DC Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_H__
+#define __VS_DC_H__
+
+#include <linux/version.h>
+#include <linux/mm_types.h>
+
+#include <drm/drm_modes.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#include "vs_plane.h"
+#include "vs_crtc.h"
+#include "vs_dc_hw.h"
+#include "vs_dc_dec.h"
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+struct vs_dc_funcs {
+ void (*dump_enable)(struct device *dev, dma_addr_t addr,
+ unsigned int pitch);
+ void (*dump_disable)(struct device *dev);
+};
+
+struct vs_dc_plane {
+ enum dc_hw_plane_id id;
+};
+
+struct vs_dc {
+ struct vs_crtc *crtc[DC_DISPLAY_NUM];
+ struct dc_hw hw;
+#ifdef CONFIG_VERISILICON_DEC
+ struct dc_dec400l dec400l;
+#endif
+
+ struct clk *core_clk;
+ struct clk *pix_clk[DC_DISPLAY_NUM];
+ struct clk *pixclk[DC_DISPLAY_NUM];
+ struct clk *axi_clk;
+ struct clk *cfg_clk;
+ struct clk *dpu0pll_clk;
+ struct clk *dpu1pll_clk;
+
+ unsigned int pix_clk_rate[DC_DISPLAY_NUM]; /* in KHz */
+ unsigned int def_pix_clk_rate[DC_DISPLAY_NUM];
+
+ bool first_frame;
+ bool dpu0pll_on;
+ bool dpu1pll_on;
+
+ struct vs_dc_plane planes[PLANE_NUM];
+
+ const struct vs_dc_funcs *funcs;
+};
+
+extern struct platform_driver dc_platform_driver;
+#endif /* __VS_DC_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+
+#include "vs_dc_dec.h"
+
+#define fourcc_mod_vs_get_tile_mode(val) (u8)((val) & \
+ DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK)
+
+static inline bool _is_stream_changed(struct dc_dec400l *dec400l, u8 stream_id)
+{
+ return dec400l->stream[stream_id].dirty;
+}
+
+static inline bool _is_stream_valid(struct dc_dec400l *dec400l, u8 stream_id)
+{
+ return !!(dec400l->stream[stream_id].main_base_addr);
+}
+
+static void _enable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
+ u8 stream_id)
+{
+ if (!(dec400l->stream_status & (1 << stream_id))) {
+ if (!(dec400l->stream_status))
+ /* the first enabled steram */
+ dc_hw_dec_init(hw);
+
+ dec400l->stream_status |= 1 << stream_id;
+ }
+}
+static void _disable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
+ u8 stream_id)
+{
+ if ((dec400l->stream_status & (1 << stream_id)))
+ dec400l->stream_status &= ~(1 << stream_id);
+}
+
+static u16 get_dec_tile_size(u8 tile_mode, u8 cpp)
+{
+ u16 multi = 0;
+
+ switch (tile_mode) {
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_16X1:
+ multi = 16;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S:
+ multi = 32;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_16X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_32X2:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB:
+ multi = 64;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X16:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_32X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_64X2:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB:
+ multi = 128;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_64X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_64X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X2:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X16:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+ multi = 256;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_256X2:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X4:
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_512X1:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_128X4:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X16:
+ multi = 512;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_256X4:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_64X16:
+ case DRM_FORMAT_MOD_VS_DEC_TILE_128X8:
+ multi = 1024;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_512X4:
+ multi = 2048;
+ break;
+ default:
+ break;
+ }
+
+ return multi * cpp;
+}
+
+static void update_fb_format(struct dc_dec_fb *dec_fb)
+{
+ struct drm_framebuffer *drm_fb = dec_fb->fb;
+ u8 tile_mod = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
+ u8 norm_mod = DRM_FORMAT_MOD_VS_LINEAR;
+
+ switch (tile_mod) {
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_32X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_64X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_128X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_256X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
+ norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
+ norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+ if (drm_fb->format->format == DRM_FORMAT_YUYV ||
+ drm_fb->format->format == DRM_FORMAT_UYVY ||
+ drm_fb->format->format == DRM_FORMAT_P010)
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+ else
+ norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+ if (drm_fb->format->format == DRM_FORMAT_NV12 ||
+ drm_fb->format->format == DRM_FORMAT_P010)
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+ if (drm_fb->format->format == DRM_FORMAT_NV12)
+ norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+ break;
+ default:
+ break;
+ }
+
+ drm_fb->modifier = fourcc_mod_vs_norm_code(norm_mod);
+}
+
+static void _stream_config(struct dc_dec_fb *dec_fb,
+ struct dc_dec_stream *stream, u8 index)
+{
+ struct drm_framebuffer *drm_fb = dec_fb->fb;
+ const struct drm_format_info *info = drm_fb->format;
+ u32 plane_height = drm_format_info_plane_height(info,
+ drm_fb->height, index);
+ u16 tile_size;
+
+ stream->main_base_addr = dec_fb->addr[index];
+ stream->tile_mode = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
+ if (drm_fb->modifier & DRM_FORMAT_MOD_VS_DEC_ALIGN_32)
+ stream->align_mode = DEC_ALIGN_32;
+ else
+ stream->align_mode = DEC_ALIGN_64;
+
+ switch (drm_fb->format->format) {
+ case DRM_FORMAT_ARGB8888:
+ stream->format = DEC_FORMAT_ARGB8;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ stream->format = DEC_FORMAT_XRGB8;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_RGB565:
+ stream->format = DEC_FORMAT_R5G6B5;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ stream->format = DEC_FORMAT_A1RGB5;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ stream->format = DEC_FORMAT_X1RGB5;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ stream->format = DEC_FORMAT_ARGB4;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_XRGB4444:
+ stream->format = DEC_FORMAT_XRGB4;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_ARGB2101010:
+ stream->format = DEC_FORMAT_A2R10G10B10;
+ stream->depth = DEC_DEPTH_10;
+ break;
+ case DRM_FORMAT_YUYV:
+ stream->format = DEC_FORMAT_YUY2;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_UYVY:
+ stream->format = DEC_FORMAT_UYVY;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_YVU420:
+ stream->format = DEC_FORMAT_YUV_ONLY;
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_NV12:
+ WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_32X8 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8);
+ if (index) {
+ stream->format = DEC_FORMAT_UV_MIX;
+ switch (stream->tile_mode) {
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_TILE_32X4;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
+ break;
+ default:
+ break;
+ }
+ } else {
+ stream->format = DEC_FORMAT_YUV_ONLY;
+ }
+
+ stream->depth = DEC_DEPTH_8;
+ break;
+ case DRM_FORMAT_P010:
+ WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_64X1 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR);
+ if (index) {
+ stream->format = DEC_FORMAT_UV_MIX;
+ switch (stream->tile_mode) {
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_32X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_TILE_8X4;
+ break;
+ default:
+ break;
+ }
+ } else {
+ stream->format = DEC_FORMAT_YUV_ONLY;
+ }
+
+ stream->depth = DEC_DEPTH_10;
+ break;
+ case DRM_FORMAT_NV16:
+ WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
+ stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1);
+ if (index) {
+ stream->format = DEC_FORMAT_UV_MIX;
+ switch (stream->tile_mode) {
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
+ break;
+ case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+ stream->tile_mode =
+ DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+ break;
+ default:
+ break;
+ }
+ } else {
+ stream->format = DEC_FORMAT_YUV_ONLY;
+ }
+
+ stream->depth = DEC_DEPTH_8;
+ break;
+ }
+
+ tile_size = get_dec_tile_size(stream->tile_mode, info->cpp[index]);
+ stream->aligned_stride = ALIGN(dec_fb->stride[index], tile_size);
+ stream->ts_base_addr = stream->main_base_addr +
+ stream->aligned_stride * plane_height;
+}
+
+static int _dec400l_config(struct dc_dec400l *dec400l,
+ struct dc_dec_fb *dec_fb, u8 stream_base)
+{
+ struct dc_dec_stream stream;
+ u8 i, stream_id, num_planes = 0;
+
+ if (dec_fb) {
+ const struct drm_format_info *info = dec_fb->fb->format;
+
+ num_planes = info->num_planes;
+ }
+
+ for (i = 0; i < STREAM_COUNT; i ++) {
+ stream_id = stream_base + i;
+
+ memset(&dec400l->stream[stream_id], 0, sizeof(struct dc_dec_stream));
+
+ if (i < num_planes) {
+ memset(&stream, 0, sizeof(struct dc_dec_stream));
+ _stream_config(dec_fb, &stream, i);
+ memcpy(&dec400l->stream[stream_id], &stream,
+ sizeof(struct dc_dec_stream));
+ }
+ dec400l->stream[stream_id].dirty = true;
+ }
+
+ if (dec_fb)
+ update_fb_format(dec_fb);
+
+ return 0;
+}
+
+int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
+ u8 stream_base)
+{
+ if (dec_fb && !dec_fb->fb)
+ return -EINVAL;
+
+ if (dec_fb && (fourcc_mod_vs_get_type(dec_fb->fb->modifier) !=
+ DRM_FORMAT_MOD_VS_TYPE_COMPRESSED))
+ _dec400l_config(dec400l, NULL, stream_base);
+ else
+ _dec400l_config(dec400l, dec_fb, stream_base);
+
+ return 0;
+}
+
+int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw)
+{
+ u8 i;
+
+ for (i = 0; i < STREAM_TOTAL; i++) {
+ if (!_is_stream_changed(dec400l, i))
+ continue;
+
+ if (_is_stream_valid(dec400l, i)) {
+ _enable_stream(dec400l, hw, i);
+ dc_hw_dec_stream_set(hw, dec400l->stream[i].main_base_addr,
+ dec400l->stream[i].ts_base_addr, dec400l->stream[i].tile_mode,
+ dec400l->stream[i].align_mode, dec400l->stream[i].format,
+ dec400l->stream[i].depth, i);
+ } else {
+ dc_hw_dec_stream_disable(hw, i);
+ _disable_stream(dec400l, hw, i);
+ }
+
+ dec400l->stream[i].dirty = false;
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef _VS_DC_DEC_H_
+#define _VS_DC_DEC_H_
+
+#include <drm/drm_fourcc.h>
+
+#include "vs_dc_hw.h"
+
+#define fourcc_mod_vs_get_type(val) \
+ (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+
+/* DEC_READ_CONFIG */
+#define COMPRESSION_EN BIT(0)
+#define COMPRESSION_FORMAT_MASK GENMASK(7, 3)
+#define COMPRESSION_ALIGN_MODE_MASK GENMASK(17, 16)
+#define TILE_MODE_MASK GENMASK(30, 25)
+
+/* DEC_READ_EX_CONFIG */
+#define BIT_DEPTH_MASK GENMASK(18, 16)
+
+/* DEC_CONTROL */
+#define FLUSH_ENABLE BIT(0)
+#define COMPRESSION_DISABLE BIT(1)
+
+/* DEC_CONTROL_EX */
+#define WRITE_MISS_POLICY1 1
+#define WRITE_MISS_POLICY_MASK BIT(19)
+#define READ_MISS_POLICY0 0
+#define READ_MISS_POLICY_MASK BIT(29)
+
+/* DEC_CONTROL_EX2 */
+#define TILE_STATUS_READ_ID 16
+#define TILE_STATUS_READ_ID_MASK GENMASK(6, 0)
+#define TILE_STATUS_READ_ID_H 0
+#define TILE_STATUS_READ_ID_H_MASK GENMASK(23, 22)
+#define DISABLE_HW_DEC_FLUSH BIT(28)
+
+#define STREAM_COUNT 3
+#define STREAM_TOTAL 32
+
+#define DEC_PLANE_MAX 3
+
+enum dc_dec_align_mode {
+ DEC_ALIGN_32 = 0x02,
+ DEC_ALIGN_64,
+};
+
+enum dc_dec_format {
+ DEC_FORMAT_ARGB8,
+ DEC_FORMAT_XRGB8,
+ DEC_FORMAT_AYUV,
+ DEC_FORMAT_UYVY,
+ DEC_FORMAT_YUY2,
+ DEC_FORMAT_YUV_ONLY,
+ DEC_FORMAT_UV_MIX,
+ DEC_FORMAT_ARGB4,
+ DEC_FORMAT_XRGB4,
+ DEC_FORMAT_A1RGB5,
+ DEC_FORMAT_X1RGB5,
+ DEC_FORMAT_R5G6B5,
+ DEC_FORMAT_A2R10G10B10 = 0x0F,
+ DEC_FORMAT_BAYER,
+ DEC_FORMAT_COEFFICIENT = 0x12,
+ DEC_FORMAT_ARGB16,
+ DEC_FORMAT_X2RGB10 = 0x15,
+};
+
+enum dc_dec_depth {
+ DEC_DEPTH_8,
+ DEC_DEPTH_10,
+ DEC_DEPTH_12,
+ DEC_DEPTH_14,
+ DEC_DEPTH_16,
+};
+
+struct dc_dec_stream {
+ u32 main_base_addr;
+ u32 aligned_stride;
+ u32 ts_base_addr;
+ u8 tile_mode;
+ u8 align_mode;
+ u8 format;
+ u8 depth;
+ bool dirty;
+};
+
+struct dc_dec_fb {
+ struct drm_framebuffer *fb;
+ u32 addr[DEC_PLANE_MAX];
+ u32 stride[DEC_PLANE_MAX];
+};
+
+struct dc_dec400l {
+ struct dc_dec_stream stream[STREAM_TOTAL];
+ u32 stream_status;
+};
+
+int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
+ u8 stream_base);
+int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw);
+
+#endif /* _VS_DC_DEC_H_ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/vs_drm.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "vs_type.h"
+#include "vs_dc_hw.h"
+#include "vs_dc_dec.h"
+
+static const u32 horKernel[] = {
+ 0x00000000, 0x20000000, 0x00002000, 0x00000000,
+ 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+ 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+ 0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+ 0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+ 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+ 0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+ 0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+ 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+ 0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+ 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+ 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+ 0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+ 0x00000000, 0x00000000, 0x00000000, 0x01470000,
+ 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+ 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+ 0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+ 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00004000, 0x00000000,
+ 0x00000000, 0x00000000, 0x20002000, 0x00000000,
+ 0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+ 0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
+ 0x00000000, 0x00000000, 0x00000000, 0x14680000,
+ 0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+ 0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
+ 0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
+ 0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+ 0x00000000, 0x087f0000, 0x00003781, 0x00000000,
+ 0x00000000, 0x00000000, 0x399a0666, 0x00000000,
+ 0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+ 0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
+ 0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+#define H_COEF_SIZE (sizeof(horKernel) / sizeof(u32))
+
+static const u32 verKernel[] = {
+ 0x00000000, 0x20000000, 0x00002000, 0x00000000,
+ 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+ 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+ 0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+ 0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+ 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+ 0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+ 0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+ 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+ 0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+ 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+ 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+ 0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+ 0x00000000, 0x00000000, 0x00000000, 0x01470000,
+ 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+ 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+ 0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+ 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00004000, 0x00000000,
+ 0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
+ 0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+ 0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+#define V_COEF_SIZE (sizeof(verKernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
+ 10279, 5395, 709,
+ 1132, 15065, 187,
+ 269, 1442, 14674
+};
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1640, 1196,
+ -404, -836, 1196, 2076,
+ 0, -916224, 558336, -1202944,
+ 64, 940, 64, 960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1844, 1196,
+ -220, -548, 1196, 2172,
+ 0, -1020672, 316672, -1188608,
+ 64, 940, 64, 960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1724, 1196,
+ -192, -668, 1196, 2200,
+ 0, -959232, 363776, -1202944,
+ 64, 940, 64, 960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
+ 230, 594, 52,
+ -125, -323, 448,
+ 448, -412, -36,
+ 64, 512, 512
+};
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
+ 0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
+ 0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
+ 0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
+ 0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
+ 0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
+ 0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
+ 0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
+ 0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+ 0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
+ 0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
+ 0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
+ 0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
+ 0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
+ 0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
+ 0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
+ 0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
+ 0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+ 0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
+ 0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
+ 0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
+ 0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
+ 0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
+ 0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
+ 0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
+ 0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
+ 0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+ 0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
+ 0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
+ 0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
+ 0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
+ 0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
+ 0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+ 0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
+ 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
+ 0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+ 0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
+ 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
+ 0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
+ 0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
+ 0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
+ 0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
+ 0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
+ 0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
+ 0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+ 0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
+ 0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
+ 0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
+ 0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
+ 0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
+ 0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
+ 0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
+ 0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
+ 0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+ 0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
+ 0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
+ 0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
+ 0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
+ 0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
+ 0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+ {
+ .y_address = DC_FRAMEBUFFER_ADDRESS,
+ .u_address = DC_FRAMEBUFFER_U_ADDRESS,
+ .v_address = DC_FRAMEBUFFER_V_ADDRESS,
+ .y_stride = DC_FRAMEBUFFER_STRIDE,
+ .u_stride = DC_FRAMEBUFFER_U_STRIDE,
+ .v_stride = DC_FRAMEBUFFER_V_STRIDE,
+ .size = DC_FRAMEBUFFER_SIZE,
+ .top_left = DC_FRAMEBUFFER_TOP_LEFT,
+ .bottom_right = DC_FRAMEBUFFER_BOTTOM_RIGHT,
+ .scale_factor_x = DC_FRAMEBUFFER_SCALE_FACTOR_X,
+ .scale_factor_y = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+ .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+ .h_filter_coef_data = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+ .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+ .v_filter_coef_data = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+ .init_offset = DC_FRAMEBUFFER_INIT_OFFSET,
+ .color_key = DC_FRAMEBUFFER_COLOR_KEY,
+ .color_key_high = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+ .clear_value = DC_FRAMEBUFFER_CLEAR_VALUE,
+ .color_table_index = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+ .color_table_data = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+ .scale_config = DC_FRAMEBUFFER_SCALE_CONFIG,
+ .water_mark = DC_FRAMEBUFFER_WATER_MARK,
+ .degamma_index = DC_FRAMEBUFFER_DEGAMMA_INDEX,
+ .degamma_data = DC_FRAMEBUFFER_DEGAMMA_DATA,
+ .degamma_ex_data = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+ .src_global_color = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
+ .dst_global_color = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
+ .blend_config = DC_FRAMEBUFFER_BLEND_CONFIG,
+ .roi_origin = DC_FRAMEBUFFER_ROI_ORIGIN,
+ .roi_size = DC_FRAMEBUFFER_ROI_SIZE,
+ .YUVToRGBCoef0 = DC_FRAMEBUFFER_YUVTORGB_COEF0,
+ .YUVToRGBCoef1 = DC_FRAMEBUFFER_YUVTORGB_COEF1,
+ .YUVToRGBCoef2 = DC_FRAMEBUFFER_YUVTORGB_COEF2,
+ .YUVToRGBCoef3 = DC_FRAMEBUFFER_YUVTORGB_COEF3,
+ .YUVToRGBCoef4 = DC_FRAMEBUFFER_YUVTORGB_COEF4,
+ .YUVToRGBCoefD0 = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+ .YUVToRGBCoefD1 = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+ .YUVToRGBCoefD2 = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+ .YClampBound = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+ .UVClampBound = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+ .RGBToRGBCoef0 = DC_FRAMEBUFFER_RGBTORGB_COEF0,
+ .RGBToRGBCoef1 = DC_FRAMEBUFFER_RGBTORGB_COEF1,
+ .RGBToRGBCoef2 = DC_FRAMEBUFFER_RGBTORGB_COEF2,
+ .RGBToRGBCoef3 = DC_FRAMEBUFFER_RGBTORGB_COEF3,
+ .RGBToRGBCoef4 = DC_FRAMEBUFFER_RGBTORGB_COEF4,
+ },
+ {
+ .y_address = DC_OVERLAY_ADDRESS,
+ .u_address = DC_OVERLAY_U_ADDRESS,
+ .v_address = DC_OVERLAY_V_ADDRESS,
+ .y_stride = DC_OVERLAY_STRIDE,
+ .u_stride = DC_OVERLAY_U_STRIDE,
+ .v_stride = DC_OVERLAY_V_STRIDE,
+ .size = DC_OVERLAY_SIZE,
+ .top_left = DC_OVERLAY_TOP_LEFT,
+ .bottom_right = DC_OVERLAY_BOTTOM_RIGHT,
+ .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
+ .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
+ .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+ .h_filter_coef_data = DC_OVERLAY_H_FILTER_COEF_DATA,
+ .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+ .v_filter_coef_data = DC_OVERLAY_V_FILTER_COEF_DATA,
+ .init_offset = DC_OVERLAY_INIT_OFFSET,
+ .color_key = DC_OVERLAY_COLOR_KEY,
+ .color_key_high = DC_OVERLAY_COLOR_KEY_HIGH,
+ .clear_value = DC_OVERLAY_CLEAR_VALUE,
+ .color_table_index = DC_OVERLAY_COLOR_TABLE_INDEX,
+ .color_table_data = DC_OVERLAY_COLOR_TABLE_DATA,
+ .scale_config = DC_OVERLAY_SCALE_CONFIG,
+ .water_mark = DC_OVERLAY_WATER_MARK,
+ .degamma_index = DC_OVERLAY_DEGAMMA_INDEX,
+ .degamma_data = DC_OVERLAY_DEGAMMA_DATA,
+ .degamma_ex_data = DC_OVERLAY_DEGAMMA_EX_DATA,
+ .src_global_color = DC_OVERLAY_SRC_GLOBAL_COLOR,
+ .dst_global_color = DC_OVERLAY_DST_GLOBAL_COLOR,
+ .blend_config = DC_OVERLAY_BLEND_CONFIG,
+ .roi_origin = DC_OVERLAY_ROI_ORIGIN,
+ .roi_size = DC_OVERLAY_ROI_SIZE,
+ .YUVToRGBCoef0 = DC_OVERLAY_YUVTORGB_COEF0,
+ .YUVToRGBCoef1 = DC_OVERLAY_YUVTORGB_COEF1,
+ .YUVToRGBCoef2 = DC_OVERLAY_YUVTORGB_COEF2,
+ .YUVToRGBCoef3 = DC_OVERLAY_YUVTORGB_COEF3,
+ .YUVToRGBCoef4 = DC_OVERLAY_YUVTORGB_COEF4,
+ .YUVToRGBCoefD0 = DC_OVERLAY_YUVTORGB_COEFD0,
+ .YUVToRGBCoefD1 = DC_OVERLAY_YUVTORGB_COEFD1,
+ .YUVToRGBCoefD2 = DC_OVERLAY_YUVTORGB_COEFD2,
+ .YClampBound = DC_OVERLAY_Y_CLAMP_BOUND,
+ .UVClampBound = DC_OVERLAY_UV_CLAMP_BOUND,
+ .RGBToRGBCoef0 = DC_OVERLAY_RGBTORGB_COEF0,
+ .RGBToRGBCoef1 = DC_OVERLAY_RGBTORGB_COEF1,
+ .RGBToRGBCoef2 = DC_OVERLAY_RGBTORGB_COEF2,
+ .RGBToRGBCoef3 = DC_OVERLAY_RGBTORGB_COEF3,
+ .RGBToRGBCoef4 = DC_OVERLAY_RGBTORGB_COEF4,
+ },
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+static const u32 mmu_reg_base = SE_MMU_REG_BASE;
+
+static const struct dc_hw_mmu_reg dc_mmu_reg = {
+ .mmu_config = SE_MMU_REG_CONFIG,
+ .mmu_control = SE_MMU_REG_CONTROL,
+ .table_array_size = SE_MMU_REG_TABLE_ARRAY_SIZE,
+ .safe_non_secure = SE_MMU_REG_SAFE_NON_SECUR,
+ .safe_secure = SE_MMU_REG_SAFE_SECURE,
+ .safe_ex = SE_MMU_REG_SAFE_EXT_ADDRESS,
+ .context_pd_entry = SE_MMU_REG_CONTEXT_PD,
+};
+#endif
+
+static const u32 primary_overlay_format0[] = {
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_XBGR4444,
+ DRM_FORMAT_RGBX4444,
+ DRM_FORMAT_BGRX4444,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_ABGR4444,
+ DRM_FORMAT_RGBA4444,
+ DRM_FORMAT_BGRA4444,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_RGBX5551,
+ DRM_FORMAT_BGRX5551,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_RGBA5551,
+ DRM_FORMAT_BGRA5551,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_YVU420,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV21,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV61,
+ DRM_FORMAT_P010,
+};
+
+static const u32 primary_overlay_format1[] = {
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_XBGR4444,
+ DRM_FORMAT_RGBX4444,
+ DRM_FORMAT_BGRX4444,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_ABGR4444,
+ DRM_FORMAT_RGBA4444,
+ DRM_FORMAT_BGRA4444,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_RGBX5551,
+ DRM_FORMAT_BGRX5551,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_RGBA5551,
+ DRM_FORMAT_BGRA5551,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_YVU420,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV21,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV61,
+ DRM_FORMAT_P010,
+ DRM_FORMAT_YUV444,
+};
+
+static const u32 cursor_formats[] = {
+ DRM_FORMAT_ARGB8888
+};
+
+static const u64 format_modifier0[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
+#ifdef CONFIG_VERISILICON_DEC
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_8X4,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_4X8,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_256X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_128X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_64X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_16X8,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_32X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_32X8,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+#endif
+ DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 format_modifier1[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
+ fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
+ fourcc_mod_vs_custom_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
+ DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+#ifdef CONFIG_VERISILICON_DEC
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_256X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_128X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_64X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+ fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_32X1,
+ DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+#endif
+ DRM_FORMAT_MOD_INVALID
+};
+
+#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
+
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
+ {
+ /* DC_REV_0 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE_0,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 0,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE_0,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 1,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_1",
+ .id = OVERLAY_PLANE_1,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+ .modifiers = secondary_format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = 0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 2,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Primary_1",
+ .id = PRIMARY_PLANE_1,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 3,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_2",
+ .id = OVERLAY_PLANE_2,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 4,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_3",
+ .id = OVERLAY_PLANE_3,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+ .modifiers = secondary_format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = 0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 5,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE_0,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ {
+ .name = "Cursor_1",
+ .id = CURSOR_PLANE_1,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ },
+ {
+ /* DC_REV_1 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE_0,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 0,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE_0,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 1,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Primary_1",
+ .id = PRIMARY_PLANE_1,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 2,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_2",
+ .id = OVERLAY_PLANE_2,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format0),
+ .formats = primary_overlay_format0,
+ .num_modifiers = ARRAY_SIZE(format_modifier0),
+ .modifiers = format_modifier0,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 3,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE_0,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ {
+ .name = "Cursor_1",
+ .id = CURSOR_PLANE_1,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ },
+ {
+ /* DC_REV_2 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE_0,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(format_modifier1),
+ .modifiers = format_modifier1,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 0,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE_0,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(format_modifier1),
+ .modifiers = format_modifier1,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 1,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_1",
+ .id = OVERLAY_PLANE_1,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+ .modifiers = secondary_format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = 0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 2,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Primary_1",
+ .id = PRIMARY_PLANE_1,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(format_modifier1),
+ .modifiers = format_modifier1,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 3,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_2",
+ .id = OVERLAY_PLANE_2,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(format_modifier1),
+ .modifiers = format_modifier1,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ .zpos = 4,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Overlay_3",
+ .id = OVERLAY_PLANE_3,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_format1),
+ .formats = primary_overlay_format1,
+ .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+ .modifiers = secondary_format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = 0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 5,
+ .watermark = true,
+ .color_mgmt = true,
+ .roi = true,
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE_0,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ {
+ .name = "Cursor_1",
+ .id = CURSOR_PLANE_1,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .num_modifiers = 0,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 64,
+ .max_height = 64,
+ .rotation = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ .zpos = 255,
+ .watermark = false,
+ .color_mgmt = false,
+ .roi = false,
+ },
+ },
+};
+
+static const struct vs_dc_info dc_info[] = {
+ {
+ /* DC_REV_0 */
+ .name = "DC8200",
+ .panel_num = 2,
+ .plane_num = 8,
+ .planes = dc_hw_planes[DC_REV_0],
+ .layer_num = 6,
+ .max_bpc = 10,
+ .color_formats = DRM_COLOR_FORMAT_RGB444 |
+ DRM_COLOR_FORMAT_YCBCR444 |
+ DRM_COLOR_FORMAT_YCBCR422 |
+ DRM_COLOR_FORMAT_YCBCR420,
+ .gamma_size = GAMMA_EX_SIZE,
+ .gamma_bits = 12,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ .background = true,
+ .panel_sync = true,
+ .cap_dec = true,
+ },
+ {
+ /* DC_REV_1 */
+ .name = "DC8200",
+ .panel_num = 2,
+ .plane_num = 6,
+ .planes = dc_hw_planes[DC_REV_1],
+ .layer_num = 4,
+ .max_bpc = 10,
+ .color_formats = DRM_COLOR_FORMAT_RGB444 |
+ DRM_COLOR_FORMAT_YCBCR444 |
+ DRM_COLOR_FORMAT_YCBCR422 |
+ DRM_COLOR_FORMAT_YCBCR420,
+ .gamma_size = GAMMA_EX_SIZE,
+ .gamma_bits = 12,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ .background = true,
+ .panel_sync = true,
+ .cap_dec = true,
+ },
+ {
+ /* DC_REV_2 */
+ .name = "DC8200",
+ .panel_num = 2,
+ .plane_num = 8,
+ .planes = dc_hw_planes[DC_REV_1],
+ .layer_num = 6,
+ .max_bpc = 10,
+ .color_formats = DRM_COLOR_FORMAT_RGB444 |
+ DRM_COLOR_FORMAT_YCBCR444 |
+ DRM_COLOR_FORMAT_YCBCR422 |
+ DRM_COLOR_FORMAT_YCBCR420,
+ .gamma_size = GAMMA_EX_SIZE,
+ .gamma_bits = 12,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ .background = true,
+ .panel_sync = true,
+ .cap_dec = false,
+ },
+};
+
+static const struct dc_hw_funcs hw_func;
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+ return readl(hw->hi_base + reg);
+}
+
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+ writel(value, hw->hi_base + reg);
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+ writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+ u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+ return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+ u32 value = dc_read(hw, reg);
+
+ value &= ~clear;
+ value |= set;
+ dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+ const struct dc_hw_plane_reg *reg, u32 offset)
+{
+ u8 i;
+
+ dc_write(hw, reg->scale_config + offset, 0x33);
+ dc_write(hw, reg->init_offset + offset, 0x80008000);
+ dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
+ for (i = 0; i < H_COEF_SIZE; i++)
+ dc_write(hw, reg->h_filter_coef_data + offset, horKernel[i]);
+
+ dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
+ for (i = 0; i < V_COEF_SIZE; i++)
+ dc_write(hw, reg->v_filter_coef_data + offset, verKernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+ u32 offset, u16 *table)
+{
+ dc_write(hw, reg->RGBToRGBCoef0 + offset, table[0] | (table[1] << 16));
+ dc_write(hw, reg->RGBToRGBCoef1 + offset, table[2] | (table[3] << 16));
+ dc_write(hw, reg->RGBToRGBCoef2 + offset, table[4] | (table[5] << 16));
+ dc_write(hw, reg->RGBToRGBCoef3 + offset, table[6] | (table[7] << 16));
+ dc_write(hw, reg->RGBToRGBCoef4 + offset, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+ u32 offset, s32 *table)
+{
+ dc_write(hw, reg->YUVToRGBCoef0 + offset,
+ (0xFFFF & table[0]) | (table[1] << 16));
+ dc_write(hw, reg->YUVToRGBCoef1 + offset,
+ (0xFFFF & table[2]) | (table[3] << 16));
+ dc_write(hw, reg->YUVToRGBCoef2 + offset,
+ (0xFFFF & table[4]) | (table[5] << 16));
+ dc_write(hw, reg->YUVToRGBCoef3 + offset,
+ (0xFFFF & table[6]) | (table[7] << 16));
+ dc_write(hw, reg->YUVToRGBCoef4 + offset, table[8]);
+ dc_write(hw, reg->YUVToRGBCoefD0 + offset, table[9]);
+ dc_write(hw, reg->YUVToRGBCoefD1 + offset, table[10]);
+ dc_write(hw, reg->YUVToRGBCoefD2 + offset, table[11]);
+ dc_write(hw, reg->YClampBound + offset, table[12] | (table[13] << 16));
+ dc_write(hw, reg->UVClampBound + offset, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
+{
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
+ table[0] | (table[1] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
+ table[2] | (table[3] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
+ table[4] | (table[5] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
+ table[6] | (table[7] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+ switch (format) {
+ case FORMAT_X4R4G4B4:
+ case FORMAT_A4R4G4B4:
+ case FORMAT_X1R5G5B5:
+ case FORMAT_A1R5G5B5:
+ case FORMAT_R5G6B5:
+ case FORMAT_X8R8G8B8:
+ case FORMAT_A8R8G8B8:
+ case FORMAT_A2R10G10B10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+ const struct dc_hw_plane_reg *reg,
+ u32 offset, u16 *table)
+{
+ u16 i;
+ u32 value;
+
+ dc_write(hw, reg->degamma_index + offset, 0);
+
+ for (i = 0; i < DEGAMMA_SIZE; i++) {
+ value = table[i] | (table[i] << 16);
+ dc_write(hw, reg->degamma_data + offset, value);
+ dc_write(hw, reg->degamma_ex_data + offset, table[i]);
+ }
+}
+
+static u32 get_addr_offset(u32 id)
+{
+ u32 offset = 0;
+
+ switch(id) {
+ case PRIMARY_PLANE_1:
+ case OVERLAY_PLANE_1:
+ offset = 0x04;
+ break;
+ case OVERLAY_PLANE_2:
+ offset = 0x08;
+ break;
+ case OVERLAY_PLANE_3:
+ offset = 0x0C;
+ break;
+ default:
+ break;
+ }
+
+ return offset;
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+ u8 i, id, panel_num, layer_num;
+ u32 offset;
+ u32 revision = hi_read(hw, DC_HW_REVISION);
+ u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+ const struct dc_hw_plane_reg *reg;
+
+ switch (revision) {
+ case 0x5720:
+ hw->rev = DC_REV_0;
+ break;
+ case 0x5721:
+ switch (cid) {
+ case 0x30B:
+ hw->rev = DC_REV_1;
+ break;
+ case 0x310:
+ hw->rev = DC_REV_2;
+ break;
+ default:
+ hw->rev = DC_REV_0;
+ break;
+ }
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
+ hw->func = (struct dc_hw_funcs *)&hw_func;
+
+ layer_num = hw->info->layer_num;
+ for (i = 0; i < layer_num; i++) {
+ id = hw->info->planes[i].id;
+ offset = get_addr_offset(id);
+ if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+ reg = &dc_plane_reg[0];
+ else
+ reg = &dc_plane_reg[1];
+
+ load_default_filter(hw, reg, offset);
+ load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
+ }
+
+ panel_num = hw->info->panel_num;
+ for (i = 0; i < panel_num; i++) {
+ offset = i << 2;
+ load_rgb_to_yuv(hw, offset, RGB2YUV);
+ dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
+
+ offset = i ? DC_CURSOR_OFFSET : 0;
+ dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
+ dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
+ }
+
+ dc_write(hw, DC_FRAMEBUFFER_WATER_MARK, 0x3000);
+ dc_write(hw, DC_FRAMEBUFFER_WATER_MARK + 0x4, 0x3000);
+ dc_write(hw, DC_OVERLAY_WATER_MARK, 0x3000);
+ dc_write(hw, DC_OVERLAY_WATER_MARK + 0x4, 0x3000);
+ dc_write(hw, DC_OVERLAY_WATER_MARK + 0x8, 0x3000);
+ dc_write(hw, DC_OVERLAY_WATER_MARK + 0xc, 0x3000);
+ dc_write(hw, DC_QOS_CONFIG, 0xc0);
+
+ dc_write(hw, DC_CLK_GATTING, 0x0);
+ dc_write(hw, DC_CLK_GATTING + 0x4, 0x0);
+
+ return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+ /* Nothing to do */
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+ struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+ struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
+ struct dc_hw_plane *plane = &hw->plane[id];
+
+ if (plane) {
+ if (fb) {
+ if (fb->enable == false)
+ plane->fb.enable = false;
+ else
+ memcpy(&plane->fb, fb,
+ sizeof(*fb) - sizeof(fb->dirty));
+ plane->fb.dirty = true;
+ }
+ if (scale) {
+ memcpy(&plane->scale, scale,
+ sizeof(*scale) - sizeof(scale->dirty));
+ plane->scale.dirty = true;
+ }
+ if (pos) {
+ memcpy(&plane->pos, pos,
+ sizeof(*pos) - sizeof(pos->dirty));
+ plane->pos.dirty = true;
+ }
+ if (blend) {
+ memcpy(&plane->blend, blend,
+ sizeof(*blend) - sizeof(blend->dirty));
+ plane->blend.dirty = true;
+ }
+ }
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
+{
+ struct dc_hw_plane *plane = &hw->plane[id];
+
+ if (plane) {
+ if (hw->info->planes[id].degamma_size) {
+ plane->degamma.mode = mode;
+ plane->degamma.dirty = true;
+ } else {
+ plane->degamma.dirty = false;
+ }
+ }
+}
+
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
+{
+ struct dc_hw_plane *plane = &hw->plane[id];
+
+ if (plane) {
+ memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+ plane->roi.dirty = true;
+ }
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+ struct dc_hw_colorkey *colorkey)
+{
+ struct dc_hw_plane *plane = &hw->plane[id];
+
+ if (plane) {
+ memcpy(&plane->colorkey, colorkey,
+ sizeof(*colorkey) - sizeof(colorkey->dirty));
+ plane->colorkey.dirty = true;
+ }
+}
+
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
+{
+ memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
+ hw->qos.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
+{
+ memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+ hw->cursor[id].dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+ u16 r, u16 g, u16 b)
+{
+ if (index >= hw->info->gamma_size)
+ return;
+
+ hw->gamma[id].gamma[index][0] = r;
+ hw->gamma[id].gamma[index][1] = g;
+ hw->gamma[id].gamma[index][2] = b;
+ hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
+{
+ hw->gamma[id].enable = enable;
+ hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch)
+{
+ dc_write(hw, 0x14F0, addr);
+ dc_write(hw, 0x14E8, addr);
+ dc_write(hw, 0x1500, pitch);
+ dc_write(hw, 0x14F8, 0x30000);
+}
+
+void dc_hw_disable_dump(struct dc_hw *hw)
+{
+ dc_write(hw, 0x14F8, 0x00);
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ u8 id = display->id;
+
+ memcpy(&hw->display[id], display, sizeof(*display));
+
+ hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+ if (enable)
+ hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
+ else
+ hi_write(hw, AQ_INTR_ENBL, 0);
+}
+
+void dc_hw_enable_irq(struct dc_hw *hw, u32 irq, bool enable)
+{
+ u32 intr_enbl = hi_read(hw, AQ_INTR_ENBL);
+
+ intr_enbl = enable ? intr_enbl | BIT(irq) : intr_enbl & ~BIT(irq);
+
+ hi_write(hw, AQ_INTR_ENBL, intr_enbl);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+ return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+ return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+ u32 i, offset;
+ u8 id, layer_num = hw->info->layer_num;
+ u8 panel_num = hw->info->panel_num;
+
+ for (i = 0; i< layer_num; i++) {
+ id = hw->info->planes[i].id;
+ offset = get_addr_offset(id);
+ if (enable) {
+ if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
+ } else {
+ if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
+ }
+ }
+
+ for (i = 0; i< panel_num; i++) {
+ offset = i << 2;
+ if (enable)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
+ else
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
+ }
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
+{
+ if (out <= OUT_DP)
+ hw->out[id] = out;
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+ u8 panel_num = hw->info->panel_num;
+ u16 i, j;
+ u32 value;
+
+ for (j = 0; j < panel_num; j++) {
+ if (hw->gamma[j].dirty) {
+ if (hw->gamma[j].enable) {
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
+ for (i = 0; i < GAMMA_EX_SIZE; i++) {
+ value = hw->gamma[j].gamma[i][2] |
+ (hw->gamma[j].gamma[i][1] << 12);
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
+ hw->gamma[j].gamma[i][0]);
+ }
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+ BIT(13), 0);
+ } else {
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+ 0, BIT(13));
+ }
+ hw->gamma[j].dirty = false;
+ }
+ }
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+ struct dc_hw_plane *plane;
+ const struct dc_hw_plane_reg *reg;
+ bool primary = false;
+ u8 id, layer_num = hw->info->layer_num;
+ u32 i, offset;
+
+ for (i = 0; i < layer_num; i++) {
+ plane = &hw->plane[i];
+ id = hw->info->planes[i].id;
+ offset = get_addr_offset(id);
+ if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+ reg = &dc_plane_reg[0];
+ primary = true;
+ } else {
+ reg = &dc_plane_reg[1];
+ primary = false;
+ }
+
+ if (plane->fb.dirty) {
+ if (plane->fb.enable) {
+ dc_write(hw, reg->y_address + offset,
+ plane->fb.y_address);
+ dc_write(hw, reg->u_address + offset,
+ plane->fb.u_address);
+ dc_write(hw, reg->v_address + offset,
+ plane->fb.v_address);
+ dc_write(hw, reg->y_stride + offset,
+ plane->fb.y_stride);
+ dc_write(hw, reg->u_stride + offset,
+ plane->fb.u_stride);
+ dc_write(hw, reg->v_stride + offset,
+ plane->fb.v_stride);
+ dc_write(hw, reg->size + offset,
+ plane->fb.width |
+ (plane->fb.height << 15));
+
+ if (plane->fb.clear_enable)
+ dc_write(hw, reg->clear_value + offset,
+ plane->fb.clear_value);
+ }
+
+ if (primary) {
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+ (plane->fb.format << 26) |
+ (plane->fb.uv_swizzle << 25) |
+ (plane->fb.swizzle << 23) |
+ (plane->fb.tile_mode << 17) |
+ (plane->fb.yuv_color_space << 14) |
+ (plane->fb.rotation << 11) |
+ (plane->fb.clear_enable << 8),
+ (0x1F << 26) |
+ BIT(25) |
+ (0x03 << 23) |
+ (0x1F << 17) |
+ (0x07 << 14) |
+ (0x07 << 11) |
+ BIT(8));
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+ (plane->fb.dec_enable << 1) |
+ (plane->fb.enable << 13) |
+ (plane->fb.zpos << 16) |
+ (plane->fb.display_id << 19),
+ BIT(1) | BIT(13) |(0x07 << 16) | BIT(19));
+ } else {
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+ (plane->fb.dec_enable << 27) |
+ (plane->fb.clear_enable << 25) |
+ (plane->fb.enable << 24) |
+ (plane->fb.format << 16) |
+ (plane->fb.uv_swizzle << 15) |
+ (plane->fb.swizzle << 13) |
+ (plane->fb.tile_mode << 8) |
+ (plane->fb.yuv_color_space << 5) |
+ (plane->fb.rotation << 2),
+ BIT(27) |
+ BIT(25) |
+ BIT(24) |
+ (0x1F << 16) |
+ BIT(15) |
+ (0x03 << 13) |
+ (0x1F << 8) |
+ (0x07 << 5) |
+ (0x07 << 2));
+ dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+ plane->fb.zpos | (plane->fb.display_id << 3),
+ 0x07| BIT(3));
+ }
+ plane->fb.dirty = false;
+ }
+
+ if (plane->scale.dirty) {
+ if (plane->scale.enable) {
+ dc_write(hw, reg->scale_factor_x + offset,
+ plane->scale.scale_factor_x);
+ dc_write(hw, reg->scale_factor_y + offset,
+ plane->scale.scale_factor_y);
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG + offset,
+ BIT(22), 0);
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_SCALE_CONFIG + offset,
+ BIT(8), 0);
+ } else {
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG + offset,
+ 0, BIT(22));
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_SCALE_CONFIG + offset,
+ 0, BIT(8));
+ }
+ plane->scale.dirty = false;
+ }
+
+ if (plane->pos.dirty) {
+ dc_write(hw, reg->top_left + offset,
+ plane->pos.start_x |
+ (plane->pos.start_y << 15));
+ dc_write(hw, reg->bottom_right + offset,
+ plane->pos.end_x |
+ (plane->pos.end_y << 15));
+ plane->pos.dirty = false;
+ }
+
+ if (plane->blend.dirty) {
+ dc_write(hw, reg->src_global_color + offset,
+ plane->blend.alpha << 24);
+ dc_write(hw, reg->dst_global_color + offset,
+ plane->blend.alpha << 24);
+ switch (plane->blend.blend_mode) {
+ case BLEND_PREMULTI:
+ dc_write(hw, reg->blend_config + offset, 0x3450);
+ break;
+ case BLEND_COVERAGE:
+ dc_write(hw, reg->blend_config + offset, 0x3950);
+ break;
+ case BLEND_PIXEL_NONE:
+ dc_write(hw, reg->blend_config + offset, 0x3548);
+ break;
+ default:
+ break;
+ }
+ plane->blend.dirty = false;
+ }
+
+ if (plane->colorkey.dirty) {
+ dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
+ dc_write(hw, reg->color_key_high + offset,
+ plane->colorkey.colorkey_high);
+
+ if (primary)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+ plane->colorkey.transparency << 9, 0x03 << 9);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+ plane->colorkey.transparency, 0x03);
+
+ plane->colorkey.dirty = false;
+ }
+
+ if (plane->roi.dirty) {
+ if (plane->roi.enable) {
+ dc_write(hw, reg->roi_origin + offset,
+ plane->roi.x | (plane->roi.y << 16));
+ dc_write(hw, reg->roi_size + offset,
+ plane->roi.width | (plane->roi.height << 16));
+ if (primary)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+ BIT(0), 0);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+ BIT(22), 0);
+ } else {
+ if (primary)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+ 0, BIT(0));
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+ 0, BIT(22));
+ }
+ plane->roi.dirty = false;
+ }
+ }
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+ struct dc_hw_plane *plane;
+ const struct dc_hw_plane_reg *reg;
+ bool primary = false;
+ u8 id, layer_num = hw->info->layer_num;
+ u32 i, offset;
+
+ for (i = 0; i < layer_num; i++) {
+ plane = &hw->plane[i];
+ id = hw->info->planes[i].id;
+ offset = get_addr_offset(id);
+ if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+ reg = &dc_plane_reg[0];
+ primary = true;
+ } else {
+ reg = &dc_plane_reg[1];
+ primary = false;
+ }
+
+ if (plane->fb.dirty) {
+ if (is_rgb(plane->fb.format)) {
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX + offset,
+ 0, BIT(8));
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_CONFIG + offset,
+ 0, BIT(30));
+ } else {
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX + offset,
+ BIT(8), BIT(6));
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_CONFIG + offset,
+ BIT(30), BIT(29));
+ switch (plane->fb.yuv_color_space) {
+ case COLOR_SPACE_601:
+ load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
+ break;
+ case COLOR_SPACE_709:
+ load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
+ break;
+ case COLOR_SPACE_2020:
+ load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (plane->degamma.dirty) {
+ switch (plane->degamma.mode) {
+ case VS_DEGAMMA_DISABLE:
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX + offset,
+ 0, BIT(5));
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_CONFIG + offset,
+ 0, BIT(28));
+ break;
+ case VS_DEGAMMA_BT709:
+ load_degamma_table(hw, reg, offset, DEGAMMA_709);
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX + offset,
+ BIT(5), 0);
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_CONFIG + offset,
+ BIT(28), 0);
+ break;
+ case VS_DEGAMMA_BT2020:
+ load_degamma_table(hw, reg, offset, DEGAMMA_2020);
+ if (primary)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX + offset,
+ BIT(5), 0);
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_CONFIG + offset,
+ BIT(28), 0);
+ break;
+ default:
+ break;
+ }
+ plane->degamma.dirty = false;
+ }
+ }
+ plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ u8 id = display->id;
+ u32 i, dpi_cfg, offset = id << 2;
+ u32 loffset, config, config_ex;
+
+ if (hw->display[id].enable) {
+ switch (display->bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dpi_cfg = 0;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ dpi_cfg = 3;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ dpi_cfg = 4;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dpi_cfg = 5;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ dpi_cfg = 6;
+ break;
+ default:
+ dpi_cfg = 5;
+ break;
+ }
+ dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
+
+ if (id == 0)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+ else
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+
+ dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
+ (hw->display[id].h_total << 16));
+ dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+ hw->display[id].h_sync_start |
+ (hw->display[id].h_sync_end << 15) |
+ (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
+ BIT(30));
+ dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
+ (hw->display[id].v_total << 16));
+ dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+ hw->display[id].v_sync_start |
+ (hw->display[id].v_sync_end << 15) |
+ (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
+ BIT(30));
+
+ if (hw->info->pipe_sync) {
+ switch (display->sync_mode) {
+ case VS_SINGLE_DC:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+ 0, BIT(3) | BIT(4));
+ break;
+ case VS_MULTI_DC_PRIMARY:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(3) | BIT(4), 0);
+ break;
+ case VS_MULTI_DC_SECONDARY:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(3), BIT(4));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (hw->info->background)
+ dc_write(hw, DC_FRAMEBUFFER_BG_COLOR + offset,
+ hw->display[id].bg_color);
+
+ if (hw->display[id].dither_enable) {
+ dc_write(hw, DC_DISPLAY_DITHER_TABLE_LOW + offset,
+ DC_DISPLAY_DITHERTABLE_LOW);
+ dc_write(hw, DC_DISPLAY_DITHER_TABLE_HIGH + offset,
+ DC_DISPLAY_DITHERTABLE_HIGH);
+ dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, BIT(31));
+ } else {
+ dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, 0);
+ }
+
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
+ if (hw->display[id].sync_enable)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(2) | BIT(3), 0);
+ else if (id == 0)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
+ else
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
+ } else {
+ /* disable layers enabled for 'id' display which is required
+ * by DPU IP to avoid possible dma drop issue
+ */
+ for (i = 0; i < hw->info->layer_num; i++) {
+ loffset = get_addr_offset(i);
+
+ if (i == PRIMARY_PLANE_0 || i == PRIMARY_PLANE_1) {
+ config_ex = dc_read(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset);
+
+ if (!!(config_ex & BIT(19)) == id && config_ex & BIT(13)) {
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset, 0, BIT(13));
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset, BIT(12), 0);
+ }
+ } else {
+ config_ex = dc_read(hw, DC_OVERLAY_CONFIG_EX + loffset);
+
+ if (!!(config_ex & BIT(3)) == id) {
+ config = dc_read(hw, DC_OVERLAY_CONFIG + loffset);
+
+ if (config & BIT(24)) {
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + loffset, 0, BIT(24));
+ dc_set_clear(hw, DC_OVERLAY_CONFIG + loffset, BIT(31), 0);
+ }
+ }
+ }
+ }
+ mdelay(20);
+
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
+ if (id == 0)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+ else
+ dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+ }
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ u8 id = display->id;
+ u32 dp_cfg, offset = id << 2;
+ bool is_yuv = false;
+
+ if (hw->display[id].enable && hw->out[id] == OUT_DP) {
+ switch (display->bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dp_cfg = 0;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ dp_cfg = 1;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dp_cfg = 2;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ dp_cfg = 3;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ dp_cfg = 2 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ dp_cfg = 4 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ dp_cfg = 8 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ dp_cfg = 10 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ dp_cfg = 12 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ dp_cfg = 13 << 4;
+ is_yuv = true;
+ break;
+ default:
+ dp_cfg = 2;
+ break;
+ }
+ if (is_yuv)
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
+ else
+ dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
+ dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
+ }
+
+ if (hw->out[id] == OUT_DPI)
+ dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
+
+ setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func= {
+ .gamma = &gamma_ex_commit,
+ .plane = &plane_ex_commit,
+ .display = setup_display_ex,
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+ u32 i, offset = 0;
+ u8 plane_num = hw->info->plane_num;
+ u8 layer_num = hw->info->layer_num;
+ u8 cursor_num = plane_num - layer_num;
+
+ hw->func->gamma(hw);
+ hw->func->plane(hw);
+
+ for (i = 0; i < cursor_num; i++) {
+ if (hw->cursor[i].dirty) {
+ offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
+ if (hw->cursor[i].enable) {
+ dc_write(hw, DC_CURSOR_ADDRESS + offset,
+ hw->cursor[i].address);
+ dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
+ (hw->cursor[i].y << 16));
+ dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+ (hw->cursor[i].hot_x << 16) |
+ (hw->cursor[i].hot_y << 8) |
+ (hw->cursor[i].size << 5) |
+ BIT(3) | BIT(2) | 0x02,
+ (0xFF << 16) |
+ (0xFF << 8) |
+ (0x07 << 5) | 0x1F);
+ } else {
+ dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
+ }
+ hw->cursor[i].dirty = false;
+ }
+ }
+
+ if (hw->qos.dirty) {
+ dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
+ hw->qos.low_value, 0xFF);
+ hw->qos.dirty = false;
+ }
+}
+
+#ifdef CONFIG_VERISILICON_DEC
+void dc_hw_dec_init(struct dc_hw *hw)
+{
+ u32 config = 0;
+
+ config = DEC_CONTROL_RESET & (~COMPRESSION_DISABLE);
+ dc_write(hw, DEC_CONTROL, config | FLUSH_ENABLE);
+
+ config = DEC_CONTROL_EX2_RESET &
+ (~TILE_STATUS_READ_ID_MASK) &
+ (~TILE_STATUS_READ_ID_H_MASK) &
+ (~DISABLE_HW_DEC_FLUSH);
+ dc_write(hw, DEC_CONTROL_EX2,
+ config | (TILE_STATUS_READ_ID_H << 22) |
+ TILE_STATUS_READ_ID);
+
+ config = DEC_CONTROL_EX_RESET &
+ (~WRITE_MISS_POLICY_MASK) &
+ (~READ_MISS_POLICY_MASK);
+ dc_write(hw, DEC_CONTROL_EX, config | (WRITE_MISS_POLICY1 << 19));
+}
+
+void dc_hw_dec_stream_set(struct dc_hw *hw,u32 main_base_addr,
+ u32 ts_base_addr, u8 tile_mode, u8 align_mode,
+ u8 format, u8 depth, u8 stream_id)
+{
+ u32 offset = stream_id << 2;
+
+ dc_set_clear(hw, DEC_READ_CONFIG + offset,
+ (tile_mode << 25) |
+ (align_mode << 16) |
+ (format << 3) |
+ COMPRESSION_EN,
+ TILE_MODE_MASK |
+ COMPRESSION_ALIGN_MODE_MASK |
+ COMPRESSION_FORMAT_MASK);
+
+ dc_set_clear(hw, DEC_READ_EX_CONFIG + offset,
+ (depth << 16), BIT_DEPTH_MASK);
+
+ dc_write(hw, DEC_READ_BUFFER_BASE + offset, main_base_addr);
+ dc_write(hw, DEC_READ_BUFFER_END + offset, ts_base_addr - 128);
+ dc_write(hw, DEC_READ_CACHE_BASE + offset, ts_base_addr);
+}
+
+void dc_hw_dec_stream_disable(struct dc_hw *hw, u8 stream_id)
+{
+ u32 offset = stream_id << 2;
+
+ dc_write(hw, DEC_READ_CONFIG + offset, DEC_READ_CONFIG_RESET);
+ dc_write(hw, DEC_READ_BUFFER_BASE + offset, 0xFFFFFFFF);
+ dc_write(hw, DEC_READ_BUFFER_END + offset, 0xFFFFFFFF);
+}
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+static u32 mmu_read(struct dc_hw *hw, u32 reg)
+{
+ return readl(hw->mmu_base + reg - mmu_reg_base);
+}
+
+static void mmu_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+ writel(value, hw->mmu_base + reg - mmu_reg_base);
+}
+
+static void mmu_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+ u32 value = mmu_read(hw, reg);
+
+ value &= ~clear;
+ value |= set;
+ mmu_write(hw, reg, value);
+}
+
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu)
+{
+ const struct dc_hw_mmu_reg *reg;
+ u32 mtlb = 0, ext_mtlb = 0;
+ u32 safe_addr = 0, ext_safe_addr = 0;
+ u32 config = 0;
+
+ reg = &dc_mmu_reg;
+
+ mtlb = (u32)(mmu->mtlb_physical & 0xFFFFFFFF);
+ ext_mtlb = (u32)(mmu->mtlb_physical >> 32);
+
+ /* more than 40bit physical address */
+ if (ext_mtlb & 0xFFFFFF00) {
+ pr_err("Mtlb address out of range.\n");
+ return -EFAULT;
+ }
+
+ config = (ext_mtlb << 20) | (mtlb >> 12);
+ if (mmu->mode == MMU_MODE_1K)
+ mmu_set_clear(hw, reg->context_pd_entry,
+ (config << 4) |BIT(0),
+ (0xFFFFFFF << 4) | (0x07));
+ else
+ mmu_set_clear(hw, reg->context_pd_entry,
+ (config << 4),
+ (0xFFFFFFF << 4) | (0x07));
+
+ safe_addr = (u32)(mmu->safe_page_physical & 0xFFFFFFFF);
+ ext_safe_addr = (u32)(mmu->safe_page_physical >> 32);
+
+ if ((safe_addr & 0x3F) || (ext_safe_addr & 0xFFFFFF00)) {
+ pr_err("Invalid safe_address.\n");
+ return -EFAULT;
+ }
+
+ mmu_write(hw, reg->table_array_size, 1);
+ mmu_write(hw, reg->safe_secure, safe_addr);
+ mmu_write(hw, reg->safe_non_secure, safe_addr);
+
+ mmu_set_clear(hw, reg->safe_ex,
+ (ext_safe_addr << 16) |ext_safe_addr,
+ BIT(31) |(0xFF << 16) | BIT(15) | 0xFF);
+
+ /* mmu configuration for ree driver */
+ mmu_write(hw, reg->mmu_control, BIT(5) | BIT(0));
+
+ mmu_write(hw, SE_MMU_REG_INTR_ENBL, 0xFFFFFFFF);
+
+ return 0;
+}
+
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable)
+{
+ if (!hw->info->mmu_prefetch)
+ return;
+
+ if (enable)
+ dc_write(hw, DC_MMU_PREFETCH, BIT(0));
+ else
+ dc_write(hw, DC_MMU_PREFETCH, 0);
+}
+
+void dc_hw_mmu_flush(struct dc_hw *hw)
+{
+ const struct dc_hw_mmu_reg *reg = &dc_mmu_reg;
+ u32 value = mmu_read(hw, reg->mmu_config);
+
+ mmu_write(hw, reg->mmu_config, value | BIT(4));
+ mmu_write(hw, reg->mmu_config, value);
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+#define AQ_INTR_ACKNOWLEDGE 0x0010
+#define AQ_INTR_ENBL 0x0014
+#define DC_HW_REVISION 0x0024
+#define DC_HW_CHIP_CID 0x0030
+
+#define DC_REG_BASE 0x0800
+#define DC_REG_RANGE 0x2000
+#define DC_SEC_REG_OFFSET 0x100000
+
+#define DC_FRAMEBUFFER_CONFIG 0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT 0x24D8
+#define DC_FRAMEBUFFER_BOTTOM_RIGHT 0x24E0
+#define DC_FRAMEBUFFER_ADDRESS 0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS 0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS 0x1538
+#define DC_FRAMEBUFFER_STRIDE 0x1408
+#define DC_FRAMEBUFFER_U_STRIDE 0x1800
+#define DC_FRAMEBUFFER_V_STRIDE 0x1808
+#define DC_FRAMEBUFFER_SIZE 0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY 0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820
+#define DC_FRAMEBUFFER_BG_COLOR 0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8
+#define DC_FRAMEBUFFER_WATER_MARK 0x1CE8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40
+#define DC_FRAMEBUFFER_BLEND_CONFIG 0x2510
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR 0x2500
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR 0x2508
+
+#define DC_OVERLAY_CONFIG 0x1540
+#define DC_OVERLAY_CONFIG_EX 0x2540
+#define DC_OVERLAY_SCALE_CONFIG 0x1C00
+#define DC_OVERLAY_BLEND_CONFIG 0x1580
+#define DC_OVERLAY_TOP_LEFT 0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT 0x1680
+#define DC_OVERLAY_ADDRESS 0x15C0
+#define DC_OVERLAY_U_ADDRESS 0x1840
+#define DC_OVERLAY_V_ADDRESS 0x1880
+#define DC_OVERLAY_STRIDE 0x1600
+#define DC_OVERLAY_U_STRIDE 0x18C0
+#define DC_OVERLAY_V_STRIDE 0x1900
+#define DC_OVERLAY_SIZE 0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80
+#define DC_OVERLAY_INIT_OFFSET 0x1BC0
+#define DC_OVERLAY_COLOR_KEY 0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780
+#define DC_OVERLAY_CLEAR_VALUE 0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700
+#define DC_OVERLAY_ROI_ORIGIN 0x1D00
+#define DC_OVERLAY_ROI_SIZE 0x1D40
+#define DC_OVERLAY_WATER_MARK 0x1DC0
+#define DC_OVERLAY_DEGAMMA_INDEX 0x2200
+#define DC_OVERLAY_DEGAMMA_DATA 0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1 0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2 0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3 0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0
+
+#define DC_CURSOR_CONFIG 0x1468
+#define DC_CURSOR_ADDRESS 0x146C
+#define DC_CURSOR_LOCATION 0x1470
+#define DC_CURSOR_BACKGROUND 0x1474
+#define DC_CURSOR_FOREGROUND 0x1478
+#define DC_CURSOR_CLK_GATING 0x1484
+#define DC_CURSOR_CONFIG_EX 0x24E8
+#define DC_CURSOR_OFFSET 0x1080
+
+#define DC_DISPLAY_DITHER_CONFIG 0x1410
+#define DC_DISPLAY_PANEL_CONFIG 0x1418
+#define DC_DISPLAY_PANEL_CONFIG_EX 0x2518
+#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428
+#define DC_DISPLAY_H 0x1430
+#define DC_DISPLAY_H_SYNC 0x1438
+#define DC_DISPLAY_V 0x1440
+#define DC_DISPLAY_V_SYNC 0x1448
+#define DC_DISPLAY_CURRENT_LOCATION 0x1450
+#define DC_DISPLAY_GAMMA_INDEX 0x1458
+#define DC_DISPLAY_GAMMA_DATA 0x1460
+#define DC_DISPLAY_INT 0x147C
+#define DC_DISPLAY_INT_ENABLE 0x1480
+#define DC_DISPLAY_DBI_CONFIG 0x1488
+#define DC_DISPLAY_GENERAL_CONFIG 0x14B0
+#define DC_DISPLAY_DPI_CONFIG 0x14B8
+#define DC_DISPLAY_PANEL_START 0x1CCC
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8
+#define DC_DISPLAY_DP_CONFIG 0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80
+
+#define DC_CLK_GATTING 0x1A28
+#define DC_QOS_CONFIG 0x1A38
+
+#define DC_TRANSPARENCY_OPAQUE 0x00
+#define DC_TRANSPARENCY_KEY 0x02
+#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2
+
+#define GAMMA_SIZE 256
+#define GAMMA_EX_SIZE 300
+#define DEGAMMA_SIZE 260
+
+#define RGB_TO_RGB_TABLE_SIZE 9
+#define YUV_TO_RGB_TABLE_SIZE 16
+#define RGB_TO_YUV_TABLE_SIZE 12
+
+#ifdef CONFIG_VERISILICON_DEC
+/* DEC400 register */
+#define DEC_CONTROL 0x0800
+#define DEC_CONTROL_EX 0x0804
+#define DEC_CONTROL_EX2 0x0808
+
+#define DEC_READ_CONFIG 0x0880
+#define DEC_READ_EX_CONFIG 0x0900
+#define DEC_READ_BUFFER_BASE 0x0A80
+#define DEC_READ_BUFFER_END 0x0B80
+#define DEC_READ_CACHE_BASE 0x1080
+
+#define DEC_CONTROL_RESET 0x0301018A
+#define DEC_CONTROL_EX_RESET 0x00080000
+#define DEC_CONTROL_EX2_RESET 0x103FC810
+#define DEC_READ_CONFIG_RESET 0x00020000
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+#define DC_MMU_PREFETCH 0x1E98
+
+#define MMU_REG_BASE 0x0180
+#define MMU_REG_RANGE 0x700
+
+#define MMU_REG_CONFIG 0x0184
+#define MMU_REG_CONTROL 0x0388
+#define MMU_REG_TABLE_ARRAY_SIZE 0x0394
+#define MMU_REG_SAFE_NON_SECURE 0x0398
+#define MMU_REG_SAFE_SECURE 0x039C
+#define MMU_REG_SAFE_EXT_ADDRESS 0x03A0
+#define MMU_REG_CONTEXT_PD 0x03B4
+
+#define DEC_REG_CONTROL 0x0800
+#define DEC_REG_CONTROL_VALUE 0x02010188
+
+#define SE_MMU_REG_BASE 0x10010
+#define SE_MMU_REG_RANGE 0x60
+
+#define SE_MMU_REG_CONFIG 0x10010
+#define SE_MMU_REG_CONTROL 0x10024
+#define SE_MMU_REG_TABLE_ARRAY_SIZE 0x10030
+#define SE_MMU_REG_SAFE_NON_SECUR 0x10034
+#define SE_MMU_REG_SAFE_SECURE 0x10038
+#define SE_MMU_REG_SAFE_EXT_ADDRESS 0x1003C
+#define SE_MMU_REG_CONTEXT_PD 0x10040
+#define SE_MMU_REG_INTR_ENBL 0x10044
+#endif
+
+#define DC_LAYER_NUM 6
+#define DC_DISPLAY_NUM 2
+#define DC_CURSOR_NUM 2
+
+enum dc_chip_rev {
+ DC_REV_0, /* For HW_REV_5720;
+ * HW_REV_5721_311 */
+ DC_REV_1, /* For HW_REV_5721_30B */
+ DC_REV_2, /* For HW_REV_5721_310 */
+};
+
+enum dc_hw_plane_id {
+ PRIMARY_PLANE_0,
+ OVERLAY_PLANE_0,
+ OVERLAY_PLANE_1,
+ PRIMARY_PLANE_1,
+ OVERLAY_PLANE_2,
+ OVERLAY_PLANE_3,
+ CURSOR_PLANE_0,
+ CURSOR_PLANE_1,
+ PLANE_NUM
+};
+
+enum dc_hw_color_format {
+ FORMAT_X4R4G4B4,
+ FORMAT_A4R4G4B4,
+ FORMAT_X1R5G5B5,
+ FORMAT_A1R5G5B5,
+ FORMAT_R5G6B5,
+ FORMAT_X8R8G8B8,
+ FORMAT_A8R8G8B8,
+ FORMAT_YUY2,
+ FORMAT_UYVY,
+ FORMAT_INDEX8,
+ FORMAT_MONOCHROME,
+ FORMAT_YV12 = 0xf,
+ FORMAT_A8,
+ FORMAT_NV12,
+ FORMAT_NV16,
+ FORMAT_RG16,
+ FORMAT_R8,
+ FORMAT_NV12_10BIT,
+ FORMAT_A2R10G10B10,
+ FORMAT_NV16_10BIT,
+ FORMAT_INDEX1,
+ FORMAT_INDEX2,
+ FORMAT_INDEX4,
+ FORMAT_P010,
+ FORMAT_YUV444,
+ FORMAT_YUV444_10BIT,
+};
+
+enum dc_hw_yuv_color_space {
+ COLOR_SPACE_601 = 0,
+ COLOR_SPACE_709 = 1,
+ COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+ ROT_0 = 0,
+ ROT_90 = 4,
+ ROT_180 = 5,
+ ROT_270 = 6,
+ FLIP_X = 1,
+ FLIP_Y = 2,
+ FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+ SWIZZLE_ARGB = 0,
+ SWIZZLE_RGBA,
+ SWIZZLE_ABGR,
+ SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+ OUT_DPI,
+ OUT_DP,
+};
+
+enum dc_hw_cursor_size {
+ CURSOR_SIZE_32X32 = 0,
+ CURSOR_SIZE_64X64,
+};
+
+enum dc_hw_blend_mode {
+ /* out.rgb = plane_alpha * fg.rgb +
+ * (1 - (plane_alpha * fg.alpha)) * bg.rgb
+ */
+ BLEND_PREMULTI,
+ /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+ * (1 - (plane_alpha * fg.alpha)) * bg.rgb
+ */
+ BLEND_COVERAGE,
+ /* out.rgb = plane_alpha * fg.rgb +
+ * (1 - plane_alpha) * bg.rgb
+ */
+ BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+ u32 y_address;
+ u32 u_address;
+ u32 v_address;
+ u32 y_stride;
+ u32 u_stride;
+ u32 v_stride;
+ u32 size;
+ u32 top_left;
+ u32 bottom_right;
+ u32 scale_factor_x;
+ u32 scale_factor_y;
+ u32 h_filter_coef_index;
+ u32 h_filter_coef_data;
+ u32 v_filter_coef_index;
+ u32 v_filter_coef_data;
+ u32 init_offset;
+ u32 color_key;
+ u32 color_key_high;
+ u32 clear_value;
+ u32 color_table_index;
+ u32 color_table_data;
+ u32 scale_config;
+ u32 water_mark;
+ u32 degamma_index;
+ u32 degamma_data;
+ u32 degamma_ex_data;
+ u32 src_global_color;
+ u32 dst_global_color;
+ u32 blend_config;
+ u32 roi_origin;
+ u32 roi_size;
+ u32 YUVToRGBCoef0;
+ u32 YUVToRGBCoef1;
+ u32 YUVToRGBCoef2;
+ u32 YUVToRGBCoef3;
+ u32 YUVToRGBCoef4;
+ u32 YUVToRGBCoefD0;
+ u32 YUVToRGBCoefD1;
+ u32 YUVToRGBCoefD2;
+ u32 YClampBound;
+ u32 UVClampBound;
+ u32 RGBToRGBCoef0;
+ u32 RGBToRGBCoef1;
+ u32 RGBToRGBCoef2;
+ u32 RGBToRGBCoef3;
+ u32 RGBToRGBCoef4;
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+struct dc_hw_mmu_reg {
+ u32 mmu_config;
+ u32 mmu_control;
+ u32 table_array_size;
+ u32 safe_non_secure;
+ u32 safe_secure;
+ u32 safe_ex;
+ u32 context_pd_entry;
+};
+#endif
+
+struct dc_hw_fb {
+ u32 y_address;
+ u32 u_address;
+ u32 v_address;
+ u32 clear_value;
+ u32 water_mark;
+ u16 y_stride;
+ u16 u_stride;
+ u16 v_stride;
+ u16 width;
+ u16 height;
+ u8 format;
+ u8 tile_mode;
+ u8 rotation;
+ u8 yuv_color_space;
+ u8 swizzle;
+ u8 uv_swizzle;
+ u8 zpos;
+ u8 display_id;
+ bool clear_enable;
+ bool dec_enable;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_scale {
+ u32 scale_factor_x;
+ u32 scale_factor_y;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_position {
+ u16 start_x;
+ u16 start_y;
+ u16 end_x;
+ u16 end_y;
+ bool dirty;
+};
+
+struct dc_hw_blend {
+ u8 alpha;
+ u8 blend_mode;
+ bool dirty;
+};
+
+struct dc_hw_colorkey {
+ u32 colorkey;
+ u32 colorkey_high;
+ u8 transparency;
+ bool dirty;
+};
+
+struct dc_hw_roi {
+ u16 x;
+ u16 y;
+ u16 width;
+ u16 height;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_cursor {
+ u32 address;
+ u16 x;
+ u16 y;
+ u16 hot_x;
+ u16 hot_y;
+ u8 size;
+ u8 display_id;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_display {
+ u32 bus_format;
+ u16 h_active;
+ u16 h_total;
+ u16 h_sync_start;
+ u16 h_sync_end;
+ u16 v_active;
+ u16 v_total;
+ u16 v_sync_start;
+ u16 v_sync_end;
+ u16 sync_mode;
+ u32 bg_color;
+ u8 id;
+ bool h_sync_polarity;
+ bool v_sync_polarity;
+ bool enable;
+ bool sync_enable;
+ bool dither_enable;
+};
+
+struct dc_hw_gamma {
+ u16 gamma[GAMMA_EX_SIZE][3];
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_degamma {
+ u16 degamma[DEGAMMA_SIZE][3];
+ u32 mode;
+ bool dirty;
+};
+
+struct dc_hw_plane {
+ struct dc_hw_fb fb;
+ struct dc_hw_position pos;
+ struct dc_hw_scale scale;
+ struct dc_hw_blend blend;
+ struct dc_hw_roi roi;
+ struct dc_hw_colorkey colorkey;
+ struct dc_hw_degamma degamma;
+};
+
+struct dc_hw_qos {
+ u8 low_value;
+ u8 high_value;
+ bool dirty;
+};
+
+struct dc_hw_read {
+ u32 reg;
+ u32 value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+ void (*gamma)(struct dc_hw *hw);
+ void (*plane)(struct dc_hw *hw);
+ void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+ enum dc_chip_rev rev;
+ enum dc_hw_out out[DC_DISPLAY_NUM];
+ void *hi_base;
+ void *reg_base;
+#ifdef CONFIG_VERISILICON_MMU
+ void *mmu_base;
+#endif
+ struct dc_hw_display display[DC_DISPLAY_NUM];
+ struct dc_hw_gamma gamma[DC_DISPLAY_NUM];
+ struct dc_hw_plane plane[DC_LAYER_NUM];
+ struct dc_hw_cursor cursor[DC_CURSOR_NUM];
+ struct dc_hw_qos qos;
+ struct dc_hw_funcs *func;
+ struct vs_dc_info *info;
+ struct regmap *vosys_regmap;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+ struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+ struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+ struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+ u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch);
+void dc_hw_disable_dump(struct dc_hw *hw);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+void dc_hw_enable_irq(struct dc_hw *hw, u32 irq, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
+void dc_hw_commit(struct dc_hw *hw);
+#ifdef CONFIG_VERISILICON_DEC
+void dc_hw_dec_init(struct dc_hw *hw);
+void dc_hw_dec_stream_set(struct dc_hw *hw,u32 main_base_addr,
+ u32 ts_base_addr, u8 tile_mode, u8 align_mode,
+ u8 format, u8 depth, u8 stream_id);
+void dc_hw_dec_stream_disable(struct dc_hw *hw, u8 stream_id);
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu);
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable);
+#endif
+
+#endif /* __VS_DC_HW_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "vs_dc_mmu.h"
+
+static bool mmu_construct = false;
+
+int _allocate_memory(u32 bytes, void **memory)
+{
+ void *mem = NULL;
+
+ if (bytes == 0 || memory == NULL) {
+ pr_err("%s has invalid arguments.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (bytes > PAGE_SIZE) {
+ mem = vmalloc(bytes);
+ }
+ else {
+ mem = kmalloc(bytes, GFP_KERNEL);
+ }
+
+ if (!mem) {
+ pr_err("%s out of memory.\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ memset((u8 *)mem, 0, bytes);
+ *memory = mem;
+
+ return 0;
+}
+
+static int _create_mutex(void **mutex)
+{
+ int ret =0;
+
+ if (mutex == NULL) {
+ return -EINVAL;
+ }
+
+ ret = _allocate_memory(sizeof(struct mutex), mutex);
+ if (ret)
+ return ret;
+
+ mutex_init(*(struct mutex **)mutex);
+
+ return 0;
+}
+
+static int _acquire_mutex(void *mutex, u32 timeout)
+{
+ if (mutex == NULL) {
+ pr_err("%s has invalid argument.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (timeout == DC_INFINITE) {
+ mutex_lock(mutex);
+ return 0;
+ }
+
+ for (;;) {
+ /* Try to acquire the mutex. */
+ if (mutex_trylock(mutex)) {
+ /* Success. */
+ return 0;
+ }
+
+ if (timeout-- == 0) {
+ break;
+ }
+
+ /* Wait for 1 millisecond. */
+ udelay(1000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int _release_mutex(void *mutex)
+{
+ if (mutex == NULL) {
+ pr_err("%s has invalid argument.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mutex_unlock(mutex);
+
+ return 0;
+}
+
+static u32 _mtlb_offset(u32 address)
+{
+ return (address & MMU_MTLB_MASK) >> MMU_MTLB_SHIFT;
+}
+
+static u32 _stlb_offset(u32 address)
+{
+ return (address & MMU_STLB_4K_MASK) >> MMU_STLB_4K_SHIFT;
+}
+
+static u32 _address_to_index(dc_mmu_pt mmu, u32 address)
+{
+ return _mtlb_offset(address) * MMU_STLB_4K_ENTRY_NUM + _stlb_offset(address);
+}
+
+static u32 _set_page(u32 page_address, u32 page_address_ext, bool writable)
+{
+ u32 entry = page_address
+ /* AddressExt */
+ | (page_address_ext << 4)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+
+ if (writable) {
+ /* writable */
+ entry |= (1 << 2);
+ }
+
+ return entry;
+}
+
+static void _write_page_entry(u32 *page_entry, u32 entry_value)
+{
+ *page_entry = entry_value;
+}
+
+static u32 _read_page_entry(u32 *page_entry)
+{
+ return *page_entry;
+}
+
+int _allocate_stlb(dc_mmu_stlb_pt *stlb)
+{
+ dc_mmu_stlb_pt stlb_t = NULL;
+ void *mem = NULL;
+
+ mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ stlb_t = (dc_mmu_stlb_pt)mem;
+
+ stlb_t->size = MMU_STLB_4K_SIZE;
+
+ *stlb = stlb_t;
+
+ return 0;
+}
+
+int _allocate_all_stlb(struct device *dev, dc_mmu_stlb_pt *stlb)
+{
+ dc_mmu_stlb_pt stlb_t = NULL;
+ void *mem = NULL;
+ void *cookie = NULL;
+ dma_addr_t dma_addr;
+ size_t size;
+
+ mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ stlb_t = (dc_mmu_stlb_pt)mem;
+
+ stlb_t->size = MMU_STLB_4K_SIZE * MMU_MTLB_ENTRY_NUM;
+ size = PAGE_ALIGN(stlb_t->size);
+
+ cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+ if (!cookie) {
+ dev_err(dev, "Failed to alloc stlb buffer.\n");
+ return -ENOMEM;
+ }
+
+ stlb_t->logical = cookie;
+ stlb_t->physBase = (u64)dma_addr;
+ memset(stlb_t->logical, 0, size);
+
+ *stlb = stlb_t;
+
+ return 0;
+}
+
+int _setup_process_address_space(struct device *dev, dc_mmu_pt mmu)
+{
+ u32 *map = NULL;
+ u32 free, i;
+ u32 dynamic_mapping_entries, address;
+ dc_mmu_stlb_pt all_stlb;
+ int ret =0;
+
+ dynamic_mapping_entries = MMU_MTLB_ENTRY_NUM;
+ mmu->dynamic_mapping_start = 0;
+ mmu->page_table_size = dynamic_mapping_entries * MMU_STLB_4K_SIZE;
+
+ mmu->page_table_entries = mmu->page_table_size / sizeof(u32);
+
+ ret = _allocate_memory(mmu->page_table_size,
+ (void **)&mmu->map_logical);
+ if (ret) {
+ pr_err("Failed to alloc mmu map buffer.\n");
+ return ret;;
+ }
+
+ map = mmu->map_logical;
+
+ /* Initialize free area*/
+ free = mmu->page_table_entries;
+ _write_page_entry(map, (free << 8) | DC_MMU_FREE);
+ _write_page_entry(map + 1, ~0U);
+
+ mmu->heap_list = 0;
+ mmu->free_nodes = false;
+
+ ret = _allocate_all_stlb(dev, &all_stlb);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < dynamic_mapping_entries; i++) {
+ dc_mmu_stlb_pt stlb;
+ dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+
+ ret = _allocate_stlb(&stlb);
+ if (ret)
+ return ret;
+
+ stlb->physBase = all_stlb->physBase + i * MMU_STLB_4K_SIZE;
+ stlb->logical = all_stlb->logical + i * MMU_STLB_4K_SIZE / sizeof(u32);
+
+ stlbs[i] = stlb;
+ }
+
+ address = (u32)all_stlb->physBase;
+
+ ret = _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+ if (ret)
+ return ret;
+
+ for (i = mmu->dynamic_mapping_start;
+ i < mmu->dynamic_mapping_start + dynamic_mapping_entries;
+ i++) {
+ u32 mtlb_entry;
+
+ mtlb_entry = address
+ | MMU_MTLB_4K_PAGE
+ | MMU_MTLB_PRESENT;
+
+ address += MMU_STLB_4K_SIZE;
+
+ /* Insert Slave TLB address to Master TLB entry.*/
+ _write_page_entry(mmu->mtlb_logical + i, mtlb_entry);
+ }
+
+ _release_mutex(mmu->page_table_mutex);
+
+ return 0;
+}
+
+/* MMU Construct */
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu)
+{
+ dc_mmu_pt mmu_t = NULL;
+ void *mem = NULL;
+ void *cookie = NULL, *cookie_safe =NULL;
+ dma_addr_t dma_addr, dma_addr_safe;
+ u32 size = 0;
+ int ret = 0;
+
+ if (mmu_construct)
+ return 0;
+
+ mem = kzalloc(sizeof(dc_mmu), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mmu_t = (dc_mmu_pt)mem;
+ mmu_t->mtlb_bytes = MMU_MTLB_SIZE;
+ size = PAGE_ALIGN(mmu_t->mtlb_bytes);
+
+ /* Allocate MTLB */
+ cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+ if (!cookie) {
+ dev_err(dev, "Failed to alloc mtlb buffer.\n");
+ return -ENOMEM;
+ }
+
+ mmu_t->mtlb_logical = cookie;
+ mmu_t->mtlb_physical = (u64)dma_addr;
+ memset(mmu_t->mtlb_logical, 0, size);
+
+ size = MMU_MTLB_ENTRY_NUM * sizeof(dc_mmu_stlb_pt);
+
+ ret = _allocate_memory(size, &mmu_t->stlbs);
+ if (ret)
+ return ret;
+
+ ret = _create_mutex(&mmu_t->page_table_mutex);
+ if (ret)
+ return ret;
+
+ mmu_t->mode = MMU_MODE_1K;
+
+ ret = _setup_process_address_space(dev, mmu_t);
+ if (ret)
+ return ret;
+
+ /* Allocate safe page */
+ cookie_safe = dma_alloc_wc(dev, 4096, &dma_addr_safe, GFP_KERNEL);
+ if (!cookie_safe) {
+ dev_err(dev, "Failed to alloc safe page.\n");
+ return -ENOMEM;
+ }
+
+ mmu_t->safe_page_logical = cookie_safe;
+ mmu_t->safe_page_physical = (u64)dma_addr_safe;
+ memset(mmu_t->safe_page_logical, 0, size);
+
+ *mmu = mmu_t;
+ mmu_construct = true;
+
+ return 0;
+}
+
+int dc_mmu_get_page_entry(dc_mmu_pt mmu, u32 address, u32 **page_table)
+{
+ dc_mmu_stlb_pt stlb;
+ dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+ u32 mtlb_offset = _mtlb_offset(address);
+ u32 stlb_offset = _stlb_offset(address);
+
+ stlb = stlbs[mtlb_offset - mmu->dynamic_mapping_start];
+ if (stlb == NULL) {
+ pr_err("BUG: invalid stlb, mmu=%p stlbs=%p mtlb_offset=0x%x %s(%d)\n",
+ mmu, stlbs ,mtlb_offset,__FUNCTION__,__LINE__);
+ return -ENXIO;
+ }
+
+ *page_table = &stlb->logical[stlb_offset];
+
+ return 0;
+}
+
+int _link(dc_mmu_pt mmu, u32 index, u32 node)
+{
+ if (index >= mmu->page_table_entries) {
+ mmu->heap_list = node;
+ }
+ else {
+ u32 *map = mmu->map_logical;
+
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ /* Previous is a single node, link to it*/
+ _write_page_entry(&map[index], (node << 8) | DC_MMU_SINGLE);
+ break;
+ case DC_MMU_FREE:
+ /* Link to FREE TYPE node */
+ _write_page_entry(&map[index + 1], node);
+ break;
+ default:
+ pr_err("MMU table corrupted at index %u!", index);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int _add_free(dc_mmu_pt mmu, u32 index, u32 node, u32 count)
+{
+ u32 *map = mmu->map_logical;
+
+ if (count == 1) {
+ /* Initialize a single page node */
+ _write_page_entry(map + node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
+ }
+ else {
+ /* Initialize the FREE node*/
+ _write_page_entry(map + node, (count << 8) | DC_MMU_FREE);
+ _write_page_entry(map + node + 1, ~0U);
+ }
+
+ return _link(mmu, index, node);
+}
+
+/* Collect free nodes */
+int _collect(dc_mmu_pt mmu)
+{
+ u32 *map = mmu->map_logical;
+ u32 count = 0, start = 0, i = 0;
+ u32 previous = ~0U;
+ int ret = 0;
+
+ mmu->heap_list = ~0U;
+ mmu->free_nodes = false;
+
+ /* Walk the entire page table */
+ for (i = 0; i < mmu->page_table_entries; i++) {
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[i]))) {
+ case DC_MMU_SINGLE:
+ if (count++ == 0) {
+ /* Set new start node */
+ start = i;
+ }
+ break;
+ case DC_MMU_FREE:
+ if (count == 0) {
+ /* Set new start node */
+ start = i;
+ }
+
+ count += _read_page_entry(&map[i]) >> 8;
+ /* Advance the index of the page table */
+ i += (_read_page_entry(&map[i]) >> 8) - 1;
+ break;
+ case DC_MMU_USED:
+ /* Meet used node, start to collect */
+ if (count > 0) {
+ /* Add free node to list*/
+ ret = _add_free(mmu, previous, start, count);
+ if (ret)
+ return ret;
+ /* Reset previous unused node index */
+ previous = start;
+ count = 0;
+ }
+ break;
+ default:
+ pr_err("MMU page table corrupted at index %u!", i);
+ return -EINVAL;
+ }
+ }
+
+ /* If left node is an open node. */
+ if (count > 0) {
+ ret = _add_free(mmu, previous, start, count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int _fill_page_table(u32 *page_table, u32 page_count, u32 entry_value)
+{
+ u32 i;
+
+ for (i = 0; i < page_count; i++) {
+ _write_page_entry(page_table + i, entry_value);
+ }
+
+ return 0;
+}
+
+int dc_mmu_allocate_pages(dc_mmu_pt mmu, u32 page_count, u32 *address)
+{
+ bool got = false, acquired = false;
+ u32 *map;
+ u32 index = 0, vaddr, left;
+ u32 previous = ~0U;
+ u32 mtlb_offset, stlb_offset;
+ int ret = 0;
+
+ if (page_count == 0 || page_count > mmu->page_table_entries) {
+ pr_err("%s has invalid arguments.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+ acquired = true;
+
+ for (map = mmu->map_logical; !got;) {
+ for (index = mmu->heap_list; !got && (index < mmu->page_table_entries);) {
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ if (page_count == 1) {
+ got = true;
+ }
+ else {
+ /* Move to next node */
+ previous = index;
+ index = _read_page_entry(&map[index]) >> 8;
+ }
+ break;
+ case DC_MMU_FREE:
+ if (page_count <= (_read_page_entry(&map[index]) >> 8)) {
+ got = true;
+ }
+ else {
+ /* Move to next node */
+ previous = index;
+ index = _read_page_entry(&map[index + 1]);
+ }
+ break;
+ default:
+ /* Only link SINGLE and FREE node */
+ pr_err("MMU table corrupted at index %u!", index);
+ ret = -EINVAL;
+ goto OnError;
+ }
+ }
+
+ /* If out of index */
+ if (index >= mmu->page_table_entries) {
+ if (mmu->free_nodes) {
+ /* Collect the free node */
+ ret = _collect(mmu);
+ if (ret)
+ goto OnError;
+ }
+ else {
+ ret = -ENODATA;
+ goto OnError;
+ }
+ }
+ }
+
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ /* Unlink single node from node list */
+ ret = _link(mmu, previous, _read_page_entry(&map[index]) >> 8);
+ if (ret)
+ goto OnError;
+ break;
+
+ case DC_MMU_FREE:
+ left = (_read_page_entry(&map[index]) >> 8) - page_count;
+ switch (left) {
+ case 0:
+ /* Unlink the entire FREE type node */
+ ret = _link(mmu, previous, _read_page_entry(&map[index + 1]));
+ if (ret)
+ goto OnError;
+ break;
+ case 1:
+ /* Keep the map[index] as a single node,
+ * mark the left as used
+ */
+ _write_page_entry(&map[index],
+ (_read_page_entry(&map[index + 1]) << 8) |
+ DC_MMU_SINGLE);
+ index++;
+ break;
+ default:
+ /* FREE type node left */
+ _write_page_entry(&map[index],
+ (left << 8) | DC_MMU_FREE);
+ index += left;
+ break;
+ }
+ break;
+ default:
+ /* Only link SINGLE and FREE node */
+ pr_err("MMU table corrupted at index %u!", index);
+ ret = -EINVAL;
+ goto OnError;
+ }
+
+ /* Mark node as used */
+ ret = _fill_page_table(&map[index], page_count, DC_MMU_USED);
+ if (ret)
+ goto OnError;
+
+ _release_mutex(mmu->page_table_mutex);
+
+ mtlb_offset = index / MMU_STLB_4K_ENTRY_NUM + mmu->dynamic_mapping_start;
+ stlb_offset = index % MMU_STLB_4K_ENTRY_NUM;
+
+ vaddr = (mtlb_offset << MMU_MTLB_SHIFT) | (stlb_offset << MMU_STLB_4K_SHIFT);
+
+ if (address != NULL) {
+ *address = vaddr;
+ }
+
+ return 0;
+
+OnError:
+ if (acquired) {
+ _release_mutex(mmu->page_table_mutex);
+ }
+
+ return ret;
+}
+
+int dc_mmu_free_pages(dc_mmu_pt mmu, u32 address, u32 page_count)
+{
+ u32 *node;
+
+ if (page_count == 0)
+ return -EINVAL;
+
+ node = mmu->map_logical + _address_to_index(mmu, address);
+
+ _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+
+ if (page_count == 1) {
+ /* Mark the Single page node free */
+ _write_page_entry(node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
+ }
+ else {
+ /* Mark the FREE type node free */
+ _write_page_entry(node, (page_count << 8) | DC_MMU_FREE);
+ _write_page_entry(node + 1, ~0U);
+ }
+
+ mmu->free_nodes = true;
+
+ _release_mutex(mmu->page_table_mutex);
+
+ return 0;
+}
+
+int dc_mmu_set_page(dc_mmu_pt mmu, u64 page_address, u32 *page_entry)
+{
+ u32 address_ext;
+ u32 address;
+
+ if (page_entry == NULL || (page_address & 0xFFF)) {
+ return -EINVAL;
+ }
+
+ /* [31:0]. */
+ address = (u32)(page_address & 0xFFFFFFFF);
+ /* [39:32]. */
+ address_ext = (u32)((page_address >> 32) & 0xFF);
+
+ _write_page_entry(page_entry, _set_page(address, address_ext, true));
+
+ return 0;
+}
+
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
+ u32 *address, bool continuous, bool security)
+{
+ u32 virutal_address, i= 0;
+ u32 mtlb_num, mtlb_entry, mtlb_offset;
+ bool allocated = false;
+ int ret = 0;
+
+ ret = dc_mmu_allocate_pages(mmu, page_count, &virutal_address);
+ if (ret)
+ goto OnError;
+
+ *address = virutal_address;
+ allocated = true;
+
+ /*Fill mtlb security bit*/
+ mtlb_num = _mtlb_offset(virutal_address + page_count * MMU_PAGE_4K_SIZE - 1) -
+ _mtlb_offset(virutal_address) + 1;
+ mtlb_offset = _mtlb_offset(virutal_address);
+ mtlb_entry = mmu->mtlb_logical[mtlb_offset];
+
+ for (i = 0; i < mtlb_num ; i++) {
+ mtlb_entry = mmu->mtlb_logical[mtlb_offset + i];
+ if(security) {
+ mtlb_entry = mtlb_entry
+ | MMU_MTLB_SECURITY
+ | MMU_MTLB_EXCEPTION;
+ _write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
+ } else {
+ mtlb_entry = mtlb_entry & (~MMU_MTLB_SECURITY);
+ _write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
+ }
+ }
+
+ /* Fill in page table */
+ for (i = 0; i < page_count; i++) {
+ u64 page_phy;
+ u32 *page_entry;
+ struct page **pages;
+
+ if (continuous == true) {
+ page_phy = physical + i * MMU_PAGE_4K_SIZE;
+ }
+ else {
+ pages = (struct page **)physical;
+ page_phy = page_to_phys(pages[i]);
+ }
+
+ ret = dc_mmu_get_page_entry(mmu, virutal_address, &page_entry);
+ if (ret)
+ goto OnError;
+
+ /* Write the page address to the page entry */
+ ret = dc_mmu_set_page(mmu, page_phy, page_entry);
+ if (ret)
+ goto OnError;
+
+ /* Get next page */
+ virutal_address += MMU_PAGE_4K_SIZE;
+ }
+
+ return 0;
+
+OnError:
+ if (allocated)
+ dc_mmu_free_pages(mmu, virutal_address, page_count);
+ pr_info("%s fail!\n", __FUNCTION__);
+
+ return ret;
+}
+
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count)
+{
+ return dc_mmu_free_pages(mmu, gpu_address, page_count);
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef _VS_DC_MMU_H_
+#define _VS_DC_MMU_H_
+
+#include "vs_type.h"
+
+#define DC_INFINITE ((u32)(~0U))
+
+#define DC_ENTRY_TYPE(x) (x & 0xF0)
+#define DC_SINGLE_PAGE_NODE_INITIALIZE (~((1U << 8) - 1))
+
+#define DC_INVALID_PHYSICAL_ADDRESS ~0ULL
+#define DC_INVALID_ADDRESS ~0U
+
+/* 1k mode */
+#define MMU_MTLB_SHIFT 24
+#define MMU_STLB_4K_SHIFT 12
+
+#define MMU_MTLB_BITS (32 - MMU_MTLB_SHIFT)
+#define MMU_PAGE_4K_BITS MMU_STLB_4K_SHIFT
+#define MMU_STLB_4K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS)
+
+#define MMU_MTLB_ENTRY_NUM (1 << MMU_MTLB_BITS)
+#define MMU_MTLB_SIZE (MMU_MTLB_ENTRY_NUM << 2)
+#define MMU_STLB_4K_ENTRY_NUM (1 << MMU_STLB_4K_BITS)
+#define MMU_STLB_4K_SIZE (MMU_STLB_4K_ENTRY_NUM << 2)
+#define MMU_PAGE_4K_SIZE (1 << MMU_STLB_4K_SHIFT)
+
+#define MMU_MTLB_MASK (~((1U << MMU_MTLB_SHIFT)-1))
+#define MMU_STLB_4K_MASK ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK)
+#define MMU_PAGE_4K_MASK (MMU_PAGE_4K_SIZE - 1)
+
+/* page offset definitions. */
+#define MMU_OFFSET_4K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS)
+#define MMU_OFFSET_4K_MASK ((1U << MMU_OFFSET_4K_BITS) - 1)
+
+#define MMU_MTLB_PRESENT 0x00000001
+#define MMU_MTLB_EXCEPTION 0x00000002
+#define MMU_MTLB_SECURITY 0x00000010
+#define MMU_MTLB_4K_PAGE 0x00000000
+
+typedef enum _dc_mmu_type
+{
+ DC_MMU_USED = (0 << 4),
+ DC_MMU_SINGLE = (1 << 4),
+ DC_MMU_FREE = (2 << 4),
+}
+dc_mmu_type;
+
+typedef enum _dc_mmu_mode
+{
+ MMU_MODE_1K,
+ MMU_MODE_4K,
+}
+dc_mmu_mode;
+
+typedef struct _dc_mmu_stlb
+{
+ u32 *logical;
+ void *physical;
+ u32 size;
+ u64 physBase;
+ u32 pageCount;
+}
+dc_mmu_stlb, *dc_mmu_stlb_pt;
+
+typedef struct _dc_mmu {
+ u32 mtlb_bytes;
+ u64 mtlb_physical;
+ u32 *mtlb_logical;
+
+ void *safe_page_logical;
+ u64 safe_page_physical;
+
+ u32 dynamic_mapping_start;
+
+ void *stlbs;
+
+ u64 stlb_physicals[MMU_MTLB_ENTRY_NUM];
+
+ u32 page_table_entries;
+ u32 page_table_size;
+ u32 heap_list;
+
+ u32 *map_logical;
+ bool free_nodes;
+
+ void *page_table_mutex;
+
+ dc_mmu_mode mode;
+
+ void *static_stlb;
+}
+dc_mmu, *dc_mmu_pt;
+
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu);
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
+ u32 *address, bool continuous, bool security);
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count);
+
+#endif /* _VS_DC_MMU_H_ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/of_graph.h>
+#include <linux/component.h>
+#include <linux/iommu.h>
+#include <linux/version.h>
+
+#include <drm/drm_of.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_modeset_helper.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_debugfs.h>
+#endif
+
+#include "vs_drv.h"
+#include "vs_fb.h"
+#include "vs_gem.h"
+#include "vs_plane.h"
+#include "vs_crtc.h"
+#include "vs_simple_enc.h"
+#include "vs_dc.h"
+#include "vs_virtual.h"
+#include "dw_mipi_dsi.h"
+#include "dw_hdmi_light.h"
+
+#define DRV_NAME "vs-drm"
+#define DRV_DESC "VeriSilicon DRM driver"
+#define DRV_DATE "20191101"
+#define DRV_MAJOR 1
+#define DRV_MINOR 0
+
+static bool has_iommu = true;
+static struct platform_driver vs_drm_platform_driver;
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .mmap = vs_gem_mmap,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int vs_debugfs_planes_show(struct seq_file *s, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *)s->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_plane *plane;
+
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ struct drm_plane_state *state = plane->state;
+ struct vs_plane_state *plane_state = to_vs_plane_state(state);
+
+ seq_printf(s, "plane[%u]: %s\n", plane->base.id, plane->name);
+ seq_printf(s, "\tcrtc = %s\n", state->crtc ?
+ state->crtc->name : "(null)");
+ seq_printf(s, "\tcrtc id = %u\n", state->crtc ?
+ state->crtc->base.id : 0);
+ seq_printf(s, "\tcrtc-pos = " DRM_RECT_FMT "\n",
+ DRM_RECT_ARG(&plane_state->status.dest));
+ seq_printf(s, "\tsrc-pos = " DRM_RECT_FP_FMT "\n",
+ DRM_RECT_FP_ARG(&plane_state->status.src));
+ seq_printf(s, "\tformat = %p4cc\n", state->fb ?
+ state->fb->format->format : "(null)");
+ seq_printf(s, "\trotation = 0x%x\n", state->rotation);
+ seq_printf(s, "\ttiling = %u\n",
+ plane_state->status.tile_mode);
+
+ seq_puts(s, "\n");
+ }
+
+ return 0;
+}
+
+static struct drm_info_list vs_debugfs_list[] = {
+ { "planes", vs_debugfs_planes_show, 0, NULL },
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+static void vs_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(vs_debugfs_list,
+ ARRAY_SIZE(vs_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#else
+static int vs_debugfs_init(struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ int ret;
+
+ ret = drm_debugfs_create_files(vs_debugfs_list,
+ ARRAY_SIZE(vs_debugfs_list),
+ minor->debugfs_root, minor);
+ if (ret)
+ dev_err(dev->dev, "could not install vs_debugfs_list.\n");
+
+ return ret;
+}
+#endif
+#endif
+
+static struct drm_driver vs_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
+ .lastclose = drm_fb_helper_lastclose,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = vs_gem_prime_import,
+ .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
+ .dumb_create = vs_gem_dumb_create,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_init = vs_debugfs_init,
+#endif
+ .fops = &fops,
+ .name = DRV_NAME,
+ .desc = DRV_DESC,
+ .date = DRV_DATE,
+ .major = DRV_MAJOR,
+ .minor = DRV_MINOR,
+};
+
+int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct vs_drm_private *priv = drm_dev->dev_private;
+ int ret;
+
+ if (!has_iommu)
+ return 0;
+
+ if (!priv->domain) {
+ priv->domain = iommu_get_domain_for_dev(dev);
+ if (IS_ERR(priv->domain))
+ return PTR_ERR(priv->domain);
+ priv->dma_dev = dev;
+ }
+
+ ret = iommu_attach_device(priv->domain, dev);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct vs_drm_private *priv = drm_dev->dev_private;
+
+ if (!has_iommu)
+ return;
+
+ iommu_detach_device(priv->domain, dev);
+
+ if (priv->dma_dev == dev)
+ priv->dma_dev = drm_dev->dev;
+}
+
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+ unsigned int alignment)
+{
+ struct vs_drm_private *priv = drm_dev->dev_private;
+
+ if (alignment > priv->pitch_alignment)
+ priv->pitch_alignment = alignment;
+}
+
+static int vs_drm_bind(struct device *dev)
+{
+ struct drm_device *drm_dev;
+ struct vs_drm_private *priv;
+ int ret;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#ifdef CONFIG_VERISILICON_MMU
+ static u64 dma_mask = DMA_BIT_MASK(40);
+#else
+ static u64 dma_mask = DMA_BIT_MASK(32);
+#endif
+#else
+ static u64 dma_mask = DMA_40BIT_MASK;
+#endif
+
+ drm_dev = drm_dev_alloc(&vs_drm_driver, dev);
+ if (IS_ERR(drm_dev))
+ return PTR_ERR(drm_dev);
+
+ dev_set_drvdata(dev, drm_dev);
+
+ priv = devm_kzalloc(drm_dev->dev, sizeof(struct vs_drm_private),
+ GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_put_dev;
+ }
+
+ priv->pitch_alignment = 64;
+ priv->dma_dev = drm_dev->dev;
+ priv->dma_dev->coherent_dma_mask = dma_mask;
+
+ drm_dev->dev_private = priv;
+
+ drm_mode_config_init(drm_dev);
+
+ /* Now try and bind all our sub-components */
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_mode;
+
+ vs_mode_config_init(drm_dev);
+
+ ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+ if (ret)
+ goto err_bind;
+
+ drm_mode_config_reset(drm_dev);
+
+ drm_dev->irq_enabled = true;
+
+ drm_kms_helper_poll_init(drm_dev);
+
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_helper;
+
+ drm_fbdev_generic_setup(drm_dev, 32);
+
+ return 0;
+
+err_helper:
+ drm_kms_helper_poll_fini(drm_dev);
+err_bind:
+ component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
+ drm_mode_config_cleanup(drm_dev);
+ if (priv->domain)
+ iommu_domain_free(priv->domain);
+err_put_dev:
+ drm_dev->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+ drm_dev_put(drm_dev);
+ return ret;
+}
+
+static void vs_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ struct vs_drm_private *priv = drm_dev->dev_private;
+
+ drm_dev_unregister(drm_dev);
+
+ drm_kms_helper_poll_fini(drm_dev);
+
+ component_unbind_all(drm_dev->dev, drm_dev);
+
+ drm_mode_config_cleanup(drm_dev);
+
+ if (priv->domain) {
+ iommu_domain_free(priv->domain);
+ priv->domain = NULL;
+ }
+
+ drm_dev->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+ drm_dev_put(drm_dev);
+}
+
+static const struct component_master_ops vs_drm_ops = {
+ .bind = vs_drm_bind,
+ .unbind = vs_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+ /* put display control driver at start */
+ &dc_platform_driver,
+
+ /* connector */
+
+ /* bridge */
+#ifdef CONFIG_VERISILICON_DW_MIPI_DSI
+ &dw_mipi_dsi_driver,
+#endif
+
+#ifdef CONFIG_VERISILICON_DW_HDMI_LIGHT
+ &dw_hdmi_light_platform_driver,
+#endif
+
+ /* encoder */
+ &simple_encoder_driver,
+
+#ifdef CONFIG_VERISILICON_VIRTUAL_DISPLAY
+ &virtual_display_platform_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
+ (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
+
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
+
+static struct component_match *vs_drm_match_add(struct device *dev)
+{
+ struct component_match *match = NULL;
+ int i;
+
+ for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
+ struct platform_driver *drv = drm_sub_drivers[i];
+ struct device *p = NULL, *d;
+
+ while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+ put_device(p);
+
+ component_match_add(dev, &match, compare_dev, d);
+
+ p = d;
+ }
+ put_device(p);
+ }
+
+ return match ?: ERR_PTR(-ENODEV);
+}
+
+static int vs_drm_platform_of_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *port;
+ bool found = false;
+ int i;
+
+ if (!np)
+ return -ENODEV;
+
+ for (i = 0;; i++) {
+ struct device_node *iommu;
+
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ iommu = of_parse_phandle(port->parent, "iommus", 0);
+
+ /*
+ * if there is a crtc not support iommu, force set all
+ * crtc use non-iommu buffer.
+ */
+ if (!iommu || !of_device_is_available(iommu->parent))
+ has_iommu = false;
+
+ found = true;
+
+ of_node_put(iommu);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ DRM_DEV_ERROR(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+
+ if (!found) {
+ DRM_DEV_ERROR(dev, "No available DC found.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int vs_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match;
+ int ret;
+
+ ret = vs_drm_platform_of_probe(dev);
+ if (ret)
+ return ret;
+
+ match = vs_drm_match_add(dev);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+
+ return component_master_add_with_match(dev, &vs_drm_ops, match);
+}
+
+static int vs_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &vs_drm_ops);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vs_drm_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ return drm_mode_config_helper_suspend(drm);
+}
+
+static int vs_drm_resume(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ return drm_mode_config_helper_resume(drm);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
+
+
+static const struct of_device_id vs_drm_dt_ids[] = {
+
+ { .compatible = "verisilicon,display-subsystem", },
+
+ { /* sentinel */ },
+
+};
+
+MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
+
+static struct platform_driver vs_drm_platform_driver = {
+ .probe = vs_drm_platform_probe,
+ .remove = vs_drm_platform_remove,
+
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = vs_drm_dt_ids,
+ .pm = &vs_drm_pm_ops,
+ },
+};
+
+static int __init vs_drm_init(void)
+{
+ int ret;
+
+ ret = platform_register_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&vs_drm_platform_driver);
+ if (ret)
+ platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+
+ return ret;
+}
+
+static void __exit vs_drm_fini(void)
+{
+ platform_driver_unregister(&vs_drm_platform_driver);
+ platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+
+module_init(vs_drm_init);
+module_exit(vs_drm_fini);
+
+MODULE_DESCRIPTION("VeriSilicon DRM Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRV_H__
+#define __VS_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#include "vs_plane.h"
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+
+/*
+ *
+ * @dma_dev: device for DMA API.
+ * - use the first attached device if support iommu
+ else use drm device (only contiguous buffer support)
+ * @domain: iommu domain for DRM.
+ * - all DC IOMMU share same domain to reduce mapping
+ * @pitch_alignment: buffer pitch alignment required by sub-devices.
+ *
+ */
+struct vs_drm_private {
+ struct device *dma_dev;
+ struct iommu_domain *domain;
+#ifdef CONFIG_VERISILICON_MMU
+ dc_mmu *mmu;
+#endif
+
+ unsigned int pitch_alignment;
+};
+
+int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
+ struct device *dev);
+
+void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
+ struct device *dev);
+
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+ unsigned int alignment);
+
+static inline struct device *to_dma_dev(struct drm_device *dev)
+{
+ struct vs_drm_private *priv = dev->dev_private;
+
+ return priv->dma_dev;
+}
+
+static inline bool is_iommu_enabled(struct drm_device *dev)
+{
+ struct vs_drm_private *priv = dev->dev_private;
+
+ return priv->domain != NULL ? true : false;
+}
+#endif /* __VS_DRV_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_fourcc.h>
+#else
+#include <drm/drmP.h>
+#endif
+#include <drm/drm_gem.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "vs_fb.h"
+#include "vs_gem.h"
+
+#define fourcc_mod_vs_get_type(val) \
+ (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+
+static struct drm_framebuffer_funcs vs_fb_funcs = {
+ .create_handle = drm_gem_fb_create_handle,
+ .destroy = drm_gem_fb_destroy,
+};
+
+static struct drm_framebuffer *
+vs_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct vs_gem_object **obj, unsigned int num_planes)
+{
+ struct drm_framebuffer *fb;
+ int ret, i;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb)
+ return ERR_PTR(-ENOMEM);
+
+ drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
+
+ for (i = 0; i < num_planes; i++)
+ fb->obj[i] = &obj[i]->base;
+
+ ret = drm_framebuffer_init(dev, fb, &vs_fb_funcs);
+ if (ret) {
+ dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+ ret);
+ kfree(fb);
+ return ERR_PTR(ret);
+ }
+
+ return fb;
+}
+
+static struct drm_framebuffer *vs_fb_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_framebuffer *fb;
+ const struct drm_format_info *info;
+ struct vs_gem_object *objs[MAX_NUM_PLANES];
+ struct drm_gem_object *obj;
+ unsigned int height, size;
+ unsigned char i, num_planes;
+ int ret = 0;
+
+ info = drm_get_format_info(dev, mode_cmd);
+ if (!info)
+ return ERR_PTR(-EINVAL);
+
+ num_planes = info->num_planes;
+ if (num_planes > MAX_NUM_PLANES)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < num_planes; i++) {
+ obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
+ if (!obj) {
+ dev_err(dev->dev, "Failed to lookup GEM object.\n");
+ ret = -ENXIO;
+ goto err;
+ }
+
+ height = drm_format_info_plane_height(info,
+ mode_cmd->height, i);
+
+ size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i];
+
+ if (obj->size < size) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ drm_gem_object_put(obj);
+#else
+ drm_gem_object_put_unlocked(obj);
+#endif
+ ret = -EINVAL;
+ goto err;
+ }
+
+ objs[i] = to_vs_gem_object(obj);
+ }
+
+ fb = vs_fb_alloc(dev, mode_cmd, objs, i);
+ if (IS_ERR(fb)) {
+ ret = PTR_ERR(fb);
+ goto err;
+ }
+
+ return fb;
+
+err:
+ for (; i > 0; i--)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ drm_gem_object_put(&objs[i-1]->base);
+#else
+ drm_gem_object_put_unlocked(&objs[i-1]->base);
+#endif
+
+ return ERR_PTR(ret);
+}
+
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned char plane)
+{
+ if (plane > MAX_NUM_PLANES)
+ return NULL;
+
+ return to_vs_gem_object(fb->obj[plane]);
+}
+
+static const struct drm_format_info vs_formats[] = {
+ {.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 },
+ .block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 },.hsub = 2, .vsub = 2, .is_yuv = true},
+ {.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 },
+ .block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 },.hsub = 1, .vsub = 1, .is_yuv = true},
+};
+
+static const struct drm_format_info *
+vs_lookup_format_info(const struct drm_format_info formats[],
+ int num_formats, u32 format)
+{
+ int i;
+
+ for (i = 0; i < num_formats; i++) {
+ if (formats[i].format == format)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static const struct drm_format_info *
+vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd)
+{
+ if (fourcc_mod_vs_get_type(cmd->modifier[0]) ==
+ DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+ return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats),
+ cmd->pixel_format);
+ else
+ return NULL;
+}
+
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
+ .fb_create = vs_fb_create,
+ .get_format_info = vs_get_format_info,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
+ .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void vs_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.fb_modifiers_not_supported = false;
+
+ if (dev->mode_config.max_width == 0 ||
+ dev->mode_config.max_height == 0) {
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+ }
+ dev->mode_config.funcs = &vs_mode_config_funcs;
+ dev->mode_config.helper_private = &vs_mode_config_helpers;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_FB_H__
+#define __VS_FB_H__
+
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned char plane);
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/dma-buf.h>
+
+#include "vs_drv.h"
+#include "vs_gem.h"
+
+static const struct drm_gem_object_funcs vs_gem_default_funcs;
+
+static void nonseq_free(struct page **pages, unsigned int nr_page)
+{
+ u32 i;
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < nr_page; i++)
+ __free_page(pages[i]);
+}
+
+static int __maybe_unused get_pages(unsigned int nr_page,
+ struct vs_gem_object *vs_obj)
+{
+ struct page *pages;
+ u32 i, num_page, page_count = 0;
+ int order = 0;
+ gfp_t gfp = GFP_KERNEL;
+
+ if (!vs_obj->pages)
+ return -EINVAL;
+
+ gfp &= ~__GFP_HIGHMEM;
+ gfp |= __GFP_DMA32;
+
+ num_page = nr_page;
+
+ do {
+ pages = NULL;
+ order = get_order(num_page * PAGE_SIZE);
+ num_page = 1 << order;
+
+ if ((num_page + page_count > nr_page) || (order >= MAX_ORDER)) {
+ num_page = num_page >> 1;
+ continue;
+ }
+
+ pages = alloc_pages(gfp, order);
+ if (!pages) {
+ if (num_page == 1) {
+ nonseq_free(vs_obj->pages, page_count);
+ return -ENOMEM;
+ }
+
+ num_page = num_page >> 1;
+ } else {
+ for (i = 0; i < num_page; i++) {
+ vs_obj->pages[page_count + i] = &pages[i];
+ SetPageReserved(vs_obj->pages[page_count + i]);
+ }
+
+ page_count += num_page;
+ num_page = nr_page - page_count;
+ }
+
+ } while (page_count < nr_page);
+
+ vs_obj->get_pages = true;
+
+ return 0;
+}
+
+static void put_pages(unsigned int nr_page, struct vs_gem_object *vs_obj)
+{
+ u32 i;
+
+ for (i = 0; i < nr_page; i++)
+ ClearPageReserved(vs_obj->pages[i]);
+
+ nonseq_free(vs_obj->pages, nr_page);
+
+ return;
+}
+
+static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj)
+{
+ struct drm_device *dev = vs_obj->base.dev;
+ unsigned int nr_pages;
+ struct sg_table sgt;
+ int ret = -ENOMEM;
+#ifdef CONFIG_VERISILICON_MMU
+ struct vs_drm_private *priv = dev->dev_private;
+#endif
+
+ if (vs_obj->dma_addr) {
+ DRM_DEV_DEBUG_KMS(dev->dev, "already allocated.\n");
+ return 0;
+ }
+
+ vs_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
+
+ if (!is_iommu_enabled(dev))
+ vs_obj->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
+
+ nr_pages = vs_obj->size >> PAGE_SHIFT;
+
+ vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!vs_obj->pages) {
+ DRM_DEV_ERROR(dev->dev, "failed to allocate pages.\n");
+ return -ENOMEM;
+ }
+
+ vs_obj->cookie = dma_alloc_attrs(to_dma_dev(dev), vs_obj->size,
+ &vs_obj->dma_addr, GFP_KERNEL,
+ vs_obj->dma_attrs);
+ if (!vs_obj->cookie) {
+#ifdef CONFIG_VERISILICON_MMU
+ ret = get_pages(nr_pages, vs_obj);
+ if (ret) {
+ DRM_DEV_ERROR(dev->dev, "fail to allocate buffer.\n");
+ goto err_free;
+ }
+#else
+ DRM_DEV_ERROR(dev->dev, "failed to allocate buffer.\n");
+ goto err_free;
+#endif
+ }
+
+#ifdef CONFIG_VERISILICON_MMU
+ /* MMU map*/
+ if (!priv->mmu) {
+ DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+ ret = -EINVAL;
+ goto err_mem_free;
+ }
+
+ /* mmu for ree driver */
+ if (!vs_obj->get_pages)
+ ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->dma_addr,
+ nr_pages, &vs_obj->iova, true, false);
+ else
+ ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->pages, nr_pages,
+ &vs_obj->iova, false, false);
+
+ if (ret) {
+ DRM_DEV_ERROR(dev->dev, "failed to do mmu map.\n");
+ goto err_mem_free;
+ }
+#else
+ vs_obj->iova = vs_obj->dma_addr;
+#endif
+
+ if (!vs_obj->get_pages) {
+ ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt,
+ vs_obj->cookie, vs_obj->dma_addr,
+ vs_obj->size, vs_obj->dma_attrs);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev->dev, "failed to get sgtable.\n");
+ goto err_mem_free;
+ }
+
+ if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
+ DRM_DEV_ERROR(dev->dev, "invalid sgtable.\n");
+ ret = -EINVAL;
+ goto err_sgt_free;
+ }
+
+ sg_free_table(&sgt);
+ }
+
+ return 0;
+
+err_sgt_free:
+ sg_free_table(&sgt);
+err_mem_free:
+ if (!vs_obj->get_pages)
+ dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
+ vs_obj->dma_addr, vs_obj->dma_attrs);
+ else
+ put_pages(nr_pages, vs_obj);
+err_free:
+ kvfree(vs_obj->pages);
+
+ return ret;
+}
+
+static void vs_gem_free_buf(struct vs_gem_object *vs_obj)
+{
+ struct drm_device *dev = vs_obj->base.dev;
+#ifdef CONFIG_VERISILICON_MMU
+ struct vs_drm_private *priv = dev->dev_private;
+ unsigned int nr_pages;
+#endif
+
+ if ((!vs_obj->get_pages) && (!vs_obj->dma_addr)) {
+ DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n");
+ return;
+ }
+
+#ifdef CONFIG_VERISILICON_MMU
+ if (!priv->mmu) {
+ DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+ return;
+ }
+
+ nr_pages = vs_obj->size >> PAGE_SHIFT;
+ dc_mmu_unmap_memory(priv->mmu, vs_obj->iova, nr_pages);
+#endif
+
+ if (!vs_obj->get_pages)
+ dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
+ (dma_addr_t)vs_obj->dma_addr, vs_obj->dma_attrs);
+ else
+ put_pages(vs_obj->size >> PAGE_SHIFT, vs_obj);
+
+ kvfree(vs_obj->pages);
+}
+
+static void vs_gem_free_object(struct drm_gem_object *obj)
+{
+ struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+ if (obj->import_attach) {
+ drm_prime_gem_destroy(obj, vs_obj->sgt);
+ kvfree(vs_obj->pages);
+ } else {
+ vs_gem_free_buf(vs_obj);
+ }
+
+ drm_gem_object_release(obj);
+
+ kfree(vs_obj);
+}
+
+static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
+ size_t size)
+{
+ struct vs_gem_object *vs_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
+ if (!vs_obj)
+ return ERR_PTR(-ENOMEM);
+
+ vs_obj->size = size;
+ obj = &vs_obj->base;
+
+ ret = drm_gem_object_init(dev, obj, size);
+ if (ret)
+ goto err_free;
+
+ vs_obj->base.funcs = &vs_gem_default_funcs;
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret) {
+ drm_gem_object_release(obj);
+ goto err_free;
+ }
+
+ return vs_obj;
+
+err_free:
+ kfree(vs_obj);
+ return ERR_PTR(ret);
+}
+
+struct vs_gem_object *vs_gem_create_object(struct drm_device *dev, size_t size)
+{
+ struct vs_gem_object *vs_obj;
+ int ret;
+
+ size = PAGE_ALIGN(size);
+
+ vs_obj = vs_gem_alloc_object(dev, size);
+ if (IS_ERR(vs_obj))
+ return vs_obj;
+
+ ret = vs_gem_alloc_buf(vs_obj);
+ if (ret) {
+ drm_gem_object_release(&vs_obj->base);
+ kfree(vs_obj);
+ return ERR_PTR(ret);
+ }
+
+ return vs_obj;
+}
+
+static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
+ struct drm_file *file,
+ size_t size,
+ unsigned int *handle)
+{
+ struct vs_gem_object *vs_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ vs_obj = vs_gem_create_object(dev, size);
+ if (IS_ERR(vs_obj))
+ return vs_obj;
+
+ obj = &vs_obj->base;
+
+ ret = drm_gem_handle_create(file, obj, handle);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ drm_gem_object_put(obj);
+#else
+ drm_gem_object_put_unlocked(obj);
+#endif
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ return vs_obj;
+}
+
+static int vs_gem_mmap_obj(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+ struct drm_device *drm_dev = vs_obj->base.dev;
+ unsigned long vm_size;
+ int ret = 0;
+
+ vm_size = vma->vm_end - vma->vm_start;
+ if (vm_size > vs_obj->size)
+ return -EINVAL;
+
+ vma->vm_pgoff = 0;
+
+ if (!vs_obj->get_pages) {
+ vm_flags_clear(vma, VM_PFNMAP);
+
+ ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, vs_obj->cookie,
+ vs_obj->dma_addr, vs_obj->size,
+ vs_obj->dma_attrs);
+ } else {
+ u32 i, nr_pages, pfn = 0U;
+ unsigned long start;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ vm_flags_set(vma,
+ VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
+ start = vma->vm_start;
+ vm_size = PAGE_ALIGN(vm_size);
+ nr_pages = vm_size >> PAGE_SHIFT;
+
+ for (i = 0; i < nr_pages; i++) {
+ pfn = page_to_pfn(vs_obj->pages[i]);
+
+ ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot);
+ if (ret < 0)
+ break;
+
+ start += PAGE_SIZE;
+ }
+ }
+
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
+ vs_obj->size >> PAGE_SHIFT);
+#else
+ return drm_prime_pages_to_sg(vs_obj->pages, vs_obj->size >> PAGE_SHIFT);
+#endif
+}
+
+static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+ struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+ return vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
+ page_address(vs_obj->cookie) :
+ vs_obj->cookie;
+}
+
+static void vs_gem_prime_vunmap(struct drm_gem_object *obj,
+ struct iosys_map *map)
+{
+ /* Nothing to do */
+}
+
+static const struct vm_operations_struct vs_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static const struct drm_gem_object_funcs vs_gem_default_funcs = {
+ .free = vs_gem_free_object,
+ .get_sg_table = vs_gem_prime_get_sg_table,
+ .vmap = vs_gem_prime_vmap,
+ .vunmap = vs_gem_prime_vunmap,
+ .vm_ops = &vs_vm_ops,
+};
+
+int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct vs_drm_private *priv = dev->dev_private;
+ struct vs_gem_object *vs_obj;
+ unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+ if (args->bpp % 10)
+ args->pitch = ALIGN(pitch, priv->pitch_alignment);
+ else
+ /* for costum 10bit format with no bit gaps */
+ args->pitch = pitch;
+ args->size = PAGE_ALIGN(args->pitch * args->height);
+
+ vs_obj =
+ vs_gem_create_with_handle(dev, file, args->size, &args->handle);
+ return PTR_ERR_OR_ZERO(vs_obj);
+}
+
+struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev));
+}
+
+struct drm_gem_object *
+vs_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt)
+{
+ struct vs_gem_object *vs_obj;
+ int npages;
+ int ret;
+ struct scatterlist *s;
+ u32 i;
+ dma_addr_t expected;
+ size_t size = attach->dmabuf->size;
+#ifdef CONFIG_VERISILICON_MMU
+ u32 iova;
+ struct vs_drm_private *priv = dev->dev_private;
+
+ if (!priv->mmu) {
+ DRM_ERROR("invalid mmu.\n");
+ ret = -EINVAL;
+ return ERR_PTR(ret);
+ }
+#endif
+
+ size = PAGE_ALIGN(size);
+
+ vs_obj = vs_gem_alloc_object(dev, size);
+ if (IS_ERR(vs_obj))
+ return ERR_CAST(vs_obj);
+
+ expected = sg_dma_address(sgt->sgl);
+ for_each_sg(sgt->sgl, s, sgt->nents, i) {
+ if (sg_dma_address(s) != expected) {
+#ifndef CONFIG_VERISILICON_MMU
+ DRM_ERROR("sg_table is not contiguous");
+ ret = -EINVAL;
+ goto err;
+#endif
+ }
+
+ if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+#ifdef CONFIG_VERISILICON_MMU
+ iova = 0;
+ npages = sg_dma_len(s) >> PAGE_SHIFT;
+ ret = dc_mmu_map_memory(priv->mmu, (u64)sg_dma_address(s),
+ npages, &iova, true, false);
+ if (ret) {
+ DRM_ERROR("failed to do mmu map.\n");
+ goto err;
+ }
+
+ if (i == 0)
+ vs_obj->iova = iova;
+#else
+ if (i == 0)
+ vs_obj->iova = sg_dma_address(s);
+#endif
+
+ expected = sg_dma_address(s) + sg_dma_len(s);
+ }
+
+ vs_obj->dma_addr = sg_dma_address(sgt->sgl);
+
+ npages = vs_obj->size >> PAGE_SHIFT;
+ vs_obj->pages =
+ kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+ if (!vs_obj->pages) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
+ if (ret)
+ goto err_free_page;
+
+ vs_obj->sgt = sgt;
+
+ return &vs_obj->base;
+
+err_free_page:
+ kvfree(vs_obj->pages);
+err:
+ vs_gem_free_object(&vs_obj->base);
+
+ return ERR_PTR(ret);
+}
+
+int vs_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ int ret = 0;
+
+ ret = drm_gem_mmap_obj(obj, obj->size, vma);
+ if (ret < 0)
+ return ret;
+
+ return vs_gem_mmap_obj(obj, vma);
+}
+
+int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ obj = vma->vm_private_data;
+
+ if (obj->import_attach)
+ return dma_buf_mmap(obj->dma_buf, vma, 0);
+
+ return vs_gem_mmap_obj(obj, vma);
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_GEM_H__
+#define __VS_GEM_H__
+
+#include <linux/dma-buf.h>
+
+#include <drm/drm_gem.h>
+
+#include "vs_drv.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_prime.h>
+#endif
+
+/*
+ *
+ * @base: drm gem object.
+ * @size: size requested from user
+ * @cookie: cookie returned by dma_alloc_attrs
+ * - not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ * - this address could be physical address without IOMMU and
+ * device address with IOMMU.
+ * @dma_attrs: attribute for DMA API
+ * @get_pages: flag for manually applying for non-contiguous memory.
+ * @pages: Array of backing pages.
+ * @sgt: Imported sg_table.
+ *
+ */
+struct vs_gem_object {
+ struct drm_gem_object base;
+ size_t size;
+ void *cookie;
+ dma_addr_t dma_addr;
+ u32 iova;
+ unsigned long dma_attrs;
+ bool get_pages;
+ struct page **pages;
+ struct sg_table *sgt;
+};
+
+static inline
+struct vs_gem_object *to_vs_gem_object(struct drm_gem_object *obj)
+{
+ return container_of(obj, struct vs_gem_object, base);
+}
+
+struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
+ size_t size);
+
+int vs_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma);
+
+int vs_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *drm,
+ struct drm_mode_create_dumb *args);
+
+int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+
+struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj);
+
+struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf);
+struct drm_gem_object *
+vs_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
+
+#endif /* __VS_GEM_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/vs_drm.h>
+
+#include "vs_type.h"
+#include "vs_crtc.h"
+#include "vs_plane.h"
+#include "vs_gem.h"
+#include "vs_fb.h"
+
+void vs_plane_destory(struct drm_plane *plane)
+{
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+
+ drm_plane_cleanup(plane);
+ kfree(vs_plane);
+}
+
+static void vs_plane_reset(struct drm_plane *plane)
+{
+ struct vs_plane_state *state;
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+
+ if (plane->state) {
+ __drm_atomic_helper_plane_destroy_state(plane->state);
+
+ state = to_vs_plane_state(plane->state);
+ kfree(state);
+ plane->state = NULL;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+
+ __drm_atomic_helper_plane_reset(plane, &state->base);
+
+ state->degamma = VS_DEGAMMA_DISABLE;
+ state->degamma_changed = false;
+ state->base.zpos = vs_plane->id;
+
+ memset(&state->status, 0, sizeof(state->status));
+}
+
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
+ struct vs_plane_state *ori_state)
+{
+ state->watermark = ori_state->watermark;
+ state->color_mgmt = ori_state->color_mgmt;
+ state->roi = ori_state->roi;
+
+ if (state->watermark)
+ drm_property_blob_get(state->watermark);
+ if (state->color_mgmt)
+ drm_property_blob_get(state->color_mgmt);
+ if (state->roi)
+ drm_property_blob_get(state->roi);
+}
+
+static int _vs_plane_set_property_blob_from_id(struct drm_device *dev,
+ struct drm_property_blob **blob,
+ uint64_t blob_id,
+ size_t expected_size)
+{
+ struct drm_property_blob *new_blob = NULL;
+
+ if (blob_id) {
+ new_blob = drm_property_lookup_blob(dev, blob_id);
+ if (new_blob == NULL)
+ return -EINVAL;
+
+ if (new_blob->length != expected_size) {
+ drm_property_blob_put(new_blob);
+ return -EINVAL;
+ }
+ }
+
+ drm_property_replace_blob(blob, new_blob);
+ drm_property_blob_put(new_blob);
+
+ return 0;
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+ struct vs_plane_state *ori_state;
+ struct vs_plane_state *state;
+
+ if (WARN_ON(!plane->state))
+ return NULL;
+
+ ori_state = to_vs_plane_state(plane->state);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+ state->degamma = ori_state->degamma;
+ state->degamma_changed = ori_state->degamma_changed;
+
+ _vs_plane_duplicate_blob(state, ori_state);
+ memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+ return &state->base;
+}
+
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+
+ __drm_atomic_helper_plane_destroy_state(state);
+
+ drm_property_blob_put(vs_plane_state->watermark);
+ drm_property_blob_put(vs_plane_state->color_mgmt);
+ drm_property_blob_put(vs_plane_state->roi);
+ kfree(vs_plane_state);
+}
+
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
+ struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+ struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+ int ret = 0;
+
+ if (property == vs_plane->degamma_mode) {
+ if (vs_plane_state->degamma != val) {
+ vs_plane_state->degamma = val;
+ vs_plane_state->degamma_changed = true;
+ } else {
+ vs_plane_state->degamma_changed = false;
+ }
+ } else if (property == vs_plane->watermark_prop) {
+ ret = _vs_plane_set_property_blob_from_id(
+ dev, &vs_plane_state->watermark, val,
+ sizeof(struct drm_vs_watermark));
+ return ret;
+ } else if (property == vs_plane->color_mgmt_prop) {
+ ret = _vs_plane_set_property_blob_from_id(
+ dev, &vs_plane_state->color_mgmt, val,
+ sizeof(struct drm_vs_color_mgmt));
+ return ret;
+ } else if (property == vs_plane->roi_prop) {
+ ret = _vs_plane_set_property_blob_from_id(
+ dev, &vs_plane_state->roi, val,
+ sizeof(struct drm_vs_roi));
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
+ const struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+ const struct vs_plane_state *vs_plane_state =
+ container_of(state, const struct vs_plane_state, base);
+
+ if (property == vs_plane->degamma_mode)
+ *val = vs_plane_state->degamma;
+ else if (property == vs_plane->watermark_prop)
+ *val = (vs_plane_state->watermark) ?
+ vs_plane_state->watermark->base.id :
+ 0;
+ else if (property == vs_plane->color_mgmt_prop)
+ *val = (vs_plane_state->color_mgmt) ?
+ vs_plane_state->color_mgmt->base.id :
+ 0;
+ else if (property == vs_plane->roi_prop)
+ *val = (vs_plane_state->roi) ? vs_plane_state->roi->base.id : 0;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct drm_plane_funcs vs_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = vs_plane_destory,
+ .reset = vs_plane_reset,
+ .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
+ .atomic_destroy_state = vs_plane_atomic_destroy_state,
+ .atomic_set_property = vs_plane_atomic_set_property,
+ .atomic_get_property = vs_plane_atomic_get_property,
+};
+
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
+{
+ const struct drm_format_info *info;
+
+ if (!fb)
+ return 0;
+
+ info = drm_format_info(fb->format->format);
+ if (!info || info->num_planes > MAX_NUM_PLANES)
+ return 0;
+
+ return info->num_planes;
+}
+
+static int vs_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *plane_state =
+ drm_atomic_get_existing_plane_state(state, plane);
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+ if (!crtc || !fb)
+ return 0;
+
+ return vs_plane->funcs->check(vs_crtc->dev, vs_plane, plane_state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+ unsigned char i, num_planes;
+ struct drm_framebuffer *fb;
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+ struct drm_plane_state *state = plane->state;
+ struct vs_crtc *vs_crtc = to_vs_crtc(state->crtc);
+ struct vs_plane_state *plane_state = to_vs_plane_state(state);
+
+ if (!state->fb || !state->crtc)
+ return;
+
+ fb = state->fb;
+
+ num_planes = vs_get_plane_number(fb);
+
+ for (i = 0; i < num_planes; i++) {
+ struct vs_gem_object *vs_obj;
+
+ vs_obj = vs_fb_get_gem_obj(fb, i);
+ vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
+ }
+
+ plane_state->status.src = drm_plane_state_src(state);
+ plane_state->status.dest = drm_plane_state_dest(state);
+
+ vs_plane->funcs->update(vs_crtc->dev, vs_plane);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+ struct vs_plane *vs_plane = to_vs_plane(plane);
+ struct drm_plane_state *old_plane_state =
+ drm_atomic_get_old_plane_state(old_state, plane);
+ struct vs_crtc *vs_crtc = to_vs_crtc(old_plane_state->crtc);
+
+ vs_plane->funcs->disable(vs_crtc->dev, vs_plane, old_plane_state);
+}
+
+const struct drm_plane_helper_funcs vs_plane_helper_funcs = {
+ .atomic_check = vs_plane_atomic_check,
+ .atomic_update = vs_plane_atomic_update,
+ .atomic_disable = vs_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
+ { VS_DEGAMMA_DISABLE, "disabled" },
+ { VS_DEGAMMA_BT709, "preset degamma for BT709" },
+ { VS_DEGAMMA_BT2020, "preset degamma for BT2020" },
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+ struct vs_plane_info *info,
+ unsigned int layer_num,
+ unsigned int possible_crtcs)
+{
+ struct vs_plane *plane;
+ int ret;
+
+ if (!info)
+ return NULL;
+
+ plane = kzalloc(sizeof(struct vs_plane), GFP_KERNEL);
+ if (!plane)
+ return NULL;
+
+ ret = drm_universal_plane_init(drm_dev, &plane->base, possible_crtcs,
+ &vs_plane_funcs, info->formats,
+ info->num_formats, info->modifiers,
+ info->type,
+ info->name ? info->name : NULL);
+ if (ret)
+ goto err_free_plane;
+
+ drm_plane_helper_add(&plane->base, &vs_plane_helper_funcs);
+
+ /* Set up the plane properties */
+ if (info->degamma_size) {
+ plane->degamma_mode = drm_property_create_enum(
+ drm_dev, 0, "DEGAMMA_MODE", vs_degamma_mode_enum_list,
+ ARRAY_SIZE(vs_degamma_mode_enum_list));
+
+ if (!plane->degamma_mode)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base,
+ plane->degamma_mode,
+ VS_DEGAMMA_DISABLE);
+ }
+
+ if (info->rotation) {
+ ret = drm_plane_create_rotation_property(
+ &plane->base, DRM_MODE_ROTATE_0, info->rotation);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->blend_mode) {
+ ret = drm_plane_create_blend_mode_property(&plane->base,
+ info->blend_mode);
+ if (ret)
+ goto error_cleanup_plane;
+ ret = drm_plane_create_alpha_property(&plane->base);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->color_encoding) {
+ ret = drm_plane_create_color_properties(
+ &plane->base, info->color_encoding,
+ BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+ DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->zpos != 255) {
+ ret = drm_plane_create_zpos_property(&plane->base, info->zpos,
+ 0, layer_num - 1);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+ else {
+ ret = drm_plane_create_zpos_immutable_property(&plane->base,
+ info->zpos);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+#endif
+
+ if (info->watermark) {
+ plane->watermark_prop = drm_property_create(
+ drm_dev, DRM_MODE_PROP_BLOB, "WATERMARK", 0);
+ if (!plane->watermark_prop)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base,
+ plane->watermark_prop, 0);
+ }
+
+ if (info->color_mgmt) {
+ plane->color_mgmt_prop = drm_property_create(
+ drm_dev, DRM_MODE_PROP_BLOB, "COLOR_CONFIG", 0);
+ if (!plane->color_mgmt_prop)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base,
+ plane->color_mgmt_prop, 0);
+ }
+
+ if (info->roi) {
+ plane->roi_prop = drm_property_create(
+ drm_dev, DRM_MODE_PROP_BLOB, "ROI", 0);
+ if (!plane->roi_prop)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base, plane->roi_prop,
+ 0);
+ }
+
+ return plane;
+
+error_cleanup_plane:
+ drm_plane_cleanup(&plane->base);
+err_free_plane:
+ kfree(plane);
+ return NULL;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_PLANE_H__
+#define __VS_PLANE_H__
+
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_fourcc.h>
+
+#include "vs_type.h"
+#include "vs_fb.h"
+
+#define MAX_NUM_PLANES 3 /* colour format plane */
+
+struct vs_plane;
+
+struct vs_plane_funcs {
+ void (*update)(struct device *dev, struct vs_plane *plane);
+ void (*disable)(struct device *dev, struct vs_plane *plane,
+ struct drm_plane_state *old_state);
+ int (*check)(struct device *dev, struct vs_plane *plane,
+ struct drm_plane_state *state);
+};
+
+struct vs_plane_status {
+ u32 tile_mode;
+ struct drm_rect src;
+ struct drm_rect dest;
+};
+
+struct vs_plane_state {
+ struct drm_plane_state base;
+ struct vs_plane_status status; /* for debugfs */
+
+ struct drm_property_blob *watermark;
+ struct drm_property_blob *color_mgmt;
+ struct drm_property_blob *roi;
+
+ u32 degamma;
+ bool degamma_changed;
+};
+
+struct vs_plane {
+ struct drm_plane base;
+ u8 id;
+ dma_addr_t dma_addr[MAX_NUM_PLANES];
+
+ struct drm_property *degamma_mode;
+ struct drm_property *watermark_prop;
+ struct drm_property *color_mgmt_prop;
+ struct drm_property *roi_prop;
+
+ const struct vs_plane_funcs *funcs;
+};
+
+void vs_plane_destory(struct drm_plane *plane);
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+ struct vs_plane_info *info,
+ unsigned int layer_num,
+ unsigned int possible_crtcs);
+
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct vs_plane, base);
+}
+
+static inline struct vs_plane_state *
+to_vs_plane_state(struct drm_plane_state *state)
+{
+ return container_of(state, struct vs_plane_state, base);
+}
+#endif /* __VS_PLANE_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+#include <linux/version.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/media-bus-format.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <linux/module.h>
+
+#include <drm/drm_bridge.h>
+#else
+#include <drm/drmP.h>
+#endif
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "vs_crtc.h"
+#include "vs_simple_enc.h"
+
+static const struct simple_encoder_priv hdmi_priv = {
+ .encoder_type = DRM_MODE_ENCODER_TMDS
+};
+
+static const struct simple_encoder_priv dsi_priv = {
+ .encoder_type = DRM_MODE_ENCODER_DSI
+};
+
+static const struct simple_encoder_priv dpi_priv = {
+ .encoder_type = DRM_MODE_ENCODER_DPI
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = drm_encoder_cleanup
+};
+
+static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
+{
+ return container_of(enc, struct simple_encoder, encoder);
+}
+
+static int encoder_parse_dt(struct device *dev)
+{
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+ int ret = 0;
+ int cnt, i;
+ u32 *vals;
+ u32 *masks;
+
+ simple->dss_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "verisilicon,dss-syscon");
+
+ if (IS_ERR(simple->dss_regmap)) {
+ if (PTR_ERR(simple->dss_regmap) != -ENODEV) {
+ dev_err(dev, "failed to get dss-syscon\n");
+ ret = PTR_ERR(simple->dss_regmap);
+ goto err;
+ }
+ simple->dss_regmap = NULL;
+ goto err;
+ }
+
+ cnt = of_property_count_elems_of_size(dev->of_node,
+ "verisilicon,mux-mask", 4);
+ if (!cnt) {
+ ret = cnt;
+ goto err;
+ }
+
+ simple->dss_regdatas = devm_kzalloc(dev,
+ sizeof(*simple->dss_regdatas) * cnt, GFP_KERNEL);
+
+ masks = kcalloc(cnt, sizeof(*masks), GFP_KERNEL);
+ if (!masks) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ vals = kcalloc(cnt, sizeof(*vals), GFP_KERNEL);
+ if (!vals) {
+ ret = -ENOMEM;
+ goto err_free_masks;
+ }
+
+ ret = of_property_read_u32_array(
+ dev->of_node, "verisilicon,mux-mask", masks, cnt);
+ if (ret)
+ goto err_free_vals;
+
+ ret = of_property_read_u32_array(
+ dev->of_node, "verisilicon,mux-val", vals, cnt);
+ if (ret)
+ goto err_free_vals;
+
+ for (i = 0; i < cnt; i++) {
+ simple->dss_regdatas[i].mask = masks[i];
+ simple->dss_regdatas[i].value = vals[i];
+ }
+
+err_free_vals:
+ kfree(vals);
+err_free_masks:
+ kfree(masks);
+err:
+ return ret;
+}
+
+void encoder_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct simple_encoder *simple = to_simple_encoder(encoder);
+ struct dss_data *data = simple->dss_regdatas;
+ int crtc_id;
+
+ if (!simple->dss_regmap)
+ return;
+
+ crtc_id = drm_of_encoder_active_endpoint_id(
+ simple->dev->of_node, encoder);
+
+ regmap_update_bits(simple->dss_regmap, 0, data[crtc_id].mask,
+ data[crtc_id].value);
+}
+
+int encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+ struct drm_connector *connector = conn_state->connector;
+ int ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+ struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
+#endif
+
+ vs_crtc_state->encoder_type = encoder->encoder_type;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
+ bridge_state = drm_atomic_get_bridge_state(
+ crtc_state->state, first_bridge);
+
+ if (IS_ERR(bridge_state)) {
+ if (connector->display_info.num_bus_formats)
+ vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
+ else
+ vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
+ } else {
+ vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
+ }
+#else
+ if (connector->display_info.num_bus_formats)
+ vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
+ else
+ vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+#endif
+
+ switch (vs_crtc_state->output_fmt) {
+ case MEDIA_BUS_FMT_FIXED:
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ /* If MEDIA_BUS_FMT_FIXED, set it to default value */
+ if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
+ vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+ return ret;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+ .atomic_enable = encoder_atomic_enable,
+ .atomic_check = encoder_atomic_check,
+};
+
+static int encoder_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ struct drm_panel *panel;
+ int ret;
+
+ encoder = &simple->encoder;
+
+ /* Encoder. */
+ ret = drm_encoder_init(drm_dev, encoder, &encoder_funcs,
+ simple->priv->encoder_type, NULL);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ /* output port is port1*/
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, &bridge);
+ if (ret)
+ goto err;
+
+ if (panel) {
+ bridge = drm_panel_bridge_add(panel);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+ ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+#else
+ ret = drm_bridge_attach(encoder, bridge, NULL);
+#endif
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ drm_encoder_cleanup(encoder);
+
+ return ret;
+}
+
+static void encoder_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+
+ drm_encoder_cleanup(&simple->encoder);
+}
+
+static const struct component_ops encoder_component_ops = {
+ .bind = encoder_bind,
+ .unbind = encoder_unbind,
+};
+
+static const struct of_device_id simple_encoder_dt_match[] = {
+ { .compatible = "verisilicon,hdmi-encoder", .data = &hdmi_priv},
+ { .compatible = "verisilicon,dp-encoder", .data = &hdmi_priv},
+ { .compatible = "verisilicon,dsi-encoder", .data = &dsi_priv},
+ { .compatible = "verisilicon,dpi-encoder", .data = &dpi_priv},
+ {},
+};
+MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
+
+static int encoder_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct simple_encoder *simple;
+ int ret;
+
+ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+ if (!simple)
+ return -ENOMEM;
+
+ simple->priv = of_device_get_match_data(dev);
+
+ simple->dev = dev;
+
+ dev_set_drvdata(dev, simple);
+
+ ret = encoder_parse_dt(dev);
+ if (ret)
+ return ret;
+
+ return component_add(dev, &encoder_component_ops);
+}
+
+static int encoder_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &encoder_component_ops);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+struct platform_driver simple_encoder_driver = {
+ .probe = encoder_probe,
+ .remove = encoder_remove,
+ .driver = {
+ .name = "vs-simple-encoder",
+ .of_match_table = of_match_ptr(simple_encoder_dt_match),
+ },
+};
+
+MODULE_DESCRIPTION("Simple Encoder Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_SIMPLE_ENC_H_
+#define __VS_SIMPLE_ENC_H_
+
+struct simple_encoder_priv {
+ unsigned char encoder_type;
+};
+
+struct dss_data {
+ u32 mask;
+ u32 value;
+};
+
+struct simple_encoder {
+ struct drm_encoder encoder;
+ struct device *dev;
+ const struct simple_encoder_priv *priv;
+ struct regmap *dss_regmap;
+ struct dss_data *dss_regdatas;
+};
+
+extern struct platform_driver simple_encoder_driver;
+#endif /* __VS_SIMPLE_ENC_H_ */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_TYPE_H__
+#define __VS_TYPE_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct vs_plane_info {
+ const char *name;
+ u8 id;
+ enum drm_plane_type type;
+ unsigned int num_formats;
+ const u32 *formats;
+ u8 num_modifiers;
+ const u64 *modifiers;
+ unsigned int min_width;
+ unsigned int min_height;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int rotation;
+ unsigned int blend_mode;
+ unsigned int color_encoding;
+
+ /* 0 means no de-gamma LUT */
+ unsigned int degamma_size;
+
+ int min_scale; /* 16.16 fixed point */
+ int max_scale; /* 16.16 fixed point */
+
+ u8 zpos; /* default zorder value,
+ * and 255 means unsupported zorder capability */
+
+ bool watermark;
+ bool color_mgmt;
+ bool roi;
+};
+
+struct vs_dc_info {
+ const char *name;
+
+ u8 panel_num;
+
+ /* planes */
+ u8 plane_num;
+ const struct vs_plane_info *planes;
+
+ u8 layer_num;
+ unsigned int max_bpc;
+ unsigned int color_formats;
+
+ /* 0 means no gamma LUT */
+ u16 gamma_size;
+ u8 gamma_bits;
+
+ u16 pitch_alignment;
+
+ bool pipe_sync;
+ bool mmu_prefetch;
+ bool background;
+ bool panel_sync;
+ bool cap_dec;
+};
+
+#endif /* __VS_TYPE_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "vs_virtual.h"
+#include "vs_dc.h"
+#include "vs_gem.h"
+
+static unsigned char __get_bpp(struct vs_virtual_display *vd)
+{
+ if (vd->bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
+ return 10;
+ return 8;
+}
+
+static void vd_dump_destroy(struct vs_virtual_display *vd)
+{
+ if (vd->dump_blob.data) {
+ vunmap(vd->dump_blob.data);
+ vd->dump_blob.data = NULL;
+ }
+ vd->dump_blob.size = 0;
+
+ debugfs_remove(vd->dump_debugfs);
+ vd->dump_debugfs = NULL;
+
+ if (vd->dump_obj) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ drm_gem_object_put(&vd->dump_obj->base);
+#else
+ drm_gem_object_put_unlocked(&vd->dump_obj->base);
+#endif
+ vd->dump_obj = NULL;
+ }
+}
+
+static void vd_dump_create(struct vs_virtual_display *vd,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *drm_dev = vd->encoder.dev;
+ struct vs_dc *dc = dev_get_drvdata(vd->dc);
+ struct vs_gem_object *obj;
+ unsigned int pitch, size;
+ void *kvaddr;
+ char *name;
+
+ if (!dc->funcs)
+ return;
+
+ vd_dump_destroy(vd);
+
+ /* dump in 4bytes XRGB format */
+ pitch = mode->hdisplay * 4;
+ pitch = ALIGN(pitch, dc->hw.info->pitch_alignment);
+ size = PAGE_ALIGN(pitch * mode->vdisplay);
+
+ obj = vs_gem_create_object(drm_dev, size);
+ if (IS_ERR(obj))
+ return;
+
+ vd->dump_obj = obj;
+ vd->pitch = pitch;
+
+ kvaddr = vmap(obj->pages, obj->size >> PAGE_SHIFT, VM_MAP,
+ pgprot_writecombine(PAGE_KERNEL));
+ if (!kvaddr)
+ goto err;
+
+ vd->dump_blob.data = kvaddr;
+ vd->dump_blob.size = obj->size;
+
+ name = kasprintf(GFP_KERNEL, "%dx%d-XRGB-%d.raw",
+ mode->hdisplay, mode->vdisplay,
+ __get_bpp(vd));
+ if (!name)
+ goto err;
+
+ vd->dump_debugfs = debugfs_create_blob(name, 0444,
+ vd->connector.debugfs_entry,
+ &vd->dump_blob);
+ kfree(name);
+
+ return;
+
+err:
+ vd_dump_destroy(vd);
+
+}
+
+static void vd_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct vs_virtual_display *vd;
+
+ drm_encoder_cleanup(encoder);
+ vd = to_virtual_display_with_encoder(encoder);
+ vd_dump_destroy(vd);
+}
+
+static const struct drm_encoder_funcs vd_encoder_funcs = {
+ .destroy = vd_encoder_destroy
+};
+
+static void vd_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct vs_virtual_display *vd;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ vd_dump_create(vd, adjusted_mode);
+}
+
+static void vd_encoder_disable(struct drm_encoder *encoder)
+{
+ struct vs_virtual_display *vd;
+ struct vs_dc *dc;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ dc = dev_get_drvdata(vd->dc);
+ if (dc->funcs && dc->funcs->dump_disable)
+ dc->funcs->dump_disable(vd->dc);
+}
+
+static void vd_encoder_enable(struct drm_encoder *encoder)
+{
+ struct vs_virtual_display *vd;
+ struct vs_dc *dc;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ dc = dev_get_drvdata(vd->dc);
+ if (dc->funcs && dc->funcs->dump_enable && vd->dump_obj)
+ dc->funcs->dump_enable(vd->dc, vd->dump_obj->iova,
+ vd->pitch);
+}
+
+static const struct drm_encoder_helper_funcs vd_encoder_helper_funcs = {
+ .mode_set = vd_mode_set,
+ .enable = vd_encoder_enable,
+ .disable = vd_encoder_disable,
+};
+
+static int vd_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode = NULL;
+ unsigned int i;
+ static const struct display_mode {
+ int w, h;
+ } cvt_mode[] = {
+ {640, 480},
+ {720, 480},
+ {800, 600},
+ {1024, 768},
+ {1280, 720},
+ {1280, 1024},
+ {1400, 1050},
+ {1680, 1050},
+ {1600, 1200},
+ {1920, 1080},
+ {1920, 1200},
+ {3840, 2160}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(cvt_mode); i++) {
+ mode = drm_cvt_mode(dev, cvt_mode[i].w, cvt_mode[i].h,
+ 60, false, false, false);
+ drm_mode_probed_add(connector, mode);
+ }
+ return 0;
+}
+
+static struct drm_encoder *vd_best_encoder(struct drm_connector *connector)
+{
+ struct vs_virtual_display *vd;
+
+ vd = to_virtual_display_with_connector(connector);
+ return &vd->encoder;
+}
+
+static enum drm_mode_status vd_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs vd_connector_helper_funcs = {
+ .get_modes = vd_get_modes,
+ .mode_valid = vd_mode_valid,
+ .best_encoder = vd_best_encoder,
+};
+
+static void vd_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+vd_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static const struct drm_connector_funcs vd_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = vd_connector_destroy,
+ .detect = vd_connector_detect,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+static int vd_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct vs_virtual_display *vd = dev_get_drvdata(dev);
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct device_node *ep, *np;
+ struct platform_device *pdev;
+ int ret;
+
+ /* Encoder */
+ encoder = &vd->encoder;
+ ret = drm_encoder_init(drm_dev, encoder, &vd_encoder_funcs,
+ DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret)
+ return ret;
+ drm_encoder_helper_add(encoder, &vd_encoder_helper_funcs);
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ /* Connector */
+ connector = &vd->connector;
+ ret = drm_connector_init(drm_dev, connector, &vd_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ if (ret)
+ goto connector_init_err;
+ drm_connector_helper_add(connector, &vd_connector_helper_funcs);
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ connector->dpms = DRM_MODE_DPMS_OFF;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ ret = drm_connector_register(connector);
+ if (ret)
+ goto connector_reg_err;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &vd->bus_format, 1);
+
+ /* attach */
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ goto attach_err;
+
+ ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+ if (!ep) {
+ ret = -EINVAL;
+ goto attach_err;
+ }
+
+ np = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (!np) {
+ ret = -EINVAL;
+ goto attach_err;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ ret = -EPROBE_DEFER;
+ goto attach_err;
+ }
+ get_device(&pdev->dev);
+ vd->dc = &pdev->dev;
+
+ return 0;
+
+attach_err:
+ drm_connector_unregister(connector);
+connector_reg_err:
+ drm_connector_cleanup(connector);
+connector_init_err:
+ drm_encoder_cleanup(encoder);
+ return ret;
+}
+
+static void vd_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct vs_virtual_display *vd = dev_get_drvdata(dev);
+
+ drm_connector_unregister(&vd->connector);
+ drm_connector_cleanup(&vd->connector);
+ drm_encoder_cleanup(&vd->encoder);
+}
+
+const struct component_ops vd_component_ops = {
+ .bind = vd_bind,
+ .unbind = vd_unbind,
+};
+
+static const struct of_device_id vd_driver_dt_match[] = {
+ { .compatible = "verisilicon,virtual_display", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vd_driver_dt_match);
+
+static int vd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vs_virtual_display *vd;
+ unsigned char bpp;
+
+ vd = devm_kzalloc(dev, sizeof(*vd), GFP_KERNEL);
+ if (!vd)
+ return -ENOMEM;
+
+ vd->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+ of_property_read_u8(dev->of_node, "bpp", &bpp);
+ if (bpp == 8)
+ vd->bus_format = MEDIA_BUS_FMT_RBG888_1X24;
+
+ dev_set_drvdata(dev, vd);
+
+ return component_add(dev, &vd_component_ops);
+}
+
+static int vd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &vd_component_ops);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+struct platform_driver virtual_display_platform_driver = {
+ .probe = vd_probe,
+ .remove = vd_remove,
+ .driver = {
+ .name = "vs-virtual-display",
+ .of_match_table = of_match_ptr(vd_driver_dt_match),
+ },
+};
+
+MODULE_DESCRIPTION("VeriSilicon Virtual Display Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+
+#ifndef __VS_VIRTUAL_H_
+#define __VS_VIRTUAL_H_
+
+#include <linux/debugfs.h>
+
+struct vs_virtual_display {
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+ struct device *dc;
+ u32 bus_format;
+
+ struct dentry *dump_debugfs;
+ struct debugfs_blob_wrapper dump_blob;
+ struct vs_gem_object *dump_obj;
+ unsigned int pitch;
+};
+
+static inline struct vs_virtual_display *
+to_virtual_display_with_connector(struct drm_connector *connector)
+{
+ return container_of(connector, struct vs_virtual_display, connector);
+}
+
+static inline struct vs_virtual_display *
+to_virtual_display_with_encoder(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct vs_virtual_display, encoder);
+}
+
+extern struct platform_driver virtual_display_platform_driver;
+#endif /* __VS_VIRTUAL_H_ */
#define DRM_FORMAT_MOD_VENDOR_ARM 0x08
#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
#define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
+#define DRM_FORMAT_MOD_VENDOR_VS 0x0b
/* add more to the end as needed */
#define AMD_FMT_MOD_CLEAR(field) \
(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
+/*
+ * VeriSilicon Compressed Format
+ *
+ * DEC(Decompressor-Compressor) format consists of category (2 bits),
+ * tile mode (6 bits) and align mode (2 bits).
+ */
+
+#define DRM_FORMAT_MOD_VS_TYPE_NORMAL 0x00
+#define DRM_FORMAT_MOD_VS_TYPE_COMPRESSED 0x01
+#define DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT 0x02
+#define DRM_FORMAT_MOD_VS_TYPE_MASK ((__u64)0x3 << 54)
+
+#define fourcc_mod_vs_code(type, val) \
+ fourcc_mod_code(VS, ((((__u64)type) << 54) | (val)))
+
+#define DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK 0x3F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR 0x00
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR 0x01
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4 0x02
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4 0x03
+#define DRM_FORMAT_MOD_VS_DEC_TILE_4X8 0x04
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X4 0x06
+#define DRM_FORMAT_MOD_VS_DEC_TILE_64X4 0x07
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4 0x08
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 0x09
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 0x0A
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X4 0x0B
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X2 0x0C
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X2 0x0D
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X4 0x0E
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X1 0x0F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X8 0x10
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X16 0x11
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_512X1 0x12
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X4 0x13
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X2 0x14
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X2 0x15
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X1 0x16
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X1 0x17
+#define DRM_FORMAT_MOD_VS_DEC_TILE_128X4 0x18
+#define DRM_FORMAT_MOD_VS_DEC_TILE_256X4 0x19
+#define DRM_FORMAT_MOD_VS_DEC_TILE_512X4 0x1A
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X16 0x1B
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X16 0x1C
+#define DRM_FORMAT_MOD_VS_DEC_TILE_64X16 0x1D
+#define DRM_FORMAT_MOD_VS_DEC_TILE_128X8 0x1E
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S 0x1F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S 0x20
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S 0x21
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB 0x22
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB 0x23
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X8 0x24
+
+#define DRM_FORMAT_MOD_VS_DEC_ALIGN_32 (0x01 << 6)
+#define DRM_FORMAT_MOD_VS_DEC_ALIGN_64 (0x01 << 7)
+
+#define fourcc_mod_vs_dec_code(tile, align) \
+ fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_COMPRESSED, \
+ ((tile) | (align)))
+
+#define DRM_FORMAT_MOD_VS_NORM_MODE_MASK 0x1F
+#define DRM_FORMAT_MOD_VS_LINEAR 0x00
+#define DRM_FORMAT_MOD_VS_TILED4x4 0x01
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR 0x02
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR 0x03
+#define DRM_FORMAT_MOD_VS_TILE_8X8 0x04
+#define DRM_FORMAT_MOD_VS_TILE_MODE1 0x05
+#define DRM_FORMAT_MOD_VS_TILE_MODE2 0x06
+#define DRM_FORMAT_MOD_VS_TILE_8X4 0x07
+#define DRM_FORMAT_MOD_VS_TILE_MODE4 0x08
+#define DRM_FORMAT_MOD_VS_TILE_MODE5 0x09
+#define DRM_FORMAT_MOD_VS_TILE_MODE6 0x0A
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4 0x0B
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8 0x0C
+#define DRM_FORMAT_MOD_VS_TILE_Y 0x0D
+#define DRM_FORMAT_MOD_VS_TILE_128X1 0x0F
+#define DRM_FORMAT_MOD_VS_TILE_256X1 0x10
+#define DRM_FORMAT_MOD_VS_TILE_32X1 0x11
+#define DRM_FORMAT_MOD_VS_TILE_64X1 0x12
+#define DRM_FORMAT_MOD_VS_TILE_MODE4X4 0x15
+
+#define fourcc_mod_vs_norm_code(tile) \
+ fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_NORMAL, \
+ (tile))
+
+#define fourcc_mod_vs_custom_code(tile) \
+ fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT, \
+ (tile))
+
#if defined(__cplusplus)
}
#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRM_H__
+#define __VS_DRM_H__
+
+#include "drm.h"
+
+enum drm_vs_degamma_mode {
+ VS_DEGAMMA_DISABLE = 0,
+ VS_DEGAMMA_BT709 = 1,
+ VS_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_vs_sync_dc_mode {
+ VS_SINGLE_DC = 0,
+ VS_MULTI_DC_PRIMARY = 1,
+ VS_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_vs_mmu_prefetch_mode {
+ VS_MMU_PREFETCH_DISABLE = 0,
+ VS_MMU_PREFETCH_ENABLE = 1,
+};
+
+struct drm_vs_watermark {
+ __u32 watermark;
+ __u8 qos_low;
+ __u8 qos_high;
+};
+
+struct drm_vs_color_mgmt {
+ __u32 colorkey;
+ __u32 colorkey_high;
+ __u32 clear_value;
+ bool clear_enable;
+ bool transparency;
+};
+
+struct drm_vs_roi {
+ bool enable;
+ __u16 roi_x;
+ __u16 roi_y;
+ __u16 roi_w;
+ __u16 roi_h;
+};
+
+#endif /* __VS_DRM_H__ */