]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'sunxi-drm-for-4.14' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorDave Airlie <airlied@redhat.com>
Sun, 20 Aug 2017 23:05:01 +0000 (09:05 +1000)
committerDave Airlie <airlied@redhat.com>
Sun, 20 Aug 2017 23:05:01 +0000 (09:05 +1000)
Allwinner DRM changes for 4.14

A few changes, but most notably improving the HDMI support merged in 4.13,
by reporting the DDC adapter as an i2c bus, and by adding CEC support
through the CEC framework.

* tag 'sunxi-drm-for-4.14' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
  sun4i_hdmi: add CEC support
  dt-bindings: display: sunxi: Improve endpoint ID scheme readability
  drm/sun4i: tcon: remove unused function
  drm/sun4i: Remove useless atomic_check
  drm/sun4i: Add if statement instead of depends on
  drm/sun4i: hdmi: Implement I2C adapter for A10s DDC bus
  drm/sun4i: constify drm_plane_helper_funcs

Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
drivers/gpu/drm/sun4i/Kconfig
drivers/gpu/drm/sun4i/Makefile
drivers/gpu/drm/sun4i/sun4i_hdmi.h
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c [new file with mode: 0644]
drivers/gpu/drm/sun4i/sun4i_layer.c
drivers/gpu/drm/sun4i/sun4i_rgb.c
drivers/gpu/drm/sun4i/sun4i_tcon.h
drivers/gpu/drm/sun4i/sun4i_tv.c

index b83e6018041d68d8fb0b552c3984233dab5b6d84..2ee6ff0ef98e7dcf6d16e977fa8dd852b10a247c 100644 (file)
@@ -4,15 +4,33 @@ Allwinner A10 Display Pipeline
 The Allwinner A10 Display pipeline is composed of several components
 that are going to be documented below:
 
-For the input port of all components up to the TCON in the display
-pipeline, if there are multiple components, the local endpoint IDs
-must correspond to the index of the upstream block. For example, if
-the remote endpoint is Frontend 1, then the local endpoint ID must
-be 1.
-
-Conversely, for the output ports of the same group, the remote endpoint
-ID must be the index of the local hardware block. If the local backend
-is backend 1, then the remote endpoint ID must be 1.
+For all connections between components up to the TCONs in the display
+pipeline, when there are multiple components of the same type at the
+same depth, the local endpoint ID must be the same as the remote
+component's index. For example, if the remote endpoint is Frontend 1,
+then the local endpoint ID must be 1.
+
+    Frontend 0  [0] ------- [0]  Backend 0  [0] ------- [0]  TCON 0
+               [1] --   -- [1]             [1] --   -- [1]
+                     \ /                         \ /
+                      X                           X
+                     / \                         / \
+               [0] --   -- [0]             [0] --   -- [0]
+    Frontend 1  [1] ------- [1]  Backend 1  [1] ------- [1]  TCON 1
+
+For a two pipeline system such as the one depicted above, the lines
+represent the connections between the components, while the numbers
+within the square brackets corresponds to the ID of the local endpoint.
+
+The same rule also applies to DE 2.0 mixer-TCON connections:
+
+    Mixer 0  [0] ----------- [0]  TCON 0
+            [1] ----   ---- [1]
+                    \ /
+                     X
+                    / \
+            [0] ----   ---- [0]
+    Mixer 1  [1] ----------- [1]  TCON 1
 
 HDMI Encoder
 ------------
index 5bcad8f5fb4f256d656f9a084778410b5dd76ea6..06f05302ee75e33b2a05f9c468b1a3bbd60619be 100644 (file)
@@ -13,17 +13,26 @@ config DRM_SUN4I
          Display Engine. If M is selected the module will be called
          sun4i-drm.
 
+if DRM_SUN4I
+
 config DRM_SUN4I_HDMI
        tristate "Allwinner A10 HDMI Controller Support"
-       depends on DRM_SUN4I
        default DRM_SUN4I
        help
          Choose this option if you have an Allwinner SoC with an HDMI
          controller.
 
