]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge airlied/drm-next into drm-misc-next
authorSean Paul <seanpaul@chromium.org>
Wed, 26 Jul 2017 22:39:07 +0000 (18:39 -0400)
committerSean Paul <seanpaul@chromium.org>
Wed, 26 Jul 2017 22:39:07 +0000 (18:39 -0400)
Backmerge drm-next with -rc2 in it to pull in a couple stm patches that
were previously incorrectly applied to -misc-next. By picking them up in
the correct manner, git will hopefully fix any errant trees that are out
in the wild.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
drivers/gpu/drm/bridge/panel.c
drivers/gpu/drm/stm/Kconfig
drivers/gpu/drm/stm/Makefile
drivers/gpu/drm/stm/drv.c
drivers/gpu/drm/stm/dw_mipi_dsi-stm.c [new file with mode: 0644]
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/vc4/Kconfig
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_regs.h
include/drm/drm_bridge.h

index 8e1476941c0ff86b4ae4d032975f60e45616346d..74b5ac7b26d65278e7453301be2b8491e1c6668c 100644 (file)
@@ -1,7 +1,6 @@
 * STMicroelectronics STM32 lcd-tft display controller
 
 - ltdc: lcd-tft display controller host
-  must be a sub-node of st-display-subsystem
   Required properties:
   - compatible: "st,stm32-ltdc"
   - reg: Physical base address of the IP registers and length of memory mapped region.
   Required nodes:
     - Video port for RGB output.
 
-Example:
+* STMicroelectronics STM32 DSI controller specific extensions to Synopsys
+  DesignWare MIPI DSI host controller
 
+The STMicroelectronics STM32 DSI controller uses the Synopsys DesignWare MIPI
+DSI host controller. For all mandatory properties & nodes, please refer
+to the related documentation in [5].
+
+Mandatory properties specific to STM32 DSI:
+- #address-cells: Should be <1>.
+- #size-cells: Should be <0>.
+- compatible: "st,stm32-dsi".
+- clock-names:
+  - phy pll reference clock string name, must be "ref".
+- resets: see [5].
+- reset-names: see [5].
+
+Mandatory nodes specific to STM32 DSI:
+- ports: A node containing DSI input & output port nodes with endpoint
+  definitions as documented in [3] & [4].
+  - port@0: DSI input port node, connected to the ltdc rgb output port.
+  - port@1: DSI output port node, connected to a panel or a bridge input port.
+- panel or bridge node: A node containing the panel or bridge description as
+  documented in [6].
+  - port: panel or bridge port node, connected to the DSI output port (port@1).
+
+Note: You can find more documentation in the following references
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/reset/reset.txt
+[3] Documentation/devicetree/bindings/media/video-interfaces.txt
+[4] Documentation/devicetree/bindings/graph.txt
+[5] Documentation/devicetree/bindings/display/bridge/dw_mipi_dsi.txt
+[6] Documentation/devicetree/bindings/display/mipi-dsi-bus.txt
+
+Example 1: RGB panel
 / {
        ...
        soc {
@@ -34,3 +65,73 @@ Example:
                };
        };
 };
+
+Example 2: DSI panel
+
+/ {
+       ...
+       soc {
+       ...
+               ltdc: display-controller@40016800 {
+                       compatible = "st,stm32-ltdc";
+                       reg = <0x40016800 0x200>;
+                       interrupts = <88>, <89>;
+                       resets = <&rcc STM32F4_APB2_RESET(LTDC)>;
+                       clocks = <&rcc 1 CLK_LCD>;
+                       clock-names = "lcd";
+
+                       port {
+                               ltdc_out_dsi: endpoint {
+                                       remote-endpoint = <&dsi_in>;
+                               };
+                       };
+               };
+
+
+               dsi: dsi@40016c00 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "st,stm32-dsi";
+                       reg = <0x40016c00 0x800>;
+                       clocks = <&rcc 1 CLK_F469_DSI>, <&clk_hse>;
+                       clock-names = "ref", "pclk";
+                       resets = <&rcc STM32F4_APB2_RESET(DSI)>;
+                       reset-names = "apb";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       dsi_in: endpoint {
+                                               remote-endpoint = <&ltdc_out_dsi>;
+                                       };
+                               };
+
+                               port@1 {
+                                       reg = <1>;
+                                       dsi_out: endpoint {
+                                               remote-endpoint = <&dsi_in_panel>;
+                                       };
+                               };
+
+                       };
+
+                       panel-dsi@0 {
+                               reg = <0>; /* dsi virtual channel (0..3) */
+                               compatible = ...;
+                               enable-gpios = ...;
+
+                               port {
+                                       dsi_in_panel: endpoint {
+                                               remote-endpoint = <&dsi_out>;
+                                       };
+                               };
+
+                       };
+
+               };
+
+       };
+};
index 685c1a480201f101a282f70d60119fcab0543c7e..292ee92a9c97b754ea98ea931d20715d4c1517cb 100644 (file)
@@ -195,3 +195,33 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
        devm_kfree(panel_bridge->panel->dev, bridge);
 }
 EXPORT_SYMBOL(drm_panel_bridge_remove);
