Merge tag 'drm-for-v4.12' of git://people.freedesktop.org/~airlied/linux into drm...
authorSean Paul <seanpaul@chromium.org>
Thu, 4 May 2017 12:38:21 +0000 (08:38 -0400)
committerSean Paul <seanpaul@chromium.org>
Thu, 4 May 2017 12:42:49 +0000 (08:42 -0400)
Backmerging Dave's 'drm-for-v4.12' pull request now that it's landed. There are
a bunch of non-drm changes which are just random bits we hadn't yet picked up in
misc-next.

main drm pull request for 4.12 kernel

Signed-off-by: Sean Paul <seanpaul@chromium.org>
Link: http://patchwork.freedesktop.org/patch/msgid/CAPM=9ty0jHgzG18zOr5CYODyTqZfH55kOCOFqNnXiWnTb_uNWw@mail.gmail.com
50 files changed:
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/zte,vou.txt
MAINTAINERS
drivers/dma-buf/dma-fence.c
drivers/dma-buf/sync_file.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/bridge/sii902x.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_color_mgmt.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/selftests/test-drm_mm.c
drivers/gpu/drm/stm/Kconfig [new file with mode: 0644]
drivers/gpu/drm/stm/Makefile [new file with mode: 0644]
drivers/gpu/drm/stm/drv.c [new file with mode: 0644]
drivers/gpu/drm/stm/ltdc.c [new file with mode: 0644]
drivers/gpu/drm/stm/ltdc.h [new file with mode: 0644]
drivers/gpu/drm/vc4/Makefile
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_drv.h
drivers/gpu/drm/vc4/vc4_fence.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_gem.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_irq.c
drivers/gpu/drm/vc4/vc4_render_cl.c
drivers/gpu/drm/vc4/vc4_v3d.c
drivers/gpu/drm/vc4/vc4_validate.c
drivers/gpu/drm/zte/Makefile
drivers/gpu/drm/zte/zx_common_regs.h [new file with mode: 0644]
drivers/gpu/drm/zte/zx_drm_drv.c
drivers/gpu/drm/zte/zx_drm_drv.h
drivers/gpu/drm/zte/zx_plane.c
drivers/gpu/drm/zte/zx_plane_regs.h
drivers/gpu/drm/zte/zx_vga.c [new file with mode: 0644]
drivers/gpu/drm/zte/zx_vga_regs.h [new file with mode: 0644]
drivers/gpu/drm/zte/zx_vou.c
drivers/gpu/drm/zte/zx_vou_regs.h
include/drm/drm_color_mgmt.h
include/drm/drm_crtc.h
include/drm/drm_dp_helper.h
include/drm/drm_fb_cma_helper.h
include/drm/drm_gem_cma_helper.h
include/linux/sync_file.h

diff --git a/Documentation/devicetree/bindings/display/st,stm32-ltdc.txt b/Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
new file mode 100644 (file)
index 0000000..8e14769
--- /dev/null
@@ -0,0 +1,36 @@
+* 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.
+  - clocks: A list of phandle + clock-specifier pairs, one for each
+    entry in 'clock-names'.
+  - clock-names: A list of clock names. For ltdc it should contain:
+      - "lcd" for the clock feeding the output pixel clock & IP clock.
+  - resets: reset to be used by the device (defined by use of RCC macro).
+  Required nodes:
+    - Video port for RGB output.
+
+Example:
+
+/ {
+       ...
+       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_rgb: endpoint {
+                               };
+                       };
+               };
+       };
+};
index 9c35628..3847647 100644 (file)
@@ -58,6 +58,18 @@ Required properties:
    integer cells.  The first cell is the offset of SYSCTRL register used
    to control TV Encoder DAC power, and the second cell is the bit mask.
 
+* VGA output device
+
+Required properties:
+ - compatible: should be "zte,zx296718-vga"
+ - reg: Physical base address and length of the VGA device IO region
+ - interrupts : VGA interrupt number to CPU
+ - clocks: Phandle with clock-specifier pointing to VGA I2C clock.
+ - clock-names: Must be "i2c_wclk".
+ - zte,vga-power-control: the phandle to SYSCTRL block followed by two
+   integer cells.  The first cell is the offset of SYSCTRL register used
+   to control VGA DAC power, and the second cell is the bit mask.
+
 Example:
 
 vou: vou@1440000 {
@@ -81,6 +93,15 @@ vou: vou@1440000 {
                              "main_wclk", "aux_wclk";
        };
 
+       vga: vga@8000 {
+               compatible = "zte,zx296718-vga";
+               reg = <0x8000 0x1000>;
+               interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&topcrm VGA_I2C_WCLK>;
+               clock-names = "i2c_wclk";
+               zte,vga-power-control = <&sysctrl 0x170 0xe0>;
+       };
+
        hdmi: hdmi@c000 {
                compatible = "zte,zx296718-hdmi";
                reg = <0xc000 0x4000>;
index 66f5b55..e8f86bc 100644 (file)
@@ -4167,6 +4167,8 @@ F:        drivers/gpu/drm/ast/
 
 DRM DRIVERS FOR BRIDGE CHIPS
 M:     Archit Taneja <architt@codeaurora.org>
+M:     Andrzej Hajda <a.hajda@samsung.com>
+R:     Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
 S:     Maintained
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 F:     drivers/gpu/drm/bridge/
@@ -4423,6 +4425,15 @@ S:       Maintained
 F:     drivers/gpu/drm/sti
 F:     Documentation/devicetree/bindings/display/st,stih4xx.txt
 
+DRM DRIVERS FOR STM
+M:     Yannick Fertre <yannick.fertre@st.com>
+M:     Philippe Cornu <philippe.cornu@st.com>
+L:     dri-devel@lists.freedesktop.org
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+S:     Maintained
+F:     drivers/gpu/drm/stm
+F:     Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
+
 DRM DRIVER FOR TDFX VIDEO CARDS
 S:     Orphan / Obsolete
 F:     drivers/gpu/drm/tdfx/
index 0918d3f..57da14c 100644 (file)
@@ -402,6 +402,11 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
                }
        }
 
+       if (!timeout) {
+               ret = 0;
+               goto out;
+       }
+
        cb.base.func = dma_fence_default_wait_cb;
        cb.task = current;
        list_add(&cb.base.node, &fence->cb_list);
index 2321035..dc89b1d 100644 (file)
@@ -41,8 +41,6 @@ static struct sync_file *sync_file_alloc(void)
        if (IS_ERR(sync_file->file))
                goto err;
 
-       kref_init(&sync_file->kref);
-
        init_waitqueue_head(&sync_file->wq);
 
        INIT_LIST_HEAD(&sync_file->cb.node);
@@ -277,22 +275,15 @@ err:
 
 }
 
-static void sync_file_free(struct kref *kref)
+static int sync_file_release(struct inode *inode, struct file *file)
 {
-       struct sync_file *sync_file = container_of(kref, struct sync_file,
-                                                    kref);
+       struct sync_file *sync_file = file->private_data;
 
        if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
                dma_fence_remove_callback(sync_file->fence, &sync_file->cb);
        dma_fence_put(sync_file->fence);
        kfree(sync_file);
-}
-
-static int sync_file_release(struct inode *inode, struct file *file)
-{
-       struct sync_file *sync_file = file->private_data;
 
-       kref_put(&sync_file->kref, sync_file_free);
        return 0;
 }
 
index 78d7fc0..f57540d 100644 (file)
@@ -246,6 +246,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig"
 
 source "drivers/gpu/drm/tegra/Kconfig"
 
+source "drivers/gpu/drm/stm/Kconfig"
+
 source "drivers/gpu/drm/panel/Kconfig"
 
 source "drivers/gpu/drm/bridge/Kconfig"
index 59f0f9b..aa62ded 100644 (file)
@@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STM) += stm/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_IMX) += imx/
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
index 9126d03..9b87067 100644 (file)
@@ -160,7 +160,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
                 time_before(jiffies, timeout));
 
        if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-               dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
+               dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
                return -ETIMEDOUT;
        }
 
@@ -202,7 +202,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
 
        if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
                      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-               dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
+               dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
                return -ETIMEDOUT;
        }
 
@@ -298,7 +298,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
 
        if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
                dev_err(&sii902x->i2c->dev,
-                       "sii902x driver is only compatible with DRM devices supporting atomic updates");
+                       "sii902x driver is only compatible with DRM devices supporting atomic updates\n");
                return -ENOTSUPP;
        }
 
index 4e1f54a..8737de8 100644 (file)
@@ -173,6 +173,8 @@ struct dw_hdmi {
 
        unsigned int reg_shift;
        struct regmap *regm;
+       void (*enable_audio)(struct dw_hdmi *hdmi);
+       void (*disable_audio)(struct dw_hdmi *hdmi);
 };
 
 #define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -542,13 +544,41 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
 
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
+{
+       hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE,
+                 HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+}
+
+static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+}
+
+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       hdmi_enable_audio_clk(hdmi, true);
+}
+
+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
+{
+       hdmi_enable_audio_clk(hdmi, false);
+}
+
 void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&hdmi->audio_lock, flags);
        hdmi->audio_enable = true;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       if (hdmi->enable_audio)
+               hdmi->enable_audio(hdmi);
        spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
@@ -559,7 +589,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
 
        spin_lock_irqsave(&hdmi->audio_lock, flags);
        hdmi->audio_enable = false;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+       if (hdmi->disable_audio)
+               hdmi->disable_audio(hdmi);
        spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
@@ -1573,11 +1604,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
                            HDMI_MC_FLOWCTRL);
 }
 
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
-{
-       hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
-}
-
 /* Workaround to clear the overflow condition */
 static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
 {
@@ -1691,7 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
                /* HDMI Initialization Step E - Configure audio */
                hdmi_clk_regenerator_update_pixel_clock(hdmi);
-               hdmi_enable_audio_clk(hdmi);
+               hdmi_enable_audio_clk(hdmi, true);
        }
 
        /* not for DVI mode */
@@ -2403,6 +2429,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
                audio.irq = irq;
                audio.hdmi = hdmi;
                audio.eld = hdmi->connector.eld;
+               hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
+               hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
 
                pdevinfo.name = "dw-hdmi-ahb-audio";
                pdevinfo.data = &audio;
@@ -2415,6 +2443,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
                audio.hdmi      = hdmi;
                audio.write     = hdmi_writeb;
                audio.read      = hdmi_readb;
+               hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
+               hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
 
                pdevinfo.name = "dw-hdmi-i2s-audio";
                pdevinfo.data = &audio;
index f32506a..be62774 100644 (file)
@@ -425,7 +425,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
 }
 
 static int
-drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
+drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
                                         struct drm_property_blob **blob,
                                         uint64_t blob_id,
                                         ssize_t expected_size,
@@ -434,7 +434,7 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
        struct drm_property_blob *new_blob = NULL;
 
        if (blob_id != 0) {
-               new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
+               new_blob = drm_property_lookup_blob(dev, blob_id);
                if (new_blob == NULL)
                        return -EINVAL;
 
@@ -483,7 +483,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                drm_property_blob_put(mode);
                return ret;
        } else if (property == config->degamma_lut_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->degamma_lut,
                                        val,
                                        -1,
@@ -491,7 +491,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                state->color_mgmt_changed |= replaced;
                return ret;
        } else if (property == config->ctm_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->ctm,
                                        val,
                                        sizeof(struct drm_color_ctm),
@@ -499,7 +499,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                state->color_mgmt_changed |= replaced;
                return ret;
        } else if (property == config->gamma_lut_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->gamma_lut,
                                        val,
                                        -1,
@@ -1618,7 +1618,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
        if (ret)
                return ret;
 
-       DRM_DEBUG_ATOMIC("commiting %p\n", state);
+       DRM_DEBUG_ATOMIC("committing %p\n", state);
 
        return config->funcs->atomic_commit(state->dev, state, false);
 }
@@ -1647,7 +1647,7 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state)
        if (ret)
                return ret;
 
-       DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state);
+       DRM_DEBUG_ATOMIC("committing %p nonblocking\n", state);
 
        return config->funcs->atomic_commit(state->dev, state, true);
 }
index 8be9719..5a3c9c0 100644 (file)
@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
  *
  * Note that @pre_swap is needed since the point where we block for fences moves
  * around depending upon whether an atomic commit is blocking or
- * non-blocking. For async commit all waiting needs to happen after
- * drm_atomic_helper_swap_state() is called, but for synchronous commits we want
+ * non-blocking. For non-blocking commit all waiting needs to happen after
+ * drm_atomic_helper_swap_state() is called, but for blocking commits we want
  * to wait **before** we do anything that can't be easily rolled back. That is
  * before we call drm_atomic_helper_swap_state().
  *
@@ -3517,7 +3517,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
  *
  * Implements support for legacy gamma correction table for drivers
  * that support color management through the DEGAMMA_LUT/GAMMA_LUT
- * properties.
+ * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
+ * how the atomic color management and gamma tables work.
  */
 int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
                                       u16 *red, u16 *green, u16 *blue,
index 533f3a3..3eda500 100644 (file)
@@ -43,7 +43,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     linear/pass-thru gamma table should be used. This is generally the
- *     driver boot-up state too.
+ *     driver boot-up state too. Drivers can access this blob through
+ *     &drm_crtc_state.degamma_lut.
  *
  * “DEGAMMA_LUT_SIZE”:
  *     Unsinged range property to give the size of the lookup table to be set
@@ -60,7 +61,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     unit/pass-thru matrix should be used. This is generally the driver
- *     boot-up state too.
+ *     boot-up state too. Drivers can access the blob for the color conversion
+ *     matrix through &drm_crtc_state.ctm.
  *
  * “GAMMA_LUT”:
  *     Blob property to set the gamma lookup table (LUT) mapping pixel data
@@ -72,7 +74,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     linear/pass-thru gamma table should be used. This is generally the
- *     driver boot-up state too.
+ *     driver boot-up state too. Drivers can access this blob through
+ *     &drm_crtc_state.gamma_lut.
  *
  * “GAMMA_LUT_SIZE”:
  *     Unsigned range property to give the size of the lookup table to be set
index 50abd1f..53f9bdf 100644 (file)
@@ -189,7 +189,7 @@ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev,
                obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
                if (!obj) {
                        dev_err(dev->dev, "Failed to lookup GEM object\n");
-                       ret = -ENXIO;
+                       ret = -ENOENT;
                        goto err_gem_object_put;
                }
 