+config DRM_SUN4I_HDMI_CEC
+       bool "Allwinner A10 HDMI CEC Support"
+       depends on DRM_SUN4I_HDMI
+       select CEC_CORE
+       depends on CEC_PIN
+       help
+         Choose this option if you have an Allwinner SoC with an HDMI
+         controller and want to use CEC.
+
 config DRM_SUN4I_BACKEND
        tristate "Support for Allwinner A10 Display Engine Backend"
-       depends on DRM_SUN4I
        default DRM_SUN4I
        help
          Choose this option if you have an Allwinner SoC with the
@@ -33,10 +42,11 @@ config DRM_SUN4I_BACKEND
 
 config DRM_SUN8I_MIXER
        tristate "Support for Allwinner Display Engine 2.0 Mixer"
-       depends on DRM_SUN4I
        default MACH_SUN8I
        help
          Choose this option if you have an Allwinner SoC with the
          Allwinner Display Engine 2.0, which has a mixer to do some
          graphics mixture and feed graphics to TCON, If M is
          selected the module will be called sun8i-mixer.
+
+endif
index e29fd3a2ba9c8e0e4977a62638e2c19795ad57af..43c753cafc884dc2bc3da67e52e2a4bcad1a8c4c 100644 (file)
@@ -2,6 +2,7 @@ sun4i-drm-y += sun4i_drv.o
 sun4i-drm-y += sun4i_framebuffer.o
 
 sun4i-drm-hdmi-y += sun4i_hdmi_enc.o
+sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o
 sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o
 sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o
 
index 2f2f2ff1ea6371e3e6379ce0a9e0ee6ade4830e9..1457750988da4930792eceb3e543f211d66f84f7 100644 (file)
@@ -15,6 +15,8 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_encoder.h>
 
+#include <media/cec.h>
+
 #define SUN4I_HDMI_CTRL_REG            0x004
 #define SUN4I_HDMI_CTRL_ENABLE                 BIT(31)
 
 #define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK   BIT(21)
 #define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT  21
 
+#define SUN4I_HDMI_CEC                 0x214
+#define SUN4I_HDMI_CEC_ENABLE                  BIT(11)
+#define SUN4I_HDMI_CEC_TX                      BIT(9)
+#define SUN4I_HDMI_CEC_RX                      BIT(8)
+
 #define SUN4I_HDMI_PKT_CTRL_REG(n)     (0x2f0 + (4 * (n)))
 #define SUN4I_HDMI_PKT_CTRL_TYPE(n, t)         ((t) << (((n) % 4) * 4))
 
 #define SUN4I_HDMI_DDC_CTRL_ENABLE             BIT(31)
 #define SUN4I_HDMI_DDC_CTRL_START_CMD          BIT(30)
 #define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK      BIT(8)
+#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE     (1 << 8)
 #define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ      (0 << 8)
 #define SUN4I_HDMI_DDC_CTRL_RESET              BIT(0)
 
 #define SUN4I_HDMI_DDC_ADDR_OFFSET(off)                (((off) & 0xff) << 8)
 #define SUN4I_HDMI_DDC_ADDR_SLAVE(addr)                ((addr) & 0xff)
 
+#define SUN4I_HDMI_DDC_INT_STATUS_REG          0x50c
+#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION       BIT(7)
+#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW                BIT(6)
+#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW         BIT(5)
+#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST                 BIT(4)
+#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR            BIT(3)
+#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR                    BIT(2)
+#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR                    BIT(1)
+#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE            BIT(0)
+
 #define SUN4I_HDMI_DDC_FIFO_CTRL_REG   0x510
 #define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR         BIT(31)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n)   (((n) & 0xf) << 4)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX  (BIT(4) - 1)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n)   ((n) & 0xf)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0)
+#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX  (BIT(4) - 1)
 
 #define SUN4I_HDMI_DDC_FIFO_DATA_REG   0x518
+
 #define SUN4I_HDMI_DDC_BYTE_COUNT_REG  0x51c
+#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX          (BIT(10) - 1)
 
 #define SUN4I_HDMI_DDC_CMD_REG         0x520
 #define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ  6
+#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ       5
+#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE      3
 
 #define SUN4I_HDMI_DDC_CLK_REG         0x528
 #define SUN4I_HDMI_DDC_CLK_M(m)                        (((m) & 0x7) << 3)