+
+static void devm_drm_panel_bridge_release(struct device *dev, void *res)
+{
+       struct drm_bridge **bridge = res;
+
+       drm_panel_bridge_remove(*bridge);
+}
+
+struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
+                                            struct drm_panel *panel,
+                                            u32 connector_type)
+{
+       struct drm_bridge **ptr, *bridge;
+
+       ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
+                          GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       bridge = drm_panel_bridge_add(panel, connector_type);
+       if (!IS_ERR(bridge)) {
+               *ptr = bridge;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return bridge;
+}
+EXPORT_SYMBOL(devm_drm_panel_bridge_add);
index 4b88223f9aedf0885f611fcef3c4c7bd8b390800..35367ada3bc1562b77f2581958022dae8230859a 100644 (file)
@@ -7,10 +7,16 @@ config DRM_STM
        select DRM_PANEL_BRIDGE
        select VIDEOMODE_HELPERS
        select FB_PROVIDE_GET_FB_UNMAPPED_AREA
-       default y
 
        help
          Enable support for the on-chip display controller on
          STMicroelectronics STM32 MCUs.
          To compile this driver as a module, choose M here: the module
          will be called stm-drm.
+
+config DRM_STM_DSI
+       tristate "STMicroelectronics specific extensions for Synopsys MIPI DSI"
+       depends on DRM_STM
+       select DRM_DW_MIPI_DSI
+       help
+         Choose this option for MIPI DSI support on STMicroelectronics SoC.
index a09ecf450218455913b3732921bc8c4c122b3407..d883adc365a2c29b00bf98cdbfae7d11b8841192 100644 (file)
@@ -2,4 +2,6 @@ stm-drm-y := \
        drv.o \
        ltdc.o
 
+obj-$(CONFIG_DRM_STM_DSI) += dw_mipi_dsi-stm.o
+
 obj-$(CONFIG_DRM_STM) += stm-drm.o
index 83ab48f1fd0087f7b26572b3ed11bc7f8736fbcd..095971f0aef36f6b2158e03b3126141f8b986e78 100644 (file)
 
 #include "ltdc.h"
 
-#define DRIVER_NAME            "stm"
-#define DRIVER_DESC            "STMicroelectronics SoC DRM"
-#define DRIVER_DATE            "20170330"
-#define DRIVER_MAJOR           1
-#define DRIVER_MINOR           0
-#define DRIVER_PATCH_LEVEL     0
-
 #define STM_MAX_FB_WIDTH       2048
 #define STM_MAX_FB_HEIGHT      2048 /* same as width to handle orientation */
 
@@ -59,12 +52,12 @@ static struct drm_driver drv_driver = {
        .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
                           DRIVER_ATOMIC,
        .lastclose = drv_lastclose,
-       .name = DRIVER_NAME,
-       .desc = DRIVER_DESC,
-       .date = DRIVER_DATE,
-       .major = DRIVER_MAJOR,
-       .minor = DRIVER_MINOR,
-       .patchlevel = DRIVER_PATCH_LEVEL,
+       .name = "stm",
+       .desc = "STMicroelectronics SoC DRM",
+       .date = "20170330",
+       .major = 1,
+       .minor = 0,
+       .patchlevel = 0,
        .fops = &drv_driver_fops,
        .dumb_create = drm_gem_cma_dumb_create,
        .dumb_map_offset = drm_gem_cma_dumb_map_offset,
@@ -206,7 +199,7 @@ static struct platform_driver stm_drm_platform_driver = {
        .probe = stm_drm_platform_probe,
        .remove = stm_drm_platform_remove,
        .driver = {
-               .name = DRIVER_NAME,
+               .name = "stm32-display",
                .of_match_table = drv_dt_ids,
        },
 };
diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
new file mode 100644 (file)
index 0000000..568c5d0
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/bridge/dw_mipi_dsi.h>
+#include <video/mipi_display.h>
+
+/* DSI wrapper register & bit definitions */
+/* Note: registers are named as in the Reference Manual */
+#define DSI_WCFGR      0x0400          /* Wrapper ConFiGuration Reg */
+#define WCFGR_DSIM     BIT(0)          /* DSI Mode */
+#define WCFGR_COLMUX   GENMASK(3, 1)   /* COLor MUltipleXing */
+
+#define DSI_WCR                0x0404          /* Wrapper Control Reg */
+#define WCR_DSIEN      BIT(3)          /* DSI ENable */
+
+#define DSI_WISR       0x040C          /* Wrapper Interrupt and Status Reg */
+#define WISR_PLLLS     BIT(8)          /* PLL Lock Status */
+#define WISR_RRS       BIT(12)         /* Regulator Ready Status */
+
+#define DSI_WPCR0      0x0418          /* Wrapper Phy Conf Reg 0 */
+#define WPCR0_UIX4     GENMASK(5, 0)   /* Unit Interval X 4 */
+#define WPCR0_TDDL     BIT(16)         /* Turn Disable Data Lanes */
+
+#define DSI_WRPCR      0x0430          /* Wrapper Regulator & Pll Ctrl Reg */
+#define WRPCR_PLLEN    BIT(0)          /* PLL ENable */
+#define WRPCR_NDIV     GENMASK(8, 2)   /* pll loop DIVision Factor */
+#define WRPCR_IDF      GENMASK(14, 11) /* pll Input Division Factor */
+#define WRPCR_ODF      GENMASK(17, 16) /* pll Output Division Factor */
+#define WRPCR_REGEN    BIT(24)         /* REGulator ENable */
+#define WRPCR_BGREN    BIT(28)         /* BandGap Reference ENable */
+#define IDF_MIN                1
+#define IDF_MAX                7
+#define NDIV_MIN       10
+#define NDIV_MAX       125
+#define ODF_MIN                1
+#define ODF_MAX                8
+
+/* dsi color format coding according to the datasheet */
+enum dsi_color {
+       DSI_RGB565_CONF1,
+       DSI_RGB565_CONF2,
+       DSI_RGB565_CONF3,
+       DSI_RGB666_CONF1,
+       DSI_RGB666_CONF2,
+       DSI_RGB888,
+};
+
+#define LANE_MIN_KBPS  31250
+#define LANE_MAX_KBPS  500000
+
+/* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */
+#define SLEEP_US       1000
+#define TIMEOUT_US     200000
+
+struct dw_mipi_dsi_stm {
+       void __iomem *base;
+       struct clk *pllref_clk;
+};
+
+static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
+{
+       writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg)
+{
+       return readl(dsi->base + reg);
+}
+
+static inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
+{
+       dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
+}
+
+static inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
+{
+       dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask);
+}
+
+static inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg,
+                                  u32 mask, u32 val)
+{
+       dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
+}
+
+static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
+{
+       switch (fmt) {
+       case MIPI_DSI_FMT_RGB888:
+               return DSI_RGB888;
+       case MIPI_DSI_FMT_RGB666:
+               return DSI_RGB666_CONF2;
+       case MIPI_DSI_FMT_RGB666_PACKED:
+               return DSI_RGB666_CONF1;
+       case MIPI_DSI_FMT_RGB565:
+               return DSI_RGB565_CONF1;
+       default:
+               DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n");
+       }
+       return DSI_RGB888;
+}
+
+static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
+{
+       /* prevent from division by 0 */
+       if (idf * odf)
+               return DIV_ROUND_CLOSEST(clkin_khz * ndiv, idf * odf);
+
+       return 0;
+}
+
+static int dsi_pll_get_params(int clkin_khz, int clkout_khz,
+                             int *idf, int *ndiv, int *odf)
+{
+       int i, o, n, n_min, n_max;
+       int fvco_min, fvco_max, delta, best_delta; /* all in khz */
+
+       /* Early checks preventing division by 0 & odd results */
+       if ((clkin_khz <= 0) || (clkout_khz <= 0))
+               return -EINVAL;
+
+       fvco_min = LANE_MIN_KBPS * 2 * ODF_MAX;
+       fvco_max = LANE_MAX_KBPS * 2 * ODF_MIN;
+
+       best_delta = 1000000; /* big started value (1000000khz) */
+
+       for (i = IDF_MIN; i <= IDF_MAX; i++) {
+               /* Compute ndiv range according to Fvco */
+               n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
+               n_max = (fvco_max * i) / (2 * clkin_khz);
+
+               /* No need to continue idf loop if we reach ndiv max */
+               if (n_min >= NDIV_MAX)
+                       break;
+
+               /* Clamp ndiv to valid values */
+               if (n_min < NDIV_MIN)
+                       n_min = NDIV_MIN;
+               if (n_max > NDIV_MAX)
+                       n_max = NDIV_MAX;
+
+               for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
+                       n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
+                       /* Check ndiv according to vco range */
+                       if ((n < n_min) || (n > n_max))
+                               continue;
+                       /* Check if new delta is better & saves parameters */
+                       delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
+                               clkout_khz;
+                       if (delta < 0)
+                               delta = -delta;
+                       if (delta < best_delta) {
+                               *idf = i;
+                               *ndiv = n;
+                               *odf = o;
+                               best_delta = delta;
+                       }
+                       /* fast return in case of "perfect result" */
+                       if (!delta)
+                               return 0;
+               }
+       }
+
+       return 0;
+}
+
+static int dw_mipi_dsi_phy_init(void *priv_data)
+{
+       struct dw_mipi_dsi_stm *dsi = priv_data;
+       u32 val;
+       int ret;
+
+       /* Enable the regulator */
+       dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
+       ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS,
+                                SLEEP_US, TIMEOUT_US);
+       if (ret)
+               DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n");
+
+       /* Enable the DSI PLL & wait for its lock */
+       dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
+       ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
+                                SLEEP_US, TIMEOUT_US);
+       if (ret)
+               DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n");
+
+       /* Enable the DSI wrapper */
+       dsi_set(dsi, DSI_WCR, WCR_DSIEN);
+
+       return 0;
+}
+
+static int
+dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
+                         unsigned long mode_flags, u32 lanes, u32 format,
+                         unsigned int *lane_mbps)
+{
+       struct dw_mipi_dsi_stm *dsi = priv_data;
+       unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
+       int ret, bpp;
+       u32 val;
+
+       pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
+
+       /* Compute requested pll out */
+       bpp = mipi_dsi_pixel_format_to_bpp(format);
+       pll_out_khz = mode->clock * bpp / lanes;
+       /* Add 20% to pll out to be higher than pixel bw (burst mode only) */
+       pll_out_khz = (pll_out_khz * 12) / 10;
+       if (pll_out_khz > LANE_MAX_KBPS) {
+               pll_out_khz = LANE_MAX_KBPS;
+               DRM_WARN("Warning max phy mbps is used\n");
+       }
+       if (pll_out_khz < LANE_MIN_KBPS) {
+               pll_out_khz = LANE_MIN_KBPS;
+               DRM_WARN("Warning min phy mbps is used\n");
+       }
+
+       /* Compute best pll parameters */
+       idf = 0;
+       ndiv = 0;
+       odf = 0;
+       ret = dsi_pll_get_params(pll_in_khz, pll_out_khz, &idf, &ndiv, &odf);
+       if (ret)
+               DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
+
+       /* Get the adjusted pll out value */
+       pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
+
+       /* Set the PLL division factors */
+       dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
+                       (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
+
+       /* Compute uix4 & set the bit period in high-speed mode */
+       val = 4000000 / pll_out_khz;
+       dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
+
+       /* Select video mode by resetting DSIM bit */
+       dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
+
+       /* Select the color coding */
+       dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
+                       dsi_color_from_mipi(format) << 1);
+
+       *lane_mbps = pll_out_khz / 1000;
+
+       DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n",
+                        pll_in_khz, pll_out_khz, *lane_mbps);
+
+       return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
+       .init = dw_mipi_dsi_phy_init,
+       .get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
+};
+
+static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = {
+       .max_data_lanes = 2,
+       .phy_ops = &dw_mipi_dsi_stm_phy_ops,
+};
+
+static const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = {
+       { .compatible = "st,stm32-dsi", .data = &dw_mipi_dsi_stm_plat_data, },
+       { },
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids);
+
+static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct dw_mipi_dsi_stm *dsi;
+       struct resource *res;
+       int ret;
+
+       dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+       if (!dsi)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               DRM_ERROR("Unable to get resource\n");
+               return -ENODEV;
+       }
+
+       dsi->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(dsi->base)) {
+               DRM_ERROR("Unable to get dsi registers\n");
+               return PTR_ERR(dsi->base);
+       }
+
+       dsi->pllref_clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(dsi->pllref_clk)) {
+               ret = PTR_ERR(dsi->pllref_clk);
+               dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(dsi->pllref_clk);
+       if (ret) {
+               dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+               return ret;
+       }
+
+       dw_mipi_dsi_stm_plat_data.base = dsi->base;
+       dw_mipi_dsi_stm_plat_data.priv_data = dsi;
+
+       ret = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
+       if (ret) {
+               DRM_ERROR("Failed to initialize mipi dsi host\n");
+               clk_disable_unprepare(dsi->pllref_clk);
+       }
+
+       return ret;
+}
+
+static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
+{
+       struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
+
+       clk_disable_unprepare(dsi->pllref_clk);
+       dw_mipi_dsi_remove(pdev);
+
+       return 0;
+}
+
+static struct platform_driver dw_mipi_dsi_stm_driver = {
+       .probe          = dw_mipi_dsi_stm_probe,
+       .remove         = dw_mipi_dsi_stm_remove,
+       .driver         = {
+               .of_match_table = dw_mipi_dsi_stm_dt_ids,
+               .name   = "dw_mipi_dsi-stm",
+       },
+};
+
+module_platform_driver(dw_mipi_dsi_stm_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver");
+MODULE_LICENSE("GPL v2");
index e46b427eacc7d119d96833dd760b4987f46c6705..04cc66d6b96f1e4d49d6dc229b8f7404b0801b02 100644 (file)
  * an extra offset specified with reg_ofs.
  */
 #define REG_OFS_NONE   0
-#define REG_OFS_4      4 /* Insertion of "Layer Configuration 2" reg */
+#define REG_OFS_4      4               /* Insertion of "Layer Conf. 2" reg */
 #define REG_OFS                (ldev->caps.reg_ofs)
-#define LAY_OFS                0x80    /* Register Offset between 2 layers */
+#define LAY_OFS                0x80            /* Register Offset between 2 layers */
 
 /* Global register offsets */
-#define LTDC_IDR       0x0000 /* IDentification */
-#define LTDC_LCR       0x0004 /* Layer Count */
-#define LTDC_SSCR      0x0008 /* Synchronization Size Configuration */
-#define LTDC_BPCR      0x000C /* Back Porch Configuration */
-#define LTDC_AWCR      0x0010 /* Active Width Configuration */
-#define LTDC_TWCR      0x0014 /* Total Width Configuration */
-#define LTDC_GCR       0x0018 /* Global Control */
-#define LTDC_GC1R      0x001C /* Global Configuration 1 */
-#define LTDC_GC2R      0x0020 /* Global Configuration 2 */
-#define LTDC_SRCR      0x0024 /* Shadow Reload Configuration */
-#define LTDC_GACR      0x0028 /* GAmma Correction */
-#define LTDC_BCCR      0x002C /* Background Color Configuration */
-#define LTDC_IER       0x0034 /* Interrupt Enable */
-#define LTDC_ISR       0x0038 /* Interrupt Status */
-#define LTDC_ICR       0x003C /* Interrupt Clear */
-#define LTDC_LIPCR     0x0040 /* Line Interrupt Position Configuration */
-#define LTDC_CPSR      0x0044 /* Current Position Status */
-#define LTDC_CDSR      0x0048 /* Current Display Status */
+#define LTDC_IDR       0x0000          /* IDentification */
+#define LTDC_LCR       0x0004          /* Layer Count */
+#define LTDC_SSCR      0x0008          /* Synchronization Size Configuration */
+#define LTDC_BPCR      0x000C          /* Back Porch Configuration */
+#define LTDC_AWCR      0x0010          /* Active Width Configuration */
+#define LTDC_TWCR      0x0014          /* Total Width Configuration */
+#define LTDC_GCR       0x0018          /* Global Control */
+#define LTDC_GC1R      0x001C          /* Global Configuration 1 */
+#define LTDC_GC2R      0x0020          /* Global Configuration 2 */
+#define LTDC_SRCR      0x0024          /* Shadow Reload Configuration */
+#define LTDC_GACR      0x0028          /* GAmma Correction */
+#define LTDC_BCCR      0x002C          /* Background Color Configuration */
+#define LTDC_IER       0x0034          /* Interrupt Enable */
+#define LTDC_ISR       0x0038          /* Interrupt Status */
+#define LTDC_ICR       0x003C          /* Interrupt Clear */
+#define LTDC_LIPCR     0x0040          /* Line Interrupt Position Conf. */
+#define LTDC_CPSR      0x0044          /* Current Position Status */
+#define LTDC_CDSR      0x0048          /* Current Display Status */
 
 /* Layer register offsets */
-#define LTDC_L1LC1R    (0x0080)           /* L1 Layer Configuration 1 */
-#define LTDC_L1LC2R    (0x0084)           /* L1 Layer Configuration 2 */
-#define LTDC_L1CR      (0x0084 + REG_OFS) /* L1 Control */
-#define LTDC_L1WHPCR   (0x0088 + REG_OFS) /* L1 Window Hor Position Config */
-#define LTDC_L1WVPCR   (0x008C + REG_OFS) /* L1 Window Vert Position Config */
-#define LTDC_L1CKCR    (0x0090 + REG_OFS) /* L1 Color Keying Configuration */
-#define LTDC_L1PFCR    (0x0094 + REG_OFS) /* L1 Pixel Format Configuration */
-#define LTDC_L1CACR    (0x0098 + REG_OFS) /* L1 Constant Alpha Config */
-#define LTDC_L1DCCR    (0x009C + REG_OFS) /* L1 Default Color Configuration */
-#define LTDC_L1BFCR    (0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */
-#define LTDC_L1FBBCR   (0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */
-#define LTDC_L1AFBCR   (0x00A8 + REG_OFS) /* L1 AuxFB Control */
-#define LTDC_L1CFBAR   (0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */
-#define LTDC_L1CFBLR   (0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */
-#define LTDC_L1CFBLNR  (0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */
-#define LTDC_L1AFBAR   (0x00B8 + REG_OFS) /* L1 AuxFB Address */
-#define LTDC_L1AFBLR   (0x00BC + REG_OFS) /* L1 AuxFB Length */
-#define LTDC_L1AFBLNR  (0x00C0 + REG_OFS) /* L1 AuxFB Line Number */
-#define LTDC_L1CLUTWR  (0x00C4 + REG_OFS) /* L1 CLUT Write */
-#define LTDC_L1YS1R    (0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */
-#define LTDC_L1YS2R    (0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */
+#define LTDC_L1LC1R    (0x80)          /* L1 Layer Configuration 1 */
+#define LTDC_L1LC2R    (0x84)          /* L1 Layer Configuration 2 */
+#define LTDC_L1CR      (0x84 + REG_OFS)/* L1 Control */
+#define LTDC_L1WHPCR   (0x88 + REG_OFS)/* L1 Window Hor Position Config */
+#define LTDC_L1WVPCR   (0x8C + REG_OFS)/* L1 Window Vert Position Config */
+#define LTDC_L1CKCR    (0x90 + REG_OFS)/* L1 Color Keying Configuration */
+#define LTDC_L1PFCR    (0x94 + REG_OFS)/* L1 Pixel Format Configuration */
+#define LTDC_L1CACR    (0x98 + REG_OFS)/* L1 Constant Alpha Config */
+#define LTDC_L1DCCR    (0x9C + REG_OFS)/* L1 Default Color Configuration */
+#define LTDC_L1BFCR    (0xA0 + REG_OFS)/* L1 Blend Factors Configuration */
+#define LTDC_L1FBBCR   (0xA4 + REG_OFS)/* L1 FrameBuffer Bus Control */
+#define LTDC_L1AFBCR   (0xA8 + REG_OFS)/* L1 AuxFB Control */
+#define LTDC_L1CFBAR   (0xAC + REG_OFS)/* L1 Color FrameBuffer Address */
+#define LTDC_L1CFBLR   (0xB0 + REG_OFS)/* L1 Color FrameBuffer Length */
+#define LTDC_L1CFBLNR  (0xB4 + REG_OFS)/* L1 Color FrameBuffer Line Nb */
+#define LTDC_L1AFBAR   (0xB8 + REG_OFS)/* L1 AuxFB Address */
+#define LTDC_L1AFBLR   (0xBC + REG_OFS)/* L1 AuxFB Length */
+#define LTDC_L1AFBLNR  (0xC0 + REG_OFS)/* L1 AuxFB Line Number */
+#define LTDC_L1CLUTWR  (0xC4 + REG_OFS)/* L1 CLUT Write */
+#define LTDC_L1YS1R    (0xE0 + REG_OFS)/* L1 YCbCr Scale 1 */
+#define LTDC_L1YS2R    (0xE4 + REG_OFS)/* L1 YCbCr Scale 2 */
 
 /* Bit definitions */
 #define SSCR_VSH       GENMASK(10, 0)  /* Vertical Synchronization Height */
 
 #define GCR_LTDCEN     BIT(0)          /* LTDC ENable */
 #define GCR_DEN                BIT(16)         /* Dither ENable */
-#define GCR_PCPOL      BIT(28)         /* Pixel Clock POLarity */
-#define GCR_DEPOL      BIT(29)         /* Data Enable POLarity */
-#define GCR_VSPOL      BIT(30)         /* Vertical Synchro POLarity */
-#define GCR_HSPOL      BIT(31)         /* Horizontal Synchro POLarity */
+#define GCR_PCPOL      BIT(28)         /* Pixel Clock POLarity-Inverted */
+#define GCR_DEPOL      BIT(29)         /* Data Enable POLarity-High */
+#define GCR_VSPOL      BIT(30)         /* Vertical Synchro POLarity-High */
+#define GCR_HSPOL      BIT(31)         /* Horizontal Synchro POLarity-High */
 
 #define GC1R_WBCH      GENMASK(3, 0)   /* Width of Blue CHannel output */
 #define GC1R_WGCH      GENMASK(7, 4)   /* Width of Green Channel output */
 #define LXCFBLR_CFBLL  GENMASK(12, 0)  /* Color Frame Buffer Line Length */
 #define LXCFBLR_CFBP   GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
 
-#define LXCFBLNR_CFBLN GENMASK(10, 0)   /* Color Frame Buffer Line Number */
+#define LXCFBLNR_CFBLN GENMASK(10, 0)  /* Color Frame Buffer Line Number */
 
-#define HSPOL_AL   0           /* Horizontal Sync POLarity Active Low */
-#define VSPOL_AL   0           /* Vertical Sync POLarity Active Low */
-#define DEPOL_AL   0           /* Data Enable POLarity Active Low */
-#define PCPOL_IPC  0           /* Input Pixel Clock */
-#define HSPOL_AH   GCR_HSPOL   /* Horizontal Sync POLarity Active High */
-#define VSPOL_AH   GCR_VSPOL   /* Vertical Sync POLarity Active High */
-#define DEPOL_AH   GCR_DEPOL   /* Data Enable POLarity Active High */
-#define PCPOL_IIPC GCR_PCPOL   /* Inverted Input Pixel Clock */
-#define CONSTA_MAX 0xFF                /* CONSTant Alpha MAX= 1.0 */
-#define BF1_PAXCA  0x600       /* Pixel Alpha x Constant Alpha */
-#define BF1_CA     0x400       /* Constant Alpha */
-#define BF2_1PAXCA 0x007       /* 1 - (Pixel Alpha x Constant Alpha) */
-#define BF2_1CA           0x005        /* 1 - Constant Alpha */
+#define CONSTA_MAX     0xFF            /* CONSTant Alpha MAX= 1.0 */
+#define BF1_PAXCA      0x600           /* Pixel Alpha x Constant Alpha */
+#define BF1_CA         0x400           /* Constant Alpha */
+#define BF2_1PAXCA     0x007           /* 1 - (Pixel Alpha x Constant Alpha) */
+#define BF2_1CA                0x005           /* 1 - Constant Alpha */
 
-#define NB_PF           8       /* Max nb of HW pixel format */
+#define NB_PF          8               /* Max nb of HW pixel format */
 
 enum ltdc_pix_fmt {
        PF_NONE,
        /* RGB formats */
-       PF_ARGB8888,    /* ARGB [32 bits] */
-       PF_RGBA8888,    /* RGBA [32 bits] */
-       PF_RGB888,      /* RGB [24 bits] */
-       PF_RGB565,      /* RGB [16 bits] */
-       PF_ARGB1555,    /* ARGB A:1 bit RGB:15 bits [16 bits] */
-       PF_ARGB4444,    /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
+       PF_ARGB8888,            /* ARGB [32 bits] */
+       PF_RGBA8888,            /* RGBA [32 bits] */
+       PF_RGB888,              /* RGB [24 bits] */
+       PF_RGB565,              /* RGB [16 bits] */
+       PF_ARGB1555,            /* ARGB A:1 bit RGB:15 bits [16 bits] */
+       PF_ARGB4444,            /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
        /* Indexed formats */
-       PF_L8,          /* Indexed 8 bits [8 bits] */
-       PF_AL44,        /* Alpha:4 bits + indexed 4 bits [8 bits] */
-       PF_AL88         /* Alpha:8 bits + indexed 8 bits [16 bits] */
+       PF_L8,                  /* Indexed 8 bits [8 bits] */
+       PF_AL44,                /* Alpha:4 bits + indexed 4 bits [8 bits] */
+       PF_AL88                 /* Alpha:8 bits + indexed 8 bits [16 bits] */
 };
 
 /* The index gives the encoding of the pixel format for an HW version */
 static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
-       PF_ARGB8888,    /* 0x00 */
-       PF_RGB888,      /* 0x01 */
-       PF_RGB565,      /* 0x02 */
-       PF_ARGB1555,    /* 0x03 */
-       PF_ARGB4444,    /* 0x04 */
-       PF_L8,          /* 0x05 */
-       PF_AL44,        /* 0x06 */
-       PF_AL88         /* 0x07 */
+       PF_ARGB8888,            /* 0x00 */
+       PF_RGB888,              /* 0x01 */
+       PF_RGB565,              /* 0x02 */
+       PF_ARGB1555,            /* 0x03 */
+       PF_ARGB4444,            /* 0x04 */
+       PF_L8,                  /* 0x05 */
+       PF_AL44,                /* 0x06 */
+       PF_AL88                 /* 0x07 */
 };
 
 static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