@@ -260,6 +260,33 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
 
 /**
+ * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
+ * @fb: The framebuffer
+ * @state: Which state of drm plane
+ * @plane: Which plane
+ * Return the CMA GEM address for given framebuffer.
+ *
+ * This function will usually be called from the PLANE callback functions.
+ */
+dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
+                                  struct drm_plane_state *state,
+                                  unsigned int plane)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+       dma_addr_t paddr;
+
+       if (plane >= 4)
+               return 0;
+
+       paddr = fb_cma->obj[plane]->paddr + fb->offsets[plane];
+       paddr += fb->format->cpp[plane] * (state->src_x >> 16);
+       paddr += fb->pitches[plane] * (state->src_y >> 16);
+
+       return paddr;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
+
+/**
  * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
  * @plane: Which plane
  * @state: Plane state attach fence to
index b84a295..9854a50 100644 (file)
@@ -381,6 +381,7 @@ EXPORT_SYMBOL(drm_primary_helper_update);
 /**
  * drm_primary_helper_disable() - Helper for primary plane disable
  * @plane: plane to disable
+ * @ctx: lock acquire context, not used here
  *
  * Provides a default plane disable handler for primary planes.  This is handler
  * is called in response to a userspace SetPlane operation on the plane with a
index d8fa7a9..1bccd82 100644 (file)
@@ -104,26 +104,18 @@ static void analogix_dp_psr_work(struct work_struct *work)
 {
        struct rockchip_dp_device *dp =
                                container_of(work, typeof(*dp), psr_work);
-       struct drm_crtc *crtc = dp->encoder.crtc;
-       int psr_state = dp->psr_state;
-       int vact_end;
        int ret;
        unsigned long flags;
 
-       if (!crtc)
-               return;
-
-       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
-
-       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
-                                         PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+       ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
+                                        PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
        if (ret) {
                dev_err(dp->dev, "line flag interrupt did not arrive\n");
                return;
        }
 
        spin_lock_irqsave(&dp->psr_lock, flags);
-       if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+       if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
                analogix_dp_enable_psr(dp->dev);
        else
                analogix_dp_disable_psr(dp->dev);
index a48fcce..47905fa 100644 (file)
@@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                                   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
                                    struct device *dev);
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-                               unsigned int mstimeout);
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
 
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
index 3f7a82d..40a5e6e 100644 (file)
@@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop)
        return !!line_flag_irq;
 }
 
-static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+static void vop_line_flag_irq_enable(struct vop *vop)
 {
        unsigned long flags;
 
@@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
 
        spin_lock_irqsave(&vop->irq_lock, flags);
 
-       VOP_CTRL_SET(vop, line_flag_num[0], line_num);
        VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
        VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
 
@@ -981,6 +980,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
        VOP_CTRL_SET(vop, vact_st_end, val);
        VOP_CTRL_SET(vop, vpost_st_end, val);
 
+       VOP_CTRL_SET(vop, line_flag_num[0], vact_end);
+
        clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
 
        VOP_CTRL_SET(vop, standby, 0);
@@ -1507,19 +1508,16 @@ static void vop_win_init(struct vop *vop)
 }
 
 /**
- * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * rockchip_drm_wait_vact_end
  * @crtc: CRTC to enable line flag
- * @line_num: interested line number
  * @mstimeout: millisecond for timeout
  *
- * Driver would hold here until the interested line flag interrupt have
- * happened or timeout to wait.
+ * Wait for vact_end line flag irq or timeout.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-                               unsigned int mstimeout)
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
 {
        struct vop *vop = to_vop(crtc);
        unsigned long jiffies_left;
@@ -1527,14 +1525,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
        if (!crtc || !vop->is_enabled)
                return -ENODEV;
 
-       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+       if (mstimeout <= 0)
                return -EINVAL;
 
        if (vop_line_flag_irq_is_enabled(vop))
                return -EBUSY;
 
        reinit_completion(&vop->line_flag_completion);
-       vop_line_flag_irq_enable(vop, line_num);
+       vop_line_flag_irq_enable(vop);
 
        jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
                                                   msecs_to_jiffies(mstimeout));
@@ -1547,7 +1545,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
 
        return 0;
 }
-EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
 
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
index fa356f5..dfdd858 100644 (file)
@@ -514,6 +514,8 @@ static int igt_reserve(void *ignored)
                ret = __igt_reserve(count, size + 1);
                if (ret)
                        return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -712,6 +714,10 @@ static int igt_insert(void *ignored)
                        return ret;
 
                ret = __igt_insert(count, size + 1, false);
+               if (ret)
+                       return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -741,6 +747,10 @@ static int igt_replace(void *ignored)
                        return ret;
 
                ret = __igt_insert(count, size + 1, true);
+               if (ret)
+                       return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -1011,6 +1021,8 @@ static int igt_insert_range(void *ignored)
                ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
                if (ret)
                        return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -1056,6 +1068,7 @@ static int igt_align(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1097,6 +1110,8 @@ static int igt_align_pot(int max)
                               align, bit);
                        goto out;
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1471,6 +1486,8 @@ static int igt_evict(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1566,6 +1583,8 @@ static int igt_evict_range(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1683,6 +1702,7 @@ static int igt_topdown(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1783,6 +1803,7 @@ static int igt_bottomup(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1970,6 +1991,8 @@ static int igt_color(void *ignored)
                        drm_mm_remove_node(node);
                        kfree(node);
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -2047,6 +2070,7 @@ static int evict_color(struct drm_mm *mm,
                }
        }
 
+       cond_resched();
        return 0;
 }
 
@@ -2132,6 +2156,8 @@ static int igt_color_evict(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -2231,6 +2257,8 @@ static int igt_color_evict_range(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig
new file mode 100644 (file)
index 0000000..2c4817f
--- /dev/null
@@ -0,0 +1,16 @@
+config DRM_STM
+       tristate "DRM Support for STMicroelectronics SoC Series"
+       depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
+       select DRM_KMS_HELPER
+       select DRM_GEM_CMA_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_PANEL
+       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.
diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile
new file mode 100644 (file)
index 0000000..e114d45
--- /dev/null
@@ -0,0 +1,7 @@
+ccflags-y := -Iinclude/drm
+
+stm-drm-y := \
+       drv.o \
+       ltdc.o
+
+obj-$(CONFIG_DRM_STM) += stm-drm.o
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
new file mode 100644 (file)
index 0000000..83ab48f
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#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 */
+
+static void drv_output_poll_changed(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       drm_fbdev_cma_hotplug_event(ldev->fbdev);
+}
+
+static const struct drm_mode_config_funcs drv_mode_config_funcs = {
+       .fb_create = drm_fb_cma_create,
+       .output_poll_changed = drv_output_poll_changed,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void drv_lastclose(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       drm_fbdev_cma_restore_mode(ldev->fbdev);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
+
+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,
+       .fops = &drv_driver_fops,
+       .dumb_create = drm_gem_cma_dumb_create,
+       .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+       .dumb_destroy = drm_gem_dumb_destroy,
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_free_object_unlocked = drm_gem_cma_free_object,
+       .gem_vm_ops = &drm_gem_cma_vm_ops,
+       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_vmap = drm_gem_cma_prime_vmap,
+       .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap = drm_gem_cma_prime_mmap,
+       .enable_vblank = ltdc_crtc_enable_vblank,
+       .disable_vblank = ltdc_crtc_disable_vblank,
+};
+
+static int drv_load(struct drm_device *ddev)
+{
+       struct platform_device *pdev = to_platform_device(ddev->dev);
+       struct drm_fbdev_cma *fbdev;
+       struct ltdc_device *ldev;
+       int ret;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
+       if (!ldev)
+               return -ENOMEM;
+
+       ddev->dev_private = (void *)ldev;
+
+       drm_mode_config_init(ddev);
+
+       /*
+        * set max width and height as default value.
+        * this value would be used to check framebuffer size limitation
+        * at drm_mode_addfb().
+        */
+       ddev->mode_config.min_width = 0;
+       ddev->mode_config.min_height = 0;
+       ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
+       ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
+       ddev->mode_config.funcs = &drv_mode_config_funcs;
+
+       ret = ltdc_load(ddev);
+       if (ret)
+               goto err;
+
+       drm_mode_config_reset(ddev);
+       drm_kms_helper_poll_init(ddev);
+
+       if (ddev->mode_config.num_connector) {
+               ldev = ddev->dev_private;
+               fbdev = drm_fbdev_cma_init(ddev, 16,
+                                          ddev->mode_config.num_connector);
+               if (IS_ERR(fbdev)) {
+                       DRM_DEBUG("Warning: fails to create fbdev\n");
+                       fbdev = NULL;
+               }
+               ldev->fbdev = fbdev;
+       }
+
+       platform_set_drvdata(pdev, ddev);
+
+       return 0;
+err:
+       drm_mode_config_cleanup(ddev);
+       return ret;
+}
+
+static void drv_unload(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       if (ldev->fbdev) {
+               drm_fbdev_cma_fini(ldev->fbdev);
+               ldev->fbdev = NULL;
+       }
+       drm_kms_helper_poll_fini(ddev);
+       ltdc_unload(ddev);
+       drm_mode_config_cleanup(ddev);
+}
+
+static int stm_drm_platform_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct drm_device *ddev;
+       int ret;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+       ddev = drm_dev_alloc(&drv_driver, dev);
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
+
+       ret = drv_load(ddev);
+       if (ret)
+               goto err_unref;
+
+       ret = drm_dev_register(ddev, 0);
+       if (ret)
+               goto err_unref;
+
+       return 0;
+
+err_unref:
+       drm_dev_unref(ddev);
+
+       return ret;
+}
+
+static int stm_drm_platform_remove(struct platform_device *pdev)
+{
+       struct drm_device *ddev = platform_get_drvdata(pdev);
+
+       DRM_DEBUG("%s\n", __func__);
+
+       drm_dev_unregister(ddev);
+       drv_unload(ddev);
+       drm_dev_unref(ddev);
+
+       return 0;
+}
+
+static const struct of_device_id drv_dt_ids[] = {
+       { .compatible = "st,stm32-ltdc"},
+       { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, drv_dt_ids);
+
+static struct platform_driver stm_drm_platform_driver = {
+       .probe = stm_drm_platform_probe,
+       .remove = stm_drm_platform_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .of_match_table = drv_dt_ids,
+       },
+};
+
+module_platform_driver(stm_drm_platform_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
new file mode 100644 (file)
index 0000000..a40418c
--- /dev/null
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_plane_helper.h>
+
+#include <video/videomode.h>
+
+#include "ltdc.h"
+
+#define NB_CRTC 1
+#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
+
+#define MAX_IRQ 4
+
+#define HWVER_10200 0x010200
+#define HWVER_10300 0x010300
+#define HWVER_20101 0x020101
+
+/*
+ * The address of some registers depends on the HW version: such registers have
+ * 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                (ldev->caps.reg_ofs)
+#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 */
+
+/* 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 */
+
+/* Bit definitions */
+#define SSCR_VSH       GENMASK(10, 0)  /* Vertical Synchronization Height */
+#define SSCR_HSW       GENMASK(27, 16) /* Horizontal Synchronization Width */
+
+#define BPCR_AVBP      GENMASK(10, 0)  /* Accumulated Vertical Back Porch */
+#define BPCR_AHBP      GENMASK(27, 16) /* Accumulated Horizontal Back Porch */
+
+#define AWCR_AAH       GENMASK(10, 0)  /* Accumulated Active Height */
+#define AWCR_AAW       GENMASK(27, 16) /* Accumulated Active Width */
+
+#define TWCR_TOTALH    GENMASK(10, 0)  /* TOTAL Height */
+#define TWCR_TOTALW    GENMASK(27, 16) /* TOTAL Width */
+
+#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 GC1R_WBCH      GENMASK(3, 0)   /* Width of Blue CHannel output */
+#define GC1R_WGCH      GENMASK(7, 4)   /* Width of Green Channel output */
+#define GC1R_WRCH      GENMASK(11, 8)  /* Width of Red Channel output */
+#define GC1R_PBEN      BIT(12)         /* Precise Blending ENable */
+#define GC1R_DT                GENMASK(15, 14) /* Dithering Technique */
+#define GC1R_GCT       GENMASK(19, 17) /* Gamma Correction Technique */
+#define GC1R_SHREN     BIT(21)         /* SHadow Registers ENabled */
+#define GC1R_BCP       BIT(22)         /* Background Colour Programmable */
+#define GC1R_BBEN      BIT(23)         /* Background Blending ENabled */
+#define GC1R_LNIP      BIT(24)         /* Line Number IRQ Position */
+#define GC1R_TP                BIT(25)         /* Timing Programmable */
+#define GC1R_IPP       BIT(26)         /* IRQ Polarity Programmable */
+#define GC1R_SPP       BIT(27)         /* Sync Polarity Programmable */
+#define GC1R_DWP       BIT(28)         /* Dither Width Programmable */
+#define GC1R_STREN     BIT(29)         /* STatus Registers ENabled */
+#define GC1R_BMEN      BIT(31)         /* Blind Mode ENabled */
+
+#define GC2R_EDCA      BIT(0)          /* External Display Control Ability  */
+#define GC2R_STSAEN    BIT(1)          /* Slave Timing Sync Ability ENabled */
+#define GC2R_DVAEN     BIT(2)          /* Dual-View Ability ENabled */
+#define GC2R_DPAEN     BIT(3)          /* Dual-Port Ability ENabled */
+#define GC2R_BW                GENMASK(6, 4)   /* Bus Width (log2 of nb of bytes) */
+#define GC2R_EDCEN     BIT(7)          /* External Display Control ENabled */
+
+#define SRCR_IMR       BIT(0)          /* IMmediate Reload */
+#define SRCR_VBR       BIT(1)          /* Vertical Blanking Reload */
+
+#define BCCR_BCBLACK   0x00            /* Background Color BLACK */
+#define BCCR_BCBLUE    GENMASK(7, 0)   /* Background Color BLUE */
+#define BCCR_BCGREEN   GENMASK(15, 8)  /* Background Color GREEN */
+#define BCCR_BCRED     GENMASK(23, 16) /* Background Color RED */
+#define BCCR_BCWHITE   GENMASK(23, 0)  /* Background Color WHITE */
+
+#define IER_LIE                BIT(0)          /* Line Interrupt Enable */
+#define IER_FUIE       BIT(1)          /* Fifo Underrun Interrupt Enable */
+#define IER_TERRIE     BIT(2)          /* Transfer ERRor Interrupt Enable */
+#define IER_RRIE       BIT(3)          /* Register Reload Interrupt enable */
+
+#define ISR_LIF                BIT(0)          /* Line Interrupt Flag */
+#define ISR_FUIF       BIT(1)          /* Fifo Underrun Interrupt Flag */
+#define ISR_TERRIF     BIT(2)          /* Transfer ERRor Interrupt Flag */
+#define ISR_RRIF       BIT(3)          /* Register Reload Interrupt Flag */
+
+#define LXCR_LEN       BIT(0)          /* Layer ENable */
+#define LXCR_COLKEN    BIT(1)          /* Color Keying Enable */
+#define LXCR_CLUTEN    BIT(4)          /* Color Look-Up Table ENable */
+
+#define LXWHPCR_WHSTPOS        GENMASK(11, 0)  /* Window Horizontal StarT POSition */
+#define LXWHPCR_WHSPPOS        GENMASK(27, 16) /* Window Horizontal StoP POSition */
+
+#define LXWVPCR_WVSTPOS        GENMASK(10, 0)  /* Window Vertical StarT POSition */
+#define LXWVPCR_WVSPPOS        GENMASK(26, 16) /* Window Vertical StoP POSition */
+
+#define LXPFCR_PF      GENMASK(2, 0)   /* Pixel Format */
+
+#define LXCACR_CONSTA  GENMASK(7, 0)   /* CONSTant Alpha */
+
+#define LXBFCR_BF2     GENMASK(2, 0)   /* Blending Factor 2 */
+#define LXBFCR_BF1     GENMASK(10, 8)  /* Blending Factor 1 */
+
+#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 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 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] */
+       /* 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] */
+};
+
+/* 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 */
+};
+
+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 */
+};
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+       return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+       writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+       reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+       reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
+                                  u32 val)
+{
+       reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
+}
+
+static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
+{
+       return (struct ltdc_device *)crtc->dev->dev_private;
+}
+
+static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
+{
+       return (struct ltdc_device *)plane->dev->dev_private;
+}
+
+static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
+{
+       return (struct ltdc_device *)enc->dev->dev_private;
+}
+
+static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con)
+{
+       return (struct ltdc_device *)con->dev->dev_private;
+}
+
+static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
+{
+       enum ltdc_pix_fmt pf;
+
+       switch (drm_fmt) {
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+               pf = PF_ARGB8888;
+               break;
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_RGBX8888:
+               pf = PF_RGBA8888;
+               break;
+       case DRM_FORMAT_RGB888:
+               pf = PF_RGB888;
+               break;
+       case DRM_FORMAT_RGB565:
+               pf = PF_RGB565;
+               break;
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               pf = PF_ARGB1555;
+               break;
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_XRGB4444:
+               pf = PF_ARGB4444;
+               break;
+       case DRM_FORMAT_C8:
+               pf = PF_L8;
+               break;
+       default:
+               pf = PF_NONE;
+               break;
+       /* Note: There are no DRM_FORMAT for AL44 and AL88 */
+       }
+
+       return pf;
+}
+
+static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+{
+       switch (pf) {
+       case PF_ARGB8888:
+               return DRM_FORMAT_ARGB8888;
+       case PF_RGBA8888:
+               return DRM_FORMAT_RGBA8888;
+       case PF_RGB888:
+               return DRM_FORMAT_RGB888;
+       case PF_RGB565:
+               return DRM_FORMAT_RGB565;
+       case PF_ARGB1555:
+               return DRM_FORMAT_ARGB1555;
+       case PF_ARGB4444:
+               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_NONE:
+       default:
+               return 0;
+       }
+}
+
+static irqreturn_t ltdc_irq_thread(int irq, void *arg)
+{
+       struct drm_device *ddev = arg;
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
+
+       /* Line IRQ : trigger the vblank event */
+       if (ldev->irq_status & ISR_LIF)
+               drm_crtc_handle_vblank(crtc);
+
+       /* Save FIFO Underrun & Transfer Error status */
+       mutex_lock(&ldev->err_lock);
+       if (ldev->irq_status & ISR_FUIF)
+               ldev->error_status |= ISR_FUIF;
+       if (ldev->irq_status & ISR_TERRIF)
+               ldev->error_status |= ISR_TERRIF;
+       mutex_unlock(&ldev->err_lock);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ltdc_irq(int irq, void *arg)
+{
+       struct drm_device *ddev = arg;
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       /* Read & Clear the interrupt status */
+       ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
+       reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+
+       return IRQ_WAKE_THREAD;
+}
+
+/*
+ * DRM_CRTC
+ */
+
+static void ltdc_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       unsigned int i, lay;
+
+       for (lay = 0; lay < ldev->caps.nb_layers; lay++)
+               for (i = 0; i < 256; i++)
+                       reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS,
+                                 ldev->clut[i]);
+}
+
+static void ltdc_crtc_enable(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       /* Sets the background color value */
+       reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+
+       /* Enable IRQ */
+       reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       /* Immediately commit the planes */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+
+       /* Enable LTDC */
+       reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+       drm_crtc_vblank_on(crtc);
+}
+
+static void ltdc_crtc_disable(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_crtc_vblank_off(crtc);
+
+       /* disable LTDC */
+       reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+       /* disable IRQ */
+       reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       /* immediately commit disable of layers before switching off LTDC */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+}
+
+static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       struct videomode vm;
+       int rate = mode->clock * 1000;
+       u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
+       u32 total_width, total_height;
+       u32 val;
+
+       drm_display_mode_to_videomode(mode, &vm);
+
+       DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
+       DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive);
+       DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
+                        vm.hfront_porch, vm.hback_porch, vm.hsync_len,
+                        vm.vfront_porch, vm.vback_porch, vm.vsync_len);
+
+       /* Convert video timings to ltdc timings */
+       hsync = vm.hsync_len - 1;
+       vsync = vm.vsync_len - 1;
+       accum_hbp = hsync + vm.hback_porch;
+       accum_vbp = vsync + vm.vback_porch;
+       accum_act_w = accum_hbp + vm.hactive;
+       accum_act_h = accum_vbp + vm.vactive;
+       total_width = accum_act_w + vm.hfront_porch;
+       total_height = accum_act_h + vm.vfront_porch;
+
+       clk_disable(ldev->pixel_clk);
+
+       if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
+               DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
+               return;
+       }
+
+       clk_enable(ldev->pixel_clk);
+
+       /* Configures the HS, VS, DE and PC polarities. */
+       val = HSPOL_AL | HSPOL_AL | DEPOL_AL | PCPOL_IPC;
+
+       if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+               val |= HSPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+               val |= VSPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+               val |= DEPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+               val |= PCPOL_IIPC;
+
+       reg_update_bits(ldev->regs, LTDC_GCR,
+                       GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+
+       /* Set Synchronization size */
+       val = (hsync << 16) | vsync;
+       reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+
+       /* Set Accumulated Back porch */
+       val = (accum_hbp << 16) | accum_vbp;
+       reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+
+       /* Set Accumulated Active Width */
+       val = (accum_act_w << 16) | accum_act_h;
+       reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+
+       /* Set total width & height */
+       val = (total_width << 16) | total_height;
+       reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+
+       reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+}
+
+static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
+                                  struct drm_crtc_state *old_crtc_state)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       struct drm_pending_vblank_event *event = crtc->state->event;
+
+       DRM_DEBUG_ATOMIC("\n");
+
+       /* Commit shadow registers = update planes at next vblank */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irq(&crtc->dev->event_lock);
+               if (drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+       }
+}
+
+static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+       .load_lut = ltdc_crtc_load_lut,
+       .enable = ltdc_crtc_enable,
+       .disable = ltdc_crtc_disable,
+       .mode_set_nofb = ltdc_crtc_mode_set_nofb,
+       .atomic_flush = ltdc_crtc_atomic_flush,
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+       reg_set(ldev->regs, LTDC_IER, IER_LIE);
+
+       return 0;
+}
+
+void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+       reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+}
+
+static 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,
+       .reset = drm_atomic_helper_crtc_reset,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * DRM_PLANE
+ */
+
+static int ltdc_plane_atomic_check(struct drm_plane *plane,
+                                  struct drm_plane_state *state)
+{
+       struct drm_framebuffer *fb = state->fb;
+       u32 src_x, src_y, src_w, src_h;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       if (!fb)
+               return 0;
+
+       /* convert src_ from 16:16 format */
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+
+       /* Reject scaling */
+       if ((src_w != state->crtc_w) || (src_h != state->crtc_h)) {
+               DRM_ERROR("Scaling is not supported");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void ltdc_plane_atomic_update(struct drm_plane *plane,
+                                    struct drm_plane_state *oldstate)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       struct drm_plane_state *state = plane->state;
+       struct drm_framebuffer *fb = state->fb;
+       u32 lofs = plane->index * LAY_OFS;
+       u32 x0 = state->crtc_x;
+       u32 x1 = state->crtc_x + state->crtc_w - 1;
+       u32 y0 = state->crtc_y;
+       u32 y1 = state->crtc_y + state->crtc_h - 1;
+       u32 src_x, src_y, src_w, src_h;
+       u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
+       enum ltdc_pix_fmt pf;
+
+       if (!state->crtc || !fb) {
+               DRM_DEBUG_DRIVER("fb or crtc NULL");
+               return;
+       }
+
+       /* convert src_ from 16:16 format */
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       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);
+
+       bpcr = reg_read(ldev->regs, LTDC_BPCR);
+       ahbp = (bpcr & BPCR_AHBP) >> 16;
+       avbp = bpcr & BPCR_AVBP;
+
+       /* Configures the horizontal start and stop position */
+       val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
+       reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
+                       LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+
+       /* Configures the vertical start and stop position */
+       val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
+       reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
+                       LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+
+       /* Specifies the pixel format */
+       pf = to_ltdc_pixelformat(fb->format->format);
+       for (val = 0; val < NB_PF; val++)
+               if (ldev->caps.pix_fmt_hw[val] == pf)
+                       break;
+
+       if (val == NB_PF) {
+               DRM_ERROR("Pixel format %.4s not supported\n",
+                         (char *)&fb->format->format);
+               val = 0; /* set by default ARGB 32 bits */
+       }
+       reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+
+       /* Configures the color frame buffer pitch in bytes & line length */
+       pitch_in_bytes = fb->pitches[0];
+       line_length = drm_format_plane_cpp(fb->format->format, 0) *
+                     (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+       val = ((pitch_in_bytes << 16) | line_length);
+       reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
+                       LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+       /* Specifies the constant alpha value */
+       val = CONSTA_MAX;
+       reg_update_bits(ldev->regs, LTDC_L1CACR + lofs,
+                       LXCACR_CONSTA, val);
+
+       /* Specifies the blending factors */
+       val = BF1_PAXCA | BF2_1PAXCA;
+       reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
+                       LXBFCR_BF2 | LXBFCR_BF1, val);
+
+       /* Configures the frame buffer line number */
+       val = y1 - y0 + 1;
+       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);
+
+       DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
+       reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
+
+       /* Enable layer and CLUT if needed */
+       val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
+       val |= LXCR_LEN;
+       reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
+                       LXCR_LEN | LXCR_CLUTEN, val);
+
+       mutex_lock(&ldev->err_lock);
+       if (ldev->error_status & ISR_FUIF) {
+               DRM_DEBUG_DRIVER("Fifo underrun\n");
+               ldev->error_status &= ~ISR_FUIF;
+       }
+       if (ldev->error_status & ISR_TERRIF) {
+               DRM_DEBUG_DRIVER("Transfer error\n");
+               ldev->error_status &= ~ISR_TERRIF;
+       }
+       mutex_unlock(&ldev->err_lock);
+}
+
+static void ltdc_plane_atomic_disable(struct drm_plane *plane,
+                                     struct drm_plane_state *oldstate)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       u32 lofs = plane->index * LAY_OFS;
+
+       /* disable layer */
+       reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+
+       DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
+                        oldstate->crtc->base.id, plane->base.id);
+}
+
+static 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,
+       .set_property = drm_atomic_helper_plane_set_property,
+       .reset = drm_atomic_helper_plane_reset,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
+       .atomic_check = ltdc_plane_atomic_check,
+       .atomic_update = ltdc_plane_atomic_update,
+       .atomic_disable = ltdc_plane_atomic_disable,
+};
+
+static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
+                                          enum drm_plane_type type)
+{
+       unsigned long possible_crtcs = CRTC_MASK;
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct device *dev = ddev->dev;
+       struct drm_plane *plane;
+       unsigned int i, nb_fmt = 0;
+       u32 formats[NB_PF];
+       u32 drm_fmt;
+       int ret;
+
+       /* Get supported pixel formats */
+       for (i = 0; i < NB_PF; i++) {
+               drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
+               if (!drm_fmt)
+                       continue;
+               formats[nb_fmt++] = drm_fmt;
+       }
+
+       plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
+       if (!plane)
+               return 0;
+
+       ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
+                                      &ltdc_plane_funcs, formats, nb_fmt,
+                                      type, NULL);
+       if (ret < 0)
+               return 0;
+
+       drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
+
+       DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
+
+       return plane;
+}
+
+static void ltdc_plane_destroy_all(struct drm_device *ddev)
+{
+       struct drm_plane *plane, *plane_temp;
+
+       list_for_each_entry_safe(plane, plane_temp,
+                                &ddev->mode_config.plane_list, head)
+               drm_plane_cleanup(plane);
+}
+
+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;
+
+       primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+       if (!primary) {
+               DRM_ERROR("Can not create primary plane\n");
+               return -EINVAL;
+       }
+
+       res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+                                       &ltdc_crtc_funcs, NULL);
+       if (res) {
+               DRM_ERROR("Can not initialize CRTC\n");
+               goto cleanup;
+       }
+
+       drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
+
+       DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
+
+       /* Add planes. Note : the first layer is used by primary plane */
+       for (i = 1; i < ldev->caps.nb_layers; i++) {
+               overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
+               if (!overlay) {
+                       res = -ENOMEM;
+                       DRM_ERROR("Can not create overlay plane %d\n", i);
+                       goto cleanup;
+               }
+       }
+
+       return 0;
+
+cleanup:
+       ltdc_plane_destroy_all(ddev);
+       return res;
+}
+
+/*
+ * DRM_ENCODER
+ */
+
+static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+       struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_panel_prepare(ldev->panel);
+       drm_panel_enable(ldev->panel);
+}
+
+static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+       struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_panel_disable(ldev->panel);
+       drm_panel_unprepare(ldev->panel);
+}
+
+static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = {
+       .enable = ltdc_rgb_encoder_enable,
+       .disable = ltdc_rgb_encoder_disable,
+};
+
+static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev)
+{
+       struct drm_encoder *encoder;
+
+       encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
+       if (!encoder)
+               return NULL;
+
+       encoder->possible_crtcs = CRTC_MASK;
+       encoder->possible_clones = 0; /* No cloning support */
+
+       drm_encoder_init(ddev, encoder, &ltdc_rgb_encoder_funcs,
+                        DRM_MODE_ENCODER_DPI, NULL);
+
+       drm_encoder_helper_add(encoder, &ltdc_rgb_encoder_helper_funcs);
+
+       DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id);
+
+       return encoder;
+}
+
+/*
+ * DRM_CONNECTOR
+ */
+
+static int ltdc_rgb_connector_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *ddev = connector->dev;
+       struct ltdc_device *ldev = ddev->dev_private;
+       int ret = 0;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       if (ldev->panel)
+               ret = drm_panel_get_modes(ldev->panel);
+
+       return ret < 0 ? 0 : ret;
+}
+
+static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = {
+       .get_modes = ltdc_rgb_connector_get_modes,
+};
+
+static enum drm_connector_status
+ltdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct ltdc_device *ldev = connector_to_ltdc(connector);
+
+       return ldev->panel ? connector_status_connected :
+              connector_status_disconnected;
+}
+
+static void ltdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ltdc_rgb_connector_funcs = {
+       .dpms = drm_atomic_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = ltdc_rgb_connector_detect,
+       .destroy = ltdc_rgb_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev)
+{
+       struct drm_connector *connector;
+       int err;
+
+       connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL);
+       if (!connector) {
+               DRM_ERROR("Failed to allocate connector\n");
+               return NULL;
+       }
+
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+       err = drm_connector_init(ddev, connector, &ltdc_rgb_connector_funcs,
+                                DRM_MODE_CONNECTOR_DPI);
+       if (err) {
+               DRM_ERROR("Failed to initialize connector\n");
+               return NULL;
+       }
+
+       drm_connector_helper_add(connector, &ltdc_rgb_connector_helper_funcs);
+
+       DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id);
+
+       return connector;
+}
+
+static int ltdc_get_caps(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+       u32 bus_width_log2, lcr, gc2r;
+
+       /* at least 1 layer must be managed */
+       lcr = reg_read(ldev->regs, LTDC_LCR);
+
+       ldev->caps.nb_layers = max_t(int, lcr, 1);
+
+       /* set data bus width */
+       gc2r = reg_read(ldev->regs, LTDC_GC2R);
+       bus_width_log2 = (gc2r & GC2R_BW) >> 4;
+       ldev->caps.bus_width = 8 << bus_width_log2;
+       ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+
+       switch (ldev->caps.hw_version) {
+       case HWVER_10200:
+       case HWVER_10300:
+               ldev->caps.reg_ofs = REG_OFS_NONE;
+               ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+               break;
+       case HWVER_20101:
+               ldev->caps.reg_ofs = REG_OFS_4;
+               ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct drm_panel *ltdc_get_panel(struct drm_device *ddev)
+{
+       struct device *dev = ddev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *entity, *port = NULL;
+       struct drm_panel *panel = NULL;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       /*
+        * Parse ltdc node to get remote port and find RGB panel / HDMI slave
+        * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc,
+        * a remote port & RGB panel will not be found.
+        */
+       for_each_endpoint_of_node(np, entity) {
+               if (!of_device_is_available(entity))
+                       continue;
+
+               port = of_graph_get_remote_port_parent(entity);
+               if (port) {
+                       panel = of_drm_find_panel(port);
+                       of_node_put(port);
+                       if (panel) {
+                               DRM_DEBUG_DRIVER("remote panel %s\n",
+                                                port->full_name);
+                       } else {
+                               DRM_DEBUG_DRIVER("panel missing\n");
+                               of_node_put(entity);
+                       }
+               }
+       }
+
+       return panel;
+}
+
+int ltdc_load(struct drm_device *ddev)
+{
+       struct platform_device *pdev = to_platform_device(ddev->dev);
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct device *dev = ddev->dev;
+       struct device_node *np = dev->of_node;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector = NULL;
+       struct drm_crtc *crtc;
+       struct reset_control *rstc;
+       struct resource res;
+       int irq, ret, i;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       ldev->panel = ltdc_get_panel(ddev);
+       if (!ldev->panel)
+               return -EPROBE_DEFER;
+
+       rstc = of_reset_control_get(np, NULL);
+
+       mutex_init(&ldev->err_lock);
+
+       ldev->pixel_clk = devm_clk_get(dev, "lcd");
+       if (IS_ERR(ldev->pixel_clk)) {
+               DRM_ERROR("Unable to get lcd clock\n");
+               return -ENODEV;
+       }
+
+       if (clk_prepare_enable(ldev->pixel_clk)) {
+               DRM_ERROR("Unable to prepare pixel clock\n");
+               return -ENODEV;
+       }
+
+       if (of_address_to_resource(np, 0, &res)) {
+               DRM_ERROR("Unable to get resource\n");
+               return -ENODEV;
+       }
+
+       ldev->regs = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(ldev->regs)) {
+               DRM_ERROR("Unable to get ltdc registers\n");
+               return PTR_ERR(ldev->regs);
+       }
+
+       for (i = 0; i < MAX_IRQ; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (irq < 0)
+                       continue;
+
+               ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
+                                               ltdc_irq_thread, IRQF_ONESHOT,
+                                               dev_name(dev), ddev);
+               if (ret) {
+                       DRM_ERROR("Failed to register LTDC interrupt\n");
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR(rstc))
+               reset_control_deassert(rstc);
+
+       /* Disable interrupts */
+       reg_clear(ldev->regs, LTDC_IER,
+                 IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       ret = ltdc_get_caps(ddev);
+       if (ret) {
+               DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
+                         ldev->caps.hw_version);
+               return ret;
+       }
+
+       DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version);
+
+       if (ldev->panel) {
+               encoder = ltdc_rgb_encoder_create(ddev);
+               if (!encoder) {
+                       DRM_ERROR("Failed to create RGB encoder\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               connector = ltdc_rgb_connector_create(ddev);
+               if (!connector) {
+                       DRM_ERROR("Failed to create RGB connector\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ret = drm_mode_connector_attach_encoder(connector, encoder);
+               if (ret) {
+                       DRM_ERROR("Failed to attach connector to encoder\n");
+                       goto err;
+               }
+
+               drm_panel_attach(ldev->panel, connector);
+       }
+
+       crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+       if (!crtc) {
+               DRM_ERROR("Failed to allocate crtc\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = ltdc_crtc_init(ddev, crtc);
+       if (ret) {
+               DRM_ERROR("Failed to init crtc\n");
+               goto err;
+       }
+
+       ret = drm_vblank_init(ddev, NB_CRTC);
+       if (ret) {
+               DRM_ERROR("Failed calling drm_vblank_init()\n");
+               goto err;
+       }
+
+       /* Allow usage of vblank without having to call drm_irq_install */
+       ddev->irq_enabled = 1;
+
+       return 0;
+err:
+       if (ldev->panel)
+               drm_panel_detach(ldev->panel);
+
+       clk_disable_unprepare(ldev->pixel_clk);
+
+       return ret;
+}
+
+void ltdc_unload(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_vblank_cleanup(ddev);
+
+       if (ldev->panel)
+               drm_panel_detach(ldev->panel);
+
+       clk_disable_unprepare(ldev->pixel_clk);
+}
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
new file mode 100644 (file)
index 0000000..d7a9c73
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LTDC_H_
+#define _LTDC_H_
+
+struct ltdc_caps {
+       u32 hw_version;         /* hardware version */
+       u32 nb_layers;          /* number of supported layers */
+       u32 reg_ofs;            /* register offset for applicable regs */
+       u32 bus_width;          /* bus width (32 or 64 bits) */
+       const u32 *pix_fmt_hw;  /* supported pixel formats */
+};
+
+struct ltdc_device {
+       struct drm_fbdev_cma *fbdev;
+       void __iomem *regs;
+       struct clk *pixel_clk;  /* lcd pixel clock */
+       struct drm_panel *panel;
+       struct mutex err_lock;  /* protecting error_status */
+       struct ltdc_caps caps;
+       u32 clut[256];          /* color look up table */
+       u32 error_status;
+       u32 irq_status;
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
+void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
+int ltdc_load(struct drm_device *ddev);
+void ltdc_unload(struct drm_device *ddev);
+
+#endif
index 61f45d1..ab687fb 100644 (file)
@@ -9,6 +9,7 @@ vc4-y := \
        vc4_drv.o \
        vc4_dpi.o \
        vc4_dsi.o \
+       vc4_fence.o \
        vc4_kms.o \
        vc4_gem.o \
        vc4_hdmi.o \
index af29432..80b2f9e 100644 (file)
@@ -19,6 +19,8 @@
  * rendering can return quickly.
  */
 
+#include <linux/dma-buf.h>
+
 #include "vc4_drv.h"
 #include "uapi/drm/vc4_drm.h"
 
@@ -88,6 +90,10 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
 
        vc4->bo_stats.num_allocated--;
        vc4->bo_stats.size_allocated -= obj->size;
+
+       if (bo->resv == &bo->_resv)
+               reservation_object_fini(bo->resv);
+
        drm_gem_cma_free_object(obj);
 }
 
@@ -244,8 +250,12 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
                        return ERR_PTR(-ENOMEM);
                }
        }
+       bo = to_vc4_bo(&cma_obj->base);
 
-       return to_vc4_bo(&cma_obj->base);
+       bo->resv = &bo->_resv;
+       reservation_object_init(bo->resv);
+
+       return bo;
 }
 
 int vc4_dumb_create(struct drm_file *file_priv,
@@ -369,6 +379,13 @@ static void vc4_bo_cache_time_timer(unsigned long data)
        schedule_work(&vc4->bo_cache.time_work);
 }
 
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj)
+{
+       struct vc4_bo *bo = to_vc4_bo(obj);
+
+       return bo->resv;
+}
+
 struct dma_buf *
 vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
 {
@@ -440,6 +457,24 @@ void *vc4_prime_vmap(struct drm_gem_object *obj)
        return drm_gem_cma_prime_vmap(obj);
 }
 
+struct drm_gem_object *
+vc4_prime_import_sg_table(struct drm_device *dev,
+                         struct dma_buf_attachment *attach,
+                         struct sg_table *sgt)
+{
+       struct drm_gem_object *obj;
+       struct vc4_bo *bo;
+
+       obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
+       if (IS_ERR(obj))
+               return obj;
+
+       bo = to_vc4_bo(obj);
+       bo->resv = attach->dmabuf->resv;
+
+       return obj;
+}
+
 int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
index 61e674b..92fb9a4 100644 (file)
@@ -168,8 +168,9 @@ static struct drm_driver vc4_drm_driver = {
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
        .gem_prime_import = drm_gem_prime_import,
        .gem_prime_export = vc4_prime_export,
+       .gem_prime_res_obj = vc4_prime_res_obj,
        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
-       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_import_sg_table = vc4_prime_import_sg_table,
        .gem_prime_vmap = vc4_prime_vmap,
        .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
        .gem_prime_mmap = vc4_prime_mmap,
index dffce62..b0967e2 100644 (file)
@@ -8,7 +8,9 @@
 
 #include "drmP.h"
 #include "drm_gem_cma_helper.h"
+#include "drm_gem_cma_helper.h"
 
+#include <linux/reservation.h>
 #include <drm/drm_encoder.h>
 
 struct vc4_dev {
@@ -56,6 +58,8 @@ struct vc4_dev {
        /* Protects bo_cache and the BO stats. */
        struct mutex bo_lock;
 
+       uint64_t dma_fence_context;
+
        /* Sequence number for the last job queued in bin_job_list.
         * Starts at 0 (no jobs emitted).
         */
@@ -95,12 +99,23 @@ struct vc4_dev {
         */
        struct list_head seqno_cb_list;
 
-       /* The binner overflow memory that's currently set up in
-        * BPOA/BPOS registers.  When overflow occurs and a new one is
-        * allocated, the previous one will be moved to
-        * vc4->current_exec's free list.
+       /* The memory used for storing binner tile alloc, tile state,
+        * and overflow memory allocations.  This is freed when V3D
+        * powers down.
+        */
+       struct vc4_bo *bin_bo;
+
+       /* Size of blocks allocated within bin_bo. */
+       uint32_t bin_alloc_size;
+
+       /* Bitmask of the bin_alloc_size chunks in bin_bo that are
+        * used.
         */
-       struct vc4_bo *overflow_mem;
+       uint32_t bin_alloc_used;
+
+       /* Bitmask of the current bin_alloc used for overflow memory. */
+       uint32_t bin_alloc_overflow;
+
        struct work_struct overflow_mem_work;
 
        int power_refcount;
@@ -150,6 +165,10 @@ struct vc4_bo {
         * DRM_IOCTL_VC4_CREATE_SHADER_BO.
         */
        struct vc4_validated_shader_info *validated_shader;
+
+       /* normally (resv == &_resv) except for imported bo's */
+       struct reservation_object *resv;
+       struct reservation_object _resv;
 };
 
 static inline struct vc4_bo *
@@ -158,6 +177,19 @@ to_vc4_bo(struct drm_gem_object *bo)
        return (struct vc4_bo *)bo;
 }
 
+struct vc4_fence {
+       struct dma_fence base;
+       struct drm_device *dev;
+       /* vc4 seqno for signaled() test */
+       uint64_t seqno;
+};
+
+static inline struct vc4_fence *
+to_vc4_fence(struct dma_fence *fence)
+{
+       return (struct vc4_fence *)fence;
+}
+
 struct vc4_seqno_cb {
        struct work_struct work;
        uint64_t seqno;
@@ -230,6 +262,8 @@ struct vc4_exec_info {
        /* Latest write_seqno of any BO that binning depends on. */
        uint64_t bin_dep_seqno;
 
+       struct dma_fence *fence;
+
        /* Last current addresses the hardware was processing when the
         * hangcheck timer checked on us.
         */
@@ -293,8 +327,12 @@ struct vc4_exec_info {
        bool found_increment_semaphore_packet;
        bool found_flush;
        uint8_t bin_tiles_x, bin_tiles_y;
-       struct drm_gem_cma_object *tile_bo;
+       /* Physical address of the start of the tile alloc array
+        * (where each tile's binned CL will start)
+        */
        uint32_t tile_alloc_offset;
+       /* Bitmask of which binner slots are freed when this job completes. */
+       uint32_t bin_slots;
 
        /**
         * Computed addresses pointing into exec_bo where we start the
@@ -436,7 +474,11 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
 int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
                             struct drm_file *file_priv);
 int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj);
 int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev,
+                                                struct dma_buf_attachment *attach,
+                                                struct sg_table *sgt);
 void *vc4_prime_vmap(struct drm_gem_object *obj);
 void vc4_bo_cache_init(struct drm_device *dev);
 void vc4_bo_cache_destroy(struct drm_device *dev);
@@ -468,6 +510,9 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
 extern struct platform_driver vc4_dsi_driver;
 int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
 
+/* vc4_fence.c */
+extern const struct dma_fence_ops vc4_fence_ops;
+
 /* vc4_gem.c */
 void vc4_gem_init(struct drm_device *dev);
 void vc4_gem_destroy(struct drm_device *dev);
@@ -522,6 +567,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
 extern struct platform_driver vc4_v3d_driver;
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4);
 
 /* vc4_validate.c */
 int
diff --git a/drivers/gpu/drm/vc4/vc4_fence.c b/drivers/gpu/drm/vc4/vc4_fence.c
new file mode 100644 (file)
index 0000000..dbf5a5a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2017 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "vc4_drv.h"
+
+static const char *vc4_fence_get_driver_name(struct dma_fence *fence)
+{
+       return "vc4";
+}
+
+static const char *vc4_fence_get_timeline_name(struct dma_fence *fence)
+{
+       return "vc4-v3d";
+}
+
+static bool vc4_fence_enable_signaling(struct dma_fence *fence)
+{
+       return true;
+}
+
+static bool vc4_fence_signaled(struct dma_fence *fence)
+{
+       struct vc4_fence *f = to_vc4_fence(fence);
+       struct vc4_dev *vc4 = to_vc4_dev(f->dev);
+
+       return vc4->finished_seqno >= f->seqno;
+}
+
+const struct dma_fence_ops vc4_fence_ops = {
+       .get_driver_name = vc4_fence_get_driver_name,
+       .get_timeline_name = vc4_fence_get_timeline_name,
+       .enable_signaling = vc4_fence_enable_signaling,
+       .signaled = vc4_fence_signaled,
+       .wait = dma_fence_default_wait,
+       .release = dma_fence_free,
+};
index e9c381c..735412e 100644 (file)
@@ -463,6 +463,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
        for (i = 0; i < exec->bo_count; i++) {
                bo = to_vc4_bo(&exec->bo[i]->base);
                bo->seqno = seqno;
+
+               reservation_object_add_shared_fence(bo->resv, exec->fence);
        }
 
        list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -472,7 +474,103 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
        for (i = 0; i < exec->rcl_write_bo_count; i++) {
                bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
                bo->write_seqno = seqno;
+
+               reservation_object_add_excl_fence(bo->resv, exec->fence);
+       }
+}
+
+static void
+vc4_unlock_bo_reservations(struct drm_device *dev,
+                          struct vc4_exec_info *exec,
+                          struct ww_acquire_ctx *acquire_ctx)
+{
+       int i;
+
+       for (i = 0; i < exec->bo_count; i++) {
+               struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ww_mutex_unlock(&bo->resv->lock);
+       }
+
+       ww_acquire_fini(acquire_ctx);
+}
+
+/* Takes the reservation lock on all the BOs being referenced, so that
+ * at queue submit time we can update the reservations.
+ *
+ * We don't lock the RCL the tile alloc/state BOs, or overflow memory
+ * (all of which are on exec->unref_list).  They're entirely private
+ * to vc4, so we don't attach dma-buf fences to them.
+ */
+static int
+vc4_lock_bo_reservations(struct drm_device *dev,
+                        struct vc4_exec_info *exec,
+                        struct ww_acquire_ctx *acquire_ctx)
+{
+       int contended_lock = -1;
+       int i, ret;
+       struct vc4_bo *bo;
+
+       ww_acquire_init(acquire_ctx, &reservation_ww_class);
+
+retry:
+       if (contended_lock != -1) {
+               bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+               ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+                                                      acquire_ctx);
+               if (ret) {
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < exec->bo_count; i++) {
+               if (i == contended_lock)
+                       continue;
+
+               bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx);
+               if (ret) {
+                       int j;
+
+                       for (j = 0; j < i; j++) {
+                               bo = to_vc4_bo(&exec->bo[j]->base);
+                               ww_mutex_unlock(&bo->resv->lock);
+                       }
+
+                       if (contended_lock != -1 && contended_lock >= i) {
+                               bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+
+                               ww_mutex_unlock(&bo->resv->lock);
+                       }
+
+                       if (ret == -EDEADLK) {
+                               contended_lock = i;
+                               goto retry;
+                       }
+
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
        }
+
+       ww_acquire_done(acquire_ctx);
+
+       /* Reserve space for our shared (read-only) fence references,
+        * before we commit the CL to the hardware.
+        */
+       for (i = 0; i < exec->bo_count; i++) {
+               bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ret = reservation_object_reserve_shared(bo->resv);
+               if (ret) {
+                       vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+                       return ret;
+               }
+       }
+
+       return 0;
 }
 
 /* Queues a struct vc4_exec_info for execution.  If no job is
@@ -484,19 +582,34 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
  * then bump the end address.  That's a change for a later date,
  * though.
  */
-static void
-vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
+static int
+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
+                struct ww_acquire_ctx *acquire_ctx)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        uint64_t seqno;
        unsigned long irqflags;
+       struct vc4_fence *fence;
+
+       fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+       if (!fence)
+               return -ENOMEM;
+       fence->dev = dev;
 
        spin_lock_irqsave(&vc4->job_lock, irqflags);
 
        seqno = ++vc4->emit_seqno;
        exec->seqno = seqno;
+
+       dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
+                      vc4->dma_fence_context, exec->seqno);
+       fence->seqno = exec->seqno;
+       exec->fence = &fence->base;
+
        vc4_update_bo_seqnos(exec, seqno);
 
+       vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+
        list_add_tail(&exec->head, &vc4->bin_job_list);
 
        /* If no job was executing, kick ours off.  Otherwise, it'll
@@ -509,6 +622,8 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
        }
 
        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       return 0;
 }
 
 /**
@@ -705,8 +820,15 @@ static void
 vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
        unsigned i;
 
+       /* If we got force-completed because of GPU reset rather than
+        * through our IRQ handler, signal the fence now.
+        */
+       if (exec->fence)
+               dma_fence_signal(exec->fence);
+
        if (exec->bo) {
                for (i = 0; i < exec->bo_count; i++)
                        drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
@@ -720,6 +842,11 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
                drm_gem_object_unreference_unlocked(&bo->base.base);
        }
 
+       /* Free up the allocation of any bin slots we used. */
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       vc4->bin_alloc_used &= ~exec->bin_slots;
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
        mutex_lock(&vc4->power_lock);
        if (--vc4->power_refcount == 0) {
                pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
@@ -874,6 +1001,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        struct drm_vc4_submit_cl *args = data;
        struct vc4_exec_info *exec;
+       struct ww_acquire_ctx acquire_ctx;
        int ret = 0;
 
        if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
@@ -888,13 +1016,16 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        }
 
        mutex_lock(&vc4->power_lock);
-       if (vc4->power_refcount++ == 0)
+       if (vc4->power_refcount++ == 0) {
                ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
-       mutex_unlock(&vc4->power_lock);
-       if (ret < 0) {
-               kfree(exec);
-               return ret;
+               if (ret < 0) {
+                       mutex_unlock(&vc4->power_lock);
+                       vc4->power_refcount--;
+                       kfree(exec);
+                       return ret;
+               }
        }
+       mutex_unlock(&vc4->power_lock);
 
        exec->args = args;
        INIT_LIST_HEAD(&exec->unref_list);
@@ -916,12 +1047,18 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        if (ret)
                goto fail;
 
+       ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
+       if (ret)
+               goto fail;
+
        /* Clear this out of the struct we'll be putting in the queue,
         * since it's part of our stack.
         */
        exec->args = NULL;
 
-       vc4_queue_submit(dev, exec);
+       ret = vc4_queue_submit(dev, exec, &acquire_ctx);
+       if (ret)
+               goto fail;
 
        /* Return the seqno for our job. */
        args->seqno = vc4->emit_seqno;
@@ -939,6 +1076,8 @@ vc4_gem_init(struct drm_device *dev)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
 
+       vc4->dma_fence_context = dma_fence_context_alloc(1);
+
        INIT_LIST_HEAD(&vc4->bin_job_list);
        INIT_LIST_HEAD(&vc4->render_job_list);
        INIT_LIST_HEAD(&vc4->job_done_list);
@@ -968,9 +1107,9 @@ vc4_gem_destroy(struct drm_device *dev)
        /* V3D should already have disabled its interrupt and cleared
         * the overflow allocation registers.  Now free the object.
         */
-       if (vc4->overflow_mem) {
-               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-               vc4->overflow_mem = NULL;
+       if (vc4->bin_bo) {
+               drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+               vc4->bin_bo = NULL;
        }
 
        if (vc4->hang_state)
index e9cbe26..3c2723f 100644 (file)
@@ -51,6 +51,7 @@
 #include "linux/of_address.h"
 #include "linux/of_gpio.h"
 #include "linux/of_platform.h"
+#include "linux/pm_runtime.h"
 #include "linux/rational.h"
 #include "sound/dmaengine_pcm.h"
 #include "sound/pcm_drm_eld.h"
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
        vc4_hdmi_set_spd_infoframe(encoder);
 }
 
-static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
-                                     struct drm_display_mode *unadjusted_mode,
-                                     struct drm_display_mode *mode)
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+       int ret;
+
+       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+       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);
+       if (ret < 0)
+               DRM_ERROR("Failed to release power domain: %d\n", ret);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
+       struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
        struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hdmi *hdmi = vc4->hdmi;
        bool debug_dump_regs = false;
        bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
        bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
                                        interlaced,
                                        VC4_HDMI_VERTB_VBP));
        u32 csc_ctl;
+       int ret;
+
+       ret = pm_runtime_get_sync(&hdmi->pdev->dev);
+       if (ret < 0) {
+               DRM_ERROR("Failed to retain power domain: %d\n", ret);
+               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));
+       if (ret) {
+               DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+               return;
+       }
+
+       ret = clk_prepare_enable(hdmi->pixel_clock);
+       if (ret) {
+               DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+               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);
+
+       HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
+
+       /* PHY should be in reset, like
+        * vc4_hdmi_encoder_disable() does.
+        */
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
        if (debug_dump_regs) {
                DRM_INFO("HDMI regs before:\n");
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
        HD_WRITE(VC4_HD_VID_CTL, 0);
 
-       clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
-                    ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
-
        HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
                   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
                   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
                DRM_INFO("HDMI regs after:\n");
                vc4_hdmi_dump_regs(dev);
        }