@@ -146,12 +174,16 @@ struct sun4i_hdmi {
        struct clk              *ddc_clk;
        struct clk              *tmds_clk;
 
+       struct i2c_adapter      *i2c;
+
        struct sun4i_drv        *drv;
 
        bool                    hdmi_monitor;
+       struct cec_adapter      *cec_adap;
 };
 
 int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
 int sun4i_tmds_create(struct sun4i_hdmi *hdmi);
+int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi);
 
 #endif /* _SUN4I_HDMI_H_ */
index f5d0d6bd1084472b0cb255235abba5f87eb9c717..9ea6cd5a1370d92e6eb78c864641986bffbc0086 100644 (file)
@@ -29,8 +29,6 @@
 #include "sun4i_hdmi.h"
 #include "sun4i_tcon.h"
 
-#define DDC_SEGMENT_ADDR       0x30
-
 static inline struct sun4i_hdmi *
 drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
 {
@@ -184,93 +182,13 @@ static const struct drm_encoder_funcs sun4i_hdmi_funcs = {
        .destroy        = drm_encoder_cleanup,
 };
 
-static int sun4i_hdmi_read_sub_block(struct sun4i_hdmi *hdmi,
-                                    unsigned int blk, unsigned int offset,
-                                    u8 *buf, unsigned int count)
-{
-       unsigned long reg;
-       int i;
-
-       reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
-       reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;
-       writel(reg | SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ,
-              hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
-
-       writel(SUN4I_HDMI_DDC_ADDR_SEGMENT(offset >> 8) |
-              SUN4I_HDMI_DDC_ADDR_EDDC(DDC_SEGMENT_ADDR << 1) |
-              SUN4I_HDMI_DDC_ADDR_OFFSET(offset) |
-              SUN4I_HDMI_DDC_ADDR_SLAVE(DDC_ADDR),
-              hdmi->base + SUN4I_HDMI_DDC_ADDR_REG);
-
-       reg = readl(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
-       writel(reg | SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR,
-              hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
-
-       writel(count, hdmi->base + SUN4I_HDMI_DDC_BYTE_COUNT_REG);
-       writel(SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ,
-              hdmi->base + SUN4I_HDMI_DDC_CMD_REG);
-
-       reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
-       writel(reg | SUN4I_HDMI_DDC_CTRL_START_CMD,
-              hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
-
-       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
-                              !(reg & SUN4I_HDMI_DDC_CTRL_START_CMD),
-                              100, 100000))
-               return -EIO;
-
-       for (i = 0; i < count; i++)
-               buf[i] = readb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG);
-
-       return 0;
-}
-
-static int sun4i_hdmi_read_edid_block(void *data, u8 *buf, unsigned int blk,
-                                     size_t length)
-{
-       struct sun4i_hdmi *hdmi = data;
-       int retry = 2, i;
-
-       do {
-               for (i = 0; i < length; i += SUN4I_HDMI_DDC_FIFO_SIZE) {
-                       unsigned char offset = blk * EDID_LENGTH + i;
-                       unsigned int count = min((unsigned int)SUN4I_HDMI_DDC_FIFO_SIZE,
-                                                length - i);
-                       int ret;
-
-                       ret = sun4i_hdmi_read_sub_block(hdmi, blk, offset,
-                                                       buf + i, count);
-                       if (ret)
-                               return ret;
-               }
-       } while (!drm_edid_block_valid(buf, blk, true, NULL) && (retry--));
-
-       return 0;
-}
-
 static int sun4i_hdmi_get_modes(struct drm_connector *connector)
 {
        struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
-       unsigned long reg;
        struct edid *edid;
        int ret;
 
-       /* Reset i2c controller */
-       writel(SUN4I_HDMI_DDC_CTRL_ENABLE | SUN4I_HDMI_DDC_CTRL_RESET,
-              hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
-       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
-                              !(reg & SUN4I_HDMI_DDC_CTRL_RESET),
-                              100, 2000))
-               return -EIO;
-
-       writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE |
-              SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,
-              hdmi->base + SUN4I_HDMI_DDC_LINE_CTRL_REG);
-
-       clk_prepare_enable(hdmi->ddc_clk);
-       clk_set_rate(hdmi->ddc_clk, 100000);
-
-       edid = drm_do_get_edid(connector, sun4i_hdmi_read_edid_block, hdmi);
+       edid = drm_get_edid(connector, hdmi->i2c);
        if (!edid)
                return 0;
 
@@ -279,11 +197,10 @@ static int sun4i_hdmi_get_modes(struct drm_connector *connector)
                         hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
 
        drm_mode_connector_update_edid_property(connector, edid);
+       cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
        ret = drm_add_edid_modes(connector, edid);
        kfree(edid);
 
-       clk_disable_unprepare(hdmi->ddc_clk);
-
        return ret;
 }
 