-       PF_ARGB8888,    /* 0x00 */
-       PF_RGB888,      /* 0x01 */
-       PF_RGB565,      /* 0x02 */
-       PF_RGBA8888,    /* 0x03 */
-       PF_AL44,        /* 0x04 */
-       PF_L8,          /* 0x05 */
-       PF_ARGB1555,    /* 0x06 */
-       PF_ARGB4444     /* 0x07 */
+       PF_ARGB8888,            /* 0x00 */
+       PF_RGB888,              /* 0x01 */
+       PF_RGB565,              /* 0x02 */
+       PF_RGBA8888,            /* 0x03 */
+       PF_AL44,                /* 0x04 */
+       PF_L8,                  /* 0x05 */
+       PF_ARGB1555,            /* 0x06 */
+       PF_ARGB4444             /* 0x07 */
 };
 
 static inline u32 reg_read(void __iomem *base, u32 reg)
@@ -302,7 +294,7 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
        default:
                pf = PF_NONE;
                break;
-       /* Note: There are no DRM_FORMAT for AL44 and AL88 */
+               /* Note: There are no DRM_FORMAT for AL44 and AL88 */
        }
 
        return pf;
@@ -325,8 +317,8 @@ static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
                return DRM_FORMAT_ARGB4444;
        case PF_L8:
                return DRM_FORMAT_C8;