-}
-
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
-{
-       struct drm_device *dev = encoder->dev;
-       struct vc4_dev *vc4 = to_vc4_dev(dev);
-
-       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
-
-       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-       HD_WRITE(VC4_HD_VID_CTL,
-                HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
-}
-
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
-{
-       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct vc4_dev *vc4 = to_vc4_dev(dev);
-       int ret;
-
-       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
        HD_WRITE(VC4_HD_VID_CTL,
                 HD_READ(VC4_HD_VID_CTL) |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 }
 
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
-       .mode_set = vc4_hdmi_encoder_mode_set,
        .disable = vc4_hdmi_encoder_disable,
        .enable = vc4_hdmi_encoder_enable,
 };
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                return -EPROBE_DEFER;
        }
 
-       /* Enable the clocks at startup.  We can't quite recover from
-        * turning off the pixel clock during disable/enables yet, so
-        * it's always running.
-        */
-       ret = clk_prepare_enable(hdmi->pixel_clock);
-       if (ret) {
-               DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
-               goto err_put_i2c;
-       }
-
-       /* 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);
-               goto err_unprepare_pix;
-       }
-
-       ret = clk_prepare_enable(hdmi->hsm_clock);
-       if (ret) {
-               DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
-                         ret);
-               goto err_unprepare_pix;
-       }
-
        /* Only use the GPIO HPD pin if present in the DT, otherwise
         * we'll use the HDMI core's register.
         */