@@ -299,8 +216,10 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
        if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
                               reg & SUN4I_HDMI_HPD_HIGH,
-                              0, 500000))
+                              0, 500000)) {
+               cec_phys_addr_invalidate(hdmi->cec_adap);
                return connector_status_disconnected;
+       }
 
        return connector_status_connected;
 }
@@ -314,6 +233,40 @@ static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 };
 
+#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
+static bool sun4i_hdmi_cec_pin_read(struct cec_adapter *adap)
+{
+       struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
+
+       return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX;
+}
+
+static void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap)
+{
+       struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
+
+       /* Start driving the CEC pin low */
+       writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC);
+}
+
+static void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap)
+{
+       struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
+
+       /*
+        * Stop driving the CEC pin, the pull up will take over
+        * unless another CEC device is driving the pin low.
+        */
+       writel(0, hdmi->base + SUN4I_HDMI_CEC);
+}
+
+static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = {
+       .read = sun4i_hdmi_cec_pin_read,
+       .low = sun4i_hdmi_cec_pin_low,
+       .high = sun4i_hdmi_cec_pin_high,
+};
+#endif
+
 static int sun4i_hdmi_bind(struct device *dev, struct device *master,
                           void *data)
 {
@@ -406,9 +359,9 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
                SUN4I_HDMI_PLL_CTRL_PLL_EN;
        writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
 
-       ret = sun4i_ddc_create(hdmi, hdmi->tmds_clk);
+       ret = sun4i_hdmi_i2c_create(dev, hdmi);
        if (ret) {
-               dev_err(dev, "Couldn't create the DDC clock\n");
+               dev_err(dev, "Couldn't create the HDMI I2C adapter\n");
                return ret;
        }
 
@@ -421,13 +374,26 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
                               NULL);
        if (ret) {
                dev_err(dev, "Couldn't initialise the HDMI encoder\n");
-               return ret;
+               goto err_del_i2c_adapter;
        }
 
        hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm,
                                                                  dev->of_node);
-       if (!hdmi->encoder.possible_crtcs)
-               return -EPROBE_DEFER;
+       if (!hdmi->encoder.possible_crtcs) {
+               ret = -EPROBE_DEFER;
+               goto err_del_i2c_adapter;
+       }
+
+#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
+       hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops,
+               hdmi, "sun4i", CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC);
+       ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
+       if (ret < 0)
+               goto err_cleanup_connector;
+       writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX,
+              hdmi->base + SUN4I_HDMI_CEC);
+#endif
 
        drm_connector_helper_add(&hdmi->connector,
                                 &sun4i_hdmi_connector_helper_funcs);
@@ -444,12 +410,18 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
        hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
                DRM_CONNECTOR_POLL_DISCONNECT;
 
+       ret = cec_register_adapter(hdmi->cec_adap, dev);
+       if (ret < 0)
+               goto err_cleanup_connector;
        drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
 
        return 0;
 
 err_cleanup_connector:
+       cec_delete_adapter(hdmi->cec_adap);
        drm_encoder_cleanup(&hdmi->encoder);
+err_del_i2c_adapter:
+       i2c_del_adapter(hdmi->i2c);
        return ret;
 }
 