-       case PF_AL44: /* No DRM support */
-       case PF_AL88: /* No DRM support */
+       case PF_AL44:           /* No DRM support */
+       case PF_AL88:           /* No DRM support */
        case PF_NONE:
        default:
                return 0;
@@ -459,20 +451,20 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 
        clk_enable(ldev->pixel_clk);
 
-       /* Configures the HS, VS, DE and PC polarities. */
-       val = HSPOL_AL | VSPOL_AL | DEPOL_AL | PCPOL_IPC;
+       /* Configures the HS, VS, DE and PC polarities. Default Active Low */
+       val = 0;
 
        if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
-               val |= HSPOL_AH;
+               val |= GCR_HSPOL;
 
        if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
-               val |= VSPOL_AH;
+               val |= GCR_VSPOL;
 
        if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
-               val |= DEPOL_AH;
+               val |= GCR_DEPOL;
 
        if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
-               val |= PCPOL_IIPC;
+               val |= GCR_PCPOL;
 
        reg_update_bits(ldev->regs, LTDC_GCR,
                        GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
@@ -519,7 +511,7 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
        }
 }
 
-static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
        .load_lut = ltdc_crtc_load_lut,
        .mode_set_nofb = ltdc_crtc_mode_set_nofb,
        .atomic_flush = ltdc_crtc_atomic_flush,