@@ -1185,7 +1216,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_unprepare_hsm;
+                       goto err_put_i2c;
                }
 
                hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
@@ -1193,25 +1224,7 @@ 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);
-
-               HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
-                          VC4_HDMI_SW_RESET_HDMI |
-                          VC4_HDMI_SW_RESET_FORMAT_DETECT);
-
-               HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
-
-               /* PHY should be in reset, like
-                * vc4_hdmi_encoder_disable() does.
-                */
-               HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-       }
+       pm_runtime_enable(dev);
 
        drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
                         DRM_MODE_ENCODER_TMDS, NULL);
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 err_destroy_encoder:
        vc4_hdmi_encoder_destroy(hdmi->encoder);
-err_unprepare_hsm:
-       clk_disable_unprepare(hdmi->hsm_clock);
-err_unprepare_pix:
-       clk_disable_unprepare(hdmi->pixel_clock);
+       pm_runtime_disable(dev);
 err_put_i2c:
        put_device(&hdmi->ddc->dev);
 
@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
        vc4_hdmi_connector_destroy(hdmi->connector);
        vc4_hdmi_encoder_destroy(hdmi->encoder);
 
-       clk_disable_unprepare(hdmi->pixel_clock);
-       clk_disable_unprepare(hdmi->hsm_clock);
+       pm_runtime_disable(dev);
+
        put_device(&hdmi->ddc->dev);
 
        vc4->hdmi = NULL;