@@ -458,8 +430,10 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
 {
        struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
 
+       cec_unregister_adapter(hdmi->cec_adap);
        drm_connector_cleanup(&hdmi->connector);
        drm_encoder_cleanup(&hdmi->encoder);
+       i2c_del_adapter(hdmi->i2c);
 }
 
 static const struct component_ops sun4i_hdmi_ops = {
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
new file mode 100644 (file)
index 0000000..2e42d09
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ * Copyright (C) 2017 Jonathan Liu <net147@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+
+#include "sun4i_hdmi.h"
+
+#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \
+       SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \
+       SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \
+       SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \
+       SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \
+       SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \
+       SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \
+)
+
+/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */
+#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX
+/* FIFO request bit is set when FIFO level is below TX_THRESHOLD during write */
+#define TX_THRESHOLD 1
+
+static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read)
+{
+       /*
+        * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz
+        * clock. As clock rate is fixed, just round it up to 100 us.
+        */
+       const unsigned long byte_time_ns = 100;
+       const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |
+                        SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |
+                        SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE;
+       u32 reg;
+
+       /* Limit transfer length by FIFO threshold */
+       len = min_t(int, len, read ? (RX_THRESHOLD + 1) :
+                             (SUN4I_HDMI_DDC_FIFO_SIZE - TX_THRESHOLD + 1));
+
+       /* Wait until error, FIFO request bit set or transfer complete */
+       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG, reg,
+                              reg & mask, len * byte_time_ns, 100000))
+               return -ETIMEDOUT;
+
+       if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK)
+               return -EIO;
+
+       if (read)
+               readsb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG, buf, len);
+       else
+               writesb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG, buf, len);
+
+       /* Clear FIFO request bit */
+       writel(SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST,
+              hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
+
+       return len;
+}
+
+static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg)
+{
+       int i, len;
+       u32 reg;
+
+       /* Set FIFO direction */
+       reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
+       reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;
+       reg |= (msg->flags & I2C_M_RD) ?
+              SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ :
+              SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE;
+       writel(reg, hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
+
+       /* Set I2C address */
+       writel(SUN4I_HDMI_DDC_ADDR_SLAVE(msg->addr),
+              hdmi->base + SUN4I_HDMI_DDC_ADDR_REG);
+
+       /* Set FIFO RX/TX thresholds and clear FIFO */
+       reg = readl(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
+       reg |= SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR;
+       reg &= ~SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK;
+       reg |= SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(RX_THRESHOLD);
+       reg &= ~SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK;
+       reg |= SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(TX_THRESHOLD);
+       writel(reg, hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
+       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG,
+                              reg,
+                              !(reg & SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR),
+                              100, 2000))
+               return -EIO;
+
+       /* Set transfer length */
+       writel(msg->len, hdmi->base + SUN4I_HDMI_DDC_BYTE_COUNT_REG);
+
+       /* Set command */
+       writel(msg->flags & I2C_M_RD ?
+              SUN4I_HDMI_DDC_CMD_IMPLICIT_READ :
+              SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE,
+              hdmi->base + SUN4I_HDMI_DDC_CMD_REG);
+
+       /* Clear interrupt status bits */
+       writel(SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |
+              SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |
+              SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE,
+              hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
+
+       /* Start command */
+       reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
+       writel(reg | SUN4I_HDMI_DDC_CTRL_START_CMD,
+              hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
+
+       /* Transfer bytes */
+       for (i = 0; i < msg->len; i += len) {
+               len = fifo_transfer(hdmi, msg->buf + i, msg->len - i,
+                                   msg->flags & I2C_M_RD);
+               if (len <= 0)
+                       return len;
+       }
+
+       /* Wait for command to finish */
+       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG,
+                              reg,
+                              !(reg & SUN4I_HDMI_DDC_CTRL_START_CMD),
+                              100, 100000))
+               return -EIO;
+
+       /* Check for errors */
+       reg = readl(hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
+       if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) ||
+           !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) {
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap,
+                              struct i2c_msg *msgs, int num)
+{
+       struct sun4i_hdmi *hdmi = i2c_get_adapdata(adap);
+       u32 reg;
+       int err, i, ret = num;
+
+       for (i = 0; i < num; i++) {
+               if (!msgs[i].len)
+                       return -EINVAL;
+               if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX)
+                       return -EINVAL;
+       }
+
+       /* Reset I2C controller */
+       writel(SUN4I_HDMI_DDC_CTRL_ENABLE | SUN4I_HDMI_DDC_CTRL_RESET,
+              hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
+       if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
+                              !(reg & SUN4I_HDMI_DDC_CTRL_RESET),
+                              100, 2000))
+               return -EIO;
+
+       writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE |
+              SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,
+              hdmi->base + SUN4I_HDMI_DDC_LINE_CTRL_REG);
+
+       clk_prepare_enable(hdmi->ddc_clk);
+       clk_set_rate(hdmi->ddc_clk, 100000);
+
+       for (i = 0; i < num; i++) {
+               err = xfer_msg(hdmi, &msgs[i]);
+               if (err) {
+                       ret = err;
+                       break;
+               }
+       }
+
+       clk_disable_unprepare(hdmi->ddc_clk);
+       return ret;
+}
+
+static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = {
+       .master_xfer    = sun4i_hdmi_i2c_xfer,
+       .functionality  = sun4i_hdmi_i2c_func,
+};
+
+int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi)
+{
+       struct i2c_adapter *adap;
+       int ret = 0;
+
+       ret = sun4i_ddc_create(hdmi, hdmi->tmds_clk);
+       if (ret)
+               return ret;
+
+       adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
+       if (!adap)
+               return -ENOMEM;
+
+       adap->owner = THIS_MODULE;
+       adap->class = I2C_CLASS_DDC;
+       adap->algo = &sun4i_hdmi_i2c_algorithm;
+       strlcpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name));
+       i2c_set_adapdata(adap, hdmi);
+
+       ret = i2c_add_adapter(adap);
+       if (ret)
+               return ret;
+
+       hdmi->i2c = adap;
+
+       return ret;
+}
index d45f3a1a0a29d6ddfcd9f497c0e50e9484236d24..7bddf12548d3f01b580e1aeceb15bd57bcabe88b 100644 (file)
@@ -25,12 +25,6 @@ struct sun4i_plane_desc {
               uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
-                                           struct drm_plane_state *state)
-{
-       return 0;
-}
-
 static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
                                               struct drm_plane_state *old_state)
 {
@@ -52,8 +46,7 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
        sun4i_backend_layer_enable(backend, layer->id, true);
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-       .atomic_check   = sun4i_backend_layer_atomic_check,
+static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
        .atomic_disable = sun4i_backend_layer_atomic_disable,
        .atomic_update  = sun4i_backend_layer_atomic_update,
 };