@@ -545,7 +537,7 @@ void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
        reg_clear(ldev->regs, LTDC_IER, IER_LIE);
 }
 
-static struct drm_crtc_funcs ltdc_crtc_funcs = {
+static const struct drm_crtc_funcs ltdc_crtc_funcs = {
        .destroy = drm_crtc_cleanup,
        .set_config = drm_atomic_helper_set_config,
        .page_flip = drm_atomic_helper_page_flip,
@@ -610,11 +602,11 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
        src_w = state->src_w >> 16;
        src_h = state->src_h >> 16;
 
-       DRM_DEBUG_DRIVER(
-               "plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
-               plane->base.id, fb->base.id,
-               src_w, src_h, src_x, src_y,
-               state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y);
+       DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
+                        plane->base.id, fb->base.id,
+                        src_w, src_h, src_x, src_y,
+                        state->crtc_w, state->crtc_h,
+                        state->crtc_x, state->crtc_y);
 
        bpcr = reg_read(ldev->regs, LTDC_BPCR);
        ahbp = (bpcr & BPCR_AHBP) >> 16;
@@ -639,7 +631,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
        if (val == NB_PF) {
                DRM_ERROR("Pixel format %.4s not supported\n",
                          (char *)&fb->format->format);
-               val = 0; /* set by default ARGB 32 bits */
+               val = 0;        /* set by default ARGB 32 bits */
        }
        reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
 
@@ -653,8 +645,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 
        /* Specifies the constant alpha value */
        val = CONSTA_MAX;
-       reg_update_bits(ldev->regs, LTDC_L1CACR + lofs,
-                       LXCACR_CONSTA, val);
+       reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
 
        /* Specifies the blending factors */
        val = BF1_PAXCA | BF2_1PAXCA;
@@ -663,8 +654,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 
        /* Configures the frame buffer line number */
        val = y1 - y0 + 1;
-       reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs,
-                       LXCFBLNR_CFBLN, val);
+       reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);
 
        /* Sets the FB address */
        paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