index cdc6e67..7d7af3a 100644 (file)
@@ -59,50 +59,45 @@ vc4_overflow_mem_work(struct work_struct *work)
 {
        struct vc4_dev *vc4 =
                container_of(work, struct vc4_dev, overflow_mem_work);
-       struct drm_device *dev = vc4->dev;
-       struct vc4_bo *bo;
+       struct vc4_bo *bo = vc4->bin_bo;
+       int bin_bo_slot;
+       struct vc4_exec_info *exec;
+       unsigned long irqflags;
 
-       bo = vc4_bo_create(dev, 256 * 1024, true);
-       if (IS_ERR(bo)) {
+       bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
+       if (bin_bo_slot < 0) {
                DRM_ERROR("Couldn't allocate binner overflow mem\n");
                return;
        }
 
-       /* If there's a job executing currently, then our previous
-        * overflow allocation is getting used in that job and we need
-        * to queue it to be released when the job is done.  But if no
-        * job is executing at all, then we can free the old overflow
-        * object direcctly.
-        *
-        * No lock necessary for this pointer since we're the only
-        * ones that update the pointer, and our workqueue won't
-        * reenter.
-        */
-       if (vc4->overflow_mem) {
-               struct vc4_exec_info *current_exec;
-               unsigned long irqflags;
-
-               spin_lock_irqsave(&vc4->job_lock, irqflags);
-               current_exec = vc4_first_bin_job(vc4);
-               if (!current_exec)
-                       current_exec = vc4_last_render_job(vc4);
-               if (current_exec) {
-                       vc4->overflow_mem->seqno = current_exec->seqno;
-                       list_add_tail(&vc4->overflow_mem->unref_head,
-                                     &current_exec->unref_list);
-                       vc4->overflow_mem = NULL;
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+
+       if (vc4->bin_alloc_overflow) {
+               /* If we had overflow memory allocated previously,
+                * then that chunk will free when the current bin job
+                * is done.  If we don't have a bin job running, then
+                * the chunk will be done whenever the list of render
+                * jobs has drained.
+                */
+               exec = vc4_first_bin_job(vc4);
+               if (!exec)
+                       exec = vc4_last_render_job(vc4);
+               if (exec) {
+                       exec->bin_slots |= vc4->bin_alloc_overflow;
+               } else {
+                       /* There's nothing queued in the hardware, so
+                        * the old slot is free immediately.
+                        */
+                       vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
                }
-               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
        }
+       vc4->bin_alloc_overflow = BIT(bin_bo_slot);
 
-       if (vc4->overflow_mem)
-               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-       vc4->overflow_mem = bo;
-
-       V3D_WRITE(V3D_BPOA, bo->base.paddr);
+       V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
        V3D_WRITE(V3D_BPOS, bo->base.base.size);
        V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
        V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 }
 
 static void
@@ -142,6 +137,10 @@ vc4_irq_finish_render_job(struct drm_device *dev)
 
        vc4->finished_seqno++;
        list_move_tail(&exec->head, &vc4->job_done_list);
+       if (exec->fence) {
+               dma_fence_signal_locked(exec->fence);
+               exec->fence = NULL;
+       }
        vc4_submit_next_render_job(dev);
 
        wake_up_all(&vc4->job_wait_queue);
index 4339471..5dc1942 100644 (file)
@@ -182,8 +182,7 @@ static void emit_tile(struct vc4_exec_info *exec,
 
        if (has_bin) {
                rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
-               rcl_u32(setup, (exec->tile_bo->paddr +
-                               exec->tile_alloc_offset +
+               rcl_u32(setup, (exec->tile_alloc_offset +
                                (y * exec->bin_tiles_x + x) * 32));
        }
 
index 7cc346a..a88078d 100644 (file)
@@ -156,6 +156,144 @@ static void vc4_v3d_init_hw(struct drm_device *dev)
        V3D_WRITE(V3D_VPMBASE, 0);
 }
 
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
+{
+       struct drm_device *dev = vc4->dev;
+       unsigned long irqflags;
+       int slot;
+       uint64_t seqno = 0;
+       struct vc4_exec_info *exec;
+
+try_again:
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       slot = ffs(~vc4->bin_alloc_used);
+       if (slot != 0) {
+               /* Switch from ffs() bit index to a 0-based index. */
+               slot--;
+               vc4->bin_alloc_used |= BIT(slot);
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return slot;
+       }
+
+       /* Couldn't find an open slot.  Wait for render to complete
+        * and try again.
+        */
+       exec = vc4_last_render_job(vc4);
+       if (exec)
+               seqno = exec->seqno;
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       if (seqno) {
+               int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
+
+               if (ret == 0)
+                       goto try_again;
+
+               return ret;
+       }
+
+       return -ENOMEM;
+}
+
+/**
+ * vc4_allocate_bin_bo() - allocates the memory that will be used for
+ * tile binning.
+ *
+ * The binner has a limitation that the addresses in the tile state
+ * buffer that point into the tile alloc buffer or binner overflow
+ * memory only have 28 bits (256MB), and the top 4 on the bus for
+ * tile alloc references end up coming from the tile state buffer's
+ * address.
+ *
+ * To work around this, we allocate a single large buffer while V3D is
+ * in use, make sure that it has the top 4 bits constant across its
+ * entire extent, and then put the tile state, tile alloc, and binner
+ * overflow memory inside that buffer.
+ *
+ * This creates a limitation where we may not be able to execute a job
+ * if it doesn't fit within the buffer that we allocated up front.
+ * However, it turns out that 16MB is "enough for anybody", and
+ * real-world applications run into allocation failures from the
+ * overall CMA pool before they make scenes complicated enough to run
+ * out of bin space.
+ */
+int
+vc4_allocate_bin_bo(struct drm_device *drm)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_v3d *v3d = vc4->v3d;
+       uint32_t size = 16 * 1024 * 1024;
+       int ret = 0;
+       struct list_head list;
+
+       /* We may need to try allocating more than once to get a BO
+        * that doesn't cross 256MB.  Track the ones we've allocated
+        * that failed so far, so that we can free them when we've got
+        * one that succeeded (if we freed them right away, our next
+        * allocation would probably be the same chunk of memory).
+        */
+       INIT_LIST_HEAD(&list);
+
+       while (true) {
+               struct vc4_bo *bo = vc4_bo_create(drm, size, true);
+
+               if (IS_ERR(bo)) {
+                       ret = PTR_ERR(bo);
+
+                       dev_err(&v3d->pdev->dev,
+                               "Failed to allocate memory for tile binning: "
+                               "%d. You may need to enable CMA or give it "
+                               "more memory.",
+                               ret);
+                       break;
+               }
+
+               /* Check if this BO won't trigger the addressing bug. */
+               if ((bo->base.paddr & 0xf0000000) ==
+                   ((bo->base.paddr + bo->base.base.size - 1) & 0xf0000000)) {
+                       vc4->bin_bo = bo;
+
+                       /* Set up for allocating 512KB chunks of
+                        * binner memory.  The biggest allocation we
+                        * need to do is for the initial tile alloc +
+                        * tile state buffer.  We can render to a
+                        * maximum of ((2048*2048) / (32*32) = 4096
+                        * tiles in a frame (until we do floating
+                        * point rendering, at which point it would be
+                        * 8192).  Tile state is 48b/tile (rounded to
+                        * a page), and tile alloc is 32b/tile
+                        * (rounded to a page), plus a page of extra,
+                        * for a total of 320kb for our worst-case.
+                        * We choose 512kb so that it divides evenly
+                        * into our 16MB, and the rest of the 512kb
+                        * will be used as storage for the overflow
+                        * from the initial 32b CL per bin.
+                        */
+                       vc4->bin_alloc_size = 512 * 1024;
+                       vc4->bin_alloc_used = 0;
+                       vc4->bin_alloc_overflow = 0;
+                       WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
+                                    bo->base.base.size / vc4->bin_alloc_size);
+
+                       break;
+               }
+
+               /* Put it on the list to free later, and try again. */
+               list_add(&bo->unref_head, &list);
+       }
+
+       /* Free all the BOs we allocated but didn't choose. */
+       while (!list_empty(&list)) {
+               struct vc4_bo *bo = list_last_entry(&list,
+                                                   struct vc4_bo, unref_head);
+
+               list_del(&bo->unref_head);
+               drm_gem_object_put_unlocked(&bo->base.base);
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_PM
 static int vc4_v3d_runtime_suspend(struct device *dev)
 {
@@ -164,6 +302,9 @@ static int vc4_v3d_runtime_suspend(struct device *dev)
 
        vc4_irq_uninstall(vc4->dev);
 
+       drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+       vc4->bin_bo = NULL;
+
        return 0;
 }
 
@@ -171,6 +312,11 @@ static int vc4_v3d_runtime_resume(struct device *dev)
 {
        struct vc4_v3d *v3d = dev_get_drvdata(dev);
        struct vc4_dev *vc4 = v3d->vc4;
+       int ret;
+
+       ret = vc4_allocate_bin_bo(vc4->dev);
+       if (ret)
+               return ret;
 
        vc4_v3d_init_hw(vc4->dev);
        vc4_irq_postinstall(vc4->dev);
@@ -208,6 +354,10 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
                return -EINVAL;
        }
 
+       ret = vc4_allocate_bin_bo(drm);
+       if (ret)
+               return ret;
+
        /* Reset the binner overflow address/size at setup, to be sure
         * we don't reuse an old one.
         */
index da6f1e1..3de8f11 100644 (file)
@@ -348,10 +348,11 @@ static int
 validate_tile_binning_config(VALIDATE_ARGS)
 {
        struct drm_device *dev = exec->exec_bo->base.dev;
-       struct vc4_bo *tile_bo;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
        uint8_t flags;
-       uint32_t tile_state_size, tile_alloc_size;
-       uint32_t tile_count;
+       uint32_t tile_state_size;
+       uint32_t tile_count, bin_addr;
+       int bin_slot;
 
        if (exec->found_tile_binning_mode_config_packet) {
                DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
@@ -377,13 +378,28 @@ validate_tile_binning_config(VALIDATE_ARGS)
                return -EINVAL;
        }
 
+       bin_slot = vc4_v3d_get_bin_slot(vc4);
+       if (bin_slot < 0) {
+               if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
+                       DRM_ERROR("Failed to allocate binner memory: %d\n",
+                                 bin_slot);
+               }
+               return bin_slot;
+       }
+
+       /* The slot we allocated will only be used by this job, and is
+        * free when the job completes rendering.
+        */
+       exec->bin_slots |= BIT(bin_slot);
+       bin_addr = vc4->bin_bo->base.paddr + bin_slot * vc4->bin_alloc_size;
+
        /* The tile state data array is 48 bytes per tile, and we put it at
         * the start of a BO containing both it and the tile alloc.
         */
        tile_state_size = 48 * tile_count;
 
        /* Since the tile alloc array will follow us, align. */
-       exec->tile_alloc_offset = roundup(tile_state_size, 4096);
+       exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
 
        *(uint8_t *)(validated + 14) =
                ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
@@ -394,35 +410,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
                 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
                               VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
 
-       /* Initial block size. */
-       tile_alloc_size = 32 * tile_count;
-
-       /*
-        * The initial allocation gets rounded to the next 256 bytes before
-        * the hardware starts fulfilling further allocations.
-        */
-       tile_alloc_size = roundup(tile_alloc_size, 256);
-
-       /* Add space for the extra allocations.  This is what gets used first,
-        * before overflow memory.  It must have at least 4096 bytes, but we
-        * want to avoid overflow memory usage if possible.
-        */
-       tile_alloc_size += 1024 * 1024;
-
-       tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
-                               true);
-       exec->tile_bo = &tile_bo->base;
-       if (IS_ERR(exec->tile_bo))
-               return PTR_ERR(exec->tile_bo);
-       list_add_tail(&tile_bo->unref_head, &exec->unref_list);
-
        /* tile alloc address. */
-       *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
-                                       exec->tile_alloc_offset);
+       *(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
        /* tile alloc size. */
-       *(uint32_t *)(validated + 4) = tile_alloc_size;
+       *(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
+                                       exec->tile_alloc_offset);
        /* tile state address. */
-       *(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
+       *(uint32_t *)(validated + 8) = bin_addr;
 
        return 0;
 }
index 01352b5..9df7766 100644 (file)
@@ -3,6 +3,7 @@ zxdrm-y := \
        zx_hdmi.o \
        zx_plane.o \
        zx_tvenc.o \
+       zx_vga.o \
        zx_vou.o
 
 obj-$(CONFIG_DRM_ZTE) += zxdrm.o
diff --git a/drivers/gpu/drm/zte/zx_common_regs.h b/drivers/gpu/drm/zte/zx_common_regs.h
new file mode 100644 (file)
index 0000000..2afd806
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_COMMON_REGS_H__
+#define __ZX_COMMON_REGS_H__
+
+/* CSC registers */
+#define CSC_CTRL0                      0x30
+#define CSC_COV_MODE_SHIFT             16
+#define CSC_COV_MODE_MASK              (0xffff << CSC_COV_MODE_SHIFT)
+#define CSC_BT601_IMAGE_RGB2YCBCR      0
+#define CSC_BT601_IMAGE_YCBCR2RGB      1
+#define CSC_BT601_VIDEO_RGB2YCBCR      2
+#define CSC_BT601_VIDEO_YCBCR2RGB      3
+#define CSC_BT709_IMAGE_RGB2YCBCR      4
+#define CSC_BT709_IMAGE_YCBCR2RGB      5
+#define CSC_BT709_VIDEO_RGB2YCBCR      6
+#define CSC_BT709_VIDEO_YCBCR2RGB      7
+#define CSC_BT2020_IMAGE_RGB2YCBCR     8
+#define CSC_BT2020_IMAGE_YCBCR2RGB     9
+#define CSC_BT2020_VIDEO_RGB2YCBCR     10
+#define CSC_BT2020_VIDEO_YCBCR2RGB     11
+#define CSC_WORK_ENABLE                        BIT(0)
+
+#endif /* __ZX_COMMON_REGS_H__ */
index 614e854..490aafc 100644 (file)
@@ -233,6 +233,7 @@ static struct platform_driver *drivers[] = {
        &zx_crtc_driver,
        &zx_hdmi_driver,
        &zx_tvenc_driver,
+       &zx_vga_driver,
        &zx_drm_platform_driver,
 };
 
index 5ca035b..2a8cdc5 100644 (file)
@@ -14,6 +14,7 @@
 extern struct platform_driver zx_crtc_driver;
 extern struct platform_driver zx_hdmi_driver;
 extern struct platform_driver zx_tvenc_driver;
+extern struct platform_driver zx_vga_driver;
 
 static inline u32 zx_readl(void __iomem *reg)
 {
index d646ac9..4a62527 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 
+#include "zx_common_regs.h"
 #include "zx_drm_drv.h"
 #include "zx_plane.h"
 #include "zx_plane_regs.h"
index 65f271a..9c655f5 100644 (file)
 #define LUMA_STRIDE(x)  (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
 #define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
 
-/* CSC registers */
-#define CSC_CTRL0                      0x30
-#define CSC_COV_MODE_SHIFT             16
-#define CSC_COV_MODE_MASK              (0xffff << CSC_COV_MODE_SHIFT)
-#define CSC_BT601_IMAGE_RGB2YCBCR      0
-#define CSC_BT601_IMAGE_YCBCR2RGB      1
-#define CSC_BT601_VIDEO_RGB2YCBCR      2
-#define CSC_BT601_VIDEO_YCBCR2RGB      3
-#define CSC_BT709_IMAGE_RGB2YCBCR      4
-#define CSC_BT709_IMAGE_YCBCR2RGB      5
-#define CSC_BT709_VIDEO_RGB2YCBCR      6
-#define CSC_BT709_VIDEO_YCBCR2RGB      7
-#define CSC_BT2020_IMAGE_RGB2YCBCR     8
-#define CSC_BT2020_IMAGE_YCBCR2RGB     9
-#define CSC_BT2020_VIDEO_RGB2YCBCR     10
-#define CSC_BT2020_VIDEO_YCBCR2RGB     11
-#define CSC_WORK_ENABLE                        BIT(0)
-
 /* RSZ registers */
 #define RSZ_SRC_CFG                    0x00
 #define RSZ_DEST_CFG                   0x04
diff --git a/drivers/gpu/drm/zte/zx_vga.c b/drivers/gpu/drm/zte/zx_vga.c
new file mode 100644 (file)
index 0000000..1e0811f
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include "zx_drm_drv.h"
+#include "zx_vga_regs.h"
+#include "zx_vou.h"
+
+struct zx_vga_pwrctrl {
+       struct regmap *regmap;
+       u32 reg;
+       u32 mask;
+};
+
+struct zx_vga_i2c {
+       struct i2c_adapter adap;
+       struct mutex lock;
+};
+
+struct zx_vga {
+       struct drm_connector connector;
+       struct drm_encoder encoder;
+       struct zx_vga_i2c *ddc;
+       struct device *dev;
+       void __iomem *mmio;
+       struct clk *i2c_wclk;
+       struct zx_vga_pwrctrl pwrctrl;
+       struct completion complete;
+       bool connected;
+};
+
+#define to_zx_vga(x) container_of(x, struct zx_vga, x)
+
+static void zx_vga_encoder_enable(struct drm_encoder *encoder)
+{
+       struct zx_vga *vga = to_zx_vga(encoder);
+       struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+       /* Set bit to power up VGA DACs */
+       regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
+                          pwrctrl->mask);
+
+       vou_inf_enable(VOU_VGA, encoder->crtc);
+}
+
+static void zx_vga_encoder_disable(struct drm_encoder *encoder)
+{
+       struct zx_vga *vga = to_zx_vga(encoder);
+       struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+       vou_inf_disable(VOU_VGA, encoder->crtc);
+
+       /* Clear bit to power down VGA DACs */
+       regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
+}
+
+static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = {
+       .enable = zx_vga_encoder_enable,
+       .disable = zx_vga_encoder_disable,
+};
+
+static const struct drm_encoder_funcs zx_vga_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static int zx_vga_connector_get_modes(struct drm_connector *connector)
+{
+       struct zx_vga *vga = to_zx_vga(connector);
+       struct edid *edid;
+       int ret;
+
+       /*
+        * Clear both detection bits to switch I2C bus from device
+        * detecting to EDID reading.
+        */
+       zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
+
+       edid = drm_get_edid(connector, &vga->ddc->adap);
+       if (!edid) {
+               /*
+                * If EDID reading fails, we set the device state into
+                * disconnected.  Locking is not required here, since the
+                * VGA_AUTO_DETECT_SEL register write in irq handler cannot
+                * be triggered when both detection bits are cleared as above.
+                */
+               zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+                         VGA_DETECT_SEL_NO_DEVICE);
+               vga->connected = false;
+               return 0;
+       }
+
+       /*
+        * As edid reading succeeds, device must be connected, so we set
+        * up detection bit for unplug interrupt here.
+        */
+       zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
+
+       drm_mode_connector_update_edid_property(connector, edid);
+       ret = drm_add_edid_modes(connector, edid);
+       kfree(edid);
+
+       return ret;
+}
+
+static enum drm_mode_status
+zx_vga_connector_mode_valid(struct drm_connector *connector,
+                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = {
+       .get_modes = zx_vga_connector_get_modes,
+       .mode_valid = zx_vga_connector_mode_valid,
+};
+
+static enum drm_connector_status
+zx_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct zx_vga *vga = to_zx_vga(connector);
+
+       return vga->connected ? connector_status_connected :
+                               connector_status_disconnected;
+}
+
+static const struct drm_connector_funcs zx_vga_connector_funcs = {
+       .dpms = drm_atomic_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = zx_vga_connector_detect,
+       .destroy = drm_connector_cleanup,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga)
+{
+       struct drm_encoder *encoder = &vga->encoder;
+       struct drm_connector *connector = &vga->connector;
+       struct device *dev = vga->dev;
+       int ret;
+
+       encoder->possible_crtcs = VOU_CRTC_MASK;
+
+       ret = drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
+                              DRM_MODE_ENCODER_DAC, NULL);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret);
+               return ret;
+       };
+
+       drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
+
+       vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+       ret = drm_connector_init(drm, connector, &zx_vga_connector_funcs,
+                                DRM_MODE_CONNECTOR_VGA);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret);
+               goto clean_encoder;
+       };
+
+       drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
+
+       ret = drm_mode_connector_attach_encoder(connector, encoder);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret);
+               goto clean_connector;
+       };
+
+       return 0;
+
+clean_connector:
+       drm_connector_cleanup(connector);
+clean_encoder:
+       drm_encoder_cleanup(encoder);
+       return ret;
+}
+
+static int zx_vga_pwrctrl_init(struct zx_vga *vga)
+{
+       struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+       struct device *dev = vga->dev;
+       struct of_phandle_args out_args;
+       struct regmap *regmap;
+       int ret;
+
+       ret = of_parse_phandle_with_fixed_args(dev->of_node,
+                               "zte,vga-power-control", 2, 0, &out_args);
+       if (ret)
+               return ret;
+
+       regmap = syscon_node_to_regmap(out_args.np);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               goto out;
+       }
+
+       pwrctrl->regmap = regmap;
+       pwrctrl->reg = out_args.args[0];
+       pwrctrl->mask = out_args.args[1];
+
+out:
+       of_node_put(out_args.np);
+       return ret;
+}
+
+static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg)
+{
+       int len = msg->len;
+       u8 *buf = msg->buf;
+       u32 offset = 0;
+       int i;
+
+       reinit_completion(&vga->complete);
+
+       /* Select combo write */
+       zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO);
+       zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0);
+
+       while (len > 0) {
+               u32 cnt;
+
+               /* Clear RX FIFO */
+               zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR,
+                              VGA_RX_FIFO_CLEAR);
+
+               /* Data offset to read from */
+               zx_writel(vga->mmio + VGA_SUB_ADDR, offset);
+
+               /* Kick off the transfer */
+               zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS,
+                              VGA_CMD_TRANS);
+
+               if (!wait_for_completion_timeout(&vga->complete,
+                                                msecs_to_jiffies(1000))) {
+                       DRM_DEV_ERROR(vga->dev, "transfer timeout\n");
+                       return -ETIMEDOUT;
+               }
+
+               cnt = zx_readl(vga->mmio + VGA_RXF_STATUS);
+               cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT;
+               /* FIFO status may report more data than we need to read */
+               cnt = min_t(u32, len, cnt);
+
+               for (i = 0; i < cnt; i++)
+                       *buf++ = zx_readl(vga->mmio + VGA_DATA);
+
+               len -= cnt;
+               offset += cnt;
+       }
+
+       return 0;
+}
+
+static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg)
+{
+       /*
+        * The DDC I2C adapter is only for reading EDID data, so we assume
+        * that the write to this adapter must be the EDID data offset.
+        */
+       if ((msg->len != 1) || ((msg->addr != DDC_ADDR)))
+               return -EINVAL;
+
+       /* Hardware will take care of the slave address shifting */
+       zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr);
+
+       return 0;
+}
+
+static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                          int num)
+{
+       struct zx_vga *vga = i2c_get_adapdata(adap);
+       struct zx_vga_i2c *ddc = vga->ddc;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&ddc->lock);
+
+       for (i = 0; i < num; i++) {
+               if (msgs[i].flags & I2C_M_RD)
+                       ret = zx_vga_i2c_read(vga, &msgs[i]);
+               else
+                       ret = zx_vga_i2c_write(vga, &msgs[i]);
+
+               if (ret < 0)
+                       break;
+       }
+
+       if (!ret)
+               ret = num;
+
+       mutex_unlock(&ddc->lock);
+
+       return ret;
+}
+
+static u32 zx_vga_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm zx_vga_algorithm = {
+       .master_xfer    = zx_vga_i2c_xfer,
+       .functionality  = zx_vga_i2c_func,
+};
+
+static int zx_vga_ddc_register(struct zx_vga *vga)
+{
+       struct device *dev = vga->dev;
+       struct i2c_adapter *adap;
+       struct zx_vga_i2c *ddc;
+       int ret;
+
+       ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
+       if (!ddc)
+               return -ENOMEM;
+
+       vga->ddc = ddc;
+       mutex_init(&ddc->lock);
+
+       adap = &ddc->adap;
+       adap->owner = THIS_MODULE;
+       adap->class = I2C_CLASS_DDC;
+       adap->dev.parent = dev;
+       adap->algo = &zx_vga_algorithm;
+       snprintf(adap->name, sizeof(adap->name), "zx vga i2c");
+
+       ret = i2c_add_adapter(adap);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret);
+               return ret;
+       }
+
+       i2c_set_adapdata(adap, vga);
+
+       return 0;
+}
+
+static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id)
+{
+       struct zx_vga *vga = dev_id;
+
+       drm_helper_hpd_irq_event(vga->connector.dev);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
+{
+       struct zx_vga *vga = dev_id;
+       u32 status;
+
+       status = zx_readl(vga->mmio + VGA_I2C_STATUS);
+
+       /* Clear interrupt status */
+       zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
+                      VGA_CLEAR_IRQ);
+
+       if (status & VGA_DEVICE_CONNECTED) {
+               /*
+                * Since VGA_DETECT_SEL bits need to be reset for switching DDC
+                * bus from device detection to EDID read, rather than setting
+                * up HAS_DEVICE bit here, we need to do that in .get_modes
+                * hook for unplug detecting after EDID read succeeds.
+                */
+               vga->connected = true;
+               return IRQ_WAKE_THREAD;
+       }
+
+       if (status & VGA_DEVICE_DISCONNECTED) {
+               zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+                         VGA_DETECT_SEL_NO_DEVICE);
+               vga->connected = false;
+               return IRQ_WAKE_THREAD;
+       }
+
+       if (status & VGA_TRANS_DONE) {
+               complete(&vga->complete);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static void zx_vga_hw_init(struct zx_vga *vga)
+{
+       unsigned long ref = clk_get_rate(vga->i2c_wclk);
+       int div;
+
+       /*
+        * Set up I2C fast speed divider per formula below to get 400kHz.
+        *   scl = ref / ((div + 1) * 4)
+        */
+       div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1;
+       zx_writel(vga->mmio + VGA_CLK_DIV_FS, div);
+
+       /* Set up device detection */
+       zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80);
+       zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE);
+
+       /*
+        * We need to poke monitor via DDC bus to get connection irq
+        * start working.
+        */
+       zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR);
+       zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS);
+}
+
+static int zx_vga_bind(struct device *dev, struct device *master, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm = data;
+       struct resource *res;
+       struct zx_vga *vga;
+       int irq;
+       int ret;
+
+       vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
+       if (!vga)
+               return -ENOMEM;
+
+       vga->dev = dev;
+       dev_set_drvdata(dev, vga);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       vga->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(vga->mmio))
+               return PTR_ERR(vga->mmio);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk");
+       if (IS_ERR(vga->i2c_wclk)) {
+               ret = PTR_ERR(vga->i2c_wclk);
+               DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret);
+               return ret;
+       }
+
+       ret = zx_vga_pwrctrl_init(vga);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
+               return ret;
+       }
+
+       ret = zx_vga_ddc_register(vga);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret);
+               return ret;
+       }
+
+       ret = zx_vga_register(drm, vga);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret);
+               return ret;
+       }
+
+       init_completion(&vga->complete);
+
+       ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler,
+                                       zx_vga_irq_thread, IRQF_SHARED,
+                                       dev_name(dev), vga);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(vga->i2c_wclk);
+       if (ret)
+               return ret;
+
+       zx_vga_hw_init(vga);
+
+       return 0;
+}
+
+static void zx_vga_unbind(struct device *dev, struct device *master,
+                         void *data)
+{
+       struct zx_vga *vga = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(vga->i2c_wclk);
+}
+
+static const struct component_ops zx_vga_component_ops = {
+       .bind = zx_vga_bind,
+       .unbind = zx_vga_unbind,
+};
+
+static int zx_vga_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &zx_vga_component_ops);
+}
+
+static int zx_vga_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &zx_vga_component_ops);
+       return 0;
+}
+
+static const struct of_device_id zx_vga_of_match[] = {
+       { .compatible = "zte,zx296718-vga", },
+       { /* end */ },
+};
+MODULE_DEVICE_TABLE(of, zx_vga_of_match);
+
+struct platform_driver zx_vga_driver = {
+       .probe = zx_vga_probe,
+       .remove = zx_vga_remove,
+       .driver = {
+               .name = "zx-vga",
+               .of_match_table = zx_vga_of_match,
+       },
+};
diff --git a/drivers/gpu/drm/zte/zx_vga_regs.h b/drivers/gpu/drm/zte/zx_vga_regs.h
new file mode 100644 (file)
index 0000000..feaa345
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_VGA_REGS_H__
+#define __ZX_VGA_REGS_H__
+
+#define VGA_CMD_CFG                    0x04
+#define VGA_CMD_TRANS                  BIT(6)
+#define VGA_CMD_COMBO                  BIT(5)
+#define VGA_CMD_RW                     BIT(4)
+#define VGA_SUB_ADDR                   0x0c
+#define VGA_DEVICE_ADDR                        0x10
+#define VGA_CLK_DIV_FS                 0x14
+#define VGA_RXF_CTRL                   0x20
+#define VGA_RX_FIFO_CLEAR              BIT(7)
+#define VGA_DATA                       0x24
+#define VGA_I2C_STATUS                 0x28
+#define VGA_DEVICE_DISCONNECTED                BIT(7)
+#define VGA_DEVICE_CONNECTED           BIT(6)
+#define VGA_CLEAR_IRQ                  BIT(4)
+#define VGA_TRANS_DONE                 BIT(0)
+#define VGA_RXF_STATUS                 0x30
+#define VGA_RXF_COUNT_SHIFT            2
+#define VGA_RXF_COUNT_MASK             GENMASK(7, 2)
+#define VGA_AUTO_DETECT_PARA           0x34
+#define VGA_AUTO_DETECT_SEL            0x38
+#define VGA_DETECT_SEL_HAS_DEVICE      BIT(1)
+#define VGA_DETECT_SEL_NO_DEVICE       BIT(0)
+
+#endif /* __ZX_VGA_REGS_H__ */
index b500c8d..5fbd10b 100644 (file)
@@ -23,6 +23,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 
+#include "zx_common_regs.h"
 #include "zx_drm_drv.h"
 #include "zx_plane.h"
 #include "zx_vou.h"