index 42a238bbb899f6cda093714cf5a2b4beb721ff38..7cd7090ad63adde22826652674550beca7efc4d5 100644 (file)
@@ -127,13 +127,6 @@ static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 };
 
-static int sun4i_rgb_atomic_check(struct drm_encoder *encoder,
-                                 struct drm_crtc_state *crtc_state,
-                                 struct drm_connector_state *conn_state)
-{
-       return 0;
-}
-
 static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
 {
        struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
@@ -181,7 +174,6 @@ static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
 }
 
 static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
-       .atomic_check   = sun4i_rgb_atomic_check,
        .mode_set       = sun4i_rgb_encoder_mode_set,
        .disable        = sun4i_rgb_encoder_disable,
        .enable         = sun4i_rgb_encoder_enable,
index e3c50ecdcd04fe6c789c5c5f27c64c197df2fdb2..552c88ec16be38d82cf3041a73e9dd27d4aafd18 100644 (file)
@@ -194,8 +194,6 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel);
 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
 
 /* Mode Related Controls */
-void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon,
-                                bool enable);
 void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
                        struct drm_encoder *encoder);
 void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
index 4edf15e299abb8a56bf9f0b7641f65ecbd2749b8..050cfd43c7a0f1bad292998f0182bb539fa1bdff 100644 (file)
@@ -341,13 +341,6 @@ static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
        mode->vtotal = mode->vsync_end  + tv_mode->vback_porch;
 }
 
-static int sun4i_tv_atomic_check(struct drm_encoder *encoder,
-                                struct drm_crtc_state *crtc_state,
-                                struct drm_connector_state *conn_state)
-{
-       return 0;
-}
-
 static void sun4i_tv_disable(struct drm_encoder *encoder)
 {
        struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
@@ -489,7 +482,6 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
 }
 
 static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
-       .atomic_check   = sun4i_tv_atomic_check,
        .disable        = sun4i_tv_disable,
        .enable         = sun4i_tv_enable,
        .mode_set       = sun4i_tv_mode_set,