@@ -703,7 +693,7 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
                         oldstate->crtc->base.id, plane->base.id);
 }
 
-static struct drm_plane_funcs ltdc_plane_funcs = {
+static const struct drm_plane_funcs ltdc_plane_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = drm_plane_cleanup,
@@ -770,7 +760,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
        struct ltdc_device *ldev = ddev->dev_private;
        struct drm_plane *primary, *overlay;
        unsigned int i;
-       int res;
+       int ret;
 
        primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
        if (!primary) {
@@ -778,9 +768,9 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
                return -EINVAL;
        }
 
-       res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+       ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
                                        &ltdc_crtc_funcs, NULL);
-       if (res) {
+       if (ret) {
                DRM_ERROR("Can not initialize CRTC\n");
                goto cleanup;
        }
@@ -793,7 +783,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
        for (i = 1; i < ldev->caps.nb_layers; i++) {
                overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
                if (!overlay) {
-                       res = -ENOMEM;
+                       ret = -ENOMEM;
                        DRM_ERROR("Can not create overlay plane %d\n", i);
                        goto cleanup;
                }
@@ -803,7 +793,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
 
 cleanup:
        ltdc_plane_destroy_all(ddev);
-       return res;
+       return ret;
 }
 
 /*
@@ -825,7 +815,7 @@ static int ltdc_encoder_init(struct drm_device *ddev)
                return -ENOMEM;
 
        encoder->possible_crtcs = CRTC_MASK;
-       encoder->possible_clones = 0; /* No cloning support */
+       encoder->possible_clones = 0;   /* No cloning support */
 
        drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs,
                         DRM_MODE_ENCODER_DPI, NULL);
@@ -884,7 +874,7 @@ int ltdc_load(struct drm_device *ddev)
        struct drm_panel *panel;
        struct drm_crtc *crtc;
        struct reset_control *rstc;
-       struct resource res;
+       struct resource *res;
        int irq, ret, i;
 
        DRM_DEBUG_DRIVER("\n");
@@ -893,7 +883,7 @@ int ltdc_load(struct drm_device *ddev)
        if (ret)
                return ret;
 
-       rstc = of_reset_control_get(np, NULL);
+       rstc = devm_reset_control_get_exclusive(dev, NULL);
 
        mutex_init(&ldev->err_lock);
 
@@ -908,13 +898,14 @@ int ltdc_load(struct drm_device *ddev)
                return -ENODEV;
        }
 
-       if (of_address_to_resource(np, 0, &res)) {
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
                DRM_ERROR("Unable to get resource\n");
                ret = -ENODEV;
                goto err;
        }
 
-       ldev->regs = devm_ioremap_resource(dev, &res);
+       ldev->regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(ldev->regs)) {
                DRM_ERROR("Unable to get ltdc registers\n");
                ret = PTR_ERR(ldev->regs);
index 4361bdcfd28a2177b762922ebe62ee15d1435fcb..fdae18aeab4fe582d99e576d5600bed3b5afc443 100644 (file)
@@ -19,3 +19,11 @@ config DRM_VC4
          This driver requires that "avoid_warnings=2" be present in
          the config.txt for the firmware, to keep it from smashing
          our display setup.
+
+config DRM_VC4_HDMI_CEC
+       bool "Broadcom VC4 HDMI CEC Support"
+       depends on DRM_VC4
+       select CEC_CORE
+       help
+         Choose this option if you have a Broadcom VC4 GPU
+         and want to use CEC.
index 406d6d83b6c6430200ff480eb55d013ed3b07a2a..da5ee8047e51f5a9fe77f2f1de8500736807a260 100644 (file)
 #include <sound/pcm_drm_eld.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include "media/cec.h"
 #include "vc4_drv.h"
 #include "vc4_regs.h"
 
+#define HSM_CLOCK_FREQ 163682864
+#define CEC_CLOCK_FREQ 40000
+#define CEC_CLOCK_DIV  (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
+
 /* HDMI audio information */
 struct vc4_hdmi_audio {
        struct snd_soc_card card;
@@ -85,6 +90,11 @@ struct vc4_hdmi {
        int hpd_gpio;
        bool hpd_active_low;
 
+       struct cec_adapter *cec_adap;
+       struct cec_msg cec_rx_msg;
+       bool cec_tx_ok;
+       bool cec_irq_was_rx;
+
        struct clk *pixel_clock;
        struct clk *hsm_clock;
 };
@@ -149,6 +159,23 @@ static const struct {
        HDMI_REG(VC4_HDMI_VERTB1),
        HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
        HDMI_REG(VC4_HDMI_TX_PHY_CTL0),
+
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_1),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_2),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_3),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_4),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_5),
+       HDMI_REG(VC4_HDMI_CPU_STATUS),
+       HDMI_REG(VC4_HDMI_CPU_MASK_STATUS),
+
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_1),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_2),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_3),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_4),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_1),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_2),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_3),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_4),
 };
 
 static const struct {
@@ -216,8 +243,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
                if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
                    vc4->hdmi->hpd_active_low)
                        return connector_status_connected;
-               else
-                       return connector_status_disconnected;
+               cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+               return connector_status_disconnected;
        }
 
        if (drm_probe_ddc(vc4->hdmi->ddc))
@@ -225,8 +252,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
        if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
                return connector_status_connected;
-       else
-               return connector_status_disconnected;
+       cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+       return connector_status_disconnected;
 }
 
 static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
@@ -247,6 +274,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
        struct edid *edid;
 
        edid = drm_get_edid(connector, vc4->hdmi->ddc);
+       cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
        if (!edid)
                return -ENODEV;
 
@@ -463,11 +491,6 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
        HD_WRITE(VC4_HD_VID_CTL,
                 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
 
-       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
-       udelay(1);
-       HD_WRITE(VC4_HD_M_CTL, 0);
-
-       clk_disable_unprepare(hdmi->hsm_clock);
        clk_disable_unprepare(hdmi->pixel_clock);
 
        ret = pm_runtime_put(&hdmi->pdev->dev);
@@ -509,16 +532,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
                return;
        }
 
-       /* This is the rate that is set by the firmware.  The number
-        * needs to be a bit higher than the pixel clock rate
-        * (generally 148.5Mhz).
-        */
-       ret = clk_set_rate(hdmi->hsm_clock, 163682864);
-       if (ret) {
-               DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
-               return;
-       }
-
        ret = clk_set_rate(hdmi->pixel_clock,
                           mode->clock * 1000 *
                           ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
@@ -533,20 +546,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
                return;
        }
 
-       ret = clk_prepare_enable(hdmi->hsm_clock);
-       if (ret) {
-               DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
-                         ret);
-               clk_disable_unprepare(hdmi->pixel_clock);
-               return;
-       }
-
-       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
-       udelay(1);
-       HD_WRITE(VC4_HD_M_CTL, 0);
-
-       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
-
        HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
                   VC4_HDMI_SW_RESET_HDMI |
                   VC4_HDMI_SW_RESET_FORMAT_DETECT);