@@ -122,6 +123,8 @@ struct zx_crtc {
        struct drm_plane *primary;
        struct zx_vou_hw *vou;
        void __iomem *chnreg;
+       void __iomem *chncsc;
+       void __iomem *dither;
        const struct zx_crtc_regs *regs;
        const struct zx_crtc_bits *bits;
        enum vou_chn_type chn_type;
@@ -204,6 +207,11 @@ static struct vou_inf vou_infs[] = {
                .clocks_en_bits = BIT(15),
                .clocks_sel_bits = BIT(11) | BIT(0),
        },
+       [VOU_VGA] = {
+               .data_sel = VOU_RGB_888,
+               .clocks_en_bits = BIT(1),
+               .clocks_sel_bits = BIT(10),
+       },
 };
 
 static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
@@ -227,9 +235,26 @@ void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
        struct zx_crtc *zcrtc = to_zx_crtc(crtc);
        struct zx_vou_hw *vou = zcrtc->vou;
        struct vou_inf *inf = &vou_infs[id];
+       void __iomem *dither = zcrtc->dither;
+       void __iomem *csc = zcrtc->chncsc;
        bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
        u32 data_sel_shift = id << 1;
 
+       if (inf->data_sel != VOU_YUV444) {
+               /* Enable channel CSC for RGB output */
+               zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
+                              CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
+               zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
+                              CSC_WORK_ENABLE);
+
+               /* Bypass Dither block for RGB output */
+               zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
+                              DITHER_BYSPASS);
+       } else {
+               zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
+               zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
+       }
+
        /* Select data format */
        zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
                       inf->data_sel << data_sel_shift);