@@ -1150,6 +1149,159 @@ static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi)
                snd_soc_unregister_codec(dev);
 }
 
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+{
+       struct vc4_dev *vc4 = priv;
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+
+       if (hdmi->cec_irq_was_rx) {
+               if (hdmi->cec_rx_msg.len)
+                       cec_received_msg(hdmi->cec_adap, &hdmi->cec_rx_msg);
+       } else if (hdmi->cec_tx_ok) {
+               cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_OK,
+                                 0, 0, 0, 0);
+       } else {
+               /*
+                * This CEC implementation makes 1 retry, so if we
+                * get a NACK, then that means it made 2 attempts.
+                */
+               cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_NACK,
+                                 0, 2, 0, 0);
+       }
+       return IRQ_HANDLED;
+}
+
+static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
+{
+       struct cec_msg *msg = &vc4->hdmi->cec_rx_msg;
+       unsigned int i;
+
+       msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+                                       VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
+       for (i = 0; i < msg->len; i += 4) {
+               u32 val = HDMI_READ(VC4_HDMI_CEC_RX_DATA_1 + i);
+
+               msg->msg[i] = val & 0xff;
+               msg->msg[i + 1] = (val >> 8) & 0xff;
+               msg->msg[i + 2] = (val >> 16) & 0xff;
+               msg->msg[i + 3] = (val >> 24) & 0xff;
+       }
+}
+
+static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+{
+       struct vc4_dev *vc4 = priv;
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+       u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
+       u32 cntrl1, cntrl5;
+
+       if (!(stat & VC4_HDMI_CPU_CEC))
+               return IRQ_NONE;
+       hdmi->cec_rx_msg.len = 0;
+       cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+       hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+       if (hdmi->cec_irq_was_rx) {
+               vc4_cec_read_msg(vc4, cntrl1);
+               cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+               cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+       } else {
+               hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+               cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+       }
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+       HDMI_WRITE(VC4_HDMI_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+
+       return IRQ_WAKE_THREAD;
+}
+
+static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+       /* clock period in microseconds */
+       const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
+       u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+
+       val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
+                VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
+                VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
+       val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
+              ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
+
+       if (enable) {
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+                          VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_2,
+                        ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
+                        ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
+                        ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
+                        ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
+                        ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_3,
+                        ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
+                        ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
+                        ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
+                        ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_4,
+                        ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
+                        ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
+                        ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+                        ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+
+               HDMI_WRITE(VC4_HDMI_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
+       } else {
+               HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+                          VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+       }
+       return 0;
+}
+
+static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
+                  (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+                  (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
+       return 0;
+}
+
+static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                     u32 signal_free_time, struct cec_msg *msg)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+       u32 val;
+       unsigned int i;
+
+       for (i = 0; i < msg->len; i += 4)
+               HDMI_WRITE(VC4_HDMI_CEC_TX_DATA_1 + i,
+                          (msg->msg[i]) |
+                          (msg->msg[i + 1] << 8) |
+                          (msg->msg[i + 2] << 16) |
+                          (msg->msg[i + 3] << 24));
+
+       val = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+       val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK;
+       val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT;
+       val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
+
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+       return 0;
+}
+
+static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
+       .adap_enable = vc4_hdmi_cec_adap_enable,
+       .adap_log_addr = vc4_hdmi_cec_adap_log_addr,
+       .adap_transmit = vc4_hdmi_cec_adap_transmit,
+};
+#endif
+
 static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1205,6 +1357,23 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                return -EPROBE_DEFER;
        }
 
+       /* This is the rate that is set by the firmware.  The number
+        * needs to be a bit higher than the pixel clock rate
+        * (generally 148.5Mhz).
+        */
+       ret = clk_set_rate(hdmi->hsm_clock, HSM_CLOCK_FREQ);
+       if (ret) {
+               DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+               goto err_put_i2c;
+       }
+
+       ret = clk_prepare_enable(hdmi->hsm_clock);
+       if (ret) {
+               DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+                         ret);
+               goto err_put_i2c;
+       }
+
        /* Only use the GPIO HPD pin if present in the DT, otherwise
         * we'll use the HDMI core's register.
         */
@@ -1216,7 +1385,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                                                         &hpd_gpio_flags);
                if (hdmi->hpd_gpio < 0) {
                        ret = hdmi->hpd_gpio;
-                       goto err_put_i2c;
+                       goto err_unprepare_hsm;
                }
 
                hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
@@ -1224,6 +1393,14 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
        vc4->hdmi = hdmi;
 
+       /* HDMI core must be enabled. */
+       if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
+               HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+               udelay(1);
+               HD_WRITE(VC4_HD_M_CTL, 0);
+
+               HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
+       }
        pm_runtime_enable(dev);
 
        drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
@@ -1235,6 +1412,37 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                ret = PTR_ERR(hdmi->connector);
                goto err_destroy_encoder;
        }
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+       hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+                                             vc4, "vc4",
+                                             CEC_CAP_TRANSMIT |
+                                             CEC_CAP_LOG_ADDRS |
+                                             CEC_CAP_PASSTHROUGH |
+                                             CEC_CAP_RC, 1);
+       ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
+       if (ret < 0)
+               goto err_destroy_conn;
+       HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+       value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+       /*
+        * Set the logical address to Unregistered and set the clock
+        * divider: the hsm_clock rate and this divider setting will
+        * give a 40 kHz CEC clock.
+        */
+       value |= VC4_HDMI_CEC_ADDR_MASK |
+                (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, value);
+       ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+                                       vc4_cec_irq_handler,
+                                       vc4_cec_irq_handler_thread, 0,
+                                       "vc4 hdmi cec", vc4);
+       if (ret)
+               goto err_delete_cec_adap;
+       ret = cec_register_adapter(hdmi->cec_adap, dev);
+       if (ret < 0)
+               goto err_delete_cec_adap;
+#endif
 
        ret = vc4_hdmi_audio_init(hdmi);
        if (ret)
@@ -1242,8 +1450,16 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
        return 0;
 
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+err_delete_cec_adap:
+       cec_delete_adapter(hdmi->cec_adap);
+err_destroy_conn:
+       vc4_hdmi_connector_destroy(hdmi->connector);
+#endif
 err_destroy_encoder:
        vc4_hdmi_encoder_destroy(hdmi->encoder);
+err_unprepare_hsm:
+       clk_disable_unprepare(hdmi->hsm_clock);
        pm_runtime_disable(dev);
 err_put_i2c:
        put_device(&hdmi->ddc->dev);
@@ -1259,10 +1475,11 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
        struct vc4_hdmi *hdmi = vc4->hdmi;
 
        vc4_hdmi_audio_cleanup(hdmi);
-
+       cec_unregister_adapter(hdmi->cec_adap);
        vc4_hdmi_connector_destroy(hdmi->connector);
        vc4_hdmi_encoder_destroy(hdmi->encoder);
 
+       clk_disable_unprepare(hdmi->hsm_clock);
        pm_runtime_disable(dev);
 
        put_device(&hdmi->ddc->dev);
index d382c34c1b9e0c6d5b949ca09bf1e8401d44f91a..55677bd50f6610795e3d04bfdeaa245cf9aad0cb 100644 (file)
 # define VC4_HDMI_VERTB_VBP_MASK               VC4_MASK(8, 0)
 # define VC4_HDMI_VERTB_VBP_SHIFT              0
 