@@ -525,20 +550,24 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
 
        if (chn_type == VOU_CHN_MAIN) {
                zplane->layer = vou->osd + MAIN_GL_OFFSET;
-               zplane->csc = vou->osd + MAIN_CSC_OFFSET;
+               zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
                zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
                zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
                zplane->bits = &zx_gl_bits[0];
                zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
+               zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
+               zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
                zcrtc->regs = &main_crtc_regs;
                zcrtc->bits = &main_crtc_bits;
        } else {
                zplane->layer = vou->osd + AUX_GL_OFFSET;
-               zplane->csc = vou->osd + AUX_CSC_OFFSET;
+               zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
                zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
                zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
                zplane->bits = &zx_gl_bits[1];
                zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
+               zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
+               zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
                zcrtc->regs = &aux_crtc_regs;
                zcrtc->bits = &aux_crtc_bits;
        }
@@ -705,9 +734,6 @@ static void vou_hw_init(struct zx_vou_hw *vou)
        /* Release reset for all VOU modules */
        zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
 
-       /* Enable clock auto-gating for all VOU modules */
-       zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
-
        /* Enable all VOU module clocks */
        zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
 
index c066ef1..5a21835 100644 (file)
 
 /* Sub-module offset */
 #define MAIN_GL_OFFSET                 0x130
-#define MAIN_CSC_OFFSET                        0x580
+#define MAIN_GL_CSC_OFFSET             0x580
+#define MAIN_CHN_CSC_OFFSET            0x6c0
 #define MAIN_HBSC_OFFSET               0x820
+#define MAIN_DITHER_OFFSET             0x960
 #define MAIN_RSZ_OFFSET                        0x600 /* OTFPPU sub-module */
 
 #define AUX_GL_OFFSET                  0x200
-#define AUX_CSC_OFFSET                 0x5d0
+#define AUX_GL_CSC_OFFSET              0x5d0
+#define AUX_CHN_CSC_OFFSET             0x710
 #define AUX_HBSC_OFFSET                        0x860
+#define AUX_DITHER_OFFSET              0x970
 #define AUX_RSZ_OFFSET                 0x800
 
 #define OSD_VL0_OFFSET                 0x040
 #define CHN_INTERLACE_BUF_CTRL         0x24
 #define CHN_INTERLACE_EN               BIT(2)
 
+/* Dither registers */
+#define OSD_DITHER_CTRL0               0x00
+#define DITHER_BYSPASS                 BIT(31)
+
 /* TIMING_CTRL registers */
 #define TIMING_TC_ENABLE               0x04
 #define AUX_TC_EN                      BIT(1)
index bce4a53..03a59cb 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <linux/ctype.h>
 
+struct drm_crtc;
+
 uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision);
 
 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
index a8176a8..adf4e91 100644 (file)
@@ -93,11 +93,6 @@ struct drm_plane_helper_funcs;
  * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings
  * @mode: current mode timings
  * @mode_blob: &drm_property_blob for @mode
- * @degamma_lut: Lookup table for converting framebuffer pixel data
- *     before apply the conversion matrix
- * @ctm: Transformation matrix
- * @gamma_lut: Lookup table for converting pixel data after the
- *     conversion matrix
  * @state: backpointer to global drm_atomic_state
  *
  * Note that the distinction between @enable and @active is rather subtile:
@@ -144,9 +139,30 @@ struct drm_crtc_state {
        /* blob property to expose current mode to atomic userspace */
        struct drm_property_blob *mode_blob;
 
-       /* blob property to expose color management to userspace */
+       /**
+        * @degamma_lut:
+        *
+        * Lookup table for converting framebuffer pixel data before apply the
+        * color conversion matrix @ctm. See drm_crtc_enable_color_mgmt(). The
+        * blob (if not NULL) is an array of &struct drm_color_lut.
+        */
        struct drm_property_blob *degamma_lut;
+
+       /**
+        * @ctm:
+        *
+        * Color transformation matrix. See drm_crtc_enable_color_mgmt(). The
+        * blob (if not NULL) is a &struct drm_color_ctm.
+        */
        struct drm_property_blob *ctm;
+
+       /**
+        * @gamma_lut:
+        *
+        * Lookup table for converting pixel data after the color conversion
+        * matrix @ctm.  See drm_crtc_enable_color_mgmt(). The blob (if not
+        * NULL) is an array of &struct drm_color_lut.
+        */
        struct drm_property_blob *gamma_lut;
 
        /**
@@ -313,6 +329,12 @@ struct drm_crtc_funcs {
         *
         * This callback is optional.
         *
+        * Atomic drivers who want to support gamma tables should implement the
+        * atomic color management support, enabled by calling
+        * drm_crtc_enable_color_mgmt(), which then supports the legacy gamma
+        * interface through the drm_atomic_helper_legacy_gamma_set()
+        * compatibility implementation.
+        *
         * NOTE:
         *
         * Drivers that support gamma tables and also fbdev emulation through
index c0bd0d7..f7007e5 100644 (file)
 
 #define DP_GUID                                    0x030   /* 1.2 */
 
+#define DP_DSC_SUPPORT                      0x060   /* DP 1.4 */
+# define DP_DSC_DECOMPRESSION_IS_SUPPORTED  (1 << 0)
+
+#define DP_DSC_REV                          0x061
+# define DP_DSC_MAJOR_MASK                  (0xf << 0)
+# define DP_DSC_MINOR_MASK                  (0xf << 4)
+# define DP_DSC_MAJOR_SHIFT                 0
+# define DP_DSC_MINOR_SHIFT                 4
+
+#define DP_DSC_RC_BUF_BLK_SIZE              0x062
+# define DP_DSC_RC_BUF_BLK_SIZE_1           0x0
+# define DP_DSC_RC_BUF_BLK_SIZE_4           0x1
+# define DP_DSC_RC_BUF_BLK_SIZE_16          0x2
+# define DP_DSC_RC_BUF_BLK_SIZE_64          0x3
+
+#define DP_DSC_RC_BUF_SIZE                  0x063
+
+#define DP_DSC_SLICE_CAP_1                  0x064
+# define DP_DSC_1_PER_DP_DSC_SINK           (1 << 0)
+# define DP_DSC_2_PER_DP_DSC_SINK           (1 << 1)
+# define DP_DSC_4_PER_DP_DSC_SINK           (1 << 3)
+# define DP_DSC_6_PER_DP_DSC_SINK           (1 << 4)
+# define DP_DSC_8_PER_DP_DSC_SINK           (1 << 5)
+# define DP_DSC_10_PER_DP_DSC_SINK          (1 << 6)
+# define DP_DSC_12_PER_DP_DSC_SINK          (1 << 7)
+
+#define DP_DSC_LINE_BUF_BIT_DEPTH           0x065
+# define DP_DSC_LINE_BUF_BIT_DEPTH_MASK     (0xf << 0)
+# define DP_DSC_LINE_BUF_BIT_DEPTH_9        0x0
+# define DP_DSC_LINE_BUF_BIT_DEPTH_10       0x1
+# define DP_DSC_LINE_BUF_BIT_DEPTH_11       0x2
+# define DP_DSC_LINE_BUF_BIT_DEPTH_12       0x3
+# define DP_DSC_LINE_BUF_BIT_DEPTH_13       0x4
+# define DP_DSC_LINE_BUF_BIT_DEPTH_14       0x5
+# define DP_DSC_LINE_BUF_BIT_DEPTH_15       0x6
+# define DP_DSC_LINE_BUF_BIT_DEPTH_16       0x7
+# define DP_DSC_LINE_BUF_BIT_DEPTH_8        0x8
+
+#define DP_DSC_BLK_PREDICTION_SUPPORT       0x066
+# define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0)
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_LOW       0x067   /* eDP 1.4 */
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_HI        0x068   /* eDP 1.4 */
+
+#define DP_DSC_DEC_COLOR_FORMAT_CAP         0x069
+# define DP_DSC_RGB                         (1 << 0)
+# define DP_DSC_YCbCr444                    (1 << 1)
+# define DP_DSC_YCbCr422_Simple             (1 << 2)
+# define DP_DSC_YCbCr422_Native             (1 << 3)
+# define DP_DSC_YCbCr420_Native             (1 << 4)
+
+#define DP_DSC_DEC_COLOR_DEPTH_CAP          0x06A
+# define DP_DSC_8_BPC                       (1 << 1)
+# define DP_DSC_10_BPC                      (1 << 2)
+# define DP_DSC_12_BPC                      (1 << 3)
+
+#define DP_DSC_PEAK_THROUGHPUT              0x06B
+# define DP_DSC_THROUGHPUT_MODE_0_MASK      (0xf << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_SHIFT     0
+# define DP_DSC_THROUGHPUT_MODE_0_340       (1 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_400       (2 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_450       (3 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_500       (4 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_550       (5 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_600       (6 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_650       (7 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_700       (8 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_750       (9 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_800       (10 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_850       (11 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_900       (12 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_950       (13 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_1000      (14 << 0)
+# define DP_DSC_THROUGHPUT_MODE_1_MASK      (0xf << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_SHIFT     4
+# define DP_DSC_THROUGHPUT_MODE_1_340       (1 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_400       (2 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_450       (3 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_500       (4 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_550       (5 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_600       (6 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_650       (7 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_700       (8 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_750       (9 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_800       (10 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_850       (11 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_900       (12 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_950       (13 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_1000      (14 << 4)
+
+#define DP_DSC_MAX_SLICE_WIDTH              0x06C
+
+#define DP_DSC_SLICE_CAP_2                  0x06D
+# define DP_DSC_16_PER_DP_DSC_SINK          (1 << 0)
+# define DP_DSC_20_PER_DP_DSC_SINK          (1 << 1)
+# define DP_DSC_24_PER_DP_DSC_SINK          (1 << 2)
+
+#define DP_DSC_BITS_PER_PIXEL_INC           0x06F
+# define DP_DSC_BITS_PER_PIXEL_1_16         0x0
+# define DP_DSC_BITS_PER_PIXEL_1_8          0x1
+# define DP_DSC_BITS_PER_PIXEL_1_4          0x2
+# define DP_DSC_BITS_PER_PIXEL_1_2          0x3
+# define DP_DSC_BITS_PER_PIXEL_1            0x4
+
 #define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
 # define DP_PSR_IS_SUPPORTED                1
 # define DP_PSR2_IS_SUPPORTED              2       /* eDP 1.4 */
 #define DP_AUX_FRAME_SYNC_VALUE                    0x15c   /* eDP 1.4 */
 # define DP_AUX_FRAME_SYNC_VALID           (1 << 0)
 
+#define DP_DSC_ENABLE                       0x160   /* DP 1.4 */
+
 #define DP_PSR_EN_CFG                      0x170   /* XXX 1.2? */
 # define DP_PSR_ENABLE                     (1 << 0)
 # define DP_PSR_MAIN_LINK_ACTIVE           (1 << 1)
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
 
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
+# define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE    (1 << 0)
+# define DP_LOCK_ACQUISITION_REQUEST         (1 << 1)
+# define DP_CEC_IRQ                          (1 << 2)
 
 #define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
 
 # define DP_VSC_EXT_CEA_SDP_SUPPORTED                  (1 << 6)  /* DP 1.4 */
 # define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED         (1 << 7)  /* DP 1.4 */
 
+/* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */
+#define DP_CEC_TUNNELING_CAPABILITY            0x3000
+# define DP_CEC_TUNNELING_CAPABLE               (1 << 0)
+# define DP_CEC_SNOOPING_CAPABLE                (1 << 1)
+# define DP_CEC_MULTIPLE_LA_CAPABLE             (1 << 2)
+
+#define DP_CEC_TUNNELING_CONTROL               0x3001
+# define DP_CEC_TUNNELING_ENABLE                (1 << 0)
+# define DP_CEC_SNOOPING_ENABLE                 (1 << 1)
+
+#define DP_CEC_RX_MESSAGE_INFO                 0x3002
+# define DP_CEC_RX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_RX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_RX_MESSAGE_HPD_STATE            (1 << 4)
+# define DP_CEC_RX_MESSAGE_HPD_LOST             (1 << 5)
+# define DP_CEC_RX_MESSAGE_ACKED                (1 << 6)
+# define DP_CEC_RX_MESSAGE_ENDED                (1 << 7)
+
+#define DP_CEC_TX_MESSAGE_INFO                 0x3003
+# define DP_CEC_TX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_TX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_TX_RETRY_COUNT_MASK             (0x7 << 4)
+# define DP_CEC_TX_RETRY_COUNT_SHIFT            4
+# define DP_CEC_TX_MESSAGE_SEND                 (1 << 7)
+
+#define DP_CEC_TUNNELING_IRQ_FLAGS             0x3004
+# define DP_CEC_RX_MESSAGE_INFO_VALID           (1 << 0)
+# define DP_CEC_RX_MESSAGE_OVERFLOW             (1 << 1)
+# define DP_CEC_TX_MESSAGE_SENT                 (1 << 4)
+# define DP_CEC_TX_LINE_ERROR                   (1 << 5)
+# define DP_CEC_TX_ADDRESS_NACK_ERROR           (1 << 6)
+# define DP_CEC_TX_DATA_NACK_ERROR              (1 << 7)
+
+#define DP_CEC_LOGICAL_ADDRESS_MASK            0x300E /* 0x300F word */
+# define DP_CEC_LOGICAL_ADDRESS_0               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_1               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_2               (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_3               (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_4               (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_5               (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_6               (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_7               (1 << 7)
+#define DP_CEC_LOGICAL_ADDRESS_MASK_2          0x300F /* 0x300E word */
+# define DP_CEC_LOGICAL_ADDRESS_8               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_9               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_10              (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_11              (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_12              (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_13              (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_14              (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_15              (1 << 7)
+
+#define DP_CEC_RX_MESSAGE_BUFFER               0x3010
+#define DP_CEC_TX_MESSAGE_BUFFER               0x3020
+#define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
+
 /* DP 1.2 Sideband message defines */
 /* peer device type - DP 1.2a Table 2-92 */
 #define DP_PEER_DEVICE_NONE            0x0
index a5ecc0a..199a63f 100644 (file)
@@ -41,6 +41,10 @@ struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev,
 struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
        unsigned int plane);
 
+dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
+                                  struct drm_plane_state *state,
+                                  unsigned int plane);
+
 int drm_fb_cma_prepare_fb(struct drm_plane *plane,
                          struct drm_plane_state *state);
 
index f962d33..b42529e 100644 (file)
@@ -26,6 +26,13 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj)
        return container_of(gem_obj, struct drm_gem_cma_object, base);
 }
 
+#ifndef CONFIG_MMU
+#define DRM_GEM_CMA_UNMAPPED_AREA_FOPS \
+       .get_unmapped_area      = drm_gem_cma_get_unmapped_area,
+#else
+#define DRM_GEM_CMA_UNMAPPED_AREA_FOPS
+#endif
+
 /**
  * DEFINE_DRM_GEM_CMA_FOPS() - macro to generate file operations for CMA drivers
  * @name: name for the generated structure
@@ -50,6 +57,7 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj)
                .read           = drm_read,\
                .llseek         = noop_llseek,\
                .mmap           = drm_gem_cma_mmap,\
+               DRM_GEM_CMA_UNMAPPED_AREA_FOPS \
        }
 
 /* free GEM object */
@@ -85,15 +93,6 @@ unsigned long drm_gem_cma_get_unmapped_area(struct file *filp,
                                            unsigned long len,
                                            unsigned long pgoff,
                                            unsigned long flags);
-#else
-static inline unsigned long drm_gem_cma_get_unmapped_area(struct file *filp,
-                                                         unsigned long addr,
-                                                         unsigned long len,
-                                                         unsigned long pgoff,
-                                                         unsigned long flags)
-{
-       return -EINVAL;
-}
 #endif
 
 #ifdef CONFIG_DEBUG_FS
index 3e3ab84..d37beef 100644 (file)
@@ -14,7 +14,6 @@
 #define _LINUX_SYNC_FILE_H
 
 #include <linux/types.h>
-#include <linux/kref.h>
 #include <linux/ktime.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
@@ -24,7 +23,6 @@
 /**
  * struct sync_file - sync file to export to the userspace
  * @file:              file representing this fence
- * @kref:              reference count on fence.
  * @name:              name of sync_file.  Useful for debugging
  * @sync_file_list:    membership in global file list
  * @wq:                        wait queue for fence signaling
@@ -33,7 +31,6 @@
  */
 struct sync_file {
        struct file             *file;
-       struct kref             kref;
        char                    name[32];
 #ifdef CONFIG_DEBUG_FS
        struct list_head        sync_file_list;