+#define VC4_HDMI_CEC_CNTRL_1                   0x0e8
+/* Set when the transmission has ended. */
+# define VC4_HDMI_CEC_TX_EOM                   BIT(31)
+/* If set, transmission was acked on the 1st or 2nd attempt (only one
+ * retry is attempted).  If in continuous mode, this means TX needs to
+ * be filled if !TX_EOM.
+ */
+# define VC4_HDMI_CEC_TX_STATUS_GOOD           BIT(30)
+# define VC4_HDMI_CEC_RX_EOM                   BIT(29)
+# define VC4_HDMI_CEC_RX_STATUS_GOOD           BIT(28)
+/* Number of bytes received for the message. */
+# define VC4_HDMI_CEC_REC_WRD_CNT_MASK         VC4_MASK(27, 24)
+# define VC4_HDMI_CEC_REC_WRD_CNT_SHIFT                24
+/* Sets continuous receive mode.  Generates interrupt after each 8
+ * bytes to signal that RX_DATA should be consumed, and at RX_EOM.
+ *
+ * If disabled, maximum 16 bytes will be received (including header),
+ * and interrupt at RX_EOM.  Later bytes will be acked but not put
+ * into the RX_DATA.
+ */
+# define VC4_HDMI_CEC_RX_CONTINUE              BIT(23)
+# define VC4_HDMI_CEC_TX_CONTINUE              BIT(22)
+/* Set this after a CEC interrupt. */
+# define VC4_HDMI_CEC_CLEAR_RECEIVE_OFF                BIT(21)
+/* Starts a TX.  Will wait for appropriate idel time before CEC
+ * activity. Must be cleared in between transmits.
+ */
+# define VC4_HDMI_CEC_START_XMIT_BEGIN         BIT(20)
+# define VC4_HDMI_CEC_MESSAGE_LENGTH_MASK      VC4_MASK(19, 16)
+# define VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT     16
+/* Device's CEC address */
+# define VC4_HDMI_CEC_ADDR_MASK                        VC4_MASK(15, 12)
+# define VC4_HDMI_CEC_ADDR_SHIFT               12
+/* Divides off of HSM clock to generate CEC bit clock. */
+/* With the current defaults the CEC bit clock is 40 kHz = 25 usec */
+# define VC4_HDMI_CEC_DIV_CLK_CNT_MASK         VC4_MASK(11, 0)
+# define VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT                0
+
+/* Set these fields to how many bit clock cycles get to that many
+ * microseconds.
+ */
+#define VC4_HDMI_CEC_CNTRL_2                   0x0ec
+# define VC4_HDMI_CEC_CNT_TO_1500_US_MASK      VC4_MASK(30, 24)
+# define VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_1300_US_MASK      VC4_MASK(23, 17)
+# define VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT     17
+# define VC4_HDMI_CEC_CNT_TO_800_US_MASK       VC4_MASK(16, 11)
+# define VC4_HDMI_CEC_CNT_TO_800_US_SHIFT      11
+# define VC4_HDMI_CEC_CNT_TO_600_US_MASK       VC4_MASK(10, 5)
+# define VC4_HDMI_CEC_CNT_TO_600_US_SHIFT      5
+# define VC4_HDMI_CEC_CNT_TO_400_US_MASK       VC4_MASK(4, 0)
+# define VC4_HDMI_CEC_CNT_TO_400_US_SHIFT      0
+
+#define VC4_HDMI_CEC_CNTRL_3                   0x0f0
+# define VC4_HDMI_CEC_CNT_TO_2750_US_MASK      VC4_MASK(31, 24)
+# define VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_2400_US_MASK      VC4_MASK(23, 16)
+# define VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT     16
+# define VC4_HDMI_CEC_CNT_TO_2050_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_1700_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT     0
+
+#define VC4_HDMI_CEC_CNTRL_4                   0x0f4
+# define VC4_HDMI_CEC_CNT_TO_4300_US_MASK      VC4_MASK(31, 24)
+# define VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_3900_US_MASK      VC4_MASK(23, 16)
+# define VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT     16
+# define VC4_HDMI_CEC_CNT_TO_3600_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_3500_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT     0
+
+#define VC4_HDMI_CEC_CNTRL_5                   0x0f8
+# define VC4_HDMI_CEC_TX_SW_RESET              BIT(27)
+# define VC4_HDMI_CEC_RX_SW_RESET              BIT(26)
+# define VC4_HDMI_CEC_PAD_SW_RESET             BIT(25)
+# define VC4_HDMI_CEC_MUX_TP_OUT_CEC           BIT(24)
+# define VC4_HDMI_CEC_RX_CEC_INT               BIT(23)
+# define VC4_HDMI_CEC_CLK_PRELOAD_MASK         VC4_MASK(22, 16)
+# define VC4_HDMI_CEC_CLK_PRELOAD_SHIFT                16
+# define VC4_HDMI_CEC_CNT_TO_4700_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_4500_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT     0
+
+/* Transmit data, first byte is low byte of the 32-bit reg.  MSB of
+ * each byte transmitted first.
+ */
+#define VC4_HDMI_CEC_TX_DATA_1                 0x0fc
+#define VC4_HDMI_CEC_TX_DATA_2                 0x100
+#define VC4_HDMI_CEC_TX_DATA_3                 0x104
+#define VC4_HDMI_CEC_TX_DATA_4                 0x108
+#define VC4_HDMI_CEC_RX_DATA_1                 0x10c
+#define VC4_HDMI_CEC_RX_DATA_2                 0x110
+#define VC4_HDMI_CEC_RX_DATA_3                 0x114
+#define VC4_HDMI_CEC_RX_DATA_4                 0x118
+
 #define VC4_HDMI_TX_PHY_RESET_CTL              0x2c0
 
 #define VC4_HDMI_TX_PHY_CTL0                   0x2c4
 # define VC4_HDMI_TX_PHY_RNG_PWRDN             BIT(25)
 
+/* Interrupt status bits */
+#define VC4_HDMI_CPU_STATUS                    0x340
+#define VC4_HDMI_CPU_SET                       0x344
+#define VC4_HDMI_CPU_CLEAR                     0x348
+# define VC4_HDMI_CPU_CEC                      BIT(6)
+# define VC4_HDMI_CPU_HOTPLUG                  BIT(0)
+
+#define VC4_HDMI_CPU_MASK_STATUS               0x34c
+#define VC4_HDMI_CPU_MASK_SET                  0x350
+#define VC4_HDMI_CPU_MASK_CLEAR                        0x354
+
 #define VC4_HDMI_GCP(x)                                (0x400 + ((x) * 0x4))
 #define VC4_HDMI_RAM_PACKET(x)                 (0x400 + ((x) * 0x24))
 #define VC4_HDMI_PACKET_STRIDE                 0x24
 
 #define VC4_HD_M_CTL                           0x00c
+/* Debug: Current receive value on the CEC pad. */
+# define VC4_HD_CECRXD                         BIT(9)
+/* Debug: Override CEC output to 0. */
+# define VC4_HD_CECOVR                         BIT(8)
 # define VC4_HD_M_REGISTER_FILE_STANDBY                (3 << 6)
 # define VC4_HD_M_RAM_STANDBY                  (3 << 4)
 # define VC4_HD_M_SW_RST                       BIT(2)
index 1dc94d5392e27d6abd6f0f7b4ad2fd8b5ecad975..6522d4cbc9d9d7426728198024ce44aa622f2b32 100644 (file)
@@ -268,6 +268,9 @@ void drm_bridge_enable(struct drm_bridge *bridge);
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
                                        u32 connector_type);
 void drm_panel_bridge_remove(struct drm_bridge *bridge);
+struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
+                                            struct drm_panel *panel,
+                                            u32 connector_type);
 #endif
 
 #